Create separate Android.mk for main build targets
The runtime, compiler, dex2oat, and oatdump now are in seperate trees
to prevent dependency creep. They can now be individually built
without rebuilding the rest of the art projects. dalvikvm and jdwpspy
were already this way. Builds in the art directory should behave as
before, building everything including tests.
Change-Id: Ic6b1151e5ed0f823c3dd301afd2b13eb2d8feb81
diff --git a/compiler/dex/arena_allocator.cc b/compiler/dex/arena_allocator.cc
new file mode 100644
index 0000000..3a3e385
--- /dev/null
+++ b/compiler/dex/arena_allocator.cc
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2013 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 "compiler_internals.h"
+#include "dex_file-inl.h"
+#include "arena_allocator.h"
+#include "base/logging.h"
+
+namespace art {
+
+static const char* alloc_names[ArenaAllocator::kNumAllocKinds] = {
+ "Misc ",
+ "BasicBlock ",
+ "LIR ",
+ "MIR ",
+ "DataFlow ",
+ "GrowList ",
+ "GrowBitMap ",
+ "Dalvik2SSA ",
+ "DebugInfo ",
+ "Successor ",
+ "RegAlloc ",
+ "Data ",
+ "Preds ",
+};
+
+ArenaAllocator::ArenaAllocator(size_t default_size)
+ : default_size_(default_size),
+ block_size_(default_size - sizeof(ArenaMemBlock)),
+ arena_head_(NULL),
+ current_block_(NULL),
+ num_arena_blocks_(0),
+ malloc_bytes_(0),
+ lost_bytes_(0),
+ num_allocations_(0) {
+ memset(&alloc_stats_[0], 0, sizeof(alloc_stats_));
+ // Start with an empty arena.
+ arena_head_ = current_block_ = EmptyArenaBlock();
+ num_arena_blocks_++;
+}
+
+ArenaAllocator::~ArenaAllocator() {
+ // Reclaim all the arena blocks allocated so far.
+ ArenaMemBlock* head = arena_head_;
+ while (head != NULL) {
+ ArenaMemBlock* p = head;
+ head = head->next;
+ free(p);
+ }
+ arena_head_ = NULL;
+ num_arena_blocks_ = 0;
+}
+
+// Return an arena with no storage for use as a sentinal.
+ArenaAllocator::ArenaMemBlock* ArenaAllocator::EmptyArenaBlock() {
+ ArenaMemBlock* res = static_cast<ArenaMemBlock*>(malloc(sizeof(ArenaMemBlock)));
+ malloc_bytes_ += sizeof(ArenaMemBlock);
+ res->block_size = 0;
+ res->bytes_allocated = 0;
+ res->next = NULL;
+ return res;
+}
+
+// Arena-based malloc for compilation tasks.
+void* ArenaAllocator::NewMem(size_t size, bool zero, ArenaAllocKind kind) {
+ DCHECK(current_block_ != NULL);
+ DCHECK(arena_head_ != NULL);
+ size = (size + 3) & ~3;
+ alloc_stats_[kind] += size;
+ ArenaMemBlock* allocation_block = current_block_; // Assume we'll fit.
+ size_t remaining_space = current_block_->block_size - current_block_->bytes_allocated;
+ if (remaining_space < size) {
+ /*
+ * Time to allocate a new block. If this is a large allocation or we have
+ * significant space remaining in the current block then fulfill the allocation
+ * request with a custom-sized malloc() - otherwise grab a new standard block.
+ */
+ size_t allocation_size = sizeof(ArenaMemBlock);
+ if ((remaining_space >= ARENA_HIGH_WATER) || (size > block_size_)) {
+ allocation_size += size;
+ } else {
+ allocation_size += block_size_;
+ }
+ ArenaMemBlock *new_block = static_cast<ArenaMemBlock*>(malloc(allocation_size));
+ if (new_block == NULL) {
+ LOG(FATAL) << "Arena allocation failure";
+ }
+ malloc_bytes_ += allocation_size;
+ new_block->block_size = allocation_size - sizeof(ArenaMemBlock);
+ new_block->bytes_allocated = 0;
+ new_block->next = NULL;
+ num_arena_blocks_++;
+ /*
+ * If the new block is completely full, insert it into the head of the list so we don't
+ * bother trying to fit more and won't hide the potentially allocatable space on the
+ * last (current_block_) block. TUNING: if we move to a mark scheme, revisit
+ * this code to keep allocation order intact.
+ */
+ if (new_block->block_size == size) {
+ new_block->next = arena_head_;
+ arena_head_ = new_block;
+ } else {
+ int lost = (current_block_->block_size - current_block_->bytes_allocated);
+ lost_bytes_ += lost;
+ current_block_->next = new_block;
+ current_block_ = new_block;
+ }
+ allocation_block = new_block;
+ }
+ void* ptr = &allocation_block->ptr[allocation_block->bytes_allocated];
+ allocation_block->bytes_allocated += size;
+ if (zero) {
+ memset(ptr, 0, size);
+ }
+ num_allocations_++;
+ return ptr;
+}
+
+// Dump memory usage stats.
+void ArenaAllocator::DumpMemStats(std::ostream& os) const {
+ size_t total = 0;
+ for (int i = 0; i < kNumAllocKinds; i++) {
+ total += alloc_stats_[i];
+ }
+ os << " MEM: used: " << total << ", allocated: " << malloc_bytes_
+ << ", lost: " << lost_bytes_ << "\n";
+ os << "Number of blocks allocated: " << num_arena_blocks_ << ", Number of allocations: "
+ << num_allocations_ << ", avg: " << total / num_allocations_ << "\n";
+ os << "===== Allocation by kind\n";
+ for (int i = 0; i < kNumAllocKinds; i++) {
+ os << alloc_names[i] << std::setw(10) << alloc_stats_[i] << "\n";
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/arena_allocator.h b/compiler/dex/arena_allocator.h
new file mode 100644
index 0000000..78d4614
--- /dev/null
+++ b/compiler/dex/arena_allocator.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_COMPILER_ARENA_ALLOCATOR_H_
+#define ART_SRC_COMPILER_DEX_COMPILER_ARENA_ALLOCATOR_H_
+
+#include <stdint.h>
+#include <stddef.h>
+#include "compiler_enums.h"
+
+namespace art {
+
+#define ARENA_DEFAULT_BLOCK_SIZE (256 * 1024)
+#define ARENA_HIGH_WATER (16 * 1024)
+
+class ArenaAllocator {
+ public:
+
+ // Type of allocation for memory tuning.
+ enum ArenaAllocKind {
+ kAllocMisc,
+ kAllocBB,
+ kAllocLIR,
+ kAllocMIR,
+ kAllocDFInfo,
+ kAllocGrowableArray,
+ kAllocGrowableBitMap,
+ kAllocDalvikToSSAMap,
+ kAllocDebugInfo,
+ kAllocSuccessor,
+ kAllocRegAlloc,
+ kAllocData,
+ kAllocPredecessors,
+ kNumAllocKinds
+ };
+
+ ArenaAllocator(size_t default_size = ARENA_DEFAULT_BLOCK_SIZE);
+ ~ArenaAllocator();
+ void* NewMem(size_t size, bool zero, ArenaAllocKind kind);
+ size_t BytesAllocated() {
+ return malloc_bytes_;
+ }
+
+ void DumpMemStats(std::ostream& os) const;
+
+ private:
+
+ // Variable-length allocation block.
+ struct ArenaMemBlock {
+ size_t block_size;
+ size_t bytes_allocated;
+ ArenaMemBlock *next;
+ char ptr[0];
+ };
+
+ ArenaMemBlock* EmptyArenaBlock();
+
+ size_t default_size_; // Smallest size of new allocation block.
+ size_t block_size_; // Amount of allocatable bytes on a default block.
+ ArenaMemBlock* arena_head_; // Head of linked list of allocation blocks.
+ ArenaMemBlock* current_block_; // NOTE: code assumes there's always at least 1 block.
+ int num_arena_blocks_;
+ uint32_t malloc_bytes_; // Number of actual bytes malloc'd
+ uint32_t alloc_stats_[kNumAllocKinds]; // Bytes used by various allocation kinds.
+ uint32_t lost_bytes_; // Lost memory at end of too-small region
+ uint32_t num_allocations_;
+
+}; // ArenaAllocator
+
+
+struct MemStats {
+ public:
+ void Dump(std::ostream& os) const {
+ arena_.DumpMemStats(os);
+ }
+ MemStats(const ArenaAllocator &arena) : arena_(arena){};
+ private:
+ const ArenaAllocator &arena_;
+}; // MemStats
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_COMPILER_ARENA_ALLOCATOR_H_
diff --git a/compiler/dex/arena_bit_vector.cc b/compiler/dex/arena_bit_vector.cc
new file mode 100644
index 0000000..1fbf774
--- /dev/null
+++ b/compiler/dex/arena_bit_vector.cc
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2011 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 "compiler_internals.h"
+#include "dex_file-inl.h"
+
+namespace art {
+
+// TODO: profile to make sure this is still a win relative to just using shifted masks.
+static uint32_t check_masks[32] = {
+ 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010,
+ 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200,
+ 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000,
+ 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000,
+ 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000,
+ 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
+ 0x40000000, 0x80000000 };
+
+ArenaBitVector::ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits,
+ bool expandable, OatBitMapKind kind)
+ : arena_(arena),
+ expandable_(expandable),
+ kind_(kind),
+ storage_size_((start_bits + 31) >> 5),
+ storage_(static_cast<uint32_t*>(arena_->NewMem(storage_size_ * sizeof(uint32_t), true,
+ ArenaAllocator::kAllocGrowableBitMap))) {
+ DCHECK_EQ(sizeof(storage_[0]), 4U); // Assuming 32-bit units.
+}
+
+/*
+ * Determine whether or not the specified bit is set.
+ */
+bool ArenaBitVector::IsBitSet(unsigned int num) {
+ DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8);
+
+ unsigned int val = storage_[num >> 5] & check_masks[num & 0x1f];
+ return (val != 0);
+}
+
+// Mark all bits bit as "clear".
+void ArenaBitVector::ClearAllBits() {
+ memset(storage_, 0, storage_size_ * sizeof(uint32_t));
+}
+
+// Mark the specified bit as "set".
+/*
+ * TUNING: this could have pathologically bad growth/expand behavior. Make sure we're
+ * not using it badly or change resize mechanism.
+ */
+void ArenaBitVector::SetBit(unsigned int num) {
+ if (num >= storage_size_ * sizeof(uint32_t) * 8) {
+ DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << num;
+
+ /* Round up to word boundaries for "num+1" bits */
+ unsigned int new_size = (num + 1 + 31) >> 5;
+ DCHECK_GT(new_size, storage_size_);
+ uint32_t *new_storage =
+ static_cast<uint32_t*>(arena_->NewMem(new_size * sizeof(uint32_t), false,
+ ArenaAllocator::kAllocGrowableBitMap));
+ memcpy(new_storage, storage_, storage_size_ * sizeof(uint32_t));
+ // Zero out the new storage words.
+ memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * sizeof(uint32_t));
+ // TOTO: collect stats on space wasted because of resize.
+ storage_ = new_storage;
+ storage_size_ = new_size;
+ }
+
+ storage_[num >> 5] |= check_masks[num & 0x1f];
+}
+
+// Mark the specified bit as "unset".
+void ArenaBitVector::ClearBit(unsigned int num) {
+ DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8);
+ storage_[num >> 5] &= ~check_masks[num & 0x1f];
+}
+
+// Copy a whole vector to the other. Sizes must match.
+void ArenaBitVector::Copy(ArenaBitVector* src) {
+ DCHECK_EQ(storage_size_, src->GetStorageSize());
+ memcpy(storage_, src->GetRawStorage(), sizeof(uint32_t) * storage_size_);
+}
+
+// Intersect with another bit vector. Sizes and expandability must be the same.
+void ArenaBitVector::Intersect(const ArenaBitVector* src) {
+ DCHECK_EQ(storage_size_, src->GetStorageSize());
+ DCHECK_EQ(expandable_, src->IsExpandable());
+ for (unsigned int idx = 0; idx < storage_size_; idx++) {
+ storage_[idx] &= src->GetRawStorageWord(idx);
+ }
+}
+
+/*
+ * Union with another bit vector. Sizes and expandability must be the same.
+ */
+void ArenaBitVector::Union(const ArenaBitVector* src) {
+ DCHECK_EQ(storage_size_, src->GetStorageSize());
+ DCHECK_EQ(expandable_, src->IsExpandable());
+ for (unsigned int idx = 0; idx < storage_size_; idx++) {
+ storage_[idx] |= src->GetRawStorageWord(idx);
+ }
+}
+
+// Count the number of bits that are set.
+int ArenaBitVector::NumSetBits()
+{
+ unsigned int count = 0;
+
+ for (unsigned int word = 0; word < storage_size_; word++) {
+ count += __builtin_popcount(storage_[word]);
+ }
+ return count;
+}
+
+/*
+ * Mark specified number of bits as "set". Cannot set all bits like ClearAll
+ * since there might be unused bits - setting those to one will confuse the
+ * iterator.
+ */
+void ArenaBitVector::SetInitialBits(unsigned int num_bits)
+{
+ DCHECK_LE(((num_bits + 31) >> 5), storage_size_);
+ unsigned int idx;
+ for (idx = 0; idx < (num_bits >> 5); idx++) {
+ storage_[idx] = -1;
+ }
+ unsigned int rem_num_bits = num_bits & 0x1f;
+ if (rem_num_bits) {
+ storage_[idx] = (1 << rem_num_bits) - 1;
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/arena_bit_vector.h b/compiler/dex/arena_bit_vector.h
new file mode 100644
index 0000000..a950e82
--- /dev/null
+++ b/compiler/dex/arena_bit_vector.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_COMPILER_ARENA_BIT_VECTOR_H_
+#define ART_SRC_COMPILER_DEX_COMPILER_ARENA_BIT_VECTOR_H_
+
+#include <stdint.h>
+#include <stddef.h>
+#include "compiler_enums.h"
+#include "arena_allocator.h"
+
+namespace art {
+
+/*
+ * Expanding bitmap, used for tracking resources. Bits are numbered starting
+ * from zero. All operations on a BitVector are unsynchronized.
+ */
+class ArenaBitVector {
+ public:
+
+ class Iterator {
+ public:
+ Iterator(ArenaBitVector* bit_vector)
+ : p_bits_(bit_vector),
+ bit_storage_(bit_vector->GetRawStorage()),
+ bit_index_(0),
+ bit_size_(p_bits_->storage_size_ * sizeof(uint32_t) * 8) {};
+
+ // Return the position of the next set bit. -1 means end-of-element reached.
+ int Next() {
+ // Did anything obviously change since we started?
+ DCHECK_EQ(bit_size_, p_bits_->GetStorageSize() * sizeof(uint32_t) * 8);
+ DCHECK_EQ(bit_storage_, p_bits_->GetRawStorage());
+
+ if (bit_index_ >= bit_size_) return -1;
+
+ uint32_t word_index = bit_index_ / 32;
+ uint32_t word = bit_storage_[word_index];
+ // Mask out any bits in the first word we've already considered.
+ word >>= bit_index_ & 0x1f;
+ if (word == 0) {
+ bit_index_ &= ~0x1f;
+ do {
+ word_index++;
+ if ((word_index * 32) >= bit_size_) {
+ bit_index_ = bit_size_;
+ return -1;
+ }
+ word = bit_storage_[word_index];
+ bit_index_ += 32;
+ } while (word == 0);
+ }
+ bit_index_ += CTZ(word) + 1;
+ return bit_index_ - 1;
+ }
+
+ static void* operator new(size_t size, ArenaAllocator* arena) {
+ return arena->NewMem(sizeof(ArenaBitVector::Iterator), true,
+ ArenaAllocator::kAllocGrowableBitMap);
+ };
+ static void operator delete(void* p) {}; // Nop.
+
+ private:
+ ArenaBitVector* const p_bits_;
+ uint32_t* const bit_storage_;
+ uint32_t bit_index_; // Current index (size in bits).
+ const uint32_t bit_size_; // Size of vector in bits.
+ };
+
+ ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits, bool expandable,
+ OatBitMapKind kind = kBitMapMisc);
+ ~ArenaBitVector() {};
+
+ static void* operator new( size_t size, ArenaAllocator* arena) {
+ return arena->NewMem(sizeof(ArenaBitVector), true, ArenaAllocator::kAllocGrowableBitMap);
+ }
+ static void operator delete(void* p) {}; // Nop.
+
+ void SetBit(unsigned int num);
+ void ClearBit(unsigned int num);
+ void MarkAllBits(bool set);
+ void DebugBitVector(char* msg, int length);
+ bool IsBitSet(unsigned int num);
+ void ClearAllBits();
+ void SetInitialBits(unsigned int num_bits);
+ void Copy(ArenaBitVector* src);
+ void Intersect(const ArenaBitVector* src2);
+ void Union(const ArenaBitVector* src);
+ // Are we equal to another bit vector? Note: expandability attributes must also match.
+ bool Equal(const ArenaBitVector* src) {
+ return (storage_size_ == src->GetStorageSize()) &&
+ (expandable_ == src->IsExpandable()) &&
+ (memcmp(storage_, src->GetRawStorage(), storage_size_ * 4) == 0);
+ }
+ int NumSetBits();
+
+ uint32_t GetStorageSize() const { return storage_size_; }
+ bool IsExpandable() const { return expandable_; }
+ uint32_t GetRawStorageWord(size_t idx) const { return storage_[idx]; }
+ uint32_t* GetRawStorage() { return storage_; }
+ const uint32_t* GetRawStorage() const { return storage_; }
+
+ private:
+ ArenaAllocator* const arena_;
+ const bool expandable_; // expand bitmap if we run out?
+ const OatBitMapKind kind_; // for memory use tuning.
+ uint32_t storage_size_; // current size, in 32-bit words.
+ uint32_t* storage_;
+};
+
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_COMPILER_ARENA_BIT_VECTOR_H_
diff --git a/compiler/dex/backend.h b/compiler/dex/backend.h
new file mode 100644
index 0000000..45a1531
--- /dev/null
+++ b/compiler/dex/backend.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_BACKEND_H_
+#define ART_SRC_COMPILER_DEX_BACKEND_H_
+
+#include "compiled_method.h"
+#include "arena_allocator.h"
+
+namespace art {
+
+class Backend {
+
+ public:
+ virtual ~Backend() {};
+ virtual void Materialize() = 0;
+ virtual CompiledMethod* GetCompiledMethod() = 0;
+
+ protected:
+ Backend(ArenaAllocator* arena) : arena_(arena) {};
+ ArenaAllocator* const arena_;
+
+}; // Class Backend
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_BACKEND_H_
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
new file mode 100644
index 0000000..bc456b2
--- /dev/null
+++ b/compiler/dex/compiler_enums.h
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_COMPILERENUMS_H_
+#define ART_SRC_COMPILER_DEX_COMPILERENUMS_H_
+
+#include "dex_instruction.h"
+
+namespace art {
+
+enum RegisterClass {
+ kCoreReg,
+ kFPReg,
+ kAnyReg,
+};
+
+enum SpecialTargetRegister {
+ kSelf, // Thread pointer.
+ kSuspend, // Used to reduce suspend checks for some targets.
+ kLr,
+ kPc,
+ kSp,
+ kArg0,
+ kArg1,
+ kArg2,
+ kArg3,
+ kFArg0,
+ kFArg1,
+ kFArg2,
+ kFArg3,
+ kRet0,
+ kRet1,
+ kInvokeTgt,
+ kCount
+};
+
+enum RegLocationType {
+ kLocDalvikFrame = 0, // Normal Dalvik register
+ kLocPhysReg,
+ kLocCompilerTemp,
+ kLocInvalid
+};
+
+enum BBType {
+ kEntryBlock,
+ kDalvikByteCode,
+ kExitBlock,
+ kExceptionHandling,
+ kDead,
+};
+
+/*
+ * Def/Use encoding in 64-bit use_mask/def_mask. Low positions used for target-specific
+ * registers (and typically use the register number as the position). High positions
+ * reserved for common and abstract resources.
+ */
+
+enum ResourceEncodingPos {
+ kMustNotAlias = 63,
+ kHeapRef = 62, // Default memory reference type.
+ kLiteral = 61, // Literal pool memory reference.
+ kDalvikReg = 60, // Dalvik v_reg memory reference.
+ kFPStatus = 59,
+ kCCode = 58,
+ kLowestCommonResource = kCCode
+};
+
+// Shared pseudo opcodes - must be < 0.
+enum LIRPseudoOpcode {
+ kPseudoExportedPC = -16,
+ kPseudoSafepointPC = -15,
+ kPseudoIntrinsicRetry = -14,
+ kPseudoSuspendTarget = -13,
+ kPseudoThrowTarget = -12,
+ kPseudoCaseLabel = -11,
+ kPseudoMethodEntry = -10,
+ kPseudoMethodExit = -9,
+ kPseudoBarrier = -8,
+ kPseudoEntryBlock = -7,
+ kPseudoExitBlock = -6,
+ kPseudoTargetLabel = -5,
+ kPseudoDalvikByteCodeBoundary = -4,
+ kPseudoPseudoAlign4 = -3,
+ kPseudoEHBlockLabel = -2,
+ kPseudoNormalBlockLabel = -1,
+};
+
+enum ExtendedMIROpcode {
+ kMirOpFirst = kNumPackedOpcodes,
+ kMirOpPhi = kMirOpFirst,
+ kMirOpCopy,
+ kMirOpFusedCmplFloat,
+ kMirOpFusedCmpgFloat,
+ kMirOpFusedCmplDouble,
+ kMirOpFusedCmpgDouble,
+ kMirOpFusedCmpLong,
+ kMirOpNop,
+ kMirOpNullCheck,
+ kMirOpRangeCheck,
+ kMirOpDivZeroCheck,
+ kMirOpCheck,
+ kMirOpCheckPart2,
+ kMirOpSelect,
+ kMirOpLast,
+};
+
+enum MIROptimizationFlagPositons {
+ kMIRIgnoreNullCheck = 0,
+ kMIRNullCheckOnly,
+ kMIRIgnoreRangeCheck,
+ kMIRRangeCheckOnly,
+ kMIRInlined, // Invoke is inlined (ie dead).
+ kMIRInlinedPred, // Invoke is inlined via prediction.
+ kMIRCallee, // Instruction is inlined from callee.
+ kMIRIgnoreSuspendCheck,
+ kMIRDup,
+ kMIRMark, // Temporary node mark.
+};
+
+// For successor_block_list.
+enum BlockListType {
+ kNotUsed = 0,
+ kCatch,
+ kPackedSwitch,
+ kSparseSwitch,
+};
+
+enum AssemblerStatus {
+ kSuccess,
+ kRetryAll,
+};
+
+enum OpSize {
+ kWord,
+ kLong,
+ kSingle,
+ kDouble,
+ kUnsignedHalf,
+ kSignedHalf,
+ kUnsignedByte,
+ kSignedByte,
+};
+
+std::ostream& operator<<(std::ostream& os, const OpSize& kind);
+
+enum OpKind {
+ kOpMov,
+ kOpMvn,
+ kOpCmp,
+ kOpLsl,
+ kOpLsr,
+ kOpAsr,
+ kOpRor,
+ kOpNot,
+ kOpAnd,
+ kOpOr,
+ kOpXor,
+ kOpNeg,
+ kOpAdd,
+ kOpAdc,
+ kOpSub,
+ kOpSbc,
+ kOpRsub,
+ kOpMul,
+ kOpDiv,
+ kOpRem,
+ kOpBic,
+ kOpCmn,
+ kOpTst,
+ kOpBkpt,
+ kOpBlx,
+ kOpPush,
+ kOpPop,
+ kOp2Char,
+ kOp2Short,
+ kOp2Byte,
+ kOpCondBr,
+ kOpUncondBr,
+ kOpBx,
+ kOpInvalid,
+};
+
+std::ostream& operator<<(std::ostream& os, const OpKind& kind);
+
+enum ConditionCode {
+ kCondEq, // equal
+ kCondNe, // not equal
+ kCondCs, // carry set (unsigned less than)
+ kCondUlt = kCondCs,
+ kCondCc, // carry clear (unsigned greater than or same)
+ kCondUge = kCondCc,
+ kCondMi, // minus
+ kCondPl, // plus, positive or zero
+ kCondVs, // overflow
+ kCondVc, // no overflow
+ kCondHi, // unsigned greater than
+ kCondLs, // unsigned lower or same
+ kCondGe, // signed greater than or equal
+ kCondLt, // signed less than
+ kCondGt, // signed greater than
+ kCondLe, // signed less than or equal
+ kCondAl, // always
+ kCondNv, // never
+};
+
+std::ostream& operator<<(std::ostream& os, const ConditionCode& kind);
+
+// Target specific condition encodings
+enum ArmConditionCode {
+ kArmCondEq = 0x0, // 0000
+ kArmCondNe = 0x1, // 0001
+ kArmCondCs = 0x2, // 0010
+ kArmCondCc = 0x3, // 0011
+ kArmCondMi = 0x4, // 0100
+ kArmCondPl = 0x5, // 0101
+ kArmCondVs = 0x6, // 0110
+ kArmCondVc = 0x7, // 0111
+ kArmCondHi = 0x8, // 1000
+ kArmCondLs = 0x9, // 1001
+ kArmCondGe = 0xa, // 1010
+ kArmCondLt = 0xb, // 1011
+ kArmCondGt = 0xc, // 1100
+ kArmCondLe = 0xd, // 1101
+ kArmCondAl = 0xe, // 1110
+ kArmCondNv = 0xf, // 1111
+};
+
+std::ostream& operator<<(std::ostream& os, const ArmConditionCode& kind);
+
+enum X86ConditionCode {
+ kX86CondO = 0x0, // overflow
+ kX86CondNo = 0x1, // not overflow
+
+ kX86CondB = 0x2, // below
+ kX86CondNae = kX86CondB, // not-above-equal
+ kX86CondC = kX86CondB, // carry
+
+ kX86CondNb = 0x3, // not-below
+ kX86CondAe = kX86CondNb, // above-equal
+ kX86CondNc = kX86CondNb, // not-carry
+
+ kX86CondZ = 0x4, // zero
+ kX86CondEq = kX86CondZ, // equal
+
+ kX86CondNz = 0x5, // not-zero
+ kX86CondNe = kX86CondNz, // not-equal
+
+ kX86CondBe = 0x6, // below-equal
+ kX86CondNa = kX86CondBe, // not-above
+
+ kX86CondNbe = 0x7, // not-below-equal
+ kX86CondA = kX86CondNbe,// above
+
+ kX86CondS = 0x8, // sign
+ kX86CondNs = 0x9, // not-sign
+
+ kX86CondP = 0xa, // 8-bit parity even
+ kX86CondPE = kX86CondP,
+
+ kX86CondNp = 0xb, // 8-bit parity odd
+ kX86CondPo = kX86CondNp,
+
+ kX86CondL = 0xc, // less-than
+ kX86CondNge = kX86CondL, // not-greater-equal
+
+ kX86CondNl = 0xd, // not-less-than
+ kX86CondGe = kX86CondNl, // not-greater-equal
+
+ kX86CondLe = 0xe, // less-than-equal
+ kX86CondNg = kX86CondLe, // not-greater
+
+ kX86CondNle = 0xf, // not-less-than
+ kX86CondG = kX86CondNle,// greater
+};
+
+std::ostream& operator<<(std::ostream& os, const X86ConditionCode& kind);
+
+enum ThrowKind {
+ kThrowNullPointer,
+ kThrowDivZero,
+ kThrowArrayBounds,
+ kThrowConstantArrayBounds,
+ kThrowNoSuchMethod,
+ kThrowStackOverflow,
+};
+
+enum SpecialCaseHandler {
+ kNoHandler,
+ kNullMethod,
+ kConstFunction,
+ kIGet,
+ kIGetBoolean,
+ kIGetObject,
+ kIGetByte,
+ kIGetChar,
+ kIGetShort,
+ kIGetWide,
+ kIPut,
+ kIPutBoolean,
+ kIPutObject,
+ kIPutByte,
+ kIPutChar,
+ kIPutShort,
+ kIPutWide,
+ kIdentity,
+};
+
+enum DividePattern {
+ DivideNone,
+ Divide3,
+ Divide5,
+ Divide7,
+};
+
+std::ostream& operator<<(std::ostream& os, const DividePattern& pattern);
+
+// Memory barrier types (see "The JSR-133 Cookbook for Compiler Writers").
+enum MemBarrierKind {
+ kLoadStore,
+ kLoadLoad,
+ kStoreStore,
+ kStoreLoad
+};
+
+std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind);
+
+enum OpFeatureFlags {
+ kIsBranch = 0,
+ kNoOperand,
+ kIsUnaryOp,
+ kIsBinaryOp,
+ kIsTertiaryOp,
+ kIsQuadOp,
+ kIsQuinOp,
+ kIsSextupleOp,
+ kIsIT,
+ kMemLoad,
+ kMemStore,
+ kPCRelFixup, // x86 FIXME: add NEEDS_FIXUP to instruction attributes.
+ kRegDef0,
+ kRegDef1,
+ kRegDefA,
+ kRegDefD,
+ kRegDefFPCSList0,
+ kRegDefFPCSList2,
+ kRegDefList0,
+ kRegDefList1,
+ kRegDefList2,
+ kRegDefLR,
+ kRegDefSP,
+ kRegUse0,
+ kRegUse1,
+ kRegUse2,
+ kRegUse3,
+ kRegUse4,
+ kRegUseA,
+ kRegUseC,
+ kRegUseD,
+ kRegUseFPCSList0,
+ kRegUseFPCSList2,
+ kRegUseList0,
+ kRegUseList1,
+ kRegUseLR,
+ kRegUsePC,
+ kRegUseSP,
+ kSetsCCodes,
+ kUsesCCodes
+};
+
+enum SelectInstructionKind {
+ kSelectNone,
+ kSelectConst,
+ kSelectMove,
+ kSelectGoto
+};
+
+std::ostream& operator<<(std::ostream& os, const SelectInstructionKind& kind);
+
+// Type of growable bitmap for memory tuning.
+enum OatBitMapKind {
+ kBitMapMisc = 0,
+ kBitMapUse,
+ kBitMapDef,
+ kBitMapLiveIn,
+ kBitMapBMatrix,
+ kBitMapDominators,
+ kBitMapIDominated,
+ kBitMapDomFrontier,
+ kBitMapPhi,
+ kBitMapTmpBlocks,
+ kBitMapInputBlocks,
+ kBitMapRegisterV,
+ kBitMapTempSSARegisterV,
+ kBitMapNullCheck,
+ kBitMapTmpBlockV,
+ kBitMapPredecessors,
+ kNumBitMapKinds
+};
+
+std::ostream& operator<<(std::ostream& os, const OatBitMapKind& kind);
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_COMPILERENUMS_H_
diff --git a/compiler/dex/compiler_internals.h b/compiler/dex/compiler_internals.h
new file mode 100644
index 0000000..a3fa25e
--- /dev/null
+++ b/compiler/dex/compiler_internals.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_COMPILER_INTERNAL_H_
+#define ART_SRC_COMPILER_DEX_COMPILER_INTERNAL_H_
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "base/logging.h"
+#include "class_linker.h"
+#include "driver/compiler_driver.h"
+#include "quick/mir_to_lir.h"
+#include "mir_graph.h"
+#include "compiler_ir.h"
+#include "frontend.h"
+#include "monitor.h"
+#include "thread.h"
+#include "utils.h"
+
+#endif // ART_SRC_COMPILER_DEX_COMPILER_INTERNAL_H_
diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h
new file mode 100644
index 0000000..c6f99f3
--- /dev/null
+++ b/compiler/dex/compiler_ir.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_COMPILER_IR_H_
+#define ART_SRC_COMPILER_DEX_COMPILER_IR_H_
+
+#include <vector>
+#include <llvm/IR/Module.h>
+#include "arena_allocator.h"
+#include "backend.h"
+#include "compiler_enums.h"
+#include "dex/quick/mir_to_lir.h"
+#include "dex_instruction.h"
+#include "driver/compiler_driver.h"
+#include "driver/dex_compilation_unit.h"
+#include "llvm/intrinsic_helper.h"
+#include "llvm/ir_builder.h"
+#include "safe_map.h"
+
+namespace art {
+
+class LLVMInfo;
+namespace llvm {
+class LlvmCompilationUnit;
+} // namespace llvm
+
+struct ArenaMemBlock;
+struct Memstats;
+class MIRGraph;
+class Mir2Lir;
+
+struct CompilationUnit {
+ CompilationUnit()
+ : compiler_driver(NULL),
+ class_linker(NULL),
+ dex_file(NULL),
+ class_loader(NULL),
+ class_def_idx(0),
+ method_idx(0),
+ code_item(NULL),
+ access_flags(0),
+ invoke_type(kDirect),
+ shorty(NULL),
+ disable_opt(0),
+ enable_debug(0),
+ verbose(false),
+ compiler_backend(kNoBackend),
+ instruction_set(kNone),
+ num_dalvik_registers(0),
+ insns(NULL),
+ num_ins(0),
+ num_outs(0),
+ num_regs(0),
+ num_compiler_temps(0),
+ compiler_flip_match(false),
+ mir_graph(NULL),
+ cg(NULL) {}
+ /*
+ * Fields needed/generated by common frontend and generally used throughout
+ * the compiler.
+ */
+ CompilerDriver* compiler_driver;
+ ClassLinker* class_linker; // Linker to resolve fields and methods.
+ const DexFile* dex_file; // DexFile containing the method being compiled.
+ jobject class_loader; // compiling method's class loader.
+ uint32_t class_def_idx; // compiling method's defining class definition index.
+ uint32_t method_idx; // compiling method's index into method_ids of DexFile.
+ const DexFile::CodeItem* code_item; // compiling method's DexFile code_item.
+ uint32_t access_flags; // compiling method's access flags.
+ InvokeType invoke_type; // compiling method's invocation type.
+ const char* shorty; // compiling method's shorty.
+ uint32_t disable_opt; // opt_control_vector flags.
+ uint32_t enable_debug; // debugControlVector flags.
+ bool verbose;
+ CompilerBackend compiler_backend;
+ InstructionSet instruction_set;
+
+ // TODO: much of this info available elsewhere. Go to the original source?
+ int num_dalvik_registers; // method->registers_size.
+ const uint16_t* insns;
+ int num_ins;
+ int num_outs;
+ int num_regs; // Unlike num_dalvik_registers, does not include ins.
+
+ // TODO: may want to move this to MIRGraph.
+ int num_compiler_temps;
+
+ // If non-empty, apply optimizer/debug flags only to matching methods.
+ std::string compiler_method_match;
+ // Flips sense of compiler_method_match - apply flags if doesn't match.
+ bool compiler_flip_match;
+
+ // TODO: move memory management to mir_graph, or just switch to using standard containers.
+ ArenaAllocator arena;
+
+ UniquePtr<MIRGraph> mir_graph; // MIR container.
+ UniquePtr<Backend> cg; // Target-specific codegen.
+};
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_COMPILER_IR_H_
diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h
new file mode 100644
index 0000000..b20004d
--- /dev/null
+++ b/compiler/dex/dataflow_iterator-inl.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_
+#define ART_SRC_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_
+
+#include "dataflow_iterator.h"
+
+namespace art {
+
+inline BasicBlock* DataflowIterator::NextBody(bool had_change) {
+ changed_ |= had_change;
+ BasicBlock* res = NULL;
+ if (reverse_) {
+ if (is_iterative_ && changed_ && (idx_ < 0)) {
+ idx_ = start_idx_;
+ changed_ = false;
+ }
+ if (idx_ >= 0) {
+ int bb_id = block_id_list_->Get(idx_--);
+ res = mir_graph_->GetBasicBlock(bb_id);
+ }
+ } else {
+ if (is_iterative_ && changed_ && (idx_ >= end_idx_)) {
+ idx_ = start_idx_;
+ changed_ = false;
+ }
+ if (idx_ < end_idx_) {
+ int bb_id = block_id_list_->Get(idx_++);
+ res = mir_graph_->GetBasicBlock(bb_id);
+ }
+ }
+ return res;
+}
+
+// AllNodes uses the existing GrowableArray iterator, so use different NextBody().
+inline BasicBlock* AllNodesIterator::NextBody(bool had_change) {
+ changed_ |= had_change;
+ BasicBlock* res = NULL;
+ bool keep_looking = true;
+ while (keep_looking) {
+ res = all_nodes_iterator_->Next();
+ if (is_iterative_ && changed_ && (res == NULL)) {
+ all_nodes_iterator_->Reset();
+ changed_ = false;
+ } else if ((res == NULL) || (!res->hidden)) {
+ keep_looking = false;
+ }
+ }
+ return res;
+}
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_
diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h
new file mode 100644
index 0000000..12cbf9c
--- /dev/null
+++ b/compiler/dex/dataflow_iterator.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_DATAFLOW_ITERATOR_H_
+#define ART_SRC_COMPILER_DEX_DATAFLOW_ITERATOR_H_
+
+#include "compiler_ir.h"
+#include "mir_graph.h"
+
+namespace art {
+
+ /*
+ * This class supports iterating over lists of basic blocks in various
+ * interesting orders. Note that for efficiency, the visit orders have been pre-computed.
+ * The order itself will not change during the iteration. However, for some uses,
+ * auxiliary data associated with the basic blocks may be changed during the iteration,
+ * necessitating another pass over the list.
+ *
+ * To support this usage, we have is_iterative_. If false, the iteration is a one-shot
+ * pass through the pre-computed list using Next(). If true, the caller must tell the
+ * iterator whether a change has been made that necessitates another pass. Use
+ * Next(had_change) for this. The general idea is that the iterative_ use case means
+ * that the iterator will keep repeating the full basic block list until a complete pass
+ * is made through it with no changes. Note that calling Next(true) does not affect
+ * the iteration order or short-curcuit the current pass - it simply tells the iterator
+ * that once it has finished walking through the block list it should reset and do another
+ * full pass through the list.
+ */
+ class DataflowIterator {
+ public:
+
+ virtual ~DataflowIterator(){}
+
+ // Return the next BasicBlock* to visit.
+ BasicBlock* Next() {
+ DCHECK(!is_iterative_);
+ return NextBody(false);
+ }
+
+ /*
+ * Return the next BasicBlock* to visit, and tell the iterator whether any change
+ * has occurred that requires another full pass over the block list.
+ */
+ BasicBlock* Next(bool had_change) {
+ DCHECK(is_iterative_);
+ return NextBody(had_change);
+ }
+
+ protected:
+ DataflowIterator(MIRGraph* mir_graph, bool is_iterative, int start_idx, int end_idx,
+ bool reverse)
+ : mir_graph_(mir_graph),
+ is_iterative_(is_iterative),
+ start_idx_(start_idx),
+ end_idx_(end_idx),
+ reverse_(reverse),
+ block_id_list_(NULL),
+ idx_(0),
+ changed_(false) {}
+
+ virtual BasicBlock* NextBody(bool had_change) ALWAYS_INLINE;
+
+ MIRGraph* const mir_graph_;
+ const bool is_iterative_;
+ const int start_idx_;
+ const int end_idx_;
+ const bool reverse_;
+ GrowableArray<int>* block_id_list_;
+ int idx_;
+ bool changed_;
+
+ }; // DataflowIterator
+
+ class ReachableNodesIterator : public DataflowIterator {
+ public:
+ ReachableNodesIterator(MIRGraph* mir_graph, bool is_iterative)
+ : DataflowIterator(mir_graph, is_iterative, 0,
+ mir_graph->GetNumReachableBlocks(), false) {
+ idx_ = start_idx_;
+ block_id_list_ = mir_graph->GetDfsOrder();
+ }
+ };
+
+ class PreOrderDfsIterator : public DataflowIterator {
+ public:
+ PreOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative)
+ : DataflowIterator(mir_graph, is_iterative, 0,
+ mir_graph->GetNumReachableBlocks(), false) {
+ idx_ = start_idx_;
+ block_id_list_ = mir_graph->GetDfsOrder();
+ }
+ };
+
+ class PostOrderDfsIterator : public DataflowIterator {
+ public:
+
+ PostOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative)
+ : DataflowIterator(mir_graph, is_iterative, 0,
+ mir_graph->GetNumReachableBlocks(), false) {
+ idx_ = start_idx_;
+ block_id_list_ = mir_graph->GetDfsPostOrder();
+ }
+ };
+
+ class ReversePostOrderDfsIterator : public DataflowIterator {
+ public:
+ ReversePostOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative)
+ : DataflowIterator(mir_graph, is_iterative,
+ mir_graph->GetNumReachableBlocks() -1, 0, true) {
+ idx_ = start_idx_;
+ block_id_list_ = mir_graph->GetDfsPostOrder();
+ }
+ };
+
+ class PostOrderDOMIterator : public DataflowIterator {
+ public:
+ PostOrderDOMIterator(MIRGraph* mir_graph, bool is_iterative)
+ : DataflowIterator(mir_graph, is_iterative, 0,
+ mir_graph->GetNumReachableBlocks(), false) {
+ idx_ = start_idx_;
+ block_id_list_ = mir_graph->GetDomPostOrder();
+ }
+ };
+
+ class AllNodesIterator : public DataflowIterator {
+ public:
+ AllNodesIterator(MIRGraph* mir_graph, bool is_iterative)
+ : DataflowIterator(mir_graph, is_iterative, 0, 0, false) {
+ all_nodes_iterator_ =
+ new (mir_graph->GetArena()) GrowableArray<BasicBlock*>::Iterator (mir_graph->GetBlockList());
+ }
+
+ void Reset() {
+ all_nodes_iterator_->Reset();
+ }
+
+ BasicBlock* NextBody(bool had_change) ALWAYS_INLINE;
+
+ private:
+ GrowableArray<BasicBlock*>::Iterator* all_nodes_iterator_;
+ };
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_DATAFLOW_ITERATOR_H_
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
new file mode 100644
index 0000000..ee68a5d
--- /dev/null
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2011 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 "base/logging.h"
+#include "base/mutex.h"
+#include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
+#include "driver/compiler_driver.h"
+#include "driver/dex_compilation_unit.h"
+#include "mirror/abstract_method-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/dex_cache.h"
+#include "mirror/field-inl.h"
+
+namespace art {
+namespace optimizer {
+
+// Controls quickening activation.
+const bool kEnableQuickening = true;
+// Controls logging.
+const bool kEnableLogging = false;
+
+class DexCompiler {
+ public:
+ DexCompiler(art::CompilerDriver& compiler,
+ const DexCompilationUnit& unit)
+ : driver_(compiler),
+ unit_(unit) {};
+
+ ~DexCompiler() {};
+
+ void Compile();
+
+ private:
+ const DexFile& GetDexFile() const {
+ return *unit_.GetDexFile();
+ }
+
+ // TODO: since the whole compilation pipeline uses a "const DexFile", we need
+ // to "unconst" here. The DEX-to-DEX compiler should work on a non-const DexFile.
+ DexFile& GetModifiableDexFile() {
+ return *const_cast<DexFile*>(unit_.GetDexFile());
+ }
+
+ // Compiles a RETURN-VOID into a RETURN-VOID-BARRIER within a constructor where
+ // a barrier is required.
+ void CompileReturnVoid(Instruction* inst, uint32_t dex_pc);
+
+ // Compiles a field access into a quick field access.
+ // The field index is replaced by an offset within an Object where we can read
+ // from / write to this field. Therefore, this does not involve any resolution
+ // at runtime.
+ // Since the field index is encoded with 16 bits, we can replace it only if the
+ // field offset can be encoded with 16 bits too.
+ void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc,
+ Instruction::Code new_opcode, bool is_put);
+
+ // Compiles a virtual method invocation into a quick virtual method invocation.
+ // The method index is replaced by the vtable index where the corresponding
+ // AbstractMethod can be found. Therefore, this does not involve any resolution
+ // at runtime.
+ // Since the method index is encoded with 16 bits, we can replace it only if the
+ // vtable index can be encoded with 16 bits too.
+ void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
+ Instruction::Code new_opcode, bool is_range);
+
+ CompilerDriver& driver_;
+ const DexCompilationUnit& unit_;
+
+ DISALLOW_COPY_AND_ASSIGN(DexCompiler);
+};
+
+// Ensures write access to a part of DEX file.
+//
+// If a DEX file is read-only, it modifies its protection (mprotect) so it allows
+// write access to the part of DEX file defined by an address and a length.
+// In this case, it also takes the DexFile::modification_lock to prevent from
+// concurrent protection modification from a parallel DEX-to-DEX compilation on
+// the same DEX file.
+// When the instance is destroyed, it recovers original protection and releases
+// the lock.
+// TODO: as this scoped class is similar to a MutexLock we should use annotalysis
+// to capture the locking behavior.
+class ScopedDexWriteAccess {
+ public:
+ ScopedDexWriteAccess(DexFile& dex_file, Instruction* inst,
+ size_t length)
+ : dex_file_(dex_file),
+ address_(reinterpret_cast<uint8_t*>(inst)),
+ length_(length),
+ is_read_only_(dex_file_.IsReadOnly()) {
+ if (is_read_only_) {
+ // We need to enable DEX write access. To avoid concurrent DEX write access
+ // modification, we take the DexFile::modification_lock before.
+ dex_file_.GetModificationLock().ExclusiveLock(Thread::Current());
+ bool success = dex_file_.EnableWrite(address_, length_);
+ DCHECK(success) << "Failed to enable DEX write access";
+ }
+ }
+
+ ~ScopedDexWriteAccess() {
+ DCHECK_EQ(is_read_only_, dex_file_.IsReadOnly());
+ if (is_read_only_) {
+ bool success = dex_file_.DisableWrite(address_, length_);
+ DCHECK(success) << "Failed to disable DEX write access";
+ // Now we recovered original read-only protection, we can release the
+ // DexFile::modification_lock.
+ dex_file_.GetModificationLock().ExclusiveUnlock(Thread::Current());
+ }
+ }
+
+ private:
+ DexFile& dex_file_;
+ // TODO: make address_ const.
+ uint8_t* address_;
+ const size_t length_;
+ const bool is_read_only_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDexWriteAccess);
+};
+
+void DexCompiler::Compile() {
+ const DexFile::CodeItem* code_item = unit_.GetCodeItem();
+ const uint16_t* insns = code_item->insns_;
+ const uint32_t insns_size = code_item->insns_size_in_code_units_;
+ Instruction* inst = const_cast<Instruction*>(Instruction::At(insns));
+
+ for (uint32_t dex_pc = 0; dex_pc < insns_size;
+ inst = const_cast<Instruction*>(inst->Next()), dex_pc = inst->GetDexPc(insns)) {
+ switch (inst->Opcode()) {
+ case Instruction::RETURN_VOID:
+ CompileReturnVoid(inst, dex_pc);
+ break;
+
+ case Instruction::IGET:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false);
+ break;
+
+ case Instruction::IGET_WIDE:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false);
+ break;
+
+ case Instruction::IGET_OBJECT:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false);
+ break;
+
+ case Instruction::IPUT:
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BYTE:
+ case Instruction::IPUT_CHAR:
+ case Instruction::IPUT_SHORT:
+ // These opcodes have the same implementation in interpreter so group
+ // them under IPUT_QUICK.
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true);
+ break;
+
+ case Instruction::IPUT_WIDE:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true);
+ break;
+
+ case Instruction::IPUT_OBJECT:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT_QUICK, true);
+ break;
+
+ case Instruction::INVOKE_VIRTUAL:
+ CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_QUICK, false);
+ break;
+
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true);
+ break;
+
+ default:
+ // Nothing to do.
+ break;
+ }
+ }
+}
+
+void DexCompiler::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) {
+ DCHECK(inst->Opcode() == Instruction::RETURN_VOID);
+ // Are we compiling a constructor ?
+ if ((unit_.GetAccessFlags() & kAccConstructor) == 0) {
+ return;
+ }
+ // Do we need a constructor barrier ?
+ if (!driver_.RequiresConstructorBarrier(Thread::Current(), unit_.GetDexFile(),
+ unit_.GetClassDefIndex())) {
+ return;
+ }
+ // Replace RETURN_VOID by RETURN_VOID_BARRIER.
+ if (kEnableLogging) {
+ LOG(INFO) << "Replacing " << Instruction::Name(inst->Opcode())
+ << " by " << Instruction::Name(Instruction::RETURN_VOID_BARRIER)
+ << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+ << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
+ }
+ ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 2u);
+ inst->SetOpcode(Instruction::RETURN_VOID_BARRIER);
+}
+
+void DexCompiler::CompileInstanceFieldAccess(Instruction* inst,
+ uint32_t dex_pc,
+ Instruction::Code new_opcode,
+ bool is_put) {
+ if (!kEnableQuickening) {
+ return;
+ }
+ uint32_t field_idx = inst->VRegC_22c();
+ int field_offset;
+ bool is_volatile;
+ bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset,
+ is_volatile, is_put);
+ if (fast_path && !is_volatile && IsUint(16, field_offset)) {
+ // TODO: use VLOG ?
+ if (kEnableLogging) {
+ LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode())
+ << " to " << Instruction::Name(new_opcode)
+ << " by replacing field index " << field_idx
+ << " by field offset " << field_offset
+ << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+ << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
+ }
+ // We are modifying 4 consecutive bytes.
+ ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
+ inst->SetOpcode(new_opcode);
+ // Replace field index by field offset.
+ inst->SetVRegC_22c(static_cast<uint16_t>(field_offset));
+ }
+}
+
+void DexCompiler::CompileInvokeVirtual(Instruction* inst,
+ uint32_t dex_pc,
+ Instruction::Code new_opcode,
+ bool is_range) {
+ if (!kEnableQuickening) {
+ return;
+ }
+ uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+ MethodReference target_method(&GetDexFile(), method_idx);
+ InvokeType invoke_type = kVirtual;
+ InvokeType original_invoke_type = invoke_type;
+ int vtable_idx;
+ uintptr_t direct_code;
+ uintptr_t direct_method;
+ bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, invoke_type,
+ target_method, vtable_idx,
+ direct_code, direct_method,
+ false);
+ // TODO: support devirtualization.
+ if (fast_path && original_invoke_type == invoke_type) {
+ if (vtable_idx >= 0 && IsUint(16, vtable_idx)) {
+ // TODO: use VLOG ?
+ if (kEnableLogging) {
+ LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode())
+ << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")"
+ << " to " << Instruction::Name(new_opcode)
+ << " by replacing method index " << method_idx
+ << " by vtable index " << vtable_idx
+ << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+ << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
+ }
+ // We are modifying 4 consecutive bytes.
+ ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
+ inst->SetOpcode(new_opcode);
+ // Replace method index by vtable index.
+ if (is_range) {
+ inst->SetVRegB_3rc(static_cast<uint16_t>(vtable_idx));
+ } else {
+ inst->SetVRegB_35c(static_cast<uint16_t>(vtable_idx));
+ }
+ }
+ }
+}
+
+} // namespace optimizer
+} // namespace art
+
+extern "C" art::CompiledMethod*
+ ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item,
+ uint32_t access_flags, art::InvokeType invoke_type,
+ uint32_t class_def_idx, uint32_t method_idx, jobject class_loader,
+ const art::DexFile& dex_file) {
+ art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(),
+ dex_file, code_item, class_def_idx, method_idx, access_flags);
+ art::optimizer::DexCompiler dex_compiler(compiler, unit);
+ dex_compiler.Compile();
+ return NULL;
+}
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
new file mode 100644
index 0000000..746d475
--- /dev/null
+++ b/compiler/dex/frontend.cc
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2011 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 <llvm/Support/Threading.h>
+
+#include "compiler_internals.h"
+#include "driver/compiler_driver.h"
+#include "dataflow_iterator-inl.h"
+#include "leb128.h"
+#include "mirror/object.h"
+#include "runtime.h"
+#include "backend.h"
+#include "base/logging.h"
+
+#if defined(ART_USE_PORTABLE_COMPILER)
+#include "dex/portable/mir_to_gbc.h"
+#include "llvm/llvm_compilation_unit.h"
+#endif
+
+namespace {
+#if !defined(ART_USE_PORTABLE_COMPILER)
+ pthread_once_t llvm_multi_init = PTHREAD_ONCE_INIT;
+#endif
+ void InitializeLLVMForQuick() {
+ ::llvm::llvm_start_multithreaded();
+ }
+}
+
+namespace art {
+namespace llvm {
+::llvm::Module* makeLLVMModuleContents(::llvm::Module* module);
+}
+
+LLVMInfo::LLVMInfo() {
+#if !defined(ART_USE_PORTABLE_COMPILER)
+ pthread_once(&llvm_multi_init, InitializeLLVMForQuick);
+#endif
+ // Create context, module, intrinsic helper & ir builder
+ llvm_context_.reset(new ::llvm::LLVMContext());
+ llvm_module_ = new ::llvm::Module("art", *llvm_context_);
+ ::llvm::StructType::create(*llvm_context_, "JavaObject");
+ art::llvm::makeLLVMModuleContents(llvm_module_);
+ intrinsic_helper_.reset( new art::llvm::IntrinsicHelper(*llvm_context_, *llvm_module_));
+ ir_builder_.reset(new art::llvm::IRBuilder(*llvm_context_, *llvm_module_, *intrinsic_helper_));
+}
+
+LLVMInfo::~LLVMInfo() {
+}
+
+extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& compiler) {
+ CHECK(compiler.GetCompilerContext() == NULL);
+ LLVMInfo* llvm_info = new LLVMInfo();
+ compiler.SetCompilerContext(llvm_info);
+}
+
+extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& compiler) {
+ delete reinterpret_cast<LLVMInfo*>(compiler.GetCompilerContext());
+ compiler.SetCompilerContext(NULL);
+}
+
+/* Default optimizer/debug setting for the compiler. */
+static uint32_t kCompilerOptimizerDisableFlags = 0 | // Disable specific optimizations
+ (1 << kLoadStoreElimination) |
+ //(1 << kLoadHoisting) |
+ //(1 << kSuppressLoads) |
+ //(1 << kNullCheckElimination) |
+ //(1 << kPromoteRegs) |
+ //(1 << kTrackLiveTemps) |
+ //(1 << kSafeOptimizations) |
+ //(1 << kBBOpt) |
+ //(1 << kMatch) |
+ //(1 << kPromoteCompilerTemps) |
+ 0;
+
+static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes
+ //(1 << kDebugDisplayMissingTargets) |
+ //(1 << kDebugVerbose) |
+ //(1 << kDebugDumpCFG) |
+ //(1 << kDebugSlowFieldPath) |
+ //(1 << kDebugSlowInvokePath) |
+ //(1 << kDebugSlowStringPath) |
+ //(1 << kDebugSlowestFieldPath) |
+ //(1 << kDebugSlowestStringPath) |
+ //(1 << kDebugExerciseResolveMethod) |
+ //(1 << kDebugVerifyDataflow) |
+ //(1 << kDebugShowMemoryUsage) |
+ //(1 << kDebugShowNops) |
+ //(1 << kDebugCountOpcodes) |
+ //(1 << kDebugDumpCheckStats) |
+ //(1 << kDebugDumpBitcodeFile) |
+ //(1 << kDebugVerifyBitcode) |
+ //(1 << kDebugShowSummaryMemoryUsage) |
+ 0;
+
+static CompiledMethod* CompileMethod(CompilerDriver& compiler,
+ const CompilerBackend compiler_backend,
+ const DexFile::CodeItem* code_item,
+ uint32_t access_flags, InvokeType invoke_type,
+ uint32_t class_def_idx, uint32_t method_idx,
+ jobject class_loader, const DexFile& dex_file
+#if defined(ART_USE_PORTABLE_COMPILER)
+ , llvm::LlvmCompilationUnit* llvm_compilation_unit
+#endif
+) {
+ VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "...";
+
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ UniquePtr<CompilationUnit> cu(new CompilationUnit);
+
+ cu->compiler_driver = &compiler;
+ cu->class_linker = class_linker;
+ cu->instruction_set = compiler.GetInstructionSet();
+ cu->compiler_backend = compiler_backend;
+ DCHECK((cu->instruction_set == kThumb2) ||
+ (cu->instruction_set == kX86) ||
+ (cu->instruction_set == kMips));
+
+
+ /* Adjust this value accordingly once inlining is performed */
+ cu->num_dalvik_registers = code_item->registers_size_;
+ // TODO: set this from command line
+ cu->compiler_flip_match = false;
+ bool use_match = !cu->compiler_method_match.empty();
+ bool match = use_match && (cu->compiler_flip_match ^
+ (PrettyMethod(method_idx, dex_file).find(cu->compiler_method_match) !=
+ std::string::npos));
+ if (!use_match || match) {
+ cu->disable_opt = kCompilerOptimizerDisableFlags;
+ cu->enable_debug = kCompilerDebugFlags;
+ cu->verbose = VLOG_IS_ON(compiler) ||
+ (cu->enable_debug & (1 << kDebugVerbose));
+ }
+
+ /*
+ * TODO: rework handling of optimization and debug flags. Should we split out
+ * MIR and backend flags? Need command-line setting as well.
+ */
+
+ if (compiler_backend == kPortable) {
+ // Fused long branches not currently usseful in bitcode.
+ cu->disable_opt |= (1 << kBranchFusing);
+ }
+
+ if (cu->instruction_set == kMips) {
+ // Disable some optimizations for mips for now
+ cu->disable_opt |= (
+ (1 << kLoadStoreElimination) |
+ (1 << kLoadHoisting) |
+ (1 << kSuppressLoads) |
+ (1 << kNullCheckElimination) |
+ (1 << kPromoteRegs) |
+ (1 << kTrackLiveTemps) |
+ (1 << kSafeOptimizations) |
+ (1 << kBBOpt) |
+ (1 << kMatch) |
+ (1 << kPromoteCompilerTemps));
+ }
+
+ cu->mir_graph.reset(new MIRGraph(cu.get(), &cu->arena));
+
+ /* Gathering opcode stats? */
+ if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
+ cu->mir_graph->EnableOpcodeCounting();
+ }
+
+ /* Build the raw MIR graph */
+ cu->mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx,
+ class_loader, dex_file);
+
+ /* Do a code layout pass */
+ cu->mir_graph->CodeLayout();
+
+ /* Perform SSA transformation for the whole method */
+ cu->mir_graph->SSATransformation();
+
+ /* Do constant propagation */
+ cu->mir_graph->PropagateConstants();
+
+ /* Count uses */
+ cu->mir_graph->MethodUseCount();
+
+ /* Perform null check elimination */
+ cu->mir_graph->NullCheckElimination();
+
+ /* Combine basic blocks where possible */
+ cu->mir_graph->BasicBlockCombine();
+
+ /* Do some basic block optimizations */
+ cu->mir_graph->BasicBlockOptimization();
+
+ if (cu->enable_debug & (1 << kDebugDumpCheckStats)) {
+ cu->mir_graph->DumpCheckStats();
+ }
+
+ if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
+ cu->mir_graph->ShowOpcodeStats();
+ }
+
+ /* Set up regLocation[] array to describe values - one for each ssa_name. */
+ cu->mir_graph->BuildRegLocations();
+
+ CompiledMethod* result = NULL;
+
+#if defined(ART_USE_PORTABLE_COMPILER)
+ if (compiler_backend == kPortable) {
+ cu->cg.reset(PortableCodeGenerator(cu.get(), cu->mir_graph.get(), &cu->arena,
+ llvm_compilation_unit));
+ } else
+#endif
+ {
+ switch (compiler.GetInstructionSet()) {
+ case kThumb2:
+ cu->cg.reset(ArmCodeGenerator(cu.get(), cu->mir_graph.get(), &cu->arena)); break;
+ case kMips:
+ cu->cg.reset(MipsCodeGenerator(cu.get(), cu->mir_graph.get(), &cu->arena)); break;
+ case kX86:
+ cu->cg.reset(X86CodeGenerator(cu.get(), cu->mir_graph.get(), &cu->arena)); break;
+ default:
+ LOG(FATAL) << "Unexpected instruction set: " << compiler.GetInstructionSet();
+ }
+ }
+
+ cu->cg->Materialize();
+
+ result = cu->cg->GetCompiledMethod();
+
+ if (result) {
+ VLOG(compiler) << "Compiled " << PrettyMethod(method_idx, dex_file);
+ } else {
+ VLOG(compiler) << "Deferred " << PrettyMethod(method_idx, dex_file);
+ }
+
+ if (cu->enable_debug & (1 << kDebugShowMemoryUsage)) {
+ if (cu->arena.BytesAllocated() > (5 * 1024 *1024)) {
+ MemStats mem_stats(cu->arena);
+ LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats);
+ }
+ }
+
+ if (cu->enable_debug & (1 << kDebugShowSummaryMemoryUsage)) {
+ LOG(INFO) << "MEMINFO " << cu->arena.BytesAllocated() << " " << cu->mir_graph->GetNumBlocks()
+ << " " << PrettyMethod(method_idx, dex_file);
+ }
+
+ return result;
+}
+
+CompiledMethod* CompileOneMethod(CompilerDriver& compiler,
+ const CompilerBackend backend,
+ const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint32_t class_def_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const DexFile& dex_file,
+ llvm::LlvmCompilationUnit* llvm_compilation_unit) {
+ return CompileMethod(compiler, backend, code_item, access_flags, invoke_type, class_def_idx,
+ method_idx, class_loader, dex_file
+#if defined(ART_USE_PORTABLE_COMPILER)
+ , llvm_compilation_unit
+#endif
+ );
+}
+
+} // namespace art
+
+extern "C" art::CompiledMethod*
+ ArtQuickCompileMethod(art::CompilerDriver& compiler,
+ const art::DexFile::CodeItem* code_item,
+ uint32_t access_flags, art::InvokeType invoke_type,
+ uint32_t class_def_idx, uint32_t method_idx, jobject class_loader,
+ const art::DexFile& dex_file) {
+ // TODO: check method fingerprint here to determine appropriate backend type. Until then, use build default
+ art::CompilerBackend backend = compiler.GetCompilerBackend();
+ return art::CompileOneMethod(compiler, backend, code_item, access_flags, invoke_type,
+ class_def_idx, method_idx, class_loader, dex_file,
+ NULL /* use thread llvm_info */);
+}
diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h
new file mode 100644
index 0000000..69d7f77
--- /dev/null
+++ b/compiler/dex/frontend.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_COMPILER_H_
+#define ART_SRC_COMPILER_DEX_COMPILER_H_
+
+#include "dex_file.h"
+#include "dex_instruction.h"
+
+
+
+
+
+
+namespace llvm {
+ class Module;
+ class LLVMContext;
+}
+
+namespace art {
+namespace llvm {
+ class IntrinsicHelper;
+ class IRBuilder;
+}
+
+/*
+ * Assembly is an iterative process, and usually terminates within
+ * two or three passes. This should be high enough to handle bizarre
+ * cases, but detect an infinite loop bug.
+ */
+#define MAX_ASSEMBLER_RETRIES 50
+
+// Suppress optimization if corresponding bit set.
+enum opt_control_vector {
+ kLoadStoreElimination = 0,
+ kLoadHoisting,
+ kSuppressLoads,
+ kNullCheckElimination,
+ kPromoteRegs,
+ kTrackLiveTemps,
+ kSafeOptimizations,
+ kBBOpt,
+ kMatch,
+ kPromoteCompilerTemps,
+ kBranchFusing,
+};
+
+// Force code generation paths for testing.
+enum debugControlVector {
+ kDebugVerbose,
+ kDebugDumpCFG,
+ kDebugSlowFieldPath,
+ kDebugSlowInvokePath,
+ kDebugSlowStringPath,
+ kDebugSlowTypePath,
+ kDebugSlowestFieldPath,
+ kDebugSlowestStringPath,
+ kDebugExerciseResolveMethod,
+ kDebugVerifyDataflow,
+ kDebugShowMemoryUsage,
+ kDebugShowNops,
+ kDebugCountOpcodes,
+ kDebugDumpCheckStats,
+ kDebugDumpBitcodeFile,
+ kDebugVerifyBitcode,
+ kDebugShowSummaryMemoryUsage,
+};
+
+class LLVMInfo {
+ public:
+ LLVMInfo();
+ ~LLVMInfo();
+
+ ::llvm::LLVMContext* GetLLVMContext() {
+ return llvm_context_.get();
+ }
+
+ ::llvm::Module* GetLLVMModule() {
+ return llvm_module_;
+ }
+
+ art::llvm::IntrinsicHelper* GetIntrinsicHelper() {
+ return intrinsic_helper_.get();
+ }
+
+ art::llvm::IRBuilder* GetIRBuilder() {
+ return ir_builder_.get();
+ }
+
+ private:
+ UniquePtr< ::llvm::LLVMContext> llvm_context_;
+ ::llvm::Module* llvm_module_; // Managed by context_.
+ UniquePtr<art::llvm::IntrinsicHelper> intrinsic_helper_;
+ UniquePtr<art::llvm::IRBuilder> ir_builder_;
+};
+
+struct CompilationUnit;
+struct BasicBlock;
+
+} // namespace art
+
+extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver,
+ const art::DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ art::InvokeType invoke_type,
+ uint32_t class_dex_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const art::DexFile& dex_file);
+
+
+
+#endif // ART_SRC_COMPILER_DEX_COMPILER_H_
diff --git a/compiler/dex/growable_array.h b/compiler/dex/growable_array.h
new file mode 100644
index 0000000..c4684a7
--- /dev/null
+++ b/compiler/dex/growable_array.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_GROWABLE_LIST_H_
+#define ART_SRC_COMPILER_DEX_GROWABLE_LIST_H_
+
+#include <stdint.h>
+#include <stddef.h>
+#include "compiler_enums.h"
+#include "arena_allocator.h"
+
+namespace art {
+
+struct CompilationUnit;
+
+// Type of growable list for memory tuning.
+enum OatListKind {
+ kGrowableArrayMisc = 0,
+ kGrowableArrayBlockList,
+ kGrowableArraySSAtoDalvikMap,
+ kGrowableArrayDfsOrder,
+ kGrowableArrayDfsPostOrder,
+ kGrowableArrayDomPostOrderTraversal,
+ kGrowableArrayThrowLaunchPads,
+ kGrowableArraySuspendLaunchPads,
+ kGrowableArraySwitchTables,
+ kGrowableArrayFillArrayData,
+ kGrowableArraySuccessorBlocks,
+ kGrowableArrayPredecessors,
+ kGNumListKinds
+};
+
+template<typename T>
+class GrowableArray {
+ public:
+
+ class Iterator {
+ public:
+ Iterator(GrowableArray* g_list)
+ : idx_(0),
+ g_list_(g_list) {};
+
+ // NOTE: returns 0/NULL when no next.
+ // TODO: redo to make usage consistent with other iterators.
+ T Next() {
+ if (idx_ >= g_list_->Size()) {
+ return 0;
+ } else {
+ return g_list_->Get(idx_++);
+ }
+ }
+
+ void Reset() {
+ idx_ = 0;
+ }
+
+ static void* operator new(size_t size, ArenaAllocator* arena) {
+ return arena->NewMem(sizeof(GrowableArray::Iterator), true, ArenaAllocator::kAllocGrowableArray);
+ };
+ static void operator delete(void* p) {}; // Nop.
+
+ private:
+ size_t idx_;
+ GrowableArray* const g_list_;
+ };
+
+ GrowableArray(ArenaAllocator* arena, size_t init_length, OatListKind kind = kGrowableArrayMisc)
+ : arena_(arena),
+ num_allocated_(init_length),
+ num_used_(0),
+ kind_(kind) {
+ elem_list_ = static_cast<T*>(arena_->NewMem(sizeof(T) * init_length, true,
+ ArenaAllocator::kAllocGrowableArray));
+ };
+
+
+ // Expand the list size to at least new length.
+ void Resize(size_t new_length) {
+ if (new_length <= num_allocated_) return;
+ // If it's a small list double the size, else grow 1.5x.
+ size_t target_length =
+ (num_allocated_ < 128) ? num_allocated_ << 1 : num_allocated_ + (num_allocated_ >> 1);
+ if (new_length > target_length) {
+ target_length = new_length;
+ }
+ T* new_array = static_cast<T*>(arena_->NewMem(sizeof(T) * target_length, true,
+ ArenaAllocator::kAllocGrowableArray));
+ memcpy(new_array, elem_list_, sizeof(T) * num_allocated_);
+ num_allocated_ = target_length;
+ elem_list_ = new_array;
+ };
+
+ // NOTE: does not return storage, just resets use count.
+ void Reset() {
+ num_used_ = 0;
+ }
+
+ // Insert an element to the end of a list, resizing if necessary.
+ void Insert(T elem) {
+ if (num_used_ == num_allocated_) {
+ Resize(num_used_ + 1);
+ }
+ elem_list_[num_used_++] = elem;
+ };
+
+ T Get(size_t index) const {
+ DCHECK_LT(index, num_used_);
+ return elem_list_[index];
+ };
+
+ // Overwrite existing element at position index. List must be large enough.
+ void Put(size_t index, T elem) {
+ DCHECK_LT(index, num_used_);
+ elem_list_[index] = elem;
+ }
+
+ void Increment(size_t index) {
+ DCHECK_LT(index, num_used_);
+ elem_list_[index]++;
+ }
+
+ void Delete(T element) {
+ bool found = false;
+ for (size_t i = 0; i < num_used_ - 1; i++) {
+ if (!found && elem_list_[i] == element) {
+ found = true;
+ }
+ if (found) {
+ elem_list_[i] = elem_list_[i+1];
+ }
+ }
+ // We should either have found the element, or it was the last (unscanned) element.
+ DCHECK(found || (element == elem_list_[num_used_ - 1]));
+ num_used_--;
+ };
+
+ size_t GetNumAllocated() const { return num_allocated_; }
+
+ size_t Size() const { return num_used_; }
+
+ T* GetRawStorage() const { return elem_list_; }
+
+ static void* operator new(size_t size, ArenaAllocator* arena) {
+ return arena->NewMem(sizeof(GrowableArray<T>), true, ArenaAllocator::kAllocGrowableArray);
+ };
+ static void operator delete(void* p) {}; // Nop.
+
+ private:
+ ArenaAllocator* const arena_;
+ size_t num_allocated_;
+ size_t num_used_;
+ OatListKind kind_;
+ T* elem_list_;
+};
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_GROWABLE_LIST_H_
diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc
new file mode 100644
index 0000000..ec5ab5d
--- /dev/null
+++ b/compiler/dex/local_value_numbering.cc
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2012 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 "local_value_numbering.h"
+
+namespace art {
+
+
+uint16_t LocalValueNumbering::GetValueNumber(MIR* mir)
+{
+ uint16_t res = NO_VALUE;
+ uint16_t opcode = mir->dalvikInsn.opcode;
+ switch (opcode) {
+ case Instruction::NOP:
+ case Instruction::RETURN_VOID:
+ case Instruction::RETURN:
+ case Instruction::RETURN_OBJECT:
+ case Instruction::RETURN_WIDE:
+ case Instruction::MONITOR_ENTER:
+ case Instruction::MONITOR_EXIT:
+ case Instruction::GOTO:
+ case Instruction::GOTO_16:
+ case Instruction::GOTO_32:
+ case Instruction::CHECK_CAST:
+ case Instruction::THROW:
+ case Instruction::FILL_ARRAY_DATA:
+ case Instruction::FILLED_NEW_ARRAY:
+ case Instruction::FILLED_NEW_ARRAY_RANGE:
+ case Instruction::PACKED_SWITCH:
+ case Instruction::SPARSE_SWITCH:
+ case Instruction::IF_EQ:
+ case Instruction::IF_NE:
+ case Instruction::IF_LT:
+ case Instruction::IF_GE:
+ case Instruction::IF_GT:
+ case Instruction::IF_LE:
+ case Instruction::IF_EQZ:
+ case Instruction::IF_NEZ:
+ case Instruction::IF_LTZ:
+ case Instruction::IF_GEZ:
+ case Instruction::IF_GTZ:
+ case Instruction::IF_LEZ:
+ case Instruction::INVOKE_STATIC_RANGE:
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_SUPER:
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ case kMirOpFusedCmplFloat:
+ case kMirOpFusedCmpgFloat:
+ case kMirOpFusedCmplDouble:
+ case kMirOpFusedCmpgDouble:
+ case kMirOpFusedCmpLong:
+ // Nothing defined - take no action.
+ break;
+
+ case Instruction::MOVE_EXCEPTION:
+ case Instruction::MOVE_RESULT:
+ case Instruction::MOVE_RESULT_OBJECT:
+ case Instruction::INSTANCE_OF:
+ case Instruction::NEW_INSTANCE:
+ case Instruction::CONST_STRING:
+ case Instruction::CONST_STRING_JUMBO:
+ case Instruction::CONST_CLASS:
+ case Instruction::NEW_ARRAY: {
+ // 1 result, treat as unique each time, use result s_reg - will be unique.
+ uint16_t res = GetOperandValue(mir->ssa_rep->defs[0]);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ break;
+ case Instruction::MOVE_RESULT_WIDE: {
+ // 1 wide result, treat as unique each time, use result s_reg - will be unique.
+ uint16_t res = GetOperandValueWide(mir->ssa_rep->defs[0]);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case kMirOpPhi:
+ /*
+ * Because we'll only see phi nodes at the beginning of an extended basic block,
+ * we can ignore them. Revisit if we shift to global value numbering.
+ */
+ break;
+
+ case Instruction::MOVE:
+ case Instruction::MOVE_OBJECT:
+ case Instruction::MOVE_16:
+ case Instruction::MOVE_OBJECT_16:
+ case Instruction::MOVE_FROM16:
+ case Instruction::MOVE_OBJECT_FROM16:
+ case kMirOpCopy: {
+ // Just copy value number of source to value number of resulit.
+ uint16_t res = GetOperandValue(mir->ssa_rep->uses[0]);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::MOVE_WIDE:
+ case Instruction::MOVE_WIDE_16:
+ case Instruction::MOVE_WIDE_FROM16: {
+ // Just copy value number of source to value number of result.
+ uint16_t res = GetOperandValueWide(mir->ssa_rep->uses[0]);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::CONST:
+ case Instruction::CONST_4:
+ case Instruction::CONST_16: {
+ uint16_t res = LookupValue(Instruction::CONST, Low16Bits(mir->dalvikInsn.vB),
+ High16Bits(mir->dalvikInsn.vB >> 16), 0);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::CONST_HIGH16: {
+ uint16_t res = LookupValue(Instruction::CONST, 0, mir->dalvikInsn.vB, 0);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::CONST_WIDE_16:
+ case Instruction::CONST_WIDE_32: {
+ uint16_t low_res = LookupValue(Instruction::CONST, Low16Bits(mir->dalvikInsn.vB),
+ High16Bits(mir->dalvikInsn.vB >> 16), 1);
+ uint16_t high_res;
+ if (mir->dalvikInsn.vB & 0x80000000) {
+ high_res = LookupValue(Instruction::CONST, 0xffff, 0xffff, 2);
+ } else {
+ high_res = LookupValue(Instruction::CONST, 0, 0, 2);
+ }
+ uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::CONST_WIDE: {
+ uint32_t low_word = Low32Bits(mir->dalvikInsn.vB_wide);
+ uint32_t high_word = High32Bits(mir->dalvikInsn.vB_wide);
+ uint16_t low_res = LookupValue(Instruction::CONST, Low16Bits(low_word),
+ High16Bits(low_word), 1);
+ uint16_t high_res = LookupValue(Instruction::CONST, Low16Bits(high_word),
+ High16Bits(high_word), 2);
+ uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::CONST_WIDE_HIGH16: {
+ uint16_t low_res = LookupValue(Instruction::CONST, 0, 0, 1);
+ uint16_t high_res = LookupValue(Instruction::CONST, 0, Low16Bits(mir->dalvikInsn.vB), 2);
+ uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::ARRAY_LENGTH:
+ case Instruction::NEG_INT:
+ case Instruction::NOT_INT:
+ case Instruction::NEG_FLOAT:
+ case Instruction::INT_TO_BYTE:
+ case Instruction::INT_TO_SHORT:
+ case Instruction::INT_TO_CHAR:
+ case Instruction::INT_TO_FLOAT:
+ case Instruction::FLOAT_TO_INT: {
+ // res = op + 1 operand
+ uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
+ uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::LONG_TO_FLOAT:
+ case Instruction::LONG_TO_INT:
+ case Instruction::DOUBLE_TO_FLOAT:
+ case Instruction::DOUBLE_TO_INT: {
+ // res = op + 1 wide operand
+ uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
+ uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+
+ case Instruction::DOUBLE_TO_LONG:
+ case Instruction::LONG_TO_DOUBLE:
+ case Instruction::NEG_LONG:
+ case Instruction::NOT_LONG:
+ case Instruction::NEG_DOUBLE: {
+ // wide res = op + 1 wide operand
+ uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
+ uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::FLOAT_TO_DOUBLE:
+ case Instruction::FLOAT_TO_LONG:
+ case Instruction::INT_TO_DOUBLE:
+ case Instruction::INT_TO_LONG: {
+ // wide res = op + 1 operand
+ uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
+ uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::CMPL_DOUBLE:
+ case Instruction::CMPG_DOUBLE:
+ case Instruction::CMP_LONG: {
+ // res = op + 2 wide operands
+ uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
+ uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]);
+ uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::CMPG_FLOAT:
+ case Instruction::CMPL_FLOAT:
+ case Instruction::ADD_INT:
+ case Instruction::ADD_INT_2ADDR:
+ case Instruction::MUL_INT:
+ case Instruction::MUL_INT_2ADDR:
+ case Instruction::AND_INT:
+ case Instruction::AND_INT_2ADDR:
+ case Instruction::OR_INT:
+ case Instruction::OR_INT_2ADDR:
+ case Instruction::XOR_INT:
+ case Instruction::XOR_INT_2ADDR:
+ case Instruction::SUB_INT:
+ case Instruction::SUB_INT_2ADDR:
+ case Instruction::DIV_INT:
+ case Instruction::DIV_INT_2ADDR:
+ case Instruction::REM_INT:
+ case Instruction::REM_INT_2ADDR:
+ case Instruction::SHL_INT:
+ case Instruction::SHL_INT_2ADDR:
+ case Instruction::SHR_INT:
+ case Instruction::SHR_INT_2ADDR:
+ case Instruction::USHR_INT:
+ case Instruction::USHR_INT_2ADDR: {
+ // res = op + 2 operands
+ uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
+ uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]);
+ uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::ADD_LONG:
+ case Instruction::SUB_LONG:
+ case Instruction::MUL_LONG:
+ case Instruction::DIV_LONG:
+ case Instruction::REM_LONG:
+ case Instruction::AND_LONG:
+ case Instruction::OR_LONG:
+ case Instruction::XOR_LONG:
+ case Instruction::ADD_LONG_2ADDR:
+ case Instruction::SUB_LONG_2ADDR:
+ case Instruction::MUL_LONG_2ADDR:
+ case Instruction::DIV_LONG_2ADDR:
+ case Instruction::REM_LONG_2ADDR:
+ case Instruction::AND_LONG_2ADDR:
+ case Instruction::OR_LONG_2ADDR:
+ case Instruction::XOR_LONG_2ADDR:
+ case Instruction::ADD_DOUBLE:
+ case Instruction::SUB_DOUBLE:
+ case Instruction::MUL_DOUBLE:
+ case Instruction::DIV_DOUBLE:
+ case Instruction::REM_DOUBLE:
+ case Instruction::ADD_DOUBLE_2ADDR:
+ case Instruction::SUB_DOUBLE_2ADDR:
+ case Instruction::MUL_DOUBLE_2ADDR:
+ case Instruction::DIV_DOUBLE_2ADDR:
+ case Instruction::REM_DOUBLE_2ADDR: {
+ // wide res = op + 2 wide operands
+ uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
+ uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]);
+ uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::SHL_LONG:
+ case Instruction::SHR_LONG:
+ case Instruction::USHR_LONG:
+ case Instruction::SHL_LONG_2ADDR:
+ case Instruction::SHR_LONG_2ADDR:
+ case Instruction::USHR_LONG_2ADDR: {
+ // wide res = op + 1 wide operand + 1 operand
+ uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
+ uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]);
+ uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::ADD_FLOAT:
+ case Instruction::SUB_FLOAT:
+ case Instruction::MUL_FLOAT:
+ case Instruction::DIV_FLOAT:
+ case Instruction::REM_FLOAT:
+ case Instruction::ADD_FLOAT_2ADDR:
+ case Instruction::SUB_FLOAT_2ADDR:
+ case Instruction::MUL_FLOAT_2ADDR:
+ case Instruction::DIV_FLOAT_2ADDR:
+ case Instruction::REM_FLOAT_2ADDR: {
+ // res = op + 2 operands
+ uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
+ uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]);
+ uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::RSUB_INT:
+ case Instruction::ADD_INT_LIT16:
+ case Instruction::MUL_INT_LIT16:
+ case Instruction::DIV_INT_LIT16:
+ case Instruction::REM_INT_LIT16:
+ case Instruction::AND_INT_LIT16:
+ case Instruction::OR_INT_LIT16:
+ case Instruction::XOR_INT_LIT16:
+ case Instruction::ADD_INT_LIT8:
+ case Instruction::RSUB_INT_LIT8:
+ case Instruction::MUL_INT_LIT8:
+ case Instruction::DIV_INT_LIT8:
+ case Instruction::REM_INT_LIT8:
+ case Instruction::AND_INT_LIT8:
+ case Instruction::OR_INT_LIT8:
+ case Instruction::XOR_INT_LIT8:
+ case Instruction::SHL_INT_LIT8:
+ case Instruction::SHR_INT_LIT8:
+ case Instruction::USHR_INT_LIT8: {
+ // Same as res = op + 2 operands, except use vB as operand 2
+ uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
+ uint16_t operand2 = LookupValue(Instruction::CONST, mir->dalvikInsn.vB, 0, 0);
+ uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ break;
+
+ case Instruction::AGET_WIDE:
+ case Instruction::AGET:
+ case Instruction::AGET_OBJECT:
+ case Instruction::AGET_BOOLEAN:
+ case Instruction::AGET_BYTE:
+ case Instruction::AGET_CHAR:
+ case Instruction::AGET_SHORT: {
+ uint16_t array = GetOperandValue(mir->ssa_rep->uses[0]);
+ if (null_checked_.find(array) != null_checked_.end()) {
+ if (cu_->verbose) {
+ LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
+ }
+ mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+ } else {
+ null_checked_.insert(array);
+ }
+ uint16_t index = GetOperandValue(mir->ssa_rep->uses[1]);
+ if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) {
+ if (cu_->verbose) {
+ LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
+ }
+ mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK;
+ }
+ mir->meta.throw_insn->optimization_flags |= mir->optimization_flags;
+ // Use side effect to note range check completed.
+ (void)LookupValue(ARRAY_REF, array, index, NO_VALUE);
+ // Establish value number for loaded register. Note use of memory version.
+ uint16_t memory_version = GetMemoryVersion(array, NO_VALUE);
+ uint16_t res = LookupValue(ARRAY_REF, array, index, memory_version);
+ if (opcode == Instruction::AGET_WIDE) {
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
+ } else {
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ }
+ break;
+
+ case Instruction::APUT_WIDE:
+ case Instruction::APUT:
+ case Instruction::APUT_OBJECT:
+ case Instruction::APUT_SHORT:
+ case Instruction::APUT_CHAR:
+ case Instruction::APUT_BYTE:
+ case Instruction::APUT_BOOLEAN: {
+ int array_idx = (opcode == Instruction::APUT_WIDE) ? 2 : 1;
+ int index_idx = array_idx + 1;
+ uint16_t array = GetOperandValue(mir->ssa_rep->uses[array_idx]);
+ if (null_checked_.find(array) != null_checked_.end()) {
+ if (cu_->verbose) {
+ LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
+ }
+ mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+ } else {
+ null_checked_.insert(array);
+ }
+ uint16_t index = GetOperandValue(mir->ssa_rep->uses[index_idx]);
+ if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) {
+ if (cu_->verbose) {
+ LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
+ }
+ mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK;
+ }
+ mir->meta.throw_insn->optimization_flags |= mir->optimization_flags;
+ // Use side effect to note range check completed.
+ (void)LookupValue(ARRAY_REF, array, index, NO_VALUE);
+ // Rev the memory version
+ AdvanceMemoryVersion(array, NO_VALUE);
+ }
+ break;
+
+ case Instruction::IGET_OBJECT:
+ case Instruction::IGET_WIDE:
+ case Instruction::IGET:
+ case Instruction::IGET_CHAR:
+ case Instruction::IGET_SHORT:
+ case Instruction::IGET_BOOLEAN:
+ case Instruction::IGET_BYTE: {
+ uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]);
+ if (null_checked_.find(base) != null_checked_.end()) {
+ if (cu_->verbose) {
+ LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
+ }
+ mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+ } else {
+ null_checked_.insert(base);
+ }
+ mir->meta.throw_insn->optimization_flags |= mir->optimization_flags;
+ uint16_t field_ref = mir->dalvikInsn.vC;
+ uint16_t memory_version = GetMemoryVersion(base, field_ref);
+ if (opcode == Instruction::IGET_WIDE) {
+ uint16_t res = LookupValue(Instruction::IGET_WIDE, base, field_ref, memory_version);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
+ } else {
+ uint16_t res = LookupValue(Instruction::IGET, base, field_ref, memory_version);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ }
+ break;
+
+ case Instruction::IPUT_WIDE:
+ case Instruction::IPUT_OBJECT:
+ case Instruction::IPUT:
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BYTE:
+ case Instruction::IPUT_CHAR:
+ case Instruction::IPUT_SHORT: {
+ int base_reg = (opcode == Instruction::IPUT_WIDE) ? 2 : 1;
+ uint16_t base = GetOperandValue(mir->ssa_rep->uses[base_reg]);
+ if (null_checked_.find(base) != null_checked_.end()) {
+ if (cu_->verbose) {
+ LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
+ }
+ mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+ } else {
+ null_checked_.insert(base);
+ }
+ mir->meta.throw_insn->optimization_flags |= mir->optimization_flags;
+ uint16_t field_ref = mir->dalvikInsn.vC;
+ AdvanceMemoryVersion(base, field_ref);
+ }
+ break;
+
+ case Instruction::SGET_OBJECT:
+ case Instruction::SGET:
+ case Instruction::SGET_BOOLEAN:
+ case Instruction::SGET_BYTE:
+ case Instruction::SGET_CHAR:
+ case Instruction::SGET_SHORT:
+ case Instruction::SGET_WIDE: {
+ uint16_t field_ref = mir->dalvikInsn.vB;
+ uint16_t memory_version = GetMemoryVersion(NO_VALUE, field_ref);
+ if (opcode == Instruction::SGET_WIDE) {
+ uint16_t res = LookupValue(Instruction::SGET_WIDE, NO_VALUE, field_ref, memory_version);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
+ } else {
+ uint16_t res = LookupValue(Instruction::SGET, NO_VALUE, field_ref, memory_version);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ }
+ }
+ break;
+
+ case Instruction::SPUT_OBJECT:
+ case Instruction::SPUT:
+ case Instruction::SPUT_BOOLEAN:
+ case Instruction::SPUT_BYTE:
+ case Instruction::SPUT_CHAR:
+ case Instruction::SPUT_SHORT:
+ case Instruction::SPUT_WIDE: {
+ uint16_t field_ref = mir->dalvikInsn.vB;
+ AdvanceMemoryVersion(NO_VALUE, field_ref);
+ }
+ break;
+
+ }
+ return res;
+}
+
+} // namespace art
diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h
new file mode 100644
index 0000000..beb4cea
--- /dev/null
+++ b/compiler/dex/local_value_numbering.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_LOCAL_VALUE_NUMBERING_H_
+#define ART_SRC_COMPILER_DEX_LOCAL_VALUE_NUMBERING_H_
+
+#include "compiler_internals.h"
+
+#define NO_VALUE 0xffff
+#define ARRAY_REF 0xfffe
+
+namespace art {
+
+// Key is s_reg, value is value name.
+typedef SafeMap<uint16_t, uint16_t> SregValueMap;
+// Key is concatenation of quad, value is value name.
+typedef SafeMap<uint64_t, uint16_t> ValueMap;
+// Key represents a memory address, value is generation.
+typedef SafeMap<uint32_t, uint16_t> MemoryVersionMap;
+
+class LocalValueNumbering {
+ public:
+ LocalValueNumbering(CompilationUnit* cu) : cu_(cu) {};
+
+ static uint64_t BuildKey(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) {
+ return (static_cast<uint64_t>(op) << 48 | static_cast<uint64_t>(operand1) << 32 |
+ static_cast<uint64_t>(operand2) << 16 | static_cast<uint64_t>(modifier));
+ };
+
+ uint16_t LookupValue(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) {
+ uint16_t res;
+ uint64_t key = BuildKey(op, operand1, operand2, modifier);
+ ValueMap::iterator it = value_map_.find(key);
+ if (it != value_map_.end()) {
+ res = it->second;
+ } else {
+ res = value_map_.size() + 1;
+ value_map_.Put(key, res);
+ }
+ return res;
+ };
+
+ bool ValueExists(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) const {
+ uint64_t key = BuildKey(op, operand1, operand2, modifier);
+ ValueMap::const_iterator it = value_map_.find(key);
+ return (it != value_map_.end());
+ };
+
+ uint16_t GetMemoryVersion(uint16_t base, uint16_t field) {
+ uint32_t key = (base << 16) | field;
+ uint16_t res;
+ MemoryVersionMap::iterator it = memory_version_map_.find(key);
+ if (it == memory_version_map_.end()) {
+ res = 0;
+ memory_version_map_.Put(key, res);
+ } else {
+ res = it->second;
+ }
+ return res;
+ };
+
+ void AdvanceMemoryVersion(uint16_t base, uint16_t field) {
+ uint32_t key = (base << 16) | field;
+ MemoryVersionMap::iterator it = memory_version_map_.find(key);
+ if (it == memory_version_map_.end()) {
+ memory_version_map_.Put(key, 0);
+ } else {
+ it->second++;
+ }
+ };
+
+ void SetOperandValue(uint16_t s_reg, uint16_t value) {
+ SregValueMap::iterator it = sreg_value_map_.find(s_reg);
+ if (it != sreg_value_map_.end()) {
+ DCHECK_EQ(it->second, value);
+ } else {
+ sreg_value_map_.Put(s_reg, value);
+ }
+ };
+
+ uint16_t GetOperandValue(int s_reg) {
+ uint16_t res = NO_VALUE;
+ SregValueMap::iterator it = sreg_value_map_.find(s_reg);
+ if (it != sreg_value_map_.end()) {
+ res = it->second;
+ } else {
+ // First use
+ res = LookupValue(NO_VALUE, s_reg, NO_VALUE, NO_VALUE);
+ sreg_value_map_.Put(s_reg, res);
+ }
+ return res;
+ };
+
+ void SetOperandValueWide(uint16_t s_reg, uint16_t value) {
+ SregValueMap::iterator it = sreg_wide_value_map_.find(s_reg);
+ if (it != sreg_wide_value_map_.end()) {
+ DCHECK_EQ(it->second, value);
+ } else {
+ sreg_wide_value_map_.Put(s_reg, value);
+ }
+ };
+
+ uint16_t GetOperandValueWide(int s_reg) {
+ uint16_t res = NO_VALUE;
+ SregValueMap::iterator it = sreg_wide_value_map_.find(s_reg);
+ if (it != sreg_wide_value_map_.end()) {
+ res = it->second;
+ } else {
+ // First use
+ res = LookupValue(NO_VALUE, s_reg, NO_VALUE, NO_VALUE);
+ sreg_wide_value_map_.Put(s_reg, res);
+ }
+ return res;
+ };
+
+ uint16_t GetValueNumber(MIR* mir);
+
+ private:
+ CompilationUnit* const cu_;
+ SregValueMap sreg_value_map_;
+ SregValueMap sreg_wide_value_map_;
+ ValueMap value_map_;
+ MemoryVersionMap memory_version_map_;
+ std::set<uint16_t> null_checked_;
+
+};
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_LOCAL_VALUE_NUMBERING_H_
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
new file mode 100644
index 0000000..6c152d2
--- /dev/null
+++ b/compiler/dex/mir_dataflow.cc
@@ -0,0 +1,1355 @@
+/*
+ * Copyright (C) 2011 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 "compiler_internals.h"
+#include "local_value_numbering.h"
+#include "dataflow_iterator-inl.h"
+
+namespace art {
+
+/*
+ * Main table containing data flow attributes for each bytecode. The
+ * first kNumPackedOpcodes entries are for Dalvik bytecode
+ * instructions, where extended opcode at the MIR level are appended
+ * afterwards.
+ *
+ * TODO - many optimization flags are incomplete - they will only limit the
+ * scope of optimizations but will not cause mis-optimizations.
+ */
+const int MIRGraph::oat_data_flow_attributes_[kMirOpLast] = {
+ // 00 NOP
+ DF_NOP,
+
+ // 01 MOVE vA, vB
+ DF_DA | DF_UB | DF_IS_MOVE,
+
+ // 02 MOVE_FROM16 vAA, vBBBB
+ DF_DA | DF_UB | DF_IS_MOVE,
+
+ // 03 MOVE_16 vAAAA, vBBBB
+ DF_DA | DF_UB | DF_IS_MOVE,
+
+ // 04 MOVE_WIDE vA, vB
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE,
+
+ // 05 MOVE_WIDE_FROM16 vAA, vBBBB
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE,
+
+ // 06 MOVE_WIDE_16 vAAAA, vBBBB
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE,
+
+ // 07 MOVE_OBJECT vA, vB
+ DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B,
+
+ // 08 MOVE_OBJECT_FROM16 vAA, vBBBB
+ DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B,
+
+ // 09 MOVE_OBJECT_16 vAAAA, vBBBB
+ DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B,
+
+ // 0A MOVE_RESULT vAA
+ DF_DA,
+
+ // 0B MOVE_RESULT_WIDE vAA
+ DF_DA | DF_A_WIDE,
+
+ // 0C MOVE_RESULT_OBJECT vAA
+ DF_DA | DF_REF_A,
+
+ // 0D MOVE_EXCEPTION vAA
+ DF_DA | DF_REF_A | DF_NON_NULL_DST,
+
+ // 0E RETURN_VOID
+ DF_NOP,
+
+ // 0F RETURN vAA
+ DF_UA,
+
+ // 10 RETURN_WIDE vAA
+ DF_UA | DF_A_WIDE,
+
+ // 11 RETURN_OBJECT vAA
+ DF_UA | DF_REF_A,
+
+ // 12 CONST_4 vA, #+B
+ DF_DA | DF_SETS_CONST,
+
+ // 13 CONST_16 vAA, #+BBBB
+ DF_DA | DF_SETS_CONST,
+
+ // 14 CONST vAA, #+BBBBBBBB
+ DF_DA | DF_SETS_CONST,
+
+ // 15 CONST_HIGH16 VAA, #+BBBB0000
+ DF_DA | DF_SETS_CONST,
+
+ // 16 CONST_WIDE_16 vAA, #+BBBB
+ DF_DA | DF_A_WIDE | DF_SETS_CONST,
+
+ // 17 CONST_WIDE_32 vAA, #+BBBBBBBB
+ DF_DA | DF_A_WIDE | DF_SETS_CONST,
+
+ // 18 CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB
+ DF_DA | DF_A_WIDE | DF_SETS_CONST,
+
+ // 19 CONST_WIDE_HIGH16 vAA, #+BBBB000000000000
+ DF_DA | DF_A_WIDE | DF_SETS_CONST,
+
+ // 1A CONST_STRING vAA, string@BBBB
+ DF_DA | DF_REF_A | DF_NON_NULL_DST,
+
+ // 1B CONST_STRING_JUMBO vAA, string@BBBBBBBB
+ DF_DA | DF_REF_A | DF_NON_NULL_DST,
+
+ // 1C CONST_CLASS vAA, type@BBBB
+ DF_DA | DF_REF_A | DF_NON_NULL_DST,
+
+ // 1D MONITOR_ENTER vAA
+ DF_UA | DF_NULL_CHK_0 | DF_REF_A,
+
+ // 1E MONITOR_EXIT vAA
+ DF_UA | DF_NULL_CHK_0 | DF_REF_A,
+
+ // 1F CHK_CAST vAA, type@BBBB
+ DF_UA | DF_REF_A | DF_UMS,
+
+ // 20 INSTANCE_OF vA, vB, type@CCCC
+ DF_DA | DF_UB | DF_CORE_A | DF_REF_B | DF_UMS,
+
+ // 21 ARRAY_LENGTH vA, vB
+ DF_DA | DF_UB | DF_NULL_CHK_0 | DF_CORE_A | DF_REF_B,
+
+ // 22 NEW_INSTANCE vAA, type@BBBB
+ DF_DA | DF_NON_NULL_DST | DF_REF_A | DF_UMS,
+
+ // 23 NEW_ARRAY vA, vB, type@CCCC
+ DF_DA | DF_UB | DF_NON_NULL_DST | DF_REF_A | DF_CORE_B | DF_UMS,
+
+ // 24 FILLED_NEW_ARRAY {vD, vE, vF, vG, vA}
+ DF_FORMAT_35C | DF_NON_NULL_RET | DF_UMS,
+
+ // 25 FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB
+ DF_FORMAT_3RC | DF_NON_NULL_RET | DF_UMS,
+
+ // 26 FILL_ARRAY_DATA vAA, +BBBBBBBB
+ DF_UA | DF_REF_A | DF_UMS,
+
+ // 27 THROW vAA
+ DF_UA | DF_REF_A | DF_UMS,
+
+ // 28 GOTO
+ DF_NOP,
+
+ // 29 GOTO_16
+ DF_NOP,
+
+ // 2A GOTO_32
+ DF_NOP,
+
+ // 2B PACKED_SWITCH vAA, +BBBBBBBB
+ DF_UA,
+
+ // 2C SPARSE_SWITCH vAA, +BBBBBBBB
+ DF_UA,
+
+ // 2D CMPL_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C | DF_CORE_A,
+
+ // 2E CMPG_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C | DF_CORE_A,
+
+ // 2F CMPL_DOUBLE vAA, vBB, vCC
+ DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_B | DF_FP_C | DF_CORE_A,
+
+ // 30 CMPG_DOUBLE vAA, vBB, vCC
+ DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_B | DF_FP_C | DF_CORE_A,
+
+ // 31 CMP_LONG vAA, vBB, vCC
+ DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 32 IF_EQ vA, vB, +CCCC
+ DF_UA | DF_UB,
+
+ // 33 IF_NE vA, vB, +CCCC
+ DF_UA | DF_UB,
+
+ // 34 IF_LT vA, vB, +CCCC
+ DF_UA | DF_UB,
+
+ // 35 IF_GE vA, vB, +CCCC
+ DF_UA | DF_UB,
+
+ // 36 IF_GT vA, vB, +CCCC
+ DF_UA | DF_UB,
+
+ // 37 IF_LE vA, vB, +CCCC
+ DF_UA | DF_UB,
+
+ // 38 IF_EQZ vAA, +BBBB
+ DF_UA,
+
+ // 39 IF_NEZ vAA, +BBBB
+ DF_UA,
+
+ // 3A IF_LTZ vAA, +BBBB
+ DF_UA,
+
+ // 3B IF_GEZ vAA, +BBBB
+ DF_UA,
+
+ // 3C IF_GTZ vAA, +BBBB
+ DF_UA,
+
+ // 3D IF_LEZ vAA, +BBBB
+ DF_UA,
+
+ // 3E UNUSED_3E
+ DF_NOP,
+
+ // 3F UNUSED_3F
+ DF_NOP,
+
+ // 40 UNUSED_40
+ DF_NOP,
+
+ // 41 UNUSED_41
+ DF_NOP,
+
+ // 42 UNUSED_42
+ DF_NOP,
+
+ // 43 UNUSED_43
+ DF_NOP,
+
+ // 44 AGET vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
+
+ // 45 AGET_WIDE vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
+
+ // 46 AGET_OBJECT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_A | DF_REF_B | DF_CORE_C,
+
+ // 47 AGET_BOOLEAN vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
+
+ // 48 AGET_BYTE vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
+
+ // 49 AGET_CHAR vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
+
+ // 4A AGET_SHORT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C,
+
+ // 4B APUT vAA, vBB, vCC
+ DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
+
+ // 4C APUT_WIDE vAA, vBB, vCC
+ DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_2 | DF_RANGE_CHK_3 | DF_REF_B | DF_CORE_C,
+
+ // 4D APUT_OBJECT vAA, vBB, vCC
+ DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_A | DF_REF_B | DF_CORE_C,
+
+ // 4E APUT_BOOLEAN vAA, vBB, vCC
+ DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
+
+ // 4F APUT_BYTE vAA, vBB, vCC
+ DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
+
+ // 50 APUT_CHAR vAA, vBB, vCC
+ DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
+
+ // 51 APUT_SHORT vAA, vBB, vCC
+ DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C,
+
+ // 52 IGET vA, vB, field@CCCC
+ DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
+
+ // 53 IGET_WIDE vA, vB, field@CCCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
+
+ // 54 IGET_OBJECT vA, vB, field@CCCC
+ DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B,
+
+ // 55 IGET_BOOLEAN vA, vB, field@CCCC
+ DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
+
+ // 56 IGET_BYTE vA, vB, field@CCCC
+ DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
+
+ // 57 IGET_CHAR vA, vB, field@CCCC
+ DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
+
+ // 58 IGET_SHORT vA, vB, field@CCCC
+ DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
+
+ // 59 IPUT vA, vB, field@CCCC
+ DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
+
+ // 5A IPUT_WIDE vA, vB, field@CCCC
+ DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B,
+
+ // 5B IPUT_OBJECT vA, vB, field@CCCC
+ DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B,
+
+ // 5C IPUT_BOOLEAN vA, vB, field@CCCC
+ DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
+
+ // 5D IPUT_BYTE vA, vB, field@CCCC
+ DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
+
+ // 5E IPUT_CHAR vA, vB, field@CCCC
+ DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
+
+ // 5F IPUT_SHORT vA, vB, field@CCCC
+ DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
+
+ // 60 SGET vAA, field@BBBB
+ DF_DA | DF_UMS,
+
+ // 61 SGET_WIDE vAA, field@BBBB
+ DF_DA | DF_A_WIDE | DF_UMS,
+
+ // 62 SGET_OBJECT vAA, field@BBBB
+ DF_DA | DF_REF_A | DF_UMS,
+
+ // 63 SGET_BOOLEAN vAA, field@BBBB
+ DF_DA | DF_UMS,
+
+ // 64 SGET_BYTE vAA, field@BBBB
+ DF_DA | DF_UMS,
+
+ // 65 SGET_CHAR vAA, field@BBBB
+ DF_DA | DF_UMS,
+
+ // 66 SGET_SHORT vAA, field@BBBB
+ DF_DA | DF_UMS,
+
+ // 67 SPUT vAA, field@BBBB
+ DF_UA | DF_UMS,
+
+ // 68 SPUT_WIDE vAA, field@BBBB
+ DF_UA | DF_A_WIDE | DF_UMS,
+
+ // 69 SPUT_OBJECT vAA, field@BBBB
+ DF_UA | DF_REF_A | DF_UMS,
+
+ // 6A SPUT_BOOLEAN vAA, field@BBBB
+ DF_UA | DF_UMS,
+
+ // 6B SPUT_BYTE vAA, field@BBBB
+ DF_UA | DF_UMS,
+
+ // 6C SPUT_CHAR vAA, field@BBBB
+ DF_UA | DF_UMS,
+
+ // 6D SPUT_SHORT vAA, field@BBBB
+ DF_UA | DF_UMS,
+
+ // 6E INVOKE_VIRTUAL {vD, vE, vF, vG, vA}
+ DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
+
+ // 6F INVOKE_SUPER {vD, vE, vF, vG, vA}
+ DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
+
+ // 70 INVOKE_DIRECT {vD, vE, vF, vG, vA}
+ DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
+
+ // 71 INVOKE_STATIC {vD, vE, vF, vG, vA}
+ DF_FORMAT_35C | DF_UMS,
+
+ // 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA}
+ DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
+
+ // 73 UNUSED_73
+ DF_NOP,
+
+ // 74 INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN}
+ DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
+
+ // 75 INVOKE_SUPER_RANGE {vCCCC .. vNNNN}
+ DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
+
+ // 76 INVOKE_DIRECT_RANGE {vCCCC .. vNNNN}
+ DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
+
+ // 77 INVOKE_STATIC_RANGE {vCCCC .. vNNNN}
+ DF_FORMAT_3RC | DF_UMS,
+
+ // 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN}
+ DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
+
+ // 79 UNUSED_79
+ DF_NOP,
+
+ // 7A UNUSED_7A
+ DF_NOP,
+
+ // 7B NEG_INT vA, vB
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // 7C NOT_INT vA, vB
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // 7D NEG_LONG vA, vB
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
+
+ // 7E NOT_LONG vA, vB
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
+
+ // 7F NEG_FLOAT vA, vB
+ DF_DA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // 80 NEG_DOUBLE vA, vB
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
+
+ // 81 INT_TO_LONG vA, vB
+ DF_DA | DF_A_WIDE | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // 82 INT_TO_FLOAT vA, vB
+ DF_DA | DF_UB | DF_FP_A | DF_CORE_B,
+
+ // 83 INT_TO_DOUBLE vA, vB
+ DF_DA | DF_A_WIDE | DF_UB | DF_FP_A | DF_CORE_B,
+
+ // 84 LONG_TO_INT vA, vB
+ DF_DA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
+
+ // 85 LONG_TO_FLOAT vA, vB
+ DF_DA | DF_UB | DF_B_WIDE | DF_FP_A | DF_CORE_B,
+
+ // 86 LONG_TO_DOUBLE vA, vB
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_CORE_B,
+
+ // 87 FLOAT_TO_INT vA, vB
+ DF_DA | DF_UB | DF_FP_B | DF_CORE_A,
+
+ // 88 FLOAT_TO_LONG vA, vB
+ DF_DA | DF_A_WIDE | DF_UB | DF_FP_B | DF_CORE_A,
+
+ // 89 FLOAT_TO_DOUBLE vA, vB
+ DF_DA | DF_A_WIDE | DF_UB | DF_FP_A | DF_FP_B,
+
+ // 8A DOUBLE_TO_INT vA, vB
+ DF_DA | DF_UB | DF_B_WIDE | DF_FP_B | DF_CORE_A,
+
+ // 8B DOUBLE_TO_LONG vA, vB
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_B | DF_CORE_A,
+
+ // 8C DOUBLE_TO_FLOAT vA, vB
+ DF_DA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
+
+ // 8D INT_TO_BYTE vA, vB
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // 8E INT_TO_CHAR vA, vB
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // 8F INT_TO_SHORT vA, vB
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // 90 ADD_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 91 SUB_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 92 MUL_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 93 DIV_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 94 REM_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 95 AND_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 96 OR_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 97 XOR_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 98 SHL_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 99 SHR_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 9A USHR_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 9B ADD_LONG vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 9C SUB_LONG vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 9D MUL_LONG vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 9E DIV_LONG vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // 9F REM_LONG vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // A0 AND_LONG vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // A1 OR_LONG vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // A2 XOR_LONG vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // A3 SHL_LONG vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // A4 SHR_LONG vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // A5 USHR_LONG vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C,
+
+ // A6 ADD_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // A7 SUB_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // A8 MUL_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // A9 DIV_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // AA REM_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // AB ADD_DOUBLE vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // AC SUB_DOUBLE vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // AD MUL_DOUBLE vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // AE DIV_DOUBLE vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // AF REM_DOUBLE vAA, vBB, vCC
+ DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // B0 ADD_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // B1 SUB_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // B2 MUL_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // B3 DIV_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // B4 REM_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // B5 AND_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // B6 OR_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // B7 XOR_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // B8 SHL_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // B9 SHR_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // BA USHR_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // BB ADD_LONG_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
+
+ // BC SUB_LONG_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
+
+ // BD MUL_LONG_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
+
+ // BE DIV_LONG_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
+
+ // BF REM_LONG_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
+
+ // C0 AND_LONG_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
+
+ // C1 OR_LONG_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
+
+ // C2 XOR_LONG_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
+
+ // C3 SHL_LONG_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // C4 SHR_LONG_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // C5 USHR_LONG_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // C6 ADD_FLOAT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // C7 SUB_FLOAT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // C8 MUL_FLOAT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // C9 DIV_FLOAT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // CA REM_FLOAT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // CB ADD_DOUBLE_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
+
+ // CC SUB_DOUBLE_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
+
+ // CD MUL_DOUBLE_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
+
+ // CE DIV_DOUBLE_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
+
+ // CF REM_DOUBLE_2ADDR vA, vB
+ DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
+
+ // D0 ADD_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // D1 RSUB_INT vA, vB, #+CCCC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // D2 MUL_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // D3 DIV_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // D4 REM_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // D5 AND_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // D6 OR_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // D7 XOR_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // D8 ADD_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // D9 RSUB_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // DA MUL_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // DB DIV_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // DC REM_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // DD AND_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // DE OR_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // DF XOR_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // E0 SHL_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // E1 SHR_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // E2 USHR_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
+
+ // E3 IGET_VOLATILE
+ DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
+
+ // E4 IPUT_VOLATILE
+ DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B,
+
+ // E5 SGET_VOLATILE
+ DF_DA | DF_UMS,
+
+ // E6 SPUT_VOLATILE
+ DF_UA | DF_UMS,
+
+ // E7 IGET_OBJECT_VOLATILE
+ DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B,
+
+ // E8 IGET_WIDE_VOLATILE
+ DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B,
+
+ // E9 IPUT_WIDE_VOLATILE
+ DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B,
+
+ // EA SGET_WIDE_VOLATILE
+ DF_DA | DF_A_WIDE | DF_UMS,
+
+ // EB SPUT_WIDE_VOLATILE
+ DF_UA | DF_A_WIDE | DF_UMS,
+
+ // EC BREAKPOINT
+ DF_NOP,
+
+ // ED THROW_VERIFICATION_ERROR
+ DF_NOP | DF_UMS,
+
+ // EE EXECUTE_INLINE
+ DF_FORMAT_35C,
+
+ // EF EXECUTE_INLINE_RANGE
+ DF_FORMAT_3RC,
+
+ // F0 INVOKE_OBJECT_INIT_RANGE
+ DF_NOP | DF_NULL_CHK_0,
+
+ // F1 RETURN_VOID_BARRIER
+ DF_NOP,
+
+ // F2 IGET_QUICK
+ DF_DA | DF_UB | DF_NULL_CHK_0,
+
+ // F3 IGET_WIDE_QUICK
+ DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0,
+
+ // F4 IGET_OBJECT_QUICK
+ DF_DA | DF_UB | DF_NULL_CHK_0,
+
+ // F5 IPUT_QUICK
+ DF_UA | DF_UB | DF_NULL_CHK_1,
+
+ // F6 IPUT_WIDE_QUICK
+ DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2,
+
+ // F7 IPUT_OBJECT_QUICK
+ DF_UA | DF_UB | DF_NULL_CHK_1,
+
+ // F8 INVOKE_VIRTUAL_QUICK
+ DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
+
+ // F9 INVOKE_VIRTUAL_QUICK_RANGE
+ DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
+
+ // FA INVOKE_SUPER_QUICK
+ DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
+
+ // FB INVOKE_SUPER_QUICK_RANGE
+ DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
+
+ // FC IPUT_OBJECT_VOLATILE
+ DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B,
+
+ // FD SGET_OBJECT_VOLATILE
+ DF_DA | DF_REF_A | DF_UMS,
+
+ // FE SPUT_OBJECT_VOLATILE
+ DF_UA | DF_REF_A | DF_UMS,
+
+ // FF UNUSED_FF
+ DF_NOP,
+
+ // Beginning of extended MIR opcodes
+ // 100 MIR_PHI
+ DF_DA | DF_NULL_TRANSFER_N,
+
+ // 101 MIR_COPY
+ DF_DA | DF_UB | DF_IS_MOVE,
+
+ // 102 MIR_FUSED_CMPL_FLOAT
+ DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // 103 MIR_FUSED_CMPG_FLOAT
+ DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // 104 MIR_FUSED_CMPL_DOUBLE
+ DF_UA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
+
+ // 105 MIR_FUSED_CMPG_DOUBLE
+ DF_UA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B,
+
+ // 106 MIR_FUSED_CMP_LONG
+ DF_UA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B,
+
+ // 107 MIR_NOP
+ DF_NOP,
+
+ // 108 MIR_NULL_CHECK
+ 0,
+
+ // 109 MIR_RANGE_CHECK
+ 0,
+
+ // 110 MIR_DIV_ZERO_CHECK
+ 0,
+
+ // 111 MIR_CHECK
+ 0,
+
+ // 112 MIR_CHECKPART2
+ 0,
+
+ // 113 MIR_SELECT
+ DF_DA | DF_UB,
+};
+
+/* Return the base virtual register for a SSA name */
+int MIRGraph::SRegToVReg(int ssa_reg) const {
+ return ssa_base_vregs_->Get(ssa_reg);
+}
+
+/* Any register that is used before being defined is considered live-in */
+void MIRGraph::HandleLiveInUse(ArenaBitVector* use_v, ArenaBitVector* def_v,
+ ArenaBitVector* live_in_v, int dalvik_reg_id)
+{
+ use_v->SetBit(dalvik_reg_id);
+ if (!def_v->IsBitSet(dalvik_reg_id)) {
+ live_in_v->SetBit(dalvik_reg_id);
+ }
+}
+
+/* Mark a reg as being defined */
+void MIRGraph::HandleDef(ArenaBitVector* def_v, int dalvik_reg_id)
+{
+ def_v->SetBit(dalvik_reg_id);
+}
+
+/*
+ * Find out live-in variables for natural loops. Variables that are live-in in
+ * the main loop body are considered to be defined in the entry block.
+ */
+bool MIRGraph::FindLocalLiveIn(BasicBlock* bb)
+{
+ MIR* mir;
+ ArenaBitVector *use_v, *def_v, *live_in_v;
+
+ if (bb->data_flow_info == NULL) return false;
+
+ use_v = bb->data_flow_info->use_v =
+ new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapUse);
+ def_v = bb->data_flow_info->def_v =
+ new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapDef);
+ live_in_v = bb->data_flow_info->live_in_v =
+ new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapLiveIn);
+
+ for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
+ DecodedInstruction *d_insn = &mir->dalvikInsn;
+
+ if (df_attributes & DF_HAS_USES) {
+ if (df_attributes & DF_UA) {
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vA);
+ if (df_attributes & DF_A_WIDE) {
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vA+1);
+ }
+ }
+ if (df_attributes & DF_UB) {
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vB);
+ if (df_attributes & DF_B_WIDE) {
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vB+1);
+ }
+ }
+ if (df_attributes & DF_UC) {
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vC);
+ if (df_attributes & DF_C_WIDE) {
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vC+1);
+ }
+ }
+ }
+ if (df_attributes & DF_FORMAT_35C) {
+ for (unsigned int i = 0; i < d_insn->vA; i++) {
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn->arg[i]);
+ }
+ }
+ if (df_attributes & DF_FORMAT_3RC) {
+ for (unsigned int i = 0; i < d_insn->vA; i++) {
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vC+i);
+ }
+ }
+ if (df_attributes & DF_HAS_DEFS) {
+ HandleDef(def_v, d_insn->vA);
+ if (df_attributes & DF_A_WIDE) {
+ HandleDef(def_v, d_insn->vA+1);
+ }
+ }
+ }
+ return true;
+}
+
+int MIRGraph::AddNewSReg(int v_reg)
+{
+ // Compiler temps always have a subscript of 0
+ int subscript = (v_reg < 0) ? 0 : ++ssa_last_defs_[v_reg];
+ int ssa_reg = GetNumSSARegs();
+ SetNumSSARegs(ssa_reg + 1);
+ ssa_base_vregs_->Insert(v_reg);
+ ssa_subscripts_->Insert(subscript);
+ DCHECK_EQ(ssa_base_vregs_->Size(), ssa_subscripts_->Size());
+ return ssa_reg;
+}
+
+/* Find out the latest SSA register for a given Dalvik register */
+void MIRGraph::HandleSSAUse(int* uses, int dalvik_reg, int reg_index)
+{
+ DCHECK((dalvik_reg >= 0) && (dalvik_reg < cu_->num_dalvik_registers));
+ uses[reg_index] = vreg_to_ssa_map_[dalvik_reg];
+}
+
+/* Setup a new SSA register for a given Dalvik register */
+void MIRGraph::HandleSSADef(int* defs, int dalvik_reg, int reg_index)
+{
+ DCHECK((dalvik_reg >= 0) && (dalvik_reg < cu_->num_dalvik_registers));
+ int ssa_reg = AddNewSReg(dalvik_reg);
+ vreg_to_ssa_map_[dalvik_reg] = ssa_reg;
+ defs[reg_index] = ssa_reg;
+}
+
+/* Look up new SSA names for format_35c instructions */
+void MIRGraph::DataFlowSSAFormat35C(MIR* mir)
+{
+ DecodedInstruction *d_insn = &mir->dalvikInsn;
+ int num_uses = d_insn->vA;
+ int i;
+
+ mir->ssa_rep->num_uses = num_uses;
+ mir->ssa_rep->uses = static_cast<int*>(arena_->NewMem(sizeof(int) * num_uses, true,
+ ArenaAllocator::kAllocDFInfo));
+ // NOTE: will be filled in during type & size inference pass
+ mir->ssa_rep->fp_use = static_cast<bool*>(arena_->NewMem(sizeof(bool) * num_uses, true,
+ ArenaAllocator::kAllocDFInfo));
+
+ for (i = 0; i < num_uses; i++) {
+ HandleSSAUse(mir->ssa_rep->uses, d_insn->arg[i], i);
+ }
+}
+
+/* Look up new SSA names for format_3rc instructions */
+void MIRGraph::DataFlowSSAFormat3RC(MIR* mir)
+{
+ DecodedInstruction *d_insn = &mir->dalvikInsn;
+ int num_uses = d_insn->vA;
+ int i;
+
+ mir->ssa_rep->num_uses = num_uses;
+ mir->ssa_rep->uses = static_cast<int*>(arena_->NewMem(sizeof(int) * num_uses, true,
+ ArenaAllocator::kAllocDFInfo));
+ // NOTE: will be filled in during type & size inference pass
+ mir->ssa_rep->fp_use = static_cast<bool*>(arena_->NewMem(sizeof(bool) * num_uses, true,
+ ArenaAllocator::kAllocDFInfo));
+
+ for (i = 0; i < num_uses; i++) {
+ HandleSSAUse(mir->ssa_rep->uses, d_insn->vC+i, i);
+ }
+}
+
+/* Entry function to convert a block into SSA representation */
+bool MIRGraph::DoSSAConversion(BasicBlock* bb)
+{
+ MIR* mir;
+
+ if (bb->data_flow_info == NULL) return false;
+
+ for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ mir->ssa_rep =
+ static_cast<struct SSARepresentation *>(arena_->NewMem(sizeof(SSARepresentation), true,
+ ArenaAllocator::kAllocDFInfo));
+
+ int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
+
+ // If not a pseudo-op, note non-leaf or can throw
+ if (static_cast<int>(mir->dalvikInsn.opcode) <
+ static_cast<int>(kNumPackedOpcodes)) {
+ int flags = Instruction::FlagsOf(mir->dalvikInsn.opcode);
+
+ if (flags & Instruction::kInvoke) {
+ attributes_ &= ~METHOD_IS_LEAF;
+ }
+ }
+
+ int num_uses = 0;
+
+ if (df_attributes & DF_FORMAT_35C) {
+ DataFlowSSAFormat35C(mir);
+ continue;
+ }
+
+ if (df_attributes & DF_FORMAT_3RC) {
+ DataFlowSSAFormat3RC(mir);
+ continue;
+ }
+
+ if (df_attributes & DF_HAS_USES) {
+ if (df_attributes & DF_UA) {
+ num_uses++;
+ if (df_attributes & DF_A_WIDE) {
+ num_uses ++;
+ }
+ }
+ if (df_attributes & DF_UB) {
+ num_uses++;
+ if (df_attributes & DF_B_WIDE) {
+ num_uses ++;
+ }
+ }
+ if (df_attributes & DF_UC) {
+ num_uses++;
+ if (df_attributes & DF_C_WIDE) {
+ num_uses ++;
+ }
+ }
+ }
+
+ if (num_uses) {
+ mir->ssa_rep->num_uses = num_uses;
+ mir->ssa_rep->uses = static_cast<int*>(arena_->NewMem(sizeof(int) * num_uses, false,
+ ArenaAllocator::kAllocDFInfo));
+ mir->ssa_rep->fp_use = static_cast<bool*>(arena_->NewMem(sizeof(bool) * num_uses, false,
+ ArenaAllocator::kAllocDFInfo));
+ }
+
+ int num_defs = 0;
+
+ if (df_attributes & DF_HAS_DEFS) {
+ num_defs++;
+ if (df_attributes & DF_A_WIDE) {
+ num_defs++;
+ }
+ }
+
+ if (num_defs) {
+ mir->ssa_rep->num_defs = num_defs;
+ mir->ssa_rep->defs = static_cast<int*>(arena_->NewMem(sizeof(int) * num_defs, false,
+ ArenaAllocator::kAllocDFInfo));
+ mir->ssa_rep->fp_def = static_cast<bool*>(arena_->NewMem(sizeof(bool) * num_defs, false,
+ ArenaAllocator::kAllocDFInfo));
+ }
+
+ DecodedInstruction *d_insn = &mir->dalvikInsn;
+
+ if (df_attributes & DF_HAS_USES) {
+ num_uses = 0;
+ if (df_attributes & DF_UA) {
+ mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_A;
+ HandleSSAUse(mir->ssa_rep->uses, d_insn->vA, num_uses++);
+ if (df_attributes & DF_A_WIDE) {
+ mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_A;
+ HandleSSAUse(mir->ssa_rep->uses, d_insn->vA+1, num_uses++);
+ }
+ }
+ if (df_attributes & DF_UB) {
+ mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_B;
+ HandleSSAUse(mir->ssa_rep->uses, d_insn->vB, num_uses++);
+ if (df_attributes & DF_B_WIDE) {
+ mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_B;
+ HandleSSAUse(mir->ssa_rep->uses, d_insn->vB+1, num_uses++);
+ }
+ }
+ if (df_attributes & DF_UC) {
+ mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_C;
+ HandleSSAUse(mir->ssa_rep->uses, d_insn->vC, num_uses++);
+ if (df_attributes & DF_C_WIDE) {
+ mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_C;
+ HandleSSAUse(mir->ssa_rep->uses, d_insn->vC+1, num_uses++);
+ }
+ }
+ }
+ if (df_attributes & DF_HAS_DEFS) {
+ mir->ssa_rep->fp_def[0] = df_attributes & DF_FP_A;
+ HandleSSADef(mir->ssa_rep->defs, d_insn->vA, 0);
+ if (df_attributes & DF_A_WIDE) {
+ mir->ssa_rep->fp_def[1] = df_attributes & DF_FP_A;
+ HandleSSADef(mir->ssa_rep->defs, d_insn->vA+1, 1);
+ }
+ }
+ }
+
+ /*
+ * Take a snapshot of Dalvik->SSA mapping at the end of each block. The
+ * input to PHI nodes can be derived from the snapshot of all
+ * predecessor blocks.
+ */
+ bb->data_flow_info->vreg_to_ssa_map =
+ static_cast<int*>(arena_->NewMem(sizeof(int) * cu_->num_dalvik_registers, false,
+ ArenaAllocator::kAllocDFInfo));
+
+ memcpy(bb->data_flow_info->vreg_to_ssa_map, vreg_to_ssa_map_,
+ sizeof(int) * cu_->num_dalvik_registers);
+ return true;
+}
+
+/* Setup the basic data structures for SSA conversion */
+void MIRGraph::CompilerInitializeSSAConversion()
+{
+ size_t num_dalvik_reg = cu_->num_dalvik_registers;
+
+ ssa_base_vregs_ = new (arena_) GrowableArray<int>(arena_, num_dalvik_reg + GetDefCount() + 128,
+ kGrowableArraySSAtoDalvikMap);
+ ssa_subscripts_ = new (arena_) GrowableArray<int>(arena_, num_dalvik_reg + GetDefCount() + 128,
+ kGrowableArraySSAtoDalvikMap);
+ /*
+ * Initial number of SSA registers is equal to the number of Dalvik
+ * registers.
+ */
+ SetNumSSARegs(num_dalvik_reg);
+
+ /*
+ * Initialize the SSA2Dalvik map list. For the first num_dalvik_reg elements,
+ * the subscript is 0 so we use the ENCODE_REG_SUB macro to encode the value
+ * into "(0 << 16) | i"
+ */
+ for (unsigned int i = 0; i < num_dalvik_reg; i++) {
+ ssa_base_vregs_->Insert(i);
+ ssa_subscripts_->Insert(0);
+ }
+
+ /*
+ * Initialize the DalvikToSSAMap map. There is one entry for each
+ * Dalvik register, and the SSA names for those are the same.
+ */
+ vreg_to_ssa_map_ =
+ static_cast<int*>(arena_->NewMem(sizeof(int) * num_dalvik_reg, false,
+ ArenaAllocator::kAllocDFInfo));
+ /* Keep track of the higest def for each dalvik reg */
+ ssa_last_defs_ =
+ static_cast<int*>(arena_->NewMem(sizeof(int) * num_dalvik_reg, false,
+ ArenaAllocator::kAllocDFInfo));
+
+ for (unsigned int i = 0; i < num_dalvik_reg; i++) {
+ vreg_to_ssa_map_[i] = i;
+ ssa_last_defs_[i] = 0;
+ }
+
+ /* Add ssa reg for Method* */
+ method_sreg_ = AddNewSReg(SSA_METHOD_BASEREG);
+
+ /*
+ * Allocate the BasicBlockDataFlow structure for the entry and code blocks
+ */
+ GrowableArray<BasicBlock*>::Iterator iterator(&block_list_);
+
+ while (true) {
+ BasicBlock* bb = iterator.Next();
+ if (bb == NULL) break;
+ if (bb->hidden == true) continue;
+ if (bb->block_type == kDalvikByteCode ||
+ bb->block_type == kEntryBlock ||
+ bb->block_type == kExitBlock) {
+ bb->data_flow_info =
+ static_cast<BasicBlockDataFlow*>(arena_->NewMem(sizeof(BasicBlockDataFlow), true,
+ ArenaAllocator::kAllocDFInfo));
+ }
+ }
+}
+
+/*
+ * This function will make a best guess at whether the invoke will
+ * end up using Method*. It isn't critical to get it exactly right,
+ * and attempting to do would involve more complexity than it's
+ * worth.
+ */
+bool MIRGraph::InvokeUsesMethodStar(MIR* mir)
+{
+ InvokeType type;
+ Instruction::Code opcode = mir->dalvikInsn.opcode;
+ switch (opcode) {
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_STATIC_RANGE:
+ type = kStatic;
+ break;
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ type = kDirect;
+ break;
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ type = kVirtual;
+ break;
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ return false;
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_SUPER:
+ type = kSuper;
+ break;
+ default:
+ LOG(WARNING) << "Unexpected invoke op: " << opcode;
+ return false;
+ }
+ DexCompilationUnit m_unit(cu_);
+ MethodReference target_method(cu_->dex_file, mir->dalvikInsn.vB);
+ int vtable_idx;
+ uintptr_t direct_code;
+ uintptr_t direct_method;
+ uint32_t current_offset = static_cast<uint32_t>(current_offset_);
+ bool fast_path =
+ cu_->compiler_driver->ComputeInvokeInfo(&m_unit, current_offset,
+ type, target_method,
+ vtable_idx,
+ direct_code, direct_method,
+ false) &&
+ !(cu_->enable_debug & (1 << kDebugSlowInvokePath));
+ return (((type == kDirect) || (type == kStatic)) &&
+ fast_path && ((direct_code == 0) || (direct_method == 0)));
+}
+
+/*
+ * Count uses, weighting by loop nesting depth. This code only
+ * counts explicitly used s_regs. A later phase will add implicit
+ * counts for things such as Method*, null-checked references, etc.
+ */
+bool MIRGraph::CountUses(struct BasicBlock* bb)
+{
+ if (bb->block_type != kDalvikByteCode) {
+ return false;
+ }
+ for (MIR* mir = bb->first_mir_insn; (mir != NULL); mir = mir->next) {
+ if (mir->ssa_rep == NULL) {
+ continue;
+ }
+ uint32_t weight = std::min(16U, static_cast<uint32_t>(bb->nesting_depth));
+ for (int i = 0; i < mir->ssa_rep->num_uses; i++) {
+ int s_reg = mir->ssa_rep->uses[i];
+ raw_use_counts_.Increment(s_reg);
+ use_counts_.Put(s_reg, use_counts_.Get(s_reg) + (1 << weight));
+ }
+ if (!(cu_->disable_opt & (1 << kPromoteCompilerTemps))) {
+ int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
+ // Implicit use of Method* ? */
+ if (df_attributes & DF_UMS) {
+ /*
+ * Some invokes will not use Method* - need to perform test similar
+ * to that found in GenInvoke() to decide whether to count refs
+ * for Method* on invoke-class opcodes.
+ * TODO: refactor for common test here, save results for GenInvoke
+ */
+ int uses_method_star = true;
+ if ((df_attributes & (DF_FORMAT_35C | DF_FORMAT_3RC)) &&
+ !(df_attributes & DF_NON_NULL_RET)) {
+ uses_method_star &= InvokeUsesMethodStar(mir);
+ }
+ if (uses_method_star) {
+ raw_use_counts_.Increment(method_sreg_);
+ use_counts_.Put(method_sreg_, use_counts_.Get(method_sreg_) + (1 << weight));
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void MIRGraph::MethodUseCount()
+{
+ // Now that we know, resize the lists.
+ int num_ssa_regs = GetNumSSARegs();
+ use_counts_.Resize(num_ssa_regs + 32);
+ raw_use_counts_.Resize(num_ssa_regs + 32);
+ // Initialize list
+ for (int i = 0; i < num_ssa_regs; i++) {
+ use_counts_.Insert(0);
+ raw_use_counts_.Insert(0);
+ }
+ if (cu_->disable_opt & (1 << kPromoteRegs)) {
+ return;
+ }
+ AllNodesIterator iter(this, false /* not iterative */);
+ for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
+ CountUses(bb);
+ }
+}
+
+/* Verify if all the successor is connected with all the claimed predecessors */
+bool MIRGraph::VerifyPredInfo(BasicBlock* bb)
+{
+ GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors);
+
+ while (true) {
+ BasicBlock *pred_bb = iter.Next();
+ if (!pred_bb) break;
+ bool found = false;
+ if (pred_bb->taken == bb) {
+ found = true;
+ } else if (pred_bb->fall_through == bb) {
+ found = true;
+ } else if (pred_bb->successor_block_list.block_list_type != kNotUsed) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(pred_bb->successor_block_list.blocks);
+ while (true) {
+ SuccessorBlockInfo *successor_block_info = iterator.Next();
+ if (successor_block_info == NULL) break;
+ BasicBlock *succ_bb = successor_block_info->block;
+ if (succ_bb == bb) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (found == false) {
+ char block_name1[BLOCK_NAME_LEN], block_name2[BLOCK_NAME_LEN];
+ GetBlockName(bb, block_name1);
+ GetBlockName(pred_bb, block_name2);
+ DumpCFG("/sdcard/cfg/", false);
+ LOG(FATAL) << "Successor " << block_name1 << "not found from "
+ << block_name2;
+ }
+ }
+ return true;
+}
+
+void MIRGraph::VerifyDataflow()
+{
+ /* Verify if all blocks are connected as claimed */
+ AllNodesIterator iter(this, false /* not iterative */);
+ for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
+ VerifyPredInfo(bb);
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
new file mode 100644
index 0000000..11e100d
--- /dev/null
+++ b/compiler/dex/mir_graph.cc
@@ -0,0 +1,1180 @@
+/*
+ * Copyright (C) 2013 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 "base/stl_util.h"
+#include "compiler_internals.h"
+#include "dex_file-inl.h"
+#include "leb128.h"
+#include "mir_graph.h"
+
+namespace art {
+
+#define MAX_PATTERN_LEN 5
+
+struct CodePattern {
+ const Instruction::Code opcodes[MAX_PATTERN_LEN];
+ const SpecialCaseHandler handler_code;
+};
+
+static const CodePattern special_patterns[] = {
+ {{Instruction::RETURN_VOID}, kNullMethod},
+ {{Instruction::CONST, Instruction::RETURN}, kConstFunction},
+ {{Instruction::CONST_4, Instruction::RETURN}, kConstFunction},
+ {{Instruction::CONST_4, Instruction::RETURN_OBJECT}, kConstFunction},
+ {{Instruction::CONST_16, Instruction::RETURN}, kConstFunction},
+ {{Instruction::IGET, Instruction:: RETURN}, kIGet},
+ {{Instruction::IGET_BOOLEAN, Instruction::RETURN}, kIGetBoolean},
+ {{Instruction::IGET_OBJECT, Instruction::RETURN_OBJECT}, kIGetObject},
+ {{Instruction::IGET_BYTE, Instruction::RETURN}, kIGetByte},
+ {{Instruction::IGET_CHAR, Instruction::RETURN}, kIGetChar},
+ {{Instruction::IGET_SHORT, Instruction::RETURN}, kIGetShort},
+ {{Instruction::IGET_WIDE, Instruction::RETURN_WIDE}, kIGetWide},
+ {{Instruction::IPUT, Instruction::RETURN_VOID}, kIPut},
+ {{Instruction::IPUT_BOOLEAN, Instruction::RETURN_VOID}, kIPutBoolean},
+ {{Instruction::IPUT_OBJECT, Instruction::RETURN_VOID}, kIPutObject},
+ {{Instruction::IPUT_BYTE, Instruction::RETURN_VOID}, kIPutByte},
+ {{Instruction::IPUT_CHAR, Instruction::RETURN_VOID}, kIPutChar},
+ {{Instruction::IPUT_SHORT, Instruction::RETURN_VOID}, kIPutShort},
+ {{Instruction::IPUT_WIDE, Instruction::RETURN_VOID}, kIPutWide},
+ {{Instruction::RETURN}, kIdentity},
+ {{Instruction::RETURN_OBJECT}, kIdentity},
+ {{Instruction::RETURN_WIDE}, kIdentity},
+};
+
+const char* MIRGraph::extended_mir_op_names_[kMirOpLast - kMirOpFirst] = {
+ "Phi",
+ "Copy",
+ "FusedCmplFloat",
+ "FusedCmpgFloat",
+ "FusedCmplDouble",
+ "FusedCmpgDouble",
+ "FusedCmpLong",
+ "Nop",
+ "OpNullCheck",
+ "OpRangeCheck",
+ "OpDivZeroCheck",
+ "Check1",
+ "Check2",
+ "Select",
+};
+
+MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena)
+ : reg_location_(NULL),
+ compiler_temps_(arena, 6, kGrowableArrayMisc),
+ cu_(cu),
+ ssa_base_vregs_(NULL),
+ ssa_subscripts_(NULL),
+ vreg_to_ssa_map_(NULL),
+ ssa_last_defs_(NULL),
+ is_constant_v_(NULL),
+ constant_values_(NULL),
+ use_counts_(arena, 256, kGrowableArrayMisc),
+ raw_use_counts_(arena, 256, kGrowableArrayMisc),
+ num_reachable_blocks_(0),
+ dfs_order_(NULL),
+ dfs_post_order_(NULL),
+ dom_post_order_traversal_(NULL),
+ i_dom_list_(NULL),
+ def_block_matrix_(NULL),
+ temp_block_v_(NULL),
+ temp_dalvik_register_v_(NULL),
+ temp_ssa_register_v_(NULL),
+ block_list_(arena, 100, kGrowableArrayBlockList),
+ try_block_addr_(NULL),
+ entry_block_(NULL),
+ exit_block_(NULL),
+ cur_block_(NULL),
+ num_blocks_(0),
+ current_code_item_(NULL),
+ current_method_(kInvalidEntry),
+ current_offset_(kInvalidEntry),
+ def_count_(0),
+ opcode_count_(NULL),
+ num_ssa_regs_(0),
+ method_sreg_(0),
+ attributes_(METHOD_IS_LEAF), // Start with leaf assumption, change on encountering invoke.
+ checkstats_(NULL),
+ arena_(arena)
+ {
+ try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */);
+}
+
+MIRGraph::~MIRGraph() {
+ STLDeleteElements(&m_units_);
+}
+
+bool MIRGraph::ContentIsInsn(const uint16_t* code_ptr) {
+ uint16_t instr = *code_ptr;
+ Instruction::Code opcode = static_cast<Instruction::Code>(instr & 0xff);
+ /*
+ * Since the low 8-bit in metadata may look like NOP, we need to check
+ * both the low and whole sub-word to determine whether it is code or data.
+ */
+ return (opcode != Instruction::NOP || instr == 0);
+}
+
+/*
+ * Parse an instruction, return the length of the instruction
+ */
+int MIRGraph::ParseInsn(const uint16_t* code_ptr, DecodedInstruction* decoded_instruction)
+{
+ // Don't parse instruction data
+ if (!ContentIsInsn(code_ptr)) {
+ return 0;
+ }
+
+ const Instruction* instruction = Instruction::At(code_ptr);
+ *decoded_instruction = DecodedInstruction(instruction);
+
+ return instruction->SizeInCodeUnits();
+}
+
+
+/* Split an existing block from the specified code offset into two */
+BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset,
+ BasicBlock* orig_block, BasicBlock** immed_pred_block_p)
+{
+ MIR* insn = orig_block->first_mir_insn;
+ while (insn) {
+ if (insn->offset == code_offset) break;
+ insn = insn->next;
+ }
+ if (insn == NULL) {
+ LOG(FATAL) << "Break split failed";
+ }
+ BasicBlock *bottom_block = NewMemBB(kDalvikByteCode, num_blocks_++);
+ block_list_.Insert(bottom_block);
+
+ bottom_block->start_offset = code_offset;
+ bottom_block->first_mir_insn = insn;
+ bottom_block->last_mir_insn = orig_block->last_mir_insn;
+
+ /* If this block was terminated by a return, the flag needs to go with the bottom block */
+ bottom_block->terminated_by_return = orig_block->terminated_by_return;
+ orig_block->terminated_by_return = false;
+
+ /* Add it to the quick lookup cache */
+ block_map_.Put(bottom_block->start_offset, bottom_block);
+
+ /* Handle the taken path */
+ bottom_block->taken = orig_block->taken;
+ if (bottom_block->taken) {
+ orig_block->taken = NULL;
+ bottom_block->taken->predecessors->Delete(orig_block);
+ bottom_block->taken->predecessors->Insert(bottom_block);
+ }
+
+ /* Handle the fallthrough path */
+ bottom_block->fall_through = orig_block->fall_through;
+ orig_block->fall_through = bottom_block;
+ bottom_block->predecessors->Insert(orig_block);
+ if (bottom_block->fall_through) {
+ bottom_block->fall_through->predecessors->Delete(orig_block);
+ bottom_block->fall_through->predecessors->Insert(bottom_block);
+ }
+
+ /* Handle the successor list */
+ if (orig_block->successor_block_list.block_list_type != kNotUsed) {
+ bottom_block->successor_block_list = orig_block->successor_block_list;
+ orig_block->successor_block_list.block_list_type = kNotUsed;
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bottom_block->successor_block_list.blocks);
+ while (true) {
+ SuccessorBlockInfo *successor_block_info = iterator.Next();
+ if (successor_block_info == NULL) break;
+ BasicBlock *bb = successor_block_info->block;
+ bb->predecessors->Delete(orig_block);
+ bb->predecessors->Insert(bottom_block);
+ }
+ }
+
+ orig_block->last_mir_insn = insn->prev;
+
+ insn->prev->next = NULL;
+ insn->prev = NULL;
+ /*
+ * Update the immediate predecessor block pointer so that outgoing edges
+ * can be applied to the proper block.
+ */
+ if (immed_pred_block_p) {
+ DCHECK_EQ(*immed_pred_block_p, orig_block);
+ *immed_pred_block_p = bottom_block;
+ }
+ return bottom_block;
+}
+
+/*
+ * Given a code offset, find out the block that starts with it. If the offset
+ * is in the middle of an existing block, split it into two. If immed_pred_block_p
+ * is not non-null and is the block being split, update *immed_pred_block_p to
+ * point to the bottom block so that outgoing edges can be set up properly
+ * (by the caller)
+ * Utilizes a map for fast lookup of the typical cases.
+ */
+BasicBlock* MIRGraph::FindBlock(unsigned int code_offset, bool split, bool create,
+ BasicBlock** immed_pred_block_p)
+{
+ BasicBlock* bb;
+ unsigned int i;
+ SafeMap<unsigned int, BasicBlock*>::iterator it;
+
+ it = block_map_.find(code_offset);
+ if (it != block_map_.end()) {
+ return it->second;
+ } else if (!create) {
+ return NULL;
+ }
+
+ if (split) {
+ for (i = 0; i < block_list_.Size(); i++) {
+ bb = block_list_.Get(i);
+ if (bb->block_type != kDalvikByteCode) continue;
+ /* Check if a branch jumps into the middle of an existing block */
+ if ((code_offset > bb->start_offset) && (bb->last_mir_insn != NULL) &&
+ (code_offset <= bb->last_mir_insn->offset)) {
+ BasicBlock *new_bb = SplitBlock(code_offset, bb, bb == *immed_pred_block_p ?
+ immed_pred_block_p : NULL);
+ return new_bb;
+ }
+ }
+ }
+
+ /* Create a new one */
+ bb = NewMemBB(kDalvikByteCode, num_blocks_++);
+ block_list_.Insert(bb);
+ bb->start_offset = code_offset;
+ block_map_.Put(bb->start_offset, bb);
+ return bb;
+}
+
+/* Identify code range in try blocks and set up the empty catch blocks */
+void MIRGraph::ProcessTryCatchBlocks()
+{
+ int tries_size = current_code_item_->tries_size_;
+ int offset;
+
+ if (tries_size == 0) {
+ return;
+ }
+
+ for (int i = 0; i < tries_size; i++) {
+ const DexFile::TryItem* pTry =
+ DexFile::GetTryItems(*current_code_item_, i);
+ int start_offset = pTry->start_addr_;
+ int end_offset = start_offset + pTry->insn_count_;
+ for (offset = start_offset; offset < end_offset; offset++) {
+ try_block_addr_->SetBit(offset);
+ }
+ }
+
+ // Iterate over each of the handlers to enqueue the empty Catch blocks
+ const byte* handlers_ptr = DexFile::GetCatchHandlerData(*current_code_item_, 0);
+ uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
+ for (uint32_t idx = 0; idx < handlers_size; idx++) {
+ CatchHandlerIterator iterator(handlers_ptr);
+ for (; iterator.HasNext(); iterator.Next()) {
+ uint32_t address = iterator.GetHandlerAddress();
+ FindBlock(address, false /* split */, true /*create*/,
+ /* immed_pred_block_p */ NULL);
+ }
+ handlers_ptr = iterator.EndDataPointer();
+ }
+}
+
+/* Process instructions with the kBranch flag */
+BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
+ int flags, const uint16_t* code_ptr,
+ const uint16_t* code_end)
+{
+ int target = cur_offset;
+ switch (insn->dalvikInsn.opcode) {
+ case Instruction::GOTO:
+ case Instruction::GOTO_16:
+ case Instruction::GOTO_32:
+ target += insn->dalvikInsn.vA;
+ break;
+ case Instruction::IF_EQ:
+ case Instruction::IF_NE:
+ case Instruction::IF_LT:
+ case Instruction::IF_GE:
+ case Instruction::IF_GT:
+ case Instruction::IF_LE:
+ cur_block->conditional_branch = true;
+ target += insn->dalvikInsn.vC;
+ break;
+ case Instruction::IF_EQZ:
+ case Instruction::IF_NEZ:
+ case Instruction::IF_LTZ:
+ case Instruction::IF_GEZ:
+ case Instruction::IF_GTZ:
+ case Instruction::IF_LEZ:
+ cur_block->conditional_branch = true;
+ target += insn->dalvikInsn.vB;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected opcode(" << insn->dalvikInsn.opcode << ") with kBranch set";
+ }
+ BasicBlock *taken_block = FindBlock(target, /* split */ true, /* create */ true,
+ /* immed_pred_block_p */ &cur_block);
+ cur_block->taken = taken_block;
+ taken_block->predecessors->Insert(cur_block);
+
+ /* Always terminate the current block for conditional branches */
+ if (flags & Instruction::kContinue) {
+ BasicBlock *fallthrough_block = FindBlock(cur_offset + width,
+ /*
+ * If the method is processed
+ * in sequential order from the
+ * beginning, we don't need to
+ * specify split for continue
+ * blocks. However, this
+ * routine can be called by
+ * compileLoop, which starts
+ * parsing the method from an
+ * arbitrary address in the
+ * method body.
+ */
+ true,
+ /* create */
+ true,
+ /* immed_pred_block_p */
+ &cur_block);
+ cur_block->fall_through = fallthrough_block;
+ fallthrough_block->predecessors->Insert(cur_block);
+ } else if (code_ptr < code_end) {
+ /* Create a fallthrough block for real instructions (incl. NOP) */
+ if (ContentIsInsn(code_ptr)) {
+ FindBlock(cur_offset + width, /* split */ false, /* create */ true,
+ /* immed_pred_block_p */ NULL);
+ }
+ }
+ return cur_block;
+}
+
+/* Process instructions with the kSwitch flag */
+void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
+ int flags)
+{
+ const uint16_t* switch_data =
+ reinterpret_cast<const uint16_t*>(GetCurrentInsns() + cur_offset + insn->dalvikInsn.vB);
+ int size;
+ const int* keyTable;
+ const int* target_table;
+ int i;
+ int first_key;
+
+ /*
+ * Packed switch data format:
+ * ushort ident = 0x0100 magic value
+ * ushort size number of entries in the table
+ * int first_key first (and lowest) switch case value
+ * int targets[size] branch targets, relative to switch opcode
+ *
+ * Total size is (4+size*2) 16-bit code units.
+ */
+ if (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) {
+ DCHECK_EQ(static_cast<int>(switch_data[0]),
+ static_cast<int>(Instruction::kPackedSwitchSignature));
+ size = switch_data[1];
+ first_key = switch_data[2] | (switch_data[3] << 16);
+ target_table = reinterpret_cast<const int*>(&switch_data[4]);
+ keyTable = NULL; // Make the compiler happy
+ /*
+ * Sparse switch data format:
+ * ushort ident = 0x0200 magic value
+ * ushort size number of entries in the table; > 0
+ * int keys[size] keys, sorted low-to-high; 32-bit aligned
+ * int targets[size] branch targets, relative to switch opcode
+ *
+ * Total size is (2+size*4) 16-bit code units.
+ */
+ } else {
+ DCHECK_EQ(static_cast<int>(switch_data[0]),
+ static_cast<int>(Instruction::kSparseSwitchSignature));
+ size = switch_data[1];
+ keyTable = reinterpret_cast<const int*>(&switch_data[2]);
+ target_table = reinterpret_cast<const int*>(&switch_data[2 + size*2]);
+ first_key = 0; // To make the compiler happy
+ }
+
+ if (cur_block->successor_block_list.block_list_type != kNotUsed) {
+ LOG(FATAL) << "Successor block list already in use: "
+ << static_cast<int>(cur_block->successor_block_list.block_list_type);
+ }
+ cur_block->successor_block_list.block_list_type =
+ (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?
+ kPackedSwitch : kSparseSwitch;
+ cur_block->successor_block_list.blocks =
+ new (arena_)GrowableArray<SuccessorBlockInfo*>(arena_, size, kGrowableArraySuccessorBlocks);
+
+ for (i = 0; i < size; i++) {
+ BasicBlock *case_block = FindBlock(cur_offset + target_table[i], /* split */ true,
+ /* create */ true, /* immed_pred_block_p */ &cur_block);
+ SuccessorBlockInfo *successor_block_info =
+ static_cast<SuccessorBlockInfo*>(arena_->NewMem(sizeof(SuccessorBlockInfo), false,
+ ArenaAllocator::kAllocSuccessor));
+ successor_block_info->block = case_block;
+ successor_block_info->key =
+ (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?
+ first_key + i : keyTable[i];
+ cur_block->successor_block_list.blocks->Insert(successor_block_info);
+ case_block->predecessors->Insert(cur_block);
+ }
+
+ /* Fall-through case */
+ BasicBlock* fallthrough_block = FindBlock( cur_offset + width, /* split */ false,
+ /* create */ true, /* immed_pred_block_p */ NULL);
+ cur_block->fall_through = fallthrough_block;
+ fallthrough_block->predecessors->Insert(cur_block);
+}
+
+/* Process instructions with the kThrow flag */
+BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
+ int flags, ArenaBitVector* try_block_addr,
+ const uint16_t* code_ptr, const uint16_t* code_end)
+{
+ bool in_try_block = try_block_addr->IsBitSet(cur_offset);
+
+ /* In try block */
+ if (in_try_block) {
+ CatchHandlerIterator iterator(*current_code_item_, cur_offset);
+
+ if (cur_block->successor_block_list.block_list_type != kNotUsed) {
+ LOG(INFO) << PrettyMethod(cu_->method_idx, *cu_->dex_file);
+ LOG(FATAL) << "Successor block list already in use: "
+ << static_cast<int>(cur_block->successor_block_list.block_list_type);
+ }
+
+ cur_block->successor_block_list.block_list_type = kCatch;
+ cur_block->successor_block_list.blocks =
+ new (arena_) GrowableArray<SuccessorBlockInfo*>(arena_, 2, kGrowableArraySuccessorBlocks);
+
+ for (;iterator.HasNext(); iterator.Next()) {
+ BasicBlock *catch_block = FindBlock(iterator.GetHandlerAddress(), false /* split*/,
+ false /* creat */, NULL /* immed_pred_block_p */);
+ catch_block->catch_entry = true;
+ if (kIsDebugBuild) {
+ catches_.insert(catch_block->start_offset);
+ }
+ SuccessorBlockInfo *successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
+ (arena_->NewMem(sizeof(SuccessorBlockInfo), false, ArenaAllocator::kAllocSuccessor));
+ successor_block_info->block = catch_block;
+ successor_block_info->key = iterator.GetHandlerTypeIndex();
+ cur_block->successor_block_list.blocks->Insert(successor_block_info);
+ catch_block->predecessors->Insert(cur_block);
+ }
+ } else {
+ BasicBlock *eh_block = NewMemBB(kExceptionHandling, num_blocks_++);
+ cur_block->taken = eh_block;
+ block_list_.Insert(eh_block);
+ eh_block->start_offset = cur_offset;
+ eh_block->predecessors->Insert(cur_block);
+ }
+
+ if (insn->dalvikInsn.opcode == Instruction::THROW){
+ cur_block->explicit_throw = true;
+ if ((code_ptr < code_end) && ContentIsInsn(code_ptr)) {
+ // Force creation of new block following THROW via side-effect
+ FindBlock(cur_offset + width, /* split */ false, /* create */ true,
+ /* immed_pred_block_p */ NULL);
+ }
+ if (!in_try_block) {
+ // Don't split a THROW that can't rethrow - we're done.
+ return cur_block;
+ }
+ }
+
+ /*
+ * Split the potentially-throwing instruction into two parts.
+ * The first half will be a pseudo-op that captures the exception
+ * edges and terminates the basic block. It always falls through.
+ * Then, create a new basic block that begins with the throwing instruction
+ * (minus exceptions). Note: this new basic block must NOT be entered into
+ * the block_map. If the potentially-throwing instruction is the target of a
+ * future branch, we need to find the check psuedo half. The new
+ * basic block containing the work portion of the instruction should
+ * only be entered via fallthrough from the block containing the
+ * pseudo exception edge MIR. Note also that this new block is
+ * not automatically terminated after the work portion, and may
+ * contain following instructions.
+ */
+ BasicBlock *new_block = NewMemBB(kDalvikByteCode, num_blocks_++);
+ block_list_.Insert(new_block);
+ new_block->start_offset = insn->offset;
+ cur_block->fall_through = new_block;
+ new_block->predecessors->Insert(cur_block);
+ MIR* new_insn = static_cast<MIR*>(arena_->NewMem(sizeof(MIR), true, ArenaAllocator::kAllocMIR));
+ *new_insn = *insn;
+ insn->dalvikInsn.opcode =
+ static_cast<Instruction::Code>(kMirOpCheck);
+ // Associate the two halves
+ insn->meta.throw_insn = new_insn;
+ new_insn->meta.throw_insn = insn;
+ AppendMIR(new_block, new_insn);
+ return new_block;
+}
+
+/* Parse a Dex method and insert it into the MIRGraph at the current insert point. */
+void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
+ InvokeType invoke_type, uint32_t class_def_idx,
+ uint32_t method_idx, jobject class_loader, const DexFile& dex_file)
+{
+ current_code_item_ = code_item;
+ method_stack_.push_back(std::make_pair(current_method_, current_offset_));
+ current_method_ = m_units_.size();
+ current_offset_ = 0;
+ // TODO: will need to snapshot stack image and use that as the mir context identification.
+ m_units_.push_back(new DexCompilationUnit(cu_, class_loader, Runtime::Current()->GetClassLinker(),
+ dex_file, current_code_item_, class_def_idx, method_idx, access_flags));
+ const uint16_t* code_ptr = current_code_item_->insns_;
+ const uint16_t* code_end =
+ current_code_item_->insns_ + current_code_item_->insns_size_in_code_units_;
+
+ // TODO: need to rework expansion of block list & try_block_addr when inlining activated.
+ block_list_.Resize(block_list_.Size() + current_code_item_->insns_size_in_code_units_);
+ // TODO: replace with explicit resize routine. Using automatic extension side effect for now.
+ try_block_addr_->SetBit(current_code_item_->insns_size_in_code_units_);
+ try_block_addr_->ClearBit(current_code_item_->insns_size_in_code_units_);
+
+ // If this is the first method, set up default entry and exit blocks.
+ if (current_method_ == 0) {
+ DCHECK(entry_block_ == NULL);
+ DCHECK(exit_block_ == NULL);
+ DCHECK(num_blocks_ == 0);
+ entry_block_ = NewMemBB(kEntryBlock, num_blocks_++);
+ exit_block_ = NewMemBB(kExitBlock, num_blocks_++);
+ block_list_.Insert(entry_block_);
+ block_list_.Insert(exit_block_);
+ // TODO: deprecate all "cu->" fields; move what's left to wherever CompilationUnit is allocated.
+ cu_->dex_file = &dex_file;
+ cu_->class_def_idx = class_def_idx;
+ cu_->method_idx = method_idx;
+ cu_->access_flags = access_flags;
+ cu_->invoke_type = invoke_type;
+ cu_->shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
+ cu_->num_ins = current_code_item_->ins_size_;
+ cu_->num_regs = current_code_item_->registers_size_ - cu_->num_ins;
+ cu_->num_outs = current_code_item_->outs_size_;
+ cu_->num_dalvik_registers = current_code_item_->registers_size_;
+ cu_->insns = current_code_item_->insns_;
+ cu_->code_item = current_code_item_;
+ } else {
+ UNIMPLEMENTED(FATAL) << "Nested inlining not implemented.";
+ /*
+ * Will need to manage storage for ins & outs, push prevous state and update
+ * insert point.
+ */
+ }
+
+ /* Current block to record parsed instructions */
+ BasicBlock *cur_block = NewMemBB(kDalvikByteCode, num_blocks_++);
+ DCHECK_EQ(current_offset_, 0);
+ cur_block->start_offset = current_offset_;
+ block_list_.Insert(cur_block);
+ /* Add first block to the fast lookup cache */
+// FIXME: block map needs association with offset/method pair rather than just offset
+ block_map_.Put(cur_block->start_offset, cur_block);
+// FIXME: this needs to insert at the insert point rather than entry block.
+ entry_block_->fall_through = cur_block;
+ cur_block->predecessors->Insert(entry_block_);
+
+ /* Identify code range in try blocks and set up the empty catch blocks */
+ ProcessTryCatchBlocks();
+
+ /* Set up for simple method detection */
+ int num_patterns = sizeof(special_patterns)/sizeof(special_patterns[0]);
+ bool live_pattern = (num_patterns > 0) && !(cu_->disable_opt & (1 << kMatch));
+ bool* dead_pattern =
+ static_cast<bool*>(arena_->NewMem(sizeof(bool) * num_patterns, true,
+ ArenaAllocator::kAllocMisc));
+ SpecialCaseHandler special_case = kNoHandler;
+ // FIXME - wire this up
+ (void)special_case;
+ int pattern_pos = 0;
+
+ /* Parse all instructions and put them into containing basic blocks */
+ while (code_ptr < code_end) {
+ MIR *insn = static_cast<MIR *>(arena_->NewMem(sizeof(MIR), true, ArenaAllocator::kAllocMIR));
+ insn->offset = current_offset_;
+ insn->m_unit_index = current_method_;
+ int width = ParseInsn(code_ptr, &insn->dalvikInsn);
+ insn->width = width;
+ Instruction::Code opcode = insn->dalvikInsn.opcode;
+ if (opcode_count_ != NULL) {
+ opcode_count_[static_cast<int>(opcode)]++;
+ }
+
+ /* Terminate when the data section is seen */
+ if (width == 0)
+ break;
+
+ /* Possible simple method? */
+ if (live_pattern) {
+ live_pattern = false;
+ special_case = kNoHandler;
+ for (int i = 0; i < num_patterns; i++) {
+ if (!dead_pattern[i]) {
+ if (special_patterns[i].opcodes[pattern_pos] == opcode) {
+ live_pattern = true;
+ special_case = special_patterns[i].handler_code;
+ } else {
+ dead_pattern[i] = true;
+ }
+ }
+ }
+ pattern_pos++;
+ }
+
+ AppendMIR(cur_block, insn);
+
+ code_ptr += width;
+ int flags = Instruction::FlagsOf(insn->dalvikInsn.opcode);
+
+ int df_flags = oat_data_flow_attributes_[insn->dalvikInsn.opcode];
+
+ if (df_flags & DF_HAS_DEFS) {
+ def_count_ += (df_flags & DF_A_WIDE) ? 2 : 1;
+ }
+
+ if (flags & Instruction::kBranch) {
+ cur_block = ProcessCanBranch(cur_block, insn, current_offset_,
+ width, flags, code_ptr, code_end);
+ } else if (flags & Instruction::kReturn) {
+ cur_block->terminated_by_return = true;
+ cur_block->fall_through = exit_block_;
+ exit_block_->predecessors->Insert(cur_block);
+ /*
+ * Terminate the current block if there are instructions
+ * afterwards.
+ */
+ if (code_ptr < code_end) {
+ /*
+ * Create a fallthrough block for real instructions
+ * (incl. NOP).
+ */
+ if (ContentIsInsn(code_ptr)) {
+ FindBlock(current_offset_ + width, /* split */ false, /* create */ true,
+ /* immed_pred_block_p */ NULL);
+ }
+ }
+ } else if (flags & Instruction::kThrow) {
+ cur_block = ProcessCanThrow(cur_block, insn, current_offset_, width, flags, try_block_addr_,
+ code_ptr, code_end);
+ } else if (flags & Instruction::kSwitch) {
+ ProcessCanSwitch(cur_block, insn, current_offset_, width, flags);
+ }
+ current_offset_ += width;
+ BasicBlock *next_block = FindBlock(current_offset_, /* split */ false, /* create */
+ false, /* immed_pred_block_p */ NULL);
+ if (next_block) {
+ /*
+ * The next instruction could be the target of a previously parsed
+ * forward branch so a block is already created. If the current
+ * instruction is not an unconditional branch, connect them through
+ * the fall-through link.
+ */
+ DCHECK(cur_block->fall_through == NULL ||
+ cur_block->fall_through == next_block ||
+ cur_block->fall_through == exit_block_);
+
+ if ((cur_block->fall_through == NULL) && (flags & Instruction::kContinue)) {
+ cur_block->fall_through = next_block;
+ next_block->predecessors->Insert(cur_block);
+ }
+ cur_block = next_block;
+ }
+ }
+ if (cu_->enable_debug & (1 << kDebugDumpCFG)) {
+ DumpCFG("/sdcard/1_post_parse_cfg/", true);
+ }
+
+ if (cu_->verbose) {
+ DumpMIRGraph();
+ }
+}
+
+void MIRGraph::ShowOpcodeStats()
+{
+ DCHECK(opcode_count_ != NULL);
+ LOG(INFO) << "Opcode Count";
+ for (int i = 0; i < kNumPackedOpcodes; i++) {
+ if (opcode_count_[i] != 0) {
+ LOG(INFO) << "-C- " << Instruction::Name(static_cast<Instruction::Code>(i))
+ << " " << opcode_count_[i];
+ }
+ }
+}
+
+// TODO: use a configurable base prefix, and adjust callers to supply pass name.
+/* Dump the CFG into a DOT graph */
+void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks)
+{
+ FILE* file;
+ std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file));
+ ReplaceSpecialChars(fname);
+ fname = StringPrintf("%s%s%x.dot", dir_prefix, fname.c_str(),
+ GetEntryBlock()->fall_through->start_offset);
+ file = fopen(fname.c_str(), "w");
+ if (file == NULL) {
+ return;
+ }
+ fprintf(file, "digraph G {\n");
+
+ fprintf(file, " rankdir=TB\n");
+
+ int num_blocks = all_blocks ? GetNumBlocks() : num_reachable_blocks_;
+ int idx;
+
+ for (idx = 0; idx < num_blocks; idx++) {
+ int block_idx = all_blocks ? idx : dfs_order_->Get(idx);
+ BasicBlock *bb = GetBasicBlock(block_idx);
+ if (bb == NULL) break;
+ if (bb->block_type == kDead) continue;
+ if (bb->block_type == kEntryBlock) {
+ fprintf(file, " entry_%d [shape=Mdiamond];\n", bb->id);
+ } else if (bb->block_type == kExitBlock) {
+ fprintf(file, " exit_%d [shape=Mdiamond];\n", bb->id);
+ } else if (bb->block_type == kDalvikByteCode) {
+ fprintf(file, " block%04x_%d [shape=record,label = \"{ \\\n",
+ bb->start_offset, bb->id);
+ const MIR *mir;
+ fprintf(file, " {block id %d\\l}%s\\\n", bb->id,
+ bb->first_mir_insn ? " | " : " ");
+ for (mir = bb->first_mir_insn; mir; mir = mir->next) {
+ int opcode = mir->dalvikInsn.opcode;
+ fprintf(file, " {%04x %s %s %s\\l}%s\\\n", mir->offset,
+ mir->ssa_rep ? GetDalvikDisassembly(mir) :
+ (opcode < kMirOpFirst) ? Instruction::Name(mir->dalvikInsn.opcode) :
+ extended_mir_op_names_[opcode - kMirOpFirst],
+ (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0 ? " no_rangecheck" : " ",
+ (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0 ? " no_nullcheck" : " ",
+ mir->next ? " | " : " ");
+ }
+ fprintf(file, " }\"];\n\n");
+ } else if (bb->block_type == kExceptionHandling) {
+ char block_name[BLOCK_NAME_LEN];
+
+ GetBlockName(bb, block_name);
+ fprintf(file, " %s [shape=invhouse];\n", block_name);
+ }
+
+ char block_name1[BLOCK_NAME_LEN], block_name2[BLOCK_NAME_LEN];
+
+ if (bb->taken) {
+ GetBlockName(bb, block_name1);
+ GetBlockName(bb->taken, block_name2);
+ fprintf(file, " %s:s -> %s:n [style=dotted]\n",
+ block_name1, block_name2);
+ }
+ if (bb->fall_through) {
+ GetBlockName(bb, block_name1);
+ GetBlockName(bb->fall_through, block_name2);
+ fprintf(file, " %s:s -> %s:n\n", block_name1, block_name2);
+ }
+
+ if (bb->successor_block_list.block_list_type != kNotUsed) {
+ fprintf(file, " succ%04x_%d [shape=%s,label = \"{ \\\n",
+ bb->start_offset, bb->id,
+ (bb->successor_block_list.block_list_type == kCatch) ?
+ "Mrecord" : "record");
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_block_list.blocks);
+ SuccessorBlockInfo *successor_block_info = iterator.Next();
+
+ int succ_id = 0;
+ while (true) {
+ if (successor_block_info == NULL) break;
+
+ BasicBlock *dest_block = successor_block_info->block;
+ SuccessorBlockInfo *next_successor_block_info = iterator.Next();
+
+ fprintf(file, " {<f%d> %04x: %04x\\l}%s\\\n",
+ succ_id++,
+ successor_block_info->key,
+ dest_block->start_offset,
+ (next_successor_block_info != NULL) ? " | " : " ");
+
+ successor_block_info = next_successor_block_info;
+ }
+ fprintf(file, " }\"];\n\n");
+
+ GetBlockName(bb, block_name1);
+ fprintf(file, " %s:s -> succ%04x_%d:n [style=dashed]\n",
+ block_name1, bb->start_offset, bb->id);
+
+ if (bb->successor_block_list.block_list_type == kPackedSwitch ||
+ bb->successor_block_list.block_list_type == kSparseSwitch) {
+
+ GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_block_list.blocks);
+
+ succ_id = 0;
+ while (true) {
+ SuccessorBlockInfo *successor_block_info = iter.Next();
+ if (successor_block_info == NULL) break;
+
+ BasicBlock *dest_block = successor_block_info->block;
+
+ GetBlockName(dest_block, block_name2);
+ fprintf(file, " succ%04x_%d:f%d:e -> %s:n\n", bb->start_offset,
+ bb->id, succ_id++, block_name2);
+ }
+ }
+ }
+ fprintf(file, "\n");
+
+ if (cu_->verbose) {
+ /* Display the dominator tree */
+ GetBlockName(bb, block_name1);
+ fprintf(file, " cfg%s [label=\"%s\", shape=none];\n",
+ block_name1, block_name1);
+ if (bb->i_dom) {
+ GetBlockName(bb->i_dom, block_name2);
+ fprintf(file, " cfg%s:s -> cfg%s:n\n\n", block_name2, block_name1);
+ }
+ }
+ }
+ fprintf(file, "}\n");
+ fclose(file);
+}
+
+/* Insert an MIR instruction to the end of a basic block */
+void MIRGraph::AppendMIR(BasicBlock* bb, MIR* mir)
+{
+ if (bb->first_mir_insn == NULL) {
+ DCHECK(bb->last_mir_insn == NULL);
+ bb->last_mir_insn = bb->first_mir_insn = mir;
+ mir->prev = mir->next = NULL;
+ } else {
+ bb->last_mir_insn->next = mir;
+ mir->prev = bb->last_mir_insn;
+ mir->next = NULL;
+ bb->last_mir_insn = mir;
+ }
+}
+
+/* Insert an MIR instruction to the head of a basic block */
+void MIRGraph::PrependMIR(BasicBlock* bb, MIR* mir)
+{
+ if (bb->first_mir_insn == NULL) {
+ DCHECK(bb->last_mir_insn == NULL);
+ bb->last_mir_insn = bb->first_mir_insn = mir;
+ mir->prev = mir->next = NULL;
+ } else {
+ bb->first_mir_insn->prev = mir;
+ mir->next = bb->first_mir_insn;
+ mir->prev = NULL;
+ bb->first_mir_insn = mir;
+ }
+}
+
+/* Insert a MIR instruction after the specified MIR */
+void MIRGraph::InsertMIRAfter(BasicBlock* bb, MIR* current_mir, MIR* new_mir)
+{
+ new_mir->prev = current_mir;
+ new_mir->next = current_mir->next;
+ current_mir->next = new_mir;
+
+ if (new_mir->next) {
+ /* Is not the last MIR in the block */
+ new_mir->next->prev = new_mir;
+ } else {
+ /* Is the last MIR in the block */
+ bb->last_mir_insn = new_mir;
+ }
+}
+
+char* MIRGraph::GetDalvikDisassembly(const MIR* mir)
+{
+ DecodedInstruction insn = mir->dalvikInsn;
+ std::string str;
+ int flags = 0;
+ int opcode = insn.opcode;
+ char* ret;
+ bool nop = false;
+ SSARepresentation* ssa_rep = mir->ssa_rep;
+ Instruction::Format dalvik_format = Instruction::k10x; // Default to no-operand format
+ int defs = (ssa_rep != NULL) ? ssa_rep->num_defs : 0;
+ int uses = (ssa_rep != NULL) ? ssa_rep->num_uses : 0;
+
+ // Handle special cases.
+ if ((opcode == kMirOpCheck) || (opcode == kMirOpCheckPart2)) {
+ str.append(extended_mir_op_names_[opcode - kMirOpFirst]);
+ str.append(": ");
+ // Recover the original Dex instruction
+ insn = mir->meta.throw_insn->dalvikInsn;
+ ssa_rep = mir->meta.throw_insn->ssa_rep;
+ defs = ssa_rep->num_defs;
+ uses = ssa_rep->num_uses;
+ opcode = insn.opcode;
+ } else if (opcode == kMirOpNop) {
+ str.append("[");
+ insn.opcode = mir->meta.original_opcode;
+ opcode = mir->meta.original_opcode;
+ nop = true;
+ }
+
+ if (opcode >= kMirOpFirst) {
+ str.append(extended_mir_op_names_[opcode - kMirOpFirst]);
+ } else {
+ dalvik_format = Instruction::FormatOf(insn.opcode);
+ flags = Instruction::FlagsOf(insn.opcode);
+ str.append(Instruction::Name(insn.opcode));
+ }
+
+ if (opcode == kMirOpPhi) {
+ int* incoming = reinterpret_cast<int*>(insn.vB);
+ str.append(StringPrintf(" %s = (%s",
+ GetSSANameWithConst(ssa_rep->defs[0], true).c_str(),
+ GetSSANameWithConst(ssa_rep->uses[0], true).c_str()));
+ str.append(StringPrintf(":%d",incoming[0]));
+ int i;
+ for (i = 1; i < uses; i++) {
+ str.append(StringPrintf(", %s:%d",
+ GetSSANameWithConst(ssa_rep->uses[i], true).c_str(),
+ incoming[i]));
+ }
+ str.append(")");
+ } else if ((flags & Instruction::kBranch) != 0) {
+ // For branches, decode the instructions to print out the branch targets.
+ int offset = 0;
+ switch (dalvik_format) {
+ case Instruction::k21t:
+ str.append(StringPrintf(" %s,", GetSSANameWithConst(ssa_rep->uses[0], false).c_str()));
+ offset = insn.vB;
+ break;
+ case Instruction::k22t:
+ str.append(StringPrintf(" %s, %s,", GetSSANameWithConst(ssa_rep->uses[0], false).c_str(),
+ GetSSANameWithConst(ssa_rep->uses[1], false).c_str()));
+ offset = insn.vC;
+ break;
+ case Instruction::k10t:
+ case Instruction::k20t:
+ case Instruction::k30t:
+ offset = insn.vA;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected branch format " << dalvik_format << " from " << insn.opcode;
+ }
+ str.append(StringPrintf(" 0x%x (%c%x)", mir->offset + offset,
+ offset > 0 ? '+' : '-', offset > 0 ? offset : -offset));
+ } else {
+ // For invokes-style formats, treat wide regs as a pair of singles
+ bool show_singles = ((dalvik_format == Instruction::k35c) ||
+ (dalvik_format == Instruction::k3rc));
+ if (defs != 0) {
+ str.append(StringPrintf(" %s", GetSSANameWithConst(ssa_rep->defs[0], false).c_str()));
+ if (uses != 0) {
+ str.append(", ");
+ }
+ }
+ for (int i = 0; i < uses; i++) {
+ str.append(
+ StringPrintf(" %s", GetSSANameWithConst(ssa_rep->uses[i], show_singles).c_str()));
+ if (!show_singles && (reg_location_ != NULL) && reg_location_[i].wide) {
+ // For the listing, skip the high sreg.
+ i++;
+ }
+ if (i != (uses -1)) {
+ str.append(",");
+ }
+ }
+ switch (dalvik_format) {
+ case Instruction::k11n: // Add one immediate from vB
+ case Instruction::k21s:
+ case Instruction::k31i:
+ case Instruction::k21h:
+ str.append(StringPrintf(", #%d", insn.vB));
+ break;
+ case Instruction::k51l: // Add one wide immediate
+ str.append(StringPrintf(", #%lld", insn.vB_wide));
+ break;
+ case Instruction::k21c: // One register, one string/type/method index
+ case Instruction::k31c:
+ str.append(StringPrintf(", index #%d", insn.vB));
+ break;
+ case Instruction::k22c: // Two registers, one string/type/method index
+ str.append(StringPrintf(", index #%d", insn.vC));
+ break;
+ case Instruction::k22s: // Add one immediate from vC
+ case Instruction::k22b:
+ str.append(StringPrintf(", #%d", insn.vC));
+ break;
+ default:
+ ; // Nothing left to print
+ }
+ }
+ if (nop) {
+ str.append("]--optimized away");
+ }
+ int length = str.length() + 1;
+ ret = static_cast<char*>(arena_->NewMem(length, false, ArenaAllocator::kAllocDFInfo));
+ strncpy(ret, str.c_str(), length);
+ return ret;
+}
+
+/* Turn method name into a legal Linux file name */
+void MIRGraph::ReplaceSpecialChars(std::string& str)
+{
+ static const struct { const char before; const char after; } match[] =
+ {{'/','-'}, {';','#'}, {' ','#'}, {'$','+'},
+ {'(','@'}, {')','@'}, {'<','='}, {'>','='}};
+ for (unsigned int i = 0; i < sizeof(match)/sizeof(match[0]); i++) {
+ std::replace(str.begin(), str.end(), match[i].before, match[i].after);
+ }
+}
+
+std::string MIRGraph::GetSSAName(int ssa_reg)
+{
+ // TODO: This value is needed for LLVM and debugging. Currently, we compute this and then copy to
+ // the arena. We should be smarter and just place straight into the arena, or compute the
+ // value more lazily.
+ return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+}
+
+// Similar to GetSSAName, but if ssa name represents an immediate show that as well.
+std::string MIRGraph::GetSSANameWithConst(int ssa_reg, bool singles_only)
+{
+ if (reg_location_ == NULL) {
+ // Pre-SSA - just use the standard name
+ return GetSSAName(ssa_reg);
+ }
+ if (IsConst(reg_location_[ssa_reg])) {
+ if (!singles_only && reg_location_[ssa_reg].wide) {
+ return StringPrintf("v%d_%d#0x%llx", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg),
+ ConstantValueWide(reg_location_[ssa_reg]));
+ } else {
+ return StringPrintf("v%d_%d#0x%x", SRegToVReg(ssa_reg),GetSSASubscript(ssa_reg),
+ ConstantValue(reg_location_[ssa_reg]));
+ }
+ } else {
+ return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+ }
+}
+
+void MIRGraph::GetBlockName(BasicBlock* bb, char* name)
+{
+ switch (bb->block_type) {
+ case kEntryBlock:
+ snprintf(name, BLOCK_NAME_LEN, "entry_%d", bb->id);
+ break;
+ case kExitBlock:
+ snprintf(name, BLOCK_NAME_LEN, "exit_%d", bb->id);
+ break;
+ case kDalvikByteCode:
+ snprintf(name, BLOCK_NAME_LEN, "block%04x_%d", bb->start_offset, bb->id);
+ break;
+ case kExceptionHandling:
+ snprintf(name, BLOCK_NAME_LEN, "exception%04x_%d", bb->start_offset,
+ bb->id);
+ break;
+ default:
+ snprintf(name, BLOCK_NAME_LEN, "_%d", bb->id);
+ break;
+ }
+}
+
+const char* MIRGraph::GetShortyFromTargetIdx(int target_idx)
+{
+ // FIXME: use current code unit for inline support.
+ const DexFile::MethodId& method_id = cu_->dex_file->GetMethodId(target_idx);
+ return cu_->dex_file->GetShorty(method_id.proto_idx_);
+}
+
+/* Debug Utility - dump a compilation unit */
+void MIRGraph::DumpMIRGraph()
+{
+ BasicBlock* bb;
+ const char* block_type_names[] = {
+ "Entry Block",
+ "Code Block",
+ "Exit Block",
+ "Exception Handling",
+ "Catch Block"
+ };
+
+ LOG(INFO) << "Compiling " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
+ LOG(INFO) << cu_->insns << " insns";
+ LOG(INFO) << GetNumBlocks() << " blocks in total";
+ GrowableArray<BasicBlock*>::Iterator iterator(&block_list_);
+
+ while (true) {
+ bb = iterator.Next();
+ if (bb == NULL) break;
+ LOG(INFO) << StringPrintf("Block %d (%s) (insn %04x - %04x%s)",
+ bb->id,
+ block_type_names[bb->block_type],
+ bb->start_offset,
+ bb->last_mir_insn ? bb->last_mir_insn->offset : bb->start_offset,
+ bb->last_mir_insn ? "" : " empty");
+ if (bb->taken) {
+ LOG(INFO) << " Taken branch: block " << bb->taken->id
+ << "(0x" << std::hex << bb->taken->start_offset << ")";
+ }
+ if (bb->fall_through) {
+ LOG(INFO) << " Fallthrough : block " << bb->fall_through->id
+ << " (0x" << std::hex << bb->fall_through->start_offset << ")";
+ }
+ }
+}
+
+/*
+ * Build an array of location records for the incoming arguments.
+ * Note: one location record per word of arguments, with dummy
+ * high-word loc for wide arguments. Also pull up any following
+ * MOVE_RESULT and incorporate it into the invoke.
+ */
+CallInfo* MIRGraph::NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type,
+ bool is_range)
+{
+ CallInfo* info = static_cast<CallInfo*>(arena_->NewMem(sizeof(CallInfo), true,
+ ArenaAllocator::kAllocMisc));
+ MIR* move_result_mir = FindMoveResult(bb, mir);
+ if (move_result_mir == NULL) {
+ info->result.location = kLocInvalid;
+ } else {
+ info->result = GetRawDest(move_result_mir);
+ move_result_mir->meta.original_opcode = move_result_mir->dalvikInsn.opcode;
+ move_result_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+ }
+ info->num_arg_words = mir->ssa_rep->num_uses;
+ info->args = (info->num_arg_words == 0) ? NULL : static_cast<RegLocation*>
+ (arena_->NewMem(sizeof(RegLocation) * info->num_arg_words, false,
+ ArenaAllocator::kAllocMisc));
+ for (int i = 0; i < info->num_arg_words; i++) {
+ info->args[i] = GetRawSrc(mir, i);
+ }
+ info->opt_flags = mir->optimization_flags;
+ info->type = type;
+ info->is_range = is_range;
+ info->index = mir->dalvikInsn.vB;
+ info->offset = mir->offset;
+ return info;
+}
+
+// Allocate a new basic block.
+BasicBlock* MIRGraph::NewMemBB(BBType block_type, int block_id)
+{
+ BasicBlock* bb = static_cast<BasicBlock*>(arena_->NewMem(sizeof(BasicBlock), true,
+ ArenaAllocator::kAllocBB));
+ bb->block_type = block_type;
+ bb->id = block_id;
+ // TUNING: better estimate of the exit block predecessors?
+ bb->predecessors = new (arena_)
+ GrowableArray<BasicBlock*>(arena_, (block_type == kExitBlock) ? 2048 : 2, kGrowableArrayPredecessors);
+ bb->successor_block_list.block_list_type = kNotUsed;
+ block_id_map_.Put(block_id, block_id);
+ return bb;
+}
+
+} // namespace art
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
new file mode 100644
index 0000000..2b1c21f
--- /dev/null
+++ b/compiler/dex/mir_graph.h
@@ -0,0 +1,670 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_MIRGRAPH_H_
+#define ART_SRC_COMPILER_DEX_MIRGRAPH_H_
+
+#include "dex_file.h"
+#include "dex_instruction.h"
+#include "compiler_ir.h"
+#include "arena_bit_vector.h"
+#include "growable_array.h"
+
+namespace art {
+
+enum DataFlowAttributePos {
+ kUA = 0,
+ kUB,
+ kUC,
+ kAWide,
+ kBWide,
+ kCWide,
+ kDA,
+ kIsMove,
+ kSetsConst,
+ kFormat35c,
+ kFormat3rc,
+ kNullCheckSrc0, // Null check of uses[0].
+ kNullCheckSrc1, // Null check of uses[1].
+ kNullCheckSrc2, // Null check of uses[2].
+ kNullCheckOut0, // Null check out outgoing arg0.
+ kDstNonNull, // May assume dst is non-null.
+ kRetNonNull, // May assume retval is non-null.
+ kNullTransferSrc0, // Object copy src[0] -> dst.
+ kNullTransferSrcN, // Phi null check state transfer.
+ kRangeCheckSrc1, // Range check of uses[1].
+ kRangeCheckSrc2, // Range check of uses[2].
+ kRangeCheckSrc3, // Range check of uses[3].
+ kFPA,
+ kFPB,
+ kFPC,
+ kCoreA,
+ kCoreB,
+ kCoreC,
+ kRefA,
+ kRefB,
+ kRefC,
+ kUsesMethodStar, // Implicit use of Method*.
+};
+
+#define DF_NOP 0
+#define DF_UA (1 << kUA)
+#define DF_UB (1 << kUB)
+#define DF_UC (1 << kUC)
+#define DF_A_WIDE (1 << kAWide)
+#define DF_B_WIDE (1 << kBWide)
+#define DF_C_WIDE (1 << kCWide)
+#define DF_DA (1 << kDA)
+#define DF_IS_MOVE (1 << kIsMove)
+#define DF_SETS_CONST (1 << kSetsConst)
+#define DF_FORMAT_35C (1 << kFormat35c)
+#define DF_FORMAT_3RC (1 << kFormat3rc)
+#define DF_NULL_CHK_0 (1 << kNullCheckSrc0)
+#define DF_NULL_CHK_1 (1 << kNullCheckSrc1)
+#define DF_NULL_CHK_2 (1 << kNullCheckSrc2)
+#define DF_NULL_CHK_OUT0 (1 << kNullCheckOut0)
+#define DF_NON_NULL_DST (1 << kDstNonNull)
+#define DF_NON_NULL_RET (1 << kRetNonNull)
+#define DF_NULL_TRANSFER_0 (1 << kNullTransferSrc0)
+#define DF_NULL_TRANSFER_N (1 << kNullTransferSrcN)
+#define DF_RANGE_CHK_1 (1 << kRangeCheckSrc1)
+#define DF_RANGE_CHK_2 (1 << kRangeCheckSrc2)
+#define DF_RANGE_CHK_3 (1 << kRangeCheckSrc3)
+#define DF_FP_A (1 << kFPA)
+#define DF_FP_B (1 << kFPB)
+#define DF_FP_C (1 << kFPC)
+#define DF_CORE_A (1 << kCoreA)
+#define DF_CORE_B (1 << kCoreB)
+#define DF_CORE_C (1 << kCoreC)
+#define DF_REF_A (1 << kRefA)
+#define DF_REF_B (1 << kRefB)
+#define DF_REF_C (1 << kRefC)
+#define DF_UMS (1 << kUsesMethodStar)
+
+#define DF_HAS_USES (DF_UA | DF_UB | DF_UC)
+
+#define DF_HAS_DEFS (DF_DA)
+
+#define DF_HAS_NULL_CHKS (DF_NULL_CHK_0 | \
+ DF_NULL_CHK_1 | \
+ DF_NULL_CHK_2 | \
+ DF_NULL_CHK_OUT0)
+
+#define DF_HAS_RANGE_CHKS (DF_RANGE_CHK_1 | \
+ DF_RANGE_CHK_2 | \
+ DF_RANGE_CHK_3)
+
+#define DF_HAS_NR_CHKS (DF_HAS_NULL_CHKS | \
+ DF_HAS_RANGE_CHKS)
+
+#define DF_A_IS_REG (DF_UA | DF_DA)
+#define DF_B_IS_REG (DF_UB)
+#define DF_C_IS_REG (DF_UC)
+#define DF_IS_GETTER_OR_SETTER (DF_IS_GETTER | DF_IS_SETTER)
+#define DF_USES_FP (DF_FP_A | DF_FP_B | DF_FP_C)
+
+enum OatMethodAttributes {
+ kIsLeaf, // Method is leaf.
+ kHasLoop, // Method contains simple loop.
+};
+
+#define METHOD_IS_LEAF (1 << kIsLeaf)
+#define METHOD_HAS_LOOP (1 << kHasLoop)
+
+// Minimum field size to contain Dalvik v_reg number.
+#define VREG_NUM_WIDTH 16
+
+#define INVALID_SREG (-1)
+#define INVALID_VREG (0xFFFFU)
+#define INVALID_REG (0xFF)
+#define INVALID_OFFSET (0xDEADF00FU)
+
+/* SSA encodings for special registers */
+#define SSA_METHOD_BASEREG (-2)
+/* First compiler temp basereg, grows smaller */
+#define SSA_CTEMP_BASEREG (SSA_METHOD_BASEREG - 1)
+
+#define MIR_IGNORE_NULL_CHECK (1 << kMIRIgnoreNullCheck)
+#define MIR_NULL_CHECK_ONLY (1 << kMIRNullCheckOnly)
+#define MIR_IGNORE_RANGE_CHECK (1 << kMIRIgnoreRangeCheck)
+#define MIR_RANGE_CHECK_ONLY (1 << kMIRRangeCheckOnly)
+#define MIR_INLINED (1 << kMIRInlined)
+#define MIR_INLINED_PRED (1 << kMIRInlinedPred)
+#define MIR_CALLEE (1 << kMIRCallee)
+#define MIR_IGNORE_SUSPEND_CHECK (1 << kMIRIgnoreSuspendCheck)
+#define MIR_DUP (1 << kMIRDup)
+
+#define BLOCK_NAME_LEN 80
+
+/*
+ * In general, vreg/sreg describe Dalvik registers that originated with dx. However,
+ * it is useful to have compiler-generated temporary registers and have them treated
+ * in the same manner as dx-generated virtual registers. This struct records the SSA
+ * name of compiler-introduced temporaries.
+ */
+struct CompilerTemp {
+ int s_reg;
+};
+
+// When debug option enabled, records effectiveness of null and range check elimination.
+struct Checkstats {
+ int null_checks;
+ int null_checks_eliminated;
+ int range_checks;
+ int range_checks_eliminated;
+};
+
+// Dataflow attributes of a basic block.
+struct BasicBlockDataFlow {
+ ArenaBitVector* use_v;
+ ArenaBitVector* def_v;
+ ArenaBitVector* live_in_v;
+ ArenaBitVector* phi_v;
+ int* vreg_to_ssa_map;
+ ArenaBitVector* ending_null_check_v;
+};
+
+/*
+ * Normalized use/def for a MIR operation using SSA names rather than vregs. Note that
+ * uses/defs retain the Dalvik convention that long operations operate on a pair of 32-bit
+ * vregs. For example, "ADD_LONG v0, v2, v3" would have 2 defs (v0/v1) and 4 uses (v2/v3, v4/v5).
+ * Following SSA renaming, this is the primary struct used by code generators to locate
+ * operand and result registers. This is a somewhat confusing and unhelpful convention that
+ * we may want to revisit in the future.
+ */
+struct SSARepresentation {
+ int num_uses;
+ int* uses;
+ bool* fp_use;
+ int num_defs;
+ int* defs;
+ bool* fp_def;
+};
+
+/*
+ * The Midlevel Intermediate Representation node, which may be largely considered a
+ * wrapper around a Dalvik byte code.
+ */
+struct MIR {
+ DecodedInstruction dalvikInsn;
+ unsigned int width;
+ unsigned int offset;
+ int m_unit_index; // From which method was this MIR included
+ MIR* prev;
+ MIR* next;
+ SSARepresentation* ssa_rep;
+ int optimization_flags;
+ union {
+ // Establish link between two halves of throwing instructions.
+ MIR* throw_insn;
+ // Saved opcode for NOP'd MIRs
+ Instruction::Code original_opcode;
+ } meta;
+};
+
+struct SuccessorBlockInfo;
+
+struct BasicBlock {
+ int id;
+ int dfs_id;
+ bool visited;
+ bool hidden;
+ bool catch_entry;
+ bool explicit_throw;
+ bool conditional_branch;
+ bool terminated_by_return; // Block ends with a Dalvik return opcode.
+ bool dominates_return; // Is a member of return extended basic block.
+ uint16_t start_offset;
+ uint16_t nesting_depth;
+ BBType block_type;
+ MIR* first_mir_insn;
+ MIR* last_mir_insn;
+ BasicBlock* fall_through;
+ BasicBlock* taken;
+ BasicBlock* i_dom; // Immediate dominator.
+ BasicBlockDataFlow* data_flow_info;
+ GrowableArray<BasicBlock*>* predecessors;
+ ArenaBitVector* dominators;
+ ArenaBitVector* i_dominated; // Set nodes being immediately dominated.
+ ArenaBitVector* dom_frontier; // Dominance frontier.
+ struct { // For one-to-many successors like.
+ BlockListType block_list_type; // switch and exception handling.
+ GrowableArray<SuccessorBlockInfo*>* blocks;
+ } successor_block_list;
+};
+
+/*
+ * The "blocks" field in "successor_block_list" points to an array of elements with the type
+ * "SuccessorBlockInfo". For catch blocks, key is type index for the exception. For swtich
+ * blocks, key is the case value.
+ */
+// TODO: make class with placement new.
+struct SuccessorBlockInfo {
+ BasicBlock* block;
+ int key;
+};
+
+/*
+ * Whereas a SSA name describes a definition of a Dalvik vreg, the RegLocation describes
+ * the type of an SSA name (and, can also be used by code generators to record where the
+ * value is located (i.e. - physical register, frame, spill, etc.). For each SSA name (SReg)
+ * there is a RegLocation.
+ * FIXME: The orig_sreg field was added as a workaround for llvm bitcode generation. With
+ * the latest restructuring, we should be able to remove it and rely on s_reg_low throughout.
+ */
+struct RegLocation {
+ RegLocationType location:3;
+ unsigned wide:1;
+ unsigned defined:1; // Do we know the type?
+ unsigned is_const:1; // Constant, value in mir_graph->constant_values[].
+ unsigned fp:1; // Floating point?
+ unsigned core:1; // Non-floating point?
+ unsigned ref:1; // Something GC cares about.
+ unsigned high_word:1; // High word of pair?
+ unsigned home:1; // Does this represent the home location?
+ uint8_t low_reg; // First physical register.
+ uint8_t high_reg; // 2nd physical register (if wide).
+ int32_t s_reg_low; // SSA name for low Dalvik word.
+ int32_t orig_sreg; // TODO: remove after Bitcode gen complete
+ // and consolodate usage w/ s_reg_low.
+};
+
+/*
+ * Collection of information describing an invoke, and the destination of
+ * the subsequent MOVE_RESULT (if applicable). Collected as a unit to enable
+ * more efficient invoke code generation.
+ */
+struct CallInfo {
+ int num_arg_words; // Note: word count, not arg count.
+ RegLocation* args; // One for each word of arguments.
+ RegLocation result; // Eventual target of MOVE_RESULT.
+ int opt_flags;
+ InvokeType type;
+ uint32_t dex_idx;
+ uint32_t index; // Method idx for invokes, type idx for FilledNewArray.
+ uintptr_t direct_code;
+ uintptr_t direct_method;
+ RegLocation target; // Target of following move_result.
+ bool skip_this;
+ bool is_range;
+ int offset; // Dalvik offset.
+};
+
+
+const RegLocation bad_loc = {kLocDalvikFrame, 0, 0, 0, 0, 0, 0, 0, 0,
+ INVALID_REG, INVALID_REG, INVALID_SREG, INVALID_SREG};
+
+class MIRGraph {
+ public:
+ MIRGraph(CompilationUnit* cu, ArenaAllocator* arena);
+ ~MIRGraph();
+
+ /*
+ * Parse dex method and add MIR at current insert point. Returns id (which is
+ * actually the index of the method in the m_units_ array).
+ */
+ void InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
+ InvokeType invoke_type, uint32_t class_def_idx,
+ uint32_t method_idx, jobject class_loader, const DexFile& dex_file);
+
+ /* Find existing block */
+ BasicBlock* FindBlock(unsigned int code_offset) {
+ return FindBlock(code_offset, false, false, NULL);
+ }
+
+ const uint16_t* GetCurrentInsns() const {
+ return current_code_item_->insns_;
+ }
+
+ const uint16_t* GetInsns(int m_unit_index) const {
+ return m_units_[m_unit_index]->GetCodeItem()->insns_;
+ }
+
+ int GetNumBlocks() const {
+ return num_blocks_;
+ }
+
+ ArenaBitVector* GetTryBlockAddr() const {
+ return try_block_addr_;
+ }
+
+ BasicBlock* GetEntryBlock() const {
+ return entry_block_;
+ }
+
+ BasicBlock* GetExitBlock() const {
+ return exit_block_;
+ }
+
+ BasicBlock* GetBasicBlock(int block_id) const {
+ return block_list_.Get(block_id);
+ }
+
+ size_t GetBasicBlockListCount() const {
+ return block_list_.Size();
+ }
+
+ GrowableArray<BasicBlock*>* GetBlockList() {
+ return &block_list_;
+ }
+
+ GrowableArray<int>* GetDfsOrder() {
+ return dfs_order_;
+ }
+
+ GrowableArray<int>* GetDfsPostOrder() {
+ return dfs_post_order_;
+ }
+
+ GrowableArray<int>* GetDomPostOrder() {
+ return dom_post_order_traversal_;
+ }
+
+ int GetDefCount() const {
+ return def_count_;
+ }
+
+ ArenaAllocator* GetArena() {
+ return arena_;
+ }
+
+ void EnableOpcodeCounting() {
+ opcode_count_ = static_cast<int*>(arena_->NewMem(kNumPackedOpcodes * sizeof(int), true,
+ ArenaAllocator::kAllocMisc));
+ }
+
+ void ShowOpcodeStats();
+
+ DexCompilationUnit* GetCurrentDexCompilationUnit() const {
+ return m_units_[current_method_];
+ }
+
+ void DumpCFG(const char* dir_prefix, bool all_blocks);
+
+ void BuildRegLocations();
+
+ void DumpRegLocTable(RegLocation* table, int count);
+
+ void BasicBlockOptimization();
+
+ bool IsConst(int32_t s_reg) const {
+ return is_constant_v_->IsBitSet(s_reg);
+ }
+
+ bool IsConst(RegLocation loc) const {
+ return (IsConst(loc.orig_sreg));
+ }
+
+ int32_t ConstantValue(RegLocation loc) const {
+ DCHECK(IsConst(loc));
+ return constant_values_[loc.orig_sreg];
+ }
+
+ int32_t ConstantValue(int32_t s_reg) const {
+ DCHECK(IsConst(s_reg));
+ return constant_values_[s_reg];
+ }
+
+ int64_t ConstantValueWide(RegLocation loc) const {
+ DCHECK(IsConst(loc));
+ return (static_cast<int64_t>(constant_values_[loc.orig_sreg + 1]) << 32) |
+ Low32Bits(static_cast<int64_t>(constant_values_[loc.orig_sreg]));
+ }
+
+ bool IsConstantNullRef(RegLocation loc) const {
+ return loc.ref && loc.is_const && (ConstantValue(loc) == 0);
+ }
+
+ int GetNumSSARegs() const {
+ return num_ssa_regs_;
+ }
+
+ void SetNumSSARegs(int new_num) {
+ num_ssa_regs_ = new_num;
+ }
+
+ unsigned int GetNumReachableBlocks() const {
+ return num_reachable_blocks_;
+ }
+
+ int GetUseCount(int vreg) const {
+ return use_counts_.Get(vreg);
+ }
+
+ int GetRawUseCount(int vreg) const {
+ return raw_use_counts_.Get(vreg);
+ }
+
+ int GetSSASubscript(int ssa_reg) const {
+ return ssa_subscripts_->Get(ssa_reg);
+ }
+
+ RegLocation GetRawSrc(MIR* mir, int num)
+ {
+ DCHECK(num < mir->ssa_rep->num_uses);
+ RegLocation res = reg_location_[mir->ssa_rep->uses[num]];
+ return res;
+ }
+
+ RegLocation GetRawDest(MIR* mir)
+ {
+ DCHECK_GT(mir->ssa_rep->num_defs, 0);
+ RegLocation res = reg_location_[mir->ssa_rep->defs[0]];
+ return res;
+ }
+
+ RegLocation GetDest(MIR* mir)
+ {
+ RegLocation res = GetRawDest(mir);
+ DCHECK(!res.wide);
+ return res;
+ }
+
+ RegLocation GetSrc(MIR* mir, int num)
+ {
+ RegLocation res = GetRawSrc(mir, num);
+ DCHECK(!res.wide);
+ return res;
+ }
+
+ RegLocation GetDestWide(MIR* mir)
+ {
+ RegLocation res = GetRawDest(mir);
+ DCHECK(res.wide);
+ return res;
+ }
+
+ RegLocation GetSrcWide(MIR* mir, int low)
+ {
+ RegLocation res = GetRawSrc(mir, low);
+ DCHECK(res.wide);
+ return res;
+ }
+
+ RegLocation GetBadLoc() {
+ return bad_loc;
+ }
+
+ int GetMethodSReg() {
+ return method_sreg_;
+ }
+
+ bool MethodIsLeaf() {
+ return attributes_ & METHOD_IS_LEAF;
+ }
+
+ RegLocation GetRegLocation(int index) {
+ DCHECK((index >= 0) && (index > num_ssa_regs_));
+ return reg_location_[index];
+ }
+
+ RegLocation GetMethodLoc() {
+ return reg_location_[method_sreg_];
+ }
+
+ void BasicBlockCombine();
+ void CodeLayout();
+ void DumpCheckStats();
+ void PropagateConstants();
+ MIR* FindMoveResult(BasicBlock* bb, MIR* mir);
+ int SRegToVReg(int ssa_reg) const;
+ void VerifyDataflow();
+ void MethodUseCount();
+ void SSATransformation();
+ void CheckForDominanceFrontier(BasicBlock* dom_bb, const BasicBlock* succ_bb);
+ void NullCheckElimination();
+ bool SetFp(int index, bool is_fp);
+ bool SetCore(int index, bool is_core);
+ bool SetRef(int index, bool is_ref);
+ bool SetWide(int index, bool is_wide);
+ bool SetHigh(int index, bool is_high);
+ void AppendMIR(BasicBlock* bb, MIR* mir);
+ void PrependMIR(BasicBlock* bb, MIR* mir);
+ void InsertMIRAfter(BasicBlock* bb, MIR* current_mir, MIR* new_mir);
+ char* GetDalvikDisassembly(const MIR* mir);
+ void ReplaceSpecialChars(std::string& str);
+ std::string GetSSAName(int ssa_reg);
+ std::string GetSSANameWithConst(int ssa_reg, bool singles_only);
+ void GetBlockName(BasicBlock* bb, char* name);
+ const char* GetShortyFromTargetIdx(int);
+ void DumpMIRGraph();
+ CallInfo* NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, bool is_range);
+ BasicBlock* NewMemBB(BBType block_type, int block_id);
+
+ /*
+ * IsDebugBuild sanity check: keep track of the Dex PCs for catch entries so that later on
+ * we can verify that all catch entries have native PC entries.
+ */
+ std::set<uint32_t> catches_;
+
+ // TODO: make these private.
+ RegLocation* reg_location_; // Map SSA names to location.
+ GrowableArray<CompilerTemp*> compiler_temps_;
+ SafeMap<unsigned int, unsigned int> block_id_map_; // Block collapse lookup cache.
+
+ static const int oat_data_flow_attributes_[kMirOpLast];
+ static const char* extended_mir_op_names_[kMirOpLast - kMirOpFirst];
+
+ private:
+
+ int FindCommonParent(int block1, int block2);
+ void ComputeSuccLineIn(ArenaBitVector* dest, const ArenaBitVector* src1,
+ const ArenaBitVector* src2);
+ void HandleLiveInUse(ArenaBitVector* use_v, ArenaBitVector* def_v,
+ ArenaBitVector* live_in_v, int dalvik_reg_id);
+ void HandleDef(ArenaBitVector* def_v, int dalvik_reg_id);
+ void CompilerInitializeSSAConversion();
+ bool DoSSAConversion(BasicBlock* bb);
+ bool InvokeUsesMethodStar(MIR* mir);
+ int ParseInsn(const uint16_t* code_ptr, DecodedInstruction* decoded_instruction);
+ bool ContentIsInsn(const uint16_t* code_ptr);
+ BasicBlock* SplitBlock(unsigned int code_offset, BasicBlock* orig_block,
+ BasicBlock** immed_pred_block_p);
+ BasicBlock* FindBlock(unsigned int code_offset, bool split, bool create,
+ BasicBlock** immed_pred_block_p);
+ void ProcessTryCatchBlocks();
+ BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
+ int flags, const uint16_t* code_ptr, const uint16_t* code_end);
+ void ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, int flags);
+ BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
+ int flags, ArenaBitVector* try_block_addr, const uint16_t* code_ptr,
+ const uint16_t* code_end);
+ int AddNewSReg(int v_reg);
+ void HandleSSAUse(int* uses, int dalvik_reg, int reg_index);
+ void HandleSSADef(int* defs, int dalvik_reg, int reg_index);
+ void DataFlowSSAFormat35C(MIR* mir);
+ void DataFlowSSAFormat3RC(MIR* mir);
+ bool FindLocalLiveIn(BasicBlock* bb);
+ void ClearAllVisitedFlags();
+ bool CountUses(struct BasicBlock* bb);
+ bool InferTypeAndSize(BasicBlock* bb);
+ bool VerifyPredInfo(BasicBlock* bb);
+ BasicBlock* NeedsVisit(BasicBlock* bb);
+ BasicBlock* NextUnvisitedSuccessor(BasicBlock* bb);
+ void MarkPreOrder(BasicBlock* bb);
+ void RecordDFSOrders(BasicBlock* bb);
+ void ComputeDFSOrders();
+ void ComputeDefBlockMatrix();
+ void ComputeDomPostOrderTraversal(BasicBlock* bb);
+ void ComputeDominators();
+ void InsertPhiNodes();
+ void DoDFSPreOrderSSARename(BasicBlock* block);
+ void SetConstant(int32_t ssa_reg, int value);
+ void SetConstantWide(int ssa_reg, int64_t value);
+ int GetSSAUseCount(int s_reg);
+ bool BasicBlockOpt(BasicBlock* bb);
+ bool EliminateNullChecks(BasicBlock* bb);
+ void NullCheckEliminationInit(BasicBlock* bb);
+ bool BuildExtendedBBList(struct BasicBlock* bb);
+ bool FillDefBlockMatrix(BasicBlock* bb);
+ void InitializeDominationInfo(BasicBlock* bb);
+ bool ComputeblockIDom(BasicBlock* bb);
+ bool ComputeBlockDominators(BasicBlock* bb);
+ bool SetDominators(BasicBlock* bb);
+ bool ComputeBlockLiveIns(BasicBlock* bb);
+ bool InsertPhiNodeOperands(BasicBlock* bb);
+ bool ComputeDominanceFrontier(BasicBlock* bb);
+ void DoConstantPropogation(BasicBlock* bb);
+ void CountChecks(BasicBlock* bb);
+ bool CombineBlocks(BasicBlock* bb);
+
+ CompilationUnit* const cu_;
+ GrowableArray<int>* ssa_base_vregs_;
+ GrowableArray<int>* ssa_subscripts_;
+ // Map original Dalvik virtual reg i to the current SSA name.
+ int* vreg_to_ssa_map_; // length == method->registers_size
+ int* ssa_last_defs_; // length == method->registers_size
+ ArenaBitVector* is_constant_v_; // length == num_ssa_reg
+ int* constant_values_; // length == num_ssa_reg
+ // Use counts of ssa names.
+ GrowableArray<uint32_t> use_counts_; // Weighted by nesting depth
+ GrowableArray<uint32_t> raw_use_counts_; // Not weighted
+ unsigned int num_reachable_blocks_;
+ GrowableArray<int>* dfs_order_;
+ GrowableArray<int>* dfs_post_order_;
+ GrowableArray<int>* dom_post_order_traversal_;
+ int* i_dom_list_;
+ ArenaBitVector** def_block_matrix_; // num_dalvik_register x num_blocks.
+ ArenaBitVector* temp_block_v_;
+ ArenaBitVector* temp_dalvik_register_v_;
+ ArenaBitVector* temp_ssa_register_v_; // num_ssa_regs.
+ static const int kInvalidEntry = -1;
+ GrowableArray<BasicBlock*> block_list_;
+ ArenaBitVector* try_block_addr_;
+ BasicBlock* entry_block_;
+ BasicBlock* exit_block_;
+ BasicBlock* cur_block_;
+ int num_blocks_;
+ const DexFile::CodeItem* current_code_item_;
+ SafeMap<unsigned int, BasicBlock*> block_map_; // FindBlock lookup cache.
+ std::vector<DexCompilationUnit*> m_units_; // List of methods included in this graph
+ typedef std::pair<int, int> MIRLocation; // Insert point, (m_unit_ index, offset)
+ std::vector<MIRLocation> method_stack_; // Include stack
+ int current_method_;
+ int current_offset_;
+ int def_count_; // Used to estimate size of ssa name storage.
+ int* opcode_count_; // Dex opcode coverage stats.
+ int num_ssa_regs_; // Number of names following SSA transformation.
+ std::vector<BasicBlock*> extended_basic_blocks_; // Heads of block "traces".
+ int method_sreg_;
+ unsigned int attributes_;
+ Checkstats* checkstats_;
+ ArenaAllocator* arena_;
+};
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_MIRGRAPH_H_
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
new file mode 100644
index 0000000..6b8f3f0
--- /dev/null
+++ b/compiler/dex/mir_optimization.cc
@@ -0,0 +1,893 @@
+/*
+ * Copyright (C) 2011 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 "compiler_internals.h"
+#include "local_value_numbering.h"
+#include "dataflow_iterator-inl.h"
+
+namespace art {
+
+static unsigned int Predecessors(BasicBlock* bb)
+{
+ return bb->predecessors->Size();
+}
+
+/* Setup a constant value for opcodes thare have the DF_SETS_CONST attribute */
+void MIRGraph::SetConstant(int32_t ssa_reg, int value)
+{
+ is_constant_v_->SetBit(ssa_reg);
+ constant_values_[ssa_reg] = value;
+}
+
+void MIRGraph::SetConstantWide(int ssa_reg, int64_t value)
+{
+ is_constant_v_->SetBit(ssa_reg);
+ constant_values_[ssa_reg] = Low32Bits(value);
+ constant_values_[ssa_reg + 1] = High32Bits(value);
+}
+
+void MIRGraph::DoConstantPropogation(BasicBlock* bb)
+{
+ MIR* mir;
+
+ for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
+
+ DecodedInstruction *d_insn = &mir->dalvikInsn;
+
+ if (!(df_attributes & DF_HAS_DEFS)) continue;
+
+ /* Handle instructions that set up constants directly */
+ if (df_attributes & DF_SETS_CONST) {
+ if (df_attributes & DF_DA) {
+ int32_t vB = static_cast<int32_t>(d_insn->vB);
+ switch (d_insn->opcode) {
+ case Instruction::CONST_4:
+ case Instruction::CONST_16:
+ case Instruction::CONST:
+ SetConstant(mir->ssa_rep->defs[0], vB);
+ break;
+ case Instruction::CONST_HIGH16:
+ SetConstant(mir->ssa_rep->defs[0], vB << 16);
+ break;
+ case Instruction::CONST_WIDE_16:
+ case Instruction::CONST_WIDE_32:
+ SetConstantWide(mir->ssa_rep->defs[0], static_cast<int64_t>(vB));
+ break;
+ case Instruction::CONST_WIDE:
+ SetConstantWide(mir->ssa_rep->defs[0],d_insn->vB_wide);
+ break;
+ case Instruction::CONST_WIDE_HIGH16:
+ SetConstantWide(mir->ssa_rep->defs[0], static_cast<int64_t>(vB) << 48);
+ break;
+ default:
+ break;
+ }
+ }
+ /* Handle instructions that set up constants directly */
+ } else if (df_attributes & DF_IS_MOVE) {
+ int i;
+
+ for (i = 0; i < mir->ssa_rep->num_uses; i++) {
+ if (!is_constant_v_->IsBitSet(mir->ssa_rep->uses[i])) break;
+ }
+ /* Move a register holding a constant to another register */
+ if (i == mir->ssa_rep->num_uses) {
+ SetConstant(mir->ssa_rep->defs[0], constant_values_[mir->ssa_rep->uses[0]]);
+ if (df_attributes & DF_A_WIDE) {
+ SetConstant(mir->ssa_rep->defs[1], constant_values_[mir->ssa_rep->uses[1]]);
+ }
+ }
+ }
+ }
+ /* TODO: implement code to handle arithmetic operations */
+}
+
+void MIRGraph::PropagateConstants()
+{
+ is_constant_v_ = new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false);
+ constant_values_ = static_cast<int*>(arena_->NewMem(sizeof(int) * GetNumSSARegs(), true,
+ ArenaAllocator::kAllocDFInfo));
+ AllNodesIterator iter(this, false /* not iterative */);
+ for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
+ DoConstantPropogation(bb);
+ }
+}
+
+/* Advance to next strictly dominated MIR node in an extended basic block */
+static MIR* AdvanceMIR(BasicBlock** p_bb, MIR* mir)
+{
+ BasicBlock* bb = *p_bb;
+ if (mir != NULL) {
+ mir = mir->next;
+ if (mir == NULL) {
+ bb = bb->fall_through;
+ if ((bb == NULL) || Predecessors(bb) != 1) {
+ mir = NULL;
+ } else {
+ *p_bb = bb;
+ mir = bb->first_mir_insn;
+ }
+ }
+ }
+ return mir;
+}
+
+/*
+ * To be used at an invoke mir. If the logically next mir node represents
+ * a move-result, return it. Else, return NULL. If a move-result exists,
+ * it is required to immediately follow the invoke with no intervening
+ * opcodes or incoming arcs. However, if the result of the invoke is not
+ * used, a move-result may not be present.
+ */
+MIR* MIRGraph::FindMoveResult(BasicBlock* bb, MIR* mir)
+{
+ BasicBlock* tbb = bb;
+ mir = AdvanceMIR(&tbb, mir);
+ while (mir != NULL) {
+ int opcode = mir->dalvikInsn.opcode;
+ if ((mir->dalvikInsn.opcode == Instruction::MOVE_RESULT) ||
+ (mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) ||
+ (mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE)) {
+ break;
+ }
+ // Keep going if pseudo op, otherwise terminate
+ if (opcode < kNumPackedOpcodes) {
+ mir = NULL;
+ } else {
+ mir = AdvanceMIR(&tbb, mir);
+ }
+ }
+ return mir;
+}
+
+static BasicBlock* NextDominatedBlock(BasicBlock* bb)
+{
+ if (bb->block_type == kDead) {
+ return NULL;
+ }
+ DCHECK((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode)
+ || (bb->block_type == kExitBlock));
+ bb = bb->fall_through;
+ if (bb == NULL || (Predecessors(bb) != 1)) {
+ return NULL;
+ }
+ DCHECK((bb->block_type == kDalvikByteCode) || (bb->block_type == kExitBlock));
+ return bb;
+}
+
+static MIR* FindPhi(BasicBlock* bb, int ssa_name)
+{
+ for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ if (static_cast<int>(mir->dalvikInsn.opcode) == kMirOpPhi) {
+ for (int i = 0; i < mir->ssa_rep->num_uses; i++) {
+ if (mir->ssa_rep->uses[i] == ssa_name) {
+ return mir;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+static SelectInstructionKind SelectKind(MIR* mir)
+{
+ switch (mir->dalvikInsn.opcode) {
+ case Instruction::MOVE:
+ case Instruction::MOVE_OBJECT:
+ case Instruction::MOVE_16:
+ case Instruction::MOVE_OBJECT_16:
+ case Instruction::MOVE_FROM16:
+ case Instruction::MOVE_OBJECT_FROM16:
+ return kSelectMove;
+ case Instruction::CONST:
+ case Instruction::CONST_4:
+ case Instruction::CONST_16:
+ return kSelectConst;
+ case Instruction::GOTO:
+ case Instruction::GOTO_16:
+ case Instruction::GOTO_32:
+ return kSelectGoto;
+ default:;
+ }
+ return kSelectNone;
+}
+
+int MIRGraph::GetSSAUseCount(int s_reg)
+{
+ return raw_use_counts_.Get(s_reg);
+}
+
+
+/* Do some MIR-level extended basic block optimizations */
+bool MIRGraph::BasicBlockOpt(BasicBlock* bb)
+{
+ if (bb->block_type == kDead) {
+ return true;
+ }
+ int num_temps = 0;
+ LocalValueNumbering local_valnum(cu_);
+ while (bb != NULL) {
+ for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ // TUNING: use the returned value number for CSE.
+ local_valnum.GetValueNumber(mir);
+ // Look for interesting opcodes, skip otherwise
+ Instruction::Code opcode = mir->dalvikInsn.opcode;
+ switch (opcode) {
+ case Instruction::CMPL_FLOAT:
+ case Instruction::CMPL_DOUBLE:
+ case Instruction::CMPG_FLOAT:
+ case Instruction::CMPG_DOUBLE:
+ case Instruction::CMP_LONG:
+ if ((cu_->disable_opt & (1 << kBranchFusing)) != 0) {
+ // Bitcode doesn't allow this optimization.
+ break;
+ }
+ if (mir->next != NULL) {
+ MIR* mir_next = mir->next;
+ Instruction::Code br_opcode = mir_next->dalvikInsn.opcode;
+ ConditionCode ccode = kCondNv;
+ switch(br_opcode) {
+ case Instruction::IF_EQZ:
+ ccode = kCondEq;
+ break;
+ case Instruction::IF_NEZ:
+ ccode = kCondNe;
+ break;
+ case Instruction::IF_LTZ:
+ ccode = kCondLt;
+ break;
+ case Instruction::IF_GEZ:
+ ccode = kCondGe;
+ break;
+ case Instruction::IF_GTZ:
+ ccode = kCondGt;
+ break;
+ case Instruction::IF_LEZ:
+ ccode = kCondLe;
+ break;
+ default:
+ break;
+ }
+ // Make sure result of cmp is used by next insn and nowhere else
+ if ((ccode != kCondNv) &&
+ (mir->ssa_rep->defs[0] == mir_next->ssa_rep->uses[0]) &&
+ (GetSSAUseCount(mir->ssa_rep->defs[0]) == 1)) {
+ mir_next->dalvikInsn.arg[0] = ccode;
+ switch(opcode) {
+ case Instruction::CMPL_FLOAT:
+ mir_next->dalvikInsn.opcode =
+ static_cast<Instruction::Code>(kMirOpFusedCmplFloat);
+ break;
+ case Instruction::CMPL_DOUBLE:
+ mir_next->dalvikInsn.opcode =
+ static_cast<Instruction::Code>(kMirOpFusedCmplDouble);
+ break;
+ case Instruction::CMPG_FLOAT:
+ mir_next->dalvikInsn.opcode =
+ static_cast<Instruction::Code>(kMirOpFusedCmpgFloat);
+ break;
+ case Instruction::CMPG_DOUBLE:
+ mir_next->dalvikInsn.opcode =
+ static_cast<Instruction::Code>(kMirOpFusedCmpgDouble);
+ break;
+ case Instruction::CMP_LONG:
+ mir_next->dalvikInsn.opcode =
+ static_cast<Instruction::Code>(kMirOpFusedCmpLong);
+ break;
+ default: LOG(ERROR) << "Unexpected opcode: " << opcode;
+ }
+ mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+ mir_next->ssa_rep->num_uses = mir->ssa_rep->num_uses;
+ mir_next->ssa_rep->uses = mir->ssa_rep->uses;
+ mir_next->ssa_rep->fp_use = mir->ssa_rep->fp_use;
+ mir_next->ssa_rep->num_defs = 0;
+ mir->ssa_rep->num_uses = 0;
+ mir->ssa_rep->num_defs = 0;
+ }
+ }
+ break;
+ case Instruction::GOTO:
+ case Instruction::GOTO_16:
+ case Instruction::GOTO_32:
+ case Instruction::IF_EQ:
+ case Instruction::IF_NE:
+ case Instruction::IF_LT:
+ case Instruction::IF_GE:
+ case Instruction::IF_GT:
+ case Instruction::IF_LE:
+ case Instruction::IF_EQZ:
+ case Instruction::IF_NEZ:
+ case Instruction::IF_LTZ:
+ case Instruction::IF_GEZ:
+ case Instruction::IF_GTZ:
+ case Instruction::IF_LEZ:
+ if (bb->taken->dominates_return) {
+ mir->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK;
+ if (cu_->verbose) {
+ LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex << mir->offset;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ // Is this the select pattern?
+ // TODO: flesh out support for Mips and X86. NOTE: llvm's select op doesn't quite work here.
+ // TUNING: expand to support IF_xx compare & branches
+ if (!(cu_->compiler_backend == kPortable) && (cu_->instruction_set == kThumb2) &&
+ ((mir->dalvikInsn.opcode == Instruction::IF_EQZ) ||
+ (mir->dalvikInsn.opcode == Instruction::IF_NEZ))) {
+ BasicBlock* ft = bb->fall_through;
+ DCHECK(ft != NULL);
+ BasicBlock* ft_ft = ft->fall_through;
+ BasicBlock* ft_tk = ft->taken;
+
+ BasicBlock* tk = bb->taken;
+ DCHECK(tk != NULL);
+ BasicBlock* tk_ft = tk->fall_through;
+ BasicBlock* tk_tk = tk->taken;
+
+ /*
+ * In the select pattern, the taken edge goes to a block that unconditionally
+ * transfers to the rejoin block and the fall_though edge goes to a block that
+ * unconditionally falls through to the rejoin block.
+ */
+ if ((tk_ft == NULL) && (ft_tk == NULL) && (tk_tk == ft_ft) &&
+ (Predecessors(tk) == 1) && (Predecessors(ft) == 1)) {
+ /*
+ * Okay - we have the basic diamond shape. At the very least, we can eliminate the
+ * suspend check on the taken-taken branch back to the join point.
+ */
+ if (SelectKind(tk->last_mir_insn) == kSelectGoto) {
+ tk->last_mir_insn->optimization_flags |= (MIR_IGNORE_SUSPEND_CHECK);
+ }
+ // Are the block bodies something we can handle?
+ if ((ft->first_mir_insn == ft->last_mir_insn) &&
+ (tk->first_mir_insn != tk->last_mir_insn) &&
+ (tk->first_mir_insn->next == tk->last_mir_insn) &&
+ ((SelectKind(ft->first_mir_insn) == kSelectMove) ||
+ (SelectKind(ft->first_mir_insn) == kSelectConst)) &&
+ (SelectKind(ft->first_mir_insn) == SelectKind(tk->first_mir_insn)) &&
+ (SelectKind(tk->last_mir_insn) == kSelectGoto)) {
+ // Almost there. Are the instructions targeting the same vreg?
+ MIR* if_true = tk->first_mir_insn;
+ MIR* if_false = ft->first_mir_insn;
+ // It's possible that the target of the select isn't used - skip those (rare) cases.
+ MIR* phi = FindPhi(tk_tk, if_true->ssa_rep->defs[0]);
+ if ((phi != NULL) && (if_true->dalvikInsn.vA == if_false->dalvikInsn.vA)) {
+ /*
+ * We'll convert the IF_EQZ/IF_NEZ to a SELECT. We need to find the
+ * Phi node in the merge block and delete it (while using the SSA name
+ * of the merge as the target of the SELECT. Delete both taken and
+ * fallthrough blocks, and set fallthrough to merge block.
+ * NOTE: not updating other dataflow info (no longer used at this point).
+ * If this changes, need to update i_dom, etc. here (and in CombineBlocks).
+ */
+ if (opcode == Instruction::IF_NEZ) {
+ // Normalize.
+ MIR* tmp_mir = if_true;
+ if_true = if_false;
+ if_false = tmp_mir;
+ }
+ mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpSelect);
+ bool const_form = (SelectKind(if_true) == kSelectConst);
+ if ((SelectKind(if_true) == kSelectMove)) {
+ if (IsConst(if_true->ssa_rep->uses[0]) &&
+ IsConst(if_false->ssa_rep->uses[0])) {
+ const_form = true;
+ if_true->dalvikInsn.vB = ConstantValue(if_true->ssa_rep->uses[0]);
+ if_false->dalvikInsn.vB = ConstantValue(if_false->ssa_rep->uses[0]);
+ }
+ }
+ if (const_form) {
+ // "true" set val in vB
+ mir->dalvikInsn.vB = if_true->dalvikInsn.vB;
+ // "false" set val in vC
+ mir->dalvikInsn.vC = if_false->dalvikInsn.vB;
+ } else {
+ DCHECK_EQ(SelectKind(if_true), kSelectMove);
+ DCHECK_EQ(SelectKind(if_false), kSelectMove);
+ int* src_ssa =
+ static_cast<int*>(arena_->NewMem(sizeof(int) * 3, false,
+ ArenaAllocator::kAllocDFInfo));
+ src_ssa[0] = mir->ssa_rep->uses[0];
+ src_ssa[1] = if_true->ssa_rep->uses[0];
+ src_ssa[2] = if_false->ssa_rep->uses[0];
+ mir->ssa_rep->uses = src_ssa;
+ mir->ssa_rep->num_uses = 3;
+ }
+ mir->ssa_rep->num_defs = 1;
+ mir->ssa_rep->defs =
+ static_cast<int*>(arena_->NewMem(sizeof(int) * 1, false,
+ ArenaAllocator::kAllocDFInfo));
+ mir->ssa_rep->fp_def =
+ static_cast<bool*>(arena_->NewMem(sizeof(bool) * 1, false,
+ ArenaAllocator::kAllocDFInfo));
+ mir->ssa_rep->fp_def[0] = if_true->ssa_rep->fp_def[0];
+ // Match type of uses to def.
+ mir->ssa_rep->fp_use =
+ static_cast<bool*>(arena_->NewMem(sizeof(bool) * mir->ssa_rep->num_uses, false,
+ ArenaAllocator::kAllocDFInfo));
+ for (int i = 0; i < mir->ssa_rep->num_uses; i++) {
+ mir->ssa_rep->fp_use[i] = mir->ssa_rep->fp_def[0];
+ }
+ /*
+ * There is usually a Phi node in the join block for our two cases. If the
+ * Phi node only contains our two cases as input, we will use the result
+ * SSA name of the Phi node as our select result and delete the Phi. If
+ * the Phi node has more than two operands, we will arbitrarily use the SSA
+ * name of the "true" path, delete the SSA name of the "false" path from the
+ * Phi node (and fix up the incoming arc list).
+ */
+ if (phi->ssa_rep->num_uses == 2) {
+ mir->ssa_rep->defs[0] = phi->ssa_rep->defs[0];
+ phi->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+ } else {
+ int dead_def = if_false->ssa_rep->defs[0];
+ int live_def = if_true->ssa_rep->defs[0];
+ mir->ssa_rep->defs[0] = live_def;
+ int* incoming = reinterpret_cast<int*>(phi->dalvikInsn.vB);
+ for (int i = 0; i < phi->ssa_rep->num_uses; i++) {
+ if (phi->ssa_rep->uses[i] == live_def) {
+ incoming[i] = bb->id;
+ }
+ }
+ for (int i = 0; i < phi->ssa_rep->num_uses; i++) {
+ if (phi->ssa_rep->uses[i] == dead_def) {
+ int last_slot = phi->ssa_rep->num_uses - 1;
+ phi->ssa_rep->uses[i] = phi->ssa_rep->uses[last_slot];
+ incoming[i] = incoming[last_slot];
+ }
+ }
+ }
+ phi->ssa_rep->num_uses--;
+ bb->taken = NULL;
+ tk->block_type = kDead;
+ for (MIR* tmir = ft->first_mir_insn; tmir != NULL; tmir = tmir->next) {
+ tmir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+ }
+ }
+ }
+ }
+ }
+ }
+ bb = NextDominatedBlock(bb);
+ }
+
+ if (num_temps > cu_->num_compiler_temps) {
+ cu_->num_compiler_temps = num_temps;
+ }
+ return true;
+}
+
+void MIRGraph::NullCheckEliminationInit(struct BasicBlock* bb)
+{
+ if (bb->data_flow_info != NULL) {
+ bb->data_flow_info->ending_null_check_v =
+ new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false, kBitMapNullCheck);
+ }
+}
+
+/* Collect stats on number of checks removed */
+void MIRGraph::CountChecks(struct BasicBlock* bb)
+{
+ if (bb->data_flow_info != NULL) {
+ for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ if (mir->ssa_rep == NULL) {
+ continue;
+ }
+ int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
+ if (df_attributes & DF_HAS_NULL_CHKS) {
+ checkstats_->null_checks++;
+ if (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) {
+ checkstats_->null_checks_eliminated++;
+ }
+ }
+ if (df_attributes & DF_HAS_RANGE_CHKS) {
+ checkstats_->range_checks++;
+ if (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) {
+ checkstats_->range_checks_eliminated++;
+ }
+ }
+ }
+ }
+}
+
+/* Try to make common case the fallthrough path */
+static bool LayoutBlocks(struct BasicBlock* bb)
+{
+ // TODO: For now, just looking for direct throws. Consider generalizing for profile feedback
+ if (!bb->explicit_throw) {
+ return false;
+ }
+ BasicBlock* walker = bb;
+ while (true) {
+ // Check termination conditions
+ if ((walker->block_type == kEntryBlock) || (Predecessors(walker) != 1)) {
+ break;
+ }
+ BasicBlock* prev = walker->predecessors->Get(0);
+ if (prev->conditional_branch) {
+ if (prev->fall_through == walker) {
+ // Already done - return
+ break;
+ }
+ DCHECK_EQ(walker, prev->taken);
+ // Got one. Flip it and exit
+ Instruction::Code opcode = prev->last_mir_insn->dalvikInsn.opcode;
+ switch (opcode) {
+ case Instruction::IF_EQ: opcode = Instruction::IF_NE; break;
+ case Instruction::IF_NE: opcode = Instruction::IF_EQ; break;
+ case Instruction::IF_LT: opcode = Instruction::IF_GE; break;
+ case Instruction::IF_GE: opcode = Instruction::IF_LT; break;
+ case Instruction::IF_GT: opcode = Instruction::IF_LE; break;
+ case Instruction::IF_LE: opcode = Instruction::IF_GT; break;
+ case Instruction::IF_EQZ: opcode = Instruction::IF_NEZ; break;
+ case Instruction::IF_NEZ: opcode = Instruction::IF_EQZ; break;
+ case Instruction::IF_LTZ: opcode = Instruction::IF_GEZ; break;
+ case Instruction::IF_GEZ: opcode = Instruction::IF_LTZ; break;
+ case Instruction::IF_GTZ: opcode = Instruction::IF_LEZ; break;
+ case Instruction::IF_LEZ: opcode = Instruction::IF_GTZ; break;
+ default: LOG(FATAL) << "Unexpected opcode " << opcode;
+ }
+ prev->last_mir_insn->dalvikInsn.opcode = opcode;
+ BasicBlock* t_bb = prev->taken;
+ prev->taken = prev->fall_through;
+ prev->fall_through = t_bb;
+ break;
+ }
+ walker = prev;
+ }
+ return false;
+}
+
+/* Combine any basic blocks terminated by instructions that we now know can't throw */
+bool MIRGraph::CombineBlocks(struct BasicBlock* bb)
+{
+ // Loop here to allow combining a sequence of blocks
+ while (true) {
+ // Check termination conditions
+ if ((bb->first_mir_insn == NULL)
+ || (bb->data_flow_info == NULL)
+ || (bb->block_type == kExceptionHandling)
+ || (bb->block_type == kExitBlock)
+ || (bb->block_type == kDead)
+ || ((bb->taken == NULL) || (bb->taken->block_type != kExceptionHandling))
+ || (bb->successor_block_list.block_list_type != kNotUsed)
+ || (static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) != kMirOpCheck)) {
+ break;
+ }
+
+ // Test the kMirOpCheck instruction
+ MIR* mir = bb->last_mir_insn;
+ // Grab the attributes from the paired opcode
+ MIR* throw_insn = mir->meta.throw_insn;
+ int df_attributes = oat_data_flow_attributes_[throw_insn->dalvikInsn.opcode];
+ bool can_combine = true;
+ if (df_attributes & DF_HAS_NULL_CHKS) {
+ can_combine &= ((throw_insn->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0);
+ }
+ if (df_attributes & DF_HAS_RANGE_CHKS) {
+ can_combine &= ((throw_insn->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0);
+ }
+ if (!can_combine) {
+ break;
+ }
+ // OK - got one. Combine
+ BasicBlock* bb_next = bb->fall_through;
+ DCHECK(!bb_next->catch_entry);
+ DCHECK_EQ(Predecessors(bb_next), 1U);
+ MIR* t_mir = bb->last_mir_insn->prev;
+ // Overwrite the kOpCheck insn with the paired opcode
+ DCHECK_EQ(bb_next->first_mir_insn, throw_insn);
+ *bb->last_mir_insn = *throw_insn;
+ bb->last_mir_insn->prev = t_mir;
+ // Use the successor info from the next block
+ bb->successor_block_list = bb_next->successor_block_list;
+ // Use the ending block linkage from the next block
+ bb->fall_through = bb_next->fall_through;
+ bb->taken->block_type = kDead; // Kill the unused exception block
+ bb->taken = bb_next->taken;
+ // Include the rest of the instructions
+ bb->last_mir_insn = bb_next->last_mir_insn;
+ /*
+ * If lower-half of pair of blocks to combine contained a return, move the flag
+ * to the newly combined block.
+ */
+ bb->terminated_by_return = bb_next->terminated_by_return;
+
+ /*
+ * NOTE: we aren't updating all dataflow info here. Should either make sure this pass
+ * happens after uses of i_dominated, dom_frontier or update the dataflow info here.
+ */
+
+ // Kill bb_next and remap now-dead id to parent
+ bb_next->block_type = kDead;
+ block_id_map_.Overwrite(bb_next->id, bb->id);
+
+ // Now, loop back and see if we can keep going
+ }
+ return false;
+}
+
+/* Eliminate unnecessary null checks for a basic block. */
+bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb)
+{
+ if (bb->data_flow_info == NULL) return false;
+
+ /*
+ * Set initial state. Be conservative with catch
+ * blocks and start with no assumptions about null check
+ * status (except for "this").
+ */
+ if ((bb->block_type == kEntryBlock) | bb->catch_entry) {
+ temp_ssa_register_v_->ClearAllBits();
+ if ((cu_->access_flags & kAccStatic) == 0) {
+ // If non-static method, mark "this" as non-null
+ int this_reg = cu_->num_dalvik_registers - cu_->num_ins;
+ temp_ssa_register_v_->SetBit(this_reg);
+ }
+ } else if (bb->predecessors->Size() == 1) {
+ BasicBlock* pred_bb = bb->predecessors->Get(0);
+ temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v);
+ if (pred_bb->block_type == kDalvikByteCode) {
+ // Check to see if predecessor had an explicit null-check.
+ MIR* last_insn = pred_bb->last_mir_insn;
+ Instruction::Code last_opcode = last_insn->dalvikInsn.opcode;
+ if (last_opcode == Instruction::IF_EQZ) {
+ if (pred_bb->fall_through == bb) {
+ // The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that
+ // it can't be null.
+ temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]);
+ }
+ } else if (last_opcode == Instruction::IF_NEZ) {
+ if (pred_bb->taken == bb) {
+ // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be
+ // null.
+ temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]);
+ }
+ }
+ }
+ } else {
+ // Starting state is intersection of all incoming arcs
+ GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors);
+ BasicBlock* pred_bb = iter.Next();
+ DCHECK(pred_bb != NULL);
+ temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v);
+ while (true) {
+ pred_bb = iter.Next();
+ if (!pred_bb) break;
+ if ((pred_bb->data_flow_info == NULL) ||
+ (pred_bb->data_flow_info->ending_null_check_v == NULL)) {
+ continue;
+ }
+ temp_ssa_register_v_->Intersect(pred_bb->data_flow_info->ending_null_check_v);
+ }
+ }
+
+ // Walk through the instruction in the block, updating as necessary
+ for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ if (mir->ssa_rep == NULL) {
+ continue;
+ }
+ int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
+
+ // Mark target of NEW* as non-null
+ if (df_attributes & DF_NON_NULL_DST) {
+ temp_ssa_register_v_->SetBit(mir->ssa_rep->defs[0]);
+ }
+
+ // Mark non-null returns from invoke-style NEW*
+ if (df_attributes & DF_NON_NULL_RET) {
+ MIR* next_mir = mir->next;
+ // Next should be an MOVE_RESULT_OBJECT
+ if (next_mir &&
+ next_mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
+ // Mark as null checked
+ temp_ssa_register_v_->SetBit(next_mir->ssa_rep->defs[0]);
+ } else {
+ if (next_mir) {
+ LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode;
+ } else if (bb->fall_through) {
+ // Look in next basic block
+ struct BasicBlock* next_bb = bb->fall_through;
+ for (MIR* tmir = next_bb->first_mir_insn; tmir != NULL;
+ tmir =tmir->next) {
+ if (static_cast<int>(tmir->dalvikInsn.opcode) >= static_cast<int>(kMirOpFirst)) {
+ continue;
+ }
+ // First non-pseudo should be MOVE_RESULT_OBJECT
+ if (tmir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
+ // Mark as null checked
+ temp_ssa_register_v_->SetBit(tmir->ssa_rep->defs[0]);
+ } else {
+ LOG(WARNING) << "Unexpected op after new: " << tmir->dalvikInsn.opcode;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * Propagate nullcheck state on register copies (including
+ * Phi pseudo copies. For the latter, nullcheck state is
+ * the "and" of all the Phi's operands.
+ */
+ if (df_attributes & (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)) {
+ int tgt_sreg = mir->ssa_rep->defs[0];
+ int operands = (df_attributes & DF_NULL_TRANSFER_0) ? 1 :
+ mir->ssa_rep->num_uses;
+ bool null_checked = true;
+ for (int i = 0; i < operands; i++) {
+ null_checked &= temp_ssa_register_v_->IsBitSet(mir->ssa_rep->uses[i]);
+ }
+ if (null_checked) {
+ temp_ssa_register_v_->SetBit(tgt_sreg);
+ }
+ }
+
+ // Already nullchecked?
+ if ((df_attributes & DF_HAS_NULL_CHKS) && !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) {
+ int src_idx;
+ if (df_attributes & DF_NULL_CHK_1) {
+ src_idx = 1;
+ } else if (df_attributes & DF_NULL_CHK_2) {
+ src_idx = 2;
+ } else {
+ src_idx = 0;
+ }
+ int src_sreg = mir->ssa_rep->uses[src_idx];
+ if (temp_ssa_register_v_->IsBitSet(src_sreg)) {
+ // Eliminate the null check
+ mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+ } else {
+ // Mark s_reg as null-checked
+ temp_ssa_register_v_->SetBit(src_sreg);
+ }
+ }
+ }
+
+ // Did anything change?
+ bool changed = !temp_ssa_register_v_->Equal(bb->data_flow_info->ending_null_check_v);
+ if (changed) {
+ bb->data_flow_info->ending_null_check_v->Copy(temp_ssa_register_v_);
+ }
+ return changed;
+}
+
+void MIRGraph::NullCheckElimination()
+{
+ if (!(cu_->disable_opt & (1 << kNullCheckElimination))) {
+ DCHECK(temp_ssa_register_v_ != NULL);
+ AllNodesIterator iter(this, false /* not iterative */);
+ for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
+ NullCheckEliminationInit(bb);
+ }
+ PreOrderDfsIterator iter2(this, true /* iterative */);
+ bool change = false;
+ for (BasicBlock* bb = iter2.Next(change); bb != NULL; bb = iter2.Next(change)) {
+ change = EliminateNullChecks(bb);
+ }
+ }
+ if (cu_->enable_debug & (1 << kDebugDumpCFG)) {
+ DumpCFG("/sdcard/4_post_nce_cfg/", false);
+ }
+}
+
+void MIRGraph::BasicBlockCombine()
+{
+ PreOrderDfsIterator iter(this, false /* not iterative */);
+ for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
+ CombineBlocks(bb);
+ }
+ if (cu_->enable_debug & (1 << kDebugDumpCFG)) {
+ DumpCFG("/sdcard/5_post_bbcombine_cfg/", false);
+ }
+}
+
+void MIRGraph::CodeLayout()
+{
+ if (cu_->enable_debug & (1 << kDebugVerifyDataflow)) {
+ VerifyDataflow();
+ }
+ AllNodesIterator iter(this, false /* not iterative */);
+ for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
+ LayoutBlocks(bb);
+ }
+ if (cu_->enable_debug & (1 << kDebugDumpCFG)) {
+ DumpCFG("/sdcard/2_post_layout_cfg/", true);
+ }
+}
+
+void MIRGraph::DumpCheckStats()
+{
+ Checkstats* stats =
+ static_cast<Checkstats*>(arena_->NewMem(sizeof(Checkstats), true,
+ ArenaAllocator::kAllocDFInfo));
+ checkstats_ = stats;
+ AllNodesIterator iter(this, false /* not iterative */);
+ for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
+ CountChecks(bb);
+ }
+ if (stats->null_checks > 0) {
+ float eliminated = static_cast<float>(stats->null_checks_eliminated);
+ float checks = static_cast<float>(stats->null_checks);
+ LOG(INFO) << "Null Checks: " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
+ << stats->null_checks_eliminated << " of " << stats->null_checks << " -> "
+ << (eliminated/checks) * 100.0 << "%";
+ }
+ if (stats->range_checks > 0) {
+ float eliminated = static_cast<float>(stats->range_checks_eliminated);
+ float checks = static_cast<float>(stats->range_checks);
+ LOG(INFO) << "Range Checks: " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
+ << stats->range_checks_eliminated << " of " << stats->range_checks << " -> "
+ << (eliminated/checks) * 100.0 << "%";
+ }
+}
+
+bool MIRGraph::BuildExtendedBBList(struct BasicBlock* bb)
+{
+ if (bb->visited) return false;
+ if (!((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode)
+ || (bb->block_type == kExitBlock))) {
+ // Ignore special blocks
+ bb->visited = true;
+ return false;
+ }
+ // Must be head of extended basic block.
+ BasicBlock* start_bb = bb;
+ extended_basic_blocks_.push_back(bb);
+ bool terminated_by_return = false;
+ // Visit blocks strictly dominated by this head.
+ while (bb != NULL) {
+ bb->visited = true;
+ terminated_by_return |= bb->terminated_by_return;
+ bb = NextDominatedBlock(bb);
+ }
+ if (terminated_by_return) {
+ // This extended basic block contains a return, so mark all members.
+ bb = start_bb;
+ while (bb != NULL) {
+ bb->dominates_return = true;
+ bb = NextDominatedBlock(bb);
+ }
+ }
+ return false; // Not iterative - return value will be ignored
+}
+
+
+void MIRGraph::BasicBlockOptimization()
+{
+ if (!(cu_->disable_opt & (1 << kBBOpt))) {
+ DCHECK_EQ(cu_->num_compiler_temps, 0);
+ ClearAllVisitedFlags();
+ PreOrderDfsIterator iter2(this, false /* not iterative */);
+ for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
+ BuildExtendedBBList(bb);
+ }
+ // Perform extended basic block optimizations.
+ for (unsigned int i = 0; i < extended_basic_blocks_.size(); i++) {
+ BasicBlockOpt(extended_basic_blocks_[i]);
+ }
+ }
+ if (cu_->enable_debug & (1 << kDebugDumpCFG)) {
+ DumpCFG("/sdcard/6_post_bbo_cfg/", false);
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc
new file mode 100644
index 0000000..2be1ef4
--- /dev/null
+++ b/compiler/dex/portable/mir_to_gbc.cc
@@ -0,0 +1,2056 @@
+/*
+ * Copyright (C) 2011 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 "object_utils.h"
+
+#include <llvm/ADT/DepthFirstIterator.h>
+#include <llvm/Analysis/Verifier.h>
+#include <llvm/Bitcode/ReaderWriter.h>
+#include <llvm/IR/Instruction.h>
+#include <llvm/IR/Instructions.h>
+#include <llvm/IR/Metadata.h>
+#include <llvm/IR/Type.h>
+#include <llvm/Support/Casting.h>
+#include <llvm/Support/InstIterator.h>
+#include <llvm/Support/ToolOutputFile.h>
+
+#include "dex/compiler_internals.h"
+#include "dex/dataflow_iterator-inl.h"
+#include "dex/frontend.h"
+#include "mir_to_gbc.h"
+
+#include "llvm/llvm_compilation_unit.h"
+#include "llvm/utils_llvm.h"
+
+const char* kLabelFormat = "%c0x%x_%d";
+const char kInvalidBlock = 0xff;
+const char kNormalBlock = 'L';
+const char kCatchBlock = 'C';
+
+namespace art {
+
+::llvm::BasicBlock* MirConverter::GetLLVMBlock(int id)
+{
+ return id_to_block_map_.Get(id);
+}
+
+::llvm::Value* MirConverter::GetLLVMValue(int s_reg)
+{
+ return llvm_values_.Get(s_reg);
+}
+
+void MirConverter::SetVregOnValue(::llvm::Value* val, int s_reg)
+{
+ // Set vreg for debugging
+ art::llvm::IntrinsicHelper::IntrinsicId id = art::llvm::IntrinsicHelper::SetVReg;
+ ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id);
+ int v_reg = mir_graph_->SRegToVReg(s_reg);
+ ::llvm::Value* table_slot = irb_->getInt32(v_reg);
+ ::llvm::Value* args[] = { table_slot, val };
+ irb_->CreateCall(func, args);
+}
+
+// Replace the placeholder value with the real definition
+void MirConverter::DefineValueOnly(::llvm::Value* val, int s_reg)
+{
+ ::llvm::Value* placeholder = GetLLVMValue(s_reg);
+ if (placeholder == NULL) {
+ // This can happen on instruction rewrite on verification failure
+ LOG(WARNING) << "Null placeholder";
+ return;
+ }
+ placeholder->replaceAllUsesWith(val);
+ val->takeName(placeholder);
+ llvm_values_.Put(s_reg, val);
+ ::llvm::Instruction* inst = ::llvm::dyn_cast< ::llvm::Instruction>(placeholder);
+ DCHECK(inst != NULL);
+ inst->eraseFromParent();
+
+}
+
+void MirConverter::DefineValue(::llvm::Value* val, int s_reg)
+{
+ DefineValueOnly(val, s_reg);
+ SetVregOnValue(val, s_reg);
+}
+
+::llvm::Type* MirConverter::LlvmTypeFromLocRec(RegLocation loc)
+{
+ ::llvm::Type* res = NULL;
+ if (loc.wide) {
+ if (loc.fp)
+ res = irb_->getDoubleTy();
+ else
+ res = irb_->getInt64Ty();
+ } else {
+ if (loc.fp) {
+ res = irb_->getFloatTy();
+ } else {
+ if (loc.ref)
+ res = irb_->getJObjectTy();
+ else
+ res = irb_->getInt32Ty();
+ }
+ }
+ return res;
+}
+
+void MirConverter::InitIR()
+{
+ if (llvm_info_ == NULL) {
+ CompilerTls* tls = cu_->compiler_driver->GetTls();
+ CHECK(tls != NULL);
+ llvm_info_ = static_cast<LLVMInfo*>(tls->GetLLVMInfo());
+ if (llvm_info_ == NULL) {
+ llvm_info_ = new LLVMInfo();
+ tls->SetLLVMInfo(llvm_info_);
+ }
+ }
+ context_ = llvm_info_->GetLLVMContext();
+ module_ = llvm_info_->GetLLVMModule();
+ intrinsic_helper_ = llvm_info_->GetIntrinsicHelper();
+ irb_ = llvm_info_->GetIRBuilder();
+}
+
+::llvm::BasicBlock* MirConverter::FindCaseTarget(uint32_t vaddr)
+{
+ BasicBlock* bb = mir_graph_->FindBlock(vaddr);
+ DCHECK(bb != NULL);
+ return GetLLVMBlock(bb->id);
+}
+
+void MirConverter::ConvertPackedSwitch(BasicBlock* bb,
+ int32_t table_offset, RegLocation rl_src)
+{
+ const Instruction::PackedSwitchPayload* payload =
+ reinterpret_cast<const Instruction::PackedSwitchPayload*>(
+ cu_->insns + current_dalvik_offset_ + table_offset);
+
+ ::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg);
+
+ ::llvm::SwitchInst* sw =
+ irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through->id),
+ payload->case_count);
+
+ for (uint16_t i = 0; i < payload->case_count; ++i) {
+ ::llvm::BasicBlock* llvm_bb =
+ FindCaseTarget(current_dalvik_offset_ + payload->targets[i]);
+ sw->addCase(irb_->getInt32(payload->first_key + i), llvm_bb);
+ }
+ ::llvm::MDNode* switch_node =
+ ::llvm::MDNode::get(*context_, irb_->getInt32(table_offset));
+ sw->setMetadata("SwitchTable", switch_node);
+ bb->taken = NULL;
+ bb->fall_through = NULL;
+}
+
+void MirConverter::ConvertSparseSwitch(BasicBlock* bb,
+ int32_t table_offset, RegLocation rl_src)
+{
+ const Instruction::SparseSwitchPayload* payload =
+ reinterpret_cast<const Instruction::SparseSwitchPayload*>(
+ cu_->insns + current_dalvik_offset_ + table_offset);
+
+ const int32_t* keys = payload->GetKeys();
+ const int32_t* targets = payload->GetTargets();
+
+ ::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg);
+
+ ::llvm::SwitchInst* sw =
+ irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through->id),
+ payload->case_count);
+
+ for (size_t i = 0; i < payload->case_count; ++i) {
+ ::llvm::BasicBlock* llvm_bb =
+ FindCaseTarget(current_dalvik_offset_ + targets[i]);
+ sw->addCase(irb_->getInt32(keys[i]), llvm_bb);
+ }
+ ::llvm::MDNode* switch_node =
+ ::llvm::MDNode::get(*context_, irb_->getInt32(table_offset));
+ sw->setMetadata("SwitchTable", switch_node);
+ bb->taken = NULL;
+ bb->fall_through = NULL;
+}
+
+void MirConverter::ConvertSget(int32_t field_index,
+ art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest)
+{
+ ::llvm::Constant* field_idx = irb_->getInt32(field_index);
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::Value* res = irb_->CreateCall(intr, field_idx);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertSput(int32_t field_index,
+ art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_src)
+{
+ ::llvm::SmallVector< ::llvm::Value*, 2> args;
+ args.push_back(irb_->getInt32(field_index));
+ args.push_back(GetLLVMValue(rl_src.orig_sreg));
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ irb_->CreateCall(intr, args);
+}
+
+void MirConverter::ConvertFillArrayData(int32_t offset, RegLocation rl_array)
+{
+ art::llvm::IntrinsicHelper::IntrinsicId id;
+ id = art::llvm::IntrinsicHelper::HLFillArrayData;
+ ::llvm::SmallVector< ::llvm::Value*, 2> args;
+ args.push_back(irb_->getInt32(offset));
+ args.push_back(GetLLVMValue(rl_array.orig_sreg));
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ irb_->CreateCall(intr, args);
+}
+
+::llvm::Value* MirConverter::EmitConst(::llvm::ArrayRef< ::llvm::Value*> src,
+ RegLocation loc)
+{
+ art::llvm::IntrinsicHelper::IntrinsicId id;
+ if (loc.wide) {
+ if (loc.fp) {
+ id = art::llvm::IntrinsicHelper::ConstDouble;
+ } else {
+ id = art::llvm::IntrinsicHelper::ConstLong;
+ }
+ } else {
+ if (loc.fp) {
+ id = art::llvm::IntrinsicHelper::ConstFloat;
+ } else if (loc.ref) {
+ id = art::llvm::IntrinsicHelper::ConstObj;
+ } else {
+ id = art::llvm::IntrinsicHelper::ConstInt;
+ }
+ }
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ return irb_->CreateCall(intr, src);
+}
+
+void MirConverter::EmitPopShadowFrame()
+{
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(
+ art::llvm::IntrinsicHelper::PopShadowFrame);
+ irb_->CreateCall(intr);
+}
+
+::llvm::Value* MirConverter::EmitCopy(::llvm::ArrayRef< ::llvm::Value*> src,
+ RegLocation loc)
+{
+ art::llvm::IntrinsicHelper::IntrinsicId id;
+ if (loc.wide) {
+ if (loc.fp) {
+ id = art::llvm::IntrinsicHelper::CopyDouble;
+ } else {
+ id = art::llvm::IntrinsicHelper::CopyLong;
+ }
+ } else {
+ if (loc.fp) {
+ id = art::llvm::IntrinsicHelper::CopyFloat;
+ } else if (loc.ref) {
+ id = art::llvm::IntrinsicHelper::CopyObj;
+ } else {
+ id = art::llvm::IntrinsicHelper::CopyInt;
+ }
+ }
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ return irb_->CreateCall(intr, src);
+}
+
+void MirConverter::ConvertMoveException(RegLocation rl_dest)
+{
+ ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(
+ art::llvm::IntrinsicHelper::GetException);
+ ::llvm::Value* res = irb_->CreateCall(func);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertThrow(RegLocation rl_src)
+{
+ ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
+ ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(
+ art::llvm::IntrinsicHelper::HLThrowException);
+ irb_->CreateCall(func, src);
+}
+
+void MirConverter::ConvertMonitorEnterExit(int opt_flags,
+ art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_src)
+{
+ ::llvm::SmallVector< ::llvm::Value*, 2> args;
+ args.push_back(irb_->getInt32(opt_flags));
+ args.push_back(GetLLVMValue(rl_src.orig_sreg));
+ ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id);
+ irb_->CreateCall(func, args);
+}
+
+void MirConverter::ConvertArrayLength(int opt_flags,
+ RegLocation rl_dest, RegLocation rl_src)
+{
+ ::llvm::SmallVector< ::llvm::Value*, 2> args;
+ args.push_back(irb_->getInt32(opt_flags));
+ args.push_back(GetLLVMValue(rl_src.orig_sreg));
+ ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(
+ art::llvm::IntrinsicHelper::OptArrayLength);
+ ::llvm::Value* res = irb_->CreateCall(func, args);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::EmitSuspendCheck()
+{
+ art::llvm::IntrinsicHelper::IntrinsicId id =
+ art::llvm::IntrinsicHelper::CheckSuspend;
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ irb_->CreateCall(intr);
+}
+
+::llvm::Value* MirConverter::ConvertCompare(ConditionCode cc,
+ ::llvm::Value* src1, ::llvm::Value* src2)
+{
+ ::llvm::Value* res = NULL;
+ DCHECK_EQ(src1->getType(), src2->getType());
+ switch(cc) {
+ case kCondEq: res = irb_->CreateICmpEQ(src1, src2); break;
+ case kCondNe: res = irb_->CreateICmpNE(src1, src2); break;
+ case kCondLt: res = irb_->CreateICmpSLT(src1, src2); break;
+ case kCondGe: res = irb_->CreateICmpSGE(src1, src2); break;
+ case kCondGt: res = irb_->CreateICmpSGT(src1, src2); break;
+ case kCondLe: res = irb_->CreateICmpSLE(src1, src2); break;
+ default: LOG(FATAL) << "Unexpected cc value " << cc;
+ }
+ return res;
+}
+
+void MirConverter::ConvertCompareAndBranch(BasicBlock* bb, MIR* mir,
+ ConditionCode cc, RegLocation rl_src1, RegLocation rl_src2)
+{
+ if (bb->taken->start_offset <= mir->offset) {
+ EmitSuspendCheck();
+ }
+ ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
+ ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg);
+ ::llvm::Value* cond_value = ConvertCompare(cc, src1, src2);
+ cond_value->setName(StringPrintf("t%d", temp_name_++));
+ irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken->id),
+ GetLLVMBlock(bb->fall_through->id));
+ // Don't redo the fallthrough branch in the BB driver
+ bb->fall_through = NULL;
+}
+
+void MirConverter::ConvertCompareZeroAndBranch(BasicBlock* bb,
+ MIR* mir, ConditionCode cc, RegLocation rl_src1)
+{
+ if (bb->taken->start_offset <= mir->offset) {
+ EmitSuspendCheck();
+ }
+ ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
+ ::llvm::Value* src2;
+ if (rl_src1.ref) {
+ src2 = irb_->getJNull();
+ } else {
+ src2 = irb_->getInt32(0);
+ }
+ ::llvm::Value* cond_value = ConvertCompare(cc, src1, src2);
+ irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken->id),
+ GetLLVMBlock(bb->fall_through->id));
+ // Don't redo the fallthrough branch in the BB driver
+ bb->fall_through = NULL;
+}
+
+::llvm::Value* MirConverter::GenDivModOp(bool is_div, bool is_long,
+ ::llvm::Value* src1, ::llvm::Value* src2)
+{
+ art::llvm::IntrinsicHelper::IntrinsicId id;
+ if (is_long) {
+ if (is_div) {
+ id = art::llvm::IntrinsicHelper::DivLong;
+ } else {
+ id = art::llvm::IntrinsicHelper::RemLong;
+ }
+ } else {
+ if (is_div) {
+ id = art::llvm::IntrinsicHelper::DivInt;
+ } else {
+ id = art::llvm::IntrinsicHelper::RemInt;
+ }
+ }
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::SmallVector< ::llvm::Value*, 2>args;
+ args.push_back(src1);
+ args.push_back(src2);
+ return irb_->CreateCall(intr, args);
+}
+
+::llvm::Value* MirConverter::GenArithOp(OpKind op, bool is_long,
+ ::llvm::Value* src1, ::llvm::Value* src2)
+{
+ ::llvm::Value* res = NULL;
+ switch(op) {
+ case kOpAdd: res = irb_->CreateAdd(src1, src2); break;
+ case kOpSub: res = irb_->CreateSub(src1, src2); break;
+ case kOpRsub: res = irb_->CreateSub(src2, src1); break;
+ case kOpMul: res = irb_->CreateMul(src1, src2); break;
+ case kOpOr: res = irb_->CreateOr(src1, src2); break;
+ case kOpAnd: res = irb_->CreateAnd(src1, src2); break;
+ case kOpXor: res = irb_->CreateXor(src1, src2); break;
+ case kOpDiv: res = GenDivModOp(true, is_long, src1, src2); break;
+ case kOpRem: res = GenDivModOp(false, is_long, src1, src2); break;
+ case kOpLsl: res = irb_->CreateShl(src1, src2); break;
+ case kOpLsr: res = irb_->CreateLShr(src1, src2); break;
+ case kOpAsr: res = irb_->CreateAShr(src1, src2); break;
+ default:
+ LOG(FATAL) << "Invalid op " << op;
+ }
+ return res;
+}
+
+void MirConverter::ConvertFPArithOp(OpKind op, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2)
+{
+ ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
+ ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg);
+ ::llvm::Value* res = NULL;
+ switch(op) {
+ case kOpAdd: res = irb_->CreateFAdd(src1, src2); break;
+ case kOpSub: res = irb_->CreateFSub(src1, src2); break;
+ case kOpMul: res = irb_->CreateFMul(src1, src2); break;
+ case kOpDiv: res = irb_->CreateFDiv(src1, src2); break;
+ case kOpRem: res = irb_->CreateFRem(src1, src2); break;
+ default:
+ LOG(FATAL) << "Invalid op " << op;
+ }
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertShift(art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2)
+{
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::SmallVector< ::llvm::Value*, 2>args;
+ args.push_back(GetLLVMValue(rl_src1.orig_sreg));
+ args.push_back(GetLLVMValue(rl_src2.orig_sreg));
+ ::llvm::Value* res = irb_->CreateCall(intr, args);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertShiftLit(art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_dest, RegLocation rl_src, int shift_amount)
+{
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::SmallVector< ::llvm::Value*, 2>args;
+ args.push_back(GetLLVMValue(rl_src.orig_sreg));
+ args.push_back(irb_->getInt32(shift_amount));
+ ::llvm::Value* res = irb_->CreateCall(intr, args);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertArithOp(OpKind op, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2)
+{
+ ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
+ ::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg);
+ DCHECK_EQ(src1->getType(), src2->getType());
+ ::llvm::Value* res = GenArithOp(op, rl_dest.wide, src1, src2);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertArithOpLit(OpKind op, RegLocation rl_dest,
+ RegLocation rl_src1, int32_t imm)
+{
+ ::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
+ ::llvm::Value* src2 = irb_->getInt32(imm);
+ ::llvm::Value* res = GenArithOp(op, rl_dest.wide, src1, src2);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+/*
+ * Process arguments for invoke. Note: this code is also used to
+ * collect and process arguments for NEW_FILLED_ARRAY and NEW_FILLED_ARRAY_RANGE.
+ * The requirements are similar.
+ */
+void MirConverter::ConvertInvoke(BasicBlock* bb, MIR* mir,
+ InvokeType invoke_type, bool is_range, bool is_filled_new_array)
+{
+ CallInfo* info = mir_graph_->NewMemCallInfo(bb, mir, invoke_type, is_range);
+ ::llvm::SmallVector< ::llvm::Value*, 10> args;
+ // Insert the invoke_type
+ args.push_back(irb_->getInt32(static_cast<int>(invoke_type)));
+ // Insert the method_idx
+ args.push_back(irb_->getInt32(info->index));
+ // Insert the optimization flags
+ args.push_back(irb_->getInt32(info->opt_flags));
+ // Now, insert the actual arguments
+ for (int i = 0; i < info->num_arg_words;) {
+ ::llvm::Value* val = GetLLVMValue(info->args[i].orig_sreg);
+ args.push_back(val);
+ i += info->args[i].wide ? 2 : 1;
+ }
+ /*
+ * Choose the invoke return type based on actual usage. Note: may
+ * be different than shorty. For example, if a function return value
+ * is not used, we'll treat this as a void invoke.
+ */
+ art::llvm::IntrinsicHelper::IntrinsicId id;
+ if (is_filled_new_array) {
+ id = art::llvm::IntrinsicHelper::HLFilledNewArray;
+ } else if (info->result.location == kLocInvalid) {
+ id = art::llvm::IntrinsicHelper::HLInvokeVoid;
+ } else {
+ if (info->result.wide) {
+ if (info->result.fp) {
+ id = art::llvm::IntrinsicHelper::HLInvokeDouble;
+ } else {
+ id = art::llvm::IntrinsicHelper::HLInvokeLong;
+ }
+ } else if (info->result.ref) {
+ id = art::llvm::IntrinsicHelper::HLInvokeObj;
+ } else if (info->result.fp) {
+ id = art::llvm::IntrinsicHelper::HLInvokeFloat;
+ } else {
+ id = art::llvm::IntrinsicHelper::HLInvokeInt;
+ }
+ }
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::Value* res = irb_->CreateCall(intr, args);
+ if (info->result.location != kLocInvalid) {
+ DefineValue(res, info->result.orig_sreg);
+ }
+}
+
+void MirConverter::ConvertConstObject(uint32_t idx,
+ art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest)
+{
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::Value* index = irb_->getInt32(idx);
+ ::llvm::Value* res = irb_->CreateCall(intr, index);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertCheckCast(uint32_t type_idx, RegLocation rl_src)
+{
+ art::llvm::IntrinsicHelper::IntrinsicId id;
+ id = art::llvm::IntrinsicHelper::HLCheckCast;
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::SmallVector< ::llvm::Value*, 2> args;
+ args.push_back(irb_->getInt32(type_idx));
+ args.push_back(GetLLVMValue(rl_src.orig_sreg));
+ irb_->CreateCall(intr, args);
+}
+
+void MirConverter::ConvertNewInstance(uint32_t type_idx, RegLocation rl_dest)
+{
+ art::llvm::IntrinsicHelper::IntrinsicId id;
+ id = art::llvm::IntrinsicHelper::NewInstance;
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::Value* index = irb_->getInt32(type_idx);
+ ::llvm::Value* res = irb_->CreateCall(intr, index);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertNewArray(uint32_t type_idx,
+ RegLocation rl_dest, RegLocation rl_src)
+{
+ art::llvm::IntrinsicHelper::IntrinsicId id;
+ id = art::llvm::IntrinsicHelper::NewArray;
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::SmallVector< ::llvm::Value*, 2> args;
+ args.push_back(irb_->getInt32(type_idx));
+ args.push_back(GetLLVMValue(rl_src.orig_sreg));
+ ::llvm::Value* res = irb_->CreateCall(intr, args);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertAget(int opt_flags,
+ art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_dest, RegLocation rl_array, RegLocation rl_index)
+{
+ ::llvm::SmallVector< ::llvm::Value*, 3> args;
+ args.push_back(irb_->getInt32(opt_flags));
+ args.push_back(GetLLVMValue(rl_array.orig_sreg));
+ args.push_back(GetLLVMValue(rl_index.orig_sreg));
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::Value* res = irb_->CreateCall(intr, args);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertAput(int opt_flags,
+ art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_src, RegLocation rl_array, RegLocation rl_index)
+{
+ ::llvm::SmallVector< ::llvm::Value*, 4> args;
+ args.push_back(irb_->getInt32(opt_flags));
+ args.push_back(GetLLVMValue(rl_src.orig_sreg));
+ args.push_back(GetLLVMValue(rl_array.orig_sreg));
+ args.push_back(GetLLVMValue(rl_index.orig_sreg));
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ irb_->CreateCall(intr, args);
+}
+
+void MirConverter::ConvertIget(int opt_flags,
+ art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_dest, RegLocation rl_obj, int field_index)
+{
+ ::llvm::SmallVector< ::llvm::Value*, 3> args;
+ args.push_back(irb_->getInt32(opt_flags));
+ args.push_back(GetLLVMValue(rl_obj.orig_sreg));
+ args.push_back(irb_->getInt32(field_index));
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::Value* res = irb_->CreateCall(intr, args);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertIput(int opt_flags,
+ art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_src, RegLocation rl_obj, int field_index)
+{
+ ::llvm::SmallVector< ::llvm::Value*, 4> args;
+ args.push_back(irb_->getInt32(opt_flags));
+ args.push_back(GetLLVMValue(rl_src.orig_sreg));
+ args.push_back(GetLLVMValue(rl_obj.orig_sreg));
+ args.push_back(irb_->getInt32(field_index));
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ irb_->CreateCall(intr, args);
+}
+
+void MirConverter::ConvertInstanceOf(uint32_t type_idx,
+ RegLocation rl_dest, RegLocation rl_src)
+{
+ art::llvm::IntrinsicHelper::IntrinsicId id;
+ id = art::llvm::IntrinsicHelper::InstanceOf;
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::SmallVector< ::llvm::Value*, 2> args;
+ args.push_back(irb_->getInt32(type_idx));
+ args.push_back(GetLLVMValue(rl_src.orig_sreg));
+ ::llvm::Value* res = irb_->CreateCall(intr, args);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertIntToLong(RegLocation rl_dest, RegLocation rl_src)
+{
+ ::llvm::Value* res = irb_->CreateSExt(GetLLVMValue(rl_src.orig_sreg),
+ irb_->getInt64Ty());
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertLongToInt(RegLocation rl_dest, RegLocation rl_src)
+{
+ ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
+ ::llvm::Value* res = irb_->CreateTrunc(src, irb_->getInt32Ty());
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertFloatToDouble(RegLocation rl_dest, RegLocation rl_src)
+{
+ ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
+ ::llvm::Value* res = irb_->CreateFPExt(src, irb_->getDoubleTy());
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertDoubleToFloat(RegLocation rl_dest, RegLocation rl_src)
+{
+ ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
+ ::llvm::Value* res = irb_->CreateFPTrunc(src, irb_->getFloatTy());
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertWideComparison(art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ DCHECK_EQ(rl_src1.fp, rl_src2.fp);
+ DCHECK_EQ(rl_src1.wide, rl_src2.wide);
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::SmallVector< ::llvm::Value*, 2> args;
+ args.push_back(GetLLVMValue(rl_src1.orig_sreg));
+ args.push_back(GetLLVMValue(rl_src2.orig_sreg));
+ ::llvm::Value* res = irb_->CreateCall(intr, args);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertIntNarrowing(RegLocation rl_dest, RegLocation rl_src,
+ art::llvm::IntrinsicHelper::IntrinsicId id)
+{
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::Value* res =
+ irb_->CreateCall(intr, GetLLVMValue(rl_src.orig_sreg));
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertNeg(RegLocation rl_dest, RegLocation rl_src)
+{
+ ::llvm::Value* res = irb_->CreateNeg(GetLLVMValue(rl_src.orig_sreg));
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertIntToFP(::llvm::Type* ty, RegLocation rl_dest,
+ RegLocation rl_src)
+{
+ ::llvm::Value* res =
+ irb_->CreateSIToFP(GetLLVMValue(rl_src.orig_sreg), ty);
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertFPToInt(art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_dest,
+ RegLocation rl_src)
+{
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::Value* res = irb_->CreateCall(intr, GetLLVMValue(rl_src.orig_sreg));
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+
+void MirConverter::ConvertNegFP(RegLocation rl_dest, RegLocation rl_src)
+{
+ ::llvm::Value* res =
+ irb_->CreateFNeg(GetLLVMValue(rl_src.orig_sreg));
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::ConvertNot(RegLocation rl_dest, RegLocation rl_src)
+{
+ ::llvm::Value* src = GetLLVMValue(rl_src.orig_sreg);
+ ::llvm::Value* res = irb_->CreateXor(src, static_cast<uint64_t>(-1));
+ DefineValue(res, rl_dest.orig_sreg);
+}
+
+void MirConverter::EmitConstructorBarrier() {
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(
+ art::llvm::IntrinsicHelper::ConstructorBarrier);
+ irb_->CreateCall(intr);
+}
+
+/*
+ * Target-independent code generation. Use only high-level
+ * load/store utilities here, or target-dependent genXX() handlers
+ * when necessary.
+ */
+bool MirConverter::ConvertMIRNode(MIR* mir, BasicBlock* bb,
+ ::llvm::BasicBlock* llvm_bb)
+{
+ bool res = false; // Assume success
+ RegLocation rl_src[3];
+ RegLocation rl_dest = mir_graph_->GetBadLoc();
+ Instruction::Code opcode = mir->dalvikInsn.opcode;
+ int op_val = opcode;
+ uint32_t vB = mir->dalvikInsn.vB;
+ uint32_t vC = mir->dalvikInsn.vC;
+ int opt_flags = mir->optimization_flags;
+
+ if (cu_->verbose) {
+ if (op_val < kMirOpFirst) {
+ LOG(INFO) << ".. " << Instruction::Name(opcode) << " 0x" << std::hex << op_val;
+ } else {
+ LOG(INFO) << mir_graph_->extended_mir_op_names_[op_val - kMirOpFirst] << " 0x" << std::hex << op_val;
+ }
+ }
+
+ /* Prep Src and Dest locations */
+ int next_sreg = 0;
+ int next_loc = 0;
+ int attrs = mir_graph_->oat_data_flow_attributes_[opcode];
+ rl_src[0] = rl_src[1] = rl_src[2] = mir_graph_->GetBadLoc();
+ if (attrs & DF_UA) {
+ if (attrs & DF_A_WIDE) {
+ rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg);
+ next_sreg+= 2;
+ } else {
+ rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg);
+ next_sreg++;
+ }
+ }
+ if (attrs & DF_UB) {
+ if (attrs & DF_B_WIDE) {
+ rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg);
+ next_sreg+= 2;
+ } else {
+ rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg);
+ next_sreg++;
+ }
+ }
+ if (attrs & DF_UC) {
+ if (attrs & DF_C_WIDE) {
+ rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg);
+ } else {
+ rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg);
+ }
+ }
+ if (attrs & DF_DA) {
+ if (attrs & DF_A_WIDE) {
+ rl_dest = mir_graph_->GetDestWide(mir);
+ } else {
+ rl_dest = mir_graph_->GetDest(mir);
+ }
+ }
+
+ switch (opcode) {
+ case Instruction::NOP:
+ break;
+
+ case Instruction::MOVE:
+ case Instruction::MOVE_OBJECT:
+ case Instruction::MOVE_16:
+ case Instruction::MOVE_OBJECT_16:
+ case Instruction::MOVE_OBJECT_FROM16:
+ case Instruction::MOVE_FROM16:
+ case Instruction::MOVE_WIDE:
+ case Instruction::MOVE_WIDE_16:
+ case Instruction::MOVE_WIDE_FROM16: {
+ /*
+ * Moves/copies are meaningless in pure SSA register form,
+ * but we need to preserve them for the conversion back into
+ * MIR (at least until we stop using the Dalvik register maps).
+ * Insert a dummy intrinsic copy call, which will be recognized
+ * by the quick path and removed by the portable path.
+ */
+ ::llvm::Value* src = GetLLVMValue(rl_src[0].orig_sreg);
+ ::llvm::Value* res = EmitCopy(src, rl_dest);
+ DefineValue(res, rl_dest.orig_sreg);
+ }
+ break;
+
+ case Instruction::CONST:
+ case Instruction::CONST_4:
+ case Instruction::CONST_16: {
+ ::llvm::Constant* imm_value = irb_->getJInt(vB);
+ ::llvm::Value* res = EmitConst(imm_value, rl_dest);
+ DefineValue(res, rl_dest.orig_sreg);
+ }
+ break;
+
+ case Instruction::CONST_WIDE_16:
+ case Instruction::CONST_WIDE_32: {
+ // Sign extend to 64 bits
+ int64_t imm = static_cast<int32_t>(vB);
+ ::llvm::Constant* imm_value = irb_->getJLong(imm);
+ ::llvm::Value* res = EmitConst(imm_value, rl_dest);
+ DefineValue(res, rl_dest.orig_sreg);
+ }
+ break;
+
+ case Instruction::CONST_HIGH16: {
+ ::llvm::Constant* imm_value = irb_->getJInt(vB << 16);
+ ::llvm::Value* res = EmitConst(imm_value, rl_dest);
+ DefineValue(res, rl_dest.orig_sreg);
+ }
+ break;
+
+ case Instruction::CONST_WIDE: {
+ ::llvm::Constant* imm_value =
+ irb_->getJLong(mir->dalvikInsn.vB_wide);
+ ::llvm::Value* res = EmitConst(imm_value, rl_dest);
+ DefineValue(res, rl_dest.orig_sreg);
+ }
+ break;
+ case Instruction::CONST_WIDE_HIGH16: {
+ int64_t imm = static_cast<int64_t>(vB) << 48;
+ ::llvm::Constant* imm_value = irb_->getJLong(imm);
+ ::llvm::Value* res = EmitConst(imm_value, rl_dest);
+ DefineValue(res, rl_dest.orig_sreg);
+ }
+ break;
+
+ case Instruction::SPUT_OBJECT:
+ ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputObject,
+ rl_src[0]);
+ break;
+ case Instruction::SPUT:
+ if (rl_src[0].fp) {
+ ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputFloat,
+ rl_src[0]);
+ } else {
+ ConvertSput(vB, art::llvm::IntrinsicHelper::HLSput, rl_src[0]);
+ }
+ break;
+ case Instruction::SPUT_BOOLEAN:
+ ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputBoolean,
+ rl_src[0]);
+ break;
+ case Instruction::SPUT_BYTE:
+ ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputByte, rl_src[0]);
+ break;
+ case Instruction::SPUT_CHAR:
+ ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputChar, rl_src[0]);
+ break;
+ case Instruction::SPUT_SHORT:
+ ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputShort, rl_src[0]);
+ break;
+ case Instruction::SPUT_WIDE:
+ if (rl_src[0].fp) {
+ ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputDouble,
+ rl_src[0]);
+ } else {
+ ConvertSput(vB, art::llvm::IntrinsicHelper::HLSputWide,
+ rl_src[0]);
+ }
+ break;
+
+ case Instruction::SGET_OBJECT:
+ ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetObject, rl_dest);
+ break;
+ case Instruction::SGET:
+ if (rl_dest.fp) {
+ ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetFloat, rl_dest);
+ } else {
+ ConvertSget(vB, art::llvm::IntrinsicHelper::HLSget, rl_dest);
+ }
+ break;
+ case Instruction::SGET_BOOLEAN:
+ ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetBoolean, rl_dest);
+ break;
+ case Instruction::SGET_BYTE:
+ ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetByte, rl_dest);
+ break;
+ case Instruction::SGET_CHAR:
+ ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetChar, rl_dest);
+ break;
+ case Instruction::SGET_SHORT:
+ ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetShort, rl_dest);
+ break;
+ case Instruction::SGET_WIDE:
+ if (rl_dest.fp) {
+ ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetDouble,
+ rl_dest);
+ } else {
+ ConvertSget(vB, art::llvm::IntrinsicHelper::HLSgetWide, rl_dest);
+ }
+ break;
+
+ case Instruction::RETURN_WIDE:
+ case Instruction::RETURN:
+ case Instruction::RETURN_OBJECT: {
+ if (!mir_graph_->MethodIsLeaf()) {
+ EmitSuspendCheck();
+ }
+ EmitPopShadowFrame();
+ irb_->CreateRet(GetLLVMValue(rl_src[0].orig_sreg));
+ DCHECK(bb->terminated_by_return);
+ }
+ break;
+
+ case Instruction::RETURN_VOID: {
+ if (((cu_->access_flags & kAccConstructor) != 0) &&
+ cu_->compiler_driver->RequiresConstructorBarrier(Thread::Current(),
+ cu_->dex_file,
+ cu_->class_def_idx)) {
+ EmitConstructorBarrier();
+ }
+ if (!mir_graph_->MethodIsLeaf()) {
+ EmitSuspendCheck();
+ }
+ EmitPopShadowFrame();
+ irb_->CreateRetVoid();
+ DCHECK(bb->terminated_by_return);
+ }
+ break;
+
+ case Instruction::IF_EQ:
+ ConvertCompareAndBranch(bb, mir, kCondEq, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::IF_NE:
+ ConvertCompareAndBranch(bb, mir, kCondNe, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::IF_LT:
+ ConvertCompareAndBranch(bb, mir, kCondLt, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::IF_GE:
+ ConvertCompareAndBranch(bb, mir, kCondGe, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::IF_GT:
+ ConvertCompareAndBranch(bb, mir, kCondGt, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::IF_LE:
+ ConvertCompareAndBranch(bb, mir, kCondLe, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::IF_EQZ:
+ ConvertCompareZeroAndBranch(bb, mir, kCondEq, rl_src[0]);
+ break;
+ case Instruction::IF_NEZ:
+ ConvertCompareZeroAndBranch(bb, mir, kCondNe, rl_src[0]);
+ break;
+ case Instruction::IF_LTZ:
+ ConvertCompareZeroAndBranch(bb, mir, kCondLt, rl_src[0]);
+ break;
+ case Instruction::IF_GEZ:
+ ConvertCompareZeroAndBranch(bb, mir, kCondGe, rl_src[0]);
+ break;
+ case Instruction::IF_GTZ:
+ ConvertCompareZeroAndBranch(bb, mir, kCondGt, rl_src[0]);
+ break;
+ case Instruction::IF_LEZ:
+ ConvertCompareZeroAndBranch(bb, mir, kCondLe, rl_src[0]);
+ break;
+
+ case Instruction::GOTO:
+ case Instruction::GOTO_16:
+ case Instruction::GOTO_32: {
+ if (bb->taken->start_offset <= bb->start_offset) {
+ EmitSuspendCheck();
+ }
+ irb_->CreateBr(GetLLVMBlock(bb->taken->id));
+ }
+ break;
+
+ case Instruction::ADD_LONG:
+ case Instruction::ADD_LONG_2ADDR:
+ case Instruction::ADD_INT:
+ case Instruction::ADD_INT_2ADDR:
+ ConvertArithOp(kOpAdd, rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::SUB_LONG:
+ case Instruction::SUB_LONG_2ADDR:
+ case Instruction::SUB_INT:
+ case Instruction::SUB_INT_2ADDR:
+ ConvertArithOp(kOpSub, rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::MUL_LONG:
+ case Instruction::MUL_LONG_2ADDR:
+ case Instruction::MUL_INT:
+ case Instruction::MUL_INT_2ADDR:
+ ConvertArithOp(kOpMul, rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::DIV_LONG:
+ case Instruction::DIV_LONG_2ADDR:
+ case Instruction::DIV_INT:
+ case Instruction::DIV_INT_2ADDR:
+ ConvertArithOp(kOpDiv, rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::REM_LONG:
+ case Instruction::REM_LONG_2ADDR:
+ case Instruction::REM_INT:
+ case Instruction::REM_INT_2ADDR:
+ ConvertArithOp(kOpRem, rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::AND_LONG:
+ case Instruction::AND_LONG_2ADDR:
+ case Instruction::AND_INT:
+ case Instruction::AND_INT_2ADDR:
+ ConvertArithOp(kOpAnd, rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::OR_LONG:
+ case Instruction::OR_LONG_2ADDR:
+ case Instruction::OR_INT:
+ case Instruction::OR_INT_2ADDR:
+ ConvertArithOp(kOpOr, rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::XOR_LONG:
+ case Instruction::XOR_LONG_2ADDR:
+ case Instruction::XOR_INT:
+ case Instruction::XOR_INT_2ADDR:
+ ConvertArithOp(kOpXor, rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::SHL_LONG:
+ case Instruction::SHL_LONG_2ADDR:
+ ConvertShift(art::llvm::IntrinsicHelper::SHLLong,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::SHL_INT:
+ case Instruction::SHL_INT_2ADDR:
+ ConvertShift(art::llvm::IntrinsicHelper::SHLInt,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::SHR_LONG:
+ case Instruction::SHR_LONG_2ADDR:
+ ConvertShift(art::llvm::IntrinsicHelper::SHRLong,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::SHR_INT:
+ case Instruction::SHR_INT_2ADDR:
+ ConvertShift(art::llvm::IntrinsicHelper::SHRInt,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::USHR_LONG:
+ case Instruction::USHR_LONG_2ADDR:
+ ConvertShift(art::llvm::IntrinsicHelper::USHRLong,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::USHR_INT:
+ case Instruction::USHR_INT_2ADDR:
+ ConvertShift(art::llvm::IntrinsicHelper::USHRInt,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+
+ case Instruction::ADD_INT_LIT16:
+ case Instruction::ADD_INT_LIT8:
+ ConvertArithOpLit(kOpAdd, rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::RSUB_INT:
+ case Instruction::RSUB_INT_LIT8:
+ ConvertArithOpLit(kOpRsub, rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::MUL_INT_LIT16:
+ case Instruction::MUL_INT_LIT8:
+ ConvertArithOpLit(kOpMul, rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::DIV_INT_LIT16:
+ case Instruction::DIV_INT_LIT8:
+ ConvertArithOpLit(kOpDiv, rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::REM_INT_LIT16:
+ case Instruction::REM_INT_LIT8:
+ ConvertArithOpLit(kOpRem, rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::AND_INT_LIT16:
+ case Instruction::AND_INT_LIT8:
+ ConvertArithOpLit(kOpAnd, rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::OR_INT_LIT16:
+ case Instruction::OR_INT_LIT8:
+ ConvertArithOpLit(kOpOr, rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::XOR_INT_LIT16:
+ case Instruction::XOR_INT_LIT8:
+ ConvertArithOpLit(kOpXor, rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::SHL_INT_LIT8:
+ ConvertShiftLit(art::llvm::IntrinsicHelper::SHLInt,
+ rl_dest, rl_src[0], vC & 0x1f);
+ break;
+ case Instruction::SHR_INT_LIT8:
+ ConvertShiftLit(art::llvm::IntrinsicHelper::SHRInt,
+ rl_dest, rl_src[0], vC & 0x1f);
+ break;
+ case Instruction::USHR_INT_LIT8:
+ ConvertShiftLit(art::llvm::IntrinsicHelper::USHRInt,
+ rl_dest, rl_src[0], vC & 0x1f);
+ break;
+
+ case Instruction::ADD_FLOAT:
+ case Instruction::ADD_FLOAT_2ADDR:
+ case Instruction::ADD_DOUBLE:
+ case Instruction::ADD_DOUBLE_2ADDR:
+ ConvertFPArithOp(kOpAdd, rl_dest, rl_src[0], rl_src[1]);
+ break;
+
+ case Instruction::SUB_FLOAT:
+ case Instruction::SUB_FLOAT_2ADDR:
+ case Instruction::SUB_DOUBLE:
+ case Instruction::SUB_DOUBLE_2ADDR:
+ ConvertFPArithOp(kOpSub, rl_dest, rl_src[0], rl_src[1]);
+ break;
+
+ case Instruction::MUL_FLOAT:
+ case Instruction::MUL_FLOAT_2ADDR:
+ case Instruction::MUL_DOUBLE:
+ case Instruction::MUL_DOUBLE_2ADDR:
+ ConvertFPArithOp(kOpMul, rl_dest, rl_src[0], rl_src[1]);
+ break;
+
+ case Instruction::DIV_FLOAT:
+ case Instruction::DIV_FLOAT_2ADDR:
+ case Instruction::DIV_DOUBLE:
+ case Instruction::DIV_DOUBLE_2ADDR:
+ ConvertFPArithOp(kOpDiv, rl_dest, rl_src[0], rl_src[1]);
+ break;
+
+ case Instruction::REM_FLOAT:
+ case Instruction::REM_FLOAT_2ADDR:
+ case Instruction::REM_DOUBLE:
+ case Instruction::REM_DOUBLE_2ADDR:
+ ConvertFPArithOp(kOpRem, rl_dest, rl_src[0], rl_src[1]);
+ break;
+
+ case Instruction::INVOKE_STATIC:
+ ConvertInvoke(bb, mir, kStatic, false /*range*/,
+ false /* NewFilledArray */);
+ break;
+ case Instruction::INVOKE_STATIC_RANGE:
+ ConvertInvoke(bb, mir, kStatic, true /*range*/,
+ false /* NewFilledArray */);
+ break;
+
+ case Instruction::INVOKE_DIRECT:
+ ConvertInvoke(bb, mir, kDirect, false /*range*/,
+ false /* NewFilledArray */);
+ break;
+ case Instruction::INVOKE_DIRECT_RANGE:
+ ConvertInvoke(bb, mir, kDirect, true /*range*/,
+ false /* NewFilledArray */);
+ break;
+
+ case Instruction::INVOKE_VIRTUAL:
+ ConvertInvoke(bb, mir, kVirtual, false /*range*/,
+ false /* NewFilledArray */);
+ break;
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ ConvertInvoke(bb, mir, kVirtual, true /*range*/,
+ false /* NewFilledArray */);
+ break;
+
+ case Instruction::INVOKE_SUPER:
+ ConvertInvoke(bb, mir, kSuper, false /*range*/,
+ false /* NewFilledArray */);
+ break;
+ case Instruction::INVOKE_SUPER_RANGE:
+ ConvertInvoke(bb, mir, kSuper, true /*range*/,
+ false /* NewFilledArray */);
+ break;
+
+ case Instruction::INVOKE_INTERFACE:
+ ConvertInvoke(bb, mir, kInterface, false /*range*/,
+ false /* NewFilledArray */);
+ break;
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ ConvertInvoke(bb, mir, kInterface, true /*range*/,
+ false /* NewFilledArray */);
+ break;
+ case Instruction::FILLED_NEW_ARRAY:
+ ConvertInvoke(bb, mir, kInterface, false /*range*/,
+ true /* NewFilledArray */);
+ break;
+ case Instruction::FILLED_NEW_ARRAY_RANGE:
+ ConvertInvoke(bb, mir, kInterface, true /*range*/,
+ true /* NewFilledArray */);
+ break;
+
+ case Instruction::CONST_STRING:
+ case Instruction::CONST_STRING_JUMBO:
+ ConvertConstObject(vB, art::llvm::IntrinsicHelper::ConstString,
+ rl_dest);
+ break;
+
+ case Instruction::CONST_CLASS:
+ ConvertConstObject(vB, art::llvm::IntrinsicHelper::ConstClass,
+ rl_dest);
+ break;
+
+ case Instruction::CHECK_CAST:
+ ConvertCheckCast(vB, rl_src[0]);
+ break;
+
+ case Instruction::NEW_INSTANCE:
+ ConvertNewInstance(vB, rl_dest);
+ break;
+
+ case Instruction::MOVE_EXCEPTION:
+ ConvertMoveException(rl_dest);
+ break;
+
+ case Instruction::THROW:
+ ConvertThrow(rl_src[0]);
+ /*
+ * If this throw is standalone, terminate.
+ * If it might rethrow, force termination
+ * of the following block.
+ */
+ if (bb->fall_through == NULL) {
+ irb_->CreateUnreachable();
+ } else {
+ bb->fall_through->fall_through = NULL;
+ bb->fall_through->taken = NULL;
+ }
+ break;
+
+ case Instruction::MOVE_RESULT_WIDE:
+ case Instruction::MOVE_RESULT:
+ case Instruction::MOVE_RESULT_OBJECT:
+ /*
+ * All move_results should have been folded into the preceeding invoke.
+ */
+ LOG(FATAL) << "Unexpected move_result";
+ break;
+
+ case Instruction::MONITOR_ENTER:
+ ConvertMonitorEnterExit(opt_flags,
+ art::llvm::IntrinsicHelper::MonitorEnter,
+ rl_src[0]);
+ break;
+
+ case Instruction::MONITOR_EXIT:
+ ConvertMonitorEnterExit(opt_flags,
+ art::llvm::IntrinsicHelper::MonitorExit,
+ rl_src[0]);
+ break;
+
+ case Instruction::ARRAY_LENGTH:
+ ConvertArrayLength(opt_flags, rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::NEW_ARRAY:
+ ConvertNewArray(vC, rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::INSTANCE_OF:
+ ConvertInstanceOf(vC, rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::AGET:
+ if (rl_dest.fp) {
+ ConvertAget(opt_flags,
+ art::llvm::IntrinsicHelper::HLArrayGetFloat,
+ rl_dest, rl_src[0], rl_src[1]);
+ } else {
+ ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGet,
+ rl_dest, rl_src[0], rl_src[1]);
+ }
+ break;
+ case Instruction::AGET_OBJECT:
+ ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetObject,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::AGET_BOOLEAN:
+ ConvertAget(opt_flags,
+ art::llvm::IntrinsicHelper::HLArrayGetBoolean,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::AGET_BYTE:
+ ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetByte,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::AGET_CHAR:
+ ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetChar,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::AGET_SHORT:
+ ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetShort,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::AGET_WIDE:
+ if (rl_dest.fp) {
+ ConvertAget(opt_flags,
+ art::llvm::IntrinsicHelper::HLArrayGetDouble,
+ rl_dest, rl_src[0], rl_src[1]);
+ } else {
+ ConvertAget(opt_flags, art::llvm::IntrinsicHelper::HLArrayGetWide,
+ rl_dest, rl_src[0], rl_src[1]);
+ }
+ break;
+
+ case Instruction::APUT:
+ if (rl_src[0].fp) {
+ ConvertAput(opt_flags,
+ art::llvm::IntrinsicHelper::HLArrayPutFloat,
+ rl_src[0], rl_src[1], rl_src[2]);
+ } else {
+ ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPut,
+ rl_src[0], rl_src[1], rl_src[2]);
+ }
+ break;
+ case Instruction::APUT_OBJECT:
+ ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutObject,
+ rl_src[0], rl_src[1], rl_src[2]);
+ break;
+ case Instruction::APUT_BOOLEAN:
+ ConvertAput(opt_flags,
+ art::llvm::IntrinsicHelper::HLArrayPutBoolean,
+ rl_src[0], rl_src[1], rl_src[2]);
+ break;
+ case Instruction::APUT_BYTE:
+ ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutByte,
+ rl_src[0], rl_src[1], rl_src[2]);
+ break;
+ case Instruction::APUT_CHAR:
+ ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutChar,
+ rl_src[0], rl_src[1], rl_src[2]);
+ break;
+ case Instruction::APUT_SHORT:
+ ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutShort,
+ rl_src[0], rl_src[1], rl_src[2]);
+ break;
+ case Instruction::APUT_WIDE:
+ if (rl_src[0].fp) {
+ ConvertAput(opt_flags,
+ art::llvm::IntrinsicHelper::HLArrayPutDouble,
+ rl_src[0], rl_src[1], rl_src[2]);
+ } else {
+ ConvertAput(opt_flags, art::llvm::IntrinsicHelper::HLArrayPutWide,
+ rl_src[0], rl_src[1], rl_src[2]);
+ }
+ break;
+
+ case Instruction::IGET:
+ if (rl_dest.fp) {
+ ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetFloat,
+ rl_dest, rl_src[0], vC);
+ } else {
+ ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGet,
+ rl_dest, rl_src[0], vC);
+ }
+ break;
+ case Instruction::IGET_OBJECT:
+ ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetObject,
+ rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::IGET_BOOLEAN:
+ ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetBoolean,
+ rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::IGET_BYTE:
+ ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetByte,
+ rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::IGET_CHAR:
+ ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetChar,
+ rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::IGET_SHORT:
+ ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetShort,
+ rl_dest, rl_src[0], vC);
+ break;
+ case Instruction::IGET_WIDE:
+ if (rl_dest.fp) {
+ ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetDouble,
+ rl_dest, rl_src[0], vC);
+ } else {
+ ConvertIget(opt_flags, art::llvm::IntrinsicHelper::HLIGetWide,
+ rl_dest, rl_src[0], vC);
+ }
+ break;
+ case Instruction::IPUT:
+ if (rl_src[0].fp) {
+ ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutFloat,
+ rl_src[0], rl_src[1], vC);
+ } else {
+ ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPut,
+ rl_src[0], rl_src[1], vC);
+ }
+ break;
+ case Instruction::IPUT_OBJECT:
+ ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutObject,
+ rl_src[0], rl_src[1], vC);
+ break;
+ case Instruction::IPUT_BOOLEAN:
+ ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutBoolean,
+ rl_src[0], rl_src[1], vC);
+ break;
+ case Instruction::IPUT_BYTE:
+ ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutByte,
+ rl_src[0], rl_src[1], vC);
+ break;
+ case Instruction::IPUT_CHAR:
+ ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutChar,
+ rl_src[0], rl_src[1], vC);
+ break;
+ case Instruction::IPUT_SHORT:
+ ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutShort,
+ rl_src[0], rl_src[1], vC);
+ break;
+ case Instruction::IPUT_WIDE:
+ if (rl_src[0].fp) {
+ ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutDouble,
+ rl_src[0], rl_src[1], vC);
+ } else {
+ ConvertIput(opt_flags, art::llvm::IntrinsicHelper::HLIPutWide,
+ rl_src[0], rl_src[1], vC);
+ }
+ break;
+
+ case Instruction::FILL_ARRAY_DATA:
+ ConvertFillArrayData(vB, rl_src[0]);
+ break;
+
+ case Instruction::LONG_TO_INT:
+ ConvertLongToInt(rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::INT_TO_LONG:
+ ConvertIntToLong(rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::INT_TO_CHAR:
+ ConvertIntNarrowing(rl_dest, rl_src[0],
+ art::llvm::IntrinsicHelper::IntToChar);
+ break;
+ case Instruction::INT_TO_BYTE:
+ ConvertIntNarrowing(rl_dest, rl_src[0],
+ art::llvm::IntrinsicHelper::IntToByte);
+ break;
+ case Instruction::INT_TO_SHORT:
+ ConvertIntNarrowing(rl_dest, rl_src[0],
+ art::llvm::IntrinsicHelper::IntToShort);
+ break;
+
+ case Instruction::INT_TO_FLOAT:
+ case Instruction::LONG_TO_FLOAT:
+ ConvertIntToFP(irb_->getFloatTy(), rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::INT_TO_DOUBLE:
+ case Instruction::LONG_TO_DOUBLE:
+ ConvertIntToFP(irb_->getDoubleTy(), rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::FLOAT_TO_DOUBLE:
+ ConvertFloatToDouble(rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::DOUBLE_TO_FLOAT:
+ ConvertDoubleToFloat(rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::NEG_LONG:
+ case Instruction::NEG_INT:
+ ConvertNeg(rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::NEG_FLOAT:
+ case Instruction::NEG_DOUBLE:
+ ConvertNegFP(rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::NOT_LONG:
+ case Instruction::NOT_INT:
+ ConvertNot(rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::FLOAT_TO_INT:
+ ConvertFPToInt(art::llvm::IntrinsicHelper::F2I, rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::DOUBLE_TO_INT:
+ ConvertFPToInt(art::llvm::IntrinsicHelper::D2I, rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::FLOAT_TO_LONG:
+ ConvertFPToInt(art::llvm::IntrinsicHelper::F2L, rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::DOUBLE_TO_LONG:
+ ConvertFPToInt(art::llvm::IntrinsicHelper::D2L, rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::CMPL_FLOAT:
+ ConvertWideComparison(art::llvm::IntrinsicHelper::CmplFloat,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::CMPG_FLOAT:
+ ConvertWideComparison(art::llvm::IntrinsicHelper::CmpgFloat,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::CMPL_DOUBLE:
+ ConvertWideComparison(art::llvm::IntrinsicHelper::CmplDouble,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::CMPG_DOUBLE:
+ ConvertWideComparison(art::llvm::IntrinsicHelper::CmpgDouble,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+ case Instruction::CMP_LONG:
+ ConvertWideComparison(art::llvm::IntrinsicHelper::CmpLong,
+ rl_dest, rl_src[0], rl_src[1]);
+ break;
+
+ case Instruction::PACKED_SWITCH:
+ ConvertPackedSwitch(bb, vB, rl_src[0]);
+ break;
+
+ case Instruction::SPARSE_SWITCH:
+ ConvertSparseSwitch(bb, vB, rl_src[0]);
+ break;
+
+ default:
+ UNIMPLEMENTED(FATAL) << "Unsupported Dex opcode 0x" << std::hex << opcode;
+ res = true;
+ }
+ return res;
+}
+
+void MirConverter::SetDexOffset(int32_t offset)
+{
+ current_dalvik_offset_ = offset;
+ ::llvm::SmallVector< ::llvm::Value*, 1> array_ref;
+ array_ref.push_back(irb_->getInt32(offset));
+ ::llvm::MDNode* node = ::llvm::MDNode::get(*context_, array_ref);
+ irb_->SetDexOffset(node);
+}
+
+// Attach method info as metadata to special intrinsic
+void MirConverter::SetMethodInfo()
+{
+ // We don't want dex offset on this
+ irb_->SetDexOffset(NULL);
+ art::llvm::IntrinsicHelper::IntrinsicId id;
+ id = art::llvm::IntrinsicHelper::MethodInfo;
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::Instruction* inst = irb_->CreateCall(intr);
+ ::llvm::SmallVector< ::llvm::Value*, 2> reg_info;
+ reg_info.push_back(irb_->getInt32(cu_->num_ins));
+ reg_info.push_back(irb_->getInt32(cu_->num_regs));
+ reg_info.push_back(irb_->getInt32(cu_->num_outs));
+ reg_info.push_back(irb_->getInt32(cu_->num_compiler_temps));
+ reg_info.push_back(irb_->getInt32(mir_graph_->GetNumSSARegs()));
+ ::llvm::MDNode* reg_info_node = ::llvm::MDNode::get(*context_, reg_info);
+ inst->setMetadata("RegInfo", reg_info_node);
+ SetDexOffset(current_dalvik_offset_);
+}
+
+void MirConverter::HandlePhiNodes(BasicBlock* bb, ::llvm::BasicBlock* llvm_bb)
+{
+ SetDexOffset(bb->start_offset);
+ for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ int opcode = mir->dalvikInsn.opcode;
+ if (opcode < kMirOpFirst) {
+ // Stop after first non-pseudo MIR op.
+ continue;
+ }
+ if (opcode != kMirOpPhi) {
+ // Skip other mir Pseudos.
+ continue;
+ }
+ RegLocation rl_dest = mir_graph_->reg_location_[mir->ssa_rep->defs[0]];
+ /*
+ * The Art compiler's Phi nodes only handle 32-bit operands,
+ * representing wide values using a matched set of Phi nodes
+ * for the lower and upper halves. In the llvm world, we only
+ * want a single Phi for wides. Here we will simply discard
+ * the Phi node representing the high word.
+ */
+ if (rl_dest.high_word) {
+ continue; // No Phi node - handled via low word
+ }
+ int* incoming = reinterpret_cast<int*>(mir->dalvikInsn.vB);
+ ::llvm::Type* phi_type =
+ LlvmTypeFromLocRec(rl_dest);
+ ::llvm::PHINode* phi = irb_->CreatePHI(phi_type, mir->ssa_rep->num_uses);
+ for (int i = 0; i < mir->ssa_rep->num_uses; i++) {
+ RegLocation loc;
+ // Don't check width here.
+ loc = mir_graph_->GetRawSrc(mir, i);
+ DCHECK_EQ(rl_dest.wide, loc.wide);
+ DCHECK_EQ(rl_dest.wide & rl_dest.high_word, loc.wide & loc.high_word);
+ DCHECK_EQ(rl_dest.fp, loc.fp);
+ DCHECK_EQ(rl_dest.core, loc.core);
+ DCHECK_EQ(rl_dest.ref, loc.ref);
+ SafeMap<unsigned int, unsigned int>::iterator it;
+ it = mir_graph_->block_id_map_.find(incoming[i]);
+ DCHECK(it != mir_graph_->block_id_map_.end());
+ DCHECK(GetLLVMValue(loc.orig_sreg) != NULL);
+ DCHECK(GetLLVMBlock(it->second) != NULL);
+ phi->addIncoming(GetLLVMValue(loc.orig_sreg),
+ GetLLVMBlock(it->second));
+ }
+ DefineValueOnly(phi, rl_dest.orig_sreg);
+ }
+}
+
+/* Extended MIR instructions like PHI */
+void MirConverter::ConvertExtendedMIR(BasicBlock* bb, MIR* mir,
+ ::llvm::BasicBlock* llvm_bb)
+{
+
+ switch (static_cast<ExtendedMIROpcode>(mir->dalvikInsn.opcode)) {
+ case kMirOpPhi: {
+ // The llvm Phi node already emitted - just DefineValue() here.
+ RegLocation rl_dest = mir_graph_->reg_location_[mir->ssa_rep->defs[0]];
+ if (!rl_dest.high_word) {
+ // Only consider low word of pairs.
+ DCHECK(GetLLVMValue(rl_dest.orig_sreg) != NULL);
+ ::llvm::Value* phi = GetLLVMValue(rl_dest.orig_sreg);
+ if (1) SetVregOnValue(phi, rl_dest.orig_sreg);
+ }
+ break;
+ }
+ case kMirOpCopy: {
+ UNIMPLEMENTED(WARNING) << "unimp kMirOpPhi";
+ break;
+ }
+ case kMirOpNop:
+ if ((mir == bb->last_mir_insn) && (bb->taken == NULL) &&
+ (bb->fall_through == NULL)) {
+ irb_->CreateUnreachable();
+ }
+ break;
+
+ // TODO: need GBC intrinsic to take advantage of fused operations
+ case kMirOpFusedCmplFloat:
+ UNIMPLEMENTED(FATAL) << "kMirOpFusedCmpFloat unsupported";
+ break;
+ case kMirOpFusedCmpgFloat:
+ UNIMPLEMENTED(FATAL) << "kMirOpFusedCmgFloat unsupported";
+ break;
+ case kMirOpFusedCmplDouble:
+ UNIMPLEMENTED(FATAL) << "kMirOpFusedCmplDouble unsupported";
+ break;
+ case kMirOpFusedCmpgDouble:
+ UNIMPLEMENTED(FATAL) << "kMirOpFusedCmpgDouble unsupported";
+ break;
+ case kMirOpFusedCmpLong:
+ UNIMPLEMENTED(FATAL) << "kMirOpLongCmpBranch unsupported";
+ break;
+ default:
+ break;
+ }
+}
+
+/* Handle the content in each basic block */
+bool MirConverter::BlockBitcodeConversion(BasicBlock* bb)
+{
+ if (bb->block_type == kDead) return false;
+ ::llvm::BasicBlock* llvm_bb = GetLLVMBlock(bb->id);
+ if (llvm_bb == NULL) {
+ CHECK(bb->block_type == kExitBlock);
+ } else {
+ irb_->SetInsertPoint(llvm_bb);
+ SetDexOffset(bb->start_offset);
+ }
+
+ if (cu_->verbose) {
+ LOG(INFO) << "................................";
+ LOG(INFO) << "Block id " << bb->id;
+ if (llvm_bb != NULL) {
+ LOG(INFO) << "label " << llvm_bb->getName().str().c_str();
+ } else {
+ LOG(INFO) << "llvm_bb is NULL";
+ }
+ }
+
+ if (bb->block_type == kEntryBlock) {
+ SetMethodInfo();
+
+ { // Allocate shadowframe.
+ art::llvm::IntrinsicHelper::IntrinsicId id =
+ art::llvm::IntrinsicHelper::AllocaShadowFrame;
+ ::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id);
+ ::llvm::Value* entries = irb_->getInt32(cu_->num_dalvik_registers);
+ irb_->CreateCall(func, entries);
+ }
+
+ { // Store arguments to vregs.
+ uint16_t arg_reg = cu_->num_regs;
+
+ ::llvm::Function::arg_iterator arg_iter(func_->arg_begin());
+ ::llvm::Function::arg_iterator arg_end(func_->arg_end());
+
+ const char* shorty = cu_->shorty;
+ uint32_t shorty_size = strlen(shorty);
+ CHECK_GE(shorty_size, 1u);
+
+ ++arg_iter; // skip method object
+
+ if ((cu_->access_flags & kAccStatic) == 0) {
+ SetVregOnValue(arg_iter, arg_reg);
+ ++arg_iter;
+ ++arg_reg;
+ }
+
+ for (uint32_t i = 1; i < shorty_size; ++i, ++arg_iter) {
+ SetVregOnValue(arg_iter, arg_reg);
+
+ ++arg_reg;
+ if (shorty[i] == 'J' || shorty[i] == 'D') {
+ // Wide types, such as long and double, are using a pair of registers
+ // to store the value, so we have to increase arg_reg again.
+ ++arg_reg;
+ }
+ }
+ }
+ } else if (bb->block_type == kExitBlock) {
+ /*
+ * Because of the differences between how MIR/LIR and llvm handle exit
+ * blocks, we won't explicitly covert them. On the llvm-to-lir
+ * path, it will need to be regenereated.
+ */
+ return false;
+ } else if (bb->block_type == kExceptionHandling) {
+ /*
+ * Because we're deferring null checking, delete the associated empty
+ * exception block.
+ */
+ llvm_bb->eraseFromParent();
+ return false;
+ }
+
+ HandlePhiNodes(bb, llvm_bb);
+
+ for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+
+ SetDexOffset(mir->offset);
+
+ int opcode = mir->dalvikInsn.opcode;
+ Instruction::Format dalvik_format =
+ Instruction::FormatOf(mir->dalvikInsn.opcode);
+
+ if (opcode == kMirOpCheck) {
+ // Combine check and work halves of throwing instruction.
+ MIR* work_half = mir->meta.throw_insn;
+ mir->dalvikInsn.opcode = work_half->dalvikInsn.opcode;
+ opcode = mir->dalvikInsn.opcode;
+ SSARepresentation* ssa_rep = work_half->ssa_rep;
+ work_half->ssa_rep = mir->ssa_rep;
+ mir->ssa_rep = ssa_rep;
+ work_half->meta.original_opcode = work_half->dalvikInsn.opcode;
+ work_half->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+ if (bb->successor_block_list.block_list_type == kCatch) {
+ ::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(
+ art::llvm::IntrinsicHelper::CatchTargets);
+ ::llvm::Value* switch_key =
+ irb_->CreateCall(intr, irb_->getInt32(mir->offset));
+ GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_block_list.blocks);
+ // New basic block to use for work half
+ ::llvm::BasicBlock* work_bb =
+ ::llvm::BasicBlock::Create(*context_, "", func_);
+ ::llvm::SwitchInst* sw =
+ irb_->CreateSwitch(switch_key, work_bb,
+ bb->successor_block_list.blocks->Size());
+ while (true) {
+ SuccessorBlockInfo *successor_block_info = iter.Next();
+ if (successor_block_info == NULL) break;
+ ::llvm::BasicBlock *target =
+ GetLLVMBlock(successor_block_info->block->id);
+ int type_index = successor_block_info->key;
+ sw->addCase(irb_->getInt32(type_index), target);
+ }
+ llvm_bb = work_bb;
+ irb_->SetInsertPoint(llvm_bb);
+ }
+ }
+
+ if (opcode >= kMirOpFirst) {
+ ConvertExtendedMIR(bb, mir, llvm_bb);
+ continue;
+ }
+
+ bool not_handled = ConvertMIRNode(mir, bb, llvm_bb);
+ if (not_handled) {
+ Instruction::Code dalvik_opcode = static_cast<Instruction::Code>(opcode);
+ LOG(WARNING) << StringPrintf("%#06x: Op %#x (%s) / Fmt %d not handled",
+ mir->offset, opcode,
+ Instruction::Name(dalvik_opcode),
+ dalvik_format);
+ }
+ }
+
+ if (bb->block_type == kEntryBlock) {
+ entry_target_bb_ = GetLLVMBlock(bb->fall_through->id);
+ } else if ((bb->fall_through != NULL) && !bb->terminated_by_return) {
+ irb_->CreateBr(GetLLVMBlock(bb->fall_through->id));
+ }
+
+ return false;
+}
+
+char RemapShorty(char shorty_type) {
+ /*
+ * TODO: might want to revisit this. Dalvik registers are 32-bits wide,
+ * and longs/doubles are represented as a pair of registers. When sub-word
+ * arguments (and method results) are passed, they are extended to Dalvik
+ * virtual register containers. Because llvm is picky about type consistency,
+ * we must either cast the "real" type to 32-bit container multiple Dalvik
+ * register types, or always use the expanded values.
+ * Here, we're doing the latter. We map the shorty signature to container
+ * types (which is valid so long as we always do a real expansion of passed
+ * arguments and field loads).
+ */
+ switch(shorty_type) {
+ case 'Z' : shorty_type = 'I'; break;
+ case 'B' : shorty_type = 'I'; break;
+ case 'S' : shorty_type = 'I'; break;
+ case 'C' : shorty_type = 'I'; break;
+ default: break;
+ }
+ return shorty_type;
+}
+
+::llvm::FunctionType* MirConverter::GetFunctionType() {
+
+ // Get return type
+ ::llvm::Type* ret_type = irb_->getJType(RemapShorty(cu_->shorty[0]));
+
+ // Get argument type
+ std::vector< ::llvm::Type*> args_type;
+
+ // method object
+ args_type.push_back(irb_->getJMethodTy());
+
+ // Do we have a "this"?
+ if ((cu_->access_flags & kAccStatic) == 0) {
+ args_type.push_back(irb_->getJObjectTy());
+ }
+
+ for (uint32_t i = 1; i < strlen(cu_->shorty); ++i) {
+ args_type.push_back(irb_->getJType(RemapShorty(cu_->shorty[i])));
+ }
+
+ return ::llvm::FunctionType::get(ret_type, args_type, false);
+}
+
+bool MirConverter::CreateFunction() {
+ ::llvm::FunctionType* func_type = GetFunctionType();
+ if (func_type == NULL) {
+ return false;
+ }
+
+ func_ = ::llvm::Function::Create(func_type,
+ ::llvm::Function::InternalLinkage,
+ symbol_, module_);
+
+ ::llvm::Function::arg_iterator arg_iter(func_->arg_begin());
+ ::llvm::Function::arg_iterator arg_end(func_->arg_end());
+
+ arg_iter->setName("method");
+ ++arg_iter;
+
+ int start_sreg = cu_->num_regs;
+
+ for (unsigned i = 0; arg_iter != arg_end; ++i, ++arg_iter) {
+ arg_iter->setName(StringPrintf("v%i_0", start_sreg));
+ start_sreg += mir_graph_->reg_location_[start_sreg].wide ? 2 : 1;
+ }
+
+ return true;
+}
+
+bool MirConverter::CreateLLVMBasicBlock(BasicBlock* bb)
+{
+ // Skip the exit block
+ if ((bb->block_type == kDead) ||(bb->block_type == kExitBlock)) {
+ id_to_block_map_.Put(bb->id, NULL);
+ } else {
+ int offset = bb->start_offset;
+ bool entry_block = (bb->block_type == kEntryBlock);
+ ::llvm::BasicBlock* llvm_bb =
+ ::llvm::BasicBlock::Create(*context_, entry_block ? "entry" :
+ StringPrintf(kLabelFormat, bb->catch_entry ? kCatchBlock :
+ kNormalBlock, offset, bb->id), func_);
+ if (entry_block) {
+ entry_bb_ = llvm_bb;
+ placeholder_bb_ =
+ ::llvm::BasicBlock::Create(*context_, "placeholder",
+ func_);
+ }
+ id_to_block_map_.Put(bb->id, llvm_bb);
+ }
+ return false;
+}
+
+
+/*
+ * Convert MIR to LLVM_IR
+ * o For each ssa name, create LLVM named value. Type these
+ * appropriately, and ignore high half of wide and double operands.
+ * o For each MIR basic block, create an LLVM basic block.
+ * o Iterate through the MIR a basic block at a time, setting arguments
+ * to recovered ssa name.
+ */
+void MirConverter::MethodMIR2Bitcode()
+{
+ InitIR();
+
+ // Create the function
+ CreateFunction();
+
+ // Create an LLVM basic block for each MIR block in dfs preorder
+ PreOrderDfsIterator iter(mir_graph_, false /* not iterative */);
+ for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
+ CreateLLVMBasicBlock(bb);
+ }
+
+ /*
+ * Create an llvm named value for each MIR SSA name. Note: we'll use
+ * placeholders for all non-argument values (because we haven't seen
+ * the definition yet).
+ */
+ irb_->SetInsertPoint(placeholder_bb_);
+ ::llvm::Function::arg_iterator arg_iter(func_->arg_begin());
+ arg_iter++; /* Skip path method */
+ for (int i = 0; i < mir_graph_->GetNumSSARegs(); i++) {
+ ::llvm::Value* val;
+ RegLocation rl_temp = mir_graph_->reg_location_[i];
+ if ((mir_graph_->SRegToVReg(i) < 0) || rl_temp.high_word) {
+ llvm_values_.Insert(0);
+ } else if ((i < cu_->num_regs) ||
+ (i >= (cu_->num_regs + cu_->num_ins))) {
+ ::llvm::Constant* imm_value = mir_graph_->reg_location_[i].wide ?
+ irb_->getJLong(0) : irb_->getJInt(0);
+ val = EmitConst(imm_value, mir_graph_->reg_location_[i]);
+ val->setName(mir_graph_->GetSSAName(i));
+ llvm_values_.Insert(val);
+ } else {
+ // Recover previously-created argument values
+ ::llvm::Value* arg_val = arg_iter++;
+ llvm_values_.Insert(arg_val);
+ }
+ }
+
+ PreOrderDfsIterator iter2(mir_graph_, false /* not iterative */);
+ for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
+ BlockBitcodeConversion(bb);
+ }
+
+ /*
+ * In a few rare cases of verification failure, the verifier will
+ * replace one or more Dalvik opcodes with the special
+ * throw-verification-failure opcode. This can leave the SSA graph
+ * in an invalid state, as definitions may be lost, while uses retained.
+ * To work around this problem, we insert placeholder definitions for
+ * all Dalvik SSA regs in the "placeholder" block. Here, after
+ * bitcode conversion is complete, we examine those placeholder definitions
+ * and delete any with no references (which normally is all of them).
+ *
+ * If any definitions remain, we link the placeholder block into the
+ * CFG. Otherwise, it is deleted.
+ */
+ for (::llvm::BasicBlock::iterator it = placeholder_bb_->begin(),
+ it_end = placeholder_bb_->end(); it != it_end;) {
+ ::llvm::Instruction* inst = ::llvm::dyn_cast< ::llvm::Instruction>(it++);
+ DCHECK(inst != NULL);
+ ::llvm::Value* val = ::llvm::dyn_cast< ::llvm::Value>(inst);
+ DCHECK(val != NULL);
+ if (val->getNumUses() == 0) {
+ inst->eraseFromParent();
+ }
+ }
+ SetDexOffset(0);
+ if (placeholder_bb_->empty()) {
+ placeholder_bb_->eraseFromParent();
+ } else {
+ irb_->SetInsertPoint(placeholder_bb_);
+ irb_->CreateBr(entry_target_bb_);
+ entry_target_bb_ = placeholder_bb_;
+ }
+ irb_->SetInsertPoint(entry_bb_);
+ irb_->CreateBr(entry_target_bb_);
+
+ if (cu_->enable_debug & (1 << kDebugVerifyBitcode)) {
+ if (::llvm::verifyFunction(*func_, ::llvm::PrintMessageAction)) {
+ LOG(INFO) << "Bitcode verification FAILED for "
+ << PrettyMethod(cu_->method_idx, *cu_->dex_file)
+ << " of size " << cu_->code_item->insns_size_in_code_units_;
+ cu_->enable_debug |= (1 << kDebugDumpBitcodeFile);
+ }
+ }
+
+ if (cu_->enable_debug & (1 << kDebugDumpBitcodeFile)) {
+ // Write bitcode to file
+ std::string errmsg;
+ std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file));
+ mir_graph_->ReplaceSpecialChars(fname);
+ // TODO: make configurable change naming mechanism to avoid fname length issues.
+ fname = StringPrintf("/sdcard/Bitcode/%s.bc", fname.c_str());
+
+ if (fname.size() > 240) {
+ LOG(INFO) << "Warning: bitcode filename too long. Truncated.";
+ fname.resize(240);
+ }
+
+ ::llvm::OwningPtr< ::llvm::tool_output_file> out_file(
+ new ::llvm::tool_output_file(fname.c_str(), errmsg,
+ ::llvm::raw_fd_ostream::F_Binary));
+
+ if (!errmsg.empty()) {
+ LOG(ERROR) << "Failed to create bitcode output file: " << errmsg;
+ }
+
+ ::llvm::WriteBitcodeToFile(module_, out_file->os());
+ out_file->keep();
+ }
+}
+
+Backend* PortableCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena,
+ llvm::LlvmCompilationUnit* const llvm_compilation_unit) {
+ return new MirConverter(cu, mir_graph, arena, llvm_compilation_unit);
+}
+
+} // namespace art
diff --git a/compiler/dex/portable/mir_to_gbc.h b/compiler/dex/portable/mir_to_gbc.h
new file mode 100644
index 0000000..8aa0271
--- /dev/null
+++ b/compiler/dex/portable/mir_to_gbc.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_PORTABLE_MIRTOGBC_H_
+#define ART_SRC_COMPILER_DEX_PORTABLE_MIRTOGBC_H_
+
+#include "invoke_type.h"
+#include "compiled_method.h"
+#include "dex/compiler_enums.h"
+#include "dex/compiler_ir.h"
+#include "dex/backend.h"
+#include "llvm/llvm_compilation_unit.h"
+#include "safe_map.h"
+
+namespace art {
+
+struct BasicBlock;
+struct CallInfo;
+struct CompilationUnit;
+struct MIR;
+struct RegLocation;
+struct RegisterInfo;
+class MIRGraph;
+
+// Target-specific initialization.
+Backend* PortableCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena,
+ llvm::LlvmCompilationUnit* const llvm_compilation_unit);
+
+class MirConverter : public Backend {
+
+ public:
+ // TODO: flesh out and integrate into new world order.
+ MirConverter(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena,
+ llvm::LlvmCompilationUnit* llvm_compilation_unit)
+ : Backend(arena),
+ cu_(cu),
+ mir_graph_(mir_graph),
+ llvm_compilation_unit_(llvm_compilation_unit),
+ llvm_info_(llvm_compilation_unit->GetQuickContext()),
+ symbol_(llvm_compilation_unit->GetDexCompilationUnit()->GetSymbol()),
+ context_(NULL),
+ module_(NULL),
+ func_(NULL),
+ intrinsic_helper_(NULL),
+ irb_(NULL),
+ placeholder_bb_(NULL),
+ entry_bb_(NULL),
+ entry_target_bb_(NULL),
+ llvm_values_(arena, mir_graph->GetNumSSARegs()),
+ temp_name_(0),
+ current_dalvik_offset_(0) {
+ if (kIsDebugBuild) {
+ cu->enable_debug |= (1 << kDebugVerifyBitcode);
+ }
+ }
+
+ void Materialize() {
+ MethodMIR2Bitcode();
+ }
+
+ CompiledMethod* GetCompiledMethod() {
+ return NULL;
+ }
+
+ private:
+ ::llvm::BasicBlock* GetLLVMBlock(int id);
+ ::llvm::Value* GetLLVMValue(int s_reg);
+ void SetVregOnValue(::llvm::Value* val, int s_reg);
+ void DefineValueOnly(::llvm::Value* val, int s_reg);
+ void DefineValue(::llvm::Value* val, int s_reg);
+ ::llvm::Type* LlvmTypeFromLocRec(RegLocation loc);
+ void InitIR();
+ ::llvm::BasicBlock* FindCaseTarget(uint32_t vaddr);
+ void ConvertPackedSwitch(BasicBlock* bb, int32_t table_offset,
+ RegLocation rl_src);
+ void ConvertSparseSwitch(BasicBlock* bb, int32_t table_offset,
+ RegLocation rl_src);
+ void ConvertSget(int32_t field_index,
+ art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest);
+ void ConvertSput(int32_t field_index,
+ art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_src);
+ void ConvertFillArrayData(int32_t offset, RegLocation rl_array);
+ ::llvm::Value* EmitConst(::llvm::ArrayRef< ::llvm::Value*> src,
+ RegLocation loc);
+ void EmitPopShadowFrame();
+ ::llvm::Value* EmitCopy(::llvm::ArrayRef< ::llvm::Value*> src,
+ RegLocation loc);
+ void ConvertMoveException(RegLocation rl_dest);
+ void ConvertThrow(RegLocation rl_src);
+ void ConvertMonitorEnterExit(int opt_flags,
+ art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_src);
+ void ConvertArrayLength(int opt_flags, RegLocation rl_dest,
+ RegLocation rl_src);
+ void EmitSuspendCheck();
+ ::llvm::Value* ConvertCompare(ConditionCode cc,
+ ::llvm::Value* src1, ::llvm::Value* src2);
+ void ConvertCompareAndBranch(BasicBlock* bb, MIR* mir, ConditionCode cc,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void ConvertCompareZeroAndBranch(BasicBlock* bb, MIR* mir, ConditionCode cc,
+ RegLocation rl_src1);
+ ::llvm::Value* GenDivModOp(bool is_div, bool is_long, ::llvm::Value* src1,
+ ::llvm::Value* src2);
+ ::llvm::Value* GenArithOp(OpKind op, bool is_long, ::llvm::Value* src1,
+ ::llvm::Value* src2);
+ void ConvertFPArithOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2);
+ void ConvertShift(art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void ConvertShiftLit(art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_dest, RegLocation rl_src, int shift_amount);
+ void ConvertArithOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2);
+ void ConvertArithOpLit(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
+ int32_t imm);
+ void ConvertInvoke(BasicBlock* bb, MIR* mir, InvokeType invoke_type,
+ bool is_range, bool is_filled_new_array);
+ void ConvertConstObject(uint32_t idx,
+ art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest);
+ void ConvertCheckCast(uint32_t type_idx, RegLocation rl_src);
+ void ConvertNewInstance(uint32_t type_idx, RegLocation rl_dest);
+ void ConvertNewArray(uint32_t type_idx, RegLocation rl_dest,
+ RegLocation rl_src);
+ void ConvertAget(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_dest, RegLocation rl_array, RegLocation rl_index);
+ void ConvertAput(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_src, RegLocation rl_array, RegLocation rl_index);
+ void ConvertIget(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_dest, RegLocation rl_obj, int field_index);
+ void ConvertIput(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_src, RegLocation rl_obj, int field_index);
+ void ConvertInstanceOf(uint32_t type_idx, RegLocation rl_dest,
+ RegLocation rl_src);
+ void ConvertIntToLong(RegLocation rl_dest, RegLocation rl_src);
+ void ConvertLongToInt(RegLocation rl_dest, RegLocation rl_src);
+ void ConvertFloatToDouble(RegLocation rl_dest, RegLocation rl_src);
+ void ConvertDoubleToFloat(RegLocation rl_dest, RegLocation rl_src);
+ void ConvertWideComparison(art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void ConvertIntNarrowing(RegLocation rl_dest, RegLocation rl_src,
+ art::llvm::IntrinsicHelper::IntrinsicId id);
+ void ConvertNeg(RegLocation rl_dest, RegLocation rl_src);
+ void ConvertIntToFP(::llvm::Type* ty, RegLocation rl_dest, RegLocation rl_src);
+ void ConvertFPToInt(art::llvm::IntrinsicHelper::IntrinsicId id,
+ RegLocation rl_dest, RegLocation rl_src);
+ void ConvertNegFP(RegLocation rl_dest, RegLocation rl_src);
+ void ConvertNot(RegLocation rl_dest, RegLocation rl_src);
+ void EmitConstructorBarrier();
+ bool ConvertMIRNode(MIR* mir, BasicBlock* bb, ::llvm::BasicBlock* llvm_bb);
+ void SetDexOffset(int32_t offset);
+ void SetMethodInfo();
+ void HandlePhiNodes(BasicBlock* bb, ::llvm::BasicBlock* llvm_bb);
+ void ConvertExtendedMIR(BasicBlock* bb, MIR* mir, ::llvm::BasicBlock* llvm_bb);
+ bool BlockBitcodeConversion(BasicBlock* bb);
+ ::llvm::FunctionType* GetFunctionType();
+ bool CreateFunction();
+ bool CreateLLVMBasicBlock(BasicBlock* bb);
+ void MethodMIR2Bitcode();
+
+ CompilationUnit* cu_;
+ MIRGraph* mir_graph_;
+ llvm::LlvmCompilationUnit* const llvm_compilation_unit_;
+ LLVMInfo* llvm_info_;
+ std::string symbol_;
+ ::llvm::LLVMContext* context_;
+ ::llvm::Module* module_;
+ ::llvm::Function* func_;
+ art::llvm::IntrinsicHelper* intrinsic_helper_;
+ art::llvm::IRBuilder* irb_;
+ ::llvm::BasicBlock* placeholder_bb_;
+ ::llvm::BasicBlock* entry_bb_;
+ ::llvm::BasicBlock* entry_target_bb_;
+ std::string bitcode_filename_;
+ GrowableArray< ::llvm::Value*> llvm_values_;
+ int32_t temp_name_;
+ SafeMap<int32_t, ::llvm::BasicBlock*> id_to_block_map_; // block id -> llvm bb.
+ int current_dalvik_offset_;
+}; // Class MirConverter
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_PORTABLE_MIRTOGBC_H_
diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h
new file mode 100644
index 0000000..9dd7daf
--- /dev/null
+++ b/compiler/dex/quick/arm/arm_lir.h
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_QUICK_ARM_ARMLIR_H_
+#define ART_SRC_COMPILER_DEX_QUICK_ARM_ARMLIR_H_
+
+#include "dex/compiler_internals.h"
+
+namespace art {
+
+/*
+ * Runtime register usage conventions.
+ *
+ * r0-r3: Argument registers in both Dalvik and C/C++ conventions.
+ * However, for Dalvik->Dalvik calls we'll pass the target's Method*
+ * pointer in r0 as a hidden arg0. Otherwise used as codegen scratch
+ * registers.
+ * r0-r1: As in C/C++ r0 is 32-bit return register and r0/r1 is 64-bit
+ * r4 : (rARM_SUSPEND) is reserved (suspend check/debugger assist)
+ * r5 : Callee save (promotion target)
+ * r6 : Callee save (promotion target)
+ * r7 : Callee save (promotion target)
+ * r8 : Callee save (promotion target)
+ * r9 : (rARM_SELF) is reserved (pointer to thread-local storage)
+ * r10 : Callee save (promotion target)
+ * r11 : Callee save (promotion target)
+ * r12 : Scratch, may be trashed by linkage stubs
+ * r13 : (sp) is reserved
+ * r14 : (lr) is reserved
+ * r15 : (pc) is reserved
+ *
+ * 5 core temps that codegen can use (r0, r1, r2, r3, r12)
+ * 7 core registers that can be used for promotion
+ *
+ * Floating pointer registers
+ * s0-s31
+ * d0-d15, where d0={s0,s1}, d1={s2,s3}, ... , d15={s30,s31}
+ *
+ * s16-s31 (d8-d15) preserved across C calls
+ * s0-s15 (d0-d7) trashed across C calls
+ *
+ * s0-s15/d0-d7 used as codegen temp/scratch
+ * s16-s31/d8-d31 can be used for promotion.
+ *
+ * Calling convention
+ * o On a call to a Dalvik method, pass target's Method* in r0
+ * o r1-r3 will be used for up to the first 3 words of arguments
+ * o Arguments past the first 3 words will be placed in appropriate
+ * out slots by the caller.
+ * o If a 64-bit argument would span the register/memory argument
+ * boundary, it will instead be fully passed in the frame.
+ * o Maintain a 16-byte stack alignment
+ *
+ * Stack frame diagram (stack grows down, higher addresses at top):
+ *
+ * +------------------------+
+ * | IN[ins-1] | {Note: resides in caller's frame}
+ * | . |
+ * | IN[0] |
+ * | caller's Method* |
+ * +========================+ {Note: start of callee's frame}
+ * | spill region | {variable sized - will include lr if non-leaf.}
+ * +------------------------+
+ * | ...filler word... | {Note: used as 2nd word of V[locals-1] if long]
+ * +------------------------+
+ * | V[locals-1] |
+ * | V[locals-2] |
+ * | . |
+ * | . |
+ * | V[1] |
+ * | V[0] |
+ * +------------------------+
+ * | 0 to 3 words padding |
+ * +------------------------+
+ * | OUT[outs-1] |
+ * | OUT[outs-2] |
+ * | . |
+ * | OUT[0] |
+ * | cur_method* | <<== sp w/ 16-byte alignment
+ * +========================+
+ */
+
+// Offset to distingish FP regs.
+#define ARM_FP_REG_OFFSET 32
+// Offset to distinguish DP FP regs.
+#define ARM_FP_DOUBLE 64
+// First FP callee save.
+#define ARM_FP_CALLEE_SAVE_BASE 16
+// Reg types.
+#define ARM_REGTYPE(x) (x & (ARM_FP_REG_OFFSET | ARM_FP_DOUBLE))
+#define ARM_FPREG(x) ((x & ARM_FP_REG_OFFSET) == ARM_FP_REG_OFFSET)
+#define ARM_LOWREG(x) ((x & 0x7) == x)
+#define ARM_DOUBLEREG(x) ((x & ARM_FP_DOUBLE) == ARM_FP_DOUBLE)
+#define ARM_SINGLEREG(x) (ARM_FPREG(x) && !ARM_DOUBLEREG(x))
+
+/*
+ * Note: the low register of a floating point pair is sufficient to
+ * create the name of a double, but require both names to be passed to
+ * allow for asserts to verify that the pair is consecutive if significant
+ * rework is done in this area. Also, it is a good reminder in the calling
+ * code that reg locations always describe doubles as a pair of singles.
+ */
+#define ARM_S2D(x,y) ((x) | ARM_FP_DOUBLE)
+// Mask to strip off fp flags.
+#define ARM_FP_REG_MASK (ARM_FP_REG_OFFSET-1)
+
+// RegisterLocation templates return values (r0, or r0/r1).
+#define ARM_LOC_C_RETURN {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, r0, INVALID_REG,\
+ INVALID_SREG, INVALID_SREG}
+#define ARM_LOC_C_RETURN_WIDE {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1, \
+ INVALID_SREG, INVALID_SREG}
+#define ARM_LOC_C_RETURN_FLOAT ARM_LOC_C_RETURN
+#define ARM_LOC_C_RETURN_DOUBLE ARM_LOC_C_RETURN_WIDE
+
+enum ArmResourceEncodingPos {
+ kArmGPReg0 = 0,
+ kArmRegSP = 13,
+ kArmRegLR = 14,
+ kArmRegPC = 15,
+ kArmFPReg0 = 16,
+ kArmFPReg16 = 32,
+ kArmRegEnd = 48,
+};
+
+#define ENCODE_ARM_REG_LIST(N) (static_cast<uint64_t>(N))
+#define ENCODE_ARM_REG_SP (1ULL << kArmRegSP)
+#define ENCODE_ARM_REG_LR (1ULL << kArmRegLR)
+#define ENCODE_ARM_REG_PC (1ULL << kArmRegPC)
+#define ENCODE_ARM_REG_FPCS_LIST(N) (static_cast<uint64_t>(N) << kArmFPReg16)
+
+enum ArmNativeRegisterPool {
+ r0 = 0,
+ r1 = 1,
+ r2 = 2,
+ r3 = 3,
+ rARM_SUSPEND = 4,
+ r5 = 5,
+ r6 = 6,
+ r7 = 7,
+ r8 = 8,
+ rARM_SELF = 9,
+ r10 = 10,
+ r11 = 11,
+ r12 = 12,
+ r13sp = 13,
+ rARM_SP = 13,
+ r14lr = 14,
+ rARM_LR = 14,
+ r15pc = 15,
+ rARM_PC = 15,
+ fr0 = 0 + ARM_FP_REG_OFFSET,
+ fr1 = 1 + ARM_FP_REG_OFFSET,
+ fr2 = 2 + ARM_FP_REG_OFFSET,
+ fr3 = 3 + ARM_FP_REG_OFFSET,
+ fr4 = 4 + ARM_FP_REG_OFFSET,
+ fr5 = 5 + ARM_FP_REG_OFFSET,
+ fr6 = 6 + ARM_FP_REG_OFFSET,
+ fr7 = 7 + ARM_FP_REG_OFFSET,
+ fr8 = 8 + ARM_FP_REG_OFFSET,
+ fr9 = 9 + ARM_FP_REG_OFFSET,
+ fr10 = 10 + ARM_FP_REG_OFFSET,
+ fr11 = 11 + ARM_FP_REG_OFFSET,
+ fr12 = 12 + ARM_FP_REG_OFFSET,
+ fr13 = 13 + ARM_FP_REG_OFFSET,
+ fr14 = 14 + ARM_FP_REG_OFFSET,
+ fr15 = 15 + ARM_FP_REG_OFFSET,
+ fr16 = 16 + ARM_FP_REG_OFFSET,
+ fr17 = 17 + ARM_FP_REG_OFFSET,
+ fr18 = 18 + ARM_FP_REG_OFFSET,
+ fr19 = 19 + ARM_FP_REG_OFFSET,
+ fr20 = 20 + ARM_FP_REG_OFFSET,
+ fr21 = 21 + ARM_FP_REG_OFFSET,
+ fr22 = 22 + ARM_FP_REG_OFFSET,
+ fr23 = 23 + ARM_FP_REG_OFFSET,
+ fr24 = 24 + ARM_FP_REG_OFFSET,
+ fr25 = 25 + ARM_FP_REG_OFFSET,
+ fr26 = 26 + ARM_FP_REG_OFFSET,
+ fr27 = 27 + ARM_FP_REG_OFFSET,
+ fr28 = 28 + ARM_FP_REG_OFFSET,
+ fr29 = 29 + ARM_FP_REG_OFFSET,
+ fr30 = 30 + ARM_FP_REG_OFFSET,
+ fr31 = 31 + ARM_FP_REG_OFFSET,
+ dr0 = fr0 + ARM_FP_DOUBLE,
+ dr1 = fr2 + ARM_FP_DOUBLE,
+ dr2 = fr4 + ARM_FP_DOUBLE,
+ dr3 = fr6 + ARM_FP_DOUBLE,
+ dr4 = fr8 + ARM_FP_DOUBLE,
+ dr5 = fr10 + ARM_FP_DOUBLE,
+ dr6 = fr12 + ARM_FP_DOUBLE,
+ dr7 = fr14 + ARM_FP_DOUBLE,
+ dr8 = fr16 + ARM_FP_DOUBLE,
+ dr9 = fr18 + ARM_FP_DOUBLE,
+ dr10 = fr20 + ARM_FP_DOUBLE,
+ dr11 = fr22 + ARM_FP_DOUBLE,
+ dr12 = fr24 + ARM_FP_DOUBLE,
+ dr13 = fr26 + ARM_FP_DOUBLE,
+ dr14 = fr28 + ARM_FP_DOUBLE,
+ dr15 = fr30 + ARM_FP_DOUBLE,
+};
+
+// Target-independent aliases.
+#define rARM_ARG0 r0
+#define rARM_ARG1 r1
+#define rARM_ARG2 r2
+#define rARM_ARG3 r3
+#define rARM_FARG0 r0
+#define rARM_FARG1 r1
+#define rARM_FARG2 r2
+#define rARM_FARG3 r3
+#define rARM_RET0 r0
+#define rARM_RET1 r1
+#define rARM_INVOKE_TGT rARM_LR
+#define rARM_COUNT INVALID_REG
+
+enum ArmShiftEncodings {
+ kArmLsl = 0x0,
+ kArmLsr = 0x1,
+ kArmAsr = 0x2,
+ kArmRor = 0x3
+};
+
+/*
+ * The following enum defines the list of supported Thumb instructions by the
+ * assembler. Their corresponding EncodingMap positions will be defined in
+ * Assemble.cc.
+ */
+enum ArmOpcode {
+ kArmFirst = 0,
+ kArm16BitData = kArmFirst, // DATA [0] rd[15..0].
+ kThumbAdcRR, // adc [0100000101] rm[5..3] rd[2..0].
+ kThumbAddRRI3, // add(1) [0001110] imm_3[8..6] rn[5..3] rd[2..0]*/
+ kThumbAddRI8, // add(2) [00110] rd[10..8] imm_8[7..0].
+ kThumbAddRRR, // add(3) [0001100] rm[8..6] rn[5..3] rd[2..0].
+ kThumbAddRRLH, // add(4) [01000100] H12[01] rm[5..3] rd[2..0].
+ kThumbAddRRHL, // add(4) [01001000] H12[10] rm[5..3] rd[2..0].
+ kThumbAddRRHH, // add(4) [01001100] H12[11] rm[5..3] rd[2..0].
+ kThumbAddPcRel, // add(5) [10100] rd[10..8] imm_8[7..0].
+ kThumbAddSpRel, // add(6) [10101] rd[10..8] imm_8[7..0].
+ kThumbAddSpI7, // add(7) [101100000] imm_7[6..0].
+ kThumbAndRR, // and [0100000000] rm[5..3] rd[2..0].
+ kThumbAsrRRI5, // asr(1) [00010] imm_5[10..6] rm[5..3] rd[2..0].
+ kThumbAsrRR, // asr(2) [0100000100] rs[5..3] rd[2..0].
+ kThumbBCond, // b(1) [1101] cond[11..8] offset_8[7..0].
+ kThumbBUncond, // b(2) [11100] offset_11[10..0].
+ kThumbBicRR, // bic [0100001110] rm[5..3] rd[2..0].
+ kThumbBkpt, // bkpt [10111110] imm_8[7..0].
+ kThumbBlx1, // blx(1) [111] H[10] offset_11[10..0].
+ kThumbBlx2, // blx(1) [111] H[01] offset_11[10..0].
+ kThumbBl1, // blx(1) [111] H[10] offset_11[10..0].
+ kThumbBl2, // blx(1) [111] H[11] offset_11[10..0].
+ kThumbBlxR, // blx(2) [010001111] rm[6..3] [000].
+ kThumbBx, // bx [010001110] H2[6..6] rm[5..3] SBZ[000].
+ kThumbCmnRR, // cmn [0100001011] rm[5..3] rd[2..0].
+ kThumbCmpRI8, // cmp(1) [00101] rn[10..8] imm_8[7..0].
+ kThumbCmpRR, // cmp(2) [0100001010] rm[5..3] rd[2..0].
+ kThumbCmpLH, // cmp(3) [01000101] H12[01] rm[5..3] rd[2..0].
+ kThumbCmpHL, // cmp(3) [01000110] H12[10] rm[5..3] rd[2..0].
+ kThumbCmpHH, // cmp(3) [01000111] H12[11] rm[5..3] rd[2..0].
+ kThumbEorRR, // eor [0100000001] rm[5..3] rd[2..0].
+ kThumbLdmia, // ldmia [11001] rn[10..8] reglist [7..0].
+ kThumbLdrRRI5, // ldr(1) [01101] imm_5[10..6] rn[5..3] rd[2..0].
+ kThumbLdrRRR, // ldr(2) [0101100] rm[8..6] rn[5..3] rd[2..0].
+ kThumbLdrPcRel, // ldr(3) [01001] rd[10..8] imm_8[7..0].
+ kThumbLdrSpRel, // ldr(4) [10011] rd[10..8] imm_8[7..0].
+ kThumbLdrbRRI5, // ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0].
+ kThumbLdrbRRR, // ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0].
+ kThumbLdrhRRI5, // ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0].
+ kThumbLdrhRRR, // ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0].
+ kThumbLdrsbRRR, // ldrsb [0101011] rm[8..6] rn[5..3] rd[2..0].
+ kThumbLdrshRRR, // ldrsh [0101111] rm[8..6] rn[5..3] rd[2..0].
+ kThumbLslRRI5, // lsl(1) [00000] imm_5[10..6] rm[5..3] rd[2..0].
+ kThumbLslRR, // lsl(2) [0100000010] rs[5..3] rd[2..0].
+ kThumbLsrRRI5, // lsr(1) [00001] imm_5[10..6] rm[5..3] rd[2..0].
+ kThumbLsrRR, // lsr(2) [0100000011] rs[5..3] rd[2..0].
+ kThumbMovImm, // mov(1) [00100] rd[10..8] imm_8[7..0].
+ kThumbMovRR, // mov(2) [0001110000] rn[5..3] rd[2..0].
+ kThumbMovRR_H2H, // mov(3) [01000111] H12[11] rm[5..3] rd[2..0].
+ kThumbMovRR_H2L, // mov(3) [01000110] H12[01] rm[5..3] rd[2..0].
+ kThumbMovRR_L2H, // mov(3) [01000101] H12[10] rm[5..3] rd[2..0].
+ kThumbMul, // mul [0100001101] rm[5..3] rd[2..0].
+ kThumbMvn, // mvn [0100001111] rm[5..3] rd[2..0].
+ kThumbNeg, // neg [0100001001] rm[5..3] rd[2..0].
+ kThumbOrr, // orr [0100001100] rm[5..3] rd[2..0].
+ kThumbPop, // pop [1011110] r[8..8] rl[7..0].
+ kThumbPush, // push [1011010] r[8..8] rl[7..0].
+ kThumbRorRR, // ror [0100000111] rs[5..3] rd[2..0].
+ kThumbSbc, // sbc [0100000110] rm[5..3] rd[2..0].
+ kThumbStmia, // stmia [11000] rn[10..8] reglist [7.. 0].
+ kThumbStrRRI5, // str(1) [01100] imm_5[10..6] rn[5..3] rd[2..0].
+ kThumbStrRRR, // str(2) [0101000] rm[8..6] rn[5..3] rd[2..0].
+ kThumbStrSpRel, // str(3) [10010] rd[10..8] imm_8[7..0].
+ kThumbStrbRRI5, // strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0].
+ kThumbStrbRRR, // strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0].
+ kThumbStrhRRI5, // strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0].
+ kThumbStrhRRR, // strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0].
+ kThumbSubRRI3, // sub(1) [0001111] imm_3[8..6] rn[5..3] rd[2..0]*/
+ kThumbSubRI8, // sub(2) [00111] rd[10..8] imm_8[7..0].
+ kThumbSubRRR, // sub(3) [0001101] rm[8..6] rn[5..3] rd[2..0].
+ kThumbSubSpI7, // sub(4) [101100001] imm_7[6..0].
+ kThumbSwi, // swi [11011111] imm_8[7..0].
+ kThumbTst, // tst [0100001000] rm[5..3] rn[2..0].
+ kThumb2Vldrs, // vldr low sx [111011011001] rn[19..16] rd[15-12] [1010] imm_8[7..0].
+ kThumb2Vldrd, // vldr low dx [111011011001] rn[19..16] rd[15-12] [1011] imm_8[7..0].
+ kThumb2Vmuls, // vmul vd, vn, vm [111011100010] rn[19..16] rd[15-12] [10100000] rm[3..0].
+ kThumb2Vmuld, // vmul vd, vn, vm [111011100010] rn[19..16] rd[15-12] [10110000] rm[3..0].
+ kThumb2Vstrs, // vstr low sx [111011011000] rn[19..16] rd[15-12] [1010] imm_8[7..0].
+ kThumb2Vstrd, // vstr low dx [111011011000] rn[19..16] rd[15-12] [1011] imm_8[7..0].
+ kThumb2Vsubs, // vsub vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10100040] rm[3..0].
+ kThumb2Vsubd, // vsub vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10110040] rm[3..0].
+ kThumb2Vadds, // vadd vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10100000] rm[3..0].
+ kThumb2Vaddd, // vadd vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10110000] rm[3..0].
+ kThumb2Vdivs, // vdiv vd, vn, vm [111011101000] rn[19..16] rd[15-12] [10100000] rm[3..0].
+ kThumb2Vdivd, // vdiv vd, vn, vm [111011101000] rn[19..16] rd[15-12] [10110000] rm[3..0].
+ kThumb2VcvtIF, // vcvt.F32 vd, vm [1110111010111000] vd[15..12] [10101100] vm[3..0].
+ kThumb2VcvtID, // vcvt.F64 vd, vm [1110111010111000] vd[15..12] [10111100] vm[3..0].
+ kThumb2VcvtFI, // vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] [10101100] vm[3..0].
+ kThumb2VcvtDI, // vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] [10111100] vm[3..0].
+ kThumb2VcvtFd, // vcvt.F64.F32 vd, vm [1110111010110111] vd[15..12] [10101100] vm[3..0].
+ kThumb2VcvtDF, // vcvt.F32.F64 vd, vm [1110111010110111] vd[15..12] [10111100] vm[3..0].
+ kThumb2Vsqrts, // vsqrt.f32 vd, vm [1110111010110001] vd[15..12] [10101100] vm[3..0].
+ kThumb2Vsqrtd, // vsqrt.f64 vd, vm [1110111010110001] vd[15..12] [10111100] vm[3..0].
+ kThumb2MovImmShift,// mov(T2) rd, #<const> [11110] i [00001001111] imm3 rd[11..8] imm8.
+ kThumb2MovImm16, // mov(T3) rd, #<const> [11110] i [0010100] imm4 [0] imm3 rd[11..8] imm8.
+ kThumb2StrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0].
+ kThumb2LdrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0].
+ kThumb2StrRRI8Predec, // str(Imm,T4) rd,[rn,#-imm8] [111110000100] rn[19..16] rt[15..12] [1100] imm[7..0]*/
+ kThumb2LdrRRI8Predec, // ldr(Imm,T4) rd,[rn,#-imm8] [111110000101] rn[19..16] rt[15..12] [1100] imm[7..0]*/
+ kThumb2Cbnz, // cbnz rd,<label> [101110] i [1] imm5[7..3] rn[2..0].
+ kThumb2Cbz, // cbn rd,<label> [101100] i [1] imm5[7..3] rn[2..0].
+ kThumb2AddRRI12, // add rd, rn, #imm12 [11110] i [100000] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0].
+ kThumb2MovRR, // mov rd, rm [11101010010011110000] rd[11..8] [0000] rm[3..0].
+ kThumb2Vmovs, // vmov.f32 vd, vm [111011101] D [110000] vd[15..12] 101001] M [0] vm[3..0].
+ kThumb2Vmovd, // vmov.f64 vd, vm [111011101] D [110000] vd[15..12] 101101] M [0] vm[3..0].
+ kThumb2Ldmia, // ldmia [111010001001[ rn[19..16] mask[15..0].
+ kThumb2Stmia, // stmia [111010001000[ rn[19..16] mask[15..0].
+ kThumb2AddRRR, // add [111010110000] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2SubRRR, // sub [111010111010] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2SbcRRR, // sbc [111010110110] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2CmpRR, // cmp [111010111011] rn[19..16] [0000] [1111] [0000] rm[3..0].
+ kThumb2SubRRI12, // sub rd, rn, #imm12 [11110] i [01010] rn[19..16] [0] imm3[14..12] rd[11..8] imm8[7..0].
+ kThumb2MvnImm12, // mov(T2) rd, #<const> [11110] i [00011011110] imm3 rd[11..8] imm8.
+ kThumb2Sel, // sel rd, rn, rm [111110101010] rn[19-16] rd[11-8] rm[3-0].
+ kThumb2Ubfx, // ubfx rd,rn,#lsb,#width [111100111100] rn[19..16] [0] imm3[14-12] rd[11-8] w[4-0].
+ kThumb2Sbfx, // ubfx rd,rn,#lsb,#width [111100110100] rn[19..16] [0] imm3[14-12] rd[11-8] w[4-0].
+ kThumb2LdrRRR, // ldr rt,[rn,rm,LSL #imm] [111110000101] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2LdrhRRR, // ldrh rt,[rn,rm,LSL #imm] [111110000101] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2LdrshRRR, // ldrsh rt,[rn,rm,LSL #imm] [111110000101] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2LdrbRRR, // ldrb rt,[rn,rm,LSL #imm] [111110000101] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2LdrsbRRR, // ldrsb rt,[rn,rm,LSL #imm] [111110000101] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2StrRRR, // str rt,[rn,rm,LSL #imm] [111110000100] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2StrhRRR, // str rt,[rn,rm,LSL #imm] [111110000010] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2StrbRRR, // str rt,[rn,rm,LSL #imm] [111110000000] rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0].
+ kThumb2LdrhRRI12, // ldrh rt,[rn,#imm12] [111110001011] rt[15..12] rn[19..16] imm12[11..0].
+ kThumb2LdrshRRI12, // ldrsh rt,[rn,#imm12] [111110011011] rt[15..12] rn[19..16] imm12[11..0].
+ kThumb2LdrbRRI12, // ldrb rt,[rn,#imm12] [111110001001] rt[15..12] rn[19..16] imm12[11..0].
+ kThumb2LdrsbRRI12, // ldrsb rt,[rn,#imm12] [111110011001] rt[15..12] rn[19..16] imm12[11..0].
+ kThumb2StrhRRI12, // strh rt,[rn,#imm12] [111110001010] rt[15..12] rn[19..16] imm12[11..0].
+ kThumb2StrbRRI12, // strb rt,[rn,#imm12] [111110001000] rt[15..12] rn[19..16] imm12[11..0].
+ kThumb2Pop, // pop [1110100010111101] list[15-0]*/
+ kThumb2Push, // push [1110100100101101] list[15-0]*/
+ kThumb2CmpRI12, // cmp rn, #<const> [11110] i [011011] rn[19-16] [0] imm3 [1111] imm8[7..0].
+ kThumb2AdcRRR, // adc [111010110101] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2AndRRR, // and [111010100000] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2BicRRR, // bic [111010100010] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2CmnRR, // cmn [111010110001] rn[19..16] [0000] [1111] [0000] rm[3..0].
+ kThumb2EorRRR, // eor [111010101000] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2MulRRR, // mul [111110110000] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
+ kThumb2MnvRR, // mvn [11101010011011110] rd[11-8] [0000] rm[3..0].
+ kThumb2RsubRRI8, // rsub [111100011100] rn[19..16] [0000] rd[11..8] imm8[7..0].
+ kThumb2NegRR, // actually rsub rd, rn, #0.
+ kThumb2OrrRRR, // orr [111010100100] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2TstRR, // tst [111010100001] rn[19..16] [0000] [1111] [0000] rm[3..0].
+ kThumb2LslRRR, // lsl [111110100000] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
+ kThumb2LsrRRR, // lsr [111110100010] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
+ kThumb2AsrRRR, // asr [111110100100] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
+ kThumb2RorRRR, // ror [111110100110] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
+ kThumb2LslRRI5, // lsl [11101010010011110] imm[14.12] rd[11..8] [00] rm[3..0].
+ kThumb2LsrRRI5, // lsr [11101010010011110] imm[14.12] rd[11..8] [01] rm[3..0].
+ kThumb2AsrRRI5, // asr [11101010010011110] imm[14.12] rd[11..8] [10] rm[3..0].
+ kThumb2RorRRI5, // ror [11101010010011110] imm[14.12] rd[11..8] [11] rm[3..0].
+ kThumb2BicRRI8, // bic [111100000010] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2AndRRI8, // bic [111100000000] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2OrrRRI8, // orr [111100000100] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2EorRRI8, // eor [111100001000] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2AddRRI8, // add [111100001000] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2AdcRRI8, // adc [111100010101] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2SubRRI8, // sub [111100011011] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2SbcRRI8, // sbc [111100010111] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2It, // it [10111111] firstcond[7-4] mask[3-0].
+ kThumb2Fmstat, // fmstat [11101110111100011111101000010000].
+ kThumb2Vcmpd, // vcmp [111011101] D [11011] rd[15-12] [1011] E [1] M [0] rm[3-0].
+ kThumb2Vcmps, // vcmp [111011101] D [11010] rd[15-12] [1011] E [1] M [0] rm[3-0].
+ kThumb2LdrPcRel12, // ldr rd,[pc,#imm12] [1111100011011111] rt[15-12] imm12[11-0].
+ kThumb2BCond, // b<c> [1110] S cond[25-22] imm6[21-16] [10] J1 [0] J2 imm11[10..0].
+ kThumb2Vmovd_RR, // vmov [111011101] D [110000] vd[15-12 [101101] M [0] vm[3-0].
+ kThumb2Vmovs_RR, // vmov [111011101] D [110000] vd[15-12 [101001] M [0] vm[3-0].
+ kThumb2Fmrs, // vmov [111011100000] vn[19-16] rt[15-12] [1010] N [0010000].
+ kThumb2Fmsr, // vmov [111011100001] vn[19-16] rt[15-12] [1010] N [0010000].
+ kThumb2Fmrrd, // vmov [111011000100] rt2[19-16] rt[15-12] [101100] M [1] vm[3-0].
+ kThumb2Fmdrr, // vmov [111011000101] rt2[19-16] rt[15-12] [101100] M [1] vm[3-0].
+ kThumb2Vabsd, // vabs.f64 [111011101] D [110000] rd[15-12] [1011110] M [0] vm[3-0].
+ kThumb2Vabss, // vabs.f32 [111011101] D [110000] rd[15-12] [1010110] M [0] vm[3-0].
+ kThumb2Vnegd, // vneg.f64 [111011101] D [110000] rd[15-12] [1011110] M [0] vm[3-0].
+ kThumb2Vnegs, // vneg.f32 [111011101] D [110000] rd[15-12] [1010110] M [0] vm[3-0].
+ kThumb2Vmovs_IMM8, // vmov.f32 [111011101] D [11] imm4h[19-16] vd[15-12] [10100000] imm4l[3-0].
+ kThumb2Vmovd_IMM8, // vmov.f64 [111011101] D [11] imm4h[19-16] vd[15-12] [10110000] imm4l[3-0].
+ kThumb2Mla, // mla [111110110000] rn[19-16] ra[15-12] rd[7-4] [0000] rm[3-0].
+ kThumb2Umull, // umull [111110111010] rn[19-16], rdlo[15-12] rdhi[11-8] [0000] rm[3-0].
+ kThumb2Ldrex, // ldrex [111010000101] rn[19-16] rt[11-8] [1111] imm8[7-0].
+ kThumb2Strex, // strex [111010000100] rn[19-16] rt[11-8] rd[11-8] imm8[7-0].
+ kThumb2Clrex, // clrex [111100111011111110000111100101111].
+ kThumb2Bfi, // bfi [111100110110] rn[19-16] [0] imm3[14-12] rd[11-8] imm2[7-6] [0] msb[4-0].
+ kThumb2Bfc, // bfc [11110011011011110] [0] imm3[14-12] rd[11-8] imm2[7-6] [0] msb[4-0].
+ kThumb2Dmb, // dmb [1111001110111111100011110101] option[3-0].
+ kThumb2LdrPcReln12,// ldr rd,[pc,-#imm12] [1111100011011111] rt[15-12] imm12[11-0].
+ kThumb2Stm, // stm <list> [111010010000] rn[19-16] 000 rl[12-0].
+ kThumbUndefined, // undefined [11011110xxxxxxxx].
+ kThumb2VPopCS, // vpop <list of callee save fp singles (s16+).
+ kThumb2VPushCS, // vpush <list callee save fp singles (s16+).
+ kThumb2Vldms, // vldms rd, <list>.
+ kThumb2Vstms, // vstms rd, <list>.
+ kThumb2BUncond, // b <label>.
+ kThumb2MovImm16H, // similar to kThumb2MovImm16, but target high hw.
+ kThumb2AddPCR, // Thumb2 2-operand add with hard-coded PC target.
+ kThumb2Adr, // Special purpose encoding of ADR for switch tables.
+ kThumb2MovImm16LST,// Special purpose version for switch table use.
+ kThumb2MovImm16HST,// Special purpose version for switch table use.
+ kThumb2LdmiaWB, // ldmia [111010011001[ rn[19..16] mask[15..0].
+ kThumb2SubsRRI12, // setflags encoding.
+ kThumb2OrrRRRs, // orrx [111010100101] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2Push1, // t3 encoding of push.
+ kThumb2Pop1, // t3 encoding of pop.
+ kThumb2RsubRRR, // rsb [111010111101] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
+ kThumb2Smull, // smull [111110111000] rn[19-16], rdlo[15-12] rdhi[11-8] [0000] rm[3-0].
+ kThumb2LdrdPcRel8, // ldrd rt, rt2, pc +-/1024.
+ kThumb2LdrdI8, // ldrd rt, rt2, [rn +-/1024].
+ kThumb2StrdI8, // strd rt, rt2, [rn +-/1024].
+ kArmLast,
+};
+
+enum ArmOpDmbOptions {
+ kSY = 0xf,
+ kST = 0xe,
+ kISH = 0xb,
+ kISHST = 0xa,
+ kNSH = 0x7,
+ kNSHST = 0x6
+};
+
+// Instruction assembly field_loc kind.
+enum ArmEncodingKind {
+ kFmtUnused,
+ kFmtBitBlt, // Bit string using end/start.
+ kFmtDfp, // Double FP reg.
+ kFmtSfp, // Single FP reg.
+ kFmtModImm, // Shifted 8-bit immed using [26,14..12,7..0].
+ kFmtImm16, // Zero-extended immed using [26,19..16,14..12,7..0].
+ kFmtImm6, // Encoded branch target using [9,7..3]0.
+ kFmtImm12, // Zero-extended immediate using [26,14..12,7..0].
+ kFmtShift, // Shift descriptor, [14..12,7..4].
+ kFmtLsb, // least significant bit using [14..12][7..6].
+ kFmtBWidth, // bit-field width, encoded as width-1.
+ kFmtShift5, // Shift count, [14..12,7..6].
+ kFmtBrOffset, // Signed extended [26,11,13,21-16,10-0]:0.
+ kFmtFPImm, // Encoded floating point immediate.
+ kFmtOff24, // 24-bit Thumb2 unconditional branch encoding.
+};
+
+// Struct used to define the snippet positions for each Thumb opcode.
+struct ArmEncodingMap {
+ uint32_t skeleton;
+ struct {
+ ArmEncodingKind kind;
+ int end; // end for kFmtBitBlt, 1-bit slice end for FP regs.
+ int start; // start for kFmtBitBlt, 4-bit slice end for FP regs.
+ } field_loc[4];
+ ArmOpcode opcode;
+ uint64_t flags;
+ const char* name;
+ const char* fmt;
+ int size; // Note: size is in bytes.
+};
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_QUICK_ARM_ARMLIR_H_
diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc
new file mode 100644
index 0000000..e804215
--- /dev/null
+++ b/compiler/dex/quick/arm/assemble_arm.cc
@@ -0,0 +1,1397 @@
+/*
+ * Copyright (C) 2011 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 "arm_lir.h"
+#include "codegen_arm.h"
+#include "dex/quick/mir_to_lir-inl.h"
+
+namespace art {
+
+/*
+ * opcode: ArmOpcode enum
+ * skeleton: pre-designated bit-pattern for this opcode
+ * k0: key to applying ds/de
+ * ds: dest start bit position
+ * de: dest end bit position
+ * k1: key to applying s1s/s1e
+ * s1s: src1 start bit position
+ * s1e: src1 end bit position
+ * k2: key to applying s2s/s2e
+ * s2s: src2 start bit position
+ * s2e: src2 end bit position
+ * operands: number of operands (for sanity check purposes)
+ * name: mnemonic name
+ * fmt: for pretty-printing
+ */
+#define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \
+ k3, k3s, k3e, flags, name, fmt, size) \
+ {skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \
+ {k3, k3s, k3e}}, opcode, flags, name, fmt, size}
+
+/* Instruction dump string format keys: !pf, where "!" is the start
+ * of the key, "p" is which numeric operand to use and "f" is the
+ * print format.
+ *
+ * [p]ositions:
+ * 0 -> operands[0] (dest)
+ * 1 -> operands[1] (src1)
+ * 2 -> operands[2] (src2)
+ * 3 -> operands[3] (extra)
+ *
+ * [f]ormats:
+ * h -> 4-digit hex
+ * d -> decimal
+ * E -> decimal*4
+ * F -> decimal*2
+ * c -> branch condition (beq, bne, etc.)
+ * t -> pc-relative target
+ * u -> 1st half of bl[x] target
+ * v -> 2nd half ob bl[x] target
+ * R -> register list
+ * s -> single precision floating point register
+ * S -> double precision floating point register
+ * m -> Thumb2 modified immediate
+ * n -> complimented Thumb2 modified immediate
+ * M -> Thumb2 16-bit zero-extended immediate
+ * b -> 4-digit binary
+ * B -> dmb option string (sy, st, ish, ishst, nsh, hshst)
+ * H -> operand shift
+ * C -> core register name
+ * P -> fp cs register list (base of s16)
+ * Q -> fp cs register list (base of s0)
+ *
+ * [!] escape. To insert "!", use "!!"
+ */
+/* NOTE: must be kept in sync with enum ArmOpcode from LIR.h */
+const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = {
+ ENCODING_MAP(kArm16BitData, 0x0000,
+ kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP, "data", "0x!0h(!0d)", 2),
+ ENCODING_MAP(kThumbAdcRR, 0x4140,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES | USES_CCODES,
+ "adcs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbAddRRI3, 0x1c00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "adds", "!0C, !1C, #!2d", 2),
+ ENCODING_MAP(kThumbAddRI8, 0x3000,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
+ "adds", "!0C, !0C, #!1d", 2),
+ ENCODING_MAP(kThumbAddRRR, 0x1800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "adds", "!0C, !1C, !2C", 2),
+ ENCODING_MAP(kThumbAddRRLH, 0x4440,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+ "add", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbAddRRHL, 0x4480,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+ "add", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbAddRRHH, 0x44c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+ "add", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbAddPcRel, 0xa000,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | IS_BRANCH | NEEDS_FIXUP,
+ "add", "!0C, pc, #!1E", 2),
+ ENCODING_MAP(kThumbAddSpRel, 0xa800,
+ kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF_SP | REG_USE_SP,
+ "add", "!0C, sp, #!2E", 2),
+ ENCODING_MAP(kThumbAddSpI7, 0xb000,
+ kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
+ "add", "sp, #!0d*4", 2),
+ ENCODING_MAP(kThumbAndRR, 0x4000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "ands", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbAsrRRI5, 0x1000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "asrs", "!0C, !1C, #!2d", 2),
+ ENCODING_MAP(kThumbAsrRR, 0x4100,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "asrs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbBCond, 0xd000,
+ kFmtBitBlt, 7, 0, kFmtBitBlt, 11, 8, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | USES_CCODES |
+ NEEDS_FIXUP, "b!1c", "!0t", 2),
+ ENCODING_MAP(kThumbBUncond, 0xe000,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP,
+ "b", "!0t", 2),
+ ENCODING_MAP(kThumbBicRR, 0x4380,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "bics", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbBkpt, 0xbe00,
+ kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+ "bkpt", "!0d", 2),
+ ENCODING_MAP(kThumbBlx1, 0xf000,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR |
+ NEEDS_FIXUP, "blx_1", "!0u", 2),
+ ENCODING_MAP(kThumbBlx2, 0xe800,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR |
+ NEEDS_FIXUP, "blx_2", "!0v", 2),
+ ENCODING_MAP(kThumbBl1, 0xf000,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+ "bl_1", "!0u", 2),
+ ENCODING_MAP(kThumbBl2, 0xf800,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+ "bl_2", "!0v", 2),
+ ENCODING_MAP(kThumbBlxR, 0x4780,
+ kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_USE0 | IS_BRANCH | REG_DEF_LR,
+ "blx", "!0C", 2),
+ ENCODING_MAP(kThumbBx, 0x4700,
+ kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+ "bx", "!0C", 2),
+ ENCODING_MAP(kThumbCmnRR, 0x42c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmn", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbCmpRI8, 0x2800,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | SETS_CCODES,
+ "cmp", "!0C, #!1d", 2),
+ ENCODING_MAP(kThumbCmpRR, 0x4280,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbCmpLH, 0x4540,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbCmpHL, 0x4580,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbCmpHH, 0x45c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbEorRR, 0x4040,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "eors", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbLdmia, 0xc800,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
+ "ldmia", "!0C!!, <!1R>", 2),
+ ENCODING_MAP(kThumbLdrRRI5, 0x6800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldr", "!0C, [!1C, #!2E]", 2),
+ ENCODING_MAP(kThumbLdrRRR, 0x5800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldr", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbLdrPcRel, 0x4800,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC
+ | IS_LOAD | NEEDS_FIXUP, "ldr", "!0C, [pc, #!1E]", 2),
+ ENCODING_MAP(kThumbLdrSpRel, 0x9800,
+ kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_SP
+ | IS_LOAD, "ldr", "!0C, [sp, #!2E]", 2),
+ ENCODING_MAP(kThumbLdrbRRI5, 0x7800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrb", "!0C, [!1C, #2d]", 2),
+ ENCODING_MAP(kThumbLdrbRRR, 0x5c00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrb", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbLdrhRRI5, 0x8800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrh", "!0C, [!1C, #!2F]", 2),
+ ENCODING_MAP(kThumbLdrhRRR, 0x5a00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrh", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbLdrsbRRR, 0x5600,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrsb", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbLdrshRRR, 0x5e00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrsh", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbLslRRI5, 0x0000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "lsls", "!0C, !1C, #!2d", 2),
+ ENCODING_MAP(kThumbLslRR, 0x4080,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "lsls", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbLsrRRI5, 0x0800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "lsrs", "!0C, !1C, #!2d", 2),
+ ENCODING_MAP(kThumbLsrRR, 0x40c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "lsrs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbMovImm, 0x2000,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0 | SETS_CCODES,
+ "movs", "!0C, #!1d", 2),
+ ENCODING_MAP(kThumbMovRR, 0x1c00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "movs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbMovRR_H2H, 0x46c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbMovRR_H2L, 0x4640,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbMovRR_L2H, 0x4680,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbMul, 0x4340,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "muls", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbMvn, 0x43c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "mvns", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbNeg, 0x4240,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "negs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbOrr, 0x4300,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "orrs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbPop, 0xbc00,
+ kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
+ | IS_LOAD, "pop", "<!0R>", 2),
+ ENCODING_MAP(kThumbPush, 0xb400,
+ kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
+ | IS_STORE, "push", "<!0R>", 2),
+ ENCODING_MAP(kThumbRorRR, 0x41c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "rors", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbSbc, 0x4180,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | USES_CCODES | SETS_CCODES,
+ "sbcs", "!0C, !1C", 2),
+ ENCODING_MAP(kThumbStmia, 0xc000,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0 | REG_USE0 | REG_USE_LIST1 | IS_STORE,
+ "stmia", "!0C!!, <!1R>", 2),
+ ENCODING_MAP(kThumbStrRRI5, 0x6000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "str", "!0C, [!1C, #!2E]", 2),
+ ENCODING_MAP(kThumbStrRRR, 0x5000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+ "str", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbStrSpRel, 0x9000,
+ kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | REG_USE_SP
+ | IS_STORE, "str", "!0C, [sp, #!2E]", 2),
+ ENCODING_MAP(kThumbStrbRRI5, 0x7000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "strb", "!0C, [!1C, #!2d]", 2),
+ ENCODING_MAP(kThumbStrbRRR, 0x5400,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+ "strb", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbStrhRRI5, 0x8000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "strh", "!0C, [!1C, #!2F]", 2),
+ ENCODING_MAP(kThumbStrhRRR, 0x5200,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+ "strh", "!0C, [!1C, !2C]", 2),
+ ENCODING_MAP(kThumbSubRRI3, 0x1e00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "subs", "!0C, !1C, #!2d", 2),
+ ENCODING_MAP(kThumbSubRI8, 0x3800,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
+ "subs", "!0C, #!1d", 2),
+ ENCODING_MAP(kThumbSubRRR, 0x1a00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "subs", "!0C, !1C, !2C", 2),
+ ENCODING_MAP(kThumbSubSpI7, 0xb080,
+ kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
+ "sub", "sp, #!0d*4", 2),
+ ENCODING_MAP(kThumbSwi, 0xdf00,
+ kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+ "swi", "!0d", 2),
+ ENCODING_MAP(kThumbTst, 0x4200,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE01 | SETS_CCODES,
+ "tst", "!0C, !1C", 2),
+ ENCODING_MAP(kThumb2Vldrs, 0xed900a00,
+ kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD |
+ REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0s, [!1C, #!2E]", 4),
+ ENCODING_MAP(kThumb2Vldrd, 0xed900b00,
+ kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD |
+ REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0S, [!1C, #!2E]", 4),
+ ENCODING_MAP(kThumb2Vmuls, 0xee200a00,
+ kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vmuls", "!0s, !1s, !2s", 4),
+ ENCODING_MAP(kThumb2Vmuld, 0xee200b00,
+ kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vmuld", "!0S, !1S, !2S", 4),
+ ENCODING_MAP(kThumb2Vstrs, 0xed800a00,
+ kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "vstr", "!0s, [!1C, #!2E]", 4),
+ ENCODING_MAP(kThumb2Vstrd, 0xed800b00,
+ kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "vstr", "!0S, [!1C, #!2E]", 4),
+ ENCODING_MAP(kThumb2Vsubs, 0xee300a40,
+ kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vsub", "!0s, !1s, !2s", 4),
+ ENCODING_MAP(kThumb2Vsubd, 0xee300b40,
+ kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vsub", "!0S, !1S, !2S", 4),
+ ENCODING_MAP(kThumb2Vadds, 0xee300a00,
+ kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vadd", "!0s, !1s, !2s", 4),
+ ENCODING_MAP(kThumb2Vaddd, 0xee300b00,
+ kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vadd", "!0S, !1S, !2S", 4),
+ ENCODING_MAP(kThumb2Vdivs, 0xee800a00,
+ kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vdivs", "!0s, !1s, !2s", 4),
+ ENCODING_MAP(kThumb2Vdivd, 0xee800b00,
+ kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vdivd", "!0S, !1S, !2S", 4),
+ ENCODING_MAP(kThumb2VcvtIF, 0xeeb80ac0,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.f32", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2VcvtID, 0xeeb80bc0,
+ kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.f64", "!0S, !1s", 4),
+ ENCODING_MAP(kThumb2VcvtFI, 0xeebd0ac0,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.s32.f32 ", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2VcvtDI, 0xeebd0bc0,
+ kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.s32.f64 ", "!0s, !1S", 4),
+ ENCODING_MAP(kThumb2VcvtFd, 0xeeb70ac0,
+ kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.f64.f32 ", "!0S, !1s", 4),
+ ENCODING_MAP(kThumb2VcvtDF, 0xeeb70bc0,
+ kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.f32.f64 ", "!0s, !1S", 4),
+ ENCODING_MAP(kThumb2Vsqrts, 0xeeb10ac0,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vsqrt.f32 ", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2Vsqrtd, 0xeeb10bc0,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vsqrt.f64 ", "!0S, !1S", 4),
+ ENCODING_MAP(kThumb2MovImmShift, 0xf04f0000, /* no setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "mov", "!0C, #!1m", 4),
+ ENCODING_MAP(kThumb2MovImm16, 0xf2400000,
+ kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "mov", "!0C, #!1M", 4),
+ ENCODING_MAP(kThumb2StrRRI12, 0xf8c00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "str", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2LdrRRI12, 0xf8d00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldr", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2StrRRI8Predec, 0xf8400c00,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "str", "!0C, [!1C, #-!2d]", 4),
+ ENCODING_MAP(kThumb2LdrRRI8Predec, 0xf8500c00,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldr", "!0C, [!1C, #-!2d]", 4),
+ ENCODING_MAP(kThumb2Cbnz, 0xb900, /* Note: does not affect flags */
+ kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH |
+ NEEDS_FIXUP, "cbnz", "!0C,!1t", 2),
+ ENCODING_MAP(kThumb2Cbz, 0xb100, /* Note: does not affect flags */
+ kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH |
+ NEEDS_FIXUP, "cbz", "!0C,!1t", 2),
+ ENCODING_MAP(kThumb2AddRRI12, 0xf2000000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */
+ "add", "!0C,!1C,#!2d", 4),
+ ENCODING_MAP(kThumb2MovRR, 0xea4f0000, /* no setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov", "!0C, !1C", 4),
+ ENCODING_MAP(kThumb2Vmovs, 0xeeb00a40,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vmov.f32 ", " !0s, !1s", 4),
+ ENCODING_MAP(kThumb2Vmovd, 0xeeb00b40,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vmov.f64 ", " !0S, !1S", 4),
+ ENCODING_MAP(kThumb2Ldmia, 0xe8900000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
+ "ldmia", "!0C!!, <!1R>", 4),
+ ENCODING_MAP(kThumb2Stmia, 0xe8800000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | REG_USE_LIST1 | IS_STORE,
+ "stmia", "!0C!!, <!1R>", 4),
+ ENCODING_MAP(kThumb2AddRRR, 0xeb100000, /* setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "adds", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2SubRRR, 0xebb00000, /* setflags enconding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "subs", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2SbcRRR, 0xeb700000, /* setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | USES_CCODES | SETS_CCODES,
+ "sbcs", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2CmpRR, 0xebb00f00,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "!0C, !1C", 4),
+ ENCODING_MAP(kThumb2SubRRI12, 0xf2a00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */
+ "sub", "!0C,!1C,#!2d", 4),
+ ENCODING_MAP(kThumb2MvnImm12, 0xf06f0000, /* no setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtImm12, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "mvn", "!0C, #!1n", 4),
+ ENCODING_MAP(kThumb2Sel, 0xfaa0f080,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12 | USES_CCODES,
+ "sel", "!0C, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2Ubfx, 0xf3c00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1,
+ kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
+ "ubfx", "!0C, !1C, #!2d, #!3d", 4),
+ ENCODING_MAP(kThumb2Sbfx, 0xf3400000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1,
+ kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
+ "sbfx", "!0C, !1C, #!2d, #!3d", 4),
+ ENCODING_MAP(kThumb2LdrRRR, 0xf8500000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldr", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2LdrhRRR, 0xf8300000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrh", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2LdrshRRR, 0xf9300000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrsh", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2LdrbRRR, 0xf8100000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrb", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2LdrsbRRR, 0xf9100000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrsb", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2StrRRR, 0xf8400000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
+ "str", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2StrhRRR, 0xf8200000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
+ "strh", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2StrbRRR, 0xf8000000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
+ "strb", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ ENCODING_MAP(kThumb2LdrhRRI12, 0xf8b00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrh", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2LdrshRRI12, 0xf9b00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrsh", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2LdrbRRI12, 0xf8900000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrb", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2LdrsbRRI12, 0xf9900000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrsb", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2StrhRRI12, 0xf8a00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "strh", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2StrbRRI12, 0xf8800000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "strb", "!0C, [!1C, #!2d]", 4),
+ ENCODING_MAP(kThumb2Pop, 0xe8bd0000,
+ kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
+ | IS_LOAD | NEEDS_FIXUP, "pop", "<!0R>", 4),
+ ENCODING_MAP(kThumb2Push, 0xe92d0000,
+ kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
+ | IS_STORE | NEEDS_FIXUP, "push", "<!0R>", 4),
+ ENCODING_MAP(kThumb2CmpRI12, 0xf1b00f00,
+ kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_USE0 | SETS_CCODES,
+ "cmp", "!0C, #!1m", 4),
+ ENCODING_MAP(kThumb2AdcRRR, 0xeb500000, /* setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "adcs", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2AndRRR, 0xea000000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+ "and", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2BicRRR, 0xea200000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+ "bic", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2CmnRR, 0xeb000000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "cmn", "!0C, !1C, shift !2d", 4),
+ ENCODING_MAP(kThumb2EorRRR, 0xea800000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+ "eor", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2MulRRR, 0xfb00f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "mul", "!0C, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2MnvRR, 0xea6f0000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "mvn", "!0C, !1C, shift !2d", 4),
+ ENCODING_MAP(kThumb2RsubRRI8, 0xf1d00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "rsb", "!0C,!1C,#!2m", 4),
+ ENCODING_MAP(kThumb2NegRR, 0xf1d00000, /* instance of rsub */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "neg", "!0C,!1C", 4),
+ ENCODING_MAP(kThumb2OrrRRR, 0xea400000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+ "orr", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2TstRR, 0xea100f00,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
+ "tst", "!0C, !1C, shift !2d", 4),
+ ENCODING_MAP(kThumb2LslRRR, 0xfa00f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "lsl", "!0C, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2LsrRRR, 0xfa20f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "lsr", "!0C, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2AsrRRR, 0xfa40f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "asr", "!0C, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2RorRRR, 0xfa60f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "ror", "!0C, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2LslRRI5, 0xea4f0000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "lsl", "!0C, !1C, #!2d", 4),
+ ENCODING_MAP(kThumb2LsrRRI5, 0xea4f0010,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "lsr", "!0C, !1C, #!2d", 4),
+ ENCODING_MAP(kThumb2AsrRRI5, 0xea4f0020,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "asr", "!0C, !1C, #!2d", 4),
+ ENCODING_MAP(kThumb2RorRRI5, 0xea4f0030,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "ror", "!0C, !1C, #!2d", 4),
+ ENCODING_MAP(kThumb2BicRRI8, 0xf0200000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "bic", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2AndRRI8, 0xf0000000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "and", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2OrrRRI8, 0xf0400000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "orr", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2EorRRI8, 0xf0800000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "eor", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2AddRRI8, 0xf1100000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "adds", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2AdcRRI8, 0xf1500000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
+ "adcs", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2SubRRI8, 0xf1b00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "subs", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2SbcRRI8, 0xf1700000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
+ "sbcs", "!0C, !1C, #!2m", 4),
+ ENCODING_MAP(kThumb2It, 0xbf00,
+ kFmtBitBlt, 7, 4, kFmtBitBlt, 3, 0, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_IT | USES_CCODES,
+ "it:!1b", "!0c", 2),
+ ENCODING_MAP(kThumb2Fmstat, 0xeef1fa10,
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND | SETS_CCODES,
+ "fmstat", "", 4),
+ ENCODING_MAP(kThumb2Vcmpd, 0xeeb40b40,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
+ "vcmp.f64", "!0S, !1S", 4),
+ ENCODING_MAP(kThumb2Vcmps, 0xeeb40a40,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
+ "vcmp.f32", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2LdrPcRel12, 0xf8df0000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP,
+ "ldr", "!0C, [r15pc, #!1d]", 4),
+ ENCODING_MAP(kThumb2BCond, 0xf0008000,
+ kFmtBrOffset, -1, -1, kFmtBitBlt, 25, 22, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | IS_BRANCH | USES_CCODES | NEEDS_FIXUP,
+ "b!1c", "!0t", 4),
+ ENCODING_MAP(kThumb2Vmovd_RR, 0xeeb00b40,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vmov.f64", "!0S, !1S", 4),
+ ENCODING_MAP(kThumb2Vmovs_RR, 0xeeb00a40,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vmov.f32", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2Fmrs, 0xee100a10,
+ kFmtBitBlt, 15, 12, kFmtSfp, 7, 16, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "fmrs", "!0C, !1s", 4),
+ ENCODING_MAP(kThumb2Fmsr, 0xee000a10,
+ kFmtSfp, 7, 16, kFmtBitBlt, 15, 12, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "fmsr", "!0s, !1C", 4),
+ ENCODING_MAP(kThumb2Fmrrd, 0xec500b10,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF01_USE2,
+ "fmrrd", "!0C, !1C, !2S", 4),
+ ENCODING_MAP(kThumb2Fmdrr, 0xec400b10,
+ kFmtDfp, 5, 0, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "fmdrr", "!0S, !1C, !2C", 4),
+ ENCODING_MAP(kThumb2Vabsd, 0xeeb00bc0,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vabs.f64", "!0S, !1S", 4),
+ ENCODING_MAP(kThumb2Vabss, 0xeeb00ac0,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vabs.f32", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2Vnegd, 0xeeb10b40,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vneg.f64", "!0S, !1S", 4),
+ ENCODING_MAP(kThumb2Vnegs, 0xeeb10a40,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vneg.f32", "!0s, !1s", 4),
+ ENCODING_MAP(kThumb2Vmovs_IMM8, 0xeeb00a00,
+ kFmtSfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "vmov.f32", "!0s, #0x!1h", 4),
+ ENCODING_MAP(kThumb2Vmovd_IMM8, 0xeeb00b00,
+ kFmtDfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "vmov.f64", "!0S, #0x!1h", 4),
+ ENCODING_MAP(kThumb2Mla, 0xfb000000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 15, 12,
+ IS_QUAD_OP | REG_DEF0 | REG_USE1 | REG_USE2 | REG_USE3,
+ "mla", "!0C, !1C, !2C, !3C", 4),
+ ENCODING_MAP(kThumb2Umull, 0xfba00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
+ kFmtBitBlt, 3, 0,
+ IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | REG_USE3,
+ "umull", "!0C, !1C, !2C, !3C", 4),
+ ENCODING_MAP(kThumb2Ldrex, 0xe8500f00,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrex", "!0C, [!1C, #!2E]", 4),
+ ENCODING_MAP(kThumb2Strex, 0xe8400000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
+ kFmtBitBlt, 7, 0, IS_QUAD_OP | REG_DEF0_USE12 | IS_STORE,
+ "strex", "!0C,!1C, [!2C, #!2E]", 4),
+ ENCODING_MAP(kThumb2Clrex, 0xf3bf8f2f,
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND,
+ "clrex", "", 4),
+ ENCODING_MAP(kThumb2Bfi, 0xf3600000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtShift5, -1, -1,
+ kFmtBitBlt, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
+ "bfi", "!0C,!1C,#!2d,#!3d", 4),
+ ENCODING_MAP(kThumb2Bfc, 0xf36f0000,
+ kFmtBitBlt, 11, 8, kFmtShift5, -1, -1, kFmtBitBlt, 4, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0,
+ "bfc", "!0C,#!1d,#!2d", 4),
+ ENCODING_MAP(kThumb2Dmb, 0xf3bf8f50,
+ kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP,
+ "dmb","#!0B",4),
+ ENCODING_MAP(kThumb2LdrPcReln12, 0xf85f0000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD,
+ "ldr", "!0C, [r15pc, -#!1d]", 4),
+ ENCODING_MAP(kThumb2Stm, 0xe9000000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 12, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_USE0 | REG_USE_LIST1 | IS_STORE,
+ "stm", "!0C, <!1R>", 4),
+ ENCODING_MAP(kThumbUndefined, 0xde00,
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND,
+ "undefined", "", 2),
+ // NOTE: vpop, vpush hard-encoded for s16+ reg list
+ ENCODING_MAP(kThumb2VPopCS, 0xecbd8a00,
+ kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_FPCS_LIST0
+ | IS_LOAD, "vpop", "<!0P>", 4),
+ ENCODING_MAP(kThumb2VPushCS, 0xed2d8a00,
+ kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_FPCS_LIST0
+ | IS_STORE, "vpush", "<!0P>", 4),
+ ENCODING_MAP(kThumb2Vldms, 0xec900a00,
+ kFmtBitBlt, 19, 16, kFmtSfp, 22, 12, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_USE0 | REG_DEF_FPCS_LIST2
+ | IS_LOAD, "vldms", "!0C, <!2Q>", 4),
+ ENCODING_MAP(kThumb2Vstms, 0xec800a00,
+ kFmtBitBlt, 19, 16, kFmtSfp, 22, 12, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_USE0 | REG_USE_FPCS_LIST2
+ | IS_STORE, "vstms", "!0C, <!2Q>", 4),
+ ENCODING_MAP(kThumb2BUncond, 0xf0009000,
+ kFmtOff24, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH,
+ "b", "!0t", 4),
+ ENCODING_MAP(kThumb2MovImm16H, 0xf2c00000,
+ kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0,
+ "movt", "!0C, #!1M", 4),
+ ENCODING_MAP(kThumb2AddPCR, 0x4487,
+ kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_USE0 | IS_BRANCH,
+ "add", "rPC, !0C", 2),
+ ENCODING_MAP(kThumb2Adr, 0xf20f0000,
+ kFmtBitBlt, 11, 8, kFmtImm12, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ /* Note: doesn't affect flags */
+ IS_TERTIARY_OP | REG_DEF0 | NEEDS_FIXUP,
+ "adr", "!0C,#!1d", 4),
+ ENCODING_MAP(kThumb2MovImm16LST, 0xf2400000,
+ kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | NEEDS_FIXUP,
+ "mov", "!0C, #!1M", 4),
+ ENCODING_MAP(kThumb2MovImm16HST, 0xf2c00000,
+ kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0 | NEEDS_FIXUP,
+ "movt", "!0C, #!1M", 4),
+ ENCODING_MAP(kThumb2LdmiaWB, 0xe8b00000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
+ "ldmia", "!0C!!, <!1R>", 4),
+ ENCODING_MAP(kThumb2SubsRRI12, 0xf1b00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "subs", "!0C,!1C,#!2d", 4),
+ ENCODING_MAP(kThumb2OrrRRRs, 0xea500000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "orrs", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2Push1, 0xf84d0d04,
+ kFmtBitBlt, 15, 12, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE0
+ | IS_STORE, "push1", "!0C", 4),
+ ENCODING_MAP(kThumb2Pop1, 0xf85d0b04,
+ kFmtBitBlt, 15, 12, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF0
+ | IS_LOAD, "pop1", "!0C", 4),
+ ENCODING_MAP(kThumb2RsubRRR, 0xebd00000, /* setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "rsbs", "!0C, !1C, !2C!3H", 4),
+ ENCODING_MAP(kThumb2Smull, 0xfb800000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
+ kFmtBitBlt, 3, 0,
+ IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | REG_USE3,
+ "smull", "!0C, !1C, !2C, !3C", 4),
+ ENCODING_MAP(kThumb2LdrdPcRel8, 0xe9df0000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0 | REG_DEF1 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP,
+ "ldrd", "!0C, !1C, [pc, #!2E]", 4),
+ ENCODING_MAP(kThumb2LdrdI8, 0xe9d00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
+ kFmtBitBlt, 7, 0,
+ IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | IS_LOAD,
+ "ldrd", "!0C, !1C, [!2C, #!3E]", 4),
+ ENCODING_MAP(kThumb2StrdI8, 0xe9c00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
+ kFmtBitBlt, 7, 0,
+ IS_QUAD_OP | REG_USE0 | REG_USE1 | REG_USE2 | IS_STORE,
+ "strd", "!0C, !1C, [!2C, #!3E]", 4),
+};
+
+/*
+ * The fake NOP of moving r0 to r0 actually will incur data stalls if r0 is
+ * not ready. Since r5FP is not updated often, it is less likely to
+ * generate unnecessary stall cycles.
+ * TUNING: No longer true - find new NOP pattern.
+ */
+#define PADDING_MOV_R5_R5 0x1C2D
+
+/*
+ * Assemble the LIR into binary instruction format. Note that we may
+ * discover that pc-relative displacements may not fit the selected
+ * instruction.
+ */
+AssemblerStatus ArmMir2Lir::AssembleInstructions(uintptr_t start_addr)
+{
+ LIR* lir;
+ AssemblerStatus res = kSuccess; // Assume success
+
+ for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
+
+ if (lir->opcode < 0) {
+ /* 1 means padding is needed */
+ if ((lir->opcode == kPseudoPseudoAlign4) && (lir->operands[0] == 1)) {
+ code_buffer_.push_back(PADDING_MOV_R5_R5 & 0xFF);
+ code_buffer_.push_back((PADDING_MOV_R5_R5 >> 8) & 0xFF);
+ }
+ continue;
+ }
+
+ if (lir->flags.is_nop) {
+ continue;
+ }
+
+ /*
+ * For PC-relative displacements we won't know if the
+ * selected instruction will work until late (i.e. - now).
+ * If something doesn't fit, we must replace the short-form
+ * operation with a longer-form one. Note, though, that this
+ * can change code we've already processed, so we'll need to
+ * re-calculate offsets and restart. To limit the number of
+ * restarts, the entire list will be scanned and patched.
+ * Of course, the patching itself may cause new overflows so this
+ * is an iterative process.
+ */
+ if (lir->flags.pcRelFixup) {
+ if (lir->opcode == kThumbLdrPcRel ||
+ lir->opcode == kThumb2LdrPcRel12 ||
+ lir->opcode == kThumbAddPcRel ||
+ lir->opcode == kThumb2LdrdPcRel8 ||
+ ((lir->opcode == kThumb2Vldrd) && (lir->operands[1] == r15pc)) ||
+ ((lir->opcode == kThumb2Vldrs) && (lir->operands[1] == r15pc))) {
+ /*
+ * PC-relative loads are mostly used to load immediates
+ * that are too large to materialize directly in one shot.
+ * However, if the load displacement exceeds the limit,
+ * we revert to a multiple-instruction materialization sequence.
+ */
+ LIR *lir_target = lir->target;
+ uintptr_t pc = (lir->offset + 4) & ~3;
+ uintptr_t target = lir_target->offset;
+ int delta = target - pc;
+ if (delta & 0x3) {
+ LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
+ }
+ // First, a sanity check for cases we shouldn't see now
+ if (((lir->opcode == kThumbAddPcRel) && (delta > 1020)) ||
+ ((lir->opcode == kThumbLdrPcRel) && (delta > 1020))) {
+ // Shouldn't happen in current codegen.
+ LOG(FATAL) << "Unexpected pc-rel offset " << delta;
+ }
+ // Now, check for the difficult cases
+ if (((lir->opcode == kThumb2LdrPcRel12) && (delta > 4091)) ||
+ ((lir->opcode == kThumb2LdrdPcRel8) && (delta > 1020)) ||
+ ((lir->opcode == kThumb2Vldrs) && (delta > 1020)) ||
+ ((lir->opcode == kThumb2Vldrd) && (delta > 1020))) {
+ /*
+ * Note: because rARM_LR may be used to fix up out-of-range
+ * vldrs/vldrd we include REG_DEF_LR in the resource
+ * masks for these instructions.
+ */
+ int base_reg = ((lir->opcode == kThumb2LdrdPcRel8) || (lir->opcode == kThumb2LdrPcRel12))
+ ? lir->operands[0] : rARM_LR;
+
+ // Add new Adr to generate the address.
+ LIR* new_adr = RawLIR(lir->dalvik_offset, kThumb2Adr,
+ base_reg, 0, 0, 0, 0, lir->target);
+ InsertLIRBefore(lir, new_adr);
+
+ // Convert to normal load.
+ if (lir->opcode == kThumb2LdrPcRel12) {
+ lir->opcode = kThumb2LdrRRI12;
+ } else if (lir->opcode == kThumb2LdrdPcRel8) {
+ lir->opcode = kThumb2LdrdI8;
+ }
+ // Change the load to be relative to the new Adr base.
+ if (lir->opcode == kThumb2LdrdI8) {
+ lir->operands[3] = 0;
+ lir->operands[2] = base_reg;
+ } else {
+ lir->operands[2] = 0;
+ lir->operands[1] = base_reg;
+ }
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ } else {
+ if ((lir->opcode == kThumb2Vldrs) ||
+ (lir->opcode == kThumb2Vldrd) ||
+ (lir->opcode == kThumb2LdrdPcRel8)) {
+ lir->operands[2] = delta >> 2;
+ } else {
+ lir->operands[1] = (lir->opcode == kThumb2LdrPcRel12) ? delta :
+ delta >> 2;
+ }
+ }
+ } else if (lir->opcode == kThumb2Cbnz || lir->opcode == kThumb2Cbz) {
+ LIR *target_lir = lir->target;
+ uintptr_t pc = lir->offset + 4;
+ uintptr_t target = target_lir->offset;
+ int delta = target - pc;
+ if (delta > 126 || delta < 0) {
+ /*
+ * Convert to cmp rx,#0 / b[eq/ne] tgt pair
+ * Make new branch instruction and insert after
+ */
+ LIR* new_inst =
+ RawLIR(lir->dalvik_offset, kThumbBCond, 0,
+ (lir->opcode == kThumb2Cbz) ? kArmCondEq : kArmCondNe,
+ 0, 0, 0, lir->target);
+ InsertLIRAfter(lir, new_inst);
+ /* Convert the cb[n]z to a cmp rx, #0 ] */
+ lir->opcode = kThumbCmpRI8;
+ /* operand[0] is src1 in both cb[n]z & CmpRI8 */
+ lir->operands[1] = 0;
+ lir->target = 0;
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ } else {
+ lir->operands[1] = delta >> 1;
+ }
+ } else if (lir->opcode == kThumb2Push || lir->opcode == kThumb2Pop) {
+ if (__builtin_popcount(lir->operands[0]) == 1) {
+ /*
+ * The standard push/pop multiple instruction
+ * requires at least two registers in the list.
+ * If we've got just one, switch to the single-reg
+ * encoding.
+ */
+ lir->opcode = (lir->opcode == kThumb2Push) ? kThumb2Push1 :
+ kThumb2Pop1;
+ int reg = 0;
+ while (lir->operands[0]) {
+ if (lir->operands[0] & 0x1) {
+ break;
+ } else {
+ reg++;
+ lir->operands[0] >>= 1;
+ }
+ }
+ lir->operands[0] = reg;
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ }
+ } else if (lir->opcode == kThumbBCond || lir->opcode == kThumb2BCond) {
+ LIR *target_lir = lir->target;
+ int delta = 0;
+ DCHECK(target_lir);
+ uintptr_t pc = lir->offset + 4;
+ uintptr_t target = target_lir->offset;
+ delta = target - pc;
+ if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) {
+ lir->opcode = kThumb2BCond;
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ }
+ lir->operands[0] = delta >> 1;
+ } else if (lir->opcode == kThumb2BUncond) {
+ LIR *target_lir = lir->target;
+ uintptr_t pc = lir->offset + 4;
+ uintptr_t target = target_lir->offset;
+ int delta = target - pc;
+ lir->operands[0] = delta >> 1;
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) &&
+ lir->operands[0] == 0) { // Useless branch
+ lir->flags.is_nop = true;
+ res = kRetryAll;
+ }
+ } else if (lir->opcode == kThumbBUncond) {
+ LIR *target_lir = lir->target;
+ uintptr_t pc = lir->offset + 4;
+ uintptr_t target = target_lir->offset;
+ int delta = target - pc;
+ if (delta > 2046 || delta < -2048) {
+ // Convert to Thumb2BCond w/ kArmCondAl
+ lir->opcode = kThumb2BUncond;
+ lir->operands[0] = 0;
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ } else {
+ lir->operands[0] = delta >> 1;
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) &&
+ lir->operands[0] == -1) { // Useless branch
+ lir->flags.is_nop = true;
+ res = kRetryAll;
+ }
+ }
+ } else if (lir->opcode == kThumbBlx1) {
+ DCHECK(NEXT_LIR(lir)->opcode == kThumbBlx2);
+ /* cur_pc is Thumb */
+ uintptr_t cur_pc = (start_addr + lir->offset + 4) & ~3;
+ uintptr_t target = lir->operands[1];
+
+ /* Match bit[1] in target with base */
+ if (cur_pc & 0x2) {
+ target |= 0x2;
+ }
+ int delta = target - cur_pc;
+ DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+
+ lir->operands[0] = (delta >> 12) & 0x7ff;
+ NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+ } else if (lir->opcode == kThumbBl1) {
+ DCHECK(NEXT_LIR(lir)->opcode == kThumbBl2);
+ /* Both cur_pc and target are Thumb */
+ uintptr_t cur_pc = start_addr + lir->offset + 4;
+ uintptr_t target = lir->operands[1];
+
+ int delta = target - cur_pc;
+ DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+
+ lir->operands[0] = (delta >> 12) & 0x7ff;
+ NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+ } else if (lir->opcode == kThumb2Adr) {
+ SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[2]);
+ LIR* target = lir->target;
+ int target_disp = tab_rec ? tab_rec->offset
+ : target->offset;
+ int disp = target_disp - ((lir->offset + 4) & ~3);
+ if (disp < 4096) {
+ lir->operands[1] = disp;
+ } else {
+ // convert to ldimm16l, ldimm16h, add tgt, pc, operands[0]
+ // TUNING: if this case fires often, it can be improved. Not expected to be common.
+ LIR *new_mov16L =
+ RawLIR(lir->dalvik_offset, kThumb2MovImm16LST,
+ lir->operands[0], 0, reinterpret_cast<uintptr_t>(lir),
+ reinterpret_cast<uintptr_t>(tab_rec), 0, lir->target);
+ InsertLIRBefore(lir, new_mov16L);
+ LIR *new_mov16H =
+ RawLIR(lir->dalvik_offset, kThumb2MovImm16HST,
+ lir->operands[0], 0, reinterpret_cast<uintptr_t>(lir),
+ reinterpret_cast<uintptr_t>(tab_rec), 0, lir->target);
+ InsertLIRBefore(lir, new_mov16H);
+ if (ARM_LOWREG(lir->operands[0])) {
+ lir->opcode = kThumbAddRRLH;
+ } else {
+ lir->opcode = kThumbAddRRHH;
+ }
+ lir->operands[1] = rARM_PC;
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ }
+ } else if (lir->opcode == kThumb2MovImm16LST) {
+ // operands[1] should hold disp, [2] has add, [3] has tab_rec
+ LIR *addPCInst = reinterpret_cast<LIR*>(lir->operands[2]);
+ SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ // If tab_rec is null, this is a literal load. Use target
+ LIR* target = lir->target;
+ int target_disp = tab_rec ? tab_rec->offset : target->offset;
+ lir->operands[1] = (target_disp - (addPCInst->offset + 4)) & 0xffff;
+ } else if (lir->opcode == kThumb2MovImm16HST) {
+ // operands[1] should hold disp, [2] has add, [3] has tab_rec
+ LIR *addPCInst = reinterpret_cast<LIR*>(lir->operands[2]);
+ SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ // If tab_rec is null, this is a literal load. Use target
+ LIR* target = lir->target;
+ int target_disp = tab_rec ? tab_rec->offset : target->offset;
+ lir->operands[1] =
+ ((target_disp - (addPCInst->offset + 4)) >> 16) & 0xffff;
+ }
+ }
+ /*
+ * If one of the pc-relative instructions expanded we'll have
+ * to make another pass. Don't bother to fully assemble the
+ * instruction.
+ */
+ if (res != kSuccess) {
+ continue;
+ }
+ const ArmEncodingMap *encoder = &EncodingMap[lir->opcode];
+ uint32_t bits = encoder->skeleton;
+ int i;
+ for (i = 0; i < 4; i++) {
+ uint32_t operand;
+ uint32_t value;
+ operand = lir->operands[i];
+ switch (encoder->field_loc[i].kind) {
+ case kFmtUnused:
+ break;
+ case kFmtFPImm:
+ value = ((operand & 0xF0) >> 4) << encoder->field_loc[i].end;
+ value |= (operand & 0x0F) << encoder->field_loc[i].start;
+ bits |= value;
+ break;
+ case kFmtBrOffset:
+ value = ((operand & 0x80000) >> 19) << 26;
+ value |= ((operand & 0x40000) >> 18) << 11;
+ value |= ((operand & 0x20000) >> 17) << 13;
+ value |= ((operand & 0x1f800) >> 11) << 16;
+ value |= (operand & 0x007ff);
+ bits |= value;
+ break;
+ case kFmtShift5:
+ value = ((operand & 0x1c) >> 2) << 12;
+ value |= (operand & 0x03) << 6;
+ bits |= value;
+ break;
+ case kFmtShift:
+ value = ((operand & 0x70) >> 4) << 12;
+ value |= (operand & 0x0f) << 4;
+ bits |= value;
+ break;
+ case kFmtBWidth:
+ value = operand - 1;
+ bits |= value;
+ break;
+ case kFmtLsb:
+ value = ((operand & 0x1c) >> 2) << 12;
+ value |= (operand & 0x03) << 6;
+ bits |= value;
+ break;
+ case kFmtImm6:
+ value = ((operand & 0x20) >> 5) << 9;
+ value |= (operand & 0x1f) << 3;
+ bits |= value;
+ break;
+ case kFmtBitBlt:
+ value = (operand << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ bits |= value;
+ break;
+ case kFmtDfp: {
+ DCHECK(ARM_DOUBLEREG(operand));
+ DCHECK_EQ((operand & 0x1), 0U);
+ int reg_name = (operand & ARM_FP_REG_MASK) >> 1;
+ /* Snag the 1-bit slice and position it */
+ value = ((reg_name & 0x10) >> 4) << encoder->field_loc[i].end;
+ /* Extract and position the 4-bit slice */
+ value |= (reg_name & 0x0f) << encoder->field_loc[i].start;
+ bits |= value;
+ break;
+ }
+ case kFmtSfp:
+ DCHECK(ARM_SINGLEREG(operand));
+ /* Snag the 1-bit slice and position it */
+ value = (operand & 0x1) << encoder->field_loc[i].end;
+ /* Extract and position the 4-bit slice */
+ value |= ((operand & 0x1e) >> 1) << encoder->field_loc[i].start;
+ bits |= value;
+ break;
+ case kFmtImm12:
+ case kFmtModImm:
+ value = ((operand & 0x800) >> 11) << 26;
+ value |= ((operand & 0x700) >> 8) << 12;
+ value |= operand & 0x0ff;
+ bits |= value;
+ break;
+ case kFmtImm16:
+ value = ((operand & 0x0800) >> 11) << 26;
+ value |= ((operand & 0xf000) >> 12) << 16;
+ value |= ((operand & 0x0700) >> 8) << 12;
+ value |= operand & 0x0ff;
+ bits |= value;
+ break;
+ case kFmtOff24: {
+ uint32_t signbit = (operand >> 31) & 0x1;
+ uint32_t i1 = (operand >> 22) & 0x1;
+ uint32_t i2 = (operand >> 21) & 0x1;
+ uint32_t imm10 = (operand >> 11) & 0x03ff;
+ uint32_t imm11 = operand & 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;
+ bits |= value;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Bad fmt:" << encoder->field_loc[i].kind;
+ }
+ }
+ if (encoder->size == 4) {
+ code_buffer_.push_back((bits >> 16) & 0xff);
+ code_buffer_.push_back((bits >> 24) & 0xff);
+ }
+ code_buffer_.push_back(bits & 0xff);
+ code_buffer_.push_back((bits >> 8) & 0xff);
+ }
+ return res;
+}
+
+int ArmMir2Lir::GetInsnSize(LIR* lir)
+{
+ return EncodingMap[lir->opcode].size;
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
new file mode 100644
index 0000000..a6720ce
--- /dev/null
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/* This file contains codegen for the Thumb2 ISA. */
+
+#include "arm_lir.h"
+#include "codegen_arm.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+
+namespace art {
+
+
+/* Return the position of an ssa name within the argument list */
+int ArmMir2Lir::InPosition(int s_reg)
+{
+ int v_reg = mir_graph_->SRegToVReg(s_reg);
+ return v_reg - cu_->num_regs;
+}
+
+/*
+ * Describe an argument. If it's already in an arg register, just leave it
+ * there. NOTE: all live arg registers must be locked prior to this call
+ * to avoid having them allocated as a temp by downstream utilities.
+ */
+RegLocation ArmMir2Lir::ArgLoc(RegLocation loc)
+{
+ int arg_num = InPosition(loc.s_reg_low);
+ if (loc.wide) {
+ if (arg_num == 2) {
+ // Bad case - half in register, half in frame. Just punt
+ loc.location = kLocInvalid;
+ } else if (arg_num < 2) {
+ loc.low_reg = rARM_ARG1 + arg_num;
+ loc.high_reg = loc.low_reg + 1;
+ loc.location = kLocPhysReg;
+ } else {
+ loc.location = kLocDalvikFrame;
+ }
+ } else {
+ if (arg_num < 3) {
+ loc.low_reg = rARM_ARG1 + arg_num;
+ loc.location = kLocPhysReg;
+ } else {
+ loc.location = kLocDalvikFrame;
+ }
+ }
+ return loc;
+}
+
+/*
+ * Load an argument. If already in a register, just return. If in
+ * the frame, we can't use the normal LoadValue() because it assumed
+ * a proper frame - and we're frameless.
+ */
+RegLocation ArmMir2Lir::LoadArg(RegLocation loc)
+{
+ if (loc.location == kLocDalvikFrame) {
+ int start = (InPosition(loc.s_reg_low) + 1) * sizeof(uint32_t);
+ loc.low_reg = AllocTemp();
+ LoadWordDisp(rARM_SP, start, loc.low_reg);
+ if (loc.wide) {
+ loc.high_reg = AllocTemp();
+ LoadWordDisp(rARM_SP, start + sizeof(uint32_t), loc.high_reg);
+ }
+ loc.location = kLocPhysReg;
+ }
+ return loc;
+}
+
+/* Lock any referenced arguments that arrive in registers */
+void ArmMir2Lir::LockLiveArgs(MIR* mir)
+{
+ int first_in = cu_->num_regs;
+ const int num_arg_regs = 3; // TODO: generalize & move to RegUtil.cc
+ for (int i = 0; i < mir->ssa_rep->num_uses; i++) {
+ int v_reg = mir_graph_->SRegToVReg(mir->ssa_rep->uses[i]);
+ int InPosition = v_reg - first_in;
+ if (InPosition < num_arg_regs) {
+ LockTemp(rARM_ARG1 + InPosition);
+ }
+ }
+}
+
+/* Find the next MIR, which may be in a following basic block */
+// TODO: should this be a utility in mir_graph?
+MIR* ArmMir2Lir::GetNextMir(BasicBlock** p_bb, MIR* mir)
+{
+ BasicBlock* bb = *p_bb;
+ MIR* orig_mir = mir;
+ while (bb != NULL) {
+ if (mir != NULL) {
+ mir = mir->next;
+ }
+ if (mir != NULL) {
+ return mir;
+ } else {
+ bb = bb->fall_through;
+ *p_bb = bb;
+ if (bb) {
+ mir = bb->first_mir_insn;
+ if (mir != NULL) {
+ return mir;
+ }
+ }
+ }
+ }
+ return orig_mir;
+}
+
+/* Used for the "verbose" listing */
+//TODO: move to common code
+void ArmMir2Lir::GenPrintLabel(MIR* mir)
+{
+ /* Mark the beginning of a Dalvik instruction for line tracking */
+ char* inst_str = cu_->verbose ?
+ mir_graph_->GetDalvikDisassembly(mir) : NULL;
+ MarkBoundary(mir->offset, inst_str);
+}
+
+MIR* ArmMir2Lir::SpecialIGet(BasicBlock** bb, MIR* mir,
+ OpSize size, bool long_or_double, bool is_object)
+{
+ int field_offset;
+ bool is_volatile;
+ uint32_t field_idx = mir->dalvikInsn.vC;
+ bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false);
+ if (!fast_path || !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) {
+ return NULL;
+ }
+ RegLocation rl_obj = mir_graph_->GetSrc(mir, 0);
+ LockLiveArgs(mir);
+ rl_obj = ArmMir2Lir::ArgLoc(rl_obj);
+ RegLocation rl_dest;
+ if (long_or_double) {
+ rl_dest = GetReturnWide(false);
+ } else {
+ rl_dest = GetReturn(false);
+ }
+ // Point of no return - no aborts after this
+ ArmMir2Lir::GenPrintLabel(mir);
+ rl_obj = LoadArg(rl_obj);
+ GenIGet(field_idx, mir->optimization_flags, size, rl_dest, rl_obj, long_or_double, is_object);
+ return GetNextMir(bb, mir);
+}
+
+MIR* ArmMir2Lir::SpecialIPut(BasicBlock** bb, MIR* mir,
+ OpSize size, bool long_or_double, bool is_object)
+{
+ int field_offset;
+ bool is_volatile;
+ uint32_t field_idx = mir->dalvikInsn.vC;
+ bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false);
+ if (!fast_path || !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) {
+ return NULL;
+ }
+ RegLocation rl_src;
+ RegLocation rl_obj;
+ LockLiveArgs(mir);
+ if (long_or_double) {
+ rl_src = mir_graph_->GetSrcWide(mir, 0);
+ rl_obj = mir_graph_->GetSrc(mir, 2);
+ } else {
+ rl_src = mir_graph_->GetSrc(mir, 0);
+ rl_obj = mir_graph_->GetSrc(mir, 1);
+ }
+ rl_src = ArmMir2Lir::ArgLoc(rl_src);
+ rl_obj = ArmMir2Lir::ArgLoc(rl_obj);
+ // Reject if source is split across registers & frame
+ if (rl_obj.location == kLocInvalid) {
+ ResetRegPool();
+ return NULL;
+ }
+ // Point of no return - no aborts after this
+ ArmMir2Lir::GenPrintLabel(mir);
+ rl_obj = LoadArg(rl_obj);
+ rl_src = LoadArg(rl_src);
+ GenIPut(field_idx, mir->optimization_flags, size, rl_src, rl_obj, long_or_double, is_object);
+ return GetNextMir(bb, mir);
+}
+
+MIR* ArmMir2Lir::SpecialIdentity(MIR* mir)
+{
+ RegLocation rl_src;
+ RegLocation rl_dest;
+ bool wide = (mir->ssa_rep->num_uses == 2);
+ if (wide) {
+ rl_src = mir_graph_->GetSrcWide(mir, 0);
+ rl_dest = GetReturnWide(false);
+ } else {
+ rl_src = mir_graph_->GetSrc(mir, 0);
+ rl_dest = GetReturn(false);
+ }
+ LockLiveArgs(mir);
+ rl_src = ArmMir2Lir::ArgLoc(rl_src);
+ if (rl_src.location == kLocInvalid) {
+ ResetRegPool();
+ return NULL;
+ }
+ // Point of no return - no aborts after this
+ ArmMir2Lir::GenPrintLabel(mir);
+ rl_src = LoadArg(rl_src);
+ if (wide) {
+ StoreValueWide(rl_dest, rl_src);
+ } else {
+ StoreValue(rl_dest, rl_src);
+ }
+ return mir;
+}
+
+/*
+ * Special-case code genration for simple non-throwing leaf methods.
+ */
+void ArmMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
+ SpecialCaseHandler special_case)
+{
+ current_dalvik_offset_ = mir->offset;
+ MIR* next_mir = NULL;
+ switch (special_case) {
+ case kNullMethod:
+ DCHECK(mir->dalvikInsn.opcode == Instruction::RETURN_VOID);
+ next_mir = mir;
+ break;
+ case kConstFunction:
+ ArmMir2Lir::GenPrintLabel(mir);
+ LoadConstant(rARM_RET0, mir->dalvikInsn.vB);
+ next_mir = GetNextMir(&bb, mir);
+ break;
+ case kIGet:
+ next_mir = SpecialIGet(&bb, mir, kWord, false, false);
+ break;
+ case kIGetBoolean:
+ case kIGetByte:
+ next_mir = SpecialIGet(&bb, mir, kUnsignedByte, false, false);
+ break;
+ case kIGetObject:
+ next_mir = SpecialIGet(&bb, mir, kWord, false, true);
+ break;
+ case kIGetChar:
+ next_mir = SpecialIGet(&bb, mir, kUnsignedHalf, false, false);
+ break;
+ case kIGetShort:
+ next_mir = SpecialIGet(&bb, mir, kSignedHalf, false, false);
+ break;
+ case kIGetWide:
+ next_mir = SpecialIGet(&bb, mir, kLong, true, false);
+ break;
+ case kIPut:
+ next_mir = SpecialIPut(&bb, mir, kWord, false, false);
+ break;
+ case kIPutBoolean:
+ case kIPutByte:
+ next_mir = SpecialIPut(&bb, mir, kUnsignedByte, false, false);
+ break;
+ case kIPutObject:
+ next_mir = SpecialIPut(&bb, mir, kWord, false, true);
+ break;
+ case kIPutChar:
+ next_mir = SpecialIPut(&bb, mir, kUnsignedHalf, false, false);
+ break;
+ case kIPutShort:
+ next_mir = SpecialIPut(&bb, mir, kSignedHalf, false, false);
+ break;
+ case kIPutWide:
+ next_mir = SpecialIPut(&bb, mir, kLong, true, false);
+ break;
+ case kIdentity:
+ next_mir = SpecialIdentity(mir);
+ break;
+ default:
+ return;
+ }
+ if (next_mir != NULL) {
+ current_dalvik_offset_ = next_mir->offset;
+ if (special_case != kIdentity) {
+ ArmMir2Lir::GenPrintLabel(next_mir);
+ }
+ NewLIR1(kThumbBx, rARM_LR);
+ core_spill_mask_ = 0;
+ num_core_spills_ = 0;
+ fp_spill_mask_ = 0;
+ num_fp_spills_ = 0;
+ frame_size_ = 0;
+ core_vmap_table_.clear();
+ fp_vmap_table_.clear();
+ }
+}
+
+/*
+ * The sparse table in the literal pool is an array of <key,displacement>
+ * pairs. For each set, we'll load them as a pair using ldmia.
+ * This means that the register number of the temp we use for the key
+ * must be lower than the reg for the displacement.
+ *
+ * The test loop will look something like:
+ *
+ * adr rBase, <table>
+ * ldr r_val, [rARM_SP, v_reg_off]
+ * mov r_idx, #table_size
+ * lp:
+ * ldmia rBase!, {r_key, r_disp}
+ * sub r_idx, #1
+ * cmp r_val, r_key
+ * ifeq
+ * add rARM_PC, r_disp ; This is the branch from which we compute displacement
+ * cbnz r_idx, lp
+ */
+void ArmMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
+ RegLocation rl_src)
+{
+ const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ if (cu_->verbose) {
+ DumpSparseSwitchTable(table);
+ }
+ // Add the table to the list - we'll process it later
+ SwitchTable *tab_rec =
+ static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true,
+ ArenaAllocator::kAllocData));
+ tab_rec->table = table;
+ tab_rec->vaddr = current_dalvik_offset_;
+ int size = table[1];
+ tab_rec->targets = static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true,
+ ArenaAllocator::kAllocLIR));
+ switch_tables_.Insert(tab_rec);
+
+ // Get the switch value
+ rl_src = LoadValue(rl_src, kCoreReg);
+ int rBase = AllocTemp();
+ /* Allocate key and disp temps */
+ int r_key = AllocTemp();
+ int r_disp = AllocTemp();
+ // Make sure r_key's register number is less than r_disp's number for ldmia
+ if (r_key > r_disp) {
+ int tmp = r_disp;
+ r_disp = r_key;
+ r_key = tmp;
+ }
+ // Materialize a pointer to the switch table
+ NewLIR3(kThumb2Adr, rBase, 0, reinterpret_cast<uintptr_t>(tab_rec));
+ // Set up r_idx
+ int r_idx = AllocTemp();
+ LoadConstant(r_idx, size);
+ // Establish loop branch target
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ // Load next key/disp
+ NewLIR2(kThumb2LdmiaWB, rBase, (1 << r_key) | (1 << r_disp));
+ OpRegReg(kOpCmp, r_key, rl_src.low_reg);
+ // Go if match. NOTE: No instruction set switch here - must stay Thumb2
+ OpIT(kCondEq, "");
+ LIR* switch_branch = NewLIR1(kThumb2AddPCR, r_disp);
+ tab_rec->anchor = switch_branch;
+ // Needs to use setflags encoding here
+ NewLIR3(kThumb2SubsRRI12, r_idx, r_idx, 1);
+ OpCondBranch(kCondNe, target);
+}
+
+
+void ArmMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
+ RegLocation rl_src)
+{
+ const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ if (cu_->verbose) {
+ DumpPackedSwitchTable(table);
+ }
+ // Add the table to the list - we'll process it later
+ SwitchTable *tab_rec =
+ static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true,
+ ArenaAllocator::kAllocData));
+ tab_rec->table = table;
+ tab_rec->vaddr = current_dalvik_offset_;
+ int size = table[1];
+ tab_rec->targets =
+ static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true, ArenaAllocator::kAllocLIR));
+ switch_tables_.Insert(tab_rec);
+
+ // Get the switch value
+ rl_src = LoadValue(rl_src, kCoreReg);
+ int table_base = AllocTemp();
+ // Materialize a pointer to the switch table
+ NewLIR3(kThumb2Adr, table_base, 0, reinterpret_cast<uintptr_t>(tab_rec));
+ int low_key = s4FromSwitchData(&table[2]);
+ int keyReg;
+ // Remove the bias, if necessary
+ if (low_key == 0) {
+ keyReg = rl_src.low_reg;
+ } else {
+ keyReg = AllocTemp();
+ OpRegRegImm(kOpSub, keyReg, rl_src.low_reg, low_key);
+ }
+ // Bounds check - if < 0 or >= size continue following switch
+ OpRegImm(kOpCmp, keyReg, size-1);
+ LIR* branch_over = OpCondBranch(kCondHi, NULL);
+
+ // Load the displacement from the switch table
+ int disp_reg = AllocTemp();
+ LoadBaseIndexed(table_base, keyReg, disp_reg, 2, kWord);
+
+ // ..and go! NOTE: No instruction set switch here - must stay Thumb2
+ LIR* switch_branch = NewLIR1(kThumb2AddPCR, disp_reg);
+ tab_rec->anchor = switch_branch;
+
+ /* branch_over target here */
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ branch_over->target = target;
+}
+
+/*
+ * Array data table format:
+ * ushort ident = 0x0300 magic value
+ * ushort width width of each element in the table
+ * uint size number of elements in the table
+ * ubyte data[size*width] table of data values (may contain a single-byte
+ * padding at the end)
+ *
+ * Total size is 4+(width * size + 1)/2 16-bit code units.
+ */
+void ArmMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src)
+{
+ const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ // Add the table to the list - we'll process it later
+ FillArrayData *tab_rec =
+ static_cast<FillArrayData*>(arena_->NewMem(sizeof(FillArrayData), true,
+ ArenaAllocator::kAllocData));
+ tab_rec->table = table;
+ tab_rec->vaddr = current_dalvik_offset_;
+ uint16_t width = tab_rec->table[1];
+ uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
+ tab_rec->size = (size * width) + 8;
+
+ fill_array_data_.Insert(tab_rec);
+
+ // Making a call - use explicit registers
+ FlushAllRegs(); /* Everything to home location */
+ LoadValueDirectFixed(rl_src, r0);
+ LoadWordDisp(rARM_SELF, ENTRYPOINT_OFFSET(pHandleFillArrayDataFromCode),
+ rARM_LR);
+ // Materialize a pointer to the fill data image
+ NewLIR3(kThumb2Adr, r1, 0, reinterpret_cast<uintptr_t>(tab_rec));
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, rARM_LR);
+ MarkSafepointPC(call_inst);
+}
+
+/*
+ * Handle simple case (thin lock) inline. If it's complicated, bail
+ * out to the heavyweight lock/unlock routines. We'll use dedicated
+ * registers here in order to be in the right position in case we
+ * to bail to oat[Lock/Unlock]Object(self, object)
+ *
+ * r0 -> self pointer [arg0 for oat[Lock/Unlock]Object
+ * r1 -> object [arg1 for oat[Lock/Unlock]Object
+ * r2 -> intial contents of object->lock, later result of strex
+ * r3 -> self->thread_id
+ * r12 -> allow to be used by utilities as general temp
+ *
+ * The result of the strex is 0 if we acquire the lock.
+ *
+ * See comments in monitor.cc for the layout of the lock word.
+ * Of particular interest to this code is the test for the
+ * simple case - which we handle inline. For monitor enter, the
+ * simple case is thin lock, held by no-one. For monitor exit,
+ * the simple case is thin lock, held by the unlocking thread with
+ * a recurse count of 0.
+ *
+ * A minor complication is that there is a field in the lock word
+ * unrelated to locking: the hash state. This field must be ignored, but
+ * preserved.
+ *
+ */
+void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src)
+{
+ FlushAllRegs();
+ DCHECK_EQ(LW_SHAPE_THIN, 0);
+ LoadValueDirectFixed(rl_src, r0); // Get obj
+ LockCallTemps(); // Prepare for explicit register usage
+ GenNullCheck(rl_src.s_reg_low, r0, opt_flags);
+ LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
+ NewLIR3(kThumb2Ldrex, r1, r0,
+ mirror::Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
+ // Align owner
+ OpRegImm(kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
+ // Is lock unheld on lock or held by us (==thread_id) on unlock?
+ NewLIR4(kThumb2Bfi, r2, r1, 0, LW_LOCK_OWNER_SHIFT - 1);
+ NewLIR3(kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
+ OpRegImm(kOpCmp, r1, 0);
+ OpIT(kCondEq, "");
+ NewLIR4(kThumb2Strex, r1, r2, r0,
+ mirror::Object::MonitorOffset().Int32Value() >> 2);
+ OpRegImm(kOpCmp, r1, 0);
+ OpIT(kCondNe, "T");
+ // Go expensive route - artLockObjectFromCode(self, obj);
+ LoadWordDisp(rARM_SELF, ENTRYPOINT_OFFSET(pLockObjectFromCode), rARM_LR);
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, rARM_LR);
+ MarkSafepointPC(call_inst);
+ GenMemBarrier(kLoadLoad);
+}
+
+/*
+ * For monitor unlock, we don't have to use ldrex/strex. Once
+ * we've determined that the lock is thin and that we own it with
+ * a zero recursion count, it's safe to punch it back to the
+ * initial, unlock thin state with a store word.
+ */
+void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src)
+{
+ DCHECK_EQ(LW_SHAPE_THIN, 0);
+ FlushAllRegs();
+ LoadValueDirectFixed(rl_src, r0); // Get obj
+ LockCallTemps(); // Prepare for explicit register usage
+ GenNullCheck(rl_src.s_reg_low, r0, opt_flags);
+ LoadWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r1); // Get lock
+ LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
+ // Is lock unheld on lock or held by us (==thread_id) on unlock?
+ OpRegRegImm(kOpAnd, r3, r1,
+ (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
+ // Align owner
+ OpRegImm(kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
+ NewLIR3(kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
+ OpRegReg(kOpSub, r1, r2);
+ OpIT(kCondEq, "EE");
+ StoreWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r3);
+ // Go expensive route - UnlockObjectFromCode(obj);
+ LoadWordDisp(rARM_SELF, ENTRYPOINT_OFFSET(pUnlockObjectFromCode), rARM_LR);
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, rARM_LR);
+ MarkSafepointPC(call_inst);
+ GenMemBarrier(kStoreLoad);
+}
+
+void ArmMir2Lir::GenMoveException(RegLocation rl_dest)
+{
+ int ex_offset = Thread::ExceptionOffset().Int32Value();
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int reset_reg = AllocTemp();
+ LoadWordDisp(rARM_SELF, ex_offset, rl_result.low_reg);
+ LoadConstant(reset_reg, 0);
+ StoreWordDisp(rARM_SELF, ex_offset, reset_reg);
+ FreeTemp(reset_reg);
+ StoreValue(rl_dest, rl_result);
+}
+
+/*
+ * Mark garbage collection card. Skip if the value we're storing is null.
+ */
+void ArmMir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg)
+{
+ int reg_card_base = AllocTemp();
+ int reg_card_no = AllocTemp();
+ LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL);
+ LoadWordDisp(rARM_SELF, Thread::CardTableOffset().Int32Value(), reg_card_base);
+ OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
+ StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0,
+ kUnsignedByte);
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ branch_over->target = target;
+ FreeTemp(reg_card_base);
+ FreeTemp(reg_card_no);
+}
+
+void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method)
+{
+ int spill_count = num_core_spills_ + num_fp_spills_;
+ /*
+ * On entry, r0, r1, r2 & r3 are live. Let the register allocation
+ * mechanism know so it doesn't try to use any of them when
+ * expanding the frame or flushing. This leaves the utility
+ * code with a single temp: r12. This should be enough.
+ */
+ LockTemp(r0);
+ LockTemp(r1);
+ LockTemp(r2);
+ LockTemp(r3);
+
+ /*
+ * We can safely skip the stack overflow check if we're
+ * a leaf *and* our frame size < fudge factor.
+ */
+ bool skip_overflow_check = (mir_graph_->MethodIsLeaf() &&
+ (static_cast<size_t>(frame_size_) <
+ Thread::kStackOverflowReservedBytes));
+ NewLIR0(kPseudoMethodEntry);
+ if (!skip_overflow_check) {
+ /* Load stack limit */
+ LoadWordDisp(rARM_SELF, Thread::StackEndOffset().Int32Value(), r12);
+ }
+ /* Spill core callee saves */
+ NewLIR1(kThumb2Push, core_spill_mask_);
+ /* Need to spill any FP regs? */
+ if (num_fp_spills_) {
+ /*
+ * NOTE: fp spills are a little different from core spills in that
+ * they are pushed as a contiguous block. When promoting from
+ * the fp set, we must allocate all singles from s16..highest-promoted
+ */
+ NewLIR1(kThumb2VPushCS, num_fp_spills_);
+ }
+ if (!skip_overflow_check) {
+ OpRegRegImm(kOpSub, rARM_LR, rARM_SP, frame_size_ - (spill_count * 4));
+ GenRegRegCheck(kCondCc, rARM_LR, r12, kThrowStackOverflow);
+ OpRegCopy(rARM_SP, rARM_LR); // Establish stack
+ } else {
+ OpRegImm(kOpSub, rARM_SP, frame_size_ - (spill_count * 4));
+ }
+
+ FlushIns(ArgLocs, rl_method);
+
+ FreeTemp(r0);
+ FreeTemp(r1);
+ FreeTemp(r2);
+ FreeTemp(r3);
+}
+
+void ArmMir2Lir::GenExitSequence()
+{
+ int spill_count = num_core_spills_ + num_fp_spills_;
+ /*
+ * In the exit path, r0/r1 are live - make sure they aren't
+ * allocated by the register utilities as temps.
+ */
+ LockTemp(r0);
+ LockTemp(r1);
+
+ NewLIR0(kPseudoMethodExit);
+ OpRegImm(kOpAdd, rARM_SP, frame_size_ - (spill_count * 4));
+ /* Need to restore any FP callee saves? */
+ if (num_fp_spills_) {
+ NewLIR1(kThumb2VPopCS, num_fp_spills_);
+ }
+ if (core_spill_mask_ & (1 << rARM_LR)) {
+ /* Unspill rARM_LR to rARM_PC */
+ core_spill_mask_ &= ~(1 << rARM_LR);
+ core_spill_mask_ |= (1 << rARM_PC);
+ }
+ NewLIR1(kThumb2Pop, core_spill_mask_);
+ if (!(core_spill_mask_ & (1 << rARM_PC))) {
+ /* We didn't pop to rARM_PC, so must do a bv rARM_LR */
+ NewLIR1(kThumbBx, rARM_LR);
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
new file mode 100644
index 0000000..a9199df
--- /dev/null
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_QUICK_ARM_CODEGENARM_H_
+#define ART_SRC_COMPILER_DEX_QUICK_ARM_CODEGENARM_H_
+
+#include "dex/compiler_internals.h"
+
+namespace art {
+
+class ArmMir2Lir : public Mir2Lir {
+ public:
+ ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
+
+ // Required for target - codegen helpers.
+ bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src,
+ RegLocation rl_dest, int lit);
+ int LoadHelper(int offset);
+ LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg);
+ LIR* LoadBaseDispWide(int rBase, int displacement, int r_dest_lo, int r_dest_hi,
+ int s_reg);
+ LIR* LoadBaseIndexed(int rBase, int r_index, int r_dest, int scale, OpSize size);
+ LIR* LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
+ int r_dest, int r_dest_hi, OpSize size, int s_reg);
+ LIR* LoadConstantNoClobber(int r_dest, int value);
+ LIR* LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value);
+ LIR* StoreBaseDisp(int rBase, int displacement, int r_src, OpSize size);
+ LIR* StoreBaseDispWide(int rBase, int displacement, int r_src_lo, int r_src_hi);
+ LIR* StoreBaseIndexed(int rBase, int r_index, int r_src, int scale, OpSize size);
+ LIR* StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
+ int r_src, int r_src_hi, OpSize size, int s_reg);
+ void MarkGCCard(int val_reg, int tgt_addr_reg);
+
+ // Required for target - register utilities.
+ bool IsFpReg(int reg);
+ bool SameRegType(int reg1, int reg2);
+ int AllocTypedTemp(bool fp_hint, int reg_class);
+ int AllocTypedTempPair(bool fp_hint, int reg_class);
+ int S2d(int low_reg, int high_reg);
+ int TargetReg(SpecialTargetRegister reg);
+ RegisterInfo* GetRegInfo(int reg);
+ RegLocation GetReturnAlt();
+ RegLocation GetReturnWideAlt();
+ RegLocation LocCReturn();
+ RegLocation LocCReturnDouble();
+ RegLocation LocCReturnFloat();
+ RegLocation LocCReturnWide();
+ uint32_t FpRegMask();
+ uint64_t GetRegMaskCommon(int reg);
+ void AdjustSpillMask();
+ void ClobberCalleeSave();
+ void FlushReg(int reg);
+ void FlushRegWide(int reg1, int reg2);
+ void FreeCallTemps();
+ void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free);
+ void LockCallTemps();
+ void MarkPreservedSingle(int v_reg, int reg);
+ void CompilerInitializeRegAlloc();
+
+ // Required for target - miscellaneous.
+ AssemblerStatus AssembleInstructions(uintptr_t start_addr);
+ void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix);
+ void SetupTargetResourceMasks(LIR* lir);
+ const char* GetTargetInstFmt(int opcode);
+ const char* GetTargetInstName(int opcode);
+ std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr);
+ uint64_t GetPCUseDefEncoding();
+ uint64_t GetTargetInstFlags(int opcode);
+ int GetInsnSize(LIR* lir);
+ bool IsUnconditionalBranch(LIR* lir);
+
+ // Required for target - Dalvik-level generators.
+ void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
+ RegLocation rl_src, int scale);
+ void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_dest, int scale);
+ void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale);
+ void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_shift);
+ void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2);
+ void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src);
+ bool GenInlinedCas32(CallInfo* info, bool need_write_barrier);
+ bool GenInlinedMinMaxInt(CallInfo* info, bool is_min);
+ bool GenInlinedSqrt(CallInfo* info);
+ void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
+ void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset,
+ ThrowKind kind);
+ RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div);
+ RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div);
+ void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenDivZeroCheck(int reg_lo, int reg_hi);
+ void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
+ void GenExitSequence();
+ void GenFillArrayData(uint32_t table_offset, RegLocation rl_src);
+ void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
+ void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
+ void GenSelect(BasicBlock* bb, MIR* mir);
+ void GenMemBarrier(MemBarrierKind barrier_kind);
+ void GenMonitorEnter(int opt_flags, RegLocation rl_src);
+ void GenMonitorExit(int opt_flags, RegLocation rl_src);
+ void GenMoveException(RegLocation rl_dest);
+ void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
+ int first_bit, int second_bit);
+ void GenNegDouble(RegLocation rl_dest, RegLocation rl_src);
+ void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
+ void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
+ void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
+ void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case);
+
+ // Required for target - single operation generators.
+ LIR* OpUnconditionalBranch(LIR* target);
+ LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target);
+ LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target);
+ LIR* OpCondBranch(ConditionCode cc, LIR* target);
+ LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target);
+ LIR* OpFpRegCopy(int r_dest, int r_src);
+ LIR* OpIT(ConditionCode cond, const char* guide);
+ LIR* OpMem(OpKind op, int rBase, int disp);
+ LIR* OpPcRelLoad(int reg, LIR* target);
+ LIR* OpReg(OpKind op, int r_dest_src);
+ LIR* OpRegCopy(int r_dest, int r_src);
+ LIR* OpRegCopyNoInsert(int r_dest, int r_src);
+ LIR* OpRegImm(OpKind op, int r_dest_src1, int value);
+ LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset);
+ LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2);
+ LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value);
+ LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2);
+ LIR* OpTestSuspend(LIR* target);
+ LIR* OpThreadMem(OpKind op, int thread_offset);
+ LIR* OpVldm(int rBase, int count);
+ LIR* OpVstm(int rBase, int count);
+ void OpLea(int rBase, int reg1, int reg2, int scale, int offset);
+ void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi);
+ void OpTlsCmp(int offset, int val);
+
+ RegLocation ArgLoc(RegLocation loc);
+ LIR* LoadBaseDispBody(int rBase, int displacement, int r_dest, int r_dest_hi, OpSize size,
+ int s_reg);
+ LIR* StoreBaseDispBody(int rBase, int displacement, int r_src, int r_src_hi, OpSize size);
+ void GenPrintLabel(MIR* mir);
+ LIR* OpRegRegRegShift(OpKind op, int r_dest, int r_src1, int r_src2, int shift);
+ LIR* OpRegRegShift(OpKind op, int r_dest_src1, int r_src2, int shift);
+ static const ArmEncodingMap EncodingMap[kArmLast];
+ int EncodeShift(int code, int amount);
+ int ModifiedImmediate(uint32_t value);
+ ArmConditionCode ArmConditionEncoding(ConditionCode code);
+ bool InexpensiveConstantInt(int32_t value);
+ bool InexpensiveConstantFloat(int32_t value);
+ bool InexpensiveConstantLong(int64_t value);
+ bool InexpensiveConstantDouble(int64_t value);
+
+ private:
+ void GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, int64_t val,
+ ConditionCode ccode);
+ int InPosition(int s_reg);
+ RegLocation LoadArg(RegLocation loc);
+ void LockLiveArgs(MIR* mir);
+ MIR* GetNextMir(BasicBlock** p_bb, MIR* mir);
+ MIR* SpecialIGet(BasicBlock** bb, MIR* mir, OpSize size, bool long_or_double, bool is_object);
+ MIR* SpecialIPut(BasicBlock** bb, MIR* mir, OpSize size, bool long_or_double, bool is_object);
+ MIR* SpecialIdentity(MIR* mir);
+ LIR* LoadFPConstantValue(int r_dest, int value);
+ bool BadOverlap(RegLocation rl_src, RegLocation rl_dest);
+};
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_QUICK_ARM_CODEGENARM_H_
diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc
new file mode 100644
index 0000000..53a5e1a
--- /dev/null
+++ b/compiler/dex/quick/arm/fp_arm.cc
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2011 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 "arm_lir.h"
+#include "codegen_arm.h"
+#include "dex/quick/mir_to_lir-inl.h"
+
+namespace art {
+
+void ArmMir2Lir::GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2)
+{
+ int op = kThumbBkpt;
+ RegLocation rl_result;
+
+ /*
+ * Don't attempt to optimize register usage since these opcodes call out to
+ * the handlers.
+ */
+ switch (opcode) {
+ case Instruction::ADD_FLOAT_2ADDR:
+ case Instruction::ADD_FLOAT:
+ op = kThumb2Vadds;
+ break;
+ case Instruction::SUB_FLOAT_2ADDR:
+ case Instruction::SUB_FLOAT:
+ op = kThumb2Vsubs;
+ break;
+ case Instruction::DIV_FLOAT_2ADDR:
+ case Instruction::DIV_FLOAT:
+ op = kThumb2Vdivs;
+ break;
+ case Instruction::MUL_FLOAT_2ADDR:
+ case Instruction::MUL_FLOAT:
+ op = kThumb2Vmuls;
+ break;
+ case Instruction::REM_FLOAT_2ADDR:
+ case Instruction::REM_FLOAT:
+ FlushAllRegs(); // Send everything to home location
+ CallRuntimeHelperRegLocationRegLocation(ENTRYPOINT_OFFSET(pFmodf), rl_src1, rl_src2, false);
+ rl_result = GetReturn(true);
+ StoreValue(rl_dest, rl_result);
+ return;
+ case Instruction::NEG_FLOAT:
+ GenNegFloat(rl_dest, rl_src1);
+ return;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+ rl_src1 = LoadValue(rl_src1, kFPReg);
+ rl_src2 = LoadValue(rl_src2, kFPReg);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR3(op, rl_result.low_reg, rl_src1.low_reg, rl_src2.low_reg);
+ StoreValue(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenArithOpDouble(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2)
+{
+ int op = kThumbBkpt;
+ RegLocation rl_result;
+
+ switch (opcode) {
+ case Instruction::ADD_DOUBLE_2ADDR:
+ case Instruction::ADD_DOUBLE:
+ op = kThumb2Vaddd;
+ break;
+ case Instruction::SUB_DOUBLE_2ADDR:
+ case Instruction::SUB_DOUBLE:
+ op = kThumb2Vsubd;
+ break;
+ case Instruction::DIV_DOUBLE_2ADDR:
+ case Instruction::DIV_DOUBLE:
+ op = kThumb2Vdivd;
+ break;
+ case Instruction::MUL_DOUBLE_2ADDR:
+ case Instruction::MUL_DOUBLE:
+ op = kThumb2Vmuld;
+ break;
+ case Instruction::REM_DOUBLE_2ADDR:
+ case Instruction::REM_DOUBLE:
+ FlushAllRegs(); // Send everything to home location
+ CallRuntimeHelperRegLocationRegLocation(ENTRYPOINT_OFFSET(pFmod), rl_src1, rl_src2, false);
+ rl_result = GetReturnWide(true);
+ StoreValueWide(rl_dest, rl_result);
+ return;
+ case Instruction::NEG_DOUBLE:
+ GenNegDouble(rl_dest, rl_src1);
+ return;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+
+ rl_src1 = LoadValueWide(rl_src1, kFPReg);
+ DCHECK(rl_src1.wide);
+ rl_src2 = LoadValueWide(rl_src2, kFPReg);
+ DCHECK(rl_src2.wide);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ DCHECK(rl_dest.wide);
+ DCHECK(rl_result.wide);
+ NewLIR3(op, S2d(rl_result.low_reg, rl_result.high_reg), S2d(rl_src1.low_reg, rl_src1.high_reg),
+ S2d(rl_src2.low_reg, rl_src2.high_reg));
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenConversion(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src)
+{
+ int op = kThumbBkpt;
+ int src_reg;
+ RegLocation rl_result;
+
+ switch (opcode) {
+ case Instruction::INT_TO_FLOAT:
+ op = kThumb2VcvtIF;
+ break;
+ case Instruction::FLOAT_TO_INT:
+ op = kThumb2VcvtFI;
+ break;
+ case Instruction::DOUBLE_TO_FLOAT:
+ op = kThumb2VcvtDF;
+ break;
+ case Instruction::FLOAT_TO_DOUBLE:
+ op = kThumb2VcvtFd;
+ break;
+ case Instruction::INT_TO_DOUBLE:
+ op = kThumb2VcvtID;
+ break;
+ case Instruction::DOUBLE_TO_INT:
+ op = kThumb2VcvtDI;
+ break;
+ case Instruction::LONG_TO_DOUBLE:
+ GenConversionCall(ENTRYPOINT_OFFSET(pL2d), rl_dest, rl_src);
+ return;
+ case Instruction::FLOAT_TO_LONG:
+ GenConversionCall(ENTRYPOINT_OFFSET(pF2l), rl_dest, rl_src);
+ return;
+ case Instruction::LONG_TO_FLOAT:
+ GenConversionCall(ENTRYPOINT_OFFSET(pL2f), rl_dest, rl_src);
+ return;
+ case Instruction::DOUBLE_TO_LONG:
+ GenConversionCall(ENTRYPOINT_OFFSET(pD2l), rl_dest, rl_src);
+ return;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+ if (rl_src.wide) {
+ rl_src = LoadValueWide(rl_src, kFPReg);
+ src_reg = S2d(rl_src.low_reg, rl_src.high_reg);
+ } else {
+ rl_src = LoadValue(rl_src, kFPReg);
+ src_reg = rl_src.low_reg;
+ }
+ if (rl_dest.wide) {
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(op, S2d(rl_result.low_reg, rl_result.high_reg), src_reg);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(op, rl_result.low_reg, src_reg);
+ StoreValue(rl_dest, rl_result);
+ }
+}
+
+void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
+ bool is_double)
+{
+ LIR* target = &block_label_list_[bb->taken->id];
+ RegLocation rl_src1;
+ RegLocation rl_src2;
+ if (is_double) {
+ rl_src1 = mir_graph_->GetSrcWide(mir, 0);
+ rl_src2 = mir_graph_->GetSrcWide(mir, 2);
+ rl_src1 = LoadValueWide(rl_src1, kFPReg);
+ rl_src2 = LoadValueWide(rl_src2, kFPReg);
+ NewLIR2(kThumb2Vcmpd, S2d(rl_src1.low_reg, rl_src2.high_reg),
+ S2d(rl_src2.low_reg, rl_src2.high_reg));
+ } else {
+ rl_src1 = mir_graph_->GetSrc(mir, 0);
+ rl_src2 = mir_graph_->GetSrc(mir, 1);
+ rl_src1 = LoadValue(rl_src1, kFPReg);
+ rl_src2 = LoadValue(rl_src2, kFPReg);
+ NewLIR2(kThumb2Vcmps, rl_src1.low_reg, rl_src2.low_reg);
+ }
+ NewLIR0(kThumb2Fmstat);
+ ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
+ switch(ccode) {
+ case kCondEq:
+ case kCondNe:
+ break;
+ case kCondLt:
+ if (gt_bias) {
+ ccode = kCondMi;
+ }
+ break;
+ case kCondLe:
+ if (gt_bias) {
+ ccode = kCondLs;
+ }
+ break;
+ case kCondGt:
+ if (gt_bias) {
+ ccode = kCondHi;
+ }
+ break;
+ case kCondGe:
+ if (gt_bias) {
+ ccode = kCondCs;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected ccode: " << ccode;
+ }
+ OpCondBranch(ccode, target);
+}
+
+
+void ArmMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2)
+{
+ bool is_double = false;
+ int default_result = -1;
+ RegLocation rl_result;
+
+ switch (opcode) {
+ case Instruction::CMPL_FLOAT:
+ is_double = false;
+ default_result = -1;
+ break;
+ case Instruction::CMPG_FLOAT:
+ is_double = false;
+ default_result = 1;
+ break;
+ case Instruction::CMPL_DOUBLE:
+ is_double = true;
+ default_result = -1;
+ break;
+ case Instruction::CMPG_DOUBLE:
+ is_double = true;
+ default_result = 1;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+ if (is_double) {
+ rl_src1 = LoadValueWide(rl_src1, kFPReg);
+ rl_src2 = LoadValueWide(rl_src2, kFPReg);
+ // In case result vreg is also a src vreg, break association to avoid useless copy by EvalLoc()
+ ClobberSReg(rl_dest.s_reg_low);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ LoadConstant(rl_result.low_reg, default_result);
+ NewLIR2(kThumb2Vcmpd, S2d(rl_src1.low_reg, rl_src2.high_reg),
+ S2d(rl_src2.low_reg, rl_src2.high_reg));
+ } else {
+ rl_src1 = LoadValue(rl_src1, kFPReg);
+ rl_src2 = LoadValue(rl_src2, kFPReg);
+ // In case result vreg is also a srcvreg, break association to avoid useless copy by EvalLoc()
+ ClobberSReg(rl_dest.s_reg_low);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ LoadConstant(rl_result.low_reg, default_result);
+ NewLIR2(kThumb2Vcmps, rl_src1.low_reg, rl_src2.low_reg);
+ }
+ DCHECK(!ARM_FPREG(rl_result.low_reg));
+ NewLIR0(kThumb2Fmstat);
+
+ OpIT((default_result == -1) ? kCondGt : kCondMi, "");
+ NewLIR2(kThumb2MovImmShift, rl_result.low_reg,
+ ModifiedImmediate(-default_result)); // Must not alter ccodes
+ GenBarrier();
+
+ OpIT(kCondEq, "");
+ LoadConstant(rl_result.low_reg, 0);
+ GenBarrier();
+
+ StoreValue(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src)
+{
+ RegLocation rl_result;
+ rl_src = LoadValue(rl_src, kFPReg);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(kThumb2Vnegs, rl_result.low_reg, rl_src.low_reg);
+ StoreValue(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src)
+{
+ RegLocation rl_result;
+ rl_src = LoadValueWide(rl_src, kFPReg);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(kThumb2Vnegd, S2d(rl_result.low_reg, rl_result.high_reg),
+ S2d(rl_src.low_reg, rl_src.high_reg));
+ StoreValueWide(rl_dest, rl_result);
+}
+
+bool ArmMir2Lir::GenInlinedSqrt(CallInfo* info) {
+ DCHECK_EQ(cu_->instruction_set, kThumb2);
+ LIR *branch;
+ RegLocation rl_src = info->args[0];
+ RegLocation rl_dest = InlineTargetWide(info); // double place for result
+ rl_src = LoadValueWide(rl_src, kFPReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(kThumb2Vsqrtd, S2d(rl_result.low_reg, rl_result.high_reg),
+ S2d(rl_src.low_reg, rl_src.high_reg));
+ NewLIR2(kThumb2Vcmpd, S2d(rl_result.low_reg, rl_result.high_reg),
+ S2d(rl_result.low_reg, rl_result.high_reg));
+ NewLIR0(kThumb2Fmstat);
+ branch = NewLIR2(kThumbBCond, 0, kArmCondEq);
+ ClobberCalleeSave();
+ LockCallTemps(); // Using fixed registers
+ int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pSqrt));
+ NewLIR3(kThumb2Fmrrd, r0, r1, S2d(rl_src.low_reg, rl_src.high_reg));
+ NewLIR1(kThumbBlxR, r_tgt);
+ NewLIR3(kThumb2Fmdrr, S2d(rl_result.low_reg, rl_result.high_reg), r0, r1);
+ branch->target = NewLIR0(kPseudoTargetLabel);
+ StoreValueWide(rl_dest, rl_result);
+ return true;
+}
+
+
+} // namespace art
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
new file mode 100644
index 0000000..feea896
--- /dev/null
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -0,0 +1,1187 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/* This file contains codegen for the Thumb2 ISA. */
+
+#include "arm_lir.h"
+#include "codegen_arm.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "mirror/array.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+
+namespace art {
+
+LIR* ArmMir2Lir::OpCmpBranch(ConditionCode cond, int src1,
+ int src2, LIR* target)
+{
+ OpRegReg(kOpCmp, src1, src2);
+ return OpCondBranch(cond, target);
+}
+
+/*
+ * Generate a Thumb2 IT instruction, which can nullify up to
+ * four subsequent instructions based on a condition and its
+ * inverse. The condition applies to the first instruction, which
+ * is executed if the condition is met. The string "guide" consists
+ * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
+ * A "T" means the instruction is executed if the condition is
+ * met, and an "E" means the instruction is executed if the condition
+ * is not met.
+ */
+LIR* ArmMir2Lir::OpIT(ConditionCode ccode, const char* guide)
+{
+ int mask;
+ int mask3 = 0;
+ int mask2 = 0;
+ int mask1 = 0;
+ ArmConditionCode code = ArmConditionEncoding(ccode);
+ int cond_bit = code & 1;
+ int alt_bit = cond_bit ^ 1;
+
+ //Note: case fallthroughs intentional
+ switch (strlen(guide)) {
+ case 3:
+ mask1 = (guide[2] == 'T') ? cond_bit : alt_bit;
+ case 2:
+ mask2 = (guide[1] == 'T') ? cond_bit : alt_bit;
+ case 1:
+ mask3 = (guide[0] == 'T') ? cond_bit : alt_bit;
+ break;
+ case 0:
+ break;
+ default:
+ LOG(FATAL) << "OAT: bad case in OpIT";
+ }
+ mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
+ (1 << (3 - strlen(guide)));
+ return NewLIR2(kThumb2It, code, mask);
+}
+
+/*
+ * 64-bit 3way compare function.
+ * mov rX, #-1
+ * cmp op1hi, op2hi
+ * blt done
+ * bgt flip
+ * sub rX, op1lo, op2lo (treat as unsigned)
+ * beq done
+ * ite hi
+ * mov(hi) rX, #-1
+ * mov(!hi) rX, #1
+ * flip:
+ * neg rX
+ * done:
+ */
+void ArmMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LIR* target1;
+ LIR* target2;
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+ int t_reg = AllocTemp();
+ LoadConstant(t_reg, -1);
+ OpRegReg(kOpCmp, rl_src1.high_reg, rl_src2.high_reg);
+ LIR* branch1 = OpCondBranch(kCondLt, NULL);
+ LIR* branch2 = OpCondBranch(kCondGt, NULL);
+ OpRegRegReg(kOpSub, t_reg, rl_src1.low_reg, rl_src2.low_reg);
+ LIR* branch3 = OpCondBranch(kCondEq, NULL);
+
+ OpIT(kCondHi, "E");
+ NewLIR2(kThumb2MovImmShift, t_reg, ModifiedImmediate(-1));
+ LoadConstant(t_reg, 1);
+ GenBarrier();
+
+ target2 = NewLIR0(kPseudoTargetLabel);
+ OpRegReg(kOpNeg, t_reg, t_reg);
+
+ target1 = NewLIR0(kPseudoTargetLabel);
+
+ RegLocation rl_temp = LocCReturn(); // Just using as template, will change
+ rl_temp.low_reg = t_reg;
+ StoreValue(rl_dest, rl_temp);
+ FreeTemp(t_reg);
+
+ branch1->target = target1;
+ branch2->target = target2;
+ branch3->target = branch1->target;
+}
+
+void ArmMir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
+ int64_t val, ConditionCode ccode)
+{
+ int32_t val_lo = Low32Bits(val);
+ int32_t val_hi = High32Bits(val);
+ DCHECK(ModifiedImmediate(val_lo) >= 0);
+ DCHECK(ModifiedImmediate(val_hi) >= 0);
+ LIR* taken = &block_label_list_[bb->taken->id];
+ LIR* not_taken = &block_label_list_[bb->fall_through->id];
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ int32_t low_reg = rl_src1.low_reg;
+ int32_t high_reg = rl_src1.high_reg;
+
+ switch(ccode) {
+ case kCondEq:
+ case kCondNe:
+ LIR* target;
+ ConditionCode condition;
+ if (ccode == kCondEq) {
+ target = not_taken;
+ condition = kCondEq;
+ } else {
+ target = taken;
+ condition = kCondNe;
+ }
+ if (val == 0) {
+ int t_reg = AllocTemp();
+ NewLIR4(kThumb2OrrRRRs, t_reg, low_reg, high_reg, 0);
+ FreeTemp(t_reg);
+ OpCondBranch(condition, taken);
+ return;
+ }
+ OpCmpImmBranch(kCondNe, high_reg, val_hi, target);
+ break;
+ case kCondLt:
+ OpCmpImmBranch(kCondLt, high_reg, val_hi, taken);
+ OpCmpImmBranch(kCondGt, high_reg, val_hi, not_taken);
+ ccode = kCondCc;
+ break;
+ case kCondLe:
+ OpCmpImmBranch(kCondLt, high_reg, val_hi, taken);
+ OpCmpImmBranch(kCondGt, high_reg, val_hi, not_taken);
+ ccode = kCondLs;
+ break;
+ case kCondGt:
+ OpCmpImmBranch(kCondGt, high_reg, val_hi, taken);
+ OpCmpImmBranch(kCondLt, high_reg, val_hi, not_taken);
+ ccode = kCondHi;
+ break;
+ case kCondGe:
+ OpCmpImmBranch(kCondGt, high_reg, val_hi, taken);
+ OpCmpImmBranch(kCondLt, high_reg, val_hi, not_taken);
+ ccode = kCondCs;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected ccode: " << ccode;
+ }
+ OpCmpImmBranch(ccode, low_reg, val_lo, taken);
+}
+
+void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir)
+{
+ RegLocation rl_result;
+ RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
+ // Temporary debugging code
+ int dest_sreg = mir->ssa_rep->defs[0];
+ if ((dest_sreg < 0) || (dest_sreg >= mir_graph_->GetNumSSARegs())) {
+ LOG(INFO) << "Bad target sreg: " << dest_sreg << ", in "
+ << PrettyMethod(cu_->method_idx,*cu_->dex_file);
+ LOG(INFO) << "at dex offset 0x" << std::hex << mir->offset;
+ LOG(INFO) << "vreg = " << mir_graph_->SRegToVReg(dest_sreg);
+ LOG(INFO) << "num uses = " << mir->ssa_rep->num_uses;
+ if (mir->ssa_rep->num_uses == 1) {
+ LOG(INFO) << "CONST case, vals = " << mir->dalvikInsn.vB << ", " << mir->dalvikInsn.vC;
+ } else {
+ LOG(INFO) << "MOVE case, operands = " << mir->ssa_rep->uses[1] << ", "
+ << mir->ssa_rep->uses[2];
+ }
+ CHECK(false) << "Invalid target sreg on Select.";
+ }
+ // End temporary debugging code
+ RegLocation rl_dest = mir_graph_->GetDest(mir);
+ rl_src = LoadValue(rl_src, kCoreReg);
+ if (mir->ssa_rep->num_uses == 1) {
+ // CONST case
+ int true_val = mir->dalvikInsn.vB;
+ int false_val = mir->dalvikInsn.vC;
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if ((true_val == 1) && (false_val == 0)) {
+ OpRegRegImm(kOpRsub, rl_result.low_reg, rl_src.low_reg, 1);
+ OpIT(kCondCc, "");
+ LoadConstant(rl_result.low_reg, 0);
+ GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
+ } else if (InexpensiveConstantInt(true_val) && InexpensiveConstantInt(false_val)) {
+ OpRegImm(kOpCmp, rl_src.low_reg, 0);
+ OpIT(kCondEq, "E");
+ LoadConstant(rl_result.low_reg, true_val);
+ LoadConstant(rl_result.low_reg, false_val);
+ GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
+ } else {
+ // Unlikely case - could be tuned.
+ int t_reg1 = AllocTemp();
+ int t_reg2 = AllocTemp();
+ LoadConstant(t_reg1, true_val);
+ LoadConstant(t_reg2, false_val);
+ OpRegImm(kOpCmp, rl_src.low_reg, 0);
+ OpIT(kCondEq, "E");
+ OpRegCopy(rl_result.low_reg, t_reg1);
+ OpRegCopy(rl_result.low_reg, t_reg2);
+ GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
+ }
+ } else {
+ // MOVE case
+ RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]];
+ RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]];
+ rl_true = LoadValue(rl_true, kCoreReg);
+ rl_false = LoadValue(rl_false, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegImm(kOpCmp, rl_src.low_reg, 0);
+ OpIT(kCondEq, "E");
+ LIR* l1 = OpRegCopy(rl_result.low_reg, rl_true.low_reg);
+ l1->flags.is_nop = false; // Make sure this instruction isn't optimized away
+ LIR* l2 = OpRegCopy(rl_result.low_reg, rl_false.low_reg);
+ l2->flags.is_nop = false; // Make sure this instruction isn't optimized away
+ GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
+ }
+ StoreValue(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir)
+{
+ RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
+ RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
+ // Normalize such that if either operand is constant, src2 will be constant.
+ ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
+ if (rl_src1.is_const) {
+ RegLocation rl_temp = rl_src1;
+ rl_src1 = rl_src2;
+ rl_src2 = rl_temp;
+ ccode = FlipComparisonOrder(ccode);
+ }
+ if (rl_src2.is_const) {
+ RegLocation rl_temp = UpdateLocWide(rl_src2);
+ // Do special compare/branch against simple const operand if not already in registers.
+ int64_t val = mir_graph_->ConstantValueWide(rl_src2);
+ if ((rl_temp.location != kLocPhysReg) &&
+ ((ModifiedImmediate(Low32Bits(val)) >= 0) && (ModifiedImmediate(High32Bits(val)) >= 0))) {
+ GenFusedLongCmpImmBranch(bb, rl_src1, val, ccode);
+ return;
+ }
+ }
+ LIR* taken = &block_label_list_[bb->taken->id];
+ LIR* not_taken = &block_label_list_[bb->fall_through->id];
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+ OpRegReg(kOpCmp, rl_src1.high_reg, rl_src2.high_reg);
+ switch(ccode) {
+ case kCondEq:
+ OpCondBranch(kCondNe, not_taken);
+ break;
+ case kCondNe:
+ OpCondBranch(kCondNe, taken);
+ break;
+ case kCondLt:
+ OpCondBranch(kCondLt, taken);
+ OpCondBranch(kCondGt, not_taken);
+ ccode = kCondCc;
+ break;
+ case kCondLe:
+ OpCondBranch(kCondLt, taken);
+ OpCondBranch(kCondGt, not_taken);
+ ccode = kCondLs;
+ break;
+ case kCondGt:
+ OpCondBranch(kCondGt, taken);
+ OpCondBranch(kCondLt, not_taken);
+ ccode = kCondHi;
+ break;
+ case kCondGe:
+ OpCondBranch(kCondGt, taken);
+ OpCondBranch(kCondLt, not_taken);
+ ccode = kCondCs;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected ccode: " << ccode;
+ }
+ OpRegReg(kOpCmp, rl_src1.low_reg, rl_src2.low_reg);
+ OpCondBranch(ccode, taken);
+}
+
+/*
+ * Generate a register comparison to an immediate and branch. Caller
+ * is responsible for setting branch target field.
+ */
+LIR* ArmMir2Lir::OpCmpImmBranch(ConditionCode cond, int reg, int check_value,
+ LIR* target)
+{
+ LIR* branch;
+ int mod_imm;
+ ArmConditionCode arm_cond = ArmConditionEncoding(cond);
+ if ((ARM_LOWREG(reg)) && (check_value == 0) &&
+ ((arm_cond == kArmCondEq) || (arm_cond == kArmCondNe))) {
+ branch = NewLIR2((arm_cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz,
+ reg, 0);
+ } else {
+ mod_imm = ModifiedImmediate(check_value);
+ if (ARM_LOWREG(reg) && ((check_value & 0xff) == check_value)) {
+ NewLIR2(kThumbCmpRI8, reg, check_value);
+ } else if (mod_imm >= 0) {
+ NewLIR2(kThumb2CmpRI12, reg, mod_imm);
+ } else {
+ int t_reg = AllocTemp();
+ LoadConstant(t_reg, check_value);
+ OpRegReg(kOpCmp, reg, t_reg);
+ }
+ branch = NewLIR2(kThumbBCond, 0, arm_cond);
+ }
+ branch->target = target;
+ return branch;
+}
+
+LIR* ArmMir2Lir::OpRegCopyNoInsert(int r_dest, int r_src)
+{
+ LIR* res;
+ int opcode;
+ if (ARM_FPREG(r_dest) || ARM_FPREG(r_src))
+ return OpFpRegCopy(r_dest, r_src);
+ if (ARM_LOWREG(r_dest) && ARM_LOWREG(r_src))
+ opcode = kThumbMovRR;
+ else if (!ARM_LOWREG(r_dest) && !ARM_LOWREG(r_src))
+ opcode = kThumbMovRR_H2H;
+ else if (ARM_LOWREG(r_dest))
+ opcode = kThumbMovRR_H2L;
+ else
+ opcode = kThumbMovRR_L2H;
+ res = RawLIR(current_dalvik_offset_, opcode, r_dest, r_src);
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
+ res->flags.is_nop = true;
+ }
+ return res;
+}
+
+LIR* ArmMir2Lir::OpRegCopy(int r_dest, int r_src)
+{
+ LIR* res = OpRegCopyNoInsert(r_dest, r_src);
+ AppendLIR(res);
+ return res;
+}
+
+void ArmMir2Lir::OpRegCopyWide(int dest_lo, int dest_hi, int src_lo,
+ int src_hi)
+{
+ bool dest_fp = ARM_FPREG(dest_lo) && ARM_FPREG(dest_hi);
+ bool src_fp = ARM_FPREG(src_lo) && ARM_FPREG(src_hi);
+ DCHECK_EQ(ARM_FPREG(src_lo), ARM_FPREG(src_hi));
+ DCHECK_EQ(ARM_FPREG(dest_lo), ARM_FPREG(dest_hi));
+ if (dest_fp) {
+ if (src_fp) {
+ OpRegCopy(S2d(dest_lo, dest_hi), S2d(src_lo, src_hi));
+ } else {
+ NewLIR3(kThumb2Fmdrr, S2d(dest_lo, dest_hi), src_lo, src_hi);
+ }
+ } else {
+ if (src_fp) {
+ NewLIR3(kThumb2Fmrrd, dest_lo, dest_hi, S2d(src_lo, src_hi));
+ } else {
+ // Handle overlap
+ if (src_hi == dest_lo) {
+ OpRegCopy(dest_hi, src_hi);
+ OpRegCopy(dest_lo, src_lo);
+ } else {
+ OpRegCopy(dest_lo, src_lo);
+ OpRegCopy(dest_hi, src_hi);
+ }
+ }
+ }
+}
+
+// Table of magic divisors
+struct MagicTable {
+ uint32_t magic;
+ uint32_t shift;
+ DividePattern pattern;
+};
+
+static const MagicTable magic_table[] = {
+ {0, 0, DivideNone}, // 0
+ {0, 0, DivideNone}, // 1
+ {0, 0, DivideNone}, // 2
+ {0x55555556, 0, Divide3}, // 3
+ {0, 0, DivideNone}, // 4
+ {0x66666667, 1, Divide5}, // 5
+ {0x2AAAAAAB, 0, Divide3}, // 6
+ {0x92492493, 2, Divide7}, // 7
+ {0, 0, DivideNone}, // 8
+ {0x38E38E39, 1, Divide5}, // 9
+ {0x66666667, 2, Divide5}, // 10
+ {0x2E8BA2E9, 1, Divide5}, // 11
+ {0x2AAAAAAB, 1, Divide5}, // 12
+ {0x4EC4EC4F, 2, Divide5}, // 13
+ {0x92492493, 3, Divide7}, // 14
+ {0x88888889, 3, Divide7}, // 15
+};
+
+// Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4)
+bool ArmMir2Lir::SmallLiteralDivide(Instruction::Code dalvik_opcode,
+ RegLocation rl_src, RegLocation rl_dest, int lit)
+{
+ if ((lit < 0) || (lit >= static_cast<int>(sizeof(magic_table)/sizeof(magic_table[0])))) {
+ return false;
+ }
+ DividePattern pattern = magic_table[lit].pattern;
+ if (pattern == DivideNone) {
+ return false;
+ }
+ // Tuning: add rem patterns
+ if (dalvik_opcode != Instruction::DIV_INT_LIT8) {
+ return false;
+ }
+
+ int r_magic = AllocTemp();
+ LoadConstant(r_magic, magic_table[lit].magic);
+ rl_src = LoadValue(rl_src, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int r_hi = AllocTemp();
+ int r_lo = AllocTemp();
+ NewLIR4(kThumb2Smull, r_lo, r_hi, r_magic, rl_src.low_reg);
+ switch(pattern) {
+ case Divide3:
+ OpRegRegRegShift(kOpSub, rl_result.low_reg, r_hi,
+ rl_src.low_reg, EncodeShift(kArmAsr, 31));
+ break;
+ case Divide5:
+ OpRegRegImm(kOpAsr, r_lo, rl_src.low_reg, 31);
+ OpRegRegRegShift(kOpRsub, rl_result.low_reg, r_lo, r_hi,
+ EncodeShift(kArmAsr, magic_table[lit].shift));
+ break;
+ case Divide7:
+ OpRegReg(kOpAdd, r_hi, rl_src.low_reg);
+ OpRegRegImm(kOpAsr, r_lo, rl_src.low_reg, 31);
+ OpRegRegRegShift(kOpRsub, rl_result.low_reg, r_lo, r_hi,
+ EncodeShift(kArmAsr, magic_table[lit].shift));
+ break;
+ default:
+ LOG(FATAL) << "Unexpected pattern: " << pattern;
+ }
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+LIR* ArmMir2Lir::GenRegMemCheck(ConditionCode c_code,
+ int reg1, int base, int offset, ThrowKind kind)
+{
+ LOG(FATAL) << "Unexpected use of GenRegMemCheck for Arm";
+ return NULL;
+}
+
+RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest, int reg1, int lit,
+ bool is_div)
+{
+ LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm";
+ return rl_dest;
+}
+
+RegLocation ArmMir2Lir::GenDivRem(RegLocation rl_dest, int reg1, int reg2,
+ bool is_div)
+{
+ LOG(FATAL) << "Unexpected use of GenDivRem for Arm";
+ return rl_dest;
+}
+
+bool ArmMir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min)
+{
+ DCHECK_EQ(cu_->instruction_set, kThumb2);
+ RegLocation rl_src1 = info->args[0];
+ RegLocation rl_src2 = info->args[1];
+ rl_src1 = LoadValue(rl_src1, kCoreReg);
+ rl_src2 = LoadValue(rl_src2, kCoreReg);
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegReg(kOpCmp, rl_src1.low_reg, rl_src2.low_reg);
+ OpIT((is_min) ? kCondGt : kCondLt, "E");
+ OpRegReg(kOpMov, rl_result.low_reg, rl_src2.low_reg);
+ OpRegReg(kOpMov, rl_result.low_reg, rl_src1.low_reg);
+ GenBarrier();
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+void ArmMir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset)
+{
+ LOG(FATAL) << "Unexpected use of OpLea for Arm";
+}
+
+void ArmMir2Lir::OpTlsCmp(int offset, int val)
+{
+ LOG(FATAL) << "Unexpected use of OpTlsCmp for Arm";
+}
+
+bool ArmMir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) {
+ DCHECK_EQ(cu_->instruction_set, kThumb2);
+ // Unused - RegLocation rl_src_unsafe = info->args[0];
+ RegLocation rl_src_obj= info->args[1]; // Object - known non-null
+ RegLocation rl_src_offset= info->args[2]; // long low
+ rl_src_offset.wide = 0; // ignore high half in info->args[3]
+ RegLocation rl_src_expected= info->args[4]; // int or Object
+ RegLocation rl_src_new_value= info->args[5]; // int or Object
+ RegLocation rl_dest = InlineTarget(info); // boolean place for result
+
+
+ // Release store semantics, get the barrier out of the way. TODO: revisit
+ GenMemBarrier(kStoreLoad);
+
+ RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg);
+ RegLocation rl_new_value = LoadValue(rl_src_new_value, kCoreReg);
+
+ if (need_write_barrier && !mir_graph_->IsConstantNullRef(rl_new_value)) {
+ // Mark card for object assuming new value is stored.
+ MarkGCCard(rl_new_value.low_reg, rl_object.low_reg);
+ }
+
+ RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg);
+
+ int r_ptr = AllocTemp();
+ OpRegRegReg(kOpAdd, r_ptr, rl_object.low_reg, rl_offset.low_reg);
+
+ // Free now unneeded rl_object and rl_offset to give more temps.
+ ClobberSReg(rl_object.s_reg_low);
+ FreeTemp(rl_object.low_reg);
+ ClobberSReg(rl_offset.s_reg_low);
+ FreeTemp(rl_offset.low_reg);
+
+ int r_old_value = AllocTemp();
+ NewLIR3(kThumb2Ldrex, r_old_value, r_ptr, 0); // r_old_value := [r_ptr]
+
+ RegLocation rl_expected = LoadValue(rl_src_expected, kCoreReg);
+
+ // if (r_old_value == rExpected) {
+ // [r_ptr] <- r_new_value && r_result := success ? 0 : 1
+ // r_result ^= 1
+ // } else {
+ // r_result := 0
+ // }
+ OpRegReg(kOpCmp, r_old_value, rl_expected.low_reg);
+ FreeTemp(r_old_value); // Now unneeded.
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpIT(kCondEq, "TE");
+ NewLIR4(kThumb2Strex, rl_result.low_reg, rl_new_value.low_reg, r_ptr, 0);
+ FreeTemp(r_ptr); // Now unneeded.
+ OpRegImm(kOpXor, rl_result.low_reg, 1);
+ OpRegReg(kOpXor, rl_result.low_reg, rl_result.low_reg);
+
+ StoreValue(rl_dest, rl_result);
+
+ return true;
+}
+
+LIR* ArmMir2Lir::OpPcRelLoad(int reg, LIR* target)
+{
+ return RawLIR(current_dalvik_offset_, kThumb2LdrPcRel12, reg, 0, 0, 0, 0, target);
+}
+
+LIR* ArmMir2Lir::OpVldm(int rBase, int count)
+{
+ return NewLIR3(kThumb2Vldms, rBase, fr0, count);
+}
+
+LIR* ArmMir2Lir::OpVstm(int rBase, int count)
+{
+ return NewLIR3(kThumb2Vstms, rBase, fr0, count);
+}
+
+void ArmMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
+ RegLocation rl_result, int lit,
+ int first_bit, int second_bit)
+{
+ OpRegRegRegShift(kOpAdd, rl_result.low_reg, rl_src.low_reg, rl_src.low_reg,
+ EncodeShift(kArmLsl, second_bit - first_bit));
+ if (first_bit != 0) {
+ OpRegRegImm(kOpLsl, rl_result.low_reg, rl_result.low_reg, first_bit);
+ }
+}
+
+void ArmMir2Lir::GenDivZeroCheck(int reg_lo, int reg_hi)
+{
+ int t_reg = AllocTemp();
+ NewLIR4(kThumb2OrrRRRs, t_reg, reg_lo, reg_hi, 0);
+ FreeTemp(t_reg);
+ GenCheck(kCondEq, kThrowDivZero);
+}
+
+// Test suspend flag, return target of taken suspend branch
+LIR* ArmMir2Lir::OpTestSuspend(LIR* target)
+{
+ NewLIR2(kThumbSubRI8, rARM_SUSPEND, 1);
+ return OpCondBranch((target == NULL) ? kCondEq : kCondNe, target);
+}
+
+// Decrement register and branch on condition
+LIR* ArmMir2Lir::OpDecAndBranch(ConditionCode c_code, int reg, LIR* target)
+{
+ // Combine sub & test using sub setflags encoding here
+ NewLIR3(kThumb2SubsRRI12, reg, reg, 1);
+ return OpCondBranch(c_code, target);
+}
+
+void ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind)
+{
+#if ANDROID_SMP != 0
+ int dmb_flavor;
+ // TODO: revisit Arm barrier kinds
+ switch (barrier_kind) {
+ case kLoadStore: dmb_flavor = kSY; break;
+ case kLoadLoad: dmb_flavor = kSY; break;
+ case kStoreStore: dmb_flavor = kST; break;
+ case kStoreLoad: dmb_flavor = kSY; break;
+ default:
+ LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind;
+ dmb_flavor = kSY; // quiet gcc.
+ break;
+ }
+ LIR* dmb = NewLIR1(kThumb2Dmb, dmb_flavor);
+ dmb->def_mask = ENCODE_ALL;
+#endif
+}
+
+void ArmMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src)
+{
+ rl_src = LoadValueWide(rl_src, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int z_reg = AllocTemp();
+ LoadConstantNoClobber(z_reg, 0);
+ // Check for destructive overlap
+ if (rl_result.low_reg == rl_src.high_reg) {
+ int t_reg = AllocTemp();
+ OpRegRegReg(kOpSub, rl_result.low_reg, z_reg, rl_src.low_reg);
+ OpRegRegReg(kOpSbc, rl_result.high_reg, z_reg, t_reg);
+ FreeTemp(t_reg);
+ } else {
+ OpRegRegReg(kOpSub, rl_result.low_reg, z_reg, rl_src.low_reg);
+ OpRegRegReg(kOpSbc, rl_result.high_reg, z_reg, rl_src.high_reg);
+ }
+ FreeTemp(z_reg);
+ StoreValueWide(rl_dest, rl_result);
+}
+
+
+ /*
+ * Check to see if a result pair has a misaligned overlap with an operand pair. This
+ * is not usual for dx to generate, but it is legal (for now). In a future rev of
+ * dex, we'll want to make this case illegal.
+ */
+bool ArmMir2Lir::BadOverlap(RegLocation rl_src, RegLocation rl_dest)
+{
+ DCHECK(rl_src.wide);
+ DCHECK(rl_dest.wide);
+ return (abs(mir_graph_->SRegToVReg(rl_src.s_reg_low) - mir_graph_->SRegToVReg(rl_dest.s_reg_low)) == 1);
+}
+
+void ArmMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ /*
+ * To pull off inline multiply, we have a worst-case requirement of 8 temporary
+ * registers. Normally for Arm, we get 5. We can get to 6 by including
+ * lr in the temp set. The only problematic case is all operands and result are
+ * distinct, and none have been promoted. In that case, we can succeed by aggressively
+ * freeing operand temp registers after they are no longer needed. All other cases
+ * can proceed normally. We'll just punt on the case of the result having a misaligned
+ * overlap with either operand and send that case to a runtime handler.
+ */
+ RegLocation rl_result;
+ if (BadOverlap(rl_src1, rl_dest) || (BadOverlap(rl_src2, rl_dest))) {
+ int func_offset = ENTRYPOINT_OFFSET(pLmul);
+ FlushAllRegs();
+ CallRuntimeHelperRegLocationRegLocation(func_offset, rl_src1, rl_src2, false);
+ rl_result = GetReturnWide(false);
+ StoreValueWide(rl_dest, rl_result);
+ return;
+ }
+ // Temporarily add LR to the temp pool, and assign it to tmp1
+ MarkTemp(rARM_LR);
+ FreeTemp(rARM_LR);
+ int tmp1 = rARM_LR;
+ LockTemp(rARM_LR);
+
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+
+ bool special_case = true;
+ // If operands are the same, or any pair has been promoted we're not the special case.
+ if ((rl_src1.s_reg_low == rl_src2.s_reg_low) ||
+ (!IsTemp(rl_src1.low_reg) && !IsTemp(rl_src1.high_reg)) ||
+ (!IsTemp(rl_src2.low_reg) && !IsTemp(rl_src2.high_reg))) {
+ special_case = false;
+ }
+ // Tuning: if rl_dest has been promoted and is *not* either operand, could use directly.
+ int res_lo = AllocTemp();
+ int res_hi;
+ if (rl_src1.low_reg == rl_src2.low_reg) {
+ res_hi = AllocTemp();
+ NewLIR3(kThumb2MulRRR, tmp1, rl_src1.low_reg, rl_src1.high_reg);
+ NewLIR4(kThumb2Umull, res_lo, res_hi, rl_src1.low_reg, rl_src1.low_reg);
+ OpRegRegRegShift(kOpAdd, res_hi, res_hi, tmp1, EncodeShift(kArmLsl, 1));
+ } else {
+ // In the special case, all temps are now allocated
+ NewLIR3(kThumb2MulRRR, tmp1, rl_src2.low_reg, rl_src1.high_reg);
+ if (special_case) {
+ DCHECK_NE(rl_src1.low_reg, rl_src2.low_reg);
+ DCHECK_NE(rl_src1.high_reg, rl_src2.high_reg);
+ FreeTemp(rl_src1.high_reg);
+ }
+ res_hi = AllocTemp();
+
+ NewLIR4(kThumb2Umull, res_lo, res_hi, rl_src2.low_reg, rl_src1.low_reg);
+ NewLIR4(kThumb2Mla, tmp1, rl_src1.low_reg, rl_src2.high_reg, tmp1);
+ NewLIR4(kThumb2AddRRR, res_hi, tmp1, res_hi, 0);
+ if (special_case) {
+ FreeTemp(rl_src1.low_reg);
+ Clobber(rl_src1.low_reg);
+ Clobber(rl_src1.high_reg);
+ }
+ }
+ FreeTemp(tmp1);
+ rl_result = GetReturnWide(false); // Just using as a template.
+ rl_result.low_reg = res_lo;
+ rl_result.high_reg = res_hi;
+ StoreValueWide(rl_dest, rl_result);
+ // Now, restore lr to its non-temp status.
+ Clobber(rARM_LR);
+ UnmarkTemp(rARM_LR);
+}
+
+void ArmMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenAddLong for Arm";
+}
+
+void ArmMir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenSubLong for Arm";
+}
+
+void ArmMir2Lir::GenAndLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenAndLong for Arm";
+}
+
+void ArmMir2Lir::GenOrLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenOrLong for Arm";
+}
+
+void ArmMir2Lir::GenXorLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of genXoLong for Arm";
+}
+
+/*
+ * Generate array load
+ */
+void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_dest, int scale)
+{
+ RegisterClass reg_class = oat_reg_class_by_size(size);
+ int len_offset = mirror::Array::LengthOffset().Int32Value();
+ int data_offset;
+ RegLocation rl_result;
+ bool constant_index = rl_index.is_const;
+ rl_array = LoadValue(rl_array, kCoreReg);
+ if (!constant_index) {
+ rl_index = LoadValue(rl_index, kCoreReg);
+ }
+
+ if (rl_dest.wide) {
+ data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
+ } else {
+ data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
+ }
+
+ // If index is constant, just fold it into the data offset
+ if (constant_index) {
+ data_offset += mir_graph_->ConstantValue(rl_index) << scale;
+ }
+
+ /* null object? */
+ GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
+
+ bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
+ int reg_len = INVALID_REG;
+ if (needs_range_check) {
+ reg_len = AllocTemp();
+ /* Get len */
+ LoadWordDisp(rl_array.low_reg, len_offset, reg_len);
+ }
+ if (rl_dest.wide || rl_dest.fp || constant_index) {
+ int reg_ptr;
+ if (constant_index) {
+ reg_ptr = rl_array.low_reg; // NOTE: must not alter reg_ptr in constant case.
+ } else {
+ // No special indexed operation, lea + load w/ displacement
+ reg_ptr = AllocTemp();
+ OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.low_reg, rl_index.low_reg,
+ EncodeShift(kArmLsl, scale));
+ FreeTemp(rl_index.low_reg);
+ }
+ rl_result = EvalLoc(rl_dest, reg_class, true);
+
+ if (needs_range_check) {
+ if (constant_index) {
+ GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds);
+ } else {
+ GenRegRegCheck(kCondLs, reg_len, rl_index.low_reg, kThrowArrayBounds);
+ }
+ FreeTemp(reg_len);
+ }
+ if (rl_dest.wide) {
+ LoadBaseDispWide(reg_ptr, data_offset, rl_result.low_reg, rl_result.high_reg, INVALID_SREG);
+ if (!constant_index) {
+ FreeTemp(reg_ptr);
+ }
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ LoadBaseDisp(reg_ptr, data_offset, rl_result.low_reg, size, INVALID_SREG);
+ if (!constant_index) {
+ FreeTemp(reg_ptr);
+ }
+ StoreValue(rl_dest, rl_result);
+ }
+ } else {
+ // Offset base, then use indexed load
+ int reg_ptr = AllocTemp();
+ OpRegRegImm(kOpAdd, reg_ptr, rl_array.low_reg, data_offset);
+ FreeTemp(rl_array.low_reg);
+ rl_result = EvalLoc(rl_dest, reg_class, true);
+
+ if (needs_range_check) {
+ // TODO: change kCondCS to a more meaningful name, is the sense of
+ // carry-set/clear flipped?
+ GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
+ FreeTemp(reg_len);
+ }
+ LoadBaseIndexed(reg_ptr, rl_index.low_reg, rl_result.low_reg, scale, size);
+ FreeTemp(reg_ptr);
+ StoreValue(rl_dest, rl_result);
+ }
+}
+
+/*
+ * Generate array store
+ *
+ */
+void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale)
+{
+ RegisterClass reg_class = oat_reg_class_by_size(size);
+ int len_offset = mirror::Array::LengthOffset().Int32Value();
+ int data_offset;
+ bool constant_index = rl_index.is_const;
+
+ if (rl_src.wide) {
+ data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
+ } else {
+ data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
+ }
+
+ // If index is constant, just fold it into the data offset.
+ if (constant_index) {
+ data_offset += mir_graph_->ConstantValue(rl_index) << scale;
+ }
+
+ rl_array = LoadValue(rl_array, kCoreReg);
+ if (!constant_index) {
+ rl_index = LoadValue(rl_index, kCoreReg);
+ }
+
+ int reg_ptr;
+ if (constant_index) {
+ reg_ptr = rl_array.low_reg;
+ } else if (IsTemp(rl_array.low_reg)) {
+ Clobber(rl_array.low_reg);
+ reg_ptr = rl_array.low_reg;
+ } else {
+ reg_ptr = AllocTemp();
+ }
+
+ /* null object? */
+ GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
+
+ bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
+ int reg_len = INVALID_REG;
+ if (needs_range_check) {
+ reg_len = AllocTemp();
+ //NOTE: max live temps(4) here.
+ /* Get len */
+ LoadWordDisp(rl_array.low_reg, len_offset, reg_len);
+ }
+ /* at this point, reg_ptr points to array, 2 live temps */
+ if (rl_src.wide || rl_src.fp || constant_index) {
+ if (rl_src.wide) {
+ rl_src = LoadValueWide(rl_src, reg_class);
+ } else {
+ rl_src = LoadValue(rl_src, reg_class);
+ }
+ if (!constant_index) {
+ OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.low_reg, rl_index.low_reg,
+ EncodeShift(kArmLsl, scale));
+ }
+ if (needs_range_check) {
+ if (constant_index) {
+ GenImmedCheck(kCondLs, reg_len, mir_graph_->ConstantValue(rl_index), kThrowConstantArrayBounds);
+ } else {
+ GenRegRegCheck(kCondLs, reg_len, rl_index.low_reg, kThrowArrayBounds);
+ }
+ FreeTemp(reg_len);
+ }
+
+ if (rl_src.wide) {
+ StoreBaseDispWide(reg_ptr, data_offset, rl_src.low_reg, rl_src.high_reg);
+ } else {
+ StoreBaseDisp(reg_ptr, data_offset, rl_src.low_reg, size);
+ }
+ } else {
+ /* reg_ptr -> array data */
+ OpRegRegImm(kOpAdd, reg_ptr, rl_array.low_reg, data_offset);
+ rl_src = LoadValue(rl_src, reg_class);
+ if (needs_range_check) {
+ GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
+ FreeTemp(reg_len);
+ }
+ StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg,
+ scale, size);
+ }
+ if (!constant_index) {
+ FreeTemp(reg_ptr);
+ }
+}
+
+/*
+ * Generate array store
+ *
+ */
+void ArmMir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale)
+{
+ int len_offset = mirror::Array::LengthOffset().Int32Value();
+ int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
+
+ FlushAllRegs(); // Use explicit registers
+ LockCallTemps();
+
+ int r_value = TargetReg(kArg0); // Register holding value
+ int r_array_class = TargetReg(kArg1); // Register holding array's Class
+ int r_array = TargetReg(kArg2); // Register holding array
+ int r_index = TargetReg(kArg3); // Register holding index into array
+
+ LoadValueDirectFixed(rl_array, r_array); // Grab array
+ LoadValueDirectFixed(rl_src, r_value); // Grab value
+ LoadValueDirectFixed(rl_index, r_index); // Grab index
+
+ GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE?
+
+ // Store of null?
+ LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL);
+
+ // Get the array's class.
+ LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
+ CallRuntimeHelperRegReg(ENTRYPOINT_OFFSET(pCanPutArrayElementFromCode), r_value,
+ r_array_class, true);
+ // Redo LoadValues in case they didn't survive the call.
+ LoadValueDirectFixed(rl_array, r_array); // Reload array
+ LoadValueDirectFixed(rl_index, r_index); // Reload index
+ LoadValueDirectFixed(rl_src, r_value); // Reload value
+ r_array_class = INVALID_REG;
+
+ // Branch here if value to be stored == null
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ null_value_check->target = target;
+
+ bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
+ int reg_len = INVALID_REG;
+ if (needs_range_check) {
+ reg_len = TargetReg(kArg1);
+ LoadWordDisp(r_array, len_offset, reg_len); // Get len
+ }
+ /* r_ptr -> array data */
+ int r_ptr = AllocTemp();
+ OpRegRegImm(kOpAdd, r_ptr, r_array, data_offset);
+ if (needs_range_check) {
+ GenRegRegCheck(kCondCs, r_index, reg_len, kThrowArrayBounds);
+ }
+ StoreBaseIndexed(r_ptr, r_index, r_value, scale, kWord);
+ FreeTemp(r_ptr);
+ FreeTemp(r_index);
+ if (!mir_graph_->IsConstantNullRef(rl_src)) {
+ MarkGCCard(r_value, r_array);
+ }
+}
+
+void ArmMir2Lir::GenShiftImmOpLong(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift)
+{
+ rl_src = LoadValueWide(rl_src, kCoreReg);
+ // Per spec, we only care about low 6 bits of shift amount.
+ int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f;
+ if (shift_amount == 0) {
+ StoreValueWide(rl_dest, rl_src);
+ return;
+ }
+ if (BadOverlap(rl_src, rl_dest)) {
+ GenShiftOpLong(opcode, rl_dest, rl_src, rl_shift);
+ return;
+ }
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ switch(opcode) {
+ case Instruction::SHL_LONG:
+ case Instruction::SHL_LONG_2ADDR:
+ if (shift_amount == 1) {
+ OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, rl_src.low_reg);
+ OpRegRegReg(kOpAdc, rl_result.high_reg, rl_src.high_reg, rl_src.high_reg);
+ } else if (shift_amount == 32) {
+ OpRegCopy(rl_result.high_reg, rl_src.low_reg);
+ LoadConstant(rl_result.low_reg, 0);
+ } else if (shift_amount > 31) {
+ OpRegRegImm(kOpLsl, rl_result.high_reg, rl_src.low_reg, shift_amount - 32);
+ LoadConstant(rl_result.low_reg, 0);
+ } else {
+ OpRegRegImm(kOpLsl, rl_result.high_reg, rl_src.high_reg, shift_amount);
+ OpRegRegRegShift(kOpOr, rl_result.high_reg, rl_result.high_reg, rl_src.low_reg,
+ EncodeShift(kArmLsr, 32 - shift_amount));
+ OpRegRegImm(kOpLsl, rl_result.low_reg, rl_src.low_reg, shift_amount);
+ }
+ break;
+ case Instruction::SHR_LONG:
+ case Instruction::SHR_LONG_2ADDR:
+ if (shift_amount == 32) {
+ OpRegCopy(rl_result.low_reg, rl_src.high_reg);
+ OpRegRegImm(kOpAsr, rl_result.high_reg, rl_src.high_reg, 31);
+ } else if (shift_amount > 31) {
+ OpRegRegImm(kOpAsr, rl_result.low_reg, rl_src.high_reg, shift_amount - 32);
+ OpRegRegImm(kOpAsr, rl_result.high_reg, rl_src.high_reg, 31);
+ } else {
+ int t_reg = AllocTemp();
+ OpRegRegImm(kOpLsr, t_reg, rl_src.low_reg, shift_amount);
+ OpRegRegRegShift(kOpOr, rl_result.low_reg, t_reg, rl_src.high_reg,
+ EncodeShift(kArmLsl, 32 - shift_amount));
+ FreeTemp(t_reg);
+ OpRegRegImm(kOpAsr, rl_result.high_reg, rl_src.high_reg, shift_amount);
+ }
+ break;
+ case Instruction::USHR_LONG:
+ case Instruction::USHR_LONG_2ADDR:
+ if (shift_amount == 32) {
+ OpRegCopy(rl_result.low_reg, rl_src.high_reg);
+ LoadConstant(rl_result.high_reg, 0);
+ } else if (shift_amount > 31) {
+ OpRegRegImm(kOpLsr, rl_result.low_reg, rl_src.high_reg, shift_amount - 32);
+ LoadConstant(rl_result.high_reg, 0);
+ } else {
+ int t_reg = AllocTemp();
+ OpRegRegImm(kOpLsr, t_reg, rl_src.low_reg, shift_amount);
+ OpRegRegRegShift(kOpOr, rl_result.low_reg, t_reg, rl_src.high_reg,
+ EncodeShift(kArmLsl, 32 - shift_amount));
+ FreeTemp(t_reg);
+ OpRegRegImm(kOpLsr, rl_result.high_reg, rl_src.high_reg, shift_amount);
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected case";
+ }
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void ArmMir2Lir::GenArithImmOpLong(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2)
+{
+ if ((opcode == Instruction::SUB_LONG_2ADDR) || (opcode == Instruction::SUB_LONG)) {
+ if (!rl_src2.is_const) {
+ // Don't bother with special handling for subtract from immediate.
+ GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+ return;
+ }
+ } else {
+ // Normalize
+ if (!rl_src2.is_const) {
+ DCHECK(rl_src1.is_const);
+ RegLocation rl_temp = rl_src1;
+ rl_src1 = rl_src2;
+ rl_src2 = rl_temp;
+ }
+ }
+ if (BadOverlap(rl_src1, rl_dest)) {
+ GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+ return;
+ }
+ DCHECK(rl_src2.is_const);
+ int64_t val = mir_graph_->ConstantValueWide(rl_src2);
+ uint32_t val_lo = Low32Bits(val);
+ uint32_t val_hi = High32Bits(val);
+ int32_t mod_imm_lo = ModifiedImmediate(val_lo);
+ int32_t mod_imm_hi = ModifiedImmediate(val_hi);
+
+ // Only a subset of add/sub immediate instructions set carry - so bail if we don't fit
+ switch(opcode) {
+ case Instruction::ADD_LONG:
+ case Instruction::ADD_LONG_2ADDR:
+ case Instruction::SUB_LONG:
+ case Instruction::SUB_LONG_2ADDR:
+ if ((mod_imm_lo < 0) || (mod_imm_hi < 0)) {
+ GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ // NOTE: once we've done the EvalLoc on dest, we can no longer bail.
+ switch (opcode) {
+ case Instruction::ADD_LONG:
+ case Instruction::ADD_LONG_2ADDR:
+ NewLIR3(kThumb2AddRRI8, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo);
+ NewLIR3(kThumb2AdcRRI8, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi);
+ break;
+ case Instruction::OR_LONG:
+ case Instruction::OR_LONG_2ADDR:
+ if ((val_lo != 0) || (rl_result.low_reg != rl_src1.low_reg)) {
+ OpRegRegImm(kOpOr, rl_result.low_reg, rl_src1.low_reg, val_lo);
+ }
+ if ((val_hi != 0) || (rl_result.high_reg != rl_src1.high_reg)) {
+ OpRegRegImm(kOpOr, rl_result.high_reg, rl_src1.high_reg, val_hi);
+ }
+ break;
+ case Instruction::XOR_LONG:
+ case Instruction::XOR_LONG_2ADDR:
+ OpRegRegImm(kOpXor, rl_result.low_reg, rl_src1.low_reg, val_lo);
+ OpRegRegImm(kOpXor, rl_result.high_reg, rl_src1.high_reg, val_hi);
+ break;
+ case Instruction::AND_LONG:
+ case Instruction::AND_LONG_2ADDR:
+ if ((val_lo != 0xffffffff) || (rl_result.low_reg != rl_src1.low_reg)) {
+ OpRegRegImm(kOpAnd, rl_result.low_reg, rl_src1.low_reg, val_lo);
+ }
+ if ((val_hi != 0xffffffff) || (rl_result.high_reg != rl_src1.high_reg)) {
+ OpRegRegImm(kOpAnd, rl_result.high_reg, rl_src1.high_reg, val_hi);
+ }
+ break;
+ case Instruction::SUB_LONG_2ADDR:
+ case Instruction::SUB_LONG:
+ NewLIR3(kThumb2SubRRI8, rl_result.low_reg, rl_src1.low_reg, mod_imm_lo);
+ NewLIR3(kThumb2SbcRRI8, rl_result.high_reg, rl_src1.high_reg, mod_imm_hi);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected opcode " << opcode;
+ }
+ StoreValueWide(rl_dest, rl_result);
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
new file mode 100644
index 0000000..4bece13
--- /dev/null
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -0,0 +1,769 @@
+/*
+ * Copyright (C) 2011 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 <string>
+
+#include "arm_lir.h"
+#include "codegen_arm.h"
+#include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir-inl.h"
+
+namespace art {
+
+static int core_regs[] = {r0, r1, r2, r3, rARM_SUSPEND, r5, r6, r7, r8, rARM_SELF, r10,
+ r11, r12, rARM_SP, rARM_LR, rARM_PC};
+static int ReservedRegs[] = {rARM_SUSPEND, rARM_SELF, rARM_SP, rARM_LR, rARM_PC};
+static int FpRegs[] = {fr0, fr1, fr2, fr3, fr4, fr5, fr6, fr7,
+ fr8, fr9, fr10, fr11, fr12, fr13, fr14, fr15,
+ fr16, fr17, fr18, fr19, fr20, fr21, fr22, fr23,
+ fr24, fr25, fr26, fr27, fr28, fr29, fr30, fr31};
+static int core_temps[] = {r0, r1, r2, r3, r12};
+static int fp_temps[] = {fr0, fr1, fr2, fr3, fr4, fr5, fr6, fr7,
+ fr8, fr9, fr10, fr11, fr12, fr13, fr14, fr15};
+
+RegLocation ArmMir2Lir::LocCReturn()
+{
+ RegLocation res = ARM_LOC_C_RETURN;
+ return res;
+}
+
+RegLocation ArmMir2Lir::LocCReturnWide()
+{
+ RegLocation res = ARM_LOC_C_RETURN_WIDE;
+ return res;
+}
+
+RegLocation ArmMir2Lir::LocCReturnFloat()
+{
+ RegLocation res = ARM_LOC_C_RETURN_FLOAT;
+ return res;
+}
+
+RegLocation ArmMir2Lir::LocCReturnDouble()
+{
+ RegLocation res = ARM_LOC_C_RETURN_DOUBLE;
+ return res;
+}
+
+// Return a target-dependent special register.
+int ArmMir2Lir::TargetReg(SpecialTargetRegister reg) {
+ int res = INVALID_REG;
+ switch (reg) {
+ case kSelf: res = rARM_SELF; break;
+ case kSuspend: res = rARM_SUSPEND; break;
+ case kLr: res = rARM_LR; break;
+ case kPc: res = rARM_PC; break;
+ case kSp: res = rARM_SP; break;
+ case kArg0: res = rARM_ARG0; break;
+ case kArg1: res = rARM_ARG1; break;
+ case kArg2: res = rARM_ARG2; break;
+ case kArg3: res = rARM_ARG3; break;
+ case kFArg0: res = rARM_FARG0; break;
+ case kFArg1: res = rARM_FARG1; break;
+ case kFArg2: res = rARM_FARG2; break;
+ case kFArg3: res = rARM_FARG3; break;
+ case kRet0: res = rARM_RET0; break;
+ case kRet1: res = rARM_RET1; break;
+ case kInvokeTgt: res = rARM_INVOKE_TGT; break;
+ case kCount: res = rARM_COUNT; break;
+ }
+ return res;
+}
+
+
+// Create a double from a pair of singles.
+int ArmMir2Lir::S2d(int low_reg, int high_reg)
+{
+ return ARM_S2D(low_reg, high_reg);
+}
+
+// Return mask to strip off fp reg flags and bias.
+uint32_t ArmMir2Lir::FpRegMask()
+{
+ return ARM_FP_REG_MASK;
+}
+
+// True if both regs single, both core or both double.
+bool ArmMir2Lir::SameRegType(int reg1, int reg2)
+{
+ return (ARM_REGTYPE(reg1) == ARM_REGTYPE(reg2));
+}
+
+/*
+ * Decode the register id.
+ */
+uint64_t ArmMir2Lir::GetRegMaskCommon(int reg)
+{
+ uint64_t seed;
+ int shift;
+ int reg_id;
+
+
+ reg_id = reg & 0x1f;
+ /* Each double register is equal to a pair of single-precision FP registers */
+ seed = ARM_DOUBLEREG(reg) ? 3 : 1;
+ /* FP register starts at bit position 16 */
+ shift = ARM_FPREG(reg) ? kArmFPReg0 : 0;
+ /* Expand the double register id into single offset */
+ shift += reg_id;
+ return (seed << shift);
+}
+
+uint64_t ArmMir2Lir::GetPCUseDefEncoding()
+{
+ return ENCODE_ARM_REG_PC;
+}
+
+void ArmMir2Lir::SetupTargetResourceMasks(LIR* lir)
+{
+ DCHECK_EQ(cu_->instruction_set, kThumb2);
+
+ // Thumb2 specific setup
+ uint64_t flags = ArmMir2Lir::EncodingMap[lir->opcode].flags;
+ int opcode = lir->opcode;
+
+ if (flags & REG_DEF_SP) {
+ lir->def_mask |= ENCODE_ARM_REG_SP;
+ }
+
+ if (flags & REG_USE_SP) {
+ lir->use_mask |= ENCODE_ARM_REG_SP;
+ }
+
+ if (flags & REG_DEF_LIST0) {
+ lir->def_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_DEF_LIST1) {
+ lir->def_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]);
+ }
+
+ if (flags & REG_DEF_FPCS_LIST0) {
+ lir->def_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_DEF_FPCS_LIST2) {
+ for (int i = 0; i < lir->operands[2]; i++) {
+ SetupRegMask(&lir->def_mask, lir->operands[1] + i);
+ }
+ }
+
+ if (flags & REG_USE_PC) {
+ lir->use_mask |= ENCODE_ARM_REG_PC;
+ }
+
+ /* Conservatively treat the IT block */
+ if (flags & IS_IT) {
+ lir->def_mask = ENCODE_ALL;
+ }
+
+ if (flags & REG_USE_LIST0) {
+ lir->use_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_USE_LIST1) {
+ lir->use_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]);
+ }
+
+ if (flags & REG_USE_FPCS_LIST0) {
+ lir->use_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_USE_FPCS_LIST2) {
+ for (int i = 0; i < lir->operands[2]; i++) {
+ SetupRegMask(&lir->use_mask, lir->operands[1] + i);
+ }
+ }
+ /* Fixup for kThumbPush/lr and kThumbPop/pc */
+ if (opcode == kThumbPush || opcode == kThumbPop) {
+ uint64_t r8Mask = GetRegMaskCommon(r8);
+ if ((opcode == kThumbPush) && (lir->use_mask & r8Mask)) {
+ lir->use_mask &= ~r8Mask;
+ lir->use_mask |= ENCODE_ARM_REG_LR;
+ } else if ((opcode == kThumbPop) && (lir->def_mask & r8Mask)) {
+ lir->def_mask &= ~r8Mask;
+ lir->def_mask |= ENCODE_ARM_REG_PC;
+ }
+ }
+ if (flags & REG_DEF_LR) {
+ lir->def_mask |= ENCODE_ARM_REG_LR;
+ }
+}
+
+ArmConditionCode ArmMir2Lir::ArmConditionEncoding(ConditionCode ccode)
+{
+ ArmConditionCode res;
+ switch (ccode) {
+ case kCondEq: res = kArmCondEq; break;
+ case kCondNe: res = kArmCondNe; break;
+ case kCondCs: res = kArmCondCs; break;
+ case kCondCc: res = kArmCondCc; break;
+ case kCondMi: res = kArmCondMi; break;
+ case kCondPl: res = kArmCondPl; break;
+ case kCondVs: res = kArmCondVs; break;
+ case kCondVc: res = kArmCondVc; break;
+ case kCondHi: res = kArmCondHi; break;
+ case kCondLs: res = kArmCondLs; break;
+ case kCondGe: res = kArmCondGe; break;
+ case kCondLt: res = kArmCondLt; break;
+ case kCondGt: res = kArmCondGt; break;
+ case kCondLe: res = kArmCondLe; break;
+ case kCondAl: res = kArmCondAl; break;
+ case kCondNv: res = kArmCondNv; break;
+ default:
+ LOG(FATAL) << "Bad condition code " << ccode;
+ res = static_cast<ArmConditionCode>(0); // Quiet gcc
+ }
+ return res;
+}
+
+static const char* core_reg_names[16] = {
+ "r0",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5",
+ "r6",
+ "r7",
+ "r8",
+ "rSELF",
+ "r10",
+ "r11",
+ "r12",
+ "sp",
+ "lr",
+ "pc",
+};
+
+
+static const char* shift_names[4] = {
+ "lsl",
+ "lsr",
+ "asr",
+ "ror"};
+
+/* Decode and print a ARM register name */
+static char* DecodeRegList(int opcode, int vector, char* buf)
+{
+ int i;
+ bool printed = false;
+ buf[0] = 0;
+ for (i = 0; i < 16; i++, vector >>= 1) {
+ if (vector & 0x1) {
+ int reg_id = i;
+ if (opcode == kThumbPush && i == 8) {
+ reg_id = r14lr;
+ } else if (opcode == kThumbPop && i == 8) {
+ reg_id = r15pc;
+ }
+ if (printed) {
+ sprintf(buf + strlen(buf), ", r%d", reg_id);
+ } else {
+ printed = true;
+ sprintf(buf, "r%d", reg_id);
+ }
+ }
+ }
+ return buf;
+}
+
+static char* DecodeFPCSRegList(int count, int base, char* buf)
+{
+ sprintf(buf, "s%d", base);
+ for (int i = 1; i < count; i++) {
+ sprintf(buf + strlen(buf), ", s%d",base + i);
+ }
+ return buf;
+}
+
+static int ExpandImmediate(int value)
+{
+ int mode = (value & 0xf00) >> 8;
+ uint32_t bits = value & 0xff;
+ switch (mode) {
+ case 0:
+ return bits;
+ case 1:
+ return (bits << 16) | bits;
+ case 2:
+ return (bits << 24) | (bits << 8);
+ case 3:
+ return (bits << 24) | (bits << 16) | (bits << 8) | bits;
+ default:
+ break;
+ }
+ bits = (bits | 0x80) << 24;
+ return bits >> (((value & 0xf80) >> 7) - 8);
+}
+
+const char* cc_names[] = {"eq","ne","cs","cc","mi","pl","vs","vc",
+ "hi","ls","ge","lt","gt","le","al","nv"};
+/*
+ * Interpret a format string and build a string no longer than size
+ * See format key in Assemble.c.
+ */
+std::string ArmMir2Lir::BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr)
+{
+ std::string buf;
+ int i;
+ const char* fmt_end = &fmt[strlen(fmt)];
+ char tbuf[256];
+ const char* name;
+ char nc;
+ while (fmt < fmt_end) {
+ int operand;
+ if (*fmt == '!') {
+ fmt++;
+ DCHECK_LT(fmt, fmt_end);
+ nc = *fmt++;
+ if (nc=='!') {
+ strcpy(tbuf, "!");
+ } else {
+ DCHECK_LT(fmt, fmt_end);
+ DCHECK_LT(static_cast<unsigned>(nc-'0'), 4U);
+ operand = lir->operands[nc-'0'];
+ switch (*fmt++) {
+ case 'H':
+ if (operand != 0) {
+ sprintf(tbuf, ", %s %d",shift_names[operand & 0x3], operand >> 2);
+ } else {
+ strcpy(tbuf,"");
+ }
+ break;
+ case 'B':
+ switch (operand) {
+ case kSY:
+ name = "sy";
+ break;
+ case kST:
+ name = "st";
+ break;
+ case kISH:
+ name = "ish";
+ break;
+ case kISHST:
+ name = "ishst";
+ break;
+ case kNSH:
+ name = "nsh";
+ break;
+ case kNSHST:
+ name = "shst";
+ break;
+ default:
+ name = "DecodeError2";
+ break;
+ }
+ strcpy(tbuf, name);
+ break;
+ case 'b':
+ strcpy(tbuf,"0000");
+ for (i=3; i>= 0; i--) {
+ tbuf[i] += operand & 1;
+ operand >>= 1;
+ }
+ break;
+ case 'n':
+ operand = ~ExpandImmediate(operand);
+ sprintf(tbuf,"%d [%#x]", operand, operand);
+ break;
+ case 'm':
+ operand = ExpandImmediate(operand);
+ sprintf(tbuf,"%d [%#x]", operand, operand);
+ break;
+ case 's':
+ sprintf(tbuf,"s%d",operand & ARM_FP_REG_MASK);
+ break;
+ case 'S':
+ sprintf(tbuf,"d%d",(operand & ARM_FP_REG_MASK) >> 1);
+ break;
+ case 'h':
+ sprintf(tbuf,"%04x", operand);
+ break;
+ case 'M':
+ case 'd':
+ sprintf(tbuf,"%d", operand);
+ break;
+ case 'C':
+ DCHECK_LT(operand, static_cast<int>(
+ sizeof(core_reg_names)/sizeof(core_reg_names[0])));
+ sprintf(tbuf,"%s",core_reg_names[operand]);
+ break;
+ case 'E':
+ sprintf(tbuf,"%d", operand*4);
+ break;
+ case 'F':
+ sprintf(tbuf,"%d", operand*2);
+ break;
+ case 'c':
+ strcpy(tbuf, cc_names[operand]);
+ break;
+ case 't':
+ sprintf(tbuf,"0x%08x (L%p)",
+ reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 +
+ (operand << 1),
+ lir->target);
+ break;
+ case 'u': {
+ int offset_1 = lir->operands[0];
+ int offset_2 = NEXT_LIR(lir)->operands[0];
+ uintptr_t target =
+ (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) &
+ ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) &
+ 0xfffffffc;
+ sprintf(tbuf, "%p", reinterpret_cast<void *>(target));
+ break;
+ }
+
+ /* Nothing to print for BLX_2 */
+ case 'v':
+ strcpy(tbuf, "see above");
+ break;
+ case 'R':
+ DecodeRegList(lir->opcode, operand, tbuf);
+ break;
+ case 'P':
+ DecodeFPCSRegList(operand, 16, tbuf);
+ break;
+ case 'Q':
+ DecodeFPCSRegList(operand, 0, tbuf);
+ break;
+ default:
+ strcpy(tbuf,"DecodeError1");
+ break;
+ }
+ buf += tbuf;
+ }
+ } else {
+ buf += *fmt++;
+ }
+ }
+ return buf;
+}
+
+void ArmMir2Lir::DumpResourceMask(LIR* arm_lir, uint64_t mask, const char* prefix)
+{
+ char buf[256];
+ buf[0] = 0;
+
+ if (mask == ENCODE_ALL) {
+ strcpy(buf, "all");
+ } else {
+ char num[8];
+ int i;
+
+ for (i = 0; i < kArmRegEnd; i++) {
+ if (mask & (1ULL << i)) {
+ sprintf(num, "%d ", i);
+ strcat(buf, num);
+ }
+ }
+
+ if (mask & ENCODE_CCODE) {
+ strcat(buf, "cc ");
+ }
+ if (mask & ENCODE_FP_STATUS) {
+ strcat(buf, "fpcc ");
+ }
+
+ /* Memory bits */
+ if (arm_lir && (mask & ENCODE_DALVIK_REG)) {
+ sprintf(buf + strlen(buf), "dr%d%s", arm_lir->alias_info & 0xffff,
+ (arm_lir->alias_info & 0x80000000) ? "(+1)" : "");
+ }
+ if (mask & ENCODE_LITERAL) {
+ strcat(buf, "lit ");
+ }
+
+ if (mask & ENCODE_HEAP_REF) {
+ strcat(buf, "heap ");
+ }
+ if (mask & ENCODE_MUST_NOT_ALIAS) {
+ strcat(buf, "noalias ");
+ }
+ }
+ if (buf[0]) {
+ LOG(INFO) << prefix << ": " << buf;
+ }
+}
+
+bool ArmMir2Lir::IsUnconditionalBranch(LIR* lir)
+{
+ return ((lir->opcode == kThumbBUncond) || (lir->opcode == kThumb2BUncond));
+}
+
+ArmMir2Lir::ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
+ : Mir2Lir(cu, mir_graph, arena) {
+ // Sanity check - make sure encoding map lines up.
+ for (int i = 0; i < kArmLast; i++) {
+ if (ArmMir2Lir::EncodingMap[i].opcode != i) {
+ LOG(FATAL) << "Encoding order for " << ArmMir2Lir::EncodingMap[i].name
+ << " is wrong: expecting " << i << ", seeing "
+ << static_cast<int>(ArmMir2Lir::EncodingMap[i].opcode);
+ }
+ }
+}
+
+Mir2Lir* ArmCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena) {
+ return new ArmMir2Lir(cu, mir_graph, arena);
+}
+
+/*
+ * Alloc a pair of core registers, or a double. Low reg in low byte,
+ * high reg in next byte.
+ */
+int ArmMir2Lir::AllocTypedTempPair(bool fp_hint, int reg_class)
+{
+ int high_reg;
+ int low_reg;
+ int res = 0;
+
+ if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg)) {
+ low_reg = AllocTempDouble();
+ high_reg = low_reg + 1;
+ } else {
+ low_reg = AllocTemp();
+ high_reg = AllocTemp();
+ }
+ res = (low_reg & 0xff) | ((high_reg & 0xff) << 8);
+ return res;
+}
+
+int ArmMir2Lir::AllocTypedTemp(bool fp_hint, int reg_class)
+{
+ if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg))
+ return AllocTempFloat();
+ return AllocTemp();
+}
+
+void ArmMir2Lir::CompilerInitializeRegAlloc()
+{
+ int num_regs = sizeof(core_regs)/sizeof(*core_regs);
+ int num_reserved = sizeof(ReservedRegs)/sizeof(*ReservedRegs);
+ int num_temps = sizeof(core_temps)/sizeof(*core_temps);
+ int num_fp_regs = sizeof(FpRegs)/sizeof(*FpRegs);
+ int num_fp_temps = sizeof(fp_temps)/sizeof(*fp_temps);
+ reg_pool_ = static_cast<RegisterPool*>(arena_->NewMem(sizeof(*reg_pool_), true,
+ ArenaAllocator::kAllocRegAlloc));
+ reg_pool_->num_core_regs = num_regs;
+ reg_pool_->core_regs = reinterpret_cast<RegisterInfo*>
+ (arena_->NewMem(num_regs * sizeof(*reg_pool_->core_regs), true,
+ ArenaAllocator::kAllocRegAlloc));
+ reg_pool_->num_fp_regs = num_fp_regs;
+ reg_pool_->FPRegs = static_cast<RegisterInfo*>
+ (arena_->NewMem(num_fp_regs * sizeof(*reg_pool_->FPRegs), true,
+ ArenaAllocator::kAllocRegAlloc));
+ CompilerInitPool(reg_pool_->core_regs, core_regs, reg_pool_->num_core_regs);
+ CompilerInitPool(reg_pool_->FPRegs, FpRegs, reg_pool_->num_fp_regs);
+ // Keep special registers from being allocated
+ for (int i = 0; i < num_reserved; i++) {
+ if (NO_SUSPEND && (ReservedRegs[i] == rARM_SUSPEND)) {
+ //To measure cost of suspend check
+ continue;
+ }
+ MarkInUse(ReservedRegs[i]);
+ }
+ // Mark temp regs - all others not in use can be used for promotion
+ for (int i = 0; i < num_temps; i++) {
+ MarkTemp(core_temps[i]);
+ }
+ for (int i = 0; i < num_fp_temps; i++) {
+ MarkTemp(fp_temps[i]);
+ }
+
+ // Start allocation at r2 in an attempt to avoid clobbering return values
+ reg_pool_->next_core_reg = r2;
+}
+
+void ArmMir2Lir::FreeRegLocTemps(RegLocation rl_keep,
+ RegLocation rl_free)
+{
+ if ((rl_free.low_reg != rl_keep.low_reg) && (rl_free.low_reg != rl_keep.high_reg) &&
+ (rl_free.high_reg != rl_keep.low_reg) && (rl_free.high_reg != rl_keep.high_reg)) {
+ // No overlap, free both
+ FreeTemp(rl_free.low_reg);
+ FreeTemp(rl_free.high_reg);
+ }
+}
+/*
+ * TUNING: is true leaf? Can't just use METHOD_IS_LEAF to determine as some
+ * instructions might call out to C/assembly helper functions. Until
+ * machinery is in place, always spill lr.
+ */
+
+void ArmMir2Lir::AdjustSpillMask()
+{
+ core_spill_mask_ |= (1 << rARM_LR);
+ num_core_spills_++;
+}
+
+/*
+ * Mark a callee-save fp register as promoted. Note that
+ * vpush/vpop uses contiguous register lists so we must
+ * include any holes in the mask. Associate holes with
+ * Dalvik register INVALID_VREG (0xFFFFU).
+ */
+void ArmMir2Lir::MarkPreservedSingle(int v_reg, int reg)
+{
+ DCHECK_GE(reg, ARM_FP_REG_MASK + ARM_FP_CALLEE_SAVE_BASE);
+ reg = (reg & ARM_FP_REG_MASK) - ARM_FP_CALLEE_SAVE_BASE;
+ // Ensure fp_vmap_table is large enough
+ int table_size = fp_vmap_table_.size();
+ for (int i = table_size; i < (reg + 1); i++) {
+ fp_vmap_table_.push_back(INVALID_VREG);
+ }
+ // Add the current mapping
+ fp_vmap_table_[reg] = v_reg;
+ // Size of fp_vmap_table is high-water mark, use to set mask
+ num_fp_spills_ = fp_vmap_table_.size();
+ fp_spill_mask_ = ((1 << num_fp_spills_) - 1) << ARM_FP_CALLEE_SAVE_BASE;
+}
+
+void ArmMir2Lir::FlushRegWide(int reg1, int reg2)
+{
+ RegisterInfo* info1 = GetRegInfo(reg1);
+ RegisterInfo* info2 = GetRegInfo(reg2);
+ DCHECK(info1 && info2 && info1->pair && info2->pair &&
+ (info1->partner == info2->reg) &&
+ (info2->partner == info1->reg));
+ if ((info1->live && info1->dirty) || (info2->live && info2->dirty)) {
+ if (!(info1->is_temp && info2->is_temp)) {
+ /* Should not happen. If it does, there's a problem in eval_loc */
+ LOG(FATAL) << "Long half-temp, half-promoted";
+ }
+
+ info1->dirty = false;
+ info2->dirty = false;
+ if (mir_graph_->SRegToVReg(info2->s_reg) <
+ mir_graph_->SRegToVReg(info1->s_reg))
+ info1 = info2;
+ int v_reg = mir_graph_->SRegToVReg(info1->s_reg);
+ StoreBaseDispWide(rARM_SP, VRegOffset(v_reg), info1->reg, info1->partner);
+ }
+}
+
+void ArmMir2Lir::FlushReg(int reg)
+{
+ RegisterInfo* info = GetRegInfo(reg);
+ if (info->live && info->dirty) {
+ info->dirty = false;
+ int v_reg = mir_graph_->SRegToVReg(info->s_reg);
+ StoreBaseDisp(rARM_SP, VRegOffset(v_reg), reg, kWord);
+ }
+}
+
+/* Give access to the target-dependent FP register encoding to common code */
+bool ArmMir2Lir::IsFpReg(int reg) {
+ return ARM_FPREG(reg);
+}
+
+/* Clobber all regs that might be used by an external C call */
+void ArmMir2Lir::ClobberCalleeSave()
+{
+ Clobber(r0);
+ Clobber(r1);
+ Clobber(r2);
+ Clobber(r3);
+ Clobber(r12);
+ Clobber(r14lr);
+ Clobber(fr0);
+ Clobber(fr1);
+ Clobber(fr2);
+ Clobber(fr3);
+ Clobber(fr4);
+ Clobber(fr5);
+ Clobber(fr6);
+ Clobber(fr7);
+ Clobber(fr8);
+ Clobber(fr9);
+ Clobber(fr10);
+ Clobber(fr11);
+ Clobber(fr12);
+ Clobber(fr13);
+ Clobber(fr14);
+ Clobber(fr15);
+}
+
+RegLocation ArmMir2Lir::GetReturnWideAlt()
+{
+ RegLocation res = LocCReturnWide();
+ res.low_reg = r2;
+ res.high_reg = r3;
+ Clobber(r2);
+ Clobber(r3);
+ MarkInUse(r2);
+ MarkInUse(r3);
+ MarkPair(res.low_reg, res.high_reg);
+ return res;
+}
+
+RegLocation ArmMir2Lir::GetReturnAlt()
+{
+ RegLocation res = LocCReturn();
+ res.low_reg = r1;
+ Clobber(r1);
+ MarkInUse(r1);
+ return res;
+}
+
+ArmMir2Lir::RegisterInfo* ArmMir2Lir::GetRegInfo(int reg)
+{
+ return ARM_FPREG(reg) ? ®_pool_->FPRegs[reg & ARM_FP_REG_MASK]
+ : ®_pool_->core_regs[reg];
+}
+
+/* To be used when explicitly managing register use */
+void ArmMir2Lir::LockCallTemps()
+{
+ LockTemp(r0);
+ LockTemp(r1);
+ LockTemp(r2);
+ LockTemp(r3);
+}
+
+/* To be used when explicitly managing register use */
+void ArmMir2Lir::FreeCallTemps()
+{
+ FreeTemp(r0);
+ FreeTemp(r1);
+ FreeTemp(r2);
+ FreeTemp(r3);
+}
+
+int ArmMir2Lir::LoadHelper(int offset)
+{
+ LoadWordDisp(rARM_SELF, offset, rARM_LR);
+ return rARM_LR;
+}
+
+uint64_t ArmMir2Lir::GetTargetInstFlags(int opcode)
+{
+ return ArmMir2Lir::EncodingMap[opcode].flags;
+}
+
+const char* ArmMir2Lir::GetTargetInstName(int opcode)
+{
+ return ArmMir2Lir::EncodingMap[opcode].name;
+}
+
+const char* ArmMir2Lir::GetTargetInstFmt(int opcode)
+{
+ return ArmMir2Lir::EncodingMap[opcode].fmt;
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
new file mode 100644
index 0000000..abf921f
--- /dev/null
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -0,0 +1,1093 @@
+/*
+ * Copyright (C) 2011 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 "arm_lir.h"
+#include "codegen_arm.h"
+#include "dex/quick/mir_to_lir-inl.h"
+
+namespace art {
+
+/* This file contains codegen for the Thumb ISA. */
+
+static int EncodeImmSingle(int value)
+{
+ int res;
+ int bit_a = (value & 0x80000000) >> 31;
+ int not_bit_b = (value & 0x40000000) >> 30;
+ int bit_b = (value & 0x20000000) >> 29;
+ int b_smear = (value & 0x3e000000) >> 25;
+ int slice = (value & 0x01f80000) >> 19;
+ int zeroes = (value & 0x0007ffff);
+ if (zeroes != 0)
+ return -1;
+ if (bit_b) {
+ if ((not_bit_b != 0) || (b_smear != 0x1f))
+ return -1;
+ } else {
+ if ((not_bit_b != 1) || (b_smear != 0x0))
+ return -1;
+ }
+ res = (bit_a << 7) | (bit_b << 6) | slice;
+ return res;
+}
+
+/*
+ * Determine whether value can be encoded as a Thumb2 floating point
+ * immediate. If not, return -1. If so return encoded 8-bit value.
+ */
+static int EncodeImmDouble(int64_t value)
+{
+ int res;
+ int bit_a = (value & 0x8000000000000000ll) >> 63;
+ int not_bit_b = (value & 0x4000000000000000ll) >> 62;
+ int bit_b = (value & 0x2000000000000000ll) >> 61;
+ int b_smear = (value & 0x3fc0000000000000ll) >> 54;
+ int slice = (value & 0x003f000000000000ll) >> 48;
+ uint64_t zeroes = (value & 0x0000ffffffffffffll);
+ if (zeroes != 0)
+ return -1;
+ if (bit_b) {
+ if ((not_bit_b != 0) || (b_smear != 0xff))
+ return -1;
+ } else {
+ if ((not_bit_b != 1) || (b_smear != 0x0))
+ return -1;
+ }
+ res = (bit_a << 7) | (bit_b << 6) | slice;
+ return res;
+}
+
+LIR* ArmMir2Lir::LoadFPConstantValue(int r_dest, int value)
+{
+ DCHECK(ARM_SINGLEREG(r_dest));
+ if (value == 0) {
+ // TODO: we need better info about the target CPU. a vector exclusive or
+ // would probably be better here if we could rely on its existance.
+ // Load an immediate +2.0 (which encodes to 0)
+ NewLIR2(kThumb2Vmovs_IMM8, r_dest, 0);
+ // +0.0 = +2.0 - +2.0
+ return NewLIR3(kThumb2Vsubs, r_dest, r_dest, r_dest);
+ } else {
+ int encoded_imm = EncodeImmSingle(value);
+ if (encoded_imm >= 0) {
+ return NewLIR2(kThumb2Vmovs_IMM8, r_dest, encoded_imm);
+ }
+ }
+ LIR* data_target = ScanLiteralPool(literal_list_, value, 0);
+ if (data_target == NULL) {
+ data_target = AddWordData(&literal_list_, value);
+ }
+ LIR* load_pc_rel = RawLIR(current_dalvik_offset_, kThumb2Vldrs,
+ r_dest, r15pc, 0, 0, 0, data_target);
+ SetMemRefType(load_pc_rel, true, kLiteral);
+ load_pc_rel->alias_info = reinterpret_cast<uintptr_t>(data_target);
+ AppendLIR(load_pc_rel);
+ return load_pc_rel;
+}
+
+static int LeadingZeros(uint32_t val)
+{
+ uint32_t alt;
+ int n;
+ int count;
+
+ count = 16;
+ n = 32;
+ do {
+ alt = val >> count;
+ if (alt != 0) {
+ n = n - count;
+ val = alt;
+ }
+ count >>= 1;
+ } while (count);
+ return n - val;
+}
+
+/*
+ * Determine whether value can be encoded as a Thumb2 modified
+ * immediate. If not, return -1. If so, return i:imm3:a:bcdefgh form.
+ */
+int ArmMir2Lir::ModifiedImmediate(uint32_t value)
+{
+ int z_leading;
+ int z_trailing;
+ uint32_t b0 = value & 0xff;
+
+ /* Note: case of value==0 must use 0:000:0:0000000 encoding */
+ if (value <= 0xFF)
+ return b0; // 0:000:a:bcdefgh
+ if (value == ((b0 << 16) | b0))
+ return (0x1 << 8) | b0; /* 0:001:a:bcdefgh */
+ if (value == ((b0 << 24) | (b0 << 16) | (b0 << 8) | b0))
+ return (0x3 << 8) | b0; /* 0:011:a:bcdefgh */
+ b0 = (value >> 8) & 0xff;
+ if (value == ((b0 << 24) | (b0 << 8)))
+ return (0x2 << 8) | b0; /* 0:010:a:bcdefgh */
+ /* Can we do it with rotation? */
+ z_leading = LeadingZeros(value);
+ z_trailing = 32 - LeadingZeros(~value & (value - 1));
+ /* A run of eight or fewer active bits? */
+ if ((z_leading + z_trailing) < 24)
+ return -1; /* No - bail */
+ /* left-justify the constant, discarding msb (known to be 1) */
+ value <<= z_leading + 1;
+ /* Create bcdefgh */
+ value >>= 25;
+ /* Put it all together */
+ return value | ((0x8 + z_leading) << 7); /* [01000..11111]:bcdefgh */
+}
+
+bool ArmMir2Lir::InexpensiveConstantInt(int32_t value)
+{
+ return (ModifiedImmediate(value) >= 0) || (ModifiedImmediate(~value) >= 0);
+}
+
+bool ArmMir2Lir::InexpensiveConstantFloat(int32_t value)
+{
+ return EncodeImmSingle(value) >= 0;
+}
+
+bool ArmMir2Lir::InexpensiveConstantLong(int64_t value)
+{
+ return InexpensiveConstantInt(High32Bits(value)) && InexpensiveConstantInt(Low32Bits(value));
+}
+
+bool ArmMir2Lir::InexpensiveConstantDouble(int64_t value)
+{
+ return EncodeImmDouble(value) >= 0;
+}
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool.
+ *
+ * No additional register clobbering operation performed. Use this version when
+ * 1) r_dest is freshly returned from AllocTemp or
+ * 2) The codegen is under fixed register usage
+ */
+LIR* ArmMir2Lir::LoadConstantNoClobber(int r_dest, int value)
+{
+ LIR* res;
+ int mod_imm;
+
+ if (ARM_FPREG(r_dest)) {
+ return LoadFPConstantValue(r_dest, value);
+ }
+
+ /* See if the value can be constructed cheaply */
+ if (ARM_LOWREG(r_dest) && (value >= 0) && (value <= 255)) {
+ return NewLIR2(kThumbMovImm, r_dest, value);
+ }
+ /* Check Modified immediate special cases */
+ mod_imm = ModifiedImmediate(value);
+ if (mod_imm >= 0) {
+ res = NewLIR2(kThumb2MovImmShift, r_dest, mod_imm);
+ return res;
+ }
+ mod_imm = ModifiedImmediate(~value);
+ if (mod_imm >= 0) {
+ res = NewLIR2(kThumb2MvnImm12, r_dest, mod_imm);
+ return res;
+ }
+ /* 16-bit immediate? */
+ if ((value & 0xffff) == value) {
+ res = NewLIR2(kThumb2MovImm16, r_dest, value);
+ return res;
+ }
+ /* Do a low/high pair */
+ res = NewLIR2(kThumb2MovImm16, r_dest, Low16Bits(value));
+ NewLIR2(kThumb2MovImm16H, r_dest, High16Bits(value));
+ return res;
+}
+
+LIR* ArmMir2Lir::OpUnconditionalBranch(LIR* target)
+{
+ LIR* res = NewLIR1(kThumbBUncond, 0 /* offset to be patched during assembly*/);
+ res->target = target;
+ return res;
+}
+
+LIR* ArmMir2Lir::OpCondBranch(ConditionCode cc, LIR* target)
+{
+ LIR* branch = NewLIR2(kThumb2BCond, 0 /* offset to be patched */,
+ ArmConditionEncoding(cc));
+ branch->target = target;
+ return branch;
+}
+
+LIR* ArmMir2Lir::OpReg(OpKind op, int r_dest_src)
+{
+ ArmOpcode opcode = kThumbBkpt;
+ switch (op) {
+ case kOpBlx:
+ opcode = kThumbBlxR;
+ break;
+ default:
+ LOG(FATAL) << "Bad opcode " << op;
+ }
+ return NewLIR1(opcode, r_dest_src);
+}
+
+LIR* ArmMir2Lir::OpRegRegShift(OpKind op, int r_dest_src1, int r_src2,
+ int shift)
+{
+ bool thumb_form = ((shift == 0) && ARM_LOWREG(r_dest_src1) && ARM_LOWREG(r_src2));
+ ArmOpcode opcode = kThumbBkpt;
+ switch (op) {
+ case kOpAdc:
+ opcode = (thumb_form) ? kThumbAdcRR : kThumb2AdcRRR;
+ break;
+ case kOpAnd:
+ opcode = (thumb_form) ? kThumbAndRR : kThumb2AndRRR;
+ break;
+ case kOpBic:
+ opcode = (thumb_form) ? kThumbBicRR : kThumb2BicRRR;
+ break;
+ case kOpCmn:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbCmnRR : kThumb2CmnRR;
+ break;
+ case kOpCmp:
+ if (thumb_form)
+ opcode = kThumbCmpRR;
+ else if ((shift == 0) && !ARM_LOWREG(r_dest_src1) && !ARM_LOWREG(r_src2))
+ opcode = kThumbCmpHH;
+ else if ((shift == 0) && ARM_LOWREG(r_dest_src1))
+ opcode = kThumbCmpLH;
+ else if (shift == 0)
+ opcode = kThumbCmpHL;
+ else
+ opcode = kThumb2CmpRR;
+ break;
+ case kOpXor:
+ opcode = (thumb_form) ? kThumbEorRR : kThumb2EorRRR;
+ break;
+ case kOpMov:
+ DCHECK_EQ(shift, 0);
+ if (ARM_LOWREG(r_dest_src1) && ARM_LOWREG(r_src2))
+ opcode = kThumbMovRR;
+ else if (!ARM_LOWREG(r_dest_src1) && !ARM_LOWREG(r_src2))
+ opcode = kThumbMovRR_H2H;
+ else if (ARM_LOWREG(r_dest_src1))
+ opcode = kThumbMovRR_H2L;
+ else
+ opcode = kThumbMovRR_L2H;
+ break;
+ case kOpMul:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbMul : kThumb2MulRRR;
+ break;
+ case kOpMvn:
+ opcode = (thumb_form) ? kThumbMvn : kThumb2MnvRR;
+ break;
+ case kOpNeg:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbNeg : kThumb2NegRR;
+ break;
+ case kOpOr:
+ opcode = (thumb_form) ? kThumbOrr : kThumb2OrrRRR;
+ break;
+ case kOpSbc:
+ opcode = (thumb_form) ? kThumbSbc : kThumb2SbcRRR;
+ break;
+ case kOpTst:
+ opcode = (thumb_form) ? kThumbTst : kThumb2TstRR;
+ break;
+ case kOpLsl:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbLslRR : kThumb2LslRRR;
+ break;
+ case kOpLsr:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbLsrRR : kThumb2LsrRRR;
+ break;
+ case kOpAsr:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbAsrRR : kThumb2AsrRRR;
+ break;
+ case kOpRor:
+ DCHECK_EQ(shift, 0);
+ opcode = (thumb_form) ? kThumbRorRR : kThumb2RorRRR;
+ break;
+ case kOpAdd:
+ opcode = (thumb_form) ? kThumbAddRRR : kThumb2AddRRR;
+ break;
+ case kOpSub:
+ opcode = (thumb_form) ? kThumbSubRRR : kThumb2SubRRR;
+ break;
+ case kOp2Byte:
+ DCHECK_EQ(shift, 0);
+ return NewLIR4(kThumb2Sbfx, r_dest_src1, r_src2, 0, 8);
+ case kOp2Short:
+ DCHECK_EQ(shift, 0);
+ return NewLIR4(kThumb2Sbfx, r_dest_src1, r_src2, 0, 16);
+ case kOp2Char:
+ DCHECK_EQ(shift, 0);
+ return NewLIR4(kThumb2Ubfx, r_dest_src1, r_src2, 0, 16);
+ default:
+ LOG(FATAL) << "Bad opcode: " << op;
+ break;
+ }
+ DCHECK_GE(static_cast<int>(opcode), 0);
+ if (EncodingMap[opcode].flags & IS_BINARY_OP)
+ return NewLIR2(opcode, r_dest_src1, r_src2);
+ else if (EncodingMap[opcode].flags & IS_TERTIARY_OP) {
+ if (EncodingMap[opcode].field_loc[2].kind == kFmtShift)
+ return NewLIR3(opcode, r_dest_src1, r_src2, shift);
+ else
+ return NewLIR3(opcode, r_dest_src1, r_dest_src1, r_src2);
+ } else if (EncodingMap[opcode].flags & IS_QUAD_OP)
+ return NewLIR4(opcode, r_dest_src1, r_dest_src1, r_src2, shift);
+ else {
+ LOG(FATAL) << "Unexpected encoding operand count";
+ return NULL;
+ }
+}
+
+LIR* ArmMir2Lir::OpRegReg(OpKind op, int r_dest_src1, int r_src2)
+{
+ return OpRegRegShift(op, r_dest_src1, r_src2, 0);
+}
+
+LIR* ArmMir2Lir::OpRegRegRegShift(OpKind op, int r_dest, int r_src1,
+ int r_src2, int shift)
+{
+ ArmOpcode opcode = kThumbBkpt;
+ bool thumb_form = (shift == 0) && ARM_LOWREG(r_dest) && ARM_LOWREG(r_src1) &&
+ ARM_LOWREG(r_src2);
+ switch (op) {
+ case kOpAdd:
+ opcode = (thumb_form) ? kThumbAddRRR : kThumb2AddRRR;
+ break;
+ case kOpSub:
+ opcode = (thumb_form) ? kThumbSubRRR : kThumb2SubRRR;
+ break;
+ case kOpRsub:
+ opcode = kThumb2RsubRRR;
+ break;
+ case kOpAdc:
+ opcode = kThumb2AdcRRR;
+ break;
+ case kOpAnd:
+ opcode = kThumb2AndRRR;
+ break;
+ case kOpBic:
+ opcode = kThumb2BicRRR;
+ break;
+ case kOpXor:
+ opcode = kThumb2EorRRR;
+ break;
+ case kOpMul:
+ DCHECK_EQ(shift, 0);
+ opcode = kThumb2MulRRR;
+ break;
+ case kOpOr:
+ opcode = kThumb2OrrRRR;
+ break;
+ case kOpSbc:
+ opcode = kThumb2SbcRRR;
+ break;
+ case kOpLsl:
+ DCHECK_EQ(shift, 0);
+ opcode = kThumb2LslRRR;
+ break;
+ case kOpLsr:
+ DCHECK_EQ(shift, 0);
+ opcode = kThumb2LsrRRR;
+ break;
+ case kOpAsr:
+ DCHECK_EQ(shift, 0);
+ opcode = kThumb2AsrRRR;
+ break;
+ case kOpRor:
+ DCHECK_EQ(shift, 0);
+ opcode = kThumb2RorRRR;
+ break;
+ default:
+ LOG(FATAL) << "Bad opcode: " << op;
+ break;
+ }
+ DCHECK_GE(static_cast<int>(opcode), 0);
+ if (EncodingMap[opcode].flags & IS_QUAD_OP)
+ return NewLIR4(opcode, r_dest, r_src1, r_src2, shift);
+ else {
+ DCHECK(EncodingMap[opcode].flags & IS_TERTIARY_OP);
+ return NewLIR3(opcode, r_dest, r_src1, r_src2);
+ }
+}
+
+LIR* ArmMir2Lir::OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2)
+{
+ return OpRegRegRegShift(op, r_dest, r_src1, r_src2, 0);
+}
+
+LIR* ArmMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value)
+{
+ LIR* res;
+ bool neg = (value < 0);
+ int abs_value = (neg) ? -value : value;
+ ArmOpcode opcode = kThumbBkpt;
+ ArmOpcode alt_opcode = kThumbBkpt;
+ bool all_low_regs = (ARM_LOWREG(r_dest) && ARM_LOWREG(r_src1));
+ int mod_imm = ModifiedImmediate(value);
+ int mod_imm_neg = ModifiedImmediate(-value);
+
+ switch (op) {
+ case kOpLsl:
+ if (all_low_regs)
+ return NewLIR3(kThumbLslRRI5, r_dest, r_src1, value);
+ else
+ return NewLIR3(kThumb2LslRRI5, r_dest, r_src1, value);
+ case kOpLsr:
+ if (all_low_regs)
+ return NewLIR3(kThumbLsrRRI5, r_dest, r_src1, value);
+ else
+ return NewLIR3(kThumb2LsrRRI5, r_dest, r_src1, value);
+ case kOpAsr:
+ if (all_low_regs)
+ return NewLIR3(kThumbAsrRRI5, r_dest, r_src1, value);
+ else
+ return NewLIR3(kThumb2AsrRRI5, r_dest, r_src1, value);
+ case kOpRor:
+ return NewLIR3(kThumb2RorRRI5, r_dest, r_src1, value);
+ case kOpAdd:
+ if (ARM_LOWREG(r_dest) && (r_src1 == r13sp) &&
+ (value <= 1020) && ((value & 0x3)==0)) {
+ return NewLIR3(kThumbAddSpRel, r_dest, r_src1, value >> 2);
+ } else if (ARM_LOWREG(r_dest) && (r_src1 == r15pc) &&
+ (value <= 1020) && ((value & 0x3)==0)) {
+ return NewLIR3(kThumbAddPcRel, r_dest, r_src1, value >> 2);
+ }
+ // Note: intentional fallthrough
+ case kOpSub:
+ if (all_low_regs && ((abs_value & 0x7) == abs_value)) {
+ if (op == kOpAdd)
+ opcode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
+ else
+ opcode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
+ return NewLIR3(opcode, r_dest, r_src1, abs_value);
+ } else if ((abs_value & 0xff) == abs_value) {
+ if (op == kOpAdd)
+ opcode = (neg) ? kThumb2SubRRI12 : kThumb2AddRRI12;
+ else
+ opcode = (neg) ? kThumb2AddRRI12 : kThumb2SubRRI12;
+ return NewLIR3(opcode, r_dest, r_src1, abs_value);
+ }
+ if (mod_imm_neg >= 0) {
+ op = (op == kOpAdd) ? kOpSub : kOpAdd;
+ mod_imm = mod_imm_neg;
+ }
+ if (op == kOpSub) {
+ opcode = kThumb2SubRRI8;
+ alt_opcode = kThumb2SubRRR;
+ } else {
+ opcode = kThumb2AddRRI8;
+ alt_opcode = kThumb2AddRRR;
+ }
+ break;
+ case kOpRsub:
+ opcode = kThumb2RsubRRI8;
+ alt_opcode = kThumb2RsubRRR;
+ break;
+ case kOpAdc:
+ opcode = kThumb2AdcRRI8;
+ alt_opcode = kThumb2AdcRRR;
+ break;
+ case kOpSbc:
+ opcode = kThumb2SbcRRI8;
+ alt_opcode = kThumb2SbcRRR;
+ break;
+ case kOpOr:
+ opcode = kThumb2OrrRRI8;
+ alt_opcode = kThumb2OrrRRR;
+ break;
+ case kOpAnd:
+ opcode = kThumb2AndRRI8;
+ alt_opcode = kThumb2AndRRR;
+ break;
+ case kOpXor:
+ opcode = kThumb2EorRRI8;
+ alt_opcode = kThumb2EorRRR;
+ break;
+ case kOpMul:
+ //TUNING: power of 2, shift & add
+ mod_imm = -1;
+ alt_opcode = kThumb2MulRRR;
+ break;
+ case kOpCmp: {
+ int mod_imm = ModifiedImmediate(value);
+ LIR* res;
+ if (mod_imm >= 0) {
+ res = NewLIR2(kThumb2CmpRI12, r_src1, mod_imm);
+ } else {
+ int r_tmp = AllocTemp();
+ res = LoadConstant(r_tmp, value);
+ OpRegReg(kOpCmp, r_src1, r_tmp);
+ FreeTemp(r_tmp);
+ }
+ return res;
+ }
+ default:
+ LOG(FATAL) << "Bad opcode: " << op;
+ }
+
+ if (mod_imm >= 0) {
+ return NewLIR3(opcode, r_dest, r_src1, mod_imm);
+ } else {
+ int r_scratch = AllocTemp();
+ LoadConstant(r_scratch, value);
+ if (EncodingMap[alt_opcode].flags & IS_QUAD_OP)
+ res = NewLIR4(alt_opcode, r_dest, r_src1, r_scratch, 0);
+ else
+ res = NewLIR3(alt_opcode, r_dest, r_src1, r_scratch);
+ FreeTemp(r_scratch);
+ return res;
+ }
+}
+
+/* Handle Thumb-only variants here - otherwise punt to OpRegRegImm */
+LIR* ArmMir2Lir::OpRegImm(OpKind op, int r_dest_src1, int value)
+{
+ bool neg = (value < 0);
+ int abs_value = (neg) ? -value : value;
+ bool short_form = (((abs_value & 0xff) == abs_value) && ARM_LOWREG(r_dest_src1));
+ ArmOpcode opcode = kThumbBkpt;
+ switch (op) {
+ case kOpAdd:
+ if ( !neg && (r_dest_src1 == r13sp) && (value <= 508)) { /* sp */
+ DCHECK_EQ((value & 0x3), 0);
+ return NewLIR1(kThumbAddSpI7, value >> 2);
+ } else if (short_form) {
+ opcode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
+ }
+ break;
+ case kOpSub:
+ if (!neg && (r_dest_src1 == r13sp) && (value <= 508)) { /* sp */
+ DCHECK_EQ((value & 0x3), 0);
+ return NewLIR1(kThumbSubSpI7, value >> 2);
+ } else if (short_form) {
+ opcode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
+ }
+ break;
+ case kOpCmp:
+ if (ARM_LOWREG(r_dest_src1) && short_form)
+ opcode = (short_form) ? kThumbCmpRI8 : kThumbCmpRR;
+ else if (ARM_LOWREG(r_dest_src1))
+ opcode = kThumbCmpRR;
+ else {
+ short_form = false;
+ opcode = kThumbCmpHL;
+ }
+ break;
+ default:
+ /* Punt to OpRegRegImm - if bad case catch it there */
+ short_form = false;
+ break;
+ }
+ if (short_form)
+ return NewLIR2(opcode, r_dest_src1, abs_value);
+ else {
+ return OpRegRegImm(op, r_dest_src1, r_dest_src1, value);
+ }
+}
+
+LIR* ArmMir2Lir::LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value)
+{
+ LIR* res = NULL;
+ int32_t val_lo = Low32Bits(value);
+ int32_t val_hi = High32Bits(value);
+ int target_reg = S2d(r_dest_lo, r_dest_hi);
+ if (ARM_FPREG(r_dest_lo)) {
+ if ((val_lo == 0) && (val_hi == 0)) {
+ // TODO: we need better info about the target CPU. a vector exclusive or
+ // would probably be better here if we could rely on its existance.
+ // Load an immediate +2.0 (which encodes to 0)
+ NewLIR2(kThumb2Vmovd_IMM8, target_reg, 0);
+ // +0.0 = +2.0 - +2.0
+ res = NewLIR3(kThumb2Vsubd, target_reg, target_reg, target_reg);
+ } else {
+ int encoded_imm = EncodeImmDouble(value);
+ if (encoded_imm >= 0) {
+ res = NewLIR2(kThumb2Vmovd_IMM8, target_reg, encoded_imm);
+ }
+ }
+ } else {
+ if ((InexpensiveConstantInt(val_lo) && (InexpensiveConstantInt(val_hi)))) {
+ res = LoadConstantNoClobber(r_dest_lo, val_lo);
+ LoadConstantNoClobber(r_dest_hi, val_hi);
+ }
+ }
+ if (res == NULL) {
+ // No short form - load from the literal pool.
+ LIR* data_target = ScanLiteralPoolWide(literal_list_, val_lo, val_hi);
+ if (data_target == NULL) {
+ data_target = AddWideData(&literal_list_, val_lo, val_hi);
+ }
+ if (ARM_FPREG(r_dest_lo)) {
+ res = RawLIR(current_dalvik_offset_, kThumb2Vldrd,
+ target_reg, r15pc, 0, 0, 0, data_target);
+ } else {
+ res = RawLIR(current_dalvik_offset_, kThumb2LdrdPcRel8,
+ r_dest_lo, r_dest_hi, r15pc, 0, 0, data_target);
+ }
+ SetMemRefType(res, true, kLiteral);
+ res->alias_info = reinterpret_cast<uintptr_t>(data_target);
+ AppendLIR(res);
+ }
+ return res;
+}
+
+int ArmMir2Lir::EncodeShift(int code, int amount) {
+ return ((amount & 0x1f) << 2) | code;
+}
+
+LIR* ArmMir2Lir::LoadBaseIndexed(int rBase, int r_index, int r_dest,
+ int scale, OpSize size)
+{
+ bool all_low_regs = ARM_LOWREG(rBase) && ARM_LOWREG(r_index) && ARM_LOWREG(r_dest);
+ LIR* load;
+ ArmOpcode opcode = kThumbBkpt;
+ bool thumb_form = (all_low_regs && (scale == 0));
+ int reg_ptr;
+
+ if (ARM_FPREG(r_dest)) {
+ if (ARM_SINGLEREG(r_dest)) {
+ DCHECK((size == kWord) || (size == kSingle));
+ opcode = kThumb2Vldrs;
+ size = kSingle;
+ } else {
+ DCHECK(ARM_DOUBLEREG(r_dest));
+ DCHECK((size == kLong) || (size == kDouble));
+ DCHECK_EQ((r_dest & 0x1), 0);
+ opcode = kThumb2Vldrd;
+ size = kDouble;
+ }
+ } else {
+ if (size == kSingle)
+ size = kWord;
+ }
+
+ switch (size) {
+ case kDouble: // fall-through
+ case kSingle:
+ reg_ptr = AllocTemp();
+ if (scale) {
+ NewLIR4(kThumb2AddRRR, reg_ptr, rBase, r_index,
+ EncodeShift(kArmLsl, scale));
+ } else {
+ OpRegRegReg(kOpAdd, reg_ptr, rBase, r_index);
+ }
+ load = NewLIR3(opcode, r_dest, reg_ptr, 0);
+ FreeTemp(reg_ptr);
+ return load;
+ case kWord:
+ opcode = (thumb_form) ? kThumbLdrRRR : kThumb2LdrRRR;
+ break;
+ case kUnsignedHalf:
+ opcode = (thumb_form) ? kThumbLdrhRRR : kThumb2LdrhRRR;
+ break;
+ case kSignedHalf:
+ opcode = (thumb_form) ? kThumbLdrshRRR : kThumb2LdrshRRR;
+ break;
+ case kUnsignedByte:
+ opcode = (thumb_form) ? kThumbLdrbRRR : kThumb2LdrbRRR;
+ break;
+ case kSignedByte:
+ opcode = (thumb_form) ? kThumbLdrsbRRR : kThumb2LdrsbRRR;
+ break;
+ default:
+ LOG(FATAL) << "Bad size: " << size;
+ }
+ if (thumb_form)
+ load = NewLIR3(opcode, r_dest, rBase, r_index);
+ else
+ load = NewLIR4(opcode, r_dest, rBase, r_index, scale);
+
+ return load;
+}
+
+LIR* ArmMir2Lir::StoreBaseIndexed(int rBase, int r_index, int r_src,
+ int scale, OpSize size)
+{
+ bool all_low_regs = ARM_LOWREG(rBase) && ARM_LOWREG(r_index) && ARM_LOWREG(r_src);
+ LIR* store = NULL;
+ ArmOpcode opcode = kThumbBkpt;
+ bool thumb_form = (all_low_regs && (scale == 0));
+ int reg_ptr;
+
+ if (ARM_FPREG(r_src)) {
+ if (ARM_SINGLEREG(r_src)) {
+ DCHECK((size == kWord) || (size == kSingle));
+ opcode = kThumb2Vstrs;
+ size = kSingle;
+ } else {
+ DCHECK(ARM_DOUBLEREG(r_src));
+ DCHECK((size == kLong) || (size == kDouble));
+ DCHECK_EQ((r_src & 0x1), 0);
+ opcode = kThumb2Vstrd;
+ size = kDouble;
+ }
+ } else {
+ if (size == kSingle)
+ size = kWord;
+ }
+
+ switch (size) {
+ case kDouble: // fall-through
+ case kSingle:
+ reg_ptr = AllocTemp();
+ if (scale) {
+ NewLIR4(kThumb2AddRRR, reg_ptr, rBase, r_index,
+ EncodeShift(kArmLsl, scale));
+ } else {
+ OpRegRegReg(kOpAdd, reg_ptr, rBase, r_index);
+ }
+ store = NewLIR3(opcode, r_src, reg_ptr, 0);
+ FreeTemp(reg_ptr);
+ return store;
+ case kWord:
+ opcode = (thumb_form) ? kThumbStrRRR : kThumb2StrRRR;
+ break;
+ case kUnsignedHalf:
+ case kSignedHalf:
+ opcode = (thumb_form) ? kThumbStrhRRR : kThumb2StrhRRR;
+ break;
+ case kUnsignedByte:
+ case kSignedByte:
+ opcode = (thumb_form) ? kThumbStrbRRR : kThumb2StrbRRR;
+ break;
+ default:
+ LOG(FATAL) << "Bad size: " << size;
+ }
+ if (thumb_form)
+ store = NewLIR3(opcode, r_src, rBase, r_index);
+ else
+ store = NewLIR4(opcode, r_src, rBase, r_index, scale);
+
+ return store;
+}
+
+/*
+ * Load value from base + displacement. Optionally perform null check
+ * on base (which must have an associated s_reg and MIR). If not
+ * performing null check, incoming MIR can be null.
+ */
+LIR* ArmMir2Lir::LoadBaseDispBody(int rBase, int displacement, int r_dest,
+ int r_dest_hi, OpSize size, int s_reg)
+{
+ LIR* load = NULL;
+ ArmOpcode opcode = kThumbBkpt;
+ bool short_form = false;
+ bool thumb2Form = (displacement < 4092 && displacement >= 0);
+ bool all_low_regs = (ARM_LOWREG(rBase) && ARM_LOWREG(r_dest));
+ int encoded_disp = displacement;
+ bool is64bit = false;
+ bool already_generated = false;
+ switch (size) {
+ case kDouble:
+ case kLong:
+ is64bit = true;
+ if (ARM_FPREG(r_dest)) {
+ if (ARM_SINGLEREG(r_dest)) {
+ DCHECK(ARM_FPREG(r_dest_hi));
+ r_dest = S2d(r_dest, r_dest_hi);
+ }
+ opcode = kThumb2Vldrd;
+ if (displacement <= 1020) {
+ short_form = true;
+ encoded_disp >>= 2;
+ }
+ break;
+ } else {
+ if (displacement <= 1020) {
+ load = NewLIR4(kThumb2LdrdI8, r_dest, r_dest_hi, rBase, displacement >> 2);
+ } else {
+ load = LoadBaseDispBody(rBase, displacement, r_dest,
+ -1, kWord, s_reg);
+ LoadBaseDispBody(rBase, displacement + 4, r_dest_hi,
+ -1, kWord, INVALID_SREG);
+ }
+ already_generated = true;
+ }
+ case kSingle:
+ case kWord:
+ if (ARM_FPREG(r_dest)) {
+ opcode = kThumb2Vldrs;
+ if (displacement <= 1020) {
+ short_form = true;
+ encoded_disp >>= 2;
+ }
+ break;
+ }
+ if (ARM_LOWREG(r_dest) && (rBase == r15pc) &&
+ (displacement <= 1020) && (displacement >= 0)) {
+ short_form = true;
+ encoded_disp >>= 2;
+ opcode = kThumbLdrPcRel;
+ } else if (ARM_LOWREG(r_dest) && (rBase == r13sp) &&
+ (displacement <= 1020) && (displacement >= 0)) {
+ short_form = true;
+ encoded_disp >>= 2;
+ opcode = kThumbLdrSpRel;
+ } else if (all_low_regs && displacement < 128 && displacement >= 0) {
+ DCHECK_EQ((displacement & 0x3), 0);
+ short_form = true;
+ encoded_disp >>= 2;
+ opcode = kThumbLdrRRI5;
+ } else if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2LdrRRI12;
+ }
+ break;
+ case kUnsignedHalf:
+ if (all_low_regs && displacement < 64 && displacement >= 0) {
+ DCHECK_EQ((displacement & 0x1), 0);
+ short_form = true;
+ encoded_disp >>= 1;
+ opcode = kThumbLdrhRRI5;
+ } else if (displacement < 4092 && displacement >= 0) {
+ short_form = true;
+ opcode = kThumb2LdrhRRI12;
+ }
+ break;
+ case kSignedHalf:
+ if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2LdrshRRI12;
+ }
+ break;
+ case kUnsignedByte:
+ if (all_low_regs && displacement < 32 && displacement >= 0) {
+ short_form = true;
+ opcode = kThumbLdrbRRI5;
+ } else if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2LdrbRRI12;
+ }
+ break;
+ case kSignedByte:
+ if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2LdrsbRRI12;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Bad size: " << size;
+ }
+
+ if (!already_generated) {
+ if (short_form) {
+ load = NewLIR3(opcode, r_dest, rBase, encoded_disp);
+ } else {
+ int reg_offset = AllocTemp();
+ LoadConstant(reg_offset, encoded_disp);
+ load = LoadBaseIndexed(rBase, reg_offset, r_dest, 0, size);
+ FreeTemp(reg_offset);
+ }
+ }
+
+ // TODO: in future may need to differentiate Dalvik accesses w/ spills
+ if (rBase == rARM_SP) {
+ AnnotateDalvikRegAccess(load, displacement >> 2, true /* is_load */, is64bit);
+ }
+ return load;
+}
+
+LIR* ArmMir2Lir::LoadBaseDisp(int rBase, int displacement, int r_dest,
+ OpSize size, int s_reg)
+{
+ return LoadBaseDispBody(rBase, displacement, r_dest, -1, size, s_reg);
+}
+
+LIR* ArmMir2Lir::LoadBaseDispWide(int rBase, int displacement, int r_dest_lo,
+ int r_dest_hi, int s_reg)
+{
+ return LoadBaseDispBody(rBase, displacement, r_dest_lo, r_dest_hi, kLong, s_reg);
+}
+
+
+LIR* ArmMir2Lir::StoreBaseDispBody(int rBase, int displacement,
+ int r_src, int r_src_hi, OpSize size) {
+ LIR* store = NULL;
+ ArmOpcode opcode = kThumbBkpt;
+ bool short_form = false;
+ bool thumb2Form = (displacement < 4092 && displacement >= 0);
+ bool all_low_regs = (ARM_LOWREG(rBase) && ARM_LOWREG(r_src));
+ int encoded_disp = displacement;
+ bool is64bit = false;
+ bool already_generated = false;
+ switch (size) {
+ case kLong:
+ case kDouble:
+ is64bit = true;
+ if (!ARM_FPREG(r_src)) {
+ if (displacement <= 1020) {
+ store = NewLIR4(kThumb2StrdI8, r_src, r_src_hi, rBase, displacement >> 2);
+ } else {
+ store = StoreBaseDispBody(rBase, displacement, r_src, -1, kWord);
+ StoreBaseDispBody(rBase, displacement + 4, r_src_hi, -1, kWord);
+ }
+ already_generated = true;
+ } else {
+ if (ARM_SINGLEREG(r_src)) {
+ DCHECK(ARM_FPREG(r_src_hi));
+ r_src = S2d(r_src, r_src_hi);
+ }
+ opcode = kThumb2Vstrd;
+ if (displacement <= 1020) {
+ short_form = true;
+ encoded_disp >>= 2;
+ }
+ }
+ break;
+ case kSingle:
+ case kWord:
+ if (ARM_FPREG(r_src)) {
+ DCHECK(ARM_SINGLEREG(r_src));
+ opcode = kThumb2Vstrs;
+ if (displacement <= 1020) {
+ short_form = true;
+ encoded_disp >>= 2;
+ }
+ break;
+ }
+ if (ARM_LOWREG(r_src) && (rBase == r13sp) &&
+ (displacement <= 1020) && (displacement >= 0)) {
+ short_form = true;
+ encoded_disp >>= 2;
+ opcode = kThumbStrSpRel;
+ } else if (all_low_regs && displacement < 128 && displacement >= 0) {
+ DCHECK_EQ((displacement & 0x3), 0);
+ short_form = true;
+ encoded_disp >>= 2;
+ opcode = kThumbStrRRI5;
+ } else if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2StrRRI12;
+ }
+ break;
+ case kUnsignedHalf:
+ case kSignedHalf:
+ if (all_low_regs && displacement < 64 && displacement >= 0) {
+ DCHECK_EQ((displacement & 0x1), 0);
+ short_form = true;
+ encoded_disp >>= 1;
+ opcode = kThumbStrhRRI5;
+ } else if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2StrhRRI12;
+ }
+ break;
+ case kUnsignedByte:
+ case kSignedByte:
+ if (all_low_regs && displacement < 32 && displacement >= 0) {
+ short_form = true;
+ opcode = kThumbStrbRRI5;
+ } else if (thumb2Form) {
+ short_form = true;
+ opcode = kThumb2StrbRRI12;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Bad size: " << size;
+ }
+ if (!already_generated) {
+ if (short_form) {
+ store = NewLIR3(opcode, r_src, rBase, encoded_disp);
+ } else {
+ int r_scratch = AllocTemp();
+ LoadConstant(r_scratch, encoded_disp);
+ store = StoreBaseIndexed(rBase, r_scratch, r_src, 0, size);
+ FreeTemp(r_scratch);
+ }
+ }
+
+ // TODO: In future, may need to differentiate Dalvik & spill accesses
+ if (rBase == rARM_SP) {
+ AnnotateDalvikRegAccess(store, displacement >> 2, false /* is_load */, is64bit);
+ }
+ return store;
+}
+
+LIR* ArmMir2Lir::StoreBaseDisp(int rBase, int displacement, int r_src,
+ OpSize size)
+{
+ return StoreBaseDispBody(rBase, displacement, r_src, -1, size);
+}
+
+LIR* ArmMir2Lir::StoreBaseDispWide(int rBase, int displacement,
+ int r_src_lo, int r_src_hi)
+{
+ return StoreBaseDispBody(rBase, displacement, r_src_lo, r_src_hi, kLong);
+}
+
+LIR* ArmMir2Lir::OpFpRegCopy(int r_dest, int r_src)
+{
+ int opcode;
+ DCHECK_EQ(ARM_DOUBLEREG(r_dest), ARM_DOUBLEREG(r_src));
+ if (ARM_DOUBLEREG(r_dest)) {
+ opcode = kThumb2Vmovd;
+ } else {
+ if (ARM_SINGLEREG(r_dest)) {
+ opcode = ARM_SINGLEREG(r_src) ? kThumb2Vmovs : kThumb2Fmsr;
+ } else {
+ DCHECK(ARM_SINGLEREG(r_src));
+ opcode = kThumb2Fmrs;
+ }
+ }
+ LIR* res = RawLIR(current_dalvik_offset_, opcode, r_dest, r_src);
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
+ res->flags.is_nop = true;
+ }
+ return res;
+}
+
+LIR* ArmMir2Lir::OpThreadMem(OpKind op, int thread_offset)
+{
+ LOG(FATAL) << "Unexpected use of OpThreadMem for Arm";
+ return NULL;
+}
+
+LIR* ArmMir2Lir::OpMem(OpKind op, int rBase, int disp)
+{
+ LOG(FATAL) << "Unexpected use of OpMem for Arm";
+ return NULL;
+}
+
+LIR* ArmMir2Lir::StoreBaseIndexedDisp(int rBase, int r_index, int scale,
+ int displacement, int r_src, int r_src_hi, OpSize size,
+ int s_reg)
+{
+ LOG(FATAL) << "Unexpected use of StoreBaseIndexedDisp for Arm";
+ return NULL;
+}
+
+LIR* ArmMir2Lir::OpRegMem(OpKind op, int r_dest, int rBase, int offset)
+{
+ LOG(FATAL) << "Unexpected use of OpRegMem for Arm";
+ return NULL;
+}
+
+LIR* ArmMir2Lir::LoadBaseIndexedDisp(int rBase, int r_index, int scale,
+ int displacement, int r_dest, int r_dest_hi, OpSize size,
+ int s_reg)
+{
+ LOG(FATAL) << "Unexpected use of LoadBaseIndexedDisp for Arm";
+ return NULL;
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
new file mode 100644
index 0000000..5c10c4c
--- /dev/null
+++ b/compiler/dex/quick/codegen_util.cc
@@ -0,0 +1,1109 @@
+/*
+ * Copyright (C) 2011 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 "dex/compiler_internals.h"
+#include "dex_file-inl.h"
+#include "gc_map.h"
+#include "mir_to_lir-inl.h"
+#include "verifier/dex_gc_map.h"
+#include "verifier/method_verifier.h"
+
+namespace art {
+
+bool Mir2Lir::IsInexpensiveConstant(RegLocation rl_src)
+{
+ bool res = false;
+ if (rl_src.is_const) {
+ if (rl_src.wide) {
+ if (rl_src.fp) {
+ res = InexpensiveConstantDouble(mir_graph_->ConstantValueWide(rl_src));
+ } else {
+ res = InexpensiveConstantLong(mir_graph_->ConstantValueWide(rl_src));
+ }
+ } else {
+ if (rl_src.fp) {
+ res = InexpensiveConstantFloat(mir_graph_->ConstantValue(rl_src));
+ } else {
+ res = InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src));
+ }
+ }
+ }
+ return res;
+}
+
+void Mir2Lir::MarkSafepointPC(LIR* inst)
+{
+ inst->def_mask = ENCODE_ALL;
+ LIR* safepoint_pc = NewLIR0(kPseudoSafepointPC);
+ DCHECK_EQ(safepoint_pc->def_mask, ENCODE_ALL);
+}
+
+bool Mir2Lir::FastInstance(uint32_t field_idx, int& field_offset, bool& is_volatile, bool is_put)
+{
+ return cu_->compiler_driver->ComputeInstanceFieldInfo(
+ field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, is_volatile, is_put);
+}
+
+/* Convert an instruction to a NOP */
+void Mir2Lir::NopLIR( LIR* lir)
+{
+ lir->flags.is_nop = true;
+}
+
+void Mir2Lir::SetMemRefType(LIR* lir, bool is_load, int mem_type)
+{
+ uint64_t *mask_ptr;
+ uint64_t mask = ENCODE_MEM;;
+ DCHECK(GetTargetInstFlags(lir->opcode) & (IS_LOAD | IS_STORE));
+ if (is_load) {
+ mask_ptr = &lir->use_mask;
+ } else {
+ mask_ptr = &lir->def_mask;
+ }
+ /* Clear out the memref flags */
+ *mask_ptr &= ~mask;
+ /* ..and then add back the one we need */
+ switch (mem_type) {
+ case kLiteral:
+ DCHECK(is_load);
+ *mask_ptr |= ENCODE_LITERAL;
+ break;
+ case kDalvikReg:
+ *mask_ptr |= ENCODE_DALVIK_REG;
+ break;
+ case kHeapRef:
+ *mask_ptr |= ENCODE_HEAP_REF;
+ break;
+ case kMustNotAlias:
+ /* Currently only loads can be marked as kMustNotAlias */
+ DCHECK(!(GetTargetInstFlags(lir->opcode) & IS_STORE));
+ *mask_ptr |= ENCODE_MUST_NOT_ALIAS;
+ break;
+ default:
+ LOG(FATAL) << "Oat: invalid memref kind - " << mem_type;
+ }
+}
+
+/*
+ * Mark load/store instructions that access Dalvik registers through the stack.
+ */
+void Mir2Lir::AnnotateDalvikRegAccess(LIR* lir, int reg_id, bool is_load,
+ bool is64bit)
+{
+ SetMemRefType(lir, is_load, kDalvikReg);
+
+ /*
+ * Store the Dalvik register id in alias_info. Mark the MSB if it is a 64-bit
+ * access.
+ */
+ lir->alias_info = ENCODE_ALIAS_INFO(reg_id, is64bit);
+}
+
+/*
+ * Debugging macros
+ */
+#define DUMP_RESOURCE_MASK(X)
+
+/* Pretty-print a LIR instruction */
+void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr)
+{
+ int offset = lir->offset;
+ int dest = lir->operands[0];
+ const bool dump_nop = (cu_->enable_debug & (1 << kDebugShowNops));
+
+ /* Handle pseudo-ops individually, and all regular insns as a group */
+ switch (lir->opcode) {
+ case kPseudoMethodEntry:
+ LOG(INFO) << "-------- method entry "
+ << PrettyMethod(cu_->method_idx, *cu_->dex_file);
+ break;
+ case kPseudoMethodExit:
+ LOG(INFO) << "-------- Method_Exit";
+ break;
+ case kPseudoBarrier:
+ LOG(INFO) << "-------- BARRIER";
+ break;
+ case kPseudoEntryBlock:
+ LOG(INFO) << "-------- entry offset: 0x" << std::hex << dest;
+ break;
+ case kPseudoDalvikByteCodeBoundary:
+ if (lir->operands[0] == 0) {
+ lir->operands[0] = reinterpret_cast<uintptr_t>("No instruction string");
+ }
+ LOG(INFO) << "-------- dalvik offset: 0x" << std::hex
+ << lir->dalvik_offset << " @ " << reinterpret_cast<char*>(lir->operands[0]);
+ break;
+ case kPseudoExitBlock:
+ LOG(INFO) << "-------- exit offset: 0x" << std::hex << dest;
+ break;
+ case kPseudoPseudoAlign4:
+ LOG(INFO) << reinterpret_cast<uintptr_t>(base_addr) + offset << " (0x" << std::hex
+ << offset << "): .align4";
+ break;
+ case kPseudoEHBlockLabel:
+ LOG(INFO) << "Exception_Handling:";
+ break;
+ case kPseudoTargetLabel:
+ case kPseudoNormalBlockLabel:
+ LOG(INFO) << "L" << reinterpret_cast<void*>(lir) << ":";
+ break;
+ case kPseudoThrowTarget:
+ LOG(INFO) << "LT" << reinterpret_cast<void*>(lir) << ":";
+ break;
+ case kPseudoIntrinsicRetry:
+ LOG(INFO) << "IR" << reinterpret_cast<void*>(lir) << ":";
+ break;
+ case kPseudoSuspendTarget:
+ LOG(INFO) << "LS" << reinterpret_cast<void*>(lir) << ":";
+ break;
+ case kPseudoSafepointPC:
+ LOG(INFO) << "LsafepointPC_0x" << std::hex << lir->offset << "_" << lir->dalvik_offset << ":";
+ break;
+ case kPseudoExportedPC:
+ LOG(INFO) << "LexportedPC_0x" << std::hex << lir->offset << "_" << lir->dalvik_offset << ":";
+ break;
+ case kPseudoCaseLabel:
+ LOG(INFO) << "LC" << reinterpret_cast<void*>(lir) << ": Case target 0x"
+ << std::hex << lir->operands[0] << "|" << std::dec <<
+ lir->operands[0];
+ break;
+ default:
+ if (lir->flags.is_nop && !dump_nop) {
+ break;
+ } else {
+ std::string op_name(BuildInsnString(GetTargetInstName(lir->opcode),
+ lir, base_addr));
+ std::string op_operands(BuildInsnString(GetTargetInstFmt(lir->opcode),
+ lir, base_addr));
+ LOG(INFO) << StringPrintf("%05x: %-9s%s%s",
+ reinterpret_cast<unsigned int>(base_addr + offset),
+ op_name.c_str(), op_operands.c_str(),
+ lir->flags.is_nop ? "(nop)" : "");
+ }
+ break;
+ }
+
+ if (lir->use_mask && (!lir->flags.is_nop || dump_nop)) {
+ DUMP_RESOURCE_MASK(DumpResourceMask((LIR* ) lir, lir->use_mask, "use"));
+ }
+ if (lir->def_mask && (!lir->flags.is_nop || dump_nop)) {
+ DUMP_RESOURCE_MASK(DumpResourceMask((LIR* ) lir, lir->def_mask, "def"));
+ }
+}
+
+void Mir2Lir::DumpPromotionMap()
+{
+ int num_regs = cu_->num_dalvik_registers + cu_->num_compiler_temps + 1;
+ for (int i = 0; i < num_regs; i++) {
+ PromotionMap v_reg_map = promotion_map_[i];
+ std::string buf;
+ if (v_reg_map.fp_location == kLocPhysReg) {
+ StringAppendF(&buf, " : s%d", v_reg_map.FpReg & FpRegMask());
+ }
+
+ std::string buf3;
+ if (i < cu_->num_dalvik_registers) {
+ StringAppendF(&buf3, "%02d", i);
+ } else if (i == mir_graph_->GetMethodSReg()) {
+ buf3 = "Method*";
+ } else {
+ StringAppendF(&buf3, "ct%d", i - cu_->num_dalvik_registers);
+ }
+
+ LOG(INFO) << StringPrintf("V[%s] -> %s%d%s", buf3.c_str(),
+ v_reg_map.core_location == kLocPhysReg ?
+ "r" : "SP+", v_reg_map.core_location == kLocPhysReg ?
+ v_reg_map.core_reg : SRegOffset(i),
+ buf.c_str());
+ }
+}
+
+/* Dump a mapping table */
+void Mir2Lir::DumpMappingTable(const char* table_name, const std::string& descriptor,
+ const std::string& name, const std::string& signature,
+ const std::vector<uint32_t>& v) {
+ if (v.size() > 0) {
+ std::string line(StringPrintf("\n %s %s%s_%s_table[%zu] = {", table_name,
+ descriptor.c_str(), name.c_str(), signature.c_str(), v.size()));
+ std::replace(line.begin(), line.end(), ';', '_');
+ LOG(INFO) << line;
+ for (uint32_t i = 0; i < v.size(); i+=2) {
+ line = StringPrintf(" {0x%05x, 0x%04x},", v[i], v[i+1]);
+ LOG(INFO) << line;
+ }
+ LOG(INFO) <<" };\n\n";
+ }
+}
+
+/* Dump instructions and constant pool contents */
+void Mir2Lir::CodegenDump()
+{
+ LOG(INFO) << "Dumping LIR insns for "
+ << PrettyMethod(cu_->method_idx, *cu_->dex_file);
+ LIR* lir_insn;
+ int insns_size = cu_->code_item->insns_size_in_code_units_;
+
+ LOG(INFO) << "Regs (excluding ins) : " << cu_->num_regs;
+ LOG(INFO) << "Ins : " << cu_->num_ins;
+ LOG(INFO) << "Outs : " << cu_->num_outs;
+ LOG(INFO) << "CoreSpills : " << num_core_spills_;
+ LOG(INFO) << "FPSpills : " << num_fp_spills_;
+ LOG(INFO) << "CompilerTemps : " << cu_->num_compiler_temps;
+ LOG(INFO) << "Frame size : " << frame_size_;
+ LOG(INFO) << "code size is " << total_size_ <<
+ " bytes, Dalvik size is " << insns_size * 2;
+ LOG(INFO) << "expansion factor: "
+ << static_cast<float>(total_size_) / static_cast<float>(insns_size * 2);
+ DumpPromotionMap();
+ for (lir_insn = first_lir_insn_; lir_insn != NULL; lir_insn = lir_insn->next) {
+ DumpLIRInsn(lir_insn, 0);
+ }
+ for (lir_insn = literal_list_; lir_insn != NULL; lir_insn = lir_insn->next) {
+ LOG(INFO) << StringPrintf("%x (%04x): .word (%#x)", lir_insn->offset, lir_insn->offset,
+ lir_insn->operands[0]);
+ }
+
+ const DexFile::MethodId& method_id =
+ cu_->dex_file->GetMethodId(cu_->method_idx);
+ std::string signature(cu_->dex_file->GetMethodSignature(method_id));
+ std::string name(cu_->dex_file->GetMethodName(method_id));
+ std::string descriptor(cu_->dex_file->GetMethodDeclaringClassDescriptor(method_id));
+
+ // Dump mapping tables
+ DumpMappingTable("PC2Dex_MappingTable", descriptor, name, signature, pc2dex_mapping_table_);
+ DumpMappingTable("Dex2PC_MappingTable", descriptor, name, signature, dex2pc_mapping_table_);
+}
+
+/*
+ * Search the existing constants in the literal pool for an exact or close match
+ * within specified delta (greater or equal to 0).
+ */
+LIR* Mir2Lir::ScanLiteralPool(LIR* data_target, int value, unsigned int delta)
+{
+ while (data_target) {
+ if ((static_cast<unsigned>(value - data_target->operands[0])) <= delta)
+ return data_target;
+ data_target = data_target->next;
+ }
+ return NULL;
+}
+
+/* Search the existing constants in the literal pool for an exact wide match */
+LIR* Mir2Lir::ScanLiteralPoolWide(LIR* data_target, int val_lo, int val_hi)
+{
+ bool lo_match = false;
+ LIR* lo_target = NULL;
+ while (data_target) {
+ if (lo_match && (data_target->operands[0] == val_hi)) {
+ // Record high word in case we need to expand this later.
+ lo_target->operands[1] = val_hi;
+ return lo_target;
+ }
+ lo_match = false;
+ if (data_target->operands[0] == val_lo) {
+ lo_match = true;
+ lo_target = data_target;
+ }
+ data_target = data_target->next;
+ }
+ return NULL;
+}
+
+/*
+ * The following are building blocks to insert constants into the pool or
+ * instruction streams.
+ */
+
+/* Add a 32-bit constant to the constant pool */
+LIR* Mir2Lir::AddWordData(LIR* *constant_list_p, int value)
+{
+ /* Add the constant to the literal pool */
+ if (constant_list_p) {
+ LIR* new_value = static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocData));
+ new_value->operands[0] = value;
+ new_value->next = *constant_list_p;
+ *constant_list_p = new_value;
+ return new_value;
+ }
+ return NULL;
+}
+
+/* Add a 64-bit constant to the constant pool or mixed with code */
+LIR* Mir2Lir::AddWideData(LIR* *constant_list_p, int val_lo, int val_hi)
+{
+ AddWordData(constant_list_p, val_hi);
+ return AddWordData(constant_list_p, val_lo);
+}
+
+static void PushWord(std::vector<uint8_t>&buf, int data) {
+ buf.push_back( data & 0xff);
+ buf.push_back( (data >> 8) & 0xff);
+ buf.push_back( (data >> 16) & 0xff);
+ buf.push_back( (data >> 24) & 0xff);
+}
+
+static void AlignBuffer(std::vector<uint8_t>&buf, size_t offset) {
+ while (buf.size() < offset) {
+ buf.push_back(0);
+ }
+}
+
+/* Write the literal pool to the output stream */
+void Mir2Lir::InstallLiteralPools()
+{
+ AlignBuffer(code_buffer_, data_offset_);
+ LIR* data_lir = literal_list_;
+ while (data_lir != NULL) {
+ PushWord(code_buffer_, data_lir->operands[0]);
+ data_lir = NEXT_LIR(data_lir);
+ }
+ // Push code and method literals, record offsets for the compiler to patch.
+ data_lir = code_literal_list_;
+ while (data_lir != NULL) {
+ uint32_t target = data_lir->operands[0];
+ cu_->compiler_driver->AddCodePatch(cu_->dex_file,
+ cu_->method_idx,
+ cu_->invoke_type,
+ target,
+ static_cast<InvokeType>(data_lir->operands[1]),
+ code_buffer_.size());
+ const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target);
+ // unique based on target to ensure code deduplication works
+ uint32_t unique_patch_value = reinterpret_cast<uint32_t>(&id);
+ PushWord(code_buffer_, unique_patch_value);
+ data_lir = NEXT_LIR(data_lir);
+ }
+ data_lir = method_literal_list_;
+ while (data_lir != NULL) {
+ uint32_t target = data_lir->operands[0];
+ cu_->compiler_driver->AddMethodPatch(cu_->dex_file,
+ cu_->method_idx,
+ cu_->invoke_type,
+ target,
+ static_cast<InvokeType>(data_lir->operands[1]),
+ code_buffer_.size());
+ const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target);
+ // unique based on target to ensure code deduplication works
+ uint32_t unique_patch_value = reinterpret_cast<uint32_t>(&id);
+ PushWord(code_buffer_, unique_patch_value);
+ data_lir = NEXT_LIR(data_lir);
+ }
+}
+
+/* Write the switch tables to the output stream */
+void Mir2Lir::InstallSwitchTables()
+{
+ GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
+ while (true) {
+ Mir2Lir::SwitchTable* tab_rec = iterator.Next();
+ if (tab_rec == NULL) break;
+ AlignBuffer(code_buffer_, tab_rec->offset);
+ /*
+ * For Arm, our reference point is the address of the bx
+ * instruction that does the launch, so we have to subtract
+ * the auto pc-advance. For other targets the reference point
+ * is a label, so we can use the offset as-is.
+ */
+ int bx_offset = INVALID_OFFSET;
+ switch (cu_->instruction_set) {
+ case kThumb2:
+ bx_offset = tab_rec->anchor->offset + 4;
+ break;
+ case kX86:
+ bx_offset = 0;
+ break;
+ case kMips:
+ bx_offset = tab_rec->anchor->offset;
+ break;
+ default: LOG(FATAL) << "Unexpected instruction set: " << cu_->instruction_set;
+ }
+ if (cu_->verbose) {
+ LOG(INFO) << "Switch table for offset 0x" << std::hex << bx_offset;
+ }
+ if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
+ const int* keys = reinterpret_cast<const int*>(&(tab_rec->table[2]));
+ for (int elems = 0; elems < tab_rec->table[1]; elems++) {
+ int disp = tab_rec->targets[elems]->offset - bx_offset;
+ if (cu_->verbose) {
+ LOG(INFO) << " Case[" << elems << "] key: 0x"
+ << std::hex << keys[elems] << ", disp: 0x"
+ << std::hex << disp;
+ }
+ PushWord(code_buffer_, keys[elems]);
+ PushWord(code_buffer_,
+ tab_rec->targets[elems]->offset - bx_offset);
+ }
+ } else {
+ DCHECK_EQ(static_cast<int>(tab_rec->table[0]),
+ static_cast<int>(Instruction::kPackedSwitchSignature));
+ for (int elems = 0; elems < tab_rec->table[1]; elems++) {
+ int disp = tab_rec->targets[elems]->offset - bx_offset;
+ if (cu_->verbose) {
+ LOG(INFO) << " Case[" << elems << "] disp: 0x"
+ << std::hex << disp;
+ }
+ PushWord(code_buffer_, tab_rec->targets[elems]->offset - bx_offset);
+ }
+ }
+ }
+}
+
+/* Write the fill array dta to the output stream */
+void Mir2Lir::InstallFillArrayData()
+{
+ GrowableArray<FillArrayData*>::Iterator iterator(&fill_array_data_);
+ while (true) {
+ Mir2Lir::FillArrayData *tab_rec = iterator.Next();
+ if (tab_rec == NULL) break;
+ AlignBuffer(code_buffer_, tab_rec->offset);
+ for (int i = 0; i < (tab_rec->size + 1) / 2; i++) {
+ code_buffer_.push_back( tab_rec->table[i] & 0xFF);
+ code_buffer_.push_back( (tab_rec->table[i] >> 8) & 0xFF);
+ }
+ }
+}
+
+static int AssignLiteralOffsetCommon(LIR* lir, int offset)
+{
+ for (;lir != NULL; lir = lir->next) {
+ lir->offset = offset;
+ offset += 4;
+ }
+ return offset;
+}
+
+// Make sure we have a code address for every declared catch entry
+bool Mir2Lir::VerifyCatchEntries()
+{
+ bool success = true;
+ for (std::set<uint32_t>::const_iterator it = mir_graph_->catches_.begin();
+ it != mir_graph_->catches_.end(); ++it) {
+ uint32_t dex_pc = *it;
+ bool found = false;
+ for (size_t i = 0; i < dex2pc_mapping_table_.size(); i += 2) {
+ if (dex_pc == dex2pc_mapping_table_[i+1]) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ LOG(INFO) << "Missing native PC for catch entry @ 0x" << std::hex << dex_pc;
+ success = false;
+ }
+ }
+ // Now, try in the other direction
+ for (size_t i = 0; i < dex2pc_mapping_table_.size(); i += 2) {
+ uint32_t dex_pc = dex2pc_mapping_table_[i+1];
+ if (mir_graph_->catches_.find(dex_pc) == mir_graph_->catches_.end()) {
+ LOG(INFO) << "Unexpected catch entry @ dex pc 0x" << std::hex << dex_pc;
+ success = false;
+ }
+ }
+ if (!success) {
+ LOG(INFO) << "Bad dex2pcMapping table in " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
+ LOG(INFO) << "Entries @ decode: " << mir_graph_->catches_.size() << ", Entries in table: "
+ << dex2pc_mapping_table_.size()/2;
+ }
+ return success;
+}
+
+
+void Mir2Lir::CreateMappingTables()
+{
+ for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) {
+ if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) {
+ pc2dex_mapping_table_.push_back(tgt_lir->offset);
+ pc2dex_mapping_table_.push_back(tgt_lir->dalvik_offset);
+ }
+ if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoExportedPC)) {
+ dex2pc_mapping_table_.push_back(tgt_lir->offset);
+ dex2pc_mapping_table_.push_back(tgt_lir->dalvik_offset);
+ }
+ }
+ if (kIsDebugBuild) {
+ DCHECK(VerifyCatchEntries());
+ }
+ combined_mapping_table_.push_back(pc2dex_mapping_table_.size() +
+ dex2pc_mapping_table_.size());
+ combined_mapping_table_.push_back(pc2dex_mapping_table_.size());
+ combined_mapping_table_.insert(combined_mapping_table_.end(), pc2dex_mapping_table_.begin(),
+ pc2dex_mapping_table_.end());
+ combined_mapping_table_.insert(combined_mapping_table_.end(), dex2pc_mapping_table_.begin(),
+ dex2pc_mapping_table_.end());
+}
+
+class NativePcToReferenceMapBuilder {
+ public:
+ NativePcToReferenceMapBuilder(std::vector<uint8_t>* table,
+ size_t entries, uint32_t max_native_offset,
+ size_t references_width) : entries_(entries),
+ references_width_(references_width), in_use_(entries),
+ table_(table) {
+ // Compute width in bytes needed to hold max_native_offset.
+ native_offset_width_ = 0;
+ while (max_native_offset != 0) {
+ native_offset_width_++;
+ max_native_offset >>= 8;
+ }
+ // Resize table and set up header.
+ table->resize((EntryWidth() * entries) + sizeof(uint32_t));
+ CHECK_LT(native_offset_width_, 1U << 3);
+ (*table)[0] = native_offset_width_ & 7;
+ CHECK_LT(references_width_, 1U << 13);
+ (*table)[0] |= (references_width_ << 3) & 0xFF;
+ (*table)[1] = (references_width_ >> 5) & 0xFF;
+ CHECK_LT(entries, 1U << 16);
+ (*table)[2] = entries & 0xFF;
+ (*table)[3] = (entries >> 8) & 0xFF;
+ }
+
+ void AddEntry(uint32_t native_offset, const uint8_t* references) {
+ size_t table_index = TableIndex(native_offset);
+ while (in_use_[table_index]) {
+ table_index = (table_index + 1) % entries_;
+ }
+ in_use_[table_index] = true;
+ SetNativeOffset(table_index, native_offset);
+ DCHECK_EQ(native_offset, GetNativeOffset(table_index));
+ SetReferences(table_index, references);
+ }
+
+ private:
+ size_t TableIndex(uint32_t native_offset) {
+ return NativePcOffsetToReferenceMap::Hash(native_offset) % entries_;
+ }
+
+ uint32_t GetNativeOffset(size_t table_index) {
+ uint32_t native_offset = 0;
+ size_t table_offset = (table_index * EntryWidth()) + sizeof(uint32_t);
+ for (size_t i = 0; i < native_offset_width_; i++) {
+ native_offset |= (*table_)[table_offset + i] << (i * 8);
+ }
+ return native_offset;
+ }
+
+ void SetNativeOffset(size_t table_index, uint32_t native_offset) {
+ size_t table_offset = (table_index * EntryWidth()) + sizeof(uint32_t);
+ for (size_t i = 0; i < native_offset_width_; i++) {
+ (*table_)[table_offset + i] = (native_offset >> (i * 8)) & 0xFF;
+ }
+ }
+
+ void SetReferences(size_t table_index, const uint8_t* references) {
+ size_t table_offset = (table_index * EntryWidth()) + sizeof(uint32_t);
+ memcpy(&(*table_)[table_offset + native_offset_width_], references, references_width_);
+ }
+
+ size_t EntryWidth() const {
+ return native_offset_width_ + references_width_;
+ }
+
+ // Number of entries in the table.
+ const size_t entries_;
+ // Number of bytes used to encode the reference bitmap.
+ const size_t references_width_;
+ // Number of bytes used to encode a native offset.
+ size_t native_offset_width_;
+ // Entries that are in use.
+ std::vector<bool> in_use_;
+ // The table we're building.
+ std::vector<uint8_t>* const table_;
+};
+
+void Mir2Lir::CreateNativeGcMap() {
+ const std::vector<uint32_t>& mapping_table = pc2dex_mapping_table_;
+ uint32_t max_native_offset = 0;
+ for (size_t i = 0; i < mapping_table.size(); i += 2) {
+ uint32_t native_offset = mapping_table[i + 0];
+ if (native_offset > max_native_offset) {
+ max_native_offset = native_offset;
+ }
+ }
+ MethodReference method_ref(cu_->dex_file, cu_->method_idx);
+ const std::vector<uint8_t>* gc_map_raw = verifier::MethodVerifier::GetDexGcMap(method_ref);
+ verifier::DexPcToReferenceMap dex_gc_map(&(*gc_map_raw)[4], gc_map_raw->size() - 4);
+ // Compute native offset to references size.
+ NativePcToReferenceMapBuilder native_gc_map_builder(&native_gc_map_,
+ mapping_table.size() / 2, max_native_offset,
+ dex_gc_map.RegWidth());
+
+ for (size_t i = 0; i < mapping_table.size(); i += 2) {
+ uint32_t native_offset = mapping_table[i + 0];
+ uint32_t dex_pc = mapping_table[i + 1];
+ const uint8_t* references = dex_gc_map.FindBitMap(dex_pc, false);
+ CHECK(references != NULL) << "Missing ref for dex pc 0x" << std::hex << dex_pc;
+ native_gc_map_builder.AddEntry(native_offset, references);
+ }
+}
+
+/* Determine the offset of each literal field */
+int Mir2Lir::AssignLiteralOffset(int offset)
+{
+ offset = AssignLiteralOffsetCommon(literal_list_, offset);
+ offset = AssignLiteralOffsetCommon(code_literal_list_, offset);
+ offset = AssignLiteralOffsetCommon(method_literal_list_, offset);
+ return offset;
+}
+
+int Mir2Lir::AssignSwitchTablesOffset(int offset)
+{
+ GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
+ while (true) {
+ Mir2Lir::SwitchTable *tab_rec = iterator.Next();
+ if (tab_rec == NULL) break;
+ tab_rec->offset = offset;
+ if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
+ offset += tab_rec->table[1] * (sizeof(int) * 2);
+ } else {
+ DCHECK_EQ(static_cast<int>(tab_rec->table[0]),
+ static_cast<int>(Instruction::kPackedSwitchSignature));
+ offset += tab_rec->table[1] * sizeof(int);
+ }
+ }
+ return offset;
+}
+
+int Mir2Lir::AssignFillArrayDataOffset(int offset)
+{
+ GrowableArray<FillArrayData*>::Iterator iterator(&fill_array_data_);
+ while (true) {
+ Mir2Lir::FillArrayData *tab_rec = iterator.Next();
+ if (tab_rec == NULL) break;
+ tab_rec->offset = offset;
+ offset += tab_rec->size;
+ // word align
+ offset = (offset + 3) & ~3;
+ }
+ return offset;
+}
+
+// LIR offset assignment.
+int Mir2Lir::AssignInsnOffsets()
+{
+ LIR* lir;
+ int offset = 0;
+
+ for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
+ lir->offset = offset;
+ if (lir->opcode >= 0) {
+ if (!lir->flags.is_nop) {
+ offset += lir->flags.size;
+ }
+ } else if (lir->opcode == kPseudoPseudoAlign4) {
+ if (offset & 0x2) {
+ offset += 2;
+ lir->operands[0] = 1;
+ } else {
+ lir->operands[0] = 0;
+ }
+ }
+ /* Pseudo opcodes don't consume space */
+ }
+
+ return offset;
+}
+
+/*
+ * Walk the compilation unit and assign offsets to instructions
+ * and literals and compute the total size of the compiled unit.
+ */
+void Mir2Lir::AssignOffsets()
+{
+ int offset = AssignInsnOffsets();
+
+ /* Const values have to be word aligned */
+ offset = (offset + 3) & ~3;
+
+ /* Set up offsets for literals */
+ data_offset_ = offset;
+
+ offset = AssignLiteralOffset(offset);
+
+ offset = AssignSwitchTablesOffset(offset);
+
+ offset = AssignFillArrayDataOffset(offset);
+
+ total_size_ = offset;
+}
+
+/*
+ * Go over each instruction in the list and calculate the offset from the top
+ * before sending them off to the assembler. If out-of-range branch distance is
+ * seen rearrange the instructions a bit to correct it.
+ */
+void Mir2Lir::AssembleLIR()
+{
+ AssignOffsets();
+ int assembler_retries = 0;
+ /*
+ * Assemble here. Note that we generate code with optimistic assumptions
+ * and if found now to work, we'll have to redo the sequence and retry.
+ */
+
+ while (true) {
+ AssemblerStatus res = AssembleInstructions(0);
+ if (res == kSuccess) {
+ break;
+ } else {
+ assembler_retries++;
+ if (assembler_retries > MAX_ASSEMBLER_RETRIES) {
+ CodegenDump();
+ LOG(FATAL) << "Assembler error - too many retries";
+ }
+ // Redo offsets and try again
+ AssignOffsets();
+ code_buffer_.clear();
+ }
+ }
+
+ // Install literals
+ InstallLiteralPools();
+
+ // Install switch tables
+ InstallSwitchTables();
+
+ // Install fill array data
+ InstallFillArrayData();
+
+ // Create the mapping table and native offset to reference map.
+ CreateMappingTables();
+
+ CreateNativeGcMap();
+}
+
+/*
+ * Insert a kPseudoCaseLabel at the beginning of the Dalvik
+ * offset vaddr. This label will be used to fix up the case
+ * branch table during the assembly phase. Be sure to set
+ * all resource flags on this to prevent code motion across
+ * target boundaries. KeyVal is just there for debugging.
+ */
+LIR* Mir2Lir::InsertCaseLabel(int vaddr, int keyVal)
+{
+ SafeMap<unsigned int, LIR*>::iterator it;
+ it = boundary_map_.find(vaddr);
+ if (it == boundary_map_.end()) {
+ LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
+ }
+ LIR* new_label = static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocLIR));
+ new_label->dalvik_offset = vaddr;
+ new_label->opcode = kPseudoCaseLabel;
+ new_label->operands[0] = keyVal;
+ InsertLIRAfter(it->second, new_label);
+ return new_label;
+}
+
+void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec)
+{
+ const uint16_t* table = tab_rec->table;
+ int base_vaddr = tab_rec->vaddr;
+ const int *targets = reinterpret_cast<const int*>(&table[4]);
+ int entries = table[1];
+ int low_key = s4FromSwitchData(&table[2]);
+ for (int i = 0; i < entries; i++) {
+ tab_rec->targets[i] = InsertCaseLabel(base_vaddr + targets[i], i + low_key);
+ }
+}
+
+void Mir2Lir::MarkSparseCaseLabels(Mir2Lir::SwitchTable *tab_rec)
+{
+ const uint16_t* table = tab_rec->table;
+ int base_vaddr = tab_rec->vaddr;
+ int entries = table[1];
+ const int* keys = reinterpret_cast<const int*>(&table[2]);
+ const int* targets = &keys[entries];
+ for (int i = 0; i < entries; i++) {
+ tab_rec->targets[i] = InsertCaseLabel(base_vaddr + targets[i], keys[i]);
+ }
+}
+
+void Mir2Lir::ProcessSwitchTables()
+{
+ GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
+ while (true) {
+ Mir2Lir::SwitchTable *tab_rec = iterator.Next();
+ if (tab_rec == NULL) break;
+ if (tab_rec->table[0] == Instruction::kPackedSwitchSignature) {
+ MarkPackedCaseLabels(tab_rec);
+ } else if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
+ MarkSparseCaseLabels(tab_rec);
+ } else {
+ LOG(FATAL) << "Invalid switch table";
+ }
+ }
+}
+
+void Mir2Lir::DumpSparseSwitchTable(const uint16_t* table)
+ /*
+ * Sparse switch data format:
+ * ushort ident = 0x0200 magic value
+ * ushort size number of entries in the table; > 0
+ * int keys[size] keys, sorted low-to-high; 32-bit aligned
+ * int targets[size] branch targets, relative to switch opcode
+ *
+ * Total size is (2+size*4) 16-bit code units.
+ */
+{
+ uint16_t ident = table[0];
+ int entries = table[1];
+ const int* keys = reinterpret_cast<const int*>(&table[2]);
+ const int* targets = &keys[entries];
+ LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident
+ << ", entries: " << std::dec << entries;
+ for (int i = 0; i < entries; i++) {
+ LOG(INFO) << " Key[" << keys[i] << "] -> 0x" << std::hex << targets[i];
+ }
+}
+
+void Mir2Lir::DumpPackedSwitchTable(const uint16_t* table)
+ /*
+ * Packed switch data format:
+ * ushort ident = 0x0100 magic value
+ * ushort size number of entries in the table
+ * int first_key first (and lowest) switch case value
+ * int targets[size] branch targets, relative to switch opcode
+ *
+ * Total size is (4+size*2) 16-bit code units.
+ */
+{
+ uint16_t ident = table[0];
+ const int* targets = reinterpret_cast<const int*>(&table[4]);
+ int entries = table[1];
+ int low_key = s4FromSwitchData(&table[2]);
+ LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident
+ << ", entries: " << std::dec << entries << ", low_key: " << low_key;
+ for (int i = 0; i < entries; i++) {
+ LOG(INFO) << " Key[" << (i + low_key) << "] -> 0x" << std::hex
+ << targets[i];
+ }
+}
+
+/*
+ * Set up special LIR to mark a Dalvik byte-code instruction start and
+ * record it in the boundary_map. NOTE: in cases such as kMirOpCheck in
+ * which we split a single Dalvik instruction, only the first MIR op
+ * associated with a Dalvik PC should be entered into the map.
+ */
+LIR* Mir2Lir::MarkBoundary(int offset, const char* inst_str)
+{
+ LIR* res = NewLIR1(kPseudoDalvikByteCodeBoundary, reinterpret_cast<uintptr_t>(inst_str));
+ if (boundary_map_.find(offset) == boundary_map_.end()) {
+ boundary_map_.Put(offset, res);
+ }
+ return res;
+}
+
+bool Mir2Lir::EvaluateBranch(Instruction::Code opcode, int32_t src1, int32_t src2)
+{
+ bool is_taken;
+ switch (opcode) {
+ case Instruction::IF_EQ: is_taken = (src1 == src2); break;
+ case Instruction::IF_NE: is_taken = (src1 != src2); break;
+ case Instruction::IF_LT: is_taken = (src1 < src2); break;
+ case Instruction::IF_GE: is_taken = (src1 >= src2); break;
+ case Instruction::IF_GT: is_taken = (src1 > src2); break;
+ case Instruction::IF_LE: is_taken = (src1 <= src2); break;
+ case Instruction::IF_EQZ: is_taken = (src1 == 0); break;
+ case Instruction::IF_NEZ: is_taken = (src1 != 0); break;
+ case Instruction::IF_LTZ: is_taken = (src1 < 0); break;
+ case Instruction::IF_GEZ: is_taken = (src1 >= 0); break;
+ case Instruction::IF_GTZ: is_taken = (src1 > 0); break;
+ case Instruction::IF_LEZ: is_taken = (src1 <= 0); break;
+ default:
+ LOG(FATAL) << "Unexpected opcode " << opcode;
+ is_taken = false;
+ }
+ return is_taken;
+}
+
+// Convert relation of src1/src2 to src2/src1
+ConditionCode Mir2Lir::FlipComparisonOrder(ConditionCode before) {
+ ConditionCode res;
+ switch (before) {
+ case kCondEq: res = kCondEq; break;
+ case kCondNe: res = kCondNe; break;
+ case kCondLt: res = kCondGt; break;
+ case kCondGt: res = kCondLt; break;
+ case kCondLe: res = kCondGe; break;
+ case kCondGe: res = kCondLe; break;
+ default:
+ res = static_cast<ConditionCode>(0);
+ LOG(FATAL) << "Unexpected ccode " << before;
+ }
+ return res;
+}
+
+// TODO: move to mir_to_lir.cc
+Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
+ : Backend(arena),
+ literal_list_(NULL),
+ method_literal_list_(NULL),
+ code_literal_list_(NULL),
+ cu_(cu),
+ mir_graph_(mir_graph),
+ switch_tables_(arena, 4, kGrowableArraySwitchTables),
+ fill_array_data_(arena, 4, kGrowableArrayFillArrayData),
+ throw_launchpads_(arena, 2048, kGrowableArrayThrowLaunchPads),
+ suspend_launchpads_(arena, 4, kGrowableArraySuspendLaunchPads),
+ intrinsic_launchpads_(arena, 2048, kGrowableArrayMisc),
+ data_offset_(0),
+ total_size_(0),
+ block_label_list_(NULL),
+ current_dalvik_offset_(0),
+ reg_pool_(NULL),
+ live_sreg_(0),
+ num_core_spills_(0),
+ num_fp_spills_(0),
+ frame_size_(0),
+ core_spill_mask_(0),
+ fp_spill_mask_(0),
+ first_lir_insn_(NULL),
+ last_lir_insn_(NULL)
+ {
+ promotion_map_ = static_cast<PromotionMap*>
+ (arena_->NewMem((cu_->num_dalvik_registers + cu_->num_compiler_temps + 1) *
+ sizeof(promotion_map_[0]), true, ArenaAllocator::kAllocRegAlloc));
+}
+
+void Mir2Lir::Materialize() {
+ CompilerInitializeRegAlloc(); // Needs to happen after SSA naming
+
+ /* Allocate Registers using simple local allocation scheme */
+ SimpleRegAlloc();
+
+ //FIXME: re-enable by retrieving from mir_graph
+ SpecialCaseHandler special_case = kNoHandler;
+
+ if (special_case != kNoHandler) {
+ /*
+ * Custom codegen for special cases. If for any reason the
+ * special codegen doesn't succeed, first_lir_insn_ will
+ * set to NULL;
+ */
+ SpecialMIR2LIR(special_case);
+ }
+
+ /* Convert MIR to LIR, etc. */
+ if (first_lir_insn_ == NULL) {
+ MethodMIR2LIR();
+ }
+
+ /* Method is not empty */
+ if (first_lir_insn_) {
+
+ // mark the targets of switch statement case labels
+ ProcessSwitchTables();
+
+ /* Convert LIR into machine code. */
+ AssembleLIR();
+
+ if (cu_->verbose) {
+ CodegenDump();
+ }
+
+ }
+
+}
+
+CompiledMethod* Mir2Lir::GetCompiledMethod() {
+ // Combine vmap tables - core regs, then fp regs - into vmap_table
+ std::vector<uint16_t> vmap_table;
+ // Core regs may have been inserted out of order - sort first
+ std::sort(core_vmap_table_.begin(), core_vmap_table_.end());
+ for (size_t i = 0 ; i < core_vmap_table_.size(); i++) {
+ // Copy, stripping out the phys register sort key
+ vmap_table.push_back(~(-1 << VREG_NUM_WIDTH) & core_vmap_table_[i]);
+ }
+ // If we have a frame, push a marker to take place of lr
+ if (frame_size_ > 0) {
+ vmap_table.push_back(INVALID_VREG);
+ } else {
+ DCHECK_EQ(__builtin_popcount(core_spill_mask_), 0);
+ DCHECK_EQ(__builtin_popcount(fp_spill_mask_), 0);
+ }
+ // Combine vmap tables - core regs, then fp regs. fp regs already sorted
+ for (uint32_t i = 0; i < fp_vmap_table_.size(); i++) {
+ vmap_table.push_back(fp_vmap_table_[i]);
+ }
+ CompiledMethod* result =
+ new CompiledMethod(cu_->instruction_set, code_buffer_,
+ frame_size_, core_spill_mask_, fp_spill_mask_,
+ combined_mapping_table_, vmap_table, native_gc_map_);
+ return result;
+}
+
+int Mir2Lir::ComputeFrameSize() {
+ /* Figure out the frame size */
+ static const uint32_t kAlignMask = kStackAlignment - 1;
+ uint32_t size = (num_core_spills_ + num_fp_spills_ +
+ 1 /* filler word */ + cu_->num_regs + cu_->num_outs +
+ cu_->num_compiler_temps + 1 /* cur_method* */)
+ * sizeof(uint32_t);
+ /* Align and set */
+ return (size + kAlignMask) & ~(kAlignMask);
+}
+
+/*
+ * Append an LIR instruction to the LIR list maintained by a compilation
+ * unit
+ */
+void Mir2Lir::AppendLIR(LIR* lir)
+{
+ if (first_lir_insn_ == NULL) {
+ DCHECK(last_lir_insn_ == NULL);
+ last_lir_insn_ = first_lir_insn_ = lir;
+ lir->prev = lir->next = NULL;
+ } else {
+ last_lir_insn_->next = lir;
+ lir->prev = last_lir_insn_;
+ lir->next = NULL;
+ last_lir_insn_ = lir;
+ }
+}
+
+/*
+ * Insert an LIR instruction before the current instruction, which cannot be the
+ * first instruction.
+ *
+ * prev_lir <-> new_lir <-> current_lir
+ */
+void Mir2Lir::InsertLIRBefore(LIR* current_lir, LIR* new_lir)
+{
+ DCHECK(current_lir->prev != NULL);
+ LIR *prev_lir = current_lir->prev;
+
+ prev_lir->next = new_lir;
+ new_lir->prev = prev_lir;
+ new_lir->next = current_lir;
+ current_lir->prev = new_lir;
+}
+
+/*
+ * Insert an LIR instruction after the current instruction, which cannot be the
+ * first instruction.
+ *
+ * current_lir -> new_lir -> old_next
+ */
+void Mir2Lir::InsertLIRAfter(LIR* current_lir, LIR* new_lir)
+{
+ new_lir->prev = current_lir;
+ new_lir->next = current_lir->next;
+ current_lir->next = new_lir;
+ new_lir->next->prev = new_lir;
+}
+
+
+} // namespace art
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
new file mode 100644
index 0000000..865b9c5
--- /dev/null
+++ b/compiler/dex/quick/gen_common.cc
@@ -0,0 +1,1800 @@
+/*
+ * Copyright (C) 2012 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 "dex/compiler_ir.h"
+#include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "mirror/array.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+#include "verifier/method_verifier.h"
+
+namespace art {
+
+/*
+ * This source files contains "gen" codegen routines that should
+ * be applicable to most targets. Only mid-level support utilities
+ * and "op" calls may be used here.
+ */
+
+/*
+ * Generate an kPseudoBarrier marker to indicate the boundary of special
+ * blocks.
+ */
+void Mir2Lir::GenBarrier()
+{
+ LIR* barrier = NewLIR0(kPseudoBarrier);
+ /* Mark all resources as being clobbered */
+ barrier->def_mask = -1;
+}
+
+// FIXME: need to do some work to split out targets with
+// condition codes and those without
+LIR* Mir2Lir::GenCheck(ConditionCode c_code, ThrowKind kind)
+{
+ DCHECK_NE(cu_->instruction_set, kMips);
+ LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind, current_dalvik_offset_);
+ LIR* branch = OpCondBranch(c_code, tgt);
+ // Remember branch target - will process later
+ throw_launchpads_.Insert(tgt);
+ return branch;
+}
+
+LIR* Mir2Lir::GenImmedCheck(ConditionCode c_code, int reg, int imm_val, ThrowKind kind)
+{
+ LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind, current_dalvik_offset_, reg, imm_val);
+ LIR* branch;
+ if (c_code == kCondAl) {
+ branch = OpUnconditionalBranch(tgt);
+ } else {
+ branch = OpCmpImmBranch(c_code, reg, imm_val, tgt);
+ }
+ // Remember branch target - will process later
+ throw_launchpads_.Insert(tgt);
+ return branch;
+}
+
+/* Perform null-check on a register. */
+LIR* Mir2Lir::GenNullCheck(int s_reg, int m_reg, int opt_flags)
+{
+ if (!(cu_->disable_opt & (1 << kNullCheckElimination)) &&
+ opt_flags & MIR_IGNORE_NULL_CHECK) {
+ return NULL;
+ }
+ return GenImmedCheck(kCondEq, m_reg, 0, kThrowNullPointer);
+}
+
+/* Perform check on two registers */
+LIR* Mir2Lir::GenRegRegCheck(ConditionCode c_code, int reg1, int reg2,
+ ThrowKind kind)
+{
+ LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind, current_dalvik_offset_, reg1, reg2);
+ LIR* branch = OpCmpBranch(c_code, reg1, reg2, tgt);
+ // Remember branch target - will process later
+ throw_launchpads_.Insert(tgt);
+ return branch;
+}
+
+void Mir2Lir::GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1,
+ RegLocation rl_src2, LIR* taken,
+ LIR* fall_through)
+{
+ ConditionCode cond;
+ switch (opcode) {
+ case Instruction::IF_EQ:
+ cond = kCondEq;
+ break;
+ case Instruction::IF_NE:
+ cond = kCondNe;
+ break;
+ case Instruction::IF_LT:
+ cond = kCondLt;
+ break;
+ case Instruction::IF_GE:
+ cond = kCondGe;
+ break;
+ case Instruction::IF_GT:
+ cond = kCondGt;
+ break;
+ case Instruction::IF_LE:
+ cond = kCondLe;
+ break;
+ default:
+ cond = static_cast<ConditionCode>(0);
+ LOG(FATAL) << "Unexpected opcode " << opcode;
+ }
+
+ // Normalize such that if either operand is constant, src2 will be constant
+ if (rl_src1.is_const) {
+ RegLocation rl_temp = rl_src1;
+ rl_src1 = rl_src2;
+ rl_src2 = rl_temp;
+ cond = FlipComparisonOrder(cond);
+ }
+
+ rl_src1 = LoadValue(rl_src1, kCoreReg);
+ // Is this really an immediate comparison?
+ if (rl_src2.is_const) {
+ // If it's already live in a register or not easily materialized, just keep going
+ RegLocation rl_temp = UpdateLoc(rl_src2);
+ if ((rl_temp.location == kLocDalvikFrame) &&
+ InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src2))) {
+ // OK - convert this to a compare immediate and branch
+ OpCmpImmBranch(cond, rl_src1.low_reg, mir_graph_->ConstantValue(rl_src2), taken);
+ OpUnconditionalBranch(fall_through);
+ return;
+ }
+ }
+ rl_src2 = LoadValue(rl_src2, kCoreReg);
+ OpCmpBranch(cond, rl_src1.low_reg, rl_src2.low_reg, taken);
+ OpUnconditionalBranch(fall_through);
+}
+
+void Mir2Lir::GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src, LIR* taken,
+ LIR* fall_through)
+{
+ ConditionCode cond;
+ rl_src = LoadValue(rl_src, kCoreReg);
+ switch (opcode) {
+ case Instruction::IF_EQZ:
+ cond = kCondEq;
+ break;
+ case Instruction::IF_NEZ:
+ cond = kCondNe;
+ break;
+ case Instruction::IF_LTZ:
+ cond = kCondLt;
+ break;
+ case Instruction::IF_GEZ:
+ cond = kCondGe;
+ break;
+ case Instruction::IF_GTZ:
+ cond = kCondGt;
+ break;
+ case Instruction::IF_LEZ:
+ cond = kCondLe;
+ break;
+ default:
+ cond = static_cast<ConditionCode>(0);
+ LOG(FATAL) << "Unexpected opcode " << opcode;
+ }
+ OpCmpImmBranch(cond, rl_src.low_reg, 0, taken);
+ OpUnconditionalBranch(fall_through);
+}
+
+void Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src)
+{
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (rl_src.location == kLocPhysReg) {
+ OpRegCopy(rl_result.low_reg, rl_src.low_reg);
+ } else {
+ LoadValueDirect(rl_src, rl_result.low_reg);
+ }
+ OpRegRegImm(kOpAsr, rl_result.high_reg, rl_result.low_reg, 31);
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void Mir2Lir::GenIntNarrowing(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src)
+{
+ rl_src = LoadValue(rl_src, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpKind op = kOpInvalid;
+ switch (opcode) {
+ case Instruction::INT_TO_BYTE:
+ op = kOp2Byte;
+ break;
+ case Instruction::INT_TO_SHORT:
+ op = kOp2Short;
+ break;
+ case Instruction::INT_TO_CHAR:
+ op = kOp2Char;
+ break;
+ default:
+ LOG(ERROR) << "Bad int conversion type";
+ }
+ OpRegReg(op, rl_result.low_reg, rl_src.low_reg);
+ StoreValue(rl_dest, rl_result);
+}
+
+/*
+ * Let helper function take care of everything. Will call
+ * Array::AllocFromCode(type_idx, method, count);
+ * Note: AllocFromCode will handle checks for errNegativeArraySize.
+ */
+void Mir2Lir::GenNewArray(uint32_t type_idx, RegLocation rl_dest,
+ RegLocation rl_src)
+{
+ FlushAllRegs(); /* Everything to home location */
+ int func_offset;
+ if (cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx, *cu_->dex_file,
+ type_idx)) {
+ func_offset = ENTRYPOINT_OFFSET(pAllocArrayFromCode);
+ } else {
+ func_offset= ENTRYPOINT_OFFSET(pAllocArrayFromCodeWithAccessCheck);
+ }
+ CallRuntimeHelperImmMethodRegLocation(func_offset, type_idx, rl_src, true);
+ RegLocation rl_result = GetReturn(false);
+ StoreValue(rl_dest, rl_result);
+}
+
+/*
+ * Similar to GenNewArray, but with post-allocation initialization.
+ * Verifier guarantees we're dealing with an array class. Current
+ * code throws runtime exception "bad Filled array req" for 'D' and 'J'.
+ * Current code also throws internal unimp if not 'L', '[' or 'I'.
+ */
+void Mir2Lir::GenFilledNewArray(CallInfo* info)
+{
+ int elems = info->num_arg_words;
+ int type_idx = info->index;
+ FlushAllRegs(); /* Everything to home location */
+ int func_offset;
+ if (cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx, *cu_->dex_file,
+ type_idx)) {
+ func_offset = ENTRYPOINT_OFFSET(pCheckAndAllocArrayFromCode);
+ } else {
+ func_offset = ENTRYPOINT_OFFSET(pCheckAndAllocArrayFromCodeWithAccessCheck);
+ }
+ CallRuntimeHelperImmMethodImm(func_offset, type_idx, elems, true);
+ FreeTemp(TargetReg(kArg2));
+ FreeTemp(TargetReg(kArg1));
+ /*
+ * NOTE: the implicit target for Instruction::FILLED_NEW_ARRAY is the
+ * return region. Because AllocFromCode placed the new array
+ * in kRet0, we'll just lock it into place. When debugger support is
+ * added, it may be necessary to additionally copy all return
+ * values to a home location in thread-local storage
+ */
+ LockTemp(TargetReg(kRet0));
+
+ // TODO: use the correct component size, currently all supported types
+ // share array alignment with ints (see comment at head of function)
+ size_t component_size = sizeof(int32_t);
+
+ // Having a range of 0 is legal
+ if (info->is_range && (elems > 0)) {
+ /*
+ * Bit of ugliness here. We're going generate a mem copy loop
+ * on the register range, but it is possible that some regs
+ * in the range have been promoted. This is unlikely, but
+ * before generating the copy, we'll just force a flush
+ * of any regs in the source range that have been promoted to
+ * home location.
+ */
+ for (int i = 0; i < elems; i++) {
+ RegLocation loc = UpdateLoc(info->args[i]);
+ if (loc.location == kLocPhysReg) {
+ StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low),
+ loc.low_reg, kWord);
+ }
+ }
+ /*
+ * TUNING note: generated code here could be much improved, but
+ * this is an uncommon operation and isn't especially performance
+ * critical.
+ */
+ int r_src = AllocTemp();
+ int r_dst = AllocTemp();
+ int r_idx = AllocTemp();
+ int r_val = INVALID_REG;
+ switch(cu_->instruction_set) {
+ case kThumb2:
+ r_val = TargetReg(kLr);
+ break;
+ case kX86:
+ FreeTemp(TargetReg(kRet0));
+ r_val = AllocTemp();
+ break;
+ case kMips:
+ r_val = AllocTemp();
+ break;
+ default: LOG(FATAL) << "Unexpected instruction set: " << cu_->instruction_set;
+ }
+ // Set up source pointer
+ RegLocation rl_first = info->args[0];
+ OpRegRegImm(kOpAdd, r_src, TargetReg(kSp), SRegOffset(rl_first.s_reg_low));
+ // Set up the target pointer
+ OpRegRegImm(kOpAdd, r_dst, TargetReg(kRet0),
+ mirror::Array::DataOffset(component_size).Int32Value());
+ // Set up the loop counter (known to be > 0)
+ LoadConstant(r_idx, elems - 1);
+ // Generate the copy loop. Going backwards for convenience
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ // Copy next element
+ LoadBaseIndexed(r_src, r_idx, r_val, 2, kWord);
+ StoreBaseIndexed(r_dst, r_idx, r_val, 2, kWord);
+ FreeTemp(r_val);
+ OpDecAndBranch(kCondGe, r_idx, target);
+ if (cu_->instruction_set == kX86) {
+ // Restore the target pointer
+ OpRegRegImm(kOpAdd, TargetReg(kRet0), r_dst,
+ -mirror::Array::DataOffset(component_size).Int32Value());
+ }
+ } else if (!info->is_range) {
+ // TUNING: interleave
+ for (int i = 0; i < elems; i++) {
+ RegLocation rl_arg = LoadValue(info->args[i], kCoreReg);
+ StoreBaseDisp(TargetReg(kRet0),
+ mirror::Array::DataOffset(component_size).Int32Value() +
+ i * 4, rl_arg.low_reg, kWord);
+ // If the LoadValue caused a temp to be allocated, free it
+ if (IsTemp(rl_arg.low_reg)) {
+ FreeTemp(rl_arg.low_reg);
+ }
+ }
+ }
+ if (info->result.location != kLocInvalid) {
+ StoreValue(info->result, GetReturn(false /* not fp */));
+ }
+}
+
+void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_double,
+ bool is_object)
+{
+ int field_offset;
+ int ssb_index;
+ bool is_volatile;
+ bool is_referrers_class;
+ bool fast_path = cu_->compiler_driver->ComputeStaticFieldInfo(
+ field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, ssb_index,
+ is_referrers_class, is_volatile, true);
+ if (fast_path && !SLOW_FIELD_PATH) {
+ DCHECK_GE(field_offset, 0);
+ int rBase;
+ if (is_referrers_class) {
+ // Fast path, static storage base is this method's class
+ RegLocation rl_method = LoadCurrMethod();
+ rBase = AllocTemp();
+ LoadWordDisp(rl_method.low_reg,
+ mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), rBase);
+ if (IsTemp(rl_method.low_reg)) {
+ FreeTemp(rl_method.low_reg);
+ }
+ } else {
+ // Medium path, static storage base in a different class which requires checks that the other
+ // class is initialized.
+ // TODO: remove initialized check now that we are initializing classes in the compiler driver.
+ DCHECK_GE(ssb_index, 0);
+ // May do runtime call so everything to home locations.
+ FlushAllRegs();
+ // Using fixed register to sync with possible call to runtime support.
+ int r_method = TargetReg(kArg1);
+ LockTemp(r_method);
+ LoadCurrMethodDirect(r_method);
+ rBase = TargetReg(kArg0);
+ LockTemp(rBase);
+ LoadWordDisp(r_method,
+ mirror::AbstractMethod::DexCacheInitializedStaticStorageOffset().Int32Value(),
+ rBase);
+ LoadWordDisp(rBase,
+ mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
+ sizeof(int32_t*) * ssb_index, rBase);
+ // rBase now points at appropriate static storage base (Class*)
+ // or NULL if not initialized. Check for NULL and call helper if NULL.
+ // TUNING: fast path should fall through
+ LIR* branch_over = OpCmpImmBranch(kCondNe, rBase, 0, NULL);
+ LoadConstant(TargetReg(kArg0), ssb_index);
+ CallRuntimeHelperImm(ENTRYPOINT_OFFSET(pInitializeStaticStorage), ssb_index, true);
+ if (cu_->instruction_set == kMips) {
+ // For Arm, kRet0 = kArg0 = rBase, for Mips, we need to copy
+ OpRegCopy(rBase, TargetReg(kRet0));
+ }
+ LIR* skip_target = NewLIR0(kPseudoTargetLabel);
+ branch_over->target = skip_target;
+ FreeTemp(r_method);
+ }
+ // rBase now holds static storage base
+ if (is_long_or_double) {
+ rl_src = LoadValueWide(rl_src, kAnyReg);
+ } else {
+ rl_src = LoadValue(rl_src, kAnyReg);
+ }
+ if (is_volatile) {
+ GenMemBarrier(kStoreStore);
+ }
+ if (is_long_or_double) {
+ StoreBaseDispWide(rBase, field_offset, rl_src.low_reg,
+ rl_src.high_reg);
+ } else {
+ StoreWordDisp(rBase, field_offset, rl_src.low_reg);
+ }
+ if (is_volatile) {
+ GenMemBarrier(kStoreLoad);
+ }
+ if (is_object && !mir_graph_->IsConstantNullRef(rl_src)) {
+ MarkGCCard(rl_src.low_reg, rBase);
+ }
+ FreeTemp(rBase);
+ } else {
+ FlushAllRegs(); // Everything to home locations
+ int setter_offset = is_long_or_double ? ENTRYPOINT_OFFSET(pSet64Static) :
+ (is_object ? ENTRYPOINT_OFFSET(pSetObjStatic)
+ : ENTRYPOINT_OFFSET(pSet32Static));
+ CallRuntimeHelperImmRegLocation(setter_offset, field_idx, rl_src, true);
+ }
+}
+
+void Mir2Lir::GenSget(uint32_t field_idx, RegLocation rl_dest,
+ bool is_long_or_double, bool is_object)
+{
+ int field_offset;
+ int ssb_index;
+ bool is_volatile;
+ bool is_referrers_class;
+ bool fast_path = cu_->compiler_driver->ComputeStaticFieldInfo(
+ field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, ssb_index,
+ is_referrers_class, is_volatile, false);
+ if (fast_path && !SLOW_FIELD_PATH) {
+ DCHECK_GE(field_offset, 0);
+ int rBase;
+ if (is_referrers_class) {
+ // Fast path, static storage base is this method's class
+ RegLocation rl_method = LoadCurrMethod();
+ rBase = AllocTemp();
+ LoadWordDisp(rl_method.low_reg,
+ mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), rBase);
+ } else {
+ // Medium path, static storage base in a different class which requires checks that the other
+ // class is initialized
+ // TODO: remove initialized check now that we are initializing classes in the compiler driver.
+ DCHECK_GE(ssb_index, 0);
+ // May do runtime call so everything to home locations.
+ FlushAllRegs();
+ // Using fixed register to sync with possible call to runtime support.
+ int r_method = TargetReg(kArg1);
+ LockTemp(r_method);
+ LoadCurrMethodDirect(r_method);
+ rBase = TargetReg(kArg0);
+ LockTemp(rBase);
+ LoadWordDisp(r_method,
+ mirror::AbstractMethod::DexCacheInitializedStaticStorageOffset().Int32Value(),
+ rBase);
+ LoadWordDisp(rBase, mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
+ sizeof(int32_t*) * ssb_index, rBase);
+ // rBase now points at appropriate static storage base (Class*)
+ // or NULL if not initialized. Check for NULL and call helper if NULL.
+ // TUNING: fast path should fall through
+ LIR* branch_over = OpCmpImmBranch(kCondNe, rBase, 0, NULL);
+ CallRuntimeHelperImm(ENTRYPOINT_OFFSET(pInitializeStaticStorage), ssb_index, true);
+ if (cu_->instruction_set == kMips) {
+ // For Arm, kRet0 = kArg0 = rBase, for Mips, we need to copy
+ OpRegCopy(rBase, TargetReg(kRet0));
+ }
+ LIR* skip_target = NewLIR0(kPseudoTargetLabel);
+ branch_over->target = skip_target;
+ FreeTemp(r_method);
+ }
+ // rBase now holds static storage base
+ RegLocation rl_result = EvalLoc(rl_dest, kAnyReg, true);
+ if (is_volatile) {
+ GenMemBarrier(kLoadLoad);
+ }
+ if (is_long_or_double) {
+ LoadBaseDispWide(rBase, field_offset, rl_result.low_reg,
+ rl_result.high_reg, INVALID_SREG);
+ } else {
+ LoadWordDisp(rBase, field_offset, rl_result.low_reg);
+ }
+ FreeTemp(rBase);
+ if (is_long_or_double) {
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ StoreValue(rl_dest, rl_result);
+ }
+ } else {
+ FlushAllRegs(); // Everything to home locations
+ int getterOffset = is_long_or_double ? ENTRYPOINT_OFFSET(pGet64Static) :
+ (is_object ? ENTRYPOINT_OFFSET(pGetObjStatic)
+ : ENTRYPOINT_OFFSET(pGet32Static));
+ CallRuntimeHelperImm(getterOffset, field_idx, true);
+ if (is_long_or_double) {
+ RegLocation rl_result = GetReturnWide(rl_dest.fp);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ RegLocation rl_result = GetReturn(rl_dest.fp);
+ StoreValue(rl_dest, rl_result);
+ }
+ }
+}
+
+void Mir2Lir::HandleSuspendLaunchPads()
+{
+ int num_elems = suspend_launchpads_.Size();
+ int helper_offset = ENTRYPOINT_OFFSET(pTestSuspendFromCode);
+ for (int i = 0; i < num_elems; i++) {
+ ResetRegPool();
+ ResetDefTracking();
+ LIR* lab = suspend_launchpads_.Get(i);
+ LIR* resume_lab = reinterpret_cast<LIR*>(lab->operands[0]);
+ current_dalvik_offset_ = lab->operands[1];
+ AppendLIR(lab);
+ int r_tgt = CallHelperSetup(helper_offset);
+ CallHelper(r_tgt, helper_offset, true /* MarkSafepointPC */);
+ OpUnconditionalBranch(resume_lab);
+ }
+}
+
+void Mir2Lir::HandleIntrinsicLaunchPads()
+{
+ int num_elems = intrinsic_launchpads_.Size();
+ for (int i = 0; i < num_elems; i++) {
+ ResetRegPool();
+ ResetDefTracking();
+ LIR* lab = intrinsic_launchpads_.Get(i);
+ CallInfo* info = reinterpret_cast<CallInfo*>(lab->operands[0]);
+ current_dalvik_offset_ = info->offset;
+ AppendLIR(lab);
+ // NOTE: GenInvoke handles MarkSafepointPC
+ GenInvoke(info);
+ LIR* resume_lab = reinterpret_cast<LIR*>(lab->operands[2]);
+ if (resume_lab != NULL) {
+ OpUnconditionalBranch(resume_lab);
+ }
+ }
+}
+
+void Mir2Lir::HandleThrowLaunchPads()
+{
+ int num_elems = throw_launchpads_.Size();
+ for (int i = 0; i < num_elems; i++) {
+ ResetRegPool();
+ ResetDefTracking();
+ LIR* lab = throw_launchpads_.Get(i);
+ current_dalvik_offset_ = lab->operands[1];
+ AppendLIR(lab);
+ int func_offset = 0;
+ int v1 = lab->operands[2];
+ int v2 = lab->operands[3];
+ bool target_x86 = (cu_->instruction_set == kX86);
+ switch (lab->operands[0]) {
+ case kThrowNullPointer:
+ func_offset = ENTRYPOINT_OFFSET(pThrowNullPointerFromCode);
+ break;
+ case kThrowConstantArrayBounds: // v1 is length reg (for Arm/Mips), v2 constant index
+ // v1 holds the constant array index. Mips/Arm uses v2 for length, x86 reloads.
+ if (target_x86) {
+ OpRegMem(kOpMov, TargetReg(kArg1), v1, mirror::Array::LengthOffset().Int32Value());
+ } else {
+ OpRegCopy(TargetReg(kArg1), v1);
+ }
+ // Make sure the following LoadConstant doesn't mess with kArg1.
+ LockTemp(TargetReg(kArg1));
+ LoadConstant(TargetReg(kArg0), v2);
+ func_offset = ENTRYPOINT_OFFSET(pThrowArrayBoundsFromCode);
+ break;
+ case kThrowArrayBounds:
+ // Move v1 (array index) to kArg0 and v2 (array length) to kArg1
+ if (v2 != TargetReg(kArg0)) {
+ OpRegCopy(TargetReg(kArg0), v1);
+ if (target_x86) {
+ // x86 leaves the array pointer in v2, so load the array length that the handler expects
+ OpRegMem(kOpMov, TargetReg(kArg1), v2, mirror::Array::LengthOffset().Int32Value());
+ } else {
+ OpRegCopy(TargetReg(kArg1), v2);
+ }
+ } else {
+ if (v1 == TargetReg(kArg1)) {
+ // Swap v1 and v2, using kArg2 as a temp
+ OpRegCopy(TargetReg(kArg2), v1);
+ if (target_x86) {
+ // x86 leaves the array pointer in v2; load the array length that the handler expects
+ OpRegMem(kOpMov, TargetReg(kArg1), v2, mirror::Array::LengthOffset().Int32Value());
+ } else {
+ OpRegCopy(TargetReg(kArg1), v2);
+ }
+ OpRegCopy(TargetReg(kArg0), TargetReg(kArg2));
+ } else {
+ if (target_x86) {
+ // x86 leaves the array pointer in v2; load the array length that the handler expects
+ OpRegMem(kOpMov, TargetReg(kArg1), v2, mirror::Array::LengthOffset().Int32Value());
+ } else {
+ OpRegCopy(TargetReg(kArg1), v2);
+ }
+ OpRegCopy(TargetReg(kArg0), v1);
+ }
+ }
+ func_offset = ENTRYPOINT_OFFSET(pThrowArrayBoundsFromCode);
+ break;
+ case kThrowDivZero:
+ func_offset = ENTRYPOINT_OFFSET(pThrowDivZeroFromCode);
+ break;
+ case kThrowNoSuchMethod:
+ OpRegCopy(TargetReg(kArg0), v1);
+ func_offset =
+ ENTRYPOINT_OFFSET(pThrowNoSuchMethodFromCode);
+ break;
+ case kThrowStackOverflow:
+ func_offset = ENTRYPOINT_OFFSET(pThrowStackOverflowFromCode);
+ // Restore stack alignment
+ if (target_x86) {
+ OpRegImm(kOpAdd, TargetReg(kSp), frame_size_);
+ } else {
+ OpRegImm(kOpAdd, TargetReg(kSp), (num_core_spills_ + num_fp_spills_) * 4);
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected throw kind: " << lab->operands[0];
+ }
+ ClobberCalleeSave();
+ int r_tgt = CallHelperSetup(func_offset);
+ CallHelper(r_tgt, func_offset, true /* MarkSafepointPC */);
+ }
+}
+
+void Mir2Lir::GenIGet(uint32_t field_idx, int opt_flags, OpSize size,
+ RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double,
+ bool is_object)
+{
+ int field_offset;
+ bool is_volatile;
+
+ bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false);
+
+ if (fast_path && !SLOW_FIELD_PATH) {
+ RegLocation rl_result;
+ RegisterClass reg_class = oat_reg_class_by_size(size);
+ DCHECK_GE(field_offset, 0);
+ rl_obj = LoadValue(rl_obj, kCoreReg);
+ if (is_long_or_double) {
+ DCHECK(rl_dest.wide);
+ GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, opt_flags);
+ if (cu_->instruction_set == kX86) {
+ rl_result = EvalLoc(rl_dest, reg_class, true);
+ GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, opt_flags);
+ LoadBaseDispWide(rl_obj.low_reg, field_offset, rl_result.low_reg,
+ rl_result.high_reg, rl_obj.s_reg_low);
+ if (is_volatile) {
+ GenMemBarrier(kLoadLoad);
+ }
+ } else {
+ int reg_ptr = AllocTemp();
+ OpRegRegImm(kOpAdd, reg_ptr, rl_obj.low_reg, field_offset);
+ rl_result = EvalLoc(rl_dest, reg_class, true);
+ LoadBaseDispWide(reg_ptr, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG);
+ if (is_volatile) {
+ GenMemBarrier(kLoadLoad);
+ }
+ FreeTemp(reg_ptr);
+ }
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ rl_result = EvalLoc(rl_dest, reg_class, true);
+ GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, opt_flags);
+ LoadBaseDisp(rl_obj.low_reg, field_offset, rl_result.low_reg,
+ kWord, rl_obj.s_reg_low);
+ if (is_volatile) {
+ GenMemBarrier(kLoadLoad);
+ }
+ StoreValue(rl_dest, rl_result);
+ }
+ } else {
+ int getterOffset = is_long_or_double ? ENTRYPOINT_OFFSET(pGet64Instance) :
+ (is_object ? ENTRYPOINT_OFFSET(pGetObjInstance)
+ : ENTRYPOINT_OFFSET(pGet32Instance));
+ CallRuntimeHelperImmRegLocation(getterOffset, field_idx, rl_obj, true);
+ if (is_long_or_double) {
+ RegLocation rl_result = GetReturnWide(rl_dest.fp);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ RegLocation rl_result = GetReturn(rl_dest.fp);
+ StoreValue(rl_dest, rl_result);
+ }
+ }
+}
+
+void Mir2Lir::GenIPut(uint32_t field_idx, int opt_flags, OpSize size,
+ RegLocation rl_src, RegLocation rl_obj, bool is_long_or_double,
+ bool is_object)
+{
+ int field_offset;
+ bool is_volatile;
+
+ bool fast_path = FastInstance(field_idx, field_offset, is_volatile,
+ true);
+ if (fast_path && !SLOW_FIELD_PATH) {
+ RegisterClass reg_class = oat_reg_class_by_size(size);
+ DCHECK_GE(field_offset, 0);
+ rl_obj = LoadValue(rl_obj, kCoreReg);
+ if (is_long_or_double) {
+ int reg_ptr;
+ rl_src = LoadValueWide(rl_src, kAnyReg);
+ GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, opt_flags);
+ reg_ptr = AllocTemp();
+ OpRegRegImm(kOpAdd, reg_ptr, rl_obj.low_reg, field_offset);
+ if (is_volatile) {
+ GenMemBarrier(kStoreStore);
+ }
+ StoreBaseDispWide(reg_ptr, 0, rl_src.low_reg, rl_src.high_reg);
+ if (is_volatile) {
+ GenMemBarrier(kLoadLoad);
+ }
+ FreeTemp(reg_ptr);
+ } else {
+ rl_src = LoadValue(rl_src, reg_class);
+ GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, opt_flags);
+ if (is_volatile) {
+ GenMemBarrier(kStoreStore);
+ }
+ StoreBaseDisp(rl_obj.low_reg, field_offset, rl_src.low_reg, kWord);
+ if (is_volatile) {
+ GenMemBarrier(kLoadLoad);
+ }
+ if (is_object && !mir_graph_->IsConstantNullRef(rl_src)) {
+ MarkGCCard(rl_src.low_reg, rl_obj.low_reg);
+ }
+ }
+ } else {
+ int setter_offset = is_long_or_double ? ENTRYPOINT_OFFSET(pSet64Instance) :
+ (is_object ? ENTRYPOINT_OFFSET(pSetObjInstance)
+ : ENTRYPOINT_OFFSET(pSet32Instance));
+ CallRuntimeHelperImmRegLocationRegLocation(setter_offset, field_idx, rl_obj, rl_src, true);
+ }
+}
+
+void Mir2Lir::GenConstClass(uint32_t type_idx, RegLocation rl_dest)
+{
+ RegLocation rl_method = LoadCurrMethod();
+ int res_reg = AllocTemp();
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (!cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx,
+ *cu_->dex_file,
+ type_idx)) {
+ // Call out to helper which resolves type and verifies access.
+ // Resolved type returned in kRet0.
+ CallRuntimeHelperImmReg(ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode),
+ type_idx, rl_method.low_reg, true);
+ RegLocation rl_result = GetReturn(false);
+ StoreValue(rl_dest, rl_result);
+ } else {
+ // We're don't need access checks, load type from dex cache
+ int32_t dex_cache_offset =
+ mirror::AbstractMethod::DexCacheResolvedTypesOffset().Int32Value();
+ LoadWordDisp(rl_method.low_reg, dex_cache_offset, res_reg);
+ int32_t offset_of_type =
+ mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*)
+ * type_idx);
+ LoadWordDisp(res_reg, offset_of_type, rl_result.low_reg);
+ if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file,
+ type_idx) || SLOW_TYPE_PATH) {
+ // Slow path, at runtime test if type is null and if so initialize
+ FlushAllRegs();
+ LIR* branch1 = OpCmpImmBranch(kCondEq, rl_result.low_reg, 0, NULL);
+ // Resolved, store and hop over following code
+ StoreValue(rl_dest, rl_result);
+ /*
+ * Because we have stores of the target value on two paths,
+ * clobber temp tracking for the destination using the ssa name
+ */
+ ClobberSReg(rl_dest.s_reg_low);
+ LIR* branch2 = OpUnconditionalBranch(0);
+ // TUNING: move slow path to end & remove unconditional branch
+ LIR* target1 = NewLIR0(kPseudoTargetLabel);
+ // Call out to helper, which will return resolved type in kArg0
+ CallRuntimeHelperImmReg(ENTRYPOINT_OFFSET(pInitializeTypeFromCode), type_idx,
+ rl_method.low_reg, true);
+ RegLocation rl_result = GetReturn(false);
+ StoreValue(rl_dest, rl_result);
+ /*
+ * Because we have stores of the target value on two paths,
+ * clobber temp tracking for the destination using the ssa name
+ */
+ ClobberSReg(rl_dest.s_reg_low);
+ // Rejoin code paths
+ LIR* target2 = NewLIR0(kPseudoTargetLabel);
+ branch1->target = target1;
+ branch2->target = target2;
+ } else {
+ // Fast path, we're done - just store result
+ StoreValue(rl_dest, rl_result);
+ }
+ }
+}
+
+void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest)
+{
+ /* NOTE: Most strings should be available at compile time */
+ int32_t offset_of_string = mirror::Array::DataOffset(sizeof(mirror::String*)).Int32Value() +
+ (sizeof(mirror::String*) * string_idx);
+ if (!cu_->compiler_driver->CanAssumeStringIsPresentInDexCache(
+ *cu_->dex_file, string_idx) || SLOW_STRING_PATH) {
+ // slow path, resolve string if not in dex cache
+ FlushAllRegs();
+ LockCallTemps(); // Using explicit registers
+ LoadCurrMethodDirect(TargetReg(kArg2));
+ LoadWordDisp(TargetReg(kArg2),
+ mirror::AbstractMethod::DexCacheStringsOffset().Int32Value(), TargetReg(kArg0));
+ // Might call out to helper, which will return resolved string in kRet0
+ int r_tgt = CallHelperSetup(ENTRYPOINT_OFFSET(pResolveStringFromCode));
+ LoadWordDisp(TargetReg(kArg0), offset_of_string, TargetReg(kRet0));
+ LoadConstant(TargetReg(kArg1), string_idx);
+ if (cu_->instruction_set == kThumb2) {
+ OpRegImm(kOpCmp, TargetReg(kRet0), 0); // Is resolved?
+ GenBarrier();
+ // For testing, always force through helper
+ if (!EXERCISE_SLOWEST_STRING_PATH) {
+ OpIT(kCondEq, "T");
+ }
+ OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); // .eq
+ LIR* call_inst = OpReg(kOpBlx, r_tgt); // .eq, helper(Method*, string_idx)
+ MarkSafepointPC(call_inst);
+ FreeTemp(r_tgt);
+ } else if (cu_->instruction_set == kMips) {
+ LIR* branch = OpCmpImmBranch(kCondNe, TargetReg(kRet0), 0, NULL);
+ OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); // .eq
+ LIR* call_inst = OpReg(kOpBlx, r_tgt);
+ MarkSafepointPC(call_inst);
+ FreeTemp(r_tgt);
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ branch->target = target;
+ } else {
+ DCHECK_EQ(cu_->instruction_set, kX86);
+ CallRuntimeHelperRegReg(ENTRYPOINT_OFFSET(pResolveStringFromCode), TargetReg(kArg2), TargetReg(kArg1), true);
+ }
+ GenBarrier();
+ StoreValue(rl_dest, GetReturn(false));
+ } else {
+ RegLocation rl_method = LoadCurrMethod();
+ int res_reg = AllocTemp();
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ LoadWordDisp(rl_method.low_reg,
+ mirror::AbstractMethod::DexCacheStringsOffset().Int32Value(), res_reg);
+ LoadWordDisp(res_reg, offset_of_string, rl_result.low_reg);
+ StoreValue(rl_dest, rl_result);
+ }
+}
+
+/*
+ * Let helper function take care of everything. Will
+ * call Class::NewInstanceFromCode(type_idx, method);
+ */
+void Mir2Lir::GenNewInstance(uint32_t type_idx, RegLocation rl_dest)
+{
+ FlushAllRegs(); /* Everything to home location */
+ // alloc will always check for resolution, do we also need to verify
+ // access because the verifier was unable to?
+ int func_offset;
+ if (cu_->compiler_driver->CanAccessInstantiableTypeWithoutChecks(
+ cu_->method_idx, *cu_->dex_file, type_idx)) {
+ func_offset = ENTRYPOINT_OFFSET(pAllocObjectFromCode);
+ } else {
+ func_offset = ENTRYPOINT_OFFSET(pAllocObjectFromCodeWithAccessCheck);
+ }
+ CallRuntimeHelperImmMethod(func_offset, type_idx, true);
+ RegLocation rl_result = GetReturn(false);
+ StoreValue(rl_dest, rl_result);
+}
+
+void Mir2Lir::GenThrow(RegLocation rl_src)
+{
+ FlushAllRegs();
+ CallRuntimeHelperRegLocation(ENTRYPOINT_OFFSET(pDeliverException), rl_src, true);
+}
+
+// For final classes there are no sub-classes to check and so we can answer the instance-of
+// question with simple comparisons.
+void Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, RegLocation rl_dest,
+ RegLocation rl_src) {
+ RegLocation object = LoadValue(rl_src, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int result_reg = rl_result.low_reg;
+ if (result_reg == object.low_reg) {
+ result_reg = AllocTypedTemp(false, kCoreReg);
+ }
+ LoadConstant(result_reg, 0); // assume false
+ LIR* null_branchover = OpCmpImmBranch(kCondEq, object.low_reg, 0, NULL);
+
+ int check_class = AllocTypedTemp(false, kCoreReg);
+ int object_class = AllocTypedTemp(false, kCoreReg);
+
+ LoadCurrMethodDirect(check_class);
+ if (use_declaring_class) {
+ LoadWordDisp(check_class, mirror::AbstractMethod::DeclaringClassOffset().Int32Value(),
+ check_class);
+ LoadWordDisp(object.low_reg, mirror::Object::ClassOffset().Int32Value(), object_class);
+ } else {
+ LoadWordDisp(check_class, mirror::AbstractMethod::DexCacheResolvedTypesOffset().Int32Value(),
+ check_class);
+ LoadWordDisp(object.low_reg, mirror::Object::ClassOffset().Int32Value(), object_class);
+ int32_t offset_of_type =
+ mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() +
+ (sizeof(mirror::Class*) * type_idx);
+ LoadWordDisp(check_class, offset_of_type, check_class);
+ }
+
+ LIR* ne_branchover = NULL;
+ if (cu_->instruction_set == kThumb2) {
+ OpRegReg(kOpCmp, check_class, object_class); // Same?
+ OpIT(kCondEq, ""); // if-convert the test
+ LoadConstant(result_reg, 1); // .eq case - load true
+ } else {
+ ne_branchover = OpCmpBranch(kCondNe, check_class, object_class, NULL);
+ LoadConstant(result_reg, 1); // eq case - load true
+ }
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ null_branchover->target = target;
+ if (ne_branchover != NULL) {
+ ne_branchover->target = target;
+ }
+ FreeTemp(object_class);
+ FreeTemp(check_class);
+ if (IsTemp(result_reg)) {
+ OpRegCopy(rl_result.low_reg, result_reg);
+ FreeTemp(result_reg);
+ }
+ StoreValue(rl_dest, rl_result);
+}
+
+void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_known_final,
+ bool type_known_abstract, bool use_declaring_class,
+ bool can_assume_type_is_in_dex_cache,
+ uint32_t type_idx, RegLocation rl_dest,
+ RegLocation rl_src) {
+ FlushAllRegs();
+ // May generate a call - use explicit registers
+ LockCallTemps();
+ LoadCurrMethodDirect(TargetReg(kArg1)); // kArg1 <= current Method*
+ int class_reg = TargetReg(kArg2); // kArg2 will hold the Class*
+ if (needs_access_check) {
+ // Check we have access to type_idx and if not throw IllegalAccessError,
+ // returns Class* in kArg0
+ CallRuntimeHelperImm(ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode),
+ type_idx, true);
+ OpRegCopy(class_reg, TargetReg(kRet0)); // Align usage with fast path
+ LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref
+ } else if (use_declaring_class) {
+ LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref
+ LoadWordDisp(TargetReg(kArg1),
+ mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), class_reg);
+ } else {
+ // Load dex cache entry into class_reg (kArg2)
+ LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref
+ LoadWordDisp(TargetReg(kArg1),
+ mirror::AbstractMethod::DexCacheResolvedTypesOffset().Int32Value(), class_reg);
+ int32_t offset_of_type =
+ mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*)
+ * type_idx);
+ LoadWordDisp(class_reg, offset_of_type, class_reg);
+ if (!can_assume_type_is_in_dex_cache) {
+ // Need to test presence of type in dex cache at runtime
+ LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL);
+ // Not resolved
+ // Call out to helper, which will return resolved type in kRet0
+ CallRuntimeHelperImm(ENTRYPOINT_OFFSET(pInitializeTypeFromCode), type_idx, true);
+ OpRegCopy(TargetReg(kArg2), TargetReg(kRet0)); // Align usage with fast path
+ LoadValueDirectFixed(rl_src, TargetReg(kArg0)); /* reload Ref */
+ // Rejoin code paths
+ LIR* hop_target = NewLIR0(kPseudoTargetLabel);
+ hop_branch->target = hop_target;
+ }
+ }
+ /* kArg0 is ref, kArg2 is class. If ref==null, use directly as bool result */
+ RegLocation rl_result = GetReturn(false);
+ if (cu_->instruction_set == kMips) {
+ // On MIPS rArg0 != rl_result, place false in result if branch is taken.
+ LoadConstant(rl_result.low_reg, 0);
+ }
+ LIR* branch1 = OpCmpImmBranch(kCondEq, TargetReg(kArg0), 0, NULL);
+
+ /* load object->klass_ */
+ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0);
+ LoadWordDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1));
+ /* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class */
+ LIR* branchover = NULL;
+ if (type_known_final) {
+ // rl_result == ref == null == 0.
+ if (cu_->instruction_set == kThumb2) {
+ OpRegReg(kOpCmp, TargetReg(kArg1), TargetReg(kArg2)); // Same?
+ OpIT(kCondEq, "E"); // if-convert the test
+ LoadConstant(rl_result.low_reg, 1); // .eq case - load true
+ LoadConstant(rl_result.low_reg, 0); // .ne case - load false
+ } else {
+ LoadConstant(rl_result.low_reg, 0); // ne case - load false
+ branchover = OpCmpBranch(kCondNe, TargetReg(kArg1), TargetReg(kArg2), NULL);
+ LoadConstant(rl_result.low_reg, 1); // eq case - load true
+ }
+ } else {
+ if (cu_->instruction_set == kThumb2) {
+ int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode));
+ if (!type_known_abstract) {
+ /* Uses conditional nullification */
+ OpRegReg(kOpCmp, TargetReg(kArg1), TargetReg(kArg2)); // Same?
+ OpIT(kCondEq, "EE"); // if-convert the test
+ LoadConstant(TargetReg(kArg0), 1); // .eq case - load true
+ }
+ OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); // .ne case - arg0 <= class
+ OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class)
+ FreeTemp(r_tgt);
+ } else {
+ if (!type_known_abstract) {
+ /* Uses branchovers */
+ LoadConstant(rl_result.low_reg, 1); // assume true
+ branchover = OpCmpBranch(kCondEq, TargetReg(kArg1), TargetReg(kArg2), NULL);
+ }
+ if (cu_->instruction_set != kX86) {
+ int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode));
+ OpRegCopy(TargetReg(kArg0), TargetReg(kArg2)); // .ne case - arg0 <= class
+ OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class)
+ FreeTemp(r_tgt);
+ } else {
+ OpRegCopy(TargetReg(kArg0), TargetReg(kArg2));
+ OpThreadMem(kOpBlx, ENTRYPOINT_OFFSET(pInstanceofNonTrivialFromCode));
+ }
+ }
+ }
+ // TODO: only clobber when type isn't final?
+ ClobberCalleeSave();
+ /* branch targets here */
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ StoreValue(rl_dest, rl_result);
+ branch1->target = target;
+ if (branchover != NULL) {
+ branchover->target = target;
+ }
+}
+
+void Mir2Lir::GenInstanceof(uint32_t type_idx, RegLocation rl_dest, RegLocation rl_src) {
+ bool type_known_final, type_known_abstract, use_declaring_class;
+ bool needs_access_check = !cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx,
+ *cu_->dex_file,
+ type_idx,
+ &type_known_final,
+ &type_known_abstract,
+ &use_declaring_class);
+ bool can_assume_type_is_in_dex_cache = !needs_access_check &&
+ cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx);
+
+ if ((use_declaring_class || can_assume_type_is_in_dex_cache) && type_known_final) {
+ GenInstanceofFinal(use_declaring_class, type_idx, rl_dest, rl_src);
+ } else {
+ GenInstanceofCallingHelper(needs_access_check, type_known_final, type_known_abstract,
+ use_declaring_class, can_assume_type_is_in_dex_cache,
+ type_idx, rl_dest, rl_src);
+ }
+}
+
+void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_src)
+{
+ bool type_known_final, type_known_abstract, use_declaring_class;
+ bool needs_access_check = !cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx,
+ *cu_->dex_file,
+ type_idx,
+ &type_known_final,
+ &type_known_abstract,
+ &use_declaring_class);
+ // Note: currently type_known_final is unused, as optimizing will only improve the performance
+ // of the exception throw path.
+ DexCompilationUnit* cu = mir_graph_->GetCurrentDexCompilationUnit();
+ const MethodReference mr(cu->GetDexFile(), cu->GetDexMethodIndex());
+ if (!needs_access_check && cu_->compiler_driver->IsSafeCast(mr, insn_idx)) {
+ // Verifier type analysis proved this check cast would never cause an exception.
+ return;
+ }
+ FlushAllRegs();
+ // May generate a call - use explicit registers
+ LockCallTemps();
+ LoadCurrMethodDirect(TargetReg(kArg1)); // kArg1 <= current Method*
+ int class_reg = TargetReg(kArg2); // kArg2 will hold the Class*
+ if (needs_access_check) {
+ // Check we have access to type_idx and if not throw IllegalAccessError,
+ // returns Class* in kRet0
+ // InitializeTypeAndVerifyAccess(idx, method)
+ CallRuntimeHelperImmReg(ENTRYPOINT_OFFSET(pInitializeTypeAndVerifyAccessFromCode),
+ type_idx, TargetReg(kArg1), true);
+ OpRegCopy(class_reg, TargetReg(kRet0)); // Align usage with fast path
+ } else if (use_declaring_class) {
+ LoadWordDisp(TargetReg(kArg1),
+ mirror::AbstractMethod::DeclaringClassOffset().Int32Value(), class_reg);
+ } else {
+ // Load dex cache entry into class_reg (kArg2)
+ LoadWordDisp(TargetReg(kArg1),
+ mirror::AbstractMethod::DexCacheResolvedTypesOffset().Int32Value(), class_reg);
+ int32_t offset_of_type =
+ mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() +
+ (sizeof(mirror::Class*) * type_idx);
+ LoadWordDisp(class_reg, offset_of_type, class_reg);
+ if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx)) {
+ // Need to test presence of type in dex cache at runtime
+ LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL);
+ // Not resolved
+ // Call out to helper, which will return resolved type in kArg0
+ // InitializeTypeFromCode(idx, method)
+ CallRuntimeHelperImmReg(ENTRYPOINT_OFFSET(pInitializeTypeFromCode), type_idx, TargetReg(kArg1),
+ true);
+ OpRegCopy(class_reg, TargetReg(kRet0)); // Align usage with fast path
+ // Rejoin code paths
+ LIR* hop_target = NewLIR0(kPseudoTargetLabel);
+ hop_branch->target = hop_target;
+ }
+ }
+ // At this point, class_reg (kArg2) has class
+ LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref
+ /* Null is OK - continue */
+ LIR* branch1 = OpCmpImmBranch(kCondEq, TargetReg(kArg0), 0, NULL);
+ /* load object->klass_ */
+ DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0);
+ LoadWordDisp(TargetReg(kArg0), mirror::Object::ClassOffset().Int32Value(), TargetReg(kArg1));
+ /* kArg1 now contains object->klass_ */
+ LIR* branch2 = NULL;
+ if (!type_known_abstract) {
+ branch2 = OpCmpBranch(kCondEq, TargetReg(kArg1), class_reg, NULL);
+ }
+ CallRuntimeHelperRegReg(ENTRYPOINT_OFFSET(pCheckCastFromCode), TargetReg(kArg1), TargetReg(kArg2),
+ true);
+ /* branch target here */
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ branch1->target = target;
+ if (branch2 != NULL) {
+ branch2->target = target;
+ }
+}
+
+void Mir2Lir::GenLong3Addr(OpKind first_op, OpKind second_op, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2)
+{
+ RegLocation rl_result;
+ if (cu_->instruction_set == kThumb2) {
+ /*
+ * NOTE: This is the one place in the code in which we might have
+ * as many as six live temporary registers. There are 5 in the normal
+ * set for Arm. Until we have spill capabilities, temporarily add
+ * lr to the temp set. It is safe to do this locally, but note that
+ * lr is used explicitly elsewhere in the code generator and cannot
+ * normally be used as a general temp register.
+ */
+ MarkTemp(TargetReg(kLr)); // Add lr to the temp pool
+ FreeTemp(TargetReg(kLr)); // and make it available
+ }
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ // The longs may overlap - use intermediate temp if so
+ if ((rl_result.low_reg == rl_src1.high_reg) || (rl_result.low_reg == rl_src2.high_reg)){
+ int t_reg = AllocTemp();
+ OpRegRegReg(first_op, t_reg, rl_src1.low_reg, rl_src2.low_reg);
+ OpRegRegReg(second_op, rl_result.high_reg, rl_src1.high_reg, rl_src2.high_reg);
+ OpRegCopy(rl_result.low_reg, t_reg);
+ FreeTemp(t_reg);
+ } else {
+ OpRegRegReg(first_op, rl_result.low_reg, rl_src1.low_reg, rl_src2.low_reg);
+ OpRegRegReg(second_op, rl_result.high_reg, rl_src1.high_reg,
+ rl_src2.high_reg);
+ }
+ /*
+ * NOTE: If rl_dest refers to a frame variable in a large frame, the
+ * following StoreValueWide might need to allocate a temp register.
+ * To further work around the lack of a spill capability, explicitly
+ * free any temps from rl_src1 & rl_src2 that aren't still live in rl_result.
+ * Remove when spill is functional.
+ */
+ FreeRegLocTemps(rl_result, rl_src1);
+ FreeRegLocTemps(rl_result, rl_src2);
+ StoreValueWide(rl_dest, rl_result);
+ if (cu_->instruction_set == kThumb2) {
+ Clobber(TargetReg(kLr));
+ UnmarkTemp(TargetReg(kLr)); // Remove lr from the temp pool
+ }
+}
+
+
+void Mir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_shift)
+{
+ int func_offset = -1; // Make gcc happy
+
+ switch (opcode) {
+ case Instruction::SHL_LONG:
+ case Instruction::SHL_LONG_2ADDR:
+ func_offset = ENTRYPOINT_OFFSET(pShlLong);
+ break;
+ case Instruction::SHR_LONG:
+ case Instruction::SHR_LONG_2ADDR:
+ func_offset = ENTRYPOINT_OFFSET(pShrLong);
+ break;
+ case Instruction::USHR_LONG:
+ case Instruction::USHR_LONG_2ADDR:
+ func_offset = ENTRYPOINT_OFFSET(pUshrLong);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected case";
+ }
+ FlushAllRegs(); /* Send everything to home location */
+ CallRuntimeHelperRegLocationRegLocation(func_offset, rl_src1, rl_shift, false);
+ RegLocation rl_result = GetReturnWide(false);
+ StoreValueWide(rl_dest, rl_result);
+}
+
+
+void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2)
+{
+ OpKind op = kOpBkpt;
+ bool is_div_rem = false;
+ bool check_zero = false;
+ bool unary = false;
+ RegLocation rl_result;
+ bool shift_op = false;
+ switch (opcode) {
+ case Instruction::NEG_INT:
+ op = kOpNeg;
+ unary = true;
+ break;
+ case Instruction::NOT_INT:
+ op = kOpMvn;
+ unary = true;
+ break;
+ case Instruction::ADD_INT:
+ case Instruction::ADD_INT_2ADDR:
+ op = kOpAdd;
+ break;
+ case Instruction::SUB_INT:
+ case Instruction::SUB_INT_2ADDR:
+ op = kOpSub;
+ break;
+ case Instruction::MUL_INT:
+ case Instruction::MUL_INT_2ADDR:
+ op = kOpMul;
+ break;
+ case Instruction::DIV_INT:
+ case Instruction::DIV_INT_2ADDR:
+ check_zero = true;
+ op = kOpDiv;
+ is_div_rem = true;
+ break;
+ /* NOTE: returns in kArg1 */
+ case Instruction::REM_INT:
+ case Instruction::REM_INT_2ADDR:
+ check_zero = true;
+ op = kOpRem;
+ is_div_rem = true;
+ break;
+ case Instruction::AND_INT:
+ case Instruction::AND_INT_2ADDR:
+ op = kOpAnd;
+ break;
+ case Instruction::OR_INT:
+ case Instruction::OR_INT_2ADDR:
+ op = kOpOr;
+ break;
+ case Instruction::XOR_INT:
+ case Instruction::XOR_INT_2ADDR:
+ op = kOpXor;
+ break;
+ case Instruction::SHL_INT:
+ case Instruction::SHL_INT_2ADDR:
+ shift_op = true;
+ op = kOpLsl;
+ break;
+ case Instruction::SHR_INT:
+ case Instruction::SHR_INT_2ADDR:
+ shift_op = true;
+ op = kOpAsr;
+ break;
+ case Instruction::USHR_INT:
+ case Instruction::USHR_INT_2ADDR:
+ shift_op = true;
+ op = kOpLsr;
+ break;
+ default:
+ LOG(FATAL) << "Invalid word arith op: " << opcode;
+ }
+ if (!is_div_rem) {
+ if (unary) {
+ rl_src1 = LoadValue(rl_src1, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegReg(op, rl_result.low_reg, rl_src1.low_reg);
+ } else {
+ if (shift_op) {
+ int t_reg = INVALID_REG;
+ if (cu_->instruction_set == kX86) {
+ // X86 doesn't require masking and must use ECX
+ t_reg = TargetReg(kCount); // rCX
+ LoadValueDirectFixed(rl_src2, t_reg);
+ } else {
+ rl_src2 = LoadValue(rl_src2, kCoreReg);
+ t_reg = AllocTemp();
+ OpRegRegImm(kOpAnd, t_reg, rl_src2.low_reg, 31);
+ }
+ rl_src1 = LoadValue(rl_src1, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegRegReg(op, rl_result.low_reg, rl_src1.low_reg, t_reg);
+ FreeTemp(t_reg);
+ } else {
+ rl_src1 = LoadValue(rl_src1, kCoreReg);
+ rl_src2 = LoadValue(rl_src2, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegRegReg(op, rl_result.low_reg, rl_src1.low_reg, rl_src2.low_reg);
+ }
+ }
+ StoreValue(rl_dest, rl_result);
+ } else {
+ if (cu_->instruction_set == kMips) {
+ rl_src1 = LoadValue(rl_src1, kCoreReg);
+ rl_src2 = LoadValue(rl_src2, kCoreReg);
+ if (check_zero) {
+ GenImmedCheck(kCondEq, rl_src2.low_reg, 0, kThrowDivZero);
+ }
+ rl_result = GenDivRem(rl_dest, rl_src1.low_reg, rl_src2.low_reg, op == kOpDiv);
+ } else {
+ int func_offset = ENTRYPOINT_OFFSET(pIdivmod);
+ FlushAllRegs(); /* Send everything to home location */
+ LoadValueDirectFixed(rl_src2, TargetReg(kArg1));
+ int r_tgt = CallHelperSetup(func_offset);
+ LoadValueDirectFixed(rl_src1, TargetReg(kArg0));
+ if (check_zero) {
+ GenImmedCheck(kCondEq, TargetReg(kArg1), 0, kThrowDivZero);
+ }
+ // NOTE: callout here is not a safepoint
+ CallHelper(r_tgt, func_offset, false /* not a safepoint */ );
+ if (op == kOpDiv)
+ rl_result = GetReturn(false);
+ else
+ rl_result = GetReturnAlt();
+ }
+ StoreValue(rl_dest, rl_result);
+ }
+}
+
+/*
+ * The following are the first-level codegen routines that analyze the format
+ * of each bytecode then either dispatch special purpose codegen routines
+ * or produce corresponding Thumb instructions directly.
+ */
+
+static bool IsPowerOfTwo(int x)
+{
+ return (x & (x - 1)) == 0;
+}
+
+// Returns true if no more than two bits are set in 'x'.
+static bool IsPopCountLE2(unsigned int x)
+{
+ x &= x - 1;
+ return (x & (x - 1)) == 0;
+}
+
+// Returns the index of the lowest set bit in 'x'.
+static int LowestSetBit(unsigned int x) {
+ int bit_posn = 0;
+ while ((x & 0xf) == 0) {
+ bit_posn += 4;
+ x >>= 4;
+ }
+ while ((x & 1) == 0) {
+ bit_posn++;
+ x >>= 1;
+ }
+ return bit_posn;
+}
+
+// Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit'
+// and store the result in 'rl_dest'.
+bool Mir2Lir::HandleEasyDivide(Instruction::Code dalvik_opcode,
+ RegLocation rl_src, RegLocation rl_dest, int lit)
+{
+ if ((lit < 2) || ((cu_->instruction_set != kThumb2) && !IsPowerOfTwo(lit))) {
+ return false;
+ }
+ // No divide instruction for Arm, so check for more special cases
+ if ((cu_->instruction_set == kThumb2) && !IsPowerOfTwo(lit)) {
+ return SmallLiteralDivide(dalvik_opcode, rl_src, rl_dest, lit);
+ }
+ int k = LowestSetBit(lit);
+ if (k >= 30) {
+ // Avoid special cases.
+ return false;
+ }
+ bool div = (dalvik_opcode == Instruction::DIV_INT_LIT8 ||
+ dalvik_opcode == Instruction::DIV_INT_LIT16);
+ rl_src = LoadValue(rl_src, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (div) {
+ int t_reg = AllocTemp();
+ if (lit == 2) {
+ // Division by 2 is by far the most common division by constant.
+ OpRegRegImm(kOpLsr, t_reg, rl_src.low_reg, 32 - k);
+ OpRegRegReg(kOpAdd, t_reg, t_reg, rl_src.low_reg);
+ OpRegRegImm(kOpAsr, rl_result.low_reg, t_reg, k);
+ } else {
+ OpRegRegImm(kOpAsr, t_reg, rl_src.low_reg, 31);
+ OpRegRegImm(kOpLsr, t_reg, t_reg, 32 - k);
+ OpRegRegReg(kOpAdd, t_reg, t_reg, rl_src.low_reg);
+ OpRegRegImm(kOpAsr, rl_result.low_reg, t_reg, k);
+ }
+ } else {
+ int t_reg1 = AllocTemp();
+ int t_reg2 = AllocTemp();
+ if (lit == 2) {
+ OpRegRegImm(kOpLsr, t_reg1, rl_src.low_reg, 32 - k);
+ OpRegRegReg(kOpAdd, t_reg2, t_reg1, rl_src.low_reg);
+ OpRegRegImm(kOpAnd, t_reg2, t_reg2, lit -1);
+ OpRegRegReg(kOpSub, rl_result.low_reg, t_reg2, t_reg1);
+ } else {
+ OpRegRegImm(kOpAsr, t_reg1, rl_src.low_reg, 31);
+ OpRegRegImm(kOpLsr, t_reg1, t_reg1, 32 - k);
+ OpRegRegReg(kOpAdd, t_reg2, t_reg1, rl_src.low_reg);
+ OpRegRegImm(kOpAnd, t_reg2, t_reg2, lit - 1);
+ OpRegRegReg(kOpSub, rl_result.low_reg, t_reg2, t_reg1);
+ }
+ }
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+// Returns true if it added instructions to 'cu' to multiply 'rl_src' by 'lit'
+// and store the result in 'rl_dest'.
+bool Mir2Lir::HandleEasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit)
+{
+ // Can we simplify this multiplication?
+ bool power_of_two = false;
+ bool pop_count_le2 = false;
+ bool power_of_two_minus_one = false;
+ if (lit < 2) {
+ // Avoid special cases.
+ return false;
+ } else if (IsPowerOfTwo(lit)) {
+ power_of_two = true;
+ } else if (IsPopCountLE2(lit)) {
+ pop_count_le2 = true;
+ } else if (IsPowerOfTwo(lit + 1)) {
+ power_of_two_minus_one = true;
+ } else {
+ return false;
+ }
+ rl_src = LoadValue(rl_src, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (power_of_two) {
+ // Shift.
+ OpRegRegImm(kOpLsl, rl_result.low_reg, rl_src.low_reg, LowestSetBit(lit));
+ } else if (pop_count_le2) {
+ // Shift and add and shift.
+ int first_bit = LowestSetBit(lit);
+ int second_bit = LowestSetBit(lit ^ (1 << first_bit));
+ GenMultiplyByTwoBitMultiplier(rl_src, rl_result, lit, first_bit, second_bit);
+ } else {
+ // Reverse subtract: (src << (shift + 1)) - src.
+ DCHECK(power_of_two_minus_one);
+ // TUNING: rsb dst, src, src lsl#LowestSetBit(lit + 1)
+ int t_reg = AllocTemp();
+ OpRegRegImm(kOpLsl, t_reg, rl_src.low_reg, LowestSetBit(lit + 1));
+ OpRegRegReg(kOpSub, rl_result.low_reg, t_reg, rl_src.low_reg);
+ }
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+void Mir2Lir::GenArithOpIntLit(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src,
+ int lit)
+{
+ RegLocation rl_result;
+ OpKind op = static_cast<OpKind>(0); /* Make gcc happy */
+ int shift_op = false;
+ bool is_div = false;
+
+ switch (opcode) {
+ case Instruction::RSUB_INT_LIT8:
+ case Instruction::RSUB_INT: {
+ rl_src = LoadValue(rl_src, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (cu_->instruction_set == kThumb2) {
+ OpRegRegImm(kOpRsub, rl_result.low_reg, rl_src.low_reg, lit);
+ } else {
+ OpRegReg(kOpNeg, rl_result.low_reg, rl_src.low_reg);
+ OpRegImm(kOpAdd, rl_result.low_reg, lit);
+ }
+ StoreValue(rl_dest, rl_result);
+ return;
+ }
+
+ case Instruction::SUB_INT:
+ case Instruction::SUB_INT_2ADDR:
+ lit = -lit;
+ // Intended fallthrough
+ case Instruction::ADD_INT:
+ case Instruction::ADD_INT_2ADDR:
+ case Instruction::ADD_INT_LIT8:
+ case Instruction::ADD_INT_LIT16:
+ op = kOpAdd;
+ break;
+ case Instruction::MUL_INT:
+ case Instruction::MUL_INT_2ADDR:
+ case Instruction::MUL_INT_LIT8:
+ case Instruction::MUL_INT_LIT16: {
+ if (HandleEasyMultiply(rl_src, rl_dest, lit)) {
+ return;
+ }
+ op = kOpMul;
+ break;
+ }
+ case Instruction::AND_INT:
+ case Instruction::AND_INT_2ADDR:
+ case Instruction::AND_INT_LIT8:
+ case Instruction::AND_INT_LIT16:
+ op = kOpAnd;
+ break;
+ case Instruction::OR_INT:
+ case Instruction::OR_INT_2ADDR:
+ case Instruction::OR_INT_LIT8:
+ case Instruction::OR_INT_LIT16:
+ op = kOpOr;
+ break;
+ case Instruction::XOR_INT:
+ case Instruction::XOR_INT_2ADDR:
+ case Instruction::XOR_INT_LIT8:
+ case Instruction::XOR_INT_LIT16:
+ op = kOpXor;
+ break;
+ case Instruction::SHL_INT_LIT8:
+ case Instruction::SHL_INT:
+ case Instruction::SHL_INT_2ADDR:
+ lit &= 31;
+ shift_op = true;
+ op = kOpLsl;
+ break;
+ case Instruction::SHR_INT_LIT8:
+ case Instruction::SHR_INT:
+ case Instruction::SHR_INT_2ADDR:
+ lit &= 31;
+ shift_op = true;
+ op = kOpAsr;
+ break;
+ case Instruction::USHR_INT_LIT8:
+ case Instruction::USHR_INT:
+ case Instruction::USHR_INT_2ADDR:
+ lit &= 31;
+ shift_op = true;
+ op = kOpLsr;
+ break;
+
+ case Instruction::DIV_INT:
+ case Instruction::DIV_INT_2ADDR:
+ case Instruction::DIV_INT_LIT8:
+ case Instruction::DIV_INT_LIT16:
+ case Instruction::REM_INT:
+ case Instruction::REM_INT_2ADDR:
+ case Instruction::REM_INT_LIT8:
+ case Instruction::REM_INT_LIT16: {
+ if (lit == 0) {
+ GenImmedCheck(kCondAl, 0, 0, kThrowDivZero);
+ return;
+ }
+ if (HandleEasyDivide(opcode, rl_src, rl_dest, lit)) {
+ return;
+ }
+ if ((opcode == Instruction::DIV_INT_LIT8) ||
+ (opcode == Instruction::DIV_INT) ||
+ (opcode == Instruction::DIV_INT_2ADDR) ||
+ (opcode == Instruction::DIV_INT_LIT16)) {
+ is_div = true;
+ } else {
+ is_div = false;
+ }
+ if (cu_->instruction_set == kMips) {
+ rl_src = LoadValue(rl_src, kCoreReg);
+ rl_result = GenDivRemLit(rl_dest, rl_src.low_reg, lit, is_div);
+ } else {
+ FlushAllRegs(); /* Everything to home location */
+ LoadValueDirectFixed(rl_src, TargetReg(kArg0));
+ Clobber(TargetReg(kArg0));
+ int func_offset = ENTRYPOINT_OFFSET(pIdivmod);
+ CallRuntimeHelperRegImm(func_offset, TargetReg(kArg0), lit, false);
+ if (is_div)
+ rl_result = GetReturn(false);
+ else
+ rl_result = GetReturnAlt();
+ }
+ StoreValue(rl_dest, rl_result);
+ return;
+ }
+ default:
+ LOG(FATAL) << "Unexpected opcode " << opcode;
+ }
+ rl_src = LoadValue(rl_src, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ // Avoid shifts by literal 0 - no support in Thumb. Change to copy
+ if (shift_op && (lit == 0)) {
+ OpRegCopy(rl_result.low_reg, rl_src.low_reg);
+ } else {
+ OpRegRegImm(op, rl_result.low_reg, rl_src.low_reg, lit);
+ }
+ StoreValue(rl_dest, rl_result);
+}
+
+void Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2)
+{
+ RegLocation rl_result;
+ OpKind first_op = kOpBkpt;
+ OpKind second_op = kOpBkpt;
+ bool call_out = false;
+ bool check_zero = false;
+ int func_offset;
+ int ret_reg = TargetReg(kRet0);
+
+ switch (opcode) {
+ case Instruction::NOT_LONG:
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ // Check for destructive overlap
+ if (rl_result.low_reg == rl_src2.high_reg) {
+ int t_reg = AllocTemp();
+ OpRegCopy(t_reg, rl_src2.high_reg);
+ OpRegReg(kOpMvn, rl_result.low_reg, rl_src2.low_reg);
+ OpRegReg(kOpMvn, rl_result.high_reg, t_reg);
+ FreeTemp(t_reg);
+ } else {
+ OpRegReg(kOpMvn, rl_result.low_reg, rl_src2.low_reg);
+ OpRegReg(kOpMvn, rl_result.high_reg, rl_src2.high_reg);
+ }
+ StoreValueWide(rl_dest, rl_result);
+ return;
+ case Instruction::ADD_LONG:
+ case Instruction::ADD_LONG_2ADDR:
+ if (cu_->instruction_set != kThumb2) {
+ GenAddLong(rl_dest, rl_src1, rl_src2);
+ return;
+ }
+ first_op = kOpAdd;
+ second_op = kOpAdc;
+ break;
+ case Instruction::SUB_LONG:
+ case Instruction::SUB_LONG_2ADDR:
+ if (cu_->instruction_set != kThumb2) {
+ GenSubLong(rl_dest, rl_src1, rl_src2);
+ return;
+ }
+ first_op = kOpSub;
+ second_op = kOpSbc;
+ break;
+ case Instruction::MUL_LONG:
+ case Instruction::MUL_LONG_2ADDR:
+ if (cu_->instruction_set == kThumb2) {
+ GenMulLong(rl_dest, rl_src1, rl_src2);
+ return;
+ } else {
+ call_out = true;
+ ret_reg = TargetReg(kRet0);
+ func_offset = ENTRYPOINT_OFFSET(pLmul);
+ }
+ break;
+ case Instruction::DIV_LONG:
+ case Instruction::DIV_LONG_2ADDR:
+ call_out = true;
+ check_zero = true;
+ ret_reg = TargetReg(kRet0);
+ func_offset = ENTRYPOINT_OFFSET(pLdiv);
+ break;
+ case Instruction::REM_LONG:
+ case Instruction::REM_LONG_2ADDR:
+ call_out = true;
+ check_zero = true;
+ func_offset = ENTRYPOINT_OFFSET(pLdivmod);
+ /* NOTE - for Arm, result is in kArg2/kArg3 instead of kRet0/kRet1 */
+ ret_reg = (cu_->instruction_set == kThumb2) ? TargetReg(kArg2) : TargetReg(kRet0);
+ break;
+ case Instruction::AND_LONG_2ADDR:
+ case Instruction::AND_LONG:
+ if (cu_->instruction_set == kX86) {
+ return GenAndLong(rl_dest, rl_src1, rl_src2);
+ }
+ first_op = kOpAnd;
+ second_op = kOpAnd;
+ break;
+ case Instruction::OR_LONG:
+ case Instruction::OR_LONG_2ADDR:
+ if (cu_->instruction_set == kX86) {
+ GenOrLong(rl_dest, rl_src1, rl_src2);
+ return;
+ }
+ first_op = kOpOr;
+ second_op = kOpOr;
+ break;
+ case Instruction::XOR_LONG:
+ case Instruction::XOR_LONG_2ADDR:
+ if (cu_->instruction_set == kX86) {
+ GenXorLong(rl_dest, rl_src1, rl_src2);
+ return;
+ }
+ first_op = kOpXor;
+ second_op = kOpXor;
+ break;
+ case Instruction::NEG_LONG: {
+ GenNegLong(rl_dest, rl_src2);
+ return;
+ }
+ default:
+ LOG(FATAL) << "Invalid long arith op";
+ }
+ if (!call_out) {
+ GenLong3Addr(first_op, second_op, rl_dest, rl_src1, rl_src2);
+ } else {
+ FlushAllRegs(); /* Send everything to home location */
+ if (check_zero) {
+ LoadValueDirectWideFixed(rl_src2, TargetReg(kArg2), TargetReg(kArg3));
+ int r_tgt = CallHelperSetup(func_offset);
+ GenDivZeroCheck(TargetReg(kArg2), TargetReg(kArg3));
+ LoadValueDirectWideFixed(rl_src1, TargetReg(kArg0), TargetReg(kArg1));
+ // NOTE: callout here is not a safepoint
+ CallHelper(r_tgt, func_offset, false /* not safepoint */);
+ } else {
+ CallRuntimeHelperRegLocationRegLocation(func_offset, rl_src1, rl_src2, false);
+ }
+ // Adjust return regs in to handle case of rem returning kArg2/kArg3
+ if (ret_reg == TargetReg(kRet0))
+ rl_result = GetReturnWide(false);
+ else
+ rl_result = GetReturnWideAlt();
+ StoreValueWide(rl_dest, rl_result);
+ }
+}
+
+void Mir2Lir::GenConversionCall(int func_offset,
+ RegLocation rl_dest, RegLocation rl_src)
+{
+ /*
+ * Don't optimize the register usage since it calls out to support
+ * functions
+ */
+ FlushAllRegs(); /* Send everything to home location */
+ if (rl_src.wide) {
+ LoadValueDirectWideFixed(rl_src, rl_src.fp ? TargetReg(kFArg0) : TargetReg(kArg0),
+ rl_src.fp ? TargetReg(kFArg1) : TargetReg(kArg1));
+ } else {
+ LoadValueDirectFixed(rl_src, rl_src.fp ? TargetReg(kFArg0) : TargetReg(kArg0));
+ }
+ CallRuntimeHelperRegLocation(func_offset, rl_src, false);
+ if (rl_dest.wide) {
+ RegLocation rl_result;
+ rl_result = GetReturnWide(rl_dest.fp);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ RegLocation rl_result;
+ rl_result = GetReturn(rl_dest.fp);
+ StoreValue(rl_dest, rl_result);
+ }
+}
+
+/* Check if we need to check for pending suspend request */
+void Mir2Lir::GenSuspendTest(int opt_flags)
+{
+ if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) {
+ return;
+ }
+ FlushAllRegs();
+ LIR* branch = OpTestSuspend(NULL);
+ LIR* ret_lab = NewLIR0(kPseudoTargetLabel);
+ LIR* target = RawLIR(current_dalvik_offset_, kPseudoSuspendTarget,
+ reinterpret_cast<uintptr_t>(ret_lab), current_dalvik_offset_);
+ branch->target = target;
+ suspend_launchpads_.Insert(target);
+}
+
+/* Check if we need to check for pending suspend request */
+void Mir2Lir::GenSuspendTestAndBranch(int opt_flags, LIR* target)
+{
+ if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) {
+ OpUnconditionalBranch(target);
+ return;
+ }
+ OpTestSuspend(target);
+ LIR* launch_pad =
+ RawLIR(current_dalvik_offset_, kPseudoSuspendTarget,
+ reinterpret_cast<uintptr_t>(target), current_dalvik_offset_);
+ FlushAllRegs();
+ OpUnconditionalBranch(launch_pad);
+ suspend_launchpads_.Insert(launch_pad);
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
new file mode 100644
index 0000000..e3993e0
--- /dev/null
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -0,0 +1,1476 @@
+/*
+ * Copyright (C) 2012 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 "dex/compiler_ir.h"
+#include "dex_file-inl.h"
+#include "invoke_type.h"
+#include "mirror/array.h"
+#include "mirror/string.h"
+#include "mir_to_lir-inl.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+#include "x86/codegen_x86.h"
+
+namespace art {
+
+/*
+ * This source files contains "gen" codegen routines that should
+ * be applicable to most targets. Only mid-level support utilities
+ * and "op" calls may be used here.
+ */
+
+/*
+ * To save scheduling time, helper calls are broken into two parts: generation of
+ * the helper target address, and the actuall call to the helper. Because x86
+ * has a memory call operation, part 1 is a NOP for x86. For other targets,
+ * load arguments between the two parts.
+ */
+int Mir2Lir::CallHelperSetup(int helper_offset)
+{
+ return (cu_->instruction_set == kX86) ? 0 : LoadHelper(helper_offset);
+}
+
+/* NOTE: if r_tgt is a temp, it will be freed following use */
+LIR* Mir2Lir::CallHelper(int r_tgt, int helper_offset, bool safepoint_pc)
+{
+ LIR* call_inst;
+ if (cu_->instruction_set == kX86) {
+ call_inst = OpThreadMem(kOpBlx, helper_offset);
+ } else {
+ call_inst = OpReg(kOpBlx, r_tgt);
+ FreeTemp(r_tgt);
+ }
+ if (safepoint_pc) {
+ MarkSafepointPC(call_inst);
+ }
+ return call_inst;
+}
+
+void Mir2Lir::CallRuntimeHelperImm(int helper_offset, int arg0, bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ LoadConstant(TargetReg(kArg0), arg0);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperReg(int helper_offset, int arg0, bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ OpRegCopy(TargetReg(kArg0), arg0);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperRegLocation(int helper_offset, RegLocation arg0, bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ if (arg0.wide == 0) {
+ LoadValueDirectFixed(arg0, TargetReg(kArg0));
+ } else {
+ LoadValueDirectWideFixed(arg0, TargetReg(kArg0), TargetReg(kArg1));
+ }
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperImmImm(int helper_offset, int arg0, int arg1,
+ bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ LoadConstant(TargetReg(kArg0), arg0);
+ LoadConstant(TargetReg(kArg1), arg1);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperImmRegLocation(int helper_offset, int arg0,
+ RegLocation arg1, bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ if (arg1.wide == 0) {
+ LoadValueDirectFixed(arg1, TargetReg(kArg1));
+ } else {
+ LoadValueDirectWideFixed(arg1, TargetReg(kArg1), TargetReg(kArg2));
+ }
+ LoadConstant(TargetReg(kArg0), arg0);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperRegLocationImm(int helper_offset, RegLocation arg0, int arg1,
+ bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ LoadValueDirectFixed(arg0, TargetReg(kArg0));
+ LoadConstant(TargetReg(kArg1), arg1);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperImmReg(int helper_offset, int arg0, int arg1,
+ bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ OpRegCopy(TargetReg(kArg1), arg1);
+ LoadConstant(TargetReg(kArg0), arg0);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperRegImm(int helper_offset, int arg0, int arg1,
+ bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ OpRegCopy(TargetReg(kArg0), arg0);
+ LoadConstant(TargetReg(kArg1), arg1);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperImmMethod(int helper_offset, int arg0, bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ LoadCurrMethodDirect(TargetReg(kArg1));
+ LoadConstant(TargetReg(kArg0), arg0);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperRegLocationRegLocation(int helper_offset, RegLocation arg0,
+ RegLocation arg1, bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ if (arg0.wide == 0) {
+ LoadValueDirectFixed(arg0, arg0.fp ? TargetReg(kFArg0) : TargetReg(kArg0));
+ if (arg1.wide == 0) {
+ if (cu_->instruction_set == kMips) {
+ LoadValueDirectFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg1));
+ } else {
+ LoadValueDirectFixed(arg1, TargetReg(kArg1));
+ }
+ } else {
+ if (cu_->instruction_set == kMips) {
+ LoadValueDirectWideFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg1), arg1.fp ? TargetReg(kFArg3) : TargetReg(kArg2));
+ } else {
+ LoadValueDirectWideFixed(arg1, TargetReg(kArg1), TargetReg(kArg2));
+ }
+ }
+ } else {
+ LoadValueDirectWideFixed(arg0, arg0.fp ? TargetReg(kFArg0) : TargetReg(kArg0), arg0.fp ? TargetReg(kFArg1) : TargetReg(kArg1));
+ if (arg1.wide == 0) {
+ LoadValueDirectFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg2));
+ } else {
+ LoadValueDirectWideFixed(arg1, arg1.fp ? TargetReg(kFArg2) : TargetReg(kArg2), arg1.fp ? TargetReg(kFArg3) : TargetReg(kArg3));
+ }
+ }
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperRegReg(int helper_offset, int arg0, int arg1, bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ DCHECK_NE(TargetReg(kArg0), arg1); // check copy into arg0 won't clobber arg1
+ OpRegCopy(TargetReg(kArg0), arg0);
+ OpRegCopy(TargetReg(kArg1), arg1);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperRegRegImm(int helper_offset, int arg0, int arg1,
+ int arg2, bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ DCHECK_NE(TargetReg(kArg0), arg1); // check copy into arg0 won't clobber arg1
+ OpRegCopy(TargetReg(kArg0), arg0);
+ OpRegCopy(TargetReg(kArg1), arg1);
+ LoadConstant(TargetReg(kArg2), arg2);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperImmMethodRegLocation(int helper_offset,
+ int arg0, RegLocation arg2, bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ LoadValueDirectFixed(arg2, TargetReg(kArg2));
+ LoadCurrMethodDirect(TargetReg(kArg1));
+ LoadConstant(TargetReg(kArg0), arg0);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperImmMethodImm(int helper_offset, int arg0,
+ int arg2, bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ LoadCurrMethodDirect(TargetReg(kArg1));
+ LoadConstant(TargetReg(kArg2), arg2);
+ LoadConstant(TargetReg(kArg0), arg0);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+void Mir2Lir::CallRuntimeHelperImmRegLocationRegLocation(int helper_offset,
+ int arg0, RegLocation arg1,
+ RegLocation arg2, bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ LoadValueDirectFixed(arg1, TargetReg(kArg1));
+ if (arg2.wide == 0) {
+ LoadValueDirectFixed(arg2, TargetReg(kArg2));
+ } else {
+ LoadValueDirectWideFixed(arg2, TargetReg(kArg2), TargetReg(kArg3));
+ }
+ LoadConstant(TargetReg(kArg0), arg0);
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
+/*
+ * If there are any ins passed in registers that have not been promoted
+ * to a callee-save register, flush them to the frame. Perform intial
+ * assignment of promoted arguments.
+ *
+ * ArgLocs is an array of location records describing the incoming arguments
+ * with one location record per word of argument.
+ */
+void Mir2Lir::FlushIns(RegLocation* ArgLocs, RegLocation rl_method)
+{
+ /*
+ * Dummy up a RegLocation for the incoming Method*
+ * It will attempt to keep kArg0 live (or copy it to home location
+ * if promoted).
+ */
+ RegLocation rl_src = rl_method;
+ rl_src.location = kLocPhysReg;
+ rl_src.low_reg = TargetReg(kArg0);
+ rl_src.home = false;
+ MarkLive(rl_src.low_reg, rl_src.s_reg_low);
+ StoreValue(rl_method, rl_src);
+ // If Method* has been promoted, explicitly flush
+ if (rl_method.location == kLocPhysReg) {
+ StoreWordDisp(TargetReg(kSp), 0, TargetReg(kArg0));
+ }
+
+ if (cu_->num_ins == 0)
+ return;
+ const int num_arg_regs = 3;
+ static SpecialTargetRegister arg_regs[] = {kArg1, kArg2, kArg3};
+ int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
+ /*
+ * Copy incoming arguments to their proper home locations.
+ * NOTE: an older version of dx had an issue in which
+ * it would reuse static method argument registers.
+ * This could result in the same Dalvik virtual register
+ * being promoted to both core and fp regs. To account for this,
+ * we only copy to the corresponding promoted physical register
+ * if it matches the type of the SSA name for the incoming
+ * argument. It is also possible that long and double arguments
+ * end up half-promoted. In those cases, we must flush the promoted
+ * half to memory as well.
+ */
+ for (int i = 0; i < cu_->num_ins; i++) {
+ PromotionMap* v_map = &promotion_map_[start_vreg + i];
+ if (i < num_arg_regs) {
+ // If arriving in register
+ bool need_flush = true;
+ RegLocation* t_loc = &ArgLocs[i];
+ if ((v_map->core_location == kLocPhysReg) && !t_loc->fp) {
+ OpRegCopy(v_map->core_reg, TargetReg(arg_regs[i]));
+ need_flush = false;
+ } else if ((v_map->fp_location == kLocPhysReg) && t_loc->fp) {
+ OpRegCopy(v_map->FpReg, TargetReg(arg_regs[i]));
+ need_flush = false;
+ } else {
+ need_flush = true;
+ }
+
+ // For wide args, force flush if only half is promoted
+ if (t_loc->wide) {
+ PromotionMap* p_map = v_map + (t_loc->high_word ? -1 : +1);
+ need_flush |= (p_map->core_location != v_map->core_location) ||
+ (p_map->fp_location != v_map->fp_location);
+ }
+ if (need_flush) {
+ StoreBaseDisp(TargetReg(kSp), SRegOffset(start_vreg + i),
+ TargetReg(arg_regs[i]), kWord);
+ }
+ } else {
+ // If arriving in frame & promoted
+ if (v_map->core_location == kLocPhysReg) {
+ LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i),
+ v_map->core_reg);
+ }
+ if (v_map->fp_location == kLocPhysReg) {
+ LoadWordDisp(TargetReg(kSp), SRegOffset(start_vreg + i),
+ v_map->FpReg);
+ }
+ }
+ }
+}
+
+/*
+ * Bit of a hack here - in the absence of a real scheduling pass,
+ * emit the next instruction in static & direct invoke sequences.
+ */
+static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+ int state, const MethodReference& target_method,
+ uint32_t unused,
+ uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type)
+{
+ Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+ if (cu->instruction_set != kThumb2) {
+ // Disable sharpening
+ direct_code = 0;
+ direct_method = 0;
+ }
+ if (direct_code != 0 && direct_method != 0) {
+ switch (state) {
+ case 0: // Get the current Method* [sets kArg0]
+ if (direct_code != static_cast<unsigned int>(-1)) {
+ cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code);
+ } else {
+ CHECK_EQ(cu->dex_file, target_method.dex_file);
+ LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_,
+ target_method.dex_method_index, 0);
+ if (data_target == NULL) {
+ data_target = cg->AddWordData(&cg->code_literal_list_, target_method.dex_method_index);
+ data_target->operands[1] = type;
+ }
+ LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kInvokeTgt), data_target);
+ cg->AppendLIR(load_pc_rel);
+ DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target);
+ }
+ if (direct_method != static_cast<unsigned int>(-1)) {
+ cg->LoadConstant(cg->TargetReg(kArg0), direct_method);
+ } else {
+ CHECK_EQ(cu->dex_file, target_method.dex_file);
+ LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_,
+ target_method.dex_method_index, 0);
+ if (data_target == NULL) {
+ data_target = cg->AddWordData(&cg->method_literal_list_, target_method.dex_method_index);
+ data_target->operands[1] = type;
+ }
+ LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kArg0), data_target);
+ cg->AppendLIR(load_pc_rel);
+ DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target);
+ }
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ switch (state) {
+ case 0: // Get the current Method* [sets kArg0]
+ // TUNING: we can save a reg copy if Method* has been promoted.
+ cg->LoadCurrMethodDirect(cg->TargetReg(kArg0));
+ break;
+ case 1: // Get method->dex_cache_resolved_methods_
+ cg->LoadWordDisp(cg->TargetReg(kArg0),
+ mirror::AbstractMethod::DexCacheResolvedMethodsOffset().Int32Value(), cg->TargetReg(kArg0));
+ // Set up direct code if known.
+ if (direct_code != 0) {
+ if (direct_code != static_cast<unsigned int>(-1)) {
+ cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code);
+ } else {
+ CHECK_EQ(cu->dex_file, target_method.dex_file);
+ LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_,
+ target_method.dex_method_index, 0);
+ if (data_target == NULL) {
+ data_target = cg->AddWordData(&cg->code_literal_list_, target_method.dex_method_index);
+ data_target->operands[1] = type;
+ }
+ LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kInvokeTgt), data_target);
+ cg->AppendLIR(load_pc_rel);
+ DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target);
+ }
+ }
+ break;
+ case 2: // Grab target method*
+ CHECK_EQ(cu->dex_file, target_method.dex_file);
+ cg->LoadWordDisp(cg->TargetReg(kArg0),
+ mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
+ (target_method.dex_method_index * 4),
+ cg-> TargetReg(kArg0));
+ break;
+ case 3: // Grab the code from the method*
+ if (cu->instruction_set != kX86) {
+ if (direct_code == 0) {
+ cg->LoadWordDisp(cg->TargetReg(kArg0),
+ mirror::AbstractMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
+ }
+ break;
+ }
+ // Intentional fallthrough for x86
+ default:
+ return -1;
+ }
+ }
+ return state + 1;
+}
+
+/*
+ * Bit of a hack here - in the absence of a real scheduling pass,
+ * emit the next instruction in a virtual invoke sequence.
+ * We can use kLr as a temp prior to target address loading
+ * Note also that we'll load the first argument ("this") into
+ * kArg1 here rather than the standard LoadArgRegs.
+ */
+static int NextVCallInsn(CompilationUnit* cu, CallInfo* info,
+ int state, const MethodReference& target_method,
+ uint32_t method_idx, uintptr_t unused, uintptr_t unused2,
+ InvokeType unused3)
+{
+ Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+ /*
+ * This is the fast path in which the target virtual method is
+ * fully resolved at compile time.
+ */
+ switch (state) {
+ case 0: { // Get "this" [set kArg1]
+ RegLocation rl_arg = info->args[0];
+ cg->LoadValueDirectFixed(rl_arg, cg->TargetReg(kArg1));
+ break;
+ }
+ case 1: // Is "this" null? [use kArg1]
+ cg->GenNullCheck(info->args[0].s_reg_low, cg->TargetReg(kArg1), info->opt_flags);
+ // get this->klass_ [use kArg1, set kInvokeTgt]
+ cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
+ break;
+ case 2: // Get this->klass_->vtable [usr kInvokeTgt, set kInvokeTgt]
+ cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::VTableOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
+ break;
+ case 3: // Get target method [use kInvokeTgt, set kArg0]
+ cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), (method_idx * 4) +
+ mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(),
+ cg->TargetReg(kArg0));
+ break;
+ case 4: // Get the compiled code address [uses kArg0, sets kInvokeTgt]
+ if (cu->instruction_set != kX86) {
+ cg->LoadWordDisp(cg->TargetReg(kArg0),
+ mirror::AbstractMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
+ break;
+ }
+ // Intentional fallthrough for X86
+ default:
+ return -1;
+ }
+ return state + 1;
+}
+
+/*
+ * All invoke-interface calls bounce off of art_quick_invoke_interface_trampoline,
+ * which will locate the target and continue on via a tail call.
+ */
+static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state,
+ const MethodReference& target_method,
+ uint32_t unused, uintptr_t unused2,
+ uintptr_t direct_method, InvokeType unused4)
+{
+ Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+ if (cu->instruction_set != kThumb2) {
+ // Disable sharpening
+ direct_method = 0;
+ }
+ int trampoline = (cu->instruction_set == kX86) ? 0
+ : ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline);
+
+ if (direct_method != 0) {
+ switch (state) {
+ case 0: // Load the trampoline target [sets kInvokeTgt].
+ if (cu->instruction_set != kX86) {
+ cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline, cg->TargetReg(kInvokeTgt));
+ }
+ // Get the interface Method* [sets kArg0]
+ if (direct_method != static_cast<unsigned int>(-1)) {
+ cg->LoadConstant(cg->TargetReg(kArg0), direct_method);
+ } else {
+ CHECK_EQ(cu->dex_file, target_method.dex_file);
+ LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_,
+ target_method.dex_method_index, 0);
+ if (data_target == NULL) {
+ data_target = cg->AddWordData(&cg->method_literal_list_,
+ target_method.dex_method_index);
+ data_target->operands[1] = kInterface;
+ }
+ LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kArg0), data_target);
+ cg->AppendLIR(load_pc_rel);
+ DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target);
+ }
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ switch (state) {
+ case 0:
+ // Get the current Method* [sets kArg0] - TUNING: remove copy of method if it is promoted.
+ cg->LoadCurrMethodDirect(cg->TargetReg(kArg0));
+ // Load the trampoline target [sets kInvokeTgt].
+ if (cu->instruction_set != kX86) {
+ cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline, cg->TargetReg(kInvokeTgt));
+ }
+ break;
+ case 1: // Get method->dex_cache_resolved_methods_ [set/use kArg0]
+ cg->LoadWordDisp(cg->TargetReg(kArg0),
+ mirror::AbstractMethod::DexCacheResolvedMethodsOffset().Int32Value(),
+ cg->TargetReg(kArg0));
+ break;
+ case 2: // Grab target method* [set/use kArg0]
+ CHECK_EQ(cu->dex_file, target_method.dex_file);
+ cg->LoadWordDisp(cg->TargetReg(kArg0),
+ mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
+ (target_method.dex_method_index * 4),
+ cg->TargetReg(kArg0));
+ break;
+ default:
+ return -1;
+ }
+ }
+ return state + 1;
+}
+
+static int NextInvokeInsnSP(CompilationUnit* cu, CallInfo* info, int trampoline,
+ int state, const MethodReference& target_method,
+ uint32_t method_idx)
+{
+ Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+ /*
+ * This handles the case in which the base method is not fully
+ * resolved at compile time, we bail to a runtime helper.
+ */
+ if (state == 0) {
+ if (cu->instruction_set != kX86) {
+ // Load trampoline target
+ cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline, cg->TargetReg(kInvokeTgt));
+ }
+ // Load kArg0 with method index
+ CHECK_EQ(cu->dex_file, target_method.dex_file);
+ cg->LoadConstant(cg->TargetReg(kArg0), target_method.dex_method_index);
+ return 1;
+ }
+ return -1;
+}
+
+static int NextStaticCallInsnSP(CompilationUnit* cu, CallInfo* info,
+ int state,
+ const MethodReference& target_method,
+ uint32_t method_idx,
+ uintptr_t unused, uintptr_t unused2,
+ InvokeType unused3)
+{
+ int trampoline = ENTRYPOINT_OFFSET(pInvokeStaticTrampolineWithAccessCheck);
+ return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0);
+}
+
+static int NextDirectCallInsnSP(CompilationUnit* cu, CallInfo* info, int state,
+ const MethodReference& target_method,
+ uint32_t method_idx, uintptr_t unused,
+ uintptr_t unused2, InvokeType unused3)
+{
+ int trampoline = ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck);
+ return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0);
+}
+
+static int NextSuperCallInsnSP(CompilationUnit* cu, CallInfo* info, int state,
+ const MethodReference& target_method,
+ uint32_t method_idx, uintptr_t unused,
+ uintptr_t unused2, InvokeType unused3)
+{
+ int trampoline = ENTRYPOINT_OFFSET(pInvokeSuperTrampolineWithAccessCheck);
+ return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0);
+}
+
+static int NextVCallInsnSP(CompilationUnit* cu, CallInfo* info, int state,
+ const MethodReference& target_method,
+ uint32_t method_idx, uintptr_t unused,
+ uintptr_t unused2, InvokeType unused3)
+{
+ int trampoline = ENTRYPOINT_OFFSET(pInvokeVirtualTrampolineWithAccessCheck);
+ return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0);
+}
+
+static int NextInterfaceCallInsnWithAccessCheck(CompilationUnit* cu,
+ CallInfo* info, int state,
+ const MethodReference& target_method,
+ uint32_t unused,
+ uintptr_t unused2, uintptr_t unused3,
+ InvokeType unused4)
+{
+ int trampoline = ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck);
+ return NextInvokeInsnSP(cu, info, trampoline, state, target_method, 0);
+}
+
+int Mir2Lir::LoadArgRegs(CallInfo* info, int call_state,
+ NextCallInsn next_call_insn,
+ const MethodReference& target_method,
+ uint32_t vtable_idx, uintptr_t direct_code,
+ uintptr_t direct_method, InvokeType type, bool skip_this)
+{
+ int last_arg_reg = TargetReg(kArg3);
+ int next_reg = TargetReg(kArg1);
+ int next_arg = 0;
+ if (skip_this) {
+ next_reg++;
+ next_arg++;
+ }
+ for (; (next_reg <= last_arg_reg) && (next_arg < info->num_arg_words); next_reg++) {
+ RegLocation rl_arg = info->args[next_arg++];
+ rl_arg = UpdateRawLoc(rl_arg);
+ if (rl_arg.wide && (next_reg <= TargetReg(kArg2))) {
+ LoadValueDirectWideFixed(rl_arg, next_reg, next_reg + 1);
+ next_reg++;
+ next_arg++;
+ } else {
+ if (rl_arg.wide) {
+ rl_arg.wide = false;
+ rl_arg.is_const = false;
+ }
+ LoadValueDirectFixed(rl_arg, next_reg);
+ }
+ call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+ direct_code, direct_method, type);
+ }
+ return call_state;
+}
+
+/*
+ * Load up to 5 arguments, the first three of which will be in
+ * kArg1 .. kArg3. On entry kArg0 contains the current method pointer,
+ * and as part of the load sequence, it must be replaced with
+ * the target method pointer. Note, this may also be called
+ * for "range" variants if the number of arguments is 5 or fewer.
+ */
+int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info,
+ int call_state, LIR** pcrLabel, NextCallInsn next_call_insn,
+ const MethodReference& target_method,
+ uint32_t vtable_idx, uintptr_t direct_code,
+ uintptr_t direct_method, InvokeType type, bool skip_this)
+{
+ RegLocation rl_arg;
+
+ /* If no arguments, just return */
+ if (info->num_arg_words == 0)
+ return call_state;
+
+ call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+ direct_code, direct_method, type);
+
+ DCHECK_LE(info->num_arg_words, 5);
+ if (info->num_arg_words > 3) {
+ int32_t next_use = 3;
+ //Detect special case of wide arg spanning arg3/arg4
+ RegLocation rl_use0 = info->args[0];
+ RegLocation rl_use1 = info->args[1];
+ RegLocation rl_use2 = info->args[2];
+ if (((!rl_use0.wide && !rl_use1.wide) || rl_use0.wide) &&
+ rl_use2.wide) {
+ int reg = -1;
+ // Wide spans, we need the 2nd half of uses[2].
+ rl_arg = UpdateLocWide(rl_use2);
+ if (rl_arg.location == kLocPhysReg) {
+ reg = rl_arg.high_reg;
+ } else {
+ // kArg2 & rArg3 can safely be used here
+ reg = TargetReg(kArg3);
+ LoadWordDisp(TargetReg(kSp), SRegOffset(rl_arg.s_reg_low) + 4, reg);
+ call_state = next_call_insn(cu_, info, call_state, target_method,
+ vtable_idx, direct_code, direct_method, type);
+ }
+ StoreBaseDisp(TargetReg(kSp), (next_use + 1) * 4, reg, kWord);
+ StoreBaseDisp(TargetReg(kSp), 16 /* (3+1)*4 */, reg, kWord);
+ call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+ direct_code, direct_method, type);
+ next_use++;
+ }
+ // Loop through the rest
+ while (next_use < info->num_arg_words) {
+ int low_reg;
+ int high_reg = -1;
+ rl_arg = info->args[next_use];
+ rl_arg = UpdateRawLoc(rl_arg);
+ if (rl_arg.location == kLocPhysReg) {
+ low_reg = rl_arg.low_reg;
+ high_reg = rl_arg.high_reg;
+ } else {
+ low_reg = TargetReg(kArg2);
+ if (rl_arg.wide) {
+ high_reg = TargetReg(kArg3);
+ LoadValueDirectWideFixed(rl_arg, low_reg, high_reg);
+ } else {
+ LoadValueDirectFixed(rl_arg, low_reg);
+ }
+ call_state = next_call_insn(cu_, info, call_state, target_method,
+ vtable_idx, direct_code, direct_method, type);
+ }
+ int outs_offset = (next_use + 1) * 4;
+ if (rl_arg.wide) {
+ StoreBaseDispWide(TargetReg(kSp), outs_offset, low_reg, high_reg);
+ next_use += 2;
+ } else {
+ StoreWordDisp(TargetReg(kSp), outs_offset, low_reg);
+ next_use++;
+ }
+ call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+ direct_code, direct_method, type);
+ }
+ }
+
+ call_state = LoadArgRegs(info, call_state, next_call_insn,
+ target_method, vtable_idx, direct_code, direct_method,
+ type, skip_this);
+
+ if (pcrLabel) {
+ *pcrLabel = GenNullCheck(info->args[0].s_reg_low, TargetReg(kArg1), info->opt_flags);
+ }
+ return call_state;
+}
+
+/*
+ * May have 0+ arguments (also used for jumbo). Note that
+ * source virtual registers may be in physical registers, so may
+ * need to be flushed to home location before copying. This
+ * applies to arg3 and above (see below).
+ *
+ * Two general strategies:
+ * If < 20 arguments
+ * Pass args 3-18 using vldm/vstm block copy
+ * Pass arg0, arg1 & arg2 in kArg1-kArg3
+ * If 20+ arguments
+ * Pass args arg19+ using memcpy block copy
+ * Pass arg0, arg1 & arg2 in kArg1-kArg3
+ *
+ */
+int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
+ LIR** pcrLabel, NextCallInsn next_call_insn,
+ const MethodReference& target_method,
+ uint32_t vtable_idx, uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type, bool skip_this)
+{
+
+ // If we can treat it as non-range (Jumbo ops will use range form)
+ if (info->num_arg_words <= 5)
+ return GenDalvikArgsNoRange(info, call_state, pcrLabel,
+ next_call_insn, target_method, vtable_idx,
+ direct_code, direct_method, type, skip_this);
+ /*
+ * First load the non-register arguments. Both forms expect all
+ * of the source arguments to be in their home frame location, so
+ * scan the s_reg names and flush any that have been promoted to
+ * frame backing storage.
+ */
+ // Scan the rest of the args - if in phys_reg flush to memory
+ for (int next_arg = 0; next_arg < info->num_arg_words;) {
+ RegLocation loc = info->args[next_arg];
+ if (loc.wide) {
+ loc = UpdateLocWide(loc);
+ if ((next_arg >= 2) && (loc.location == kLocPhysReg)) {
+ StoreBaseDispWide(TargetReg(kSp), SRegOffset(loc.s_reg_low),
+ loc.low_reg, loc.high_reg);
+ }
+ next_arg += 2;
+ } else {
+ loc = UpdateLoc(loc);
+ if ((next_arg >= 3) && (loc.location == kLocPhysReg)) {
+ StoreBaseDisp(TargetReg(kSp), SRegOffset(loc.s_reg_low),
+ loc.low_reg, kWord);
+ }
+ next_arg++;
+ }
+ }
+
+ int start_offset = SRegOffset(info->args[3].s_reg_low);
+ int outs_offset = 4 /* Method* */ + (3 * 4);
+ if (cu_->instruction_set != kThumb2) {
+ // Generate memcpy
+ OpRegRegImm(kOpAdd, TargetReg(kArg0), TargetReg(kSp), outs_offset);
+ OpRegRegImm(kOpAdd, TargetReg(kArg1), TargetReg(kSp), start_offset);
+ CallRuntimeHelperRegRegImm(ENTRYPOINT_OFFSET(pMemcpy), TargetReg(kArg0),
+ TargetReg(kArg1), (info->num_arg_words - 3) * 4, false);
+ } else {
+ if (info->num_arg_words >= 20) {
+ // Generate memcpy
+ OpRegRegImm(kOpAdd, TargetReg(kArg0), TargetReg(kSp), outs_offset);
+ OpRegRegImm(kOpAdd, TargetReg(kArg1), TargetReg(kSp), start_offset);
+ CallRuntimeHelperRegRegImm(ENTRYPOINT_OFFSET(pMemcpy), TargetReg(kArg0),
+ TargetReg(kArg1), (info->num_arg_words - 3) * 4, false);
+ } else {
+ // Use vldm/vstm pair using kArg3 as a temp
+ int regs_left = std::min(info->num_arg_words - 3, 16);
+ call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+ direct_code, direct_method, type);
+ OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), start_offset);
+ LIR* ld = OpVldm(TargetReg(kArg3), regs_left);
+ //TUNING: loosen barrier
+ ld->def_mask = ENCODE_ALL;
+ SetMemRefType(ld, true /* is_load */, kDalvikReg);
+ call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+ direct_code, direct_method, type);
+ OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), 4 /* Method* */ + (3 * 4));
+ call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+ direct_code, direct_method, type);
+ LIR* st = OpVstm(TargetReg(kArg3), regs_left);
+ SetMemRefType(st, false /* is_load */, kDalvikReg);
+ st->def_mask = ENCODE_ALL;
+ call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+ direct_code, direct_method, type);
+ }
+ }
+
+ call_state = LoadArgRegs(info, call_state, next_call_insn,
+ target_method, vtable_idx, direct_code, direct_method,
+ type, skip_this);
+
+ call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
+ direct_code, direct_method, type);
+ if (pcrLabel) {
+ *pcrLabel = GenNullCheck(info->args[0].s_reg_low, TargetReg(kArg1), info->opt_flags);
+ }
+ return call_state;
+}
+
+RegLocation Mir2Lir::InlineTarget(CallInfo* info)
+{
+ RegLocation res;
+ if (info->result.location == kLocInvalid) {
+ res = GetReturn(false);
+ } else {
+ res = info->result;
+ }
+ return res;
+}
+
+RegLocation Mir2Lir::InlineTargetWide(CallInfo* info)
+{
+ RegLocation res;
+ if (info->result.location == kLocInvalid) {
+ res = GetReturnWide(false);
+ } else {
+ res = info->result;
+ }
+ return res;
+}
+
+bool Mir2Lir::GenInlinedCharAt(CallInfo* info)
+{
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ // Location of reference to data array
+ int value_offset = mirror::String::ValueOffset().Int32Value();
+ // Location of count
+ int count_offset = mirror::String::CountOffset().Int32Value();
+ // Starting offset within data array
+ int offset_offset = mirror::String::OffsetOffset().Int32Value();
+ // Start of char data with array_
+ int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value();
+
+ RegLocation rl_obj = info->args[0];
+ RegLocation rl_idx = info->args[1];
+ rl_obj = LoadValue(rl_obj, kCoreReg);
+ rl_idx = LoadValue(rl_idx, kCoreReg);
+ int reg_max;
+ GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, info->opt_flags);
+ bool range_check = (!(info->opt_flags & MIR_IGNORE_RANGE_CHECK));
+ LIR* launch_pad = NULL;
+ int reg_off = INVALID_REG;
+ int reg_ptr = INVALID_REG;
+ if (cu_->instruction_set != kX86) {
+ reg_off = AllocTemp();
+ reg_ptr = AllocTemp();
+ if (range_check) {
+ reg_max = AllocTemp();
+ LoadWordDisp(rl_obj.low_reg, count_offset, reg_max);
+ }
+ LoadWordDisp(rl_obj.low_reg, offset_offset, reg_off);
+ LoadWordDisp(rl_obj.low_reg, value_offset, reg_ptr);
+ if (range_check) {
+ // Set up a launch pad to allow retry in case of bounds violation */
+ launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast<uintptr_t>(info));
+ intrinsic_launchpads_.Insert(launch_pad);
+ OpRegReg(kOpCmp, rl_idx.low_reg, reg_max);
+ FreeTemp(reg_max);
+ OpCondBranch(kCondCs, launch_pad);
+ }
+ } else {
+ if (range_check) {
+ reg_max = AllocTemp();
+ LoadWordDisp(rl_obj.low_reg, count_offset, reg_max);
+ // Set up a launch pad to allow retry in case of bounds violation */
+ launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast<uintptr_t>(info));
+ intrinsic_launchpads_.Insert(launch_pad);
+ OpRegReg(kOpCmp, rl_idx.low_reg, reg_max);
+ FreeTemp(reg_max);
+ OpCondBranch(kCondCc, launch_pad);
+ }
+ reg_off = AllocTemp();
+ reg_ptr = AllocTemp();
+ LoadWordDisp(rl_obj.low_reg, offset_offset, reg_off);
+ LoadWordDisp(rl_obj.low_reg, value_offset, reg_ptr);
+ }
+ OpRegImm(kOpAdd, reg_ptr, data_offset);
+ OpRegReg(kOpAdd, reg_off, rl_idx.low_reg);
+ FreeTemp(rl_obj.low_reg);
+ FreeTemp(rl_idx.low_reg);
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ LoadBaseIndexed(reg_ptr, reg_off, rl_result.low_reg, 1, kUnsignedHalf);
+ FreeTemp(reg_off);
+ FreeTemp(reg_ptr);
+ StoreValue(rl_dest, rl_result);
+ if (range_check) {
+ launch_pad->operands[2] = 0; // no resumption
+ }
+ // Record that we've already inlined & null checked
+ info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK);
+ return true;
+}
+
+// Generates an inlined String.is_empty or String.length.
+bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty)
+{
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ // dst = src.length();
+ RegLocation rl_obj = info->args[0];
+ rl_obj = LoadValue(rl_obj, kCoreReg);
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ GenNullCheck(rl_obj.s_reg_low, rl_obj.low_reg, info->opt_flags);
+ LoadWordDisp(rl_obj.low_reg, mirror::String::CountOffset().Int32Value(), rl_result.low_reg);
+ if (is_empty) {
+ // dst = (dst == 0);
+ if (cu_->instruction_set == kThumb2) {
+ int t_reg = AllocTemp();
+ OpRegReg(kOpNeg, t_reg, rl_result.low_reg);
+ OpRegRegReg(kOpAdc, rl_result.low_reg, rl_result.low_reg, t_reg);
+ } else {
+ DCHECK_EQ(cu_->instruction_set, kX86);
+ OpRegImm(kOpSub, rl_result.low_reg, 1);
+ OpRegImm(kOpLsr, rl_result.low_reg, 31);
+ }
+ }
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+bool Mir2Lir::GenInlinedAbsInt(CallInfo* info)
+{
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ RegLocation rl_src = info->args[0];
+ rl_src = LoadValue(rl_src, kCoreReg);
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int sign_reg = AllocTemp();
+ // abs(x) = y<=x>>31, (x+y)^y.
+ OpRegRegImm(kOpAsr, sign_reg, rl_src.low_reg, 31);
+ OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, sign_reg);
+ OpRegReg(kOpXor, rl_result.low_reg, sign_reg);
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+bool Mir2Lir::GenInlinedAbsLong(CallInfo* info)
+{
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ if (cu_->instruction_set == kThumb2) {
+ RegLocation rl_src = info->args[0];
+ rl_src = LoadValueWide(rl_src, kCoreReg);
+ RegLocation rl_dest = InlineTargetWide(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int sign_reg = AllocTemp();
+ // abs(x) = y<=x>>31, (x+y)^y.
+ OpRegRegImm(kOpAsr, sign_reg, rl_src.high_reg, 31);
+ OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, sign_reg);
+ OpRegRegReg(kOpAdc, rl_result.high_reg, rl_src.high_reg, sign_reg);
+ OpRegReg(kOpXor, rl_result.low_reg, sign_reg);
+ OpRegReg(kOpXor, rl_result.high_reg, sign_reg);
+ StoreValueWide(rl_dest, rl_result);
+ return true;
+ } else {
+ DCHECK_EQ(cu_->instruction_set, kX86);
+ // Reuse source registers to avoid running out of temps
+ RegLocation rl_src = info->args[0];
+ rl_src = LoadValueWide(rl_src, kCoreReg);
+ RegLocation rl_dest = InlineTargetWide(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegCopyWide(rl_result.low_reg, rl_result.high_reg, rl_src.low_reg, rl_src.high_reg);
+ FreeTemp(rl_src.low_reg);
+ FreeTemp(rl_src.high_reg);
+ int sign_reg = AllocTemp();
+ // abs(x) = y<=x>>31, (x+y)^y.
+ OpRegRegImm(kOpAsr, sign_reg, rl_result.high_reg, 31);
+ OpRegReg(kOpAdd, rl_result.low_reg, sign_reg);
+ OpRegReg(kOpAdc, rl_result.high_reg, sign_reg);
+ OpRegReg(kOpXor, rl_result.low_reg, sign_reg);
+ OpRegReg(kOpXor, rl_result.high_reg, sign_reg);
+ StoreValueWide(rl_dest, rl_result);
+ return true;
+ }
+}
+
+bool Mir2Lir::GenInlinedFloatCvt(CallInfo* info)
+{
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ RegLocation rl_src = info->args[0];
+ RegLocation rl_dest = InlineTarget(info);
+ StoreValue(rl_dest, rl_src);
+ return true;
+}
+
+bool Mir2Lir::GenInlinedDoubleCvt(CallInfo* info)
+{
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ RegLocation rl_src = info->args[0];
+ RegLocation rl_dest = InlineTargetWide(info);
+ StoreValueWide(rl_dest, rl_src);
+ return true;
+}
+
+/*
+ * Fast string.index_of(I) & (II). Tests for simple case of char <= 0xffff,
+ * otherwise bails to standard library code.
+ */
+bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based)
+{
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ ClobberCalleeSave();
+ LockCallTemps(); // Using fixed registers
+ int reg_ptr = TargetReg(kArg0);
+ int reg_char = TargetReg(kArg1);
+ int reg_start = TargetReg(kArg2);
+
+ RegLocation rl_obj = info->args[0];
+ RegLocation rl_char = info->args[1];
+ RegLocation rl_start = info->args[2];
+ LoadValueDirectFixed(rl_obj, reg_ptr);
+ LoadValueDirectFixed(rl_char, reg_char);
+ if (zero_based) {
+ LoadConstant(reg_start, 0);
+ } else {
+ LoadValueDirectFixed(rl_start, reg_start);
+ }
+ int r_tgt = (cu_->instruction_set != kX86) ? LoadHelper(ENTRYPOINT_OFFSET(pIndexOf)) : 0;
+ GenNullCheck(rl_obj.s_reg_low, reg_ptr, info->opt_flags);
+ LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast<uintptr_t>(info));
+ intrinsic_launchpads_.Insert(launch_pad);
+ OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, launch_pad);
+ // NOTE: not a safepoint
+ if (cu_->instruction_set != kX86) {
+ OpReg(kOpBlx, r_tgt);
+ } else {
+ OpThreadMem(kOpBlx, ENTRYPOINT_OFFSET(pIndexOf));
+ }
+ LIR* resume_tgt = NewLIR0(kPseudoTargetLabel);
+ launch_pad->operands[2] = reinterpret_cast<uintptr_t>(resume_tgt);
+ // Record that we've already inlined & null checked
+ info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK);
+ RegLocation rl_return = GetReturn(false);
+ RegLocation rl_dest = InlineTarget(info);
+ StoreValue(rl_dest, rl_return);
+ return true;
+}
+
+/* Fast string.compareTo(Ljava/lang/string;)I. */
+bool Mir2Lir::GenInlinedStringCompareTo(CallInfo* info)
+{
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ ClobberCalleeSave();
+ LockCallTemps(); // Using fixed registers
+ int reg_this = TargetReg(kArg0);
+ int reg_cmp = TargetReg(kArg1);
+
+ RegLocation rl_this = info->args[0];
+ RegLocation rl_cmp = info->args[1];
+ LoadValueDirectFixed(rl_this, reg_this);
+ LoadValueDirectFixed(rl_cmp, reg_cmp);
+ int r_tgt = (cu_->instruction_set != kX86) ?
+ LoadHelper(ENTRYPOINT_OFFSET(pStringCompareTo)) : 0;
+ GenNullCheck(rl_this.s_reg_low, reg_this, info->opt_flags);
+ //TUNING: check if rl_cmp.s_reg_low is already null checked
+ LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast<uintptr_t>(info));
+ intrinsic_launchpads_.Insert(launch_pad);
+ OpCmpImmBranch(kCondEq, reg_cmp, 0, launch_pad);
+ // NOTE: not a safepoint
+ if (cu_->instruction_set != kX86) {
+ OpReg(kOpBlx, r_tgt);
+ } else {
+ OpThreadMem(kOpBlx, ENTRYPOINT_OFFSET(pStringCompareTo));
+ }
+ launch_pad->operands[2] = 0; // No return possible
+ // Record that we've already inlined & null checked
+ info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK);
+ RegLocation rl_return = GetReturn(false);
+ RegLocation rl_dest = InlineTarget(info);
+ StoreValue(rl_dest, rl_return);
+ return true;
+}
+
+bool Mir2Lir::GenInlinedCurrentThread(CallInfo* info) {
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int offset = Thread::PeerOffset().Int32Value();
+ if (cu_->instruction_set == kThumb2 || cu_->instruction_set == kMips) {
+ LoadWordDisp(TargetReg(kSelf), offset, rl_result.low_reg);
+ } else {
+ CHECK(cu_->instruction_set == kX86);
+ ((X86Mir2Lir*)this)->OpRegThreadMem(kOpMov, rl_result.low_reg, offset);
+ }
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+bool Mir2Lir::GenInlinedUnsafeGet(CallInfo* info,
+ bool is_long, bool is_volatile) {
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ // Unused - RegLocation rl_src_unsafe = info->args[0];
+ RegLocation rl_src_obj = info->args[1]; // Object
+ RegLocation rl_src_offset = info->args[2]; // long low
+ rl_src_offset.wide = 0; // ignore high half in info->args[3]
+ RegLocation rl_dest = InlineTarget(info); // result reg
+ if (is_volatile) {
+ GenMemBarrier(kLoadLoad);
+ }
+ RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg);
+ RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (is_long) {
+ OpRegReg(kOpAdd, rl_object.low_reg, rl_offset.low_reg);
+ LoadBaseDispWide(rl_object.low_reg, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ LoadBaseIndexed(rl_object.low_reg, rl_offset.low_reg, rl_result.low_reg, 0, kWord);
+ StoreValue(rl_dest, rl_result);
+ }
+ return true;
+}
+
+bool Mir2Lir::GenInlinedUnsafePut(CallInfo* info, bool is_long,
+ bool is_object, bool is_volatile, bool is_ordered) {
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ if (cu_->instruction_set == kX86 && is_object) {
+ // TODO: fix X86, it exhausts registers for card marking.
+ return false;
+ }
+ // Unused - RegLocation rl_src_unsafe = info->args[0];
+ RegLocation rl_src_obj = info->args[1]; // Object
+ RegLocation rl_src_offset = info->args[2]; // long low
+ rl_src_offset.wide = 0; // ignore high half in info->args[3]
+ RegLocation rl_src_value = info->args[4]; // value to store
+ if (is_volatile || is_ordered) {
+ GenMemBarrier(kStoreStore);
+ }
+ RegLocation rl_object = LoadValue(rl_src_obj, kCoreReg);
+ RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg);
+ RegLocation rl_value;
+ if (is_long) {
+ rl_value = LoadValueWide(rl_src_value, kCoreReg);
+ OpRegReg(kOpAdd, rl_object.low_reg, rl_offset.low_reg);
+ StoreBaseDispWide(rl_object.low_reg, 0, rl_value.low_reg, rl_value.high_reg);
+ } else {
+ rl_value = LoadValue(rl_src_value, kCoreReg);
+ StoreBaseIndexed(rl_object.low_reg, rl_offset.low_reg, rl_value.low_reg, 0, kWord);
+ }
+ if (is_volatile) {
+ GenMemBarrier(kStoreLoad);
+ }
+ if (is_object) {
+ MarkGCCard(rl_value.low_reg, rl_object.low_reg);
+ }
+ return true;
+}
+
+bool Mir2Lir::GenIntrinsic(CallInfo* info)
+{
+ if (info->opt_flags & MIR_INLINED) {
+ return false;
+ }
+ /*
+ * TODO: move these to a target-specific structured constant array
+ * and use a generic match function. The list of intrinsics may be
+ * slightly different depending on target.
+ * TODO: Fold this into a matching function that runs during
+ * basic block building. This should be part of the action for
+ * small method inlining and recognition of the special object init
+ * method. By doing this during basic block construction, we can also
+ * take advantage of/generate new useful dataflow info.
+ */
+ StringPiece tgt_methods_declaring_class(
+ cu_->dex_file->GetMethodDeclaringClassDescriptor(cu_->dex_file->GetMethodId(info->index)));
+ if (tgt_methods_declaring_class.starts_with("Ljava/lang/Double;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "long java.lang.Double.doubleToRawLongBits(double)") {
+ return GenInlinedDoubleCvt(info);
+ }
+ if (tgt_method == "double java.lang.Double.longBitsToDouble(long)") {
+ return GenInlinedDoubleCvt(info);
+ }
+ } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Float;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "int java.lang.Float.float_to_raw_int_bits(float)") {
+ return GenInlinedFloatCvt(info);
+ }
+ if (tgt_method == "float java.lang.Float.intBitsToFloat(int)") {
+ return GenInlinedFloatCvt(info);
+ }
+ } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Math;") ||
+ tgt_methods_declaring_class.starts_with("Ljava/lang/StrictMath;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "int java.lang.Math.abs(int)" ||
+ tgt_method == "int java.lang.StrictMath.abs(int)") {
+ return GenInlinedAbsInt(info);
+ }
+ if (tgt_method == "long java.lang.Math.abs(long)" ||
+ tgt_method == "long java.lang.StrictMath.abs(long)") {
+ return GenInlinedAbsLong(info);
+ }
+ if (tgt_method == "int java.lang.Math.max(int, int)" ||
+ tgt_method == "int java.lang.StrictMath.max(int, int)") {
+ return GenInlinedMinMaxInt(info, false /* is_min */);
+ }
+ if (tgt_method == "int java.lang.Math.min(int, int)" ||
+ tgt_method == "int java.lang.StrictMath.min(int, int)") {
+ return GenInlinedMinMaxInt(info, true /* is_min */);
+ }
+ if (tgt_method == "double java.lang.Math.sqrt(double)" ||
+ tgt_method == "double java.lang.StrictMath.sqrt(double)") {
+ return GenInlinedSqrt(info);
+ }
+ } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/String;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "char java.lang.String.charAt(int)") {
+ return GenInlinedCharAt(info);
+ }
+ if (tgt_method == "int java.lang.String.compareTo(java.lang.String)") {
+ return GenInlinedStringCompareTo(info);
+ }
+ if (tgt_method == "boolean java.lang.String.is_empty()") {
+ return GenInlinedStringIsEmptyOrLength(info, true /* is_empty */);
+ }
+ if (tgt_method == "int java.lang.String.index_of(int, int)") {
+ return GenInlinedIndexOf(info, false /* base 0 */);
+ }
+ if (tgt_method == "int java.lang.String.index_of(int)") {
+ return GenInlinedIndexOf(info, true /* base 0 */);
+ }
+ if (tgt_method == "int java.lang.String.length()") {
+ return GenInlinedStringIsEmptyOrLength(info, false /* is_empty */);
+ }
+ } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Thread;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "java.lang.Thread java.lang.Thread.currentThread()") {
+ return GenInlinedCurrentThread(info);
+ }
+ } else if (tgt_methods_declaring_class.starts_with("Lsun/misc/Unsafe;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)") {
+ return GenInlinedCas32(info, false);
+ }
+ if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapObject(java.lang.Object, long, java.lang.Object, java.lang.Object)") {
+ return GenInlinedCas32(info, true);
+ }
+ if (tgt_method == "int sun.misc.Unsafe.getInt(java.lang.Object, long)") {
+ return GenInlinedUnsafeGet(info, false /* is_long */, false /* is_volatile */);
+ }
+ if (tgt_method == "int sun.misc.Unsafe.getIntVolatile(java.lang.Object, long)") {
+ return GenInlinedUnsafeGet(info, false /* is_long */, true /* is_volatile */);
+ }
+ if (tgt_method == "void sun.misc.Unsafe.putInt(java.lang.Object, long, int)") {
+ return GenInlinedUnsafePut(info, false /* is_long */, false /* is_object */,
+ false /* is_volatile */, false /* is_ordered */);
+ }
+ if (tgt_method == "void sun.misc.Unsafe.putIntVolatile(java.lang.Object, long, int)") {
+ return GenInlinedUnsafePut(info, false /* is_long */, false /* is_object */,
+ true /* is_volatile */, false /* is_ordered */);
+ }
+ if (tgt_method == "void sun.misc.Unsafe.putOrderedInt(java.lang.Object, long, int)") {
+ return GenInlinedUnsafePut(info, false /* is_long */, false /* is_object */,
+ false /* is_volatile */, true /* is_ordered */);
+ }
+ if (tgt_method == "long sun.misc.Unsafe.getLong(java.lang.Object, long)") {
+ return GenInlinedUnsafeGet(info, true /* is_long */, false /* is_volatile */);
+ }
+ if (tgt_method == "long sun.misc.Unsafe.getLongVolatile(java.lang.Object, long)") {
+ return GenInlinedUnsafeGet(info, true /* is_long */, true /* is_volatile */);
+ }
+ if (tgt_method == "void sun.misc.Unsafe.putLong(java.lang.Object, long, long)") {
+ return GenInlinedUnsafePut(info, true /* is_long */, false /* is_object */,
+ false /* is_volatile */, false /* is_ordered */);
+ }
+ if (tgt_method == "void sun.misc.Unsafe.putLongVolatile(java.lang.Object, long, long)") {
+ return GenInlinedUnsafePut(info, true /* is_long */, false /* is_object */,
+ true /* is_volatile */, false /* is_ordered */);
+ }
+ if (tgt_method == "void sun.misc.Unsafe.putOrderedLong(java.lang.Object, long, long)") {
+ return GenInlinedUnsafePut(info, true /* is_long */, false /* is_object */,
+ false /* is_volatile */, true /* is_ordered */);
+ }
+ if (tgt_method == "java.lang.Object sun.misc.Unsafe.getObject(java.lang.Object, long)") {
+ return GenInlinedUnsafeGet(info, false /* is_long */, false /* is_volatile */);
+ }
+ if (tgt_method == "java.lang.Object sun.misc.Unsafe.getObjectVolatile(java.lang.Object, long)") {
+ return GenInlinedUnsafeGet(info, false /* is_long */, true /* is_volatile */);
+ }
+ if (tgt_method == "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)") {
+ return GenInlinedUnsafePut(info, false /* is_long */, true /* is_object */,
+ false /* is_volatile */, false /* is_ordered */);
+ }
+ if (tgt_method == "void sun.misc.Unsafe.putObjectVolatile(java.lang.Object, long, java.lang.Object)") {
+ return GenInlinedUnsafePut(info, false /* is_long */, true /* is_object */,
+ true /* is_volatile */, false /* is_ordered */);
+ }
+ if (tgt_method == "void sun.misc.Unsafe.putOrderedObject(java.lang.Object, long, java.lang.Object)") {
+ return GenInlinedUnsafePut(info, false /* is_long */, true /* is_object */,
+ false /* is_volatile */, true /* is_ordered */);
+ }
+ }
+ return false;
+}
+
+void Mir2Lir::GenInvoke(CallInfo* info)
+{
+ if (GenIntrinsic(info)) {
+ return;
+ }
+ InvokeType original_type = info->type; // avoiding mutation by ComputeInvokeInfo
+ int call_state = 0;
+ LIR* null_ck;
+ LIR** p_null_ck = NULL;
+ NextCallInsn next_call_insn;
+ FlushAllRegs(); /* Everything to home location */
+ // Explicit register usage
+ LockCallTemps();
+
+ DexCompilationUnit* cUnit = mir_graph_->GetCurrentDexCompilationUnit();
+ MethodReference target_method(cUnit->GetDexFile(), info->index);
+ int vtable_idx;
+ uintptr_t direct_code;
+ uintptr_t direct_method;
+ bool skip_this;
+ bool fast_path =
+ cu_->compiler_driver->ComputeInvokeInfo(mir_graph_->GetCurrentDexCompilationUnit(),
+ current_dalvik_offset_,
+ info->type, target_method,
+ vtable_idx,
+ direct_code, direct_method,
+ true) && !SLOW_INVOKE_PATH;
+ if (info->type == kInterface) {
+ if (fast_path) {
+ p_null_ck = &null_ck;
+ }
+ next_call_insn = fast_path ? NextInterfaceCallInsn : NextInterfaceCallInsnWithAccessCheck;
+ skip_this = false;
+ } else if (info->type == kDirect) {
+ if (fast_path) {
+ p_null_ck = &null_ck;
+ }
+ next_call_insn = fast_path ? NextSDCallInsn : NextDirectCallInsnSP;
+ skip_this = false;
+ } else if (info->type == kStatic) {
+ next_call_insn = fast_path ? NextSDCallInsn : NextStaticCallInsnSP;
+ skip_this = false;
+ } else if (info->type == kSuper) {
+ DCHECK(!fast_path); // Fast path is a direct call.
+ next_call_insn = NextSuperCallInsnSP;
+ skip_this = false;
+ } else {
+ DCHECK_EQ(info->type, kVirtual);
+ next_call_insn = fast_path ? NextVCallInsn : NextVCallInsnSP;
+ skip_this = fast_path;
+ }
+ if (!info->is_range) {
+ call_state = GenDalvikArgsNoRange(info, call_state, p_null_ck,
+ next_call_insn, target_method,
+ vtable_idx, direct_code, direct_method,
+ original_type, skip_this);
+ } else {
+ call_state = GenDalvikArgsRange(info, call_state, p_null_ck,
+ next_call_insn, target_method, vtable_idx,
+ direct_code, direct_method, original_type,
+ skip_this);
+ }
+ // Finish up any of the call sequence not interleaved in arg loading
+ while (call_state >= 0) {
+ call_state = next_call_insn(cu_, info, call_state, target_method,
+ vtable_idx, direct_code, direct_method,
+ original_type);
+ }
+ LIR* call_inst;
+ if (cu_->instruction_set != kX86) {
+ call_inst = OpReg(kOpBlx, TargetReg(kInvokeTgt));
+ } else {
+ if (fast_path && info->type != kInterface) {
+ call_inst = OpMem(kOpBlx, TargetReg(kArg0),
+ mirror::AbstractMethod::GetEntryPointFromCompiledCodeOffset().Int32Value());
+ } else {
+ int trampoline = 0;
+ switch (info->type) {
+ case kInterface:
+ trampoline = fast_path ? ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline)
+ : ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck);
+ break;
+ case kDirect:
+ trampoline = ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck);
+ break;
+ case kStatic:
+ trampoline = ENTRYPOINT_OFFSET(pInvokeStaticTrampolineWithAccessCheck);
+ break;
+ case kSuper:
+ trampoline = ENTRYPOINT_OFFSET(pInvokeSuperTrampolineWithAccessCheck);
+ break;
+ case kVirtual:
+ trampoline = ENTRYPOINT_OFFSET(pInvokeVirtualTrampolineWithAccessCheck);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke type";
+ }
+ call_inst = OpThreadMem(kOpBlx, trampoline);
+ }
+ }
+ MarkSafepointPC(call_inst);
+
+ ClobberCalleeSave();
+ if (info->result.location != kLocInvalid) {
+ // We have a following MOVE_RESULT - do it now.
+ if (info->result.wide) {
+ RegLocation ret_loc = GetReturnWide(info->result.fp);
+ StoreValueWide(info->result, ret_loc);
+ } else {
+ RegLocation ret_loc = GetReturn(info->result.fp);
+ StoreValue(info->result, ret_loc);
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/gen_loadstore.cc b/compiler/dex/quick/gen_loadstore.cc
new file mode 100644
index 0000000..6a25c1d
--- /dev/null
+++ b/compiler/dex/quick/gen_loadstore.cc
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2011 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 "dex/compiler_ir.h"
+#include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "invoke_type.h"
+
+namespace art {
+
+/* This file contains target-independent codegen and support. */
+
+/*
+ * Load an immediate value into a fixed or temp register. Target
+ * register is clobbered, and marked in_use.
+ */
+LIR* Mir2Lir::LoadConstant(int r_dest, int value)
+{
+ if (IsTemp(r_dest)) {
+ Clobber(r_dest);
+ MarkInUse(r_dest);
+ }
+ return LoadConstantNoClobber(r_dest, value);
+}
+
+/*
+ * Temporary workaround for Issue 7250540. If we're loading a constant zero into a
+ * promoted floating point register, also copy a zero into the int/ref identity of
+ * that sreg.
+ */
+void Mir2Lir::Workaround7250540(RegLocation rl_dest, int zero_reg)
+{
+ if (rl_dest.fp) {
+ int pmap_index = SRegToPMap(rl_dest.s_reg_low);
+ if (promotion_map_[pmap_index].fp_location == kLocPhysReg) {
+ // Now, determine if this vreg is ever used as a reference. If not, we're done.
+ bool used_as_reference = false;
+ int base_vreg = mir_graph_->SRegToVReg(rl_dest.s_reg_low);
+ for (int i = 0; !used_as_reference && (i < mir_graph_->GetNumSSARegs()); i++) {
+ if (mir_graph_->SRegToVReg(mir_graph_->reg_location_[i].s_reg_low) == base_vreg) {
+ used_as_reference |= mir_graph_->reg_location_[i].ref;
+ }
+ }
+ if (!used_as_reference) {
+ return;
+ }
+ int temp_reg = zero_reg;
+ if (temp_reg == INVALID_REG) {
+ temp_reg = AllocTemp();
+ LoadConstant(temp_reg, 0);
+ }
+ if (promotion_map_[pmap_index].core_location == kLocPhysReg) {
+ // Promoted - just copy in a zero
+ OpRegCopy(promotion_map_[pmap_index].core_reg, temp_reg);
+ } else {
+ // Lives in the frame, need to store.
+ StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), temp_reg, kWord);
+ }
+ if (zero_reg == INVALID_REG) {
+ FreeTemp(temp_reg);
+ }
+ }
+ }
+}
+
+/* Load a word at base + displacement. Displacement must be word multiple */
+LIR* Mir2Lir::LoadWordDisp(int rBase, int displacement, int r_dest)
+{
+ return LoadBaseDisp(rBase, displacement, r_dest, kWord,
+ INVALID_SREG);
+}
+
+LIR* Mir2Lir::StoreWordDisp(int rBase, int displacement, int r_src)
+{
+ return StoreBaseDisp(rBase, displacement, r_src, kWord);
+}
+
+/*
+ * Load a Dalvik register into a physical register. Take care when
+ * using this routine, as it doesn't perform any bookkeeping regarding
+ * register liveness. That is the responsibility of the caller.
+ */
+void Mir2Lir::LoadValueDirect(RegLocation rl_src, int r_dest)
+{
+ rl_src = UpdateLoc(rl_src);
+ if (rl_src.location == kLocPhysReg) {
+ OpRegCopy(r_dest, rl_src.low_reg);
+ } else if (IsInexpensiveConstant(rl_src)) {
+ LoadConstantNoClobber(r_dest, mir_graph_->ConstantValue(rl_src));
+ } else {
+ DCHECK((rl_src.location == kLocDalvikFrame) ||
+ (rl_src.location == kLocCompilerTemp));
+ LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest);
+ }
+}
+
+/*
+ * Similar to LoadValueDirect, but clobbers and allocates the target
+ * register. Should be used when loading to a fixed register (for example,
+ * loading arguments to an out of line call.
+ */
+void Mir2Lir::LoadValueDirectFixed(RegLocation rl_src, int r_dest)
+{
+ Clobber(r_dest);
+ MarkInUse(r_dest);
+ LoadValueDirect(rl_src, r_dest);
+}
+
+/*
+ * Load a Dalvik register pair into a physical register[s]. Take care when
+ * using this routine, as it doesn't perform any bookkeeping regarding
+ * register liveness. That is the responsibility of the caller.
+ */
+void Mir2Lir::LoadValueDirectWide(RegLocation rl_src, int reg_lo,
+ int reg_hi)
+{
+ rl_src = UpdateLocWide(rl_src);
+ if (rl_src.location == kLocPhysReg) {
+ OpRegCopyWide(reg_lo, reg_hi, rl_src.low_reg, rl_src.high_reg);
+ } else if (IsInexpensiveConstant(rl_src)) {
+ LoadConstantWide(reg_lo, reg_hi, mir_graph_->ConstantValueWide(rl_src));
+ } else {
+ DCHECK((rl_src.location == kLocDalvikFrame) ||
+ (rl_src.location == kLocCompilerTemp));
+ LoadBaseDispWide(TargetReg(kSp), SRegOffset(rl_src.s_reg_low),
+ reg_lo, reg_hi, INVALID_SREG);
+ }
+}
+
+/*
+ * Similar to LoadValueDirect, but clobbers and allocates the target
+ * registers. Should be used when loading to a fixed registers (for example,
+ * loading arguments to an out of line call.
+ */
+void Mir2Lir::LoadValueDirectWideFixed(RegLocation rl_src, int reg_lo,
+ int reg_hi)
+{
+ Clobber(reg_lo);
+ Clobber(reg_hi);
+ MarkInUse(reg_lo);
+ MarkInUse(reg_hi);
+ LoadValueDirectWide(rl_src, reg_lo, reg_hi);
+}
+
+RegLocation Mir2Lir::LoadValue(RegLocation rl_src, RegisterClass op_kind)
+{
+ rl_src = EvalLoc(rl_src, op_kind, false);
+ if (IsInexpensiveConstant(rl_src) || rl_src.location != kLocPhysReg) {
+ LoadValueDirect(rl_src, rl_src.low_reg);
+ rl_src.location = kLocPhysReg;
+ MarkLive(rl_src.low_reg, rl_src.s_reg_low);
+ }
+ return rl_src;
+}
+
+void Mir2Lir::StoreValue(RegLocation rl_dest, RegLocation rl_src)
+{
+ /*
+ * Sanity checking - should never try to store to the same
+ * ssa name during the compilation of a single instruction
+ * without an intervening ClobberSReg().
+ */
+ if (kIsDebugBuild) {
+ DCHECK((live_sreg_ == INVALID_SREG) ||
+ (rl_dest.s_reg_low != live_sreg_));
+ live_sreg_ = rl_dest.s_reg_low;
+ }
+ LIR* def_start;
+ LIR* def_end;
+ DCHECK(!rl_dest.wide);
+ DCHECK(!rl_src.wide);
+ rl_src = UpdateLoc(rl_src);
+ rl_dest = UpdateLoc(rl_dest);
+ if (rl_src.location == kLocPhysReg) {
+ if (IsLive(rl_src.low_reg) ||
+ IsPromoted(rl_src.low_reg) ||
+ (rl_dest.location == kLocPhysReg)) {
+ // Src is live/promoted or Dest has assigned reg.
+ rl_dest = EvalLoc(rl_dest, kAnyReg, false);
+ OpRegCopy(rl_dest.low_reg, rl_src.low_reg);
+ } else {
+ // Just re-assign the registers. Dest gets Src's regs
+ rl_dest.low_reg = rl_src.low_reg;
+ Clobber(rl_src.low_reg);
+ }
+ } else {
+ // Load Src either into promoted Dest or temps allocated for Dest
+ rl_dest = EvalLoc(rl_dest, kAnyReg, false);
+ LoadValueDirect(rl_src, rl_dest.low_reg);
+ }
+
+ // Dest is now live and dirty (until/if we flush it to home location)
+ MarkLive(rl_dest.low_reg, rl_dest.s_reg_low);
+ MarkDirty(rl_dest);
+
+
+ ResetDefLoc(rl_dest);
+ if (IsDirty(rl_dest.low_reg) &&
+ oat_live_out(rl_dest.s_reg_low)) {
+ def_start = last_lir_insn_;
+ StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low),
+ rl_dest.low_reg, kWord);
+ MarkClean(rl_dest);
+ def_end = last_lir_insn_;
+ if (!rl_dest.ref) {
+ // Exclude references from store elimination
+ MarkDef(rl_dest, def_start, def_end);
+ }
+ }
+}
+
+RegLocation Mir2Lir::LoadValueWide(RegLocation rl_src, RegisterClass op_kind)
+{
+ DCHECK(rl_src.wide);
+ rl_src = EvalLoc(rl_src, op_kind, false);
+ if (IsInexpensiveConstant(rl_src) || rl_src.location != kLocPhysReg) {
+ LoadValueDirectWide(rl_src, rl_src.low_reg, rl_src.high_reg);
+ rl_src.location = kLocPhysReg;
+ MarkLive(rl_src.low_reg, rl_src.s_reg_low);
+ MarkLive(rl_src.high_reg, GetSRegHi(rl_src.s_reg_low));
+ }
+ return rl_src;
+}
+
+void Mir2Lir::StoreValueWide(RegLocation rl_dest, RegLocation rl_src)
+{
+ /*
+ * Sanity checking - should never try to store to the same
+ * ssa name during the compilation of a single instruction
+ * without an intervening ClobberSReg().
+ */
+ if (kIsDebugBuild) {
+ DCHECK((live_sreg_ == INVALID_SREG) ||
+ (rl_dest.s_reg_low != live_sreg_));
+ live_sreg_ = rl_dest.s_reg_low;
+ }
+ LIR* def_start;
+ LIR* def_end;
+ DCHECK_EQ(IsFpReg(rl_src.low_reg), IsFpReg(rl_src.high_reg));
+ DCHECK(rl_dest.wide);
+ DCHECK(rl_src.wide);
+ if (rl_src.location == kLocPhysReg) {
+ if (IsLive(rl_src.low_reg) ||
+ IsLive(rl_src.high_reg) ||
+ IsPromoted(rl_src.low_reg) ||
+ IsPromoted(rl_src.high_reg) ||
+ (rl_dest.location == kLocPhysReg)) {
+ // Src is live or promoted or Dest has assigned reg.
+ rl_dest = EvalLoc(rl_dest, kAnyReg, false);
+ OpRegCopyWide(rl_dest.low_reg, rl_dest.high_reg,
+ rl_src.low_reg, rl_src.high_reg);
+ } else {
+ // Just re-assign the registers. Dest gets Src's regs
+ rl_dest.low_reg = rl_src.low_reg;
+ rl_dest.high_reg = rl_src.high_reg;
+ Clobber(rl_src.low_reg);
+ Clobber(rl_src.high_reg);
+ }
+ } else {
+ // Load Src either into promoted Dest or temps allocated for Dest
+ rl_dest = EvalLoc(rl_dest, kAnyReg, false);
+ LoadValueDirectWide(rl_src, rl_dest.low_reg, rl_dest.high_reg);
+ }
+
+ // Dest is now live and dirty (until/if we flush it to home location)
+ MarkLive(rl_dest.low_reg, rl_dest.s_reg_low);
+ MarkLive(rl_dest.high_reg, GetSRegHi(rl_dest.s_reg_low));
+ MarkDirty(rl_dest);
+ MarkPair(rl_dest.low_reg, rl_dest.high_reg);
+
+
+ ResetDefLocWide(rl_dest);
+ if ((IsDirty(rl_dest.low_reg) ||
+ IsDirty(rl_dest.high_reg)) &&
+ (oat_live_out(rl_dest.s_reg_low) ||
+ oat_live_out(GetSRegHi(rl_dest.s_reg_low)))) {
+ def_start = last_lir_insn_;
+ DCHECK_EQ((mir_graph_->SRegToVReg(rl_dest.s_reg_low)+1),
+ mir_graph_->SRegToVReg(GetSRegHi(rl_dest.s_reg_low)));
+ StoreBaseDispWide(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low),
+ rl_dest.low_reg, rl_dest.high_reg);
+ MarkClean(rl_dest);
+ def_end = last_lir_insn_;
+ MarkDefWide(rl_dest, def_start, def_end);
+ }
+}
+
+/* Utilities to load the current Method* */
+void Mir2Lir::LoadCurrMethodDirect(int r_tgt)
+{
+ LoadValueDirectFixed(mir_graph_->GetMethodLoc(), r_tgt);
+}
+
+RegLocation Mir2Lir::LoadCurrMethod()
+{
+ return LoadValue(mir_graph_->GetMethodLoc(), kCoreReg);
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc
new file mode 100644
index 0000000..ac654d8
--- /dev/null
+++ b/compiler/dex/quick/local_optimizations.cc
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2011 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 "dex/compiler_internals.h"
+
+namespace art {
+
+#define DEBUG_OPT(X)
+
+/* Check RAW, WAR, and RAW dependency on the register operands */
+#define CHECK_REG_DEP(use, def, check) ((def & check->use_mask) || \
+ ((use | def) & check->def_mask))
+
+/* Scheduler heuristics */
+#define MAX_HOIST_DISTANCE 20
+#define LDLD_DISTANCE 4
+#define LD_LATENCY 2
+
+static bool IsDalvikRegisterClobbered(LIR* lir1, LIR* lir2)
+{
+ int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->alias_info);
+ int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->alias_info);
+ int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->alias_info);
+ int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->alias_info);
+
+ return (reg1Lo == reg2Lo) || (reg1Lo == reg2Hi) || (reg1Hi == reg2Lo);
+}
+
+/* Convert a more expensive instruction (ie load) into a move */
+void Mir2Lir::ConvertMemOpIntoMove(LIR* orig_lir, int dest, int src)
+{
+ /* Insert a move to replace the load */
+ LIR* move_lir;
+ move_lir = OpRegCopyNoInsert(dest, src);
+ /*
+ * Insert the converted instruction after the original since the
+ * optimization is scannng in the top-down order and the new instruction
+ * will need to be re-checked (eg the new dest clobbers the src used in
+ * this_lir).
+ */
+ InsertLIRAfter(orig_lir, move_lir);
+}
+
+/*
+ * Perform a pass of top-down walk, from the second-last instruction in the
+ * superblock, to eliminate redundant loads and stores.
+ *
+ * An earlier load can eliminate a later load iff
+ * 1) They are must-aliases
+ * 2) The native register is not clobbered in between
+ * 3) The memory location is not written to in between
+ *
+ * An earlier store can eliminate a later load iff
+ * 1) They are must-aliases
+ * 2) The native register is not clobbered in between
+ * 3) The memory location is not written to in between
+ *
+ * A later store can be eliminated by an earlier store iff
+ * 1) They are must-aliases
+ * 2) The memory location is not written to in between
+ */
+void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir)
+{
+ LIR* this_lir;
+
+ if (head_lir == tail_lir) return;
+
+ for (this_lir = PREV_LIR(tail_lir); this_lir != head_lir; this_lir = PREV_LIR(this_lir)) {
+
+ if (is_pseudo_opcode(this_lir->opcode)) continue;
+
+ int sink_distance = 0;
+
+ uint64_t target_flags = GetTargetInstFlags(this_lir->opcode);
+
+ /* Skip non-interesting instructions */
+ if ((this_lir->flags.is_nop == true) ||
+ (target_flags & IS_BRANCH) ||
+ ((target_flags & (REG_DEF0 | REG_DEF1)) == (REG_DEF0 | REG_DEF1)) || // Skip wide loads.
+ ((target_flags & (REG_USE0 | REG_USE1 | REG_USE2)) ==
+ (REG_USE0 | REG_USE1 | REG_USE2)) || // Skip wide stores.
+ !(target_flags & (IS_LOAD | IS_STORE))) {
+ continue;
+ }
+
+ int native_reg_id;
+ if (cu_->instruction_set == kX86) {
+ // If x86, location differs depending on whether memory/reg operation.
+ native_reg_id = (GetTargetInstFlags(this_lir->opcode) & IS_STORE) ? this_lir->operands[2]
+ : this_lir->operands[0];
+ } else {
+ native_reg_id = this_lir->operands[0];
+ }
+ bool is_this_lir_load = GetTargetInstFlags(this_lir->opcode) & IS_LOAD;
+ LIR* check_lir;
+ /* Use the mem mask to determine the rough memory location */
+ uint64_t this_mem_mask = (this_lir->use_mask | this_lir->def_mask) & ENCODE_MEM;
+
+ /*
+ * Currently only eliminate redundant ld/st for constant and Dalvik
+ * register accesses.
+ */
+ if (!(this_mem_mask & (ENCODE_LITERAL | ENCODE_DALVIK_REG))) continue;
+
+ uint64_t stop_def_reg_mask = this_lir->def_mask & ~ENCODE_MEM;
+ uint64_t stop_use_reg_mask;
+ if (cu_->instruction_set == kX86) {
+ stop_use_reg_mask = (IS_BRANCH | this_lir->use_mask) & ~ENCODE_MEM;
+ } else {
+ /*
+ * Add pc to the resource mask to prevent this instruction
+ * from sinking past branch instructions. Also take out the memory
+ * region bits since stop_mask is used to check data/control
+ * dependencies.
+ */
+ stop_use_reg_mask = (GetPCUseDefEncoding() | this_lir->use_mask) & ~ENCODE_MEM;
+ }
+
+ for (check_lir = NEXT_LIR(this_lir); check_lir != tail_lir; check_lir = NEXT_LIR(check_lir)) {
+
+ /*
+ * Skip already dead instructions (whose dataflow information is
+ * outdated and misleading).
+ */
+ if (check_lir->flags.is_nop || is_pseudo_opcode(check_lir->opcode)) continue;
+
+ uint64_t check_mem_mask = (check_lir->use_mask | check_lir->def_mask) & ENCODE_MEM;
+ uint64_t alias_condition = this_mem_mask & check_mem_mask;
+ bool stop_here = false;
+
+ /*
+ * Potential aliases seen - check the alias relations
+ */
+ uint64_t check_flags = GetTargetInstFlags(check_lir->opcode);
+ // TUNING: Support instructions with multiple register targets.
+ if ((check_flags & (REG_DEF0 | REG_DEF1)) == (REG_DEF0 | REG_DEF1)) {
+ stop_here = true;
+ } else if (check_mem_mask != ENCODE_MEM && alias_condition != 0) {
+ bool is_check_lir_load = check_flags & IS_LOAD;
+ if (alias_condition == ENCODE_LITERAL) {
+ /*
+ * Should only see literal loads in the instruction
+ * stream.
+ */
+ DCHECK(!(check_flags & IS_STORE));
+ /* Same value && same register type */
+ if (check_lir->alias_info == this_lir->alias_info &&
+ SameRegType(check_lir->operands[0], native_reg_id)) {
+ /*
+ * Different destination register - insert
+ * a move
+ */
+ if (check_lir->operands[0] != native_reg_id) {
+ ConvertMemOpIntoMove(check_lir, check_lir->operands[0], native_reg_id);
+ }
+ check_lir->flags.is_nop = true;
+ }
+ } else if (alias_condition == ENCODE_DALVIK_REG) {
+ /* Must alias */
+ if (check_lir->alias_info == this_lir->alias_info) {
+ /* Only optimize compatible registers */
+ bool reg_compatible = SameRegType(check_lir->operands[0], native_reg_id);
+ if ((is_this_lir_load && is_check_lir_load) ||
+ (!is_this_lir_load && is_check_lir_load)) {
+ /* RAR or RAW */
+ if (reg_compatible) {
+ /*
+ * Different destination register -
+ * insert a move
+ */
+ if (check_lir->operands[0] !=
+ native_reg_id) {
+ ConvertMemOpIntoMove(check_lir, check_lir->operands[0], native_reg_id);
+ }
+ check_lir->flags.is_nop = true;
+ } else {
+ /*
+ * Destinaions are of different types -
+ * something complicated going on so
+ * stop looking now.
+ */
+ stop_here = true;
+ }
+ } else if (is_this_lir_load && !is_check_lir_load) {
+ /* WAR - register value is killed */
+ stop_here = true;
+ } else if (!is_this_lir_load && !is_check_lir_load) {
+ /* WAW - nuke the earlier store */
+ this_lir->flags.is_nop = true;
+ stop_here = true;
+ }
+ /* Partial overlap */
+ } else if (IsDalvikRegisterClobbered(this_lir, check_lir)) {
+ /*
+ * It is actually ok to continue if check_lir
+ * is a read. But it is hard to make a test
+ * case for this so we just stop here to be
+ * conservative.
+ */
+ stop_here = true;
+ }
+ }
+ /* Memory content may be updated. Stop looking now. */
+ if (stop_here) {
+ break;
+ /* The check_lir has been transformed - check the next one */
+ } else if (check_lir->flags.is_nop) {
+ continue;
+ }
+ }
+
+
+ /*
+ * this and check LIRs have no memory dependency. Now check if
+ * their register operands have any RAW, WAR, and WAW
+ * dependencies. If so, stop looking.
+ */
+ if (stop_here == false) {
+ stop_here = CHECK_REG_DEP(stop_use_reg_mask, stop_def_reg_mask, check_lir);
+ }
+
+ if (stop_here == true) {
+ if (cu_->instruction_set == kX86) {
+ // Prevent stores from being sunk between ops that generate ccodes and
+ // ops that use them.
+ uint64_t flags = GetTargetInstFlags(check_lir->opcode);
+ if (sink_distance > 0 && (flags & IS_BRANCH) && (flags & USES_CCODES)) {
+ check_lir = PREV_LIR(check_lir);
+ sink_distance--;
+ }
+ }
+ DEBUG_OPT(dump_dependent_insn_pair(this_lir, check_lir, "REG CLOBBERED"));
+ /* Only sink store instructions */
+ if (sink_distance && !is_this_lir_load) {
+ LIR* new_store_lir =
+ static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocLIR));
+ *new_store_lir = *this_lir;
+ /*
+ * Stop point found - insert *before* the check_lir
+ * since the instruction list is scanned in the
+ * top-down order.
+ */
+ InsertLIRBefore(check_lir, new_store_lir);
+ this_lir->flags.is_nop = true;
+ }
+ break;
+ } else if (!check_lir->flags.is_nop) {
+ sink_distance++;
+ }
+ }
+ }
+}
+
+/*
+ * Perform a pass of bottom-up walk, from the second instruction in the
+ * superblock, to try to hoist loads to earlier slots.
+ */
+void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir)
+{
+ LIR* this_lir, *check_lir;
+ /*
+ * Store the list of independent instructions that can be hoisted past.
+ * Will decide the best place to insert later.
+ */
+ LIR* prev_inst_list[MAX_HOIST_DISTANCE];
+
+ /* Empty block */
+ if (head_lir == tail_lir) return;
+
+ /* Start from the second instruction */
+ for (this_lir = NEXT_LIR(head_lir); this_lir != tail_lir; this_lir = NEXT_LIR(this_lir)) {
+
+ if (is_pseudo_opcode(this_lir->opcode)) continue;
+
+ uint64_t target_flags = GetTargetInstFlags(this_lir->opcode);
+ /* Skip non-interesting instructions */
+ if ((this_lir->flags.is_nop == true) ||
+ ((target_flags & (REG_DEF0 | REG_DEF1)) == (REG_DEF0 | REG_DEF1)) ||
+ !(target_flags & IS_LOAD)) {
+ continue;
+ }
+
+ uint64_t stop_use_all_mask = this_lir->use_mask;
+
+ if (cu_->instruction_set != kX86) {
+ /*
+ * Branches for null/range checks are marked with the true resource
+ * bits, and loads to Dalvik registers, constant pools, and non-alias
+ * locations are safe to be hoisted. So only mark the heap references
+ * conservatively here.
+ */
+ if (stop_use_all_mask & ENCODE_HEAP_REF) {
+ stop_use_all_mask |= GetPCUseDefEncoding();
+ }
+ }
+
+ /* Similar as above, but just check for pure register dependency */
+ uint64_t stop_use_reg_mask = stop_use_all_mask & ~ENCODE_MEM;
+ uint64_t stop_def_reg_mask = this_lir->def_mask & ~ENCODE_MEM;
+
+ int next_slot = 0;
+ bool stop_here = false;
+
+ /* Try to hoist the load to a good spot */
+ for (check_lir = PREV_LIR(this_lir); check_lir != head_lir; check_lir = PREV_LIR(check_lir)) {
+
+ /*
+ * Skip already dead instructions (whose dataflow information is
+ * outdated and misleading).
+ */
+ if (check_lir->flags.is_nop) continue;
+
+ uint64_t check_mem_mask = check_lir->def_mask & ENCODE_MEM;
+ uint64_t alias_condition = stop_use_all_mask & check_mem_mask;
+ stop_here = false;
+
+ /* Potential WAR alias seen - check the exact relation */
+ if (check_mem_mask != ENCODE_MEM && alias_condition != 0) {
+ /* We can fully disambiguate Dalvik references */
+ if (alias_condition == ENCODE_DALVIK_REG) {
+ /* Must alias or partually overlap */
+ if ((check_lir->alias_info == this_lir->alias_info) ||
+ IsDalvikRegisterClobbered(this_lir, check_lir)) {
+ stop_here = true;
+ }
+ /* Conservatively treat all heap refs as may-alias */
+ } else {
+ DCHECK_EQ(alias_condition, ENCODE_HEAP_REF);
+ stop_here = true;
+ }
+ /* Memory content may be updated. Stop looking now. */
+ if (stop_here) {
+ prev_inst_list[next_slot++] = check_lir;
+ break;
+ }
+ }
+
+ if (stop_here == false) {
+ stop_here = CHECK_REG_DEP(stop_use_reg_mask, stop_def_reg_mask,
+ check_lir);
+ }
+
+ /*
+ * Store the dependent or non-pseudo/indepedent instruction to the
+ * list.
+ */
+ if (stop_here || !is_pseudo_opcode(check_lir->opcode)) {
+ prev_inst_list[next_slot++] = check_lir;
+ if (next_slot == MAX_HOIST_DISTANCE) break;
+ }
+
+ /* Found a new place to put the load - move it here */
+ if (stop_here == true) {
+ DEBUG_OPT(dump_dependent_insn_pair(check_lir, this_lir "HOIST STOP"));
+ break;
+ }
+ }
+
+ /*
+ * Reached the top - use head_lir as the dependent marker as all labels
+ * are barriers.
+ */
+ if (stop_here == false && next_slot < MAX_HOIST_DISTANCE) {
+ prev_inst_list[next_slot++] = head_lir;
+ }
+
+ /*
+ * At least one independent instruction is found. Scan in the reversed
+ * direction to find a beneficial slot.
+ */
+ if (next_slot >= 2) {
+ int first_slot = next_slot - 2;
+ int slot;
+ LIR* dep_lir = prev_inst_list[next_slot-1];
+ /* If there is ld-ld dependency, wait LDLD_DISTANCE cycles */
+ if (!is_pseudo_opcode(dep_lir->opcode) &&
+ (GetTargetInstFlags(dep_lir->opcode) & IS_LOAD)) {
+ first_slot -= LDLD_DISTANCE;
+ }
+ /*
+ * Make sure we check slot >= 0 since first_slot may be negative
+ * when the loop is first entered.
+ */
+ for (slot = first_slot; slot >= 0; slot--) {
+ LIR* cur_lir = prev_inst_list[slot];
+ LIR* prev_lir = prev_inst_list[slot+1];
+
+ /* Check the highest instruction */
+ if (prev_lir->def_mask == ENCODE_ALL) {
+ /*
+ * If the first instruction is a load, don't hoist anything
+ * above it since it is unlikely to be beneficial.
+ */
+ if (GetTargetInstFlags(cur_lir->opcode) & IS_LOAD) continue;
+ /*
+ * If the remaining number of slots is less than LD_LATENCY,
+ * insert the hoisted load here.
+ */
+ if (slot < LD_LATENCY) break;
+ }
+
+ // Don't look across a barrier label
+ if ((prev_lir->opcode == kPseudoTargetLabel) ||
+ (prev_lir->opcode == kPseudoSafepointPC) ||
+ (prev_lir->opcode == kPseudoBarrier)) {
+ break;
+ }
+
+ /*
+ * Try to find two instructions with load/use dependency until
+ * the remaining instructions are less than LD_LATENCY.
+ */
+ bool prev_is_load = is_pseudo_opcode(prev_lir->opcode) ? false :
+ (GetTargetInstFlags(prev_lir->opcode) & IS_LOAD);
+ if (((cur_lir->use_mask & prev_lir->def_mask) && prev_is_load) || (slot < LD_LATENCY)) {
+ break;
+ }
+ }
+
+ /* Found a slot to hoist to */
+ if (slot >= 0) {
+ LIR* cur_lir = prev_inst_list[slot];
+ LIR* new_load_lir =
+ static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocLIR));
+ *new_load_lir = *this_lir;
+ /*
+ * Insertion is guaranteed to succeed since check_lir
+ * is never the first LIR on the list
+ */
+ InsertLIRBefore(cur_lir, new_load_lir);
+ this_lir->flags.is_nop = true;
+ }
+ }
+ }
+}
+
+void Mir2Lir::ApplyLocalOptimizations(LIR* head_lir, LIR* tail_lir)
+{
+ if (!(cu_->disable_opt & (1 << kLoadStoreElimination))) {
+ ApplyLoadStoreElimination(head_lir, tail_lir);
+ }
+ if (!(cu_->disable_opt & (1 << kLoadHoisting))) {
+ ApplyLoadHoisting(head_lir, tail_lir);
+ }
+}
+
+/*
+ * Nop any unconditional branches that go to the next instruction.
+ * Note: new redundant branches may be inserted later, and we'll
+ * use a check in final instruction assembly to nop those out.
+ */
+void Mir2Lir::RemoveRedundantBranches()
+{
+ LIR* this_lir;
+
+ for (this_lir = first_lir_insn_; this_lir != last_lir_insn_; this_lir = NEXT_LIR(this_lir)) {
+
+ /* Branch to the next instruction */
+ if (IsUnconditionalBranch(this_lir)) {
+ LIR* next_lir = this_lir;
+
+ while (true) {
+ next_lir = NEXT_LIR(next_lir);
+
+ /*
+ * Is the branch target the next instruction?
+ */
+ if (next_lir == this_lir->target) {
+ this_lir->flags.is_nop = true;
+ break;
+ }
+
+ /*
+ * Found real useful stuff between the branch and the target.
+ * Need to explicitly check the last_lir_insn_ here because it
+ * might be the last real instruction.
+ */
+ if (!is_pseudo_opcode(next_lir->opcode) ||
+ (next_lir == last_lir_insn_))
+ break;
+ }
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/mips/README.mips b/compiler/dex/quick/mips/README.mips
new file mode 100644
index 0000000..061c157
--- /dev/null
+++ b/compiler/dex/quick/mips/README.mips
@@ -0,0 +1,57 @@
+ Notes on the Mips target (3/4/2012)
+ -----------------------------------
+
+Testing
+
+The initial implementation of Mips support in the compiler is untested on
+actual hardware, and as such should be expected to have many bugs. However,
+the vast majority of code for Mips support is either shared with other
+tested targets, or was taken from the functional Mips JIT compiler. The
+expectation is that when it is first tried out on actual hardware lots of
+small bugs will be flushed out, but it should not take long to get it
+solidly running. The following areas are considered most likely to have
+problems that need to be addressed:
+
+ o Endianness. Focus was on little-endian support, and if a big-endian
+ target is desired, you should pay particular attention to the
+ code generation for switch tables, fill array data, 64-bit
+ data handling and the register usage conventions.
+
+ o The memory model. Verify that oatGenMemoryBarrier() generates the
+ appropriate flavor of sync.
+
+Register promotion
+
+The resource masks in the LIR structure are 64-bits wide, which is enough
+room to fully describe def/use info for Arm and x86 instructions. However,
+the larger number of MIPS core and float registers render this too small.
+Currently, the workaround for this limitation is to avoid using floating
+point registers 16-31. These are the callee-save registers, which therefore
+means that no floating point promotion is allowed. Among the solution are:
+ o Expand the def/use mask (which, unfortunately, is a significant change)
+ o The Arm target uses 52 of the 64 bits, so we could support float
+ registers 16-27 without much effort.
+ o We could likely assign the 4 non-register bits (kDalvikReg, kLiteral,
+ kHeapRef & kMustNotAlias) to positions occuped by MIPS registers that
+ don't need def/use bits because they are never modified by code
+ subject to scheduling: r_K0, r_K1, r_SP, r_ZERO, r_S1 (rSELF).
+
+Branch delay slots
+
+Little to no attempt was made to fill branch delay slots. Branch
+instructions in the encoding map are given a length of 8 bytes to include
+an implicit NOP. It should not be too difficult to provide a slot-filling
+pass following successful assembly, but thought should be given to the
+design. Branches are currently treated as scheduling barriers. One
+simple solution would be to copy the instruction at branch targets to the
+slot and adjust the displacement. However, given that code expansion is
+already a problem it would be preferable to use a more sophisticated
+scheduling solution.
+
+Code expansion
+
+Code expansion for the MIPS target is significantly higher than we see
+for Arm and x86. It might make sense to replace the inline code generation
+for some of the more verbose Dalik byte codes with subroutine calls to
+shared helper functions.
+
diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc
new file mode 100644
index 0000000..2482aa4
--- /dev/null
+++ b/compiler/dex/quick/mips/assemble_mips.cc
@@ -0,0 +1,716 @@
+/*
+ * Copyright (C) 2012 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 "codegen_mips.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "mips_lir.h"
+
+namespace art {
+
+#define MAX_ASSEMBLER_RETRIES 50
+
+/*
+ * opcode: MipsOpCode enum
+ * skeleton: pre-designated bit-pattern for this opcode
+ * k0: key to applying ds/de
+ * ds: dest start bit position
+ * de: dest end bit position
+ * k1: key to applying s1s/s1e
+ * s1s: src1 start bit position
+ * s1e: src1 end bit position
+ * k2: key to applying s2s/s2e
+ * s2s: src2 start bit position
+ * s2e: src2 end bit position
+ * operands: number of operands (for sanity check purposes)
+ * name: mnemonic name
+ * fmt: for pretty-printing
+ */
+#define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \
+ k3, k3s, k3e, flags, name, fmt, size) \
+ {skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \
+ {k3, k3s, k3e}}, opcode, flags, name, fmt, size}
+
+/* Instruction dump string format keys: !pf, where "!" is the start
+ * of the key, "p" is which numeric operand to use and "f" is the
+ * print format.
+ *
+ * [p]ositions:
+ * 0 -> operands[0] (dest)
+ * 1 -> operands[1] (src1)
+ * 2 -> operands[2] (src2)
+ * 3 -> operands[3] (extra)
+ *
+ * [f]ormats:
+ * h -> 4-digit hex
+ * d -> decimal
+ * E -> decimal*4
+ * F -> decimal*2
+ * c -> branch condition (beq, bne, etc.)
+ * t -> pc-relative target
+ * T -> pc-region target
+ * u -> 1st half of bl[x] target
+ * v -> 2nd half ob bl[x] target
+ * R -> register list
+ * s -> single precision floating point register
+ * S -> double precision floating point register
+ * m -> Thumb2 modified immediate
+ * n -> complimented Thumb2 modified immediate
+ * M -> Thumb2 16-bit zero-extended immediate
+ * b -> 4-digit binary
+ * N -> append a NOP
+ *
+ * [!] escape. To insert "!", use "!!"
+ */
+/* NOTE: must be kept in sync with enum MipsOpcode from LIR.h */
+/*
+ * TUNING: We're currently punting on the branch delay slots. All branch
+ * instructions in this map are given a size of 8, which during assembly
+ * is expanded to include a nop. This scheme should be replaced with
+ * an assembler pass to fill those slots when possible.
+ */
+const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = {
+ ENCODING_MAP(kMips32BitData, 0x00000000,
+ kFmtBitBlt, 31, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP,
+ "data", "0x!0h(!0d)", 4),
+ ENCODING_MAP(kMipsAddiu, 0x24000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "addiu", "!0r,!1r,0x!2h(!2d)", 4),
+ ENCODING_MAP(kMipsAddu, 0x00000021,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "addu", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsAnd, 0x00000024,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "and", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsAndi, 0x30000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "andi", "!0r,!1r,0x!2h(!2d)", 4),
+ ENCODING_MAP(kMipsB, 0x10000000,
+ kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP,
+ "b", "!0t!0N", 8),
+ ENCODING_MAP(kMipsBal, 0x04110000,
+ kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR |
+ NEEDS_FIXUP, "bal", "!0t!0N", 8),
+ ENCODING_MAP(kMipsBeq, 0x10000000,
+ kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_USE01 |
+ NEEDS_FIXUP, "beq", "!0r,!1r,!2t!0N", 8),
+ ENCODING_MAP(kMipsBeqz, 0x10000000, /* same as beq above with t = $zero */
+ kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 |
+ NEEDS_FIXUP, "beqz", "!0r,!1t!0N", 8),
+ ENCODING_MAP(kMipsBgez, 0x04010000,
+ kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 |
+ NEEDS_FIXUP, "bgez", "!0r,!1t!0N", 8),
+ ENCODING_MAP(kMipsBgtz, 0x1C000000,
+ kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 |
+ NEEDS_FIXUP, "bgtz", "!0r,!1t!0N", 8),
+ ENCODING_MAP(kMipsBlez, 0x18000000,
+ kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 |
+ NEEDS_FIXUP, "blez", "!0r,!1t!0N", 8),
+ ENCODING_MAP(kMipsBltz, 0x04000000,
+ kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 |
+ NEEDS_FIXUP, "bltz", "!0r,!1t!0N", 8),
+ ENCODING_MAP(kMipsBnez, 0x14000000, /* same as bne below with t = $zero */
+ kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 |
+ NEEDS_FIXUP, "bnez", "!0r,!1t!0N", 8),
+ ENCODING_MAP(kMipsBne, 0x14000000,
+ kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_USE01 |
+ NEEDS_FIXUP, "bne", "!0r,!1r,!2t!0N", 8),
+ ENCODING_MAP(kMipsDiv, 0x0000001a,
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtBitBlt, 25, 21,
+ kFmtBitBlt, 20, 16, IS_QUAD_OP | REG_DEF01 | REG_USE23,
+ "div", "!2r,!3r", 4),
+#if __mips_isa_rev>=2
+ ENCODING_MAP(kMipsExt, 0x7c000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 10, 6,
+ kFmtBitBlt, 15, 11, IS_QUAD_OP | REG_DEF0 | REG_USE1,
+ "ext", "!0r,!1r,!2d,!3D", 4),
+#endif
+ ENCODING_MAP(kMipsJal, 0x0c000000,
+ kFmtBitBlt, 25, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+ "jal", "!0T(!0E)!0N", 8),
+ ENCODING_MAP(kMipsJalr, 0x00000009,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF0_USE1,
+ "jalr", "!0r,!1r!0N", 8),
+ ENCODING_MAP(kMipsJr, 0x00000008,
+ kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 |
+ NEEDS_FIXUP, "jr", "!0r!0N", 8),
+ ENCODING_MAP(kMipsLahi, 0x3C000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "lahi/lui", "!0r,0x!1h(!1d)", 4),
+ ENCODING_MAP(kMipsLalo, 0x34000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "lalo/ori", "!0r,!1r,0x!2h(!2d)", 4),
+ ENCODING_MAP(kMipsLui, 0x3C000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "lui", "!0r,0x!1h(!1d)", 4),
+ ENCODING_MAP(kMipsLb, 0x80000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+ "lb", "!0r,!1d(!2r)", 4),
+ ENCODING_MAP(kMipsLbu, 0x90000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+ "lbu", "!0r,!1d(!2r)", 4),
+ ENCODING_MAP(kMipsLh, 0x84000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+ "lh", "!0r,!1d(!2r)", 4),
+ ENCODING_MAP(kMipsLhu, 0x94000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+ "lhu", "!0r,!1d(!2r)", 4),
+ ENCODING_MAP(kMipsLw, 0x8C000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+ "lw", "!0r,!1d(!2r)", 4),
+ ENCODING_MAP(kMipsMfhi, 0x00000010,
+ kFmtBitBlt, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mfhi", "!0r", 4),
+ ENCODING_MAP(kMipsMflo, 0x00000012,
+ kFmtBitBlt, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mflo", "!0r", 4),
+ ENCODING_MAP(kMipsMove, 0x00000025, /* or using zero reg */
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "move", "!0r,!1r", 4),
+ ENCODING_MAP(kMipsMovz, 0x0000000a,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "movz", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsMul, 0x70000002,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "mul", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsNop, 0x00000000,
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND,
+ "nop", ";", 4),
+ ENCODING_MAP(kMipsNor, 0x00000027, /* used for "not" too */
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "nor", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsOr, 0x00000025,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "or", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsOri, 0x34000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "ori", "!0r,!1r,0x!2h(!2d)", 4),
+ ENCODING_MAP(kMipsPref, 0xCC000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE2,
+ "pref", "!0d,!1d(!2r)", 4),
+ ENCODING_MAP(kMipsSb, 0xA0000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+ "sb", "!0r,!1d(!2r)", 4),
+#if __mips_isa_rev>=2
+ ENCODING_MAP(kMipsSeb, 0x7c000420,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "seb", "!0r,!1r", 4),
+ ENCODING_MAP(kMipsSeh, 0x7c000620,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "seh", "!0r,!1r", 4),
+#endif
+ ENCODING_MAP(kMipsSh, 0xA4000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+ "sh", "!0r,!1d(!2r)", 4),
+ ENCODING_MAP(kMipsSll, 0x00000000,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "sll", "!0r,!1r,0x!2h(!2d)", 4),
+ ENCODING_MAP(kMipsSllv, 0x00000004,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "sllv", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsSlt, 0x0000002a,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "slt", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsSlti, 0x28000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "slti", "!0r,!1r,0x!2h(!2d)", 4),
+ ENCODING_MAP(kMipsSltu, 0x0000002b,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "sltu", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsSra, 0x00000003,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "sra", "!0r,!1r,0x!2h(!2d)", 4),
+ ENCODING_MAP(kMipsSrav, 0x00000007,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "srav", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsSrl, 0x00000002,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "srl", "!0r,!1r,0x!2h(!2d)", 4),
+ ENCODING_MAP(kMipsSrlv, 0x00000006,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "srlv", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsSubu, 0x00000023, /* used for "neg" too */
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "subu", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsSw, 0xAC000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+ "sw", "!0r,!1d(!2r)", 4),
+ ENCODING_MAP(kMipsXor, 0x00000026,
+ kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "xor", "!0r,!1r,!2r", 4),
+ ENCODING_MAP(kMipsXori, 0x38000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "xori", "!0r,!1r,0x!2h(!2d)", 4),
+ ENCODING_MAP(kMipsFadds, 0x46000000,
+ kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "add.s", "!0s,!1s,!2s", 4),
+ ENCODING_MAP(kMipsFsubs, 0x46000001,
+ kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "sub.s", "!0s,!1s,!2s", 4),
+ ENCODING_MAP(kMipsFmuls, 0x46000002,
+ kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "mul.s", "!0s,!1s,!2s", 4),
+ ENCODING_MAP(kMipsFdivs, 0x46000003,
+ kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "div.s", "!0s,!1s,!2s", 4),
+ ENCODING_MAP(kMipsFaddd, 0x46200000,
+ kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "add.d", "!0S,!1S,!2S", 4),
+ ENCODING_MAP(kMipsFsubd, 0x46200001,
+ kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "sub.d", "!0S,!1S,!2S", 4),
+ ENCODING_MAP(kMipsFmuld, 0x46200002,
+ kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "mul.d", "!0S,!1S,!2S", 4),
+ ENCODING_MAP(kMipsFdivd, 0x46200003,
+ kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "div.d", "!0S,!1S,!2S", 4),
+ ENCODING_MAP(kMipsFcvtsd, 0x46200020,
+ kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "cvt.s.d", "!0s,!1S", 4),
+ ENCODING_MAP(kMipsFcvtsw, 0x46800020,
+ kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "cvt.s.w", "!0s,!1s", 4),
+ ENCODING_MAP(kMipsFcvtds, 0x46000021,
+ kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "cvt.d.s", "!0S,!1s", 4),
+ ENCODING_MAP(kMipsFcvtdw, 0x46800021,
+ kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "cvt.d.w", "!0S,!1s", 4),
+ ENCODING_MAP(kMipsFcvtws, 0x46000024,
+ kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "cvt.w.s", "!0s,!1s", 4),
+ ENCODING_MAP(kMipsFcvtwd, 0x46200024,
+ kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "cvt.w.d", "!0s,!1S", 4),
+ ENCODING_MAP(kMipsFmovs, 0x46000006,
+ kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov.s", "!0s,!1s", 4),
+ ENCODING_MAP(kMipsFmovd, 0x46200006,
+ kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov.d", "!0S,!1S", 4),
+ ENCODING_MAP(kMipsFlwc1, 0xC4000000,
+ kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+ "lwc1", "!0s,!1d(!2r)", 4),
+ ENCODING_MAP(kMipsFldc1, 0xD4000000,
+ kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD,
+ "ldc1", "!0S,!1d(!2r)", 4),
+ ENCODING_MAP(kMipsFswc1, 0xE4000000,
+ kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+ "swc1", "!0s,!1d(!2r)", 4),
+ ENCODING_MAP(kMipsFsdc1, 0xF4000000,
+ kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE,
+ "sdc1", "!0S,!1d(!2r)", 4),
+ ENCODING_MAP(kMipsMfc1, 0x44000000,
+ kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mfc1", "!0r,!1s", 4),
+ ENCODING_MAP(kMipsMtc1, 0x44800000,
+ kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | REG_DEF1,
+ "mtc1", "!0r,!1s", 4),
+ ENCODING_MAP(kMipsDelta, 0x27e00000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, 15, 0,
+ kFmtUnused, -1, -1, IS_QUAD_OP | REG_DEF0 | REG_USE_LR |
+ NEEDS_FIXUP, "addiu", "!0r,ra,0x!1h(!1d)", 4),
+ ENCODING_MAP(kMipsDeltaHi, 0x3C000000,
+ kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_QUAD_OP | REG_DEF0 | NEEDS_FIXUP,
+ "lui", "!0r,0x!1h(!1d)", 4),
+ ENCODING_MAP(kMipsDeltaLo, 0x34000000,
+ kFmtBlt5_2, 16, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_QUAD_OP | REG_DEF0_USE0 | NEEDS_FIXUP,
+ "ori", "!0r,!0r,0x!1h(!1d)", 4),
+ ENCODING_MAP(kMipsCurrPC, 0x04110001,
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH | REG_DEF_LR,
+ "addiu", "ra,pc,8", 4),
+ ENCODING_MAP(kMipsSync, 0x0000000f,
+ kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP,
+ "sync", ";", 4),
+ ENCODING_MAP(kMipsUndefined, 0x64000000,
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND,
+ "undefined", "", 4),
+};
+
+
+/*
+ * Convert a short-form branch to long form. Hopefully, this won't happen
+ * very often because the PIC sequence is especially unfortunate.
+ *
+ * Orig conditional branch
+ * -----------------------
+ * beq rs,rt,target
+ *
+ * Long conditional branch
+ * -----------------------
+ * bne rs,rt,hop
+ * bal .+8 ; r_RA <- anchor
+ * lui r_AT, ((target-anchor) >> 16)
+ * anchor:
+ * ori r_AT, r_AT, ((target-anchor) & 0xffff)
+ * addu r_AT, r_AT, r_RA
+ * jr r_AT
+ * hop:
+ *
+ * Orig unconditional branch
+ * -------------------------
+ * b target
+ *
+ * Long unconditional branch
+ * -----------------------
+ * bal .+8 ; r_RA <- anchor
+ * lui r_AT, ((target-anchor) >> 16)
+ * anchor:
+ * ori r_AT, r_AT, ((target-anchor) & 0xffff)
+ * addu r_AT, r_AT, r_RA
+ * jr r_AT
+ *
+ *
+ * NOTE: An out-of-range bal isn't supported because it should
+ * never happen with the current PIC model.
+ */
+void MipsMir2Lir::ConvertShortToLongBranch(LIR* lir)
+{
+ // For conditional branches we'll need to reverse the sense
+ bool unconditional = false;
+ int opcode = lir->opcode;
+ int dalvik_offset = lir->dalvik_offset;
+ switch (opcode) {
+ case kMipsBal:
+ LOG(FATAL) << "long branch and link unsupported";
+ case kMipsB:
+ unconditional = true;
+ break;
+ case kMipsBeq: opcode = kMipsBne; break;
+ case kMipsBne: opcode = kMipsBeq; break;
+ case kMipsBeqz: opcode = kMipsBnez; break;
+ case kMipsBgez: opcode = kMipsBltz; break;
+ case kMipsBgtz: opcode = kMipsBlez; break;
+ case kMipsBlez: opcode = kMipsBgtz; break;
+ case kMipsBltz: opcode = kMipsBgez; break;
+ case kMipsBnez: opcode = kMipsBeqz; break;
+ default:
+ LOG(FATAL) << "Unexpected branch kind " << opcode;
+ }
+ LIR* hop_target = NULL;
+ if (!unconditional) {
+ hop_target = RawLIR(dalvik_offset, kPseudoTargetLabel);
+ LIR* hop_branch = RawLIR(dalvik_offset, opcode, lir->operands[0],
+ lir->operands[1], 0, 0, 0, hop_target);
+ InsertLIRBefore(lir, hop_branch);
+ }
+ LIR* curr_pc = RawLIR(dalvik_offset, kMipsCurrPC);
+ InsertLIRBefore(lir, curr_pc);
+ LIR* anchor = RawLIR(dalvik_offset, kPseudoTargetLabel);
+ LIR* delta_hi = RawLIR(dalvik_offset, kMipsDeltaHi, r_AT, 0,
+ reinterpret_cast<uintptr_t>(anchor), 0, 0, lir->target);
+ InsertLIRBefore(lir, delta_hi);
+ InsertLIRBefore(lir, anchor);
+ LIR* delta_lo = RawLIR(dalvik_offset, kMipsDeltaLo, r_AT, 0,
+ reinterpret_cast<uintptr_t>(anchor), 0, 0, lir->target);
+ InsertLIRBefore(lir, delta_lo);
+ LIR* addu = RawLIR(dalvik_offset, kMipsAddu, r_AT, r_AT, r_RA);
+ InsertLIRBefore(lir, addu);
+ LIR* jr = RawLIR(dalvik_offset, kMipsJr, r_AT);
+ InsertLIRBefore(lir, jr);
+ if (!unconditional) {
+ InsertLIRBefore(lir, hop_target);
+ }
+ lir->flags.is_nop = true;
+}
+
+/*
+ * Assemble the LIR into binary instruction format. Note that we may
+ * discover that pc-relative displacements may not fit the selected
+ * instruction. In those cases we will try to substitute a new code
+ * sequence or request that the trace be shortened and retried.
+ */
+AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr)
+{
+ LIR *lir;
+ AssemblerStatus res = kSuccess; // Assume success
+
+ for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
+ if (lir->opcode < 0) {
+ continue;
+ }
+
+
+ if (lir->flags.is_nop) {
+ continue;
+ }
+
+ if (lir->flags.pcRelFixup) {
+ if (lir->opcode == kMipsDelta) {
+ /*
+ * The "Delta" pseudo-ops load the difference between
+ * two pc-relative locations into a the target register
+ * found in operands[0]. The delta is determined by
+ * (label2 - label1), where label1 is a standard
+ * kPseudoTargetLabel and is stored in operands[2].
+ * If operands[3] is null, then label2 is a kPseudoTargetLabel
+ * and is found in lir->target. If operands[3] is non-NULL,
+ * then it is a Switch/Data table.
+ */
+ int offset1 = (reinterpret_cast<LIR*>(lir->operands[2]))->offset;
+ SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ int offset2 = tab_rec ? tab_rec->offset : lir->target->offset;
+ int delta = offset2 - offset1;
+ if ((delta & 0xffff) == delta && ((delta & 0x8000) == 0)) {
+ // Fits
+ lir->operands[1] = delta;
+ } else {
+ // Doesn't fit - must expand to kMipsDelta[Hi|Lo] pair
+ LIR *new_delta_hi =
+ RawLIR(lir->dalvik_offset, kMipsDeltaHi,
+ lir->operands[0], 0, lir->operands[2],
+ lir->operands[3], 0, lir->target);
+ InsertLIRBefore(lir, new_delta_hi);
+ LIR *new_delta_lo =
+ RawLIR(lir->dalvik_offset, kMipsDeltaLo,
+ lir->operands[0], 0, lir->operands[2],
+ lir->operands[3], 0, lir->target);
+ InsertLIRBefore(lir, new_delta_lo);
+ LIR *new_addu =
+ RawLIR(lir->dalvik_offset, kMipsAddu,
+ lir->operands[0], lir->operands[0], r_RA);
+ InsertLIRBefore(lir, new_addu);
+ lir->flags.is_nop = true;
+ res = kRetryAll;
+ }
+ } else if (lir->opcode == kMipsDeltaLo) {
+ int offset1 = (reinterpret_cast<LIR*>(lir->operands[2]))->offset;
+ SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ int offset2 = tab_rec ? tab_rec->offset : lir->target->offset;
+ int delta = offset2 - offset1;
+ lir->operands[1] = delta & 0xffff;
+ } else if (lir->opcode == kMipsDeltaHi) {
+ int offset1 = (reinterpret_cast<LIR*>(lir->operands[2]))->offset;
+ SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ int offset2 = tab_rec ? tab_rec->offset : lir->target->offset;
+ int delta = offset2 - offset1;
+ lir->operands[1] = (delta >> 16) & 0xffff;
+ } else if (lir->opcode == kMipsB || lir->opcode == kMipsBal) {
+ LIR *target_lir = lir->target;
+ uintptr_t pc = lir->offset + 4;
+ uintptr_t target = target_lir->offset;
+ int delta = target - pc;
+ if (delta & 0x3) {
+ LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
+ }
+ if (delta > 131068 || delta < -131069) {
+ res = kRetryAll;
+ ConvertShortToLongBranch(lir);
+ } else {
+ lir->operands[0] = delta >> 2;
+ }
+ } else if (lir->opcode >= kMipsBeqz && lir->opcode <= kMipsBnez) {
+ LIR *target_lir = lir->target;
+ uintptr_t pc = lir->offset + 4;
+ uintptr_t target = target_lir->offset;
+ int delta = target - pc;
+ if (delta & 0x3) {
+ LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
+ }
+ if (delta > 131068 || delta < -131069) {
+ res = kRetryAll;
+ ConvertShortToLongBranch(lir);
+ } else {
+ lir->operands[1] = delta >> 2;
+ }
+ } else if (lir->opcode == kMipsBeq || lir->opcode == kMipsBne) {
+ LIR *target_lir = lir->target;
+ uintptr_t pc = lir->offset + 4;
+ uintptr_t target = target_lir->offset;
+ int delta = target - pc;
+ if (delta & 0x3) {
+ LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
+ }
+ if (delta > 131068 || delta < -131069) {
+ res = kRetryAll;
+ ConvertShortToLongBranch(lir);
+ } else {
+ lir->operands[2] = delta >> 2;
+ }
+ } else if (lir->opcode == kMipsJal) {
+ uintptr_t cur_pc = (start_addr + lir->offset + 4) & ~3;
+ uintptr_t target = lir->operands[0];
+ /* ensure PC-region branch can be used */
+ DCHECK_EQ((cur_pc & 0xF0000000), (target & 0xF0000000));
+ if (target & 0x3) {
+ LOG(FATAL) << "Jump target not multiple of 4: " << target;
+ }
+ lir->operands[0] = target >> 2;
+ } else if (lir->opcode == kMipsLahi) { /* ld address hi (via lui) */
+ LIR *target_lir = lir->target;
+ uintptr_t target = start_addr + target_lir->offset;
+ lir->operands[1] = target >> 16;
+ } else if (lir->opcode == kMipsLalo) { /* ld address lo (via ori) */
+ LIR *target_lir = lir->target;
+ uintptr_t target = start_addr + target_lir->offset;
+ lir->operands[2] = lir->operands[2] + target;
+ }
+ }
+
+ /*
+ * If one of the pc-relative instructions expanded we'll have
+ * to make another pass. Don't bother to fully assemble the
+ * instruction.
+ */
+ if (res != kSuccess) {
+ continue;
+ }
+ const MipsEncodingMap *encoder = &EncodingMap[lir->opcode];
+ uint32_t bits = encoder->skeleton;
+ int i;
+ for (i = 0; i < 4; i++) {
+ uint32_t operand;
+ uint32_t value;
+ operand = lir->operands[i];
+ switch (encoder->field_loc[i].kind) {
+ case kFmtUnused:
+ break;
+ case kFmtBitBlt:
+ if (encoder->field_loc[i].start == 0 && encoder->field_loc[i].end == 31) {
+ value = operand;
+ } else {
+ value = (operand << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ }
+ bits |= value;
+ break;
+ case kFmtBlt5_2:
+ value = (operand & 0x1f);
+ bits |= (value << encoder->field_loc[i].start);
+ bits |= (value << encoder->field_loc[i].end);
+ break;
+ case kFmtDfp: {
+ DCHECK(MIPS_DOUBLEREG(operand));
+ DCHECK_EQ((operand & 0x1), 0U);
+ value = ((operand & MIPS_FP_REG_MASK) << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ bits |= value;
+ break;
+ }
+ case kFmtSfp:
+ DCHECK(MIPS_SINGLEREG(operand));
+ value = ((operand & MIPS_FP_REG_MASK) << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ bits |= value;
+ break;
+ default:
+ LOG(FATAL) << "Bad encoder format: " << encoder->field_loc[i].kind;
+ }
+ }
+ // We only support little-endian MIPS.
+ code_buffer_.push_back(bits & 0xff);
+ code_buffer_.push_back((bits >> 8) & 0xff);
+ code_buffer_.push_back((bits >> 16) & 0xff);
+ code_buffer_.push_back((bits >> 24) & 0xff);
+ // TUNING: replace with proper delay slot handling
+ if (encoder->size == 8) {
+ const MipsEncodingMap *encoder = &EncodingMap[kMipsNop];
+ uint32_t bits = encoder->skeleton;
+ code_buffer_.push_back(bits & 0xff);
+ code_buffer_.push_back((bits >> 8) & 0xff);
+ code_buffer_.push_back((bits >> 16) & 0xff);
+ code_buffer_.push_back((bits >> 24) & 0xff);
+ }
+ }
+ return res;
+}
+
+int MipsMir2Lir::GetInsnSize(LIR* lir)
+{
+ return EncodingMap[lir->opcode].size;
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
new file mode 100644
index 0000000..eb0302e
--- /dev/null
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/* This file contains codegen for the Mips ISA */
+
+#include "codegen_mips.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "mips_lir.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+
+namespace art {
+
+void MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
+ SpecialCaseHandler special_case)
+{
+ // TODO
+}
+
+/*
+ * The lack of pc-relative loads on Mips presents somewhat of a challenge
+ * for our PIC switch table strategy. To materialize the current location
+ * we'll do a dummy JAL and reference our tables using r_RA as the
+ * base register. Note that r_RA will be used both as the base to
+ * locate the switch table data and as the reference base for the switch
+ * target offsets stored in the table. We'll use a special pseudo-instruction
+ * to represent the jal and trigger the construction of the
+ * switch table offsets (which will happen after final assembly and all
+ * labels are fixed).
+ *
+ * The test loop will look something like:
+ *
+ * ori rEnd, r_ZERO, #table_size ; size in bytes
+ * jal BaseLabel ; stores "return address" (BaseLabel) in r_RA
+ * nop ; opportunistically fill
+ * BaseLabel:
+ * addiu rBase, r_RA, <table> - <BaseLabel> ; table relative to BaseLabel
+ addu rEnd, rEnd, rBase ; end of table
+ * lw r_val, [rSP, v_reg_off] ; Test Value
+ * loop:
+ * beq rBase, rEnd, done
+ * lw r_key, 0(rBase)
+ * addu rBase, 8
+ * bne r_val, r_key, loop
+ * lw r_disp, -4(rBase)
+ * addu r_RA, r_disp
+ * jr r_RA
+ * done:
+ *
+ */
+void MipsMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
+ RegLocation rl_src)
+{
+ const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ if (cu_->verbose) {
+ DumpSparseSwitchTable(table);
+ }
+ // Add the table to the list - we'll process it later
+ SwitchTable *tab_rec =
+ static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true,
+ ArenaAllocator::kAllocData));
+ tab_rec->table = table;
+ tab_rec->vaddr = current_dalvik_offset_;
+ int elements = table[1];
+ tab_rec->targets =
+ static_cast<LIR**>(arena_->NewMem(elements * sizeof(LIR*), true, ArenaAllocator::kAllocLIR));
+ switch_tables_.Insert(tab_rec);
+
+ // The table is composed of 8-byte key/disp pairs
+ int byte_size = elements * 8;
+
+ int size_hi = byte_size >> 16;
+ int size_lo = byte_size & 0xffff;
+
+ int rEnd = AllocTemp();
+ if (size_hi) {
+ NewLIR2(kMipsLui, rEnd, size_hi);
+ }
+ // Must prevent code motion for the curr pc pair
+ GenBarrier(); // Scheduling barrier
+ NewLIR0(kMipsCurrPC); // Really a jal to .+8
+ // Now, fill the branch delay slot
+ if (size_hi) {
+ NewLIR3(kMipsOri, rEnd, rEnd, size_lo);
+ } else {
+ NewLIR3(kMipsOri, rEnd, r_ZERO, size_lo);
+ }
+ GenBarrier(); // Scheduling barrier
+
+ // Construct BaseLabel and set up table base register
+ LIR* base_label = NewLIR0(kPseudoTargetLabel);
+ // Remember base label so offsets can be computed later
+ tab_rec->anchor = base_label;
+ int rBase = AllocTemp();
+ NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(base_label),
+ reinterpret_cast<uintptr_t>(tab_rec));
+ OpRegRegReg(kOpAdd, rEnd, rEnd, rBase);
+
+ // Grab switch test value
+ rl_src = LoadValue(rl_src, kCoreReg);
+
+ // Test loop
+ int r_key = AllocTemp();
+ LIR* loop_label = NewLIR0(kPseudoTargetLabel);
+ LIR* exit_branch = OpCmpBranch(kCondEq, rBase, rEnd, NULL);
+ LoadWordDisp(rBase, 0, r_key);
+ OpRegImm(kOpAdd, rBase, 8);
+ OpCmpBranch(kCondNe, rl_src.low_reg, r_key, loop_label);
+ int r_disp = AllocTemp();
+ LoadWordDisp(rBase, -4, r_disp);
+ OpRegRegReg(kOpAdd, r_RA, r_RA, r_disp);
+ OpReg(kOpBx, r_RA);
+
+ // Loop exit
+ LIR* exit_label = NewLIR0(kPseudoTargetLabel);
+ exit_branch->target = exit_label;
+}
+
+/*
+ * Code pattern will look something like:
+ *
+ * lw r_val
+ * jal BaseLabel ; stores "return address" (BaseLabel) in r_RA
+ * nop ; opportunistically fill
+ * [subiu r_val, bias] ; Remove bias if low_val != 0
+ * bound check -> done
+ * lw r_disp, [r_RA, r_val]
+ * addu r_RA, r_disp
+ * jr r_RA
+ * done:
+ */
+void MipsMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
+ RegLocation rl_src)
+{
+ const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ if (cu_->verbose) {
+ DumpPackedSwitchTable(table);
+ }
+ // Add the table to the list - we'll process it later
+ SwitchTable *tab_rec =
+ static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true,
+ ArenaAllocator::kAllocData));
+ tab_rec->table = table;
+ tab_rec->vaddr = current_dalvik_offset_;
+ int size = table[1];
+ tab_rec->targets = static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true,
+ ArenaAllocator::kAllocLIR));
+ switch_tables_.Insert(tab_rec);
+
+ // Get the switch value
+ rl_src = LoadValue(rl_src, kCoreReg);
+
+ // Prepare the bias. If too big, handle 1st stage here
+ int low_key = s4FromSwitchData(&table[2]);
+ bool large_bias = false;
+ int r_key;
+ if (low_key == 0) {
+ r_key = rl_src.low_reg;
+ } else if ((low_key & 0xffff) != low_key) {
+ r_key = AllocTemp();
+ LoadConstant(r_key, low_key);
+ large_bias = true;
+ } else {
+ r_key = AllocTemp();
+ }
+
+ // Must prevent code motion for the curr pc pair
+ GenBarrier();
+ NewLIR0(kMipsCurrPC); // Really a jal to .+8
+ // Now, fill the branch delay slot with bias strip
+ if (low_key == 0) {
+ NewLIR0(kMipsNop);
+ } else {
+ if (large_bias) {
+ OpRegRegReg(kOpSub, r_key, rl_src.low_reg, r_key);
+ } else {
+ OpRegRegImm(kOpSub, r_key, rl_src.low_reg, low_key);
+ }
+ }
+ GenBarrier(); // Scheduling barrier
+
+ // Construct BaseLabel and set up table base register
+ LIR* base_label = NewLIR0(kPseudoTargetLabel);
+ // Remember base label so offsets can be computed later
+ tab_rec->anchor = base_label;
+
+ // Bounds check - if < 0 or >= size continue following switch
+ LIR* branch_over = OpCmpImmBranch(kCondHi, r_key, size-1, NULL);
+
+ // Materialize the table base pointer
+ int rBase = AllocTemp();
+ NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(base_label),
+ reinterpret_cast<uintptr_t>(tab_rec));
+
+ // Load the displacement from the switch table
+ int r_disp = AllocTemp();
+ LoadBaseIndexed(rBase, r_key, r_disp, 2, kWord);
+
+ // Add to r_AP and go
+ OpRegRegReg(kOpAdd, r_RA, r_RA, r_disp);
+ OpReg(kOpBx, r_RA);
+
+ /* branch_over target here */
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ branch_over->target = target;
+}
+
+/*
+ * Array data table format:
+ * ushort ident = 0x0300 magic value
+ * ushort width width of each element in the table
+ * uint size number of elements in the table
+ * ubyte data[size*width] table of data values (may contain a single-byte
+ * padding at the end)
+ *
+ * Total size is 4+(width * size + 1)/2 16-bit code units.
+ */
+void MipsMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src)
+{
+ const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ // Add the table to the list - we'll process it later
+ FillArrayData *tab_rec =
+ reinterpret_cast<FillArrayData*>(arena_->NewMem(sizeof(FillArrayData), true,
+ ArenaAllocator::kAllocData));
+ tab_rec->table = table;
+ tab_rec->vaddr = current_dalvik_offset_;
+ uint16_t width = tab_rec->table[1];
+ uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
+ tab_rec->size = (size * width) + 8;
+
+ fill_array_data_.Insert(tab_rec);
+
+ // Making a call - use explicit registers
+ FlushAllRegs(); /* Everything to home location */
+ LockCallTemps();
+ LoadValueDirectFixed(rl_src, rMIPS_ARG0);
+
+ // Must prevent code motion for the curr pc pair
+ GenBarrier();
+ NewLIR0(kMipsCurrPC); // Really a jal to .+8
+ // Now, fill the branch delay slot with the helper load
+ int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pHandleFillArrayDataFromCode));
+ GenBarrier(); // Scheduling barrier
+
+ // Construct BaseLabel and set up table base register
+ LIR* base_label = NewLIR0(kPseudoTargetLabel);
+
+ // Materialize a pointer to the fill data image
+ NewLIR4(kMipsDelta, rMIPS_ARG1, 0, reinterpret_cast<uintptr_t>(base_label),
+ reinterpret_cast<uintptr_t>(tab_rec));
+
+ // And go...
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, r_tgt); // ( array*, fill_data* )
+ MarkSafepointPC(call_inst);
+}
+
+/*
+ * TODO: implement fast path to short-circuit thin-lock case
+ */
+void MipsMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src)
+{
+ FlushAllRegs();
+ LoadValueDirectFixed(rl_src, rMIPS_ARG0); // Get obj
+ LockCallTemps(); // Prepare for explicit register usage
+ GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags);
+ // Go expensive route - artLockObjectFromCode(self, obj);
+ int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pLockObjectFromCode));
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, r_tgt);
+ MarkSafepointPC(call_inst);
+}
+
+/*
+ * TODO: implement fast path to short-circuit thin-lock case
+ */
+void MipsMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src)
+{
+ FlushAllRegs();
+ LoadValueDirectFixed(rl_src, rMIPS_ARG0); // Get obj
+ LockCallTemps(); // Prepare for explicit register usage
+ GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags);
+ // Go expensive route - UnlockObjectFromCode(obj);
+ int r_tgt = LoadHelper(ENTRYPOINT_OFFSET(pUnlockObjectFromCode));
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, r_tgt);
+ MarkSafepointPC(call_inst);
+}
+
+void MipsMir2Lir::GenMoveException(RegLocation rl_dest)
+{
+ int ex_offset = Thread::ExceptionOffset().Int32Value();
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int reset_reg = AllocTemp();
+ LoadWordDisp(rMIPS_SELF, ex_offset, rl_result.low_reg);
+ LoadConstant(reset_reg, 0);
+ StoreWordDisp(rMIPS_SELF, ex_offset, reset_reg);
+ FreeTemp(reset_reg);
+ StoreValue(rl_dest, rl_result);
+}
+
+/*
+ * Mark garbage collection card. Skip if the value we're storing is null.
+ */
+void MipsMir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg)
+{
+ int reg_card_base = AllocTemp();
+ int reg_card_no = AllocTemp();
+ LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL);
+ LoadWordDisp(rMIPS_SELF, Thread::CardTableOffset().Int32Value(), reg_card_base);
+ OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
+ StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0,
+ kUnsignedByte);
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ branch_over->target = target;
+ FreeTemp(reg_card_base);
+ FreeTemp(reg_card_no);
+}
+void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method)
+{
+ int spill_count = num_core_spills_ + num_fp_spills_;
+ /*
+ * On entry, rMIPS_ARG0, rMIPS_ARG1, rMIPS_ARG2 & rMIPS_ARG3 are live. Let the register
+ * allocation mechanism know so it doesn't try to use any of them when
+ * expanding the frame or flushing. This leaves the utility
+ * code with a single temp: r12. This should be enough.
+ */
+ LockTemp(rMIPS_ARG0);
+ LockTemp(rMIPS_ARG1);
+ LockTemp(rMIPS_ARG2);
+ LockTemp(rMIPS_ARG3);
+
+ /*
+ * We can safely skip the stack overflow check if we're
+ * a leaf *and* our frame size < fudge factor.
+ */
+ bool skip_overflow_check = (mir_graph_->MethodIsLeaf() &&
+ (static_cast<size_t>(frame_size_) < Thread::kStackOverflowReservedBytes));
+ NewLIR0(kPseudoMethodEntry);
+ int check_reg = AllocTemp();
+ int new_sp = AllocTemp();
+ if (!skip_overflow_check) {
+ /* Load stack limit */
+ LoadWordDisp(rMIPS_SELF, Thread::StackEndOffset().Int32Value(), check_reg);
+ }
+ /* Spill core callee saves */
+ SpillCoreRegs();
+ /* NOTE: promotion of FP regs currently unsupported, thus no FP spill */
+ DCHECK_EQ(num_fp_spills_, 0);
+ if (!skip_overflow_check) {
+ OpRegRegImm(kOpSub, new_sp, rMIPS_SP, frame_size_ - (spill_count * 4));
+ GenRegRegCheck(kCondCc, new_sp, check_reg, kThrowStackOverflow);
+ OpRegCopy(rMIPS_SP, new_sp); // Establish stack
+ } else {
+ OpRegImm(kOpSub, rMIPS_SP, frame_size_ - (spill_count * 4));
+ }
+
+ FlushIns(ArgLocs, rl_method);
+
+ FreeTemp(rMIPS_ARG0);
+ FreeTemp(rMIPS_ARG1);
+ FreeTemp(rMIPS_ARG2);
+ FreeTemp(rMIPS_ARG3);
+}
+
+void MipsMir2Lir::GenExitSequence()
+{
+ /*
+ * In the exit path, rMIPS_RET0/rMIPS_RET1 are live - make sure they aren't
+ * allocated by the register utilities as temps.
+ */
+ LockTemp(rMIPS_RET0);
+ LockTemp(rMIPS_RET1);
+
+ NewLIR0(kPseudoMethodExit);
+ UnSpillCoreRegs();
+ OpReg(kOpBx, r_RA);
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
new file mode 100644
index 0000000..9723b899
--- /dev/null
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ART_SRC_DEX_QUICK_CODEGEN_MIPS_CODEGENMIPS_H_
+#define ART_SRC_DEX_QUICK_CODEGEN_MIPS_CODEGENMIPS_H_
+
+#include "dex/compiler_internals.h"
+#include "mips_lir.h"
+
+namespace art {
+
+class MipsMir2Lir : public Mir2Lir {
+ public:
+
+ MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
+
+ // Required for target - codegen utilities.
+ bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src,
+ RegLocation rl_dest, int lit);
+ int LoadHelper(int offset);
+ LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg);
+ LIR* LoadBaseDispWide(int rBase, int displacement, int r_dest_lo, int r_dest_hi,
+ int s_reg);
+ LIR* LoadBaseIndexed(int rBase, int r_index, int r_dest, int scale, OpSize size);
+ LIR* LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
+ int r_dest, int r_dest_hi, OpSize size, int s_reg);
+ LIR* LoadConstantNoClobber(int r_dest, int value);
+ LIR* LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value);
+ LIR* StoreBaseDisp(int rBase, int displacement, int r_src, OpSize size);
+ LIR* StoreBaseDispWide(int rBase, int displacement, int r_src_lo, int r_src_hi);
+ LIR* StoreBaseIndexed(int rBase, int r_index, int r_src, int scale, OpSize size);
+ LIR* StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
+ int r_src, int r_src_hi, OpSize size, int s_reg);
+ void MarkGCCard(int val_reg, int tgt_addr_reg);
+
+ // Required for target - register utilities.
+ bool IsFpReg(int reg);
+ bool SameRegType(int reg1, int reg2);
+ int AllocTypedTemp(bool fp_hint, int reg_class);
+ int AllocTypedTempPair(bool fp_hint, int reg_class);
+ int S2d(int low_reg, int high_reg);
+ int TargetReg(SpecialTargetRegister reg);
+ RegisterInfo* GetRegInfo(int reg);
+ RegLocation GetReturnAlt();
+ RegLocation GetReturnWideAlt();
+ RegLocation LocCReturn();
+ RegLocation LocCReturnDouble();
+ RegLocation LocCReturnFloat();
+ RegLocation LocCReturnWide();
+ uint32_t FpRegMask();
+ uint64_t GetRegMaskCommon(int reg);
+ void AdjustSpillMask();
+ void ClobberCalleeSave();
+ void FlushReg(int reg);
+ void FlushRegWide(int reg1, int reg2);
+ void FreeCallTemps();
+ void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free);
+ void LockCallTemps();
+ void MarkPreservedSingle(int v_reg, int reg);
+ void CompilerInitializeRegAlloc();
+
+ // Required for target - miscellaneous.
+ AssemblerStatus AssembleInstructions(uintptr_t start_addr);
+ void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix);
+ void SetupTargetResourceMasks(LIR* lir);
+ const char* GetTargetInstFmt(int opcode);
+ const char* GetTargetInstName(int opcode);
+ std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr);
+ uint64_t GetPCUseDefEncoding();
+ uint64_t GetTargetInstFlags(int opcode);
+ int GetInsnSize(LIR* lir);
+ bool IsUnconditionalBranch(LIR* lir);
+
+ // Required for target - Dalvik-level generators.
+ void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
+ RegLocation rl_src, int scale);
+ void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_dest, int scale);
+ void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale);
+ void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_shift);
+ void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2);
+ void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src);
+ bool GenInlinedCas32(CallInfo* info, bool need_write_barrier);
+ bool GenInlinedMinMaxInt(CallInfo* info, bool is_min);
+ bool GenInlinedSqrt(CallInfo* info);
+ void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
+ void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset,
+ ThrowKind kind);
+ RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div);
+ RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div);
+ void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenDivZeroCheck(int reg_lo, int reg_hi);
+ void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
+ void GenExitSequence();
+ void GenFillArrayData(uint32_t table_offset, RegLocation rl_src);
+ void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
+ void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
+ void GenSelect(BasicBlock* bb, MIR* mir);
+ void GenMemBarrier(MemBarrierKind barrier_kind);
+ void GenMonitorEnter(int opt_flags, RegLocation rl_src);
+ void GenMonitorExit(int opt_flags, RegLocation rl_src);
+ void GenMoveException(RegLocation rl_dest);
+ void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
+ int first_bit, int second_bit);
+ void GenNegDouble(RegLocation rl_dest, RegLocation rl_src);
+ void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
+ void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
+ void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
+ void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case);
+
+ // Required for target - single operation generators.
+ LIR* OpUnconditionalBranch(LIR* target);
+ LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target);
+ LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target);
+ LIR* OpCondBranch(ConditionCode cc, LIR* target);
+ LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target);
+ LIR* OpFpRegCopy(int r_dest, int r_src);
+ LIR* OpIT(ConditionCode cond, const char* guide);
+ LIR* OpMem(OpKind op, int rBase, int disp);
+ LIR* OpPcRelLoad(int reg, LIR* target);
+ LIR* OpReg(OpKind op, int r_dest_src);
+ LIR* OpRegCopy(int r_dest, int r_src);
+ LIR* OpRegCopyNoInsert(int r_dest, int r_src);
+ LIR* OpRegImm(OpKind op, int r_dest_src1, int value);
+ LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset);
+ LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2);
+ LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value);
+ LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2);
+ LIR* OpTestSuspend(LIR* target);
+ LIR* OpThreadMem(OpKind op, int thread_offset);
+ LIR* OpVldm(int rBase, int count);
+ LIR* OpVstm(int rBase, int count);
+ void OpLea(int rBase, int reg1, int reg2, int scale, int offset);
+ void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi);
+ void OpTlsCmp(int offset, int val);
+
+ LIR* LoadBaseDispBody(int rBase, int displacement, int r_dest, int r_dest_hi, OpSize size,
+ int s_reg);
+ LIR* StoreBaseDispBody(int rBase, int displacement, int r_src, int r_src_hi, OpSize size);
+ void SpillCoreRegs();
+ void UnSpillCoreRegs();
+ static const MipsEncodingMap EncodingMap[kMipsLast];
+ bool InexpensiveConstantInt(int32_t value);
+ bool InexpensiveConstantFloat(int32_t value);
+ bool InexpensiveConstantLong(int64_t value);
+ bool InexpensiveConstantDouble(int64_t value);
+
+ private:
+ void ConvertShortToLongBranch(LIR* lir);
+
+};
+
+} // namespace art
+
+#endif // ART_SRC_DEX_QUICK_CODEGEN_MIPS_CODEGENMIPS_H_
diff --git a/compiler/dex/quick/mips/fp_mips.cc b/compiler/dex/quick/mips/fp_mips.cc
new file mode 100644
index 0000000..8581d5b
--- /dev/null
+++ b/compiler/dex/quick/mips/fp_mips.cc
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2012 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 "codegen_mips.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "mips_lir.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+
+namespace art {
+
+void MipsMir2Lir::GenArithOpFloat(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2)
+{
+ int op = kMipsNop;
+ RegLocation rl_result;
+
+ /*
+ * Don't attempt to optimize register usage since these opcodes call out to
+ * the handlers.
+ */
+ switch (opcode) {
+ case Instruction::ADD_FLOAT_2ADDR:
+ case Instruction::ADD_FLOAT:
+ op = kMipsFadds;
+ break;
+ case Instruction::SUB_FLOAT_2ADDR:
+ case Instruction::SUB_FLOAT:
+ op = kMipsFsubs;
+ break;
+ case Instruction::DIV_FLOAT_2ADDR:
+ case Instruction::DIV_FLOAT:
+ op = kMipsFdivs;
+ break;
+ case Instruction::MUL_FLOAT_2ADDR:
+ case Instruction::MUL_FLOAT:
+ op = kMipsFmuls;
+ break;
+ case Instruction::REM_FLOAT_2ADDR:
+ case Instruction::REM_FLOAT:
+ FlushAllRegs(); // Send everything to home location
+ CallRuntimeHelperRegLocationRegLocation(ENTRYPOINT_OFFSET(pFmodf), rl_src1, rl_src2, false);
+ rl_result = GetReturn(true);
+ StoreValue(rl_dest, rl_result);
+ return;
+ case Instruction::NEG_FLOAT:
+ GenNegFloat(rl_dest, rl_src1);
+ return;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+ rl_src1 = LoadValue(rl_src1, kFPReg);
+ rl_src2 = LoadValue(rl_src2, kFPReg);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR3(op, rl_result.low_reg, rl_src1.low_reg, rl_src2.low_reg);
+ StoreValue(rl_dest, rl_result);
+}
+
+void MipsMir2Lir::GenArithOpDouble(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2)
+{
+ int op = kMipsNop;
+ RegLocation rl_result;
+
+ switch (opcode) {
+ case Instruction::ADD_DOUBLE_2ADDR:
+ case Instruction::ADD_DOUBLE:
+ op = kMipsFaddd;
+ break;
+ case Instruction::SUB_DOUBLE_2ADDR:
+ case Instruction::SUB_DOUBLE:
+ op = kMipsFsubd;
+ break;
+ case Instruction::DIV_DOUBLE_2ADDR:
+ case Instruction::DIV_DOUBLE:
+ op = kMipsFdivd;
+ break;
+ case Instruction::MUL_DOUBLE_2ADDR:
+ case Instruction::MUL_DOUBLE:
+ op = kMipsFmuld;
+ break;
+ case Instruction::REM_DOUBLE_2ADDR:
+ case Instruction::REM_DOUBLE:
+ FlushAllRegs(); // Send everything to home location
+ CallRuntimeHelperRegLocationRegLocation(ENTRYPOINT_OFFSET(pFmod), rl_src1, rl_src2, false);
+ rl_result = GetReturnWide(true);
+ StoreValueWide(rl_dest, rl_result);
+ return;
+ case Instruction::NEG_DOUBLE:
+ GenNegDouble(rl_dest, rl_src1);
+ return;
+ default:
+ LOG(FATAL) << "Unpexpected opcode: " << opcode;
+ }
+ rl_src1 = LoadValueWide(rl_src1, kFPReg);
+ DCHECK(rl_src1.wide);
+ rl_src2 = LoadValueWide(rl_src2, kFPReg);
+ DCHECK(rl_src2.wide);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ DCHECK(rl_dest.wide);
+ DCHECK(rl_result.wide);
+ NewLIR3(op, S2d(rl_result.low_reg, rl_result.high_reg), S2d(rl_src1.low_reg, rl_src1.high_reg),
+ S2d(rl_src2.low_reg, rl_src2.high_reg));
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void MipsMir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src)
+{
+ int op = kMipsNop;
+ int src_reg;
+ RegLocation rl_result;
+ switch (opcode) {
+ case Instruction::INT_TO_FLOAT:
+ op = kMipsFcvtsw;
+ break;
+ case Instruction::DOUBLE_TO_FLOAT:
+ op = kMipsFcvtsd;
+ break;
+ case Instruction::FLOAT_TO_DOUBLE:
+ op = kMipsFcvtds;
+ break;
+ case Instruction::INT_TO_DOUBLE:
+ op = kMipsFcvtdw;
+ break;
+ case Instruction::FLOAT_TO_INT:
+ GenConversionCall(ENTRYPOINT_OFFSET(pF2iz), rl_dest, rl_src);
+ return;
+ case Instruction::DOUBLE_TO_INT:
+ GenConversionCall(ENTRYPOINT_OFFSET(pD2iz), rl_dest, rl_src);
+ return;
+ case Instruction::LONG_TO_DOUBLE:
+ GenConversionCall(ENTRYPOINT_OFFSET(pL2d), rl_dest, rl_src);
+ return;
+ case Instruction::FLOAT_TO_LONG:
+ GenConversionCall(ENTRYPOINT_OFFSET(pF2l), rl_dest, rl_src);
+ return;
+ case Instruction::LONG_TO_FLOAT:
+ GenConversionCall(ENTRYPOINT_OFFSET(pL2f), rl_dest, rl_src);
+ return;
+ case Instruction::DOUBLE_TO_LONG:
+ GenConversionCall(ENTRYPOINT_OFFSET(pD2l), rl_dest, rl_src);
+ return;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+ if (rl_src.wide) {
+ rl_src = LoadValueWide(rl_src, kFPReg);
+ src_reg = S2d(rl_src.low_reg, rl_src.high_reg);
+ } else {
+ rl_src = LoadValue(rl_src, kFPReg);
+ src_reg = rl_src.low_reg;
+ }
+ if (rl_dest.wide) {
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(op, S2d(rl_result.low_reg, rl_result.high_reg), src_reg);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(op, rl_result.low_reg, src_reg);
+ StoreValue(rl_dest, rl_result);
+ }
+}
+
+void MipsMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2)
+{
+ bool wide = true;
+ int offset = -1; // Make gcc happy.
+
+ switch (opcode) {
+ case Instruction::CMPL_FLOAT:
+ offset = ENTRYPOINT_OFFSET(pCmplFloat);
+ wide = false;
+ break;
+ case Instruction::CMPG_FLOAT:
+ offset = ENTRYPOINT_OFFSET(pCmpgFloat);
+ wide = false;
+ break;
+ case Instruction::CMPL_DOUBLE:
+ offset = ENTRYPOINT_OFFSET(pCmplDouble);
+ break;
+ case Instruction::CMPG_DOUBLE:
+ offset = ENTRYPOINT_OFFSET(pCmpgDouble);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+ FlushAllRegs();
+ LockCallTemps();
+ if (wide) {
+ LoadValueDirectWideFixed(rl_src1, rMIPS_FARG0, rMIPS_FARG1);
+ LoadValueDirectWideFixed(rl_src2, rMIPS_FARG2, rMIPS_FARG3);
+ } else {
+ LoadValueDirectFixed(rl_src1, rMIPS_FARG0);
+ LoadValueDirectFixed(rl_src2, rMIPS_FARG2);
+ }
+ int r_tgt = LoadHelper(offset);
+ // NOTE: not a safepoint
+ OpReg(kOpBlx, r_tgt);
+ RegLocation rl_result = GetReturn(false);
+ StoreValue(rl_dest, rl_result);
+}
+
+void MipsMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir,
+ bool gt_bias, bool is_double)
+{
+ UNIMPLEMENTED(FATAL) << "Need codegen for fused fp cmp branch";
+}
+
+void MipsMir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src)
+{
+ RegLocation rl_result;
+ rl_src = LoadValue(rl_src, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegRegImm(kOpAdd, rl_result.low_reg, rl_src.low_reg, 0x80000000);
+ StoreValue(rl_dest, rl_result);
+}
+
+void MipsMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src)
+{
+ RegLocation rl_result;
+ rl_src = LoadValueWide(rl_src, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegRegImm(kOpAdd, rl_result.high_reg, rl_src.high_reg, 0x80000000);
+ OpRegCopy(rl_result.low_reg, rl_src.low_reg);
+ StoreValueWide(rl_dest, rl_result);
+}
+
+bool MipsMir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min)
+{
+ // TODO: need Mips implementation
+ return false;
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
new file mode 100644
index 0000000..8bfc4e1
--- /dev/null
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -0,0 +1,659 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/* This file contains codegen for the Mips ISA */
+
+#include "codegen_mips.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "mips_lir.h"
+#include "mirror/array.h"
+#include "oat/runtime/oat_support_entrypoints.h"
+
+namespace art {
+
+/*
+ * Compare two 64-bit values
+ * x = y return 0
+ * x < y return -1
+ * x > y return 1
+ *
+ * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0
+ * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0
+ * subu res, t0, t1 # res = -1:1:0 for [ < > = ]
+ * bnez res, finish
+ * sltu t0, x.lo, y.lo
+ * sgtu r1, x.lo, y.lo
+ * subu res, t0, t1
+ * finish:
+ *
+ */
+void MipsMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+ int t0 = AllocTemp();
+ int t1 = AllocTemp();
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ NewLIR3(kMipsSlt, t0, rl_src1.high_reg, rl_src2.high_reg);
+ NewLIR3(kMipsSlt, t1, rl_src2.high_reg, rl_src1.high_reg);
+ NewLIR3(kMipsSubu, rl_result.low_reg, t1, t0);
+ LIR* branch = OpCmpImmBranch(kCondNe, rl_result.low_reg, 0, NULL);
+ NewLIR3(kMipsSltu, t0, rl_src1.low_reg, rl_src2.low_reg);
+ NewLIR3(kMipsSltu, t1, rl_src2.low_reg, rl_src1.low_reg);
+ NewLIR3(kMipsSubu, rl_result.low_reg, t1, t0);
+ FreeTemp(t0);
+ FreeTemp(t1);
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ branch->target = target;
+ StoreValue(rl_dest, rl_result);
+}
+
+LIR* MipsMir2Lir::OpCmpBranch(ConditionCode cond, int src1, int src2,
+ LIR* target)
+{
+ LIR* branch;
+ MipsOpCode slt_op;
+ MipsOpCode br_op;
+ bool cmp_zero = false;
+ bool swapped = false;
+ switch (cond) {
+ case kCondEq:
+ br_op = kMipsBeq;
+ cmp_zero = true;
+ break;
+ case kCondNe:
+ br_op = kMipsBne;
+ cmp_zero = true;
+ break;
+ case kCondCc:
+ slt_op = kMipsSltu;
+ br_op = kMipsBnez;
+ break;
+ case kCondCs:
+ slt_op = kMipsSltu;
+ br_op = kMipsBeqz;
+ break;
+ case kCondGe:
+ slt_op = kMipsSlt;
+ br_op = kMipsBeqz;
+ break;
+ case kCondGt:
+ slt_op = kMipsSlt;
+ br_op = kMipsBnez;
+ swapped = true;
+ break;
+ case kCondLe:
+ slt_op = kMipsSlt;
+ br_op = kMipsBeqz;
+ swapped = true;
+ break;
+ case kCondLt:
+ slt_op = kMipsSlt;
+ br_op = kMipsBnez;
+ break;
+ case kCondHi: // Gtu
+ slt_op = kMipsSltu;
+ br_op = kMipsBnez;
+ swapped = true;
+ break;
+ default:
+ LOG(FATAL) << "No support for ConditionCode: " << cond;
+ return NULL;
+ }
+ if (cmp_zero) {
+ branch = NewLIR2(br_op, src1, src2);
+ } else {
+ int t_reg = AllocTemp();
+ if (swapped) {
+ NewLIR3(slt_op, t_reg, src2, src1);
+ } else {
+ NewLIR3(slt_op, t_reg, src1, src2);
+ }
+ branch = NewLIR1(br_op, t_reg);
+ FreeTemp(t_reg);
+ }
+ branch->target = target;
+ return branch;
+}
+
+LIR* MipsMir2Lir::OpCmpImmBranch(ConditionCode cond, int reg,
+ int check_value, LIR* target)
+{
+ LIR* branch;
+ if (check_value != 0) {
+ // TUNING: handle s16 & kCondLt/Mi case using slti
+ int t_reg = AllocTemp();
+ LoadConstant(t_reg, check_value);
+ branch = OpCmpBranch(cond, reg, t_reg, target);
+ FreeTemp(t_reg);
+ return branch;
+ }
+ MipsOpCode opc;
+ switch (cond) {
+ case kCondEq: opc = kMipsBeqz; break;
+ case kCondGe: opc = kMipsBgez; break;
+ case kCondGt: opc = kMipsBgtz; break;
+ case kCondLe: opc = kMipsBlez; break;
+ //case KCondMi:
+ case kCondLt: opc = kMipsBltz; break;
+ case kCondNe: opc = kMipsBnez; break;
+ default:
+ // Tuning: use slti when applicable
+ int t_reg = AllocTemp();
+ LoadConstant(t_reg, check_value);
+ branch = OpCmpBranch(cond, reg, t_reg, target);
+ FreeTemp(t_reg);
+ return branch;
+ }
+ branch = NewLIR1(opc, reg);
+ branch->target = target;
+ return branch;
+}
+
+LIR* MipsMir2Lir::OpRegCopyNoInsert(int r_dest, int r_src)
+{
+ if (MIPS_FPREG(r_dest) || MIPS_FPREG(r_src))
+ return OpFpRegCopy(r_dest, r_src);
+ LIR* res = RawLIR(current_dalvik_offset_, kMipsMove,
+ r_dest, r_src);
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
+ res->flags.is_nop = true;
+ }
+ return res;
+}
+
+LIR* MipsMir2Lir::OpRegCopy(int r_dest, int r_src)
+{
+ LIR *res = OpRegCopyNoInsert(r_dest, r_src);
+ AppendLIR(res);
+ return res;
+}
+
+void MipsMir2Lir::OpRegCopyWide(int dest_lo, int dest_hi, int src_lo,
+ int src_hi)
+{
+ bool dest_fp = MIPS_FPREG(dest_lo) && MIPS_FPREG(dest_hi);
+ bool src_fp = MIPS_FPREG(src_lo) && MIPS_FPREG(src_hi);
+ assert(MIPS_FPREG(src_lo) == MIPS_FPREG(src_hi));
+ assert(MIPS_FPREG(dest_lo) == MIPS_FPREG(dest_hi));
+ if (dest_fp) {
+ if (src_fp) {
+ OpRegCopy(S2d(dest_lo, dest_hi), S2d(src_lo, src_hi));
+ } else {
+ /* note the operands are swapped for the mtc1 instr */
+ NewLIR2(kMipsMtc1, src_lo, dest_lo);
+ NewLIR2(kMipsMtc1, src_hi, dest_hi);
+ }
+ } else {
+ if (src_fp) {
+ NewLIR2(kMipsMfc1, dest_lo, src_lo);
+ NewLIR2(kMipsMfc1, dest_hi, src_hi);
+ } else {
+ // Handle overlap
+ if (src_hi == dest_lo) {
+ OpRegCopy(dest_hi, src_hi);
+ OpRegCopy(dest_lo, src_lo);
+ } else {
+ OpRegCopy(dest_lo, src_lo);
+ OpRegCopy(dest_hi, src_hi);
+ }
+ }
+ }
+}
+
+void MipsMir2Lir::GenSelect(BasicBlock* bb, MIR* mir)
+{
+ UNIMPLEMENTED(FATAL) << "Need codegen for select";
+}
+
+void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir)
+{
+ UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch";
+}
+
+LIR* MipsMir2Lir::GenRegMemCheck(ConditionCode c_code,
+ int reg1, int base, int offset, ThrowKind kind)
+{
+ LOG(FATAL) << "Unexpected use of GenRegMemCheck for Arm";
+ return NULL;
+}
+
+RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, int reg1, int reg2,
+ bool is_div)
+{
+ NewLIR4(kMipsDiv, r_HI, r_LO, reg1, reg2);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (is_div) {
+ NewLIR2(kMipsMflo, rl_result.low_reg, r_LO);
+ } else {
+ NewLIR2(kMipsMfhi, rl_result.low_reg, r_HI);
+ }
+ return rl_result;
+}
+
+RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, int reg1, int lit,
+ bool is_div)
+{
+ int t_reg = AllocTemp();
+ NewLIR3(kMipsAddiu, t_reg, r_ZERO, lit);
+ NewLIR4(kMipsDiv, r_HI, r_LO, reg1, t_reg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (is_div) {
+ NewLIR2(kMipsMflo, rl_result.low_reg, r_LO);
+ } else {
+ NewLIR2(kMipsMfhi, rl_result.low_reg, r_HI);
+ }
+ FreeTemp(t_reg);
+ return rl_result;
+}
+
+void MipsMir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset)
+{
+ LOG(FATAL) << "Unexpected use of OpLea for Arm";
+}
+
+void MipsMir2Lir::OpTlsCmp(int offset, int val)
+{
+ LOG(FATAL) << "Unexpected use of OpTlsCmp for Arm";
+}
+
+bool MipsMir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) {
+ DCHECK_NE(cu_->instruction_set, kThumb2);
+ return false;
+}
+
+bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) {
+ DCHECK_NE(cu_->instruction_set, kThumb2);
+ return false;
+}
+
+LIR* MipsMir2Lir::OpPcRelLoad(int reg, LIR* target) {
+ LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips";
+ return NULL;
+}
+
+LIR* MipsMir2Lir::OpVldm(int rBase, int count)
+{
+ LOG(FATAL) << "Unexpected use of OpVldm for Mips";
+ return NULL;
+}
+
+LIR* MipsMir2Lir::OpVstm(int rBase, int count)
+{
+ LOG(FATAL) << "Unexpected use of OpVstm for Mips";
+ return NULL;
+}
+
+void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
+ RegLocation rl_result, int lit,
+ int first_bit, int second_bit)
+{
+ int t_reg = AllocTemp();
+ OpRegRegImm(kOpLsl, t_reg, rl_src.low_reg, second_bit - first_bit);
+ OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, t_reg);
+ FreeTemp(t_reg);
+ if (first_bit != 0) {
+ OpRegRegImm(kOpLsl, rl_result.low_reg, rl_result.low_reg, first_bit);
+ }
+}
+
+void MipsMir2Lir::GenDivZeroCheck(int reg_lo, int reg_hi)
+{
+ int t_reg = AllocTemp();
+ OpRegRegReg(kOpOr, t_reg, reg_lo, reg_hi);
+ GenImmedCheck(kCondEq, t_reg, 0, kThrowDivZero);
+ FreeTemp(t_reg);
+}
+
+// Test suspend flag, return target of taken suspend branch
+LIR* MipsMir2Lir::OpTestSuspend(LIR* target)
+{
+ OpRegImm(kOpSub, rMIPS_SUSPEND, 1);
+ return OpCmpImmBranch((target == NULL) ? kCondEq : kCondNe, rMIPS_SUSPEND, 0, target);
+}
+
+// Decrement register and branch on condition
+LIR* MipsMir2Lir::OpDecAndBranch(ConditionCode c_code, int reg, LIR* target)
+{
+ OpRegImm(kOpSub, reg, 1);
+ return OpCmpImmBranch(c_code, reg, 0, target);
+}
+
+bool MipsMir2Lir::SmallLiteralDivide(Instruction::Code dalvik_opcode,
+ RegLocation rl_src, RegLocation rl_dest, int lit)
+{
+ LOG(FATAL) << "Unexpected use of smallLiteralDive in Mips";
+ return false;
+}
+
+LIR* MipsMir2Lir::OpIT(ConditionCode cond, const char* guide)
+{
+ LOG(FATAL) << "Unexpected use of OpIT in Mips";
+ return NULL;
+}
+
+void MipsMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenMulLong for Mips";
+}
+
+void MipsMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ /*
+ * [v1 v0] = [a1 a0] + [a3 a2];
+ * addu v0,a2,a0
+ * addu t1,a3,a1
+ * sltu v1,v0,a2
+ * addu v1,v1,t1
+ */
+
+ OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src2.low_reg, rl_src1.low_reg);
+ int t_reg = AllocTemp();
+ OpRegRegReg(kOpAdd, t_reg, rl_src2.high_reg, rl_src1.high_reg);
+ NewLIR3(kMipsSltu, rl_result.high_reg, rl_result.low_reg, rl_src2.low_reg);
+ OpRegRegReg(kOpAdd, rl_result.high_reg, rl_result.high_reg, t_reg);
+ FreeTemp(t_reg);
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void MipsMir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ /*
+ * [v1 v0] = [a1 a0] - [a3 a2];
+ * sltu t1,a0,a2
+ * subu v0,a0,a2
+ * subu v1,a1,a3
+ * subu v1,v1,t1
+ */
+
+ int t_reg = AllocTemp();
+ NewLIR3(kMipsSltu, t_reg, rl_src1.low_reg, rl_src2.low_reg);
+ OpRegRegReg(kOpSub, rl_result.low_reg, rl_src1.low_reg, rl_src2.low_reg);
+ OpRegRegReg(kOpSub, rl_result.high_reg, rl_src1.high_reg, rl_src2.high_reg);
+ OpRegRegReg(kOpSub, rl_result.high_reg, rl_result.high_reg, t_reg);
+ FreeTemp(t_reg);
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void MipsMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src)
+{
+ rl_src = LoadValueWide(rl_src, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ /*
+ * [v1 v0] = -[a1 a0]
+ * negu v0,a0
+ * negu v1,a1
+ * sltu t1,r_zero
+ * subu v1,v1,t1
+ */
+
+ OpRegReg(kOpNeg, rl_result.low_reg, rl_src.low_reg);
+ OpRegReg(kOpNeg, rl_result.high_reg, rl_src.high_reg);
+ int t_reg = AllocTemp();
+ NewLIR3(kMipsSltu, t_reg, r_ZERO, rl_result.low_reg);
+ OpRegRegReg(kOpSub, rl_result.high_reg, rl_result.high_reg, t_reg);
+ FreeTemp(t_reg);
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void MipsMir2Lir::GenAndLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenAndLong for Mips";
+}
+
+void MipsMir2Lir::GenOrLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenOrLong for Mips";
+}
+
+void MipsMir2Lir::GenXorLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenXorLong for Mips";
+}
+
+/*
+ * Generate array load
+ */
+void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_dest, int scale)
+{
+ RegisterClass reg_class = oat_reg_class_by_size(size);
+ int len_offset = mirror::Array::LengthOffset().Int32Value();
+ int data_offset;
+ RegLocation rl_result;
+ rl_array = LoadValue(rl_array, kCoreReg);
+ rl_index = LoadValue(rl_index, kCoreReg);
+
+ if (size == kLong || size == kDouble) {
+ data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
+ } else {
+ data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
+ }
+
+ /* null object? */
+ GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
+
+ int reg_ptr = AllocTemp();
+ bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
+ int reg_len = INVALID_REG;
+ if (needs_range_check) {
+ reg_len = AllocTemp();
+ /* Get len */
+ LoadWordDisp(rl_array.low_reg, len_offset, reg_len);
+ }
+ /* reg_ptr -> array data */
+ OpRegRegImm(kOpAdd, reg_ptr, rl_array.low_reg, data_offset);
+ FreeTemp(rl_array.low_reg);
+ if ((size == kLong) || (size == kDouble)) {
+ if (scale) {
+ int r_new_index = AllocTemp();
+ OpRegRegImm(kOpLsl, r_new_index, rl_index.low_reg, scale);
+ OpRegReg(kOpAdd, reg_ptr, r_new_index);
+ FreeTemp(r_new_index);
+ } else {
+ OpRegReg(kOpAdd, reg_ptr, rl_index.low_reg);
+ }
+ FreeTemp(rl_index.low_reg);
+ rl_result = EvalLoc(rl_dest, reg_class, true);
+
+ if (needs_range_check) {
+ // TODO: change kCondCS to a more meaningful name, is the sense of
+ // carry-set/clear flipped?
+ GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
+ FreeTemp(reg_len);
+ }
+ LoadBaseDispWide(reg_ptr, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG);
+
+ FreeTemp(reg_ptr);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ rl_result = EvalLoc(rl_dest, reg_class, true);
+
+ if (needs_range_check) {
+ // TODO: change kCondCS to a more meaningful name, is the sense of
+ // carry-set/clear flipped?
+ GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
+ FreeTemp(reg_len);
+ }
+ LoadBaseIndexed(reg_ptr, rl_index.low_reg, rl_result.low_reg, scale, size);
+
+ FreeTemp(reg_ptr);
+ StoreValue(rl_dest, rl_result);
+ }
+}
+
+/*
+ * Generate array store
+ *
+ */
+void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale)
+{
+ RegisterClass reg_class = oat_reg_class_by_size(size);
+ int len_offset = mirror::Array::LengthOffset().Int32Value();
+ int data_offset;
+
+ if (size == kLong || size == kDouble) {
+ data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
+ } else {
+ data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
+ }
+
+ rl_array = LoadValue(rl_array, kCoreReg);
+ rl_index = LoadValue(rl_index, kCoreReg);
+ int reg_ptr = INVALID_REG;
+ if (IsTemp(rl_array.low_reg)) {
+ Clobber(rl_array.low_reg);
+ reg_ptr = rl_array.low_reg;
+ } else {
+ reg_ptr = AllocTemp();
+ OpRegCopy(reg_ptr, rl_array.low_reg);
+ }
+
+ /* null object? */
+ GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
+
+ bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
+ int reg_len = INVALID_REG;
+ if (needs_range_check) {
+ reg_len = AllocTemp();
+ //NOTE: max live temps(4) here.
+ /* Get len */
+ LoadWordDisp(rl_array.low_reg, len_offset, reg_len);
+ }
+ /* reg_ptr -> array data */
+ OpRegImm(kOpAdd, reg_ptr, data_offset);
+ /* at this point, reg_ptr points to array, 2 live temps */
+ if ((size == kLong) || (size == kDouble)) {
+ //TUNING: specific wide routine that can handle fp regs
+ if (scale) {
+ int r_new_index = AllocTemp();
+ OpRegRegImm(kOpLsl, r_new_index, rl_index.low_reg, scale);
+ OpRegReg(kOpAdd, reg_ptr, r_new_index);
+ FreeTemp(r_new_index);
+ } else {
+ OpRegReg(kOpAdd, reg_ptr, rl_index.low_reg);
+ }
+ rl_src = LoadValueWide(rl_src, reg_class);
+
+ if (needs_range_check) {
+ GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
+ FreeTemp(reg_len);
+ }
+
+ StoreBaseDispWide(reg_ptr, 0, rl_src.low_reg, rl_src.high_reg);
+
+ FreeTemp(reg_ptr);
+ } else {
+ rl_src = LoadValue(rl_src, reg_class);
+ if (needs_range_check) {
+ GenRegRegCheck(kCondCs, rl_index.low_reg, reg_len, kThrowArrayBounds);
+ FreeTemp(reg_len);
+ }
+ StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg,
+ scale, size);
+ }
+}
+
+/*
+ * Generate array store
+ *
+ */
+void MipsMir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale)
+{
+ int len_offset = mirror::Array::LengthOffset().Int32Value();
+ int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
+
+ FlushAllRegs(); // Use explicit registers
+ LockCallTemps();
+
+ int r_value = TargetReg(kArg0); // Register holding value
+ int r_array_class = TargetReg(kArg1); // Register holding array's Class
+ int r_array = TargetReg(kArg2); // Register holding array
+ int r_index = TargetReg(kArg3); // Register holding index into array
+
+ LoadValueDirectFixed(rl_array, r_array); // Grab array
+ LoadValueDirectFixed(rl_src, r_value); // Grab value
+ LoadValueDirectFixed(rl_index, r_index); // Grab index
+
+ GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE?
+
+ // Store of null?
+ LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL);
+
+ // Get the array's class.
+ LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
+ CallRuntimeHelperRegReg(ENTRYPOINT_OFFSET(pCanPutArrayElementFromCode), r_value,
+ r_array_class, true);
+ // Redo LoadValues in case they didn't survive the call.
+ LoadValueDirectFixed(rl_array, r_array); // Reload array
+ LoadValueDirectFixed(rl_index, r_index); // Reload index
+ LoadValueDirectFixed(rl_src, r_value); // Reload value
+ r_array_class = INVALID_REG;
+
+ // Branch here if value to be stored == null
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ null_value_check->target = target;
+
+ bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
+ int reg_len = INVALID_REG;
+ if (needs_range_check) {
+ reg_len = TargetReg(kArg1);
+ LoadWordDisp(r_array, len_offset, reg_len); // Get len
+ }
+ /* r_ptr -> array data */
+ int r_ptr = AllocTemp();
+ OpRegRegImm(kOpAdd, r_ptr, r_array, data_offset);
+ if (needs_range_check) {
+ GenRegRegCheck(kCondCs, r_index, reg_len, kThrowArrayBounds);
+ }
+ StoreBaseIndexed(r_ptr, r_index, r_value, scale, kWord);
+ FreeTemp(r_ptr);
+ FreeTemp(r_index);
+ if (!mir_graph_->IsConstantNullRef(rl_src)) {
+ MarkGCCard(r_value, r_array);
+ }
+}
+
+void MipsMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_shift)
+{
+ // Default implementation is just to ignore the constant case.
+ GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift);
+}
+
+void MipsMir2Lir::GenArithImmOpLong(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2)
+{
+ // Default - bail to non-const handler.
+ GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/mips/mips_lir.h b/compiler/dex/quick/mips/mips_lir.h
new file mode 100644
index 0000000..ceab9ab
--- /dev/null
+++ b/compiler/dex/quick/mips/mips_lir.h
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_QUICK_MIPS_MIPSLIR_H_
+#define ART_SRC_COMPILER_DEX_QUICK_MIPS_MIPSLIR_H_
+
+#include "dex/compiler_internals.h"
+
+namespace art {
+
+/*
+ * Runtime register conventions.
+ *
+ * zero is always the value 0
+ * at is scratch (normally used as temp reg by assembler)
+ * v0, v1 are scratch (normally hold subroutine return values)
+ * a0-a3 are scratch (normally hold subroutine arguments)
+ * t0-t8 are scratch
+ * t9 is scratch (normally used for function calls)
+ * s0 (rMIPS_SUSPEND) is reserved [holds suspend-check counter]
+ * s1 (rMIPS_SELF) is reserved [holds current &Thread]
+ * s2-s7 are callee save (promotion target)
+ * k0, k1 are reserved for use by interrupt handlers
+ * gp is reserved for global pointer
+ * sp is reserved
+ * s8 is callee save (promotion target)
+ * ra is scratch (normally holds the return addr)
+ *
+ * Preserved across C calls: s0-s8
+ * Trashed across C calls: at, v0-v1, a0-a3, t0-t9, gp, ra
+ *
+ * Floating pointer registers
+ * NOTE: there are 32 fp registers (16 df pairs), but currently
+ * only support 16 fp registers (8 df pairs).
+ * f0-f15
+ * df0-df7, where df0={f0,f1}, df1={f2,f3}, ... , df7={f14,f15}
+ *
+ * f0-f15 (df0-df7) trashed across C calls
+ *
+ * For mips32 code use:
+ * a0-a3 to hold operands
+ * v0-v1 to hold results
+ * t0-t9 for temps
+ *
+ * All jump/branch instructions have a delay slot after it.
+ *
+ * Stack frame diagram (stack grows down, higher addresses at top):
+ *
+ * +------------------------+
+ * | IN[ins-1] | {Note: resides in caller's frame}
+ * | . |
+ * | IN[0] |
+ * | caller's Method* |
+ * +========================+ {Note: start of callee's frame}
+ * | spill region | {variable sized - will include lr if non-leaf.}
+ * +------------------------+
+ * | ...filler word... | {Note: used as 2nd word of V[locals-1] if long]
+ * +------------------------+
+ * | V[locals-1] |
+ * | V[locals-2] |
+ * | . |
+ * | . |
+ * | V[1] |
+ * | V[0] |
+ * +------------------------+
+ * | 0 to 3 words padding |
+ * +------------------------+
+ * | OUT[outs-1] |
+ * | OUT[outs-2] |
+ * | . |
+ * | OUT[0] |
+ * | cur_method* | <<== sp w/ 16-byte alignment
+ * +========================+
+ */
+
+// Offset to distingish FP regs.
+#define MIPS_FP_REG_OFFSET 32
+// Offset to distinguish DP FP regs.
+#define MIPS_FP_DOUBLE 64
+// Offset to distingish the extra regs.
+#define MIPS_EXTRA_REG_OFFSET 128
+// Reg types.
+#define MIPS_REGTYPE(x) (x & (MIPS_FP_REG_OFFSET | MIPS_FP_DOUBLE))
+#define MIPS_FPREG(x) ((x & MIPS_FP_REG_OFFSET) == MIPS_FP_REG_OFFSET)
+#define MIPS_EXTRAREG(x) ((x & MIPS_EXTRA_REG_OFFSET) == MIPS_EXTRA_REG_OFFSET)
+#define MIPS_DOUBLEREG(x) ((x & MIPS_FP_DOUBLE) == MIPS_FP_DOUBLE)
+#define MIPS_SINGLEREG(x) (MIPS_FPREG(x) && !MIPS_DOUBLEREG(x))
+/*
+ * Note: the low register of a floating point pair is sufficient to
+ * create the name of a double, but require both names to be passed to
+ * allow for asserts to verify that the pair is consecutive if significant
+ * rework is done in this area. Also, it is a good reminder in the calling
+ * code that reg locations always describe doubles as a pair of singles.
+ */
+#define MIPS_S2D(x,y) ((x) | MIPS_FP_DOUBLE)
+// Mask to strip off fp flags.
+#define MIPS_FP_REG_MASK (MIPS_FP_REG_OFFSET-1)
+
+#ifdef HAVE_LITTLE_ENDIAN
+#define LOWORD_OFFSET 0
+#define HIWORD_OFFSET 4
+#define r_ARG0 r_A0
+#define r_ARG1 r_A1
+#define r_ARG2 r_A2
+#define r_ARG3 r_A3
+#define r_RESULT0 r_V0
+#define r_RESULT1 r_V1
+#else
+#define LOWORD_OFFSET 4
+#define HIWORD_OFFSET 0
+#define r_ARG0 r_A1
+#define r_ARG1 r_A0
+#define r_ARG2 r_A3
+#define r_ARG3 r_A2
+#define r_RESULT0 r_V1
+#define r_RESULT1 r_V0
+#endif
+
+// These are the same for both big and little endian.
+#define r_FARG0 r_F12
+#define r_FARG1 r_F13
+#define r_FARG2 r_F14
+#define r_FARG3 r_F15
+#define r_FRESULT0 r_F0
+#define r_FRESULT1 r_F1
+
+// Regs not used for Mips.
+#define rMIPS_LR INVALID_REG
+#define rMIPS_PC INVALID_REG
+
+// RegisterLocation templates return values (r_V0, or r_V0/r_V1).
+#define MIPS_LOC_C_RETURN {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, r_V0, INVALID_REG, \
+ INVALID_SREG, INVALID_SREG}
+#define MIPS_LOC_C_RETURN_FLOAT {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, r_FRESULT0, \
+ INVALID_REG, INVALID_SREG, INVALID_SREG}
+#define MIPS_LOC_C_RETURN_WIDE {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r_RESULT0, \
+ r_RESULT1, INVALID_SREG, INVALID_SREG}
+#define MIPS_LOC_C_RETURN_DOUBLE {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r_FRESULT0,\
+ r_FRESULT1, INVALID_SREG, INVALID_SREG}
+
+enum MipsResourceEncodingPos {
+ kMipsGPReg0 = 0,
+ kMipsRegSP = 29,
+ kMipsRegLR = 31,
+ kMipsFPReg0 = 32, // only 16 fp regs supported currently.
+ kMipsFPRegEnd = 48,
+ kMipsRegHI = kMipsFPRegEnd,
+ kMipsRegLO,
+ kMipsRegPC,
+ kMipsRegEnd = 51,
+};
+
+#define ENCODE_MIPS_REG_LIST(N) (static_cast<uint64_t>(N))
+#define ENCODE_MIPS_REG_SP (1ULL << kMipsRegSP)
+#define ENCODE_MIPS_REG_LR (1ULL << kMipsRegLR)
+#define ENCODE_MIPS_REG_PC (1ULL << kMipsRegPC)
+
+enum MipsNativeRegisterPool {
+ r_ZERO = 0,
+ r_AT = 1,
+ r_V0 = 2,
+ r_V1 = 3,
+ r_A0 = 4,
+ r_A1 = 5,
+ r_A2 = 6,
+ r_A3 = 7,
+ r_T0 = 8,
+ r_T1 = 9,
+ r_T2 = 10,
+ r_T3 = 11,
+ r_T4 = 12,
+ r_T5 = 13,
+ r_T6 = 14,
+ r_T7 = 15,
+ r_S0 = 16,
+ r_S1 = 17,
+ r_S2 = 18,
+ r_S3 = 19,
+ r_S4 = 20,
+ r_S5 = 21,
+ r_S6 = 22,
+ r_S7 = 23,
+ r_T8 = 24,
+ r_T9 = 25,
+ r_K0 = 26,
+ r_K1 = 27,
+ r_GP = 28,
+ r_SP = 29,
+ r_FP = 30,
+ r_RA = 31,
+
+ r_F0 = 0 + MIPS_FP_REG_OFFSET,
+ r_F1,
+ r_F2,
+ r_F3,
+ r_F4,
+ r_F5,
+ r_F6,
+ r_F7,
+ r_F8,
+ r_F9,
+ r_F10,
+ r_F11,
+ r_F12,
+ r_F13,
+ r_F14,
+ r_F15,
+#if 0
+ /*
+ * TODO: The shared resource mask doesn't have enough bit positions to describe all
+ * MIPS registers. Expand it and enable use of fp registers 16 through 31.
+ */
+ r_F16,
+ r_F17,
+ r_F18,
+ r_F19,
+ r_F20,
+ r_F21,
+ r_F22,
+ r_F23,
+ r_F24,
+ r_F25,
+ r_F26,
+ r_F27,
+ r_F28,
+ r_F29,
+ r_F30,
+ r_F31,
+#endif
+ r_DF0 = r_F0 + MIPS_FP_DOUBLE,
+ r_DF1 = r_F2 + MIPS_FP_DOUBLE,
+ r_DF2 = r_F4 + MIPS_FP_DOUBLE,
+ r_DF3 = r_F6 + MIPS_FP_DOUBLE,
+ r_DF4 = r_F8 + MIPS_FP_DOUBLE,
+ r_DF5 = r_F10 + MIPS_FP_DOUBLE,
+ r_DF6 = r_F12 + MIPS_FP_DOUBLE,
+ r_DF7 = r_F14 + MIPS_FP_DOUBLE,
+#if 0 // TODO: expand resource mask to enable use of all MIPS fp registers.
+ r_DF8 = r_F16 + MIPS_FP_DOUBLE,
+ r_DF9 = r_F18 + MIPS_FP_DOUBLE,
+ r_DF10 = r_F20 + MIPS_FP_DOUBLE,
+ r_DF11 = r_F22 + MIPS_FP_DOUBLE,
+ r_DF12 = r_F24 + MIPS_FP_DOUBLE,
+ r_DF13 = r_F26 + MIPS_FP_DOUBLE,
+ r_DF14 = r_F28 + MIPS_FP_DOUBLE,
+ r_DF15 = r_F30 + MIPS_FP_DOUBLE,
+#endif
+ r_HI = MIPS_EXTRA_REG_OFFSET,
+ r_LO,
+ r_PC,
+};
+
+#define rMIPS_SUSPEND r_S0
+#define rMIPS_SELF r_S1
+#define rMIPS_SP r_SP
+#define rMIPS_ARG0 r_ARG0
+#define rMIPS_ARG1 r_ARG1
+#define rMIPS_ARG2 r_ARG2
+#define rMIPS_ARG3 r_ARG3
+#define rMIPS_FARG0 r_FARG0
+#define rMIPS_FARG1 r_FARG1
+#define rMIPS_FARG2 r_FARG2
+#define rMIPS_FARG3 r_FARG3
+#define rMIPS_RET0 r_RESULT0
+#define rMIPS_RET1 r_RESULT1
+#define rMIPS_INVOKE_TGT r_T9
+#define rMIPS_COUNT INVALID_REG
+
+enum MipsShiftEncodings {
+ kMipsLsl = 0x0,
+ kMipsLsr = 0x1,
+ kMipsAsr = 0x2,
+ kMipsRor = 0x3
+};
+
+// MIPS sync kinds (Note: support for kinds other than kSYNC0 may not exist).
+#define kSYNC0 0x00
+#define kSYNC_WMB 0x04
+#define kSYNC_MB 0x01
+#define kSYNC_ACQUIRE 0x11
+#define kSYNC_RELEASE 0x12
+#define kSYNC_RMB 0x13
+
+// TODO: Use smaller hammer when appropriate for target CPU.
+#define kST kSYNC0
+#define kSY kSYNC0
+
+/*
+ * The following enum defines the list of supported Thumb instructions by the
+ * assembler. Their corresponding EncodingMap positions will be defined in
+ * Assemble.cc.
+ */
+enum MipsOpCode {
+ kMipsFirst = 0,
+ kMips32BitData = kMipsFirst, // data [31..0].
+ kMipsAddiu, // addiu t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0].
+ kMipsAddu, // add d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100001].
+ kMipsAnd, // and d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100100].
+ kMipsAndi, // andi t,s,imm16 [001100] s[25..21] t[20..16] imm16[15..0].
+ kMipsB, // b o [0001000000000000] o[15..0].
+ kMipsBal, // bal o [0000010000010001] o[15..0].
+ // NOTE: the code tests the range kMipsBeq thru kMipsBne, so adding an instruction in this
+ // range may require updates.
+ kMipsBeq, // beq s,t,o [000100] s[25..21] t[20..16] o[15..0].
+ kMipsBeqz, // beqz s,o [000100] s[25..21] [00000] o[15..0].
+ kMipsBgez, // bgez s,o [000001] s[25..21] [00001] o[15..0].
+ kMipsBgtz, // bgtz s,o [000111] s[25..21] [00000] o[15..0].
+ kMipsBlez, // blez s,o [000110] s[25..21] [00000] o[15..0].
+ kMipsBltz, // bltz s,o [000001] s[25..21] [00000] o[15..0].
+ kMipsBnez, // bnez s,o [000101] s[25..21] [00000] o[15..0].
+ kMipsBne, // bne s,t,o [000101] s[25..21] t[20..16] o[15..0].
+ kMipsDiv, // div s,t [000000] s[25..21] t[20..16] [0000000000011010].
+#if __mips_isa_rev>=2
+ kMipsExt, // ext t,s,p,z [011111] s[25..21] t[20..16] z[15..11] p[10..6] [000000].
+#endif
+ kMipsJal, // jal t [000011] t[25..0].
+ kMipsJalr, // jalr d,s [000000] s[25..21] [00000] d[15..11] hint[10..6] [001001].
+ kMipsJr, // jr s [000000] s[25..21] [0000000000] hint[10..6] [001000].
+ kMipsLahi, // lui t,imm16 [00111100000] t[20..16] imm16[15..0] load addr hi.
+ kMipsLalo, // ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0] load addr lo.
+ kMipsLui, // lui t,imm16 [00111100000] t[20..16] imm16[15..0].
+ kMipsLb, // lb t,o(b) [100000] b[25..21] t[20..16] o[15..0].
+ kMipsLbu, // lbu t,o(b) [100100] b[25..21] t[20..16] o[15..0].
+ kMipsLh, // lh t,o(b) [100001] b[25..21] t[20..16] o[15..0].
+ kMipsLhu, // lhu t,o(b) [100101] b[25..21] t[20..16] o[15..0].
+ kMipsLw, // lw t,o(b) [100011] b[25..21] t[20..16] o[15..0].
+ kMipsMfhi, // mfhi d [0000000000000000] d[15..11] [00000010000].
+ kMipsMflo, // mflo d [0000000000000000] d[15..11] [00000010010].
+ kMipsMove, // move d,s [000000] s[25..21] [00000] d[15..11] [00000100101].
+ kMipsMovz, // movz d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000001010].
+ kMipsMul, // mul d,s,t [011100] s[25..21] t[20..16] d[15..11] [00000000010].
+ kMipsNop, // nop [00000000000000000000000000000000].
+ kMipsNor, // nor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100111].
+ kMipsOr, // or d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100101].
+ kMipsOri, // ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0].
+ kMipsPref, // pref h,o(b) [101011] b[25..21] h[20..16] o[15..0].
+ kMipsSb, // sb t,o(b) [101000] b[25..21] t[20..16] o[15..0].
+#if __mips_isa_rev>=2
+ kMipsSeb, // seb d,t [01111100000] t[20..16] d[15..11] [10000100000].
+ kMipsSeh, // seh d,t [01111100000] t[20..16] d[15..11] [11000100000].
+#endif
+ kMipsSh, // sh t,o(b) [101001] b[25..21] t[20..16] o[15..0].
+ kMipsSll, // sll d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [000000].
+ kMipsSllv, // sllv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000100].
+ kMipsSlt, // slt d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101010].
+ kMipsSlti, // slti t,s,imm16 [001010] s[25..21] t[20..16] imm16[15..0].
+ kMipsSltu, // sltu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101011].
+ kMipsSra, // sra d,s,imm5 [00000000000] t[20..16] d[15..11] imm5[10..6] [000011].
+ kMipsSrav, // srav d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000111].
+ kMipsSrl, // srl d,t,a [00000000000] t[20..16] d[20..16] a[10..6] [000010].
+ kMipsSrlv, // srlv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000110].
+ kMipsSubu, // subu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100011].
+ kMipsSw, // sw t,o(b) [101011] b[25..21] t[20..16] o[15..0].
+ kMipsXor, // xor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100110].
+ kMipsXori, // xori t,s,imm16 [001110] s[25..21] t[20..16] imm16[15..0].
+ kMipsFadds, // add.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000000].
+ kMipsFsubs, // sub.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000001].
+ kMipsFmuls, // mul.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000010].
+ kMipsFdivs, // div.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000011].
+ kMipsFaddd, // add.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000000].
+ kMipsFsubd, // sub.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000001].
+ kMipsFmuld, // mul.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000010].
+ kMipsFdivd, // div.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000011].
+ kMipsFcvtsd,// cvt.s.d d,s [01000110001] [00000] s[15..11] d[10..6] [100000].
+ kMipsFcvtsw,// cvt.s.w d,s [01000110100] [00000] s[15..11] d[10..6] [100000].
+ kMipsFcvtds,// cvt.d.s d,s [01000110000] [00000] s[15..11] d[10..6] [100001].
+ kMipsFcvtdw,// cvt.d.w d,s [01000110100] [00000] s[15..11] d[10..6] [100001].
+ kMipsFcvtws,// cvt.w.d d,s [01000110000] [00000] s[15..11] d[10..6] [100100].
+ kMipsFcvtwd,// cvt.w.d d,s [01000110001] [00000] s[15..11] d[10..6] [100100].
+ kMipsFmovs, // mov.s d,s [01000110000] [00000] s[15..11] d[10..6] [000110].
+ kMipsFmovd, // mov.d d,s [01000110001] [00000] s[15..11] d[10..6] [000110].
+ kMipsFlwc1, // lwc1 t,o(b) [110001] b[25..21] t[20..16] o[15..0].
+ kMipsFldc1, // ldc1 t,o(b) [110101] b[25..21] t[20..16] o[15..0].
+ kMipsFswc1, // swc1 t,o(b) [111001] b[25..21] t[20..16] o[15..0].
+ kMipsFsdc1, // sdc1 t,o(b) [111101] b[25..21] t[20..16] o[15..0].
+ kMipsMfc1, // mfc1 t,s [01000100000] t[20..16] s[15..11] [00000000000].
+ kMipsMtc1, // mtc1 t,s [01000100100] t[20..16] s[15..11] [00000000000].
+ kMipsDelta, // Psuedo for ori t, s, <label>-<label>.
+ kMipsDeltaHi, // Pseudo for lui t, high16(<label>-<label>).
+ kMipsDeltaLo, // Pseudo for ori t, s, low16(<label>-<label>).
+ kMipsCurrPC, // jal to .+8 to materialize pc.
+ kMipsSync, // sync kind [000000] [0000000000000000] s[10..6] [001111].
+ kMipsUndefined, // undefined [011001xxxxxxxxxxxxxxxx].
+ kMipsLast
+};
+
+// Instruction assembly field_loc kind.
+enum MipsEncodingKind {
+ kFmtUnused,
+ kFmtBitBlt, /* Bit string using end/start */
+ kFmtDfp, /* Double FP reg */
+ kFmtSfp, /* Single FP reg */
+ kFmtBlt5_2, /* Same 5-bit field to 2 locations */
+};
+
+// Struct used to define the snippet positions for each MIPS opcode.
+struct MipsEncodingMap {
+ uint32_t skeleton;
+ struct {
+ MipsEncodingKind kind;
+ int end; // end for kFmtBitBlt, 1-bit slice end for FP regs.
+ int start; // start for kFmtBitBlt, 4-bit slice end for FP regs.
+ } field_loc[4];
+ MipsOpCode opcode;
+ uint64_t flags;
+ const char *name;
+ const char* fmt;
+ int size; // Note: size is in bytes.
+};
+
+extern MipsEncodingMap EncodingMap[kMipsLast];
+
+#define IS_UIMM16(v) ((0 <= (v)) && ((v) <= 65535))
+#define IS_SIMM16(v) ((-32768 <= (v)) && ((v) <= 32766))
+#define IS_SIMM16_2WORD(v) ((-32764 <= (v)) && ((v) <= 32763)) // 2 offsets must fit.
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_QUICK_MIPS_MIPSLIR_H_
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
new file mode 100644
index 0000000..cab2c1b
--- /dev/null
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -0,0 +1,610 @@
+/*
+ * Copyright (C) 2012 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 "codegen_mips.h"
+#include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "mips_lir.h"
+
+#include <string>
+
+namespace art {
+
+static int core_regs[] = {r_ZERO, r_AT, r_V0, r_V1, r_A0, r_A1, r_A2, r_A3,
+ r_T0, r_T1, r_T2, r_T3, r_T4, r_T5, r_T6, r_T7,
+ r_S0, r_S1, r_S2, r_S3, r_S4, r_S5, r_S6, r_S7, r_T8,
+ r_T9, r_K0, r_K1, r_GP, r_SP, r_FP, r_RA};
+static int ReservedRegs[] = {r_ZERO, r_AT, r_S0, r_S1, r_K0, r_K1, r_GP, r_SP,
+ r_RA};
+static int core_temps[] = {r_V0, r_V1, r_A0, r_A1, r_A2, r_A3, r_T0, r_T1, r_T2,
+ r_T3, r_T4, r_T5, r_T6, r_T7, r_T8};
+static int FpRegs[] = {r_F0, r_F1, r_F2, r_F3, r_F4, r_F5, r_F6, r_F7,
+ r_F8, r_F9, r_F10, r_F11, r_F12, r_F13, r_F14, r_F15};
+static int fp_temps[] = {r_F0, r_F1, r_F2, r_F3, r_F4, r_F5, r_F6, r_F7,
+ r_F8, r_F9, r_F10, r_F11, r_F12, r_F13, r_F14, r_F15};
+
+RegLocation MipsMir2Lir::LocCReturn()
+{
+ RegLocation res = MIPS_LOC_C_RETURN;
+ return res;
+}
+
+RegLocation MipsMir2Lir::LocCReturnWide()
+{
+ RegLocation res = MIPS_LOC_C_RETURN_WIDE;
+ return res;
+}
+
+RegLocation MipsMir2Lir::LocCReturnFloat()
+{
+ RegLocation res = MIPS_LOC_C_RETURN_FLOAT;
+ return res;
+}
+
+RegLocation MipsMir2Lir::LocCReturnDouble()
+{
+ RegLocation res = MIPS_LOC_C_RETURN_DOUBLE;
+ return res;
+}
+
+// Return a target-dependent special register.
+int MipsMir2Lir::TargetReg(SpecialTargetRegister reg) {
+ int res = INVALID_REG;
+ switch (reg) {
+ case kSelf: res = rMIPS_SELF; break;
+ case kSuspend: res = rMIPS_SUSPEND; break;
+ case kLr: res = rMIPS_LR; break;
+ case kPc: res = rMIPS_PC; break;
+ case kSp: res = rMIPS_SP; break;
+ case kArg0: res = rMIPS_ARG0; break;
+ case kArg1: res = rMIPS_ARG1; break;
+ case kArg2: res = rMIPS_ARG2; break;
+ case kArg3: res = rMIPS_ARG3; break;
+ case kFArg0: res = rMIPS_FARG0; break;
+ case kFArg1: res = rMIPS_FARG1; break;
+ case kFArg2: res = rMIPS_FARG2; break;
+ case kFArg3: res = rMIPS_FARG3; break;
+ case kRet0: res = rMIPS_RET0; break;
+ case kRet1: res = rMIPS_RET1; break;
+ case kInvokeTgt: res = rMIPS_INVOKE_TGT; break;
+ case kCount: res = rMIPS_COUNT; break;
+ }
+ return res;
+}
+
+// Create a double from a pair of singles.
+int MipsMir2Lir::S2d(int low_reg, int high_reg)
+{
+ return MIPS_S2D(low_reg, high_reg);
+}
+
+// Return mask to strip off fp reg flags and bias.
+uint32_t MipsMir2Lir::FpRegMask()
+{
+ return MIPS_FP_REG_MASK;
+}
+
+// True if both regs single, both core or both double.
+bool MipsMir2Lir::SameRegType(int reg1, int reg2)
+{
+ return (MIPS_REGTYPE(reg1) == MIPS_REGTYPE(reg2));
+}
+
+/*
+ * Decode the register id.
+ */
+uint64_t MipsMir2Lir::GetRegMaskCommon(int reg)
+{
+ uint64_t seed;
+ int shift;
+ int reg_id;
+
+
+ reg_id = reg & 0x1f;
+ /* Each double register is equal to a pair of single-precision FP registers */
+ seed = MIPS_DOUBLEREG(reg) ? 3 : 1;
+ /* FP register starts at bit position 16 */
+ shift = MIPS_FPREG(reg) ? kMipsFPReg0 : 0;
+ /* Expand the double register id into single offset */
+ shift += reg_id;
+ return (seed << shift);
+}
+
+uint64_t MipsMir2Lir::GetPCUseDefEncoding()
+{
+ return ENCODE_MIPS_REG_PC;
+}
+
+
+void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir)
+{
+ DCHECK_EQ(cu_->instruction_set, kMips);
+
+ // Mips-specific resource map setup here.
+ uint64_t flags = MipsMir2Lir::EncodingMap[lir->opcode].flags;
+
+ if (flags & REG_DEF_SP) {
+ lir->def_mask |= ENCODE_MIPS_REG_SP;
+ }
+
+ if (flags & REG_USE_SP) {
+ lir->use_mask |= ENCODE_MIPS_REG_SP;
+ }
+
+ if (flags & REG_DEF_LR) {
+ lir->def_mask |= ENCODE_MIPS_REG_LR;
+ }
+}
+
+/* For dumping instructions */
+#define MIPS_REG_COUNT 32
+static const char *mips_reg_name[MIPS_REG_COUNT] = {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"
+};
+
+/*
+ * Interpret a format string and build a string no longer than size
+ * See format key in Assemble.c.
+ */
+std::string MipsMir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned char* base_addr)
+{
+ std::string buf;
+ int i;
+ const char *fmt_end = &fmt[strlen(fmt)];
+ char tbuf[256];
+ char nc;
+ while (fmt < fmt_end) {
+ int operand;
+ if (*fmt == '!') {
+ fmt++;
+ DCHECK_LT(fmt, fmt_end);
+ nc = *fmt++;
+ if (nc=='!') {
+ strcpy(tbuf, "!");
+ } else {
+ DCHECK_LT(fmt, fmt_end);
+ DCHECK_LT(static_cast<unsigned>(nc-'0'), 4u);
+ operand = lir->operands[nc-'0'];
+ switch (*fmt++) {
+ case 'b':
+ strcpy(tbuf,"0000");
+ for (i=3; i>= 0; i--) {
+ tbuf[i] += operand & 1;
+ operand >>= 1;
+ }
+ break;
+ case 's':
+ sprintf(tbuf,"$f%d",operand & MIPS_FP_REG_MASK);
+ break;
+ case 'S':
+ DCHECK_EQ(((operand & MIPS_FP_REG_MASK) & 1), 0);
+ sprintf(tbuf,"$f%d",operand & MIPS_FP_REG_MASK);
+ break;
+ case 'h':
+ sprintf(tbuf,"%04x", operand);
+ break;
+ case 'M':
+ case 'd':
+ sprintf(tbuf,"%d", operand);
+ break;
+ case 'D':
+ sprintf(tbuf,"%d", operand+1);
+ break;
+ case 'E':
+ sprintf(tbuf,"%d", operand*4);
+ break;
+ case 'F':
+ sprintf(tbuf,"%d", operand*2);
+ break;
+ case 't':
+ sprintf(tbuf,"0x%08x (L%p)", reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 +
+ (operand << 2), lir->target);
+ break;
+ case 'T':
+ sprintf(tbuf,"0x%08x", operand << 2);
+ break;
+ case 'u': {
+ int offset_1 = lir->operands[0];
+ int offset_2 = NEXT_LIR(lir)->operands[0];
+ uintptr_t target =
+ (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) & ~3) +
+ (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc;
+ sprintf(tbuf, "%p", reinterpret_cast<void*>(target));
+ break;
+ }
+
+ /* Nothing to print for BLX_2 */
+ case 'v':
+ strcpy(tbuf, "see above");
+ break;
+ case 'r':
+ DCHECK(operand >= 0 && operand < MIPS_REG_COUNT);
+ strcpy(tbuf, mips_reg_name[operand]);
+ break;
+ case 'N':
+ // Placeholder for delay slot handling
+ strcpy(tbuf, "; nop");
+ break;
+ default:
+ strcpy(tbuf,"DecodeError");
+ break;
+ }
+ buf += tbuf;
+ }
+ } else {
+ buf += *fmt++;
+ }
+ }
+ return buf;
+}
+
+// FIXME: need to redo resource maps for MIPS - fix this at that time
+void MipsMir2Lir::DumpResourceMask(LIR *mips_lir, uint64_t mask, const char *prefix)
+{
+ char buf[256];
+ buf[0] = 0;
+
+ if (mask == ENCODE_ALL) {
+ strcpy(buf, "all");
+ } else {
+ char num[8];
+ int i;
+
+ for (i = 0; i < kMipsRegEnd; i++) {
+ if (mask & (1ULL << i)) {
+ sprintf(num, "%d ", i);
+ strcat(buf, num);
+ }
+ }
+
+ if (mask & ENCODE_CCODE) {
+ strcat(buf, "cc ");
+ }
+ if (mask & ENCODE_FP_STATUS) {
+ strcat(buf, "fpcc ");
+ }
+ /* Memory bits */
+ if (mips_lir && (mask & ENCODE_DALVIK_REG)) {
+ sprintf(buf + strlen(buf), "dr%d%s", mips_lir->alias_info & 0xffff,
+ (mips_lir->alias_info & 0x80000000) ? "(+1)" : "");
+ }
+ if (mask & ENCODE_LITERAL) {
+ strcat(buf, "lit ");
+ }
+
+ if (mask & ENCODE_HEAP_REF) {
+ strcat(buf, "heap ");
+ }
+ if (mask & ENCODE_MUST_NOT_ALIAS) {
+ strcat(buf, "noalias ");
+ }
+ }
+ if (buf[0]) {
+ LOG(INFO) << prefix << ": " << buf;
+ }
+}
+
+/*
+ * TUNING: is true leaf? Can't just use METHOD_IS_LEAF to determine as some
+ * instructions might call out to C/assembly helper functions. Until
+ * machinery is in place, always spill lr.
+ */
+
+void MipsMir2Lir::AdjustSpillMask()
+{
+ core_spill_mask_ |= (1 << r_RA);
+ num_core_spills_++;
+}
+
+/*
+ * Mark a callee-save fp register as promoted. Note that
+ * vpush/vpop uses contiguous register lists so we must
+ * include any holes in the mask. Associate holes with
+ * Dalvik register INVALID_VREG (0xFFFFU).
+ */
+void MipsMir2Lir::MarkPreservedSingle(int s_reg, int reg)
+{
+ LOG(FATAL) << "No support yet for promoted FP regs";
+}
+
+void MipsMir2Lir::FlushRegWide(int reg1, int reg2)
+{
+ RegisterInfo* info1 = GetRegInfo(reg1);
+ RegisterInfo* info2 = GetRegInfo(reg2);
+ DCHECK(info1 && info2 && info1->pair && info2->pair &&
+ (info1->partner == info2->reg) &&
+ (info2->partner == info1->reg));
+ if ((info1->live && info1->dirty) || (info2->live && info2->dirty)) {
+ if (!(info1->is_temp && info2->is_temp)) {
+ /* Should not happen. If it does, there's a problem in eval_loc */
+ LOG(FATAL) << "Long half-temp, half-promoted";
+ }
+
+ info1->dirty = false;
+ info2->dirty = false;
+ if (mir_graph_->SRegToVReg(info2->s_reg) < mir_graph_->SRegToVReg(info1->s_reg))
+ info1 = info2;
+ int v_reg = mir_graph_->SRegToVReg(info1->s_reg);
+ StoreBaseDispWide(rMIPS_SP, VRegOffset(v_reg), info1->reg, info1->partner);
+ }
+}
+
+void MipsMir2Lir::FlushReg(int reg)
+{
+ RegisterInfo* info = GetRegInfo(reg);
+ if (info->live && info->dirty) {
+ info->dirty = false;
+ int v_reg = mir_graph_->SRegToVReg(info->s_reg);
+ StoreBaseDisp(rMIPS_SP, VRegOffset(v_reg), reg, kWord);
+ }
+}
+
+/* Give access to the target-dependent FP register encoding to common code */
+bool MipsMir2Lir::IsFpReg(int reg) {
+ return MIPS_FPREG(reg);
+}
+
+/* Clobber all regs that might be used by an external C call */
+void MipsMir2Lir::ClobberCalleeSave()
+{
+ Clobber(r_ZERO);
+ Clobber(r_AT);
+ Clobber(r_V0);
+ Clobber(r_V1);
+ Clobber(r_A0);
+ Clobber(r_A1);
+ Clobber(r_A2);
+ Clobber(r_A3);
+ Clobber(r_T0);
+ Clobber(r_T1);
+ Clobber(r_T2);
+ Clobber(r_T3);
+ Clobber(r_T4);
+ Clobber(r_T5);
+ Clobber(r_T6);
+ Clobber(r_T7);
+ Clobber(r_T8);
+ Clobber(r_T9);
+ Clobber(r_K0);
+ Clobber(r_K1);
+ Clobber(r_GP);
+ Clobber(r_FP);
+ Clobber(r_RA);
+ Clobber(r_F0);
+ Clobber(r_F1);
+ Clobber(r_F2);
+ Clobber(r_F3);
+ Clobber(r_F4);
+ Clobber(r_F5);
+ Clobber(r_F6);
+ Clobber(r_F7);
+ Clobber(r_F8);
+ Clobber(r_F9);
+ Clobber(r_F10);
+ Clobber(r_F11);
+ Clobber(r_F12);
+ Clobber(r_F13);
+ Clobber(r_F14);
+ Clobber(r_F15);
+}
+
+RegLocation MipsMir2Lir::GetReturnWideAlt()
+{
+ UNIMPLEMENTED(FATAL) << "No GetReturnWideAlt for MIPS";
+ RegLocation res = LocCReturnWide();
+ return res;
+}
+
+RegLocation MipsMir2Lir::GetReturnAlt()
+{
+ UNIMPLEMENTED(FATAL) << "No GetReturnAlt for MIPS";
+ RegLocation res = LocCReturn();
+ return res;
+}
+
+MipsMir2Lir::RegisterInfo* MipsMir2Lir::GetRegInfo(int reg)
+{
+ return MIPS_FPREG(reg) ? ®_pool_->FPRegs[reg & MIPS_FP_REG_MASK]
+ : ®_pool_->core_regs[reg];
+}
+
+/* To be used when explicitly managing register use */
+void MipsMir2Lir::LockCallTemps()
+{
+ LockTemp(rMIPS_ARG0);
+ LockTemp(rMIPS_ARG1);
+ LockTemp(rMIPS_ARG2);
+ LockTemp(rMIPS_ARG3);
+}
+
+/* To be used when explicitly managing register use */
+void MipsMir2Lir::FreeCallTemps()
+{
+ FreeTemp(rMIPS_ARG0);
+ FreeTemp(rMIPS_ARG1);
+ FreeTemp(rMIPS_ARG2);
+ FreeTemp(rMIPS_ARG3);
+}
+
+void MipsMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind)
+{
+#if ANDROID_SMP != 0
+ NewLIR1(kMipsSync, 0 /* Only stype currently supported */);
+#endif
+}
+
+/*
+ * Alloc a pair of core registers, or a double. Low reg in low byte,
+ * high reg in next byte.
+ */
+int MipsMir2Lir::AllocTypedTempPair(bool fp_hint,
+ int reg_class)
+{
+ int high_reg;
+ int low_reg;
+ int res = 0;
+
+ if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg)) {
+ low_reg = AllocTempDouble();
+ high_reg = low_reg + 1;
+ res = (low_reg & 0xff) | ((high_reg & 0xff) << 8);
+ return res;
+ }
+
+ low_reg = AllocTemp();
+ high_reg = AllocTemp();
+ res = (low_reg & 0xff) | ((high_reg & 0xff) << 8);
+ return res;
+}
+
+int MipsMir2Lir::AllocTypedTemp(bool fp_hint, int reg_class)
+{
+ if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg))
+{
+ return AllocTempFloat();
+}
+ return AllocTemp();
+}
+
+void MipsMir2Lir::CompilerInitializeRegAlloc()
+{
+ int num_regs = sizeof(core_regs)/sizeof(*core_regs);
+ int num_reserved = sizeof(ReservedRegs)/sizeof(*ReservedRegs);
+ int num_temps = sizeof(core_temps)/sizeof(*core_temps);
+ int num_fp_regs = sizeof(FpRegs)/sizeof(*FpRegs);
+ int num_fp_temps = sizeof(fp_temps)/sizeof(*fp_temps);
+ reg_pool_ = static_cast<RegisterPool*>(arena_->NewMem(sizeof(*reg_pool_), true,
+ ArenaAllocator::kAllocRegAlloc));
+ reg_pool_->num_core_regs = num_regs;
+ reg_pool_->core_regs = static_cast<RegisterInfo*>
+ (arena_->NewMem(num_regs * sizeof(*reg_pool_->core_regs), true,
+ ArenaAllocator::kAllocRegAlloc));
+ reg_pool_->num_fp_regs = num_fp_regs;
+ reg_pool_->FPRegs = static_cast<RegisterInfo*>
+ (arena_->NewMem(num_fp_regs * sizeof(*reg_pool_->FPRegs), true,
+ ArenaAllocator::kAllocRegAlloc));
+ CompilerInitPool(reg_pool_->core_regs, core_regs, reg_pool_->num_core_regs);
+ CompilerInitPool(reg_pool_->FPRegs, FpRegs, reg_pool_->num_fp_regs);
+ // Keep special registers from being allocated
+ for (int i = 0; i < num_reserved; i++) {
+ if (NO_SUSPEND && (ReservedRegs[i] == rMIPS_SUSPEND)) {
+ //To measure cost of suspend check
+ continue;
+ }
+ MarkInUse(ReservedRegs[i]);
+ }
+ // Mark temp regs - all others not in use can be used for promotion
+ for (int i = 0; i < num_temps; i++) {
+ MarkTemp(core_temps[i]);
+ }
+ for (int i = 0; i < num_fp_temps; i++) {
+ MarkTemp(fp_temps[i]);
+ }
+}
+
+void MipsMir2Lir::FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free)
+{
+ if ((rl_free.low_reg != rl_keep.low_reg) && (rl_free.low_reg != rl_keep.high_reg) &&
+ (rl_free.high_reg != rl_keep.low_reg) && (rl_free.high_reg != rl_keep.high_reg)) {
+ // No overlap, free both
+ FreeTemp(rl_free.low_reg);
+ FreeTemp(rl_free.high_reg);
+ }
+}
+/*
+ * In the Arm code a it is typical to use the link register
+ * to hold the target address. However, for Mips we must
+ * ensure that all branch instructions can be restarted if
+ * there is a trap in the shadow. Allocate a temp register.
+ */
+int MipsMir2Lir::LoadHelper(int offset)
+{
+ LoadWordDisp(rMIPS_SELF, offset, r_T9);
+ return r_T9;
+}
+
+void MipsMir2Lir::SpillCoreRegs()
+{
+ if (num_core_spills_ == 0) {
+ return;
+ }
+ uint32_t mask = core_spill_mask_;
+ int offset = num_core_spills_ * 4;
+ OpRegImm(kOpSub, rMIPS_SP, offset);
+ for (int reg = 0; mask; mask >>= 1, reg++) {
+ if (mask & 0x1) {
+ offset -= 4;
+ StoreWordDisp(rMIPS_SP, offset, reg);
+ }
+ }
+}
+
+void MipsMir2Lir::UnSpillCoreRegs()
+{
+ if (num_core_spills_ == 0) {
+ return;
+ }
+ uint32_t mask = core_spill_mask_;
+ int offset = frame_size_;
+ for (int reg = 0; mask; mask >>= 1, reg++) {
+ if (mask & 0x1) {
+ offset -= 4;
+ LoadWordDisp(rMIPS_SP, offset, reg);
+ }
+ }
+ OpRegImm(kOpAdd, rMIPS_SP, frame_size_);
+}
+
+bool MipsMir2Lir::IsUnconditionalBranch(LIR* lir)
+{
+ return (lir->opcode == kMipsB);
+}
+
+MipsMir2Lir::MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
+ : Mir2Lir(cu, mir_graph, arena) {
+ for (int i = 0; i < kMipsLast; i++) {
+ if (MipsMir2Lir::EncodingMap[i].opcode != i) {
+ LOG(FATAL) << "Encoding order for " << MipsMir2Lir::EncodingMap[i].name
+ << " is wrong: expecting " << i << ", seeing "
+ << static_cast<int>(MipsMir2Lir::EncodingMap[i].opcode);
+ }
+ }
+}
+
+Mir2Lir* MipsCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena) {
+ return new MipsMir2Lir(cu, mir_graph, arena);
+}
+
+uint64_t MipsMir2Lir::GetTargetInstFlags(int opcode)
+{
+ return MipsMir2Lir::EncodingMap[opcode].flags;
+}
+
+const char* MipsMir2Lir::GetTargetInstName(int opcode)
+{
+ return MipsMir2Lir::EncodingMap[opcode].name;
+}
+
+const char* MipsMir2Lir::GetTargetInstFmt(int opcode)
+{
+ return MipsMir2Lir::EncodingMap[opcode].fmt;
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc
new file mode 100644
index 0000000..8daafc8
--- /dev/null
+++ b/compiler/dex/quick/mips/utility_mips.cc
@@ -0,0 +1,700 @@
+/*
+ * Copyright (C) 2012 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 "codegen_mips.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "mips_lir.h"
+
+namespace art {
+
+/* This file contains codegen for the MIPS32 ISA. */
+LIR* MipsMir2Lir::OpFpRegCopy(int r_dest, int r_src)
+{
+ int opcode;
+ /* must be both DOUBLE or both not DOUBLE */
+ DCHECK_EQ(MIPS_DOUBLEREG(r_dest),MIPS_DOUBLEREG(r_src));
+ if (MIPS_DOUBLEREG(r_dest)) {
+ opcode = kMipsFmovd;
+ } else {
+ if (MIPS_SINGLEREG(r_dest)) {
+ if (MIPS_SINGLEREG(r_src)) {
+ opcode = kMipsFmovs;
+ } else {
+ /* note the operands are swapped for the mtc1 instr */
+ int t_opnd = r_src;
+ r_src = r_dest;
+ r_dest = t_opnd;
+ opcode = kMipsMtc1;
+ }
+ } else {
+ DCHECK(MIPS_SINGLEREG(r_src));
+ opcode = kMipsMfc1;
+ }
+ }
+ LIR* res = RawLIR(current_dalvik_offset_, opcode, r_src, r_dest);
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) {
+ res->flags.is_nop = true;
+ }
+ return res;
+}
+
+bool MipsMir2Lir::InexpensiveConstantInt(int32_t value)
+{
+ return ((value == 0) || IsUint(16, value) || ((value < 0) && (value >= -32768)));
+}
+
+bool MipsMir2Lir::InexpensiveConstantFloat(int32_t value)
+{
+ return false; // TUNING
+}
+
+bool MipsMir2Lir::InexpensiveConstantLong(int64_t value)
+{
+ return false; // TUNING
+}
+
+bool MipsMir2Lir::InexpensiveConstantDouble(int64_t value)
+{
+ return false; // TUNING
+}
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool. If target is
+ * a high register, build constant into a low register and copy.
+ *
+ * No additional register clobbering operation performed. Use this version when
+ * 1) r_dest is freshly returned from AllocTemp or
+ * 2) The codegen is under fixed register usage
+ */
+LIR* MipsMir2Lir::LoadConstantNoClobber(int r_dest, int value)
+{
+ LIR *res;
+
+ int r_dest_save = r_dest;
+ int is_fp_reg = MIPS_FPREG(r_dest);
+ if (is_fp_reg) {
+ DCHECK(MIPS_SINGLEREG(r_dest));
+ r_dest = AllocTemp();
+ }
+
+ /* See if the value can be constructed cheaply */
+ if (value == 0) {
+ res = NewLIR2(kMipsMove, r_dest, r_ZERO);
+ } else if ((value > 0) && (value <= 65535)) {
+ res = NewLIR3(kMipsOri, r_dest, r_ZERO, value);
+ } else if ((value < 0) && (value >= -32768)) {
+ res = NewLIR3(kMipsAddiu, r_dest, r_ZERO, value);
+ } else {
+ res = NewLIR2(kMipsLui, r_dest, value>>16);
+ if (value & 0xffff)
+ NewLIR3(kMipsOri, r_dest, r_dest, value);
+ }
+
+ if (is_fp_reg) {
+ NewLIR2(kMipsMtc1, r_dest, r_dest_save);
+ FreeTemp(r_dest);
+ }
+
+ return res;
+}
+
+LIR* MipsMir2Lir::OpUnconditionalBranch(LIR* target)
+{
+ LIR* res = NewLIR1(kMipsB, 0 /* offset to be patched during assembly*/ );
+ res->target = target;
+ return res;
+}
+
+LIR* MipsMir2Lir::OpReg(OpKind op, int r_dest_src)
+{
+ MipsOpCode opcode = kMipsNop;
+ switch (op) {
+ case kOpBlx:
+ opcode = kMipsJalr;
+ break;
+ case kOpBx:
+ return NewLIR1(kMipsJr, r_dest_src);
+ break;
+ default:
+ LOG(FATAL) << "Bad case in OpReg";
+ }
+ return NewLIR2(opcode, r_RA, r_dest_src);
+}
+
+LIR* MipsMir2Lir::OpRegImm(OpKind op, int r_dest_src1,
+ int value)
+{
+ LIR *res;
+ bool neg = (value < 0);
+ int abs_value = (neg) ? -value : value;
+ bool short_form = (abs_value & 0xff) == abs_value;
+ MipsOpCode opcode = kMipsNop;
+ switch (op) {
+ case kOpAdd:
+ return OpRegRegImm(op, r_dest_src1, r_dest_src1, value);
+ break;
+ case kOpSub:
+ return OpRegRegImm(op, r_dest_src1, r_dest_src1, value);
+ break;
+ default:
+ LOG(FATAL) << "Bad case in OpRegImm";
+ break;
+ }
+ if (short_form)
+ res = NewLIR2(opcode, r_dest_src1, abs_value);
+ else {
+ int r_scratch = AllocTemp();
+ res = LoadConstant(r_scratch, value);
+ if (op == kOpCmp)
+ NewLIR2(opcode, r_dest_src1, r_scratch);
+ else
+ NewLIR3(opcode, r_dest_src1, r_dest_src1, r_scratch);
+ }
+ return res;
+}
+
+LIR* MipsMir2Lir::OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2)
+{
+ MipsOpCode opcode = kMipsNop;
+ switch (op) {
+ case kOpAdd:
+ opcode = kMipsAddu;
+ break;
+ case kOpSub:
+ opcode = kMipsSubu;
+ break;
+ case kOpAnd:
+ opcode = kMipsAnd;
+ break;
+ case kOpMul:
+ opcode = kMipsMul;
+ break;
+ case kOpOr:
+ opcode = kMipsOr;
+ break;
+ case kOpXor:
+ opcode = kMipsXor;
+ break;
+ case kOpLsl:
+ opcode = kMipsSllv;
+ break;
+ case kOpLsr:
+ opcode = kMipsSrlv;
+ break;
+ case kOpAsr:
+ opcode = kMipsSrav;
+ break;
+ case kOpAdc:
+ case kOpSbc:
+ LOG(FATAL) << "No carry bit on MIPS";
+ break;
+ default:
+ LOG(FATAL) << "bad case in OpRegRegReg";
+ break;
+ }
+ return NewLIR3(opcode, r_dest, r_src1, r_src2);
+}
+
+LIR* MipsMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value)
+{
+ LIR *res;
+ MipsOpCode opcode = kMipsNop;
+ bool short_form = true;
+
+ switch (op) {
+ case kOpAdd:
+ if (IS_SIMM16(value)) {
+ opcode = kMipsAddiu;
+ }
+ else {
+ short_form = false;
+ opcode = kMipsAddu;
+ }
+ break;
+ case kOpSub:
+ if (IS_SIMM16((-value))) {
+ value = -value;
+ opcode = kMipsAddiu;
+ }
+ else {
+ short_form = false;
+ opcode = kMipsSubu;
+ }
+ break;
+ case kOpLsl:
+ DCHECK(value >= 0 && value <= 31);
+ opcode = kMipsSll;
+ break;
+ case kOpLsr:
+ DCHECK(value >= 0 && value <= 31);
+ opcode = kMipsSrl;
+ break;
+ case kOpAsr:
+ DCHECK(value >= 0 && value <= 31);
+ opcode = kMipsSra;
+ break;
+ case kOpAnd:
+ if (IS_UIMM16((value))) {
+ opcode = kMipsAndi;
+ }
+ else {
+ short_form = false;
+ opcode = kMipsAnd;
+ }
+ break;
+ case kOpOr:
+ if (IS_UIMM16((value))) {
+ opcode = kMipsOri;
+ }
+ else {
+ short_form = false;
+ opcode = kMipsOr;
+ }
+ break;
+ case kOpXor:
+ if (IS_UIMM16((value))) {
+ opcode = kMipsXori;
+ }
+ else {
+ short_form = false;
+ opcode = kMipsXor;
+ }
+ break;
+ case kOpMul:
+ short_form = false;
+ opcode = kMipsMul;
+ break;
+ default:
+ LOG(FATAL) << "Bad case in OpRegRegImm";
+ break;
+ }
+
+ if (short_form)
+ res = NewLIR3(opcode, r_dest, r_src1, value);
+ else {
+ if (r_dest != r_src1) {
+ res = LoadConstant(r_dest, value);
+ NewLIR3(opcode, r_dest, r_src1, r_dest);
+ } else {
+ int r_scratch = AllocTemp();
+ res = LoadConstant(r_scratch, value);
+ NewLIR3(opcode, r_dest, r_src1, r_scratch);
+ }
+ }
+ return res;
+}
+
+LIR* MipsMir2Lir::OpRegReg(OpKind op, int r_dest_src1, int r_src2)
+{
+ MipsOpCode opcode = kMipsNop;
+ LIR *res;
+ switch (op) {
+ case kOpMov:
+ opcode = kMipsMove;
+ break;
+ case kOpMvn:
+ return NewLIR3(kMipsNor, r_dest_src1, r_src2, r_ZERO);
+ case kOpNeg:
+ return NewLIR3(kMipsSubu, r_dest_src1, r_ZERO, r_src2);
+ case kOpAdd:
+ case kOpAnd:
+ case kOpMul:
+ case kOpOr:
+ case kOpSub:
+ case kOpXor:
+ return OpRegRegReg(op, r_dest_src1, r_dest_src1, r_src2);
+ case kOp2Byte:
+#if __mips_isa_rev>=2
+ res = NewLIR2(kMipsSeb, r_dest_src1, r_src2);
+#else
+ res = OpRegRegImm(kOpLsl, r_dest_src1, r_src2, 24);
+ OpRegRegImm(kOpAsr, r_dest_src1, r_dest_src1, 24);
+#endif
+ return res;
+ case kOp2Short:
+#if __mips_isa_rev>=2
+ res = NewLIR2(kMipsSeh, r_dest_src1, r_src2);
+#else
+ res = OpRegRegImm(kOpLsl, r_dest_src1, r_src2, 16);
+ OpRegRegImm(kOpAsr, r_dest_src1, r_dest_src1, 16);
+#endif
+ return res;
+ case kOp2Char:
+ return NewLIR3(kMipsAndi, r_dest_src1, r_src2, 0xFFFF);
+ default:
+ LOG(FATAL) << "Bad case in OpRegReg";
+ break;
+ }
+ return NewLIR2(opcode, r_dest_src1, r_src2);
+}
+
+LIR* MipsMir2Lir::LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value)
+{
+ LIR *res;
+ res = LoadConstantNoClobber(r_dest_lo, Low32Bits(value));
+ LoadConstantNoClobber(r_dest_hi, High32Bits(value));
+ return res;
+}
+
+/* Load value from base + scaled index. */
+LIR* MipsMir2Lir::LoadBaseIndexed(int rBase, int r_index, int r_dest,
+ int scale, OpSize size)
+{
+ LIR *first = NULL;
+ LIR *res;
+ MipsOpCode opcode = kMipsNop;
+ int t_reg = AllocTemp();
+
+ if (MIPS_FPREG(r_dest)) {
+ DCHECK(MIPS_SINGLEREG(r_dest));
+ DCHECK((size == kWord) || (size == kSingle));
+ size = kSingle;
+ } else {
+ if (size == kSingle)
+ size = kWord;
+ }
+
+ if (!scale) {
+ first = NewLIR3(kMipsAddu, t_reg , rBase, r_index);
+ } else {
+ first = OpRegRegImm(kOpLsl, t_reg, r_index, scale);
+ NewLIR3(kMipsAddu, t_reg , rBase, t_reg);
+ }
+
+ switch (size) {
+ case kSingle:
+ opcode = kMipsFlwc1;
+ break;
+ case kWord:
+ opcode = kMipsLw;
+ break;
+ case kUnsignedHalf:
+ opcode = kMipsLhu;
+ break;
+ case kSignedHalf:
+ opcode = kMipsLh;
+ break;
+ case kUnsignedByte:
+ opcode = kMipsLbu;
+ break;
+ case kSignedByte:
+ opcode = kMipsLb;
+ break;
+ default:
+ LOG(FATAL) << "Bad case in LoadBaseIndexed";
+ }
+
+ res = NewLIR3(opcode, r_dest, 0, t_reg);
+ FreeTemp(t_reg);
+ return (first) ? first : res;
+}
+
+/* store value base base + scaled index. */
+LIR* MipsMir2Lir::StoreBaseIndexed(int rBase, int r_index, int r_src,
+ int scale, OpSize size)
+{
+ LIR *first = NULL;
+ MipsOpCode opcode = kMipsNop;
+ int r_new_index = r_index;
+ int t_reg = AllocTemp();
+
+ if (MIPS_FPREG(r_src)) {
+ DCHECK(MIPS_SINGLEREG(r_src));
+ DCHECK((size == kWord) || (size == kSingle));
+ size = kSingle;
+ } else {
+ if (size == kSingle)
+ size = kWord;
+ }
+
+ if (!scale) {
+ first = NewLIR3(kMipsAddu, t_reg , rBase, r_index);
+ } else {
+ first = OpRegRegImm(kOpLsl, t_reg, r_index, scale);
+ NewLIR3(kMipsAddu, t_reg , rBase, t_reg);
+ }
+
+ switch (size) {
+ case kSingle:
+ opcode = kMipsFswc1;
+ break;
+ case kWord:
+ opcode = kMipsSw;
+ break;
+ case kUnsignedHalf:
+ case kSignedHalf:
+ opcode = kMipsSh;
+ break;
+ case kUnsignedByte:
+ case kSignedByte:
+ opcode = kMipsSb;
+ break;
+ default:
+ LOG(FATAL) << "Bad case in StoreBaseIndexed";
+ }
+ NewLIR3(opcode, r_src, 0, t_reg);
+ FreeTemp(r_new_index);
+ return first;
+}
+
+LIR* MipsMir2Lir::LoadBaseDispBody(int rBase, int displacement, int r_dest,
+ int r_dest_hi, OpSize size, int s_reg)
+/*
+ * Load value from base + displacement. Optionally perform null check
+ * on base (which must have an associated s_reg and MIR). If not
+ * performing null check, incoming MIR can be null. IMPORTANT: this
+ * code must not allocate any new temps. If a new register is needed
+ * and base and dest are the same, spill some other register to
+ * rlp and then restore.
+ */
+{
+ LIR *res;
+ LIR *load = NULL;
+ LIR *load2 = NULL;
+ MipsOpCode opcode = kMipsNop;
+ bool short_form = IS_SIMM16(displacement);
+ bool pair = false;
+
+ switch (size) {
+ case kLong:
+ case kDouble:
+ pair = true;
+ opcode = kMipsLw;
+ if (MIPS_FPREG(r_dest)) {
+ opcode = kMipsFlwc1;
+ if (MIPS_DOUBLEREG(r_dest)) {
+ r_dest = r_dest - MIPS_FP_DOUBLE;
+ } else {
+ DCHECK(MIPS_FPREG(r_dest_hi));
+ DCHECK(r_dest == (r_dest_hi - 1));
+ }
+ r_dest_hi = r_dest + 1;
+ }
+ short_form = IS_SIMM16_2WORD(displacement);
+ DCHECK_EQ((displacement & 0x3), 0);
+ break;
+ case kWord:
+ case kSingle:
+ opcode = kMipsLw;
+ if (MIPS_FPREG(r_dest)) {
+ opcode = kMipsFlwc1;
+ DCHECK(MIPS_SINGLEREG(r_dest));
+ }
+ DCHECK_EQ((displacement & 0x3), 0);
+ break;
+ case kUnsignedHalf:
+ opcode = kMipsLhu;
+ DCHECK_EQ((displacement & 0x1), 0);
+ break;
+ case kSignedHalf:
+ opcode = kMipsLh;
+ DCHECK_EQ((displacement & 0x1), 0);
+ break;
+ case kUnsignedByte:
+ opcode = kMipsLbu;
+ break;
+ case kSignedByte:
+ opcode = kMipsLb;
+ break;
+ default:
+ LOG(FATAL) << "Bad case in LoadBaseIndexedBody";
+ }
+
+ if (short_form) {
+ if (!pair) {
+ load = res = NewLIR3(opcode, r_dest, displacement, rBase);
+ } else {
+ load = res = NewLIR3(opcode, r_dest,
+ displacement + LOWORD_OFFSET, rBase);
+ load2 = NewLIR3(opcode, r_dest_hi,
+ displacement + HIWORD_OFFSET, rBase);
+ }
+ } else {
+ if (pair) {
+ int r_tmp = AllocFreeTemp();
+ res = OpRegRegImm(kOpAdd, r_tmp, rBase, displacement);
+ load = NewLIR3(opcode, r_dest, LOWORD_OFFSET, r_tmp);
+ load2 = NewLIR3(opcode, r_dest_hi, HIWORD_OFFSET, r_tmp);
+ FreeTemp(r_tmp);
+ } else {
+ int r_tmp = (rBase == r_dest) ? AllocFreeTemp() : r_dest;
+ res = OpRegRegImm(kOpAdd, r_tmp, rBase, displacement);
+ load = NewLIR3(opcode, r_dest, 0, r_tmp);
+ if (r_tmp != r_dest)
+ FreeTemp(r_tmp);
+ }
+ }
+
+ if (rBase == rMIPS_SP) {
+ AnnotateDalvikRegAccess(load, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
+ true /* is_load */, pair /* is64bit */);
+ if (pair) {
+ AnnotateDalvikRegAccess(load2, (displacement + HIWORD_OFFSET) >> 2,
+ true /* is_load */, pair /* is64bit */);
+ }
+ }
+ return load;
+}
+
+LIR* MipsMir2Lir::LoadBaseDisp(int rBase, int displacement, int r_dest,
+ OpSize size, int s_reg)
+{
+ return LoadBaseDispBody(rBase, displacement, r_dest, -1,
+ size, s_reg);
+}
+
+LIR* MipsMir2Lir::LoadBaseDispWide(int rBase, int displacement,
+ int r_dest_lo, int r_dest_hi, int s_reg)
+{
+ return LoadBaseDispBody(rBase, displacement, r_dest_lo, r_dest_hi, kLong, s_reg);
+}
+
+LIR* MipsMir2Lir::StoreBaseDispBody(int rBase, int displacement,
+ int r_src, int r_src_hi, OpSize size)
+{
+ LIR *res;
+ LIR *store = NULL;
+ LIR *store2 = NULL;
+ MipsOpCode opcode = kMipsNop;
+ bool short_form = IS_SIMM16(displacement);
+ bool pair = false;
+
+ switch (size) {
+ case kLong:
+ case kDouble:
+ pair = true;
+ opcode = kMipsSw;
+ if (MIPS_FPREG(r_src)) {
+ opcode = kMipsFswc1;
+ if (MIPS_DOUBLEREG(r_src)) {
+ r_src = r_src - MIPS_FP_DOUBLE;
+ } else {
+ DCHECK(MIPS_FPREG(r_src_hi));
+ DCHECK_EQ(r_src, (r_src_hi - 1));
+ }
+ r_src_hi = r_src + 1;
+ }
+ short_form = IS_SIMM16_2WORD(displacement);
+ DCHECK_EQ((displacement & 0x3), 0);
+ break;
+ case kWord:
+ case kSingle:
+ opcode = kMipsSw;
+ if (MIPS_FPREG(r_src)) {
+ opcode = kMipsFswc1;
+ DCHECK(MIPS_SINGLEREG(r_src));
+ }
+ DCHECK_EQ((displacement & 0x3), 0);
+ break;
+ case kUnsignedHalf:
+ case kSignedHalf:
+ opcode = kMipsSh;
+ DCHECK_EQ((displacement & 0x1), 0);
+ break;
+ case kUnsignedByte:
+ case kSignedByte:
+ opcode = kMipsSb;
+ break;
+ default:
+ LOG(FATAL) << "Bad case in StoreBaseIndexedBody";
+ }
+
+ if (short_form) {
+ if (!pair) {
+ store = res = NewLIR3(opcode, r_src, displacement, rBase);
+ } else {
+ store = res = NewLIR3(opcode, r_src, displacement + LOWORD_OFFSET,
+ rBase);
+ store2 = NewLIR3(opcode, r_src_hi, displacement + HIWORD_OFFSET,
+ rBase);
+ }
+ } else {
+ int r_scratch = AllocTemp();
+ res = OpRegRegImm(kOpAdd, r_scratch, rBase, displacement);
+ if (!pair) {
+ store = NewLIR3(opcode, r_src, 0, r_scratch);
+ } else {
+ store = NewLIR3(opcode, r_src, LOWORD_OFFSET, r_scratch);
+ store2 = NewLIR3(opcode, r_src_hi, HIWORD_OFFSET, r_scratch);
+ }
+ FreeTemp(r_scratch);
+ }
+
+ if (rBase == rMIPS_SP) {
+ AnnotateDalvikRegAccess(store, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
+ false /* is_load */, pair /* is64bit */);
+ if (pair) {
+ AnnotateDalvikRegAccess(store2, (displacement + HIWORD_OFFSET) >> 2,
+ false /* is_load */, pair /* is64bit */);
+ }
+ }
+
+ return res;
+}
+
+LIR* MipsMir2Lir::StoreBaseDisp(int rBase, int displacement, int r_src,
+ OpSize size)
+{
+ return StoreBaseDispBody(rBase, displacement, r_src, -1, size);
+}
+
+LIR* MipsMir2Lir::StoreBaseDispWide(int rBase, int displacement,
+ int r_src_lo, int r_src_hi)
+{
+ return StoreBaseDispBody(rBase, displacement, r_src_lo, r_src_hi, kLong);
+}
+
+LIR* MipsMir2Lir::OpThreadMem(OpKind op, int thread_offset)
+{
+ LOG(FATAL) << "Unexpected use of OpThreadMem for MIPS";
+ return NULL;
+}
+
+LIR* MipsMir2Lir::OpMem(OpKind op, int rBase, int disp)
+{
+ LOG(FATAL) << "Unexpected use of OpMem for MIPS";
+ return NULL;
+}
+
+LIR* MipsMir2Lir::StoreBaseIndexedDisp( int rBase, int r_index, int scale, int displacement,
+ int r_src, int r_src_hi, OpSize size, int s_reg)
+{
+ LOG(FATAL) << "Unexpected use of StoreBaseIndexedDisp for MIPS";
+ return NULL;
+}
+
+LIR* MipsMir2Lir::OpRegMem(OpKind op, int r_dest, int rBase,
+ int offset)
+{
+ LOG(FATAL) << "Unexpected use of OpRegMem for MIPS";
+ return NULL;
+}
+
+LIR* MipsMir2Lir::LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
+ int r_dest, int r_dest_hi, OpSize size, int s_reg)
+{
+ LOG(FATAL) << "Unexpected use of LoadBaseIndexedDisp for MIPS";
+ return NULL;
+}
+
+LIR* MipsMir2Lir::OpCondBranch(ConditionCode cc, LIR* target)
+{
+ LOG(FATAL) << "Unexpected use of OpCondBranch for MIPS";
+ return NULL;
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h
new file mode 100644
index 0000000..4eef264
--- /dev/null
+++ b/compiler/dex/quick/mir_to_lir-inl.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_QUICK_MIR_TO_LIR_INL_H_
+#define ART_SRC_COMPILER_DEX_QUICK_MIR_TO_LIR_INL_H_
+
+#include "mir_to_lir.h"
+
+#include "dex/compiler_internals.h"
+
+namespace art {
+
+/* Mark a temp register as dead. Does not affect allocation state. */
+inline void Mir2Lir::ClobberBody(RegisterInfo* p) {
+ if (p->is_temp) {
+ DCHECK(!(p->live && p->dirty)) << "Live & dirty temp in clobber";
+ p->live = false;
+ p->s_reg = INVALID_SREG;
+ p->def_start = NULL;
+ p->def_end = NULL;
+ if (p->pair) {
+ p->pair = false;
+ Clobber(p->partner);
+ }
+ }
+}
+
+inline LIR* Mir2Lir::RawLIR(int dalvik_offset, int opcode, int op0,
+ int op1, int op2, int op3, int op4, LIR* target) {
+ LIR* insn = static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocLIR));
+ insn->dalvik_offset = dalvik_offset;
+ insn->opcode = opcode;
+ insn->operands[0] = op0;
+ insn->operands[1] = op1;
+ insn->operands[2] = op2;
+ insn->operands[3] = op3;
+ insn->operands[4] = op4;
+ insn->target = target;
+ SetupResourceMasks(insn);
+ if ((opcode == kPseudoTargetLabel) || (opcode == kPseudoSafepointPC) ||
+ (opcode == kPseudoExportedPC)) {
+ // Always make labels scheduling barriers
+ insn->use_mask = insn->def_mask = ENCODE_ALL;
+ }
+ return insn;
+}
+
+/*
+ * The following are building blocks to construct low-level IRs with 0 - 4
+ * operands.
+ */
+inline LIR* Mir2Lir::NewLIR0(int opcode) {
+ DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & NO_OPERAND))
+ << GetTargetInstName(opcode) << " " << opcode << " "
+ << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
+ << current_dalvik_offset_;
+ LIR* insn = RawLIR(current_dalvik_offset_, opcode);
+ AppendLIR(insn);
+ return insn;
+}
+
+inline LIR* Mir2Lir::NewLIR1(int opcode, int dest) {
+ DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_UNARY_OP))
+ << GetTargetInstName(opcode) << " " << opcode << " "
+ << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
+ << current_dalvik_offset_;
+ LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest);
+ AppendLIR(insn);
+ return insn;
+}
+
+inline LIR* Mir2Lir::NewLIR2(int opcode, int dest, int src1) {
+ DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_BINARY_OP))
+ << GetTargetInstName(opcode) << " " << opcode << " "
+ << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
+ << current_dalvik_offset_;
+ LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest, src1);
+ AppendLIR(insn);
+ return insn;
+}
+
+inline LIR* Mir2Lir::NewLIR3(int opcode, int dest, int src1, int src2) {
+ DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_TERTIARY_OP))
+ << GetTargetInstName(opcode) << " " << opcode << " "
+ << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
+ << current_dalvik_offset_;
+ LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest, src1, src2);
+ AppendLIR(insn);
+ return insn;
+}
+
+inline LIR* Mir2Lir::NewLIR4(int opcode, int dest, int src1, int src2, int info) {
+ DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_QUAD_OP))
+ << GetTargetInstName(opcode) << " " << opcode << " "
+ << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
+ << current_dalvik_offset_;
+ LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest, src1, src2, info);
+ AppendLIR(insn);
+ return insn;
+}
+
+inline LIR* Mir2Lir::NewLIR5(int opcode, int dest, int src1, int src2, int info1,
+ int info2) {
+ DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_QUIN_OP))
+ << GetTargetInstName(opcode) << " " << opcode << " "
+ << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
+ << current_dalvik_offset_;
+ LIR* insn = RawLIR(current_dalvik_offset_, opcode, dest, src1, src2, info1, info2);
+ AppendLIR(insn);
+ return insn;
+}
+
+/*
+ * Mark the corresponding bit(s).
+ */
+inline void Mir2Lir::SetupRegMask(uint64_t* mask, int reg) {
+ *mask |= GetRegMaskCommon(reg);
+}
+
+/*
+ * Set up the proper fields in the resource mask
+ */
+inline void Mir2Lir::SetupResourceMasks(LIR* lir) {
+ int opcode = lir->opcode;
+
+ if (opcode <= 0) {
+ lir->use_mask = lir->def_mask = 0;
+ return;
+ }
+
+ uint64_t flags = GetTargetInstFlags(opcode);
+
+ if (flags & NEEDS_FIXUP) {
+ lir->flags.pcRelFixup = true;
+ }
+
+ /* Get the starting size of the instruction's template */
+ lir->flags.size = GetInsnSize(lir);
+
+ /* Set up the mask for resources that are updated */
+ if (flags & (IS_LOAD | IS_STORE)) {
+ /* Default to heap - will catch specialized classes later */
+ SetMemRefType(lir, flags & IS_LOAD, kHeapRef);
+ }
+
+ /*
+ * Conservatively assume the branch here will call out a function that in
+ * turn will trash everything.
+ */
+ if (flags & IS_BRANCH) {
+ lir->def_mask = lir->use_mask = ENCODE_ALL;
+ return;
+ }
+
+ if (flags & REG_DEF0) {
+ SetupRegMask(&lir->def_mask, lir->operands[0]);
+ }
+
+ if (flags & REG_DEF1) {
+ SetupRegMask(&lir->def_mask, lir->operands[1]);
+ }
+
+
+ if (flags & SETS_CCODES) {
+ lir->def_mask |= ENCODE_CCODE;
+ }
+
+ if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (flags & (1 << (kRegUse0 + i))) {
+ SetupRegMask(&lir->use_mask, lir->operands[i]);
+ }
+ }
+ }
+
+ if (flags & USES_CCODES) {
+ lir->use_mask |= ENCODE_CCODE;
+ }
+
+ // Handle target-specific actions
+ SetupTargetResourceMasks(lir);
+}
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_QUICK_MIR_TO_LIR_INL_H_
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
new file mode 100644
index 0000000..4562482
--- /dev/null
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -0,0 +1,843 @@
+/*
+ * Copyright (C) 2011 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 "dex/compiler_internals.h"
+#include "dex/dataflow_iterator-inl.h"
+#include "mir_to_lir-inl.h"
+#include "object_utils.h"
+
+namespace art {
+
+/*
+ * Target-independent code generation. Use only high-level
+ * load/store utilities here, or target-dependent genXX() handlers
+ * when necessary.
+ */
+void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list)
+{
+ RegLocation rl_src[3];
+ RegLocation rl_dest = mir_graph_->GetBadLoc();
+ RegLocation rl_result = mir_graph_->GetBadLoc();
+ Instruction::Code opcode = mir->dalvikInsn.opcode;
+ int opt_flags = mir->optimization_flags;
+ uint32_t vB = mir->dalvikInsn.vB;
+ uint32_t vC = mir->dalvikInsn.vC;
+
+ // Prep Src and Dest locations.
+ int next_sreg = 0;
+ int next_loc = 0;
+ int attrs = mir_graph_->oat_data_flow_attributes_[opcode];
+ rl_src[0] = rl_src[1] = rl_src[2] = mir_graph_->GetBadLoc();
+ if (attrs & DF_UA) {
+ if (attrs & DF_A_WIDE) {
+ rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg);
+ next_sreg+= 2;
+ } else {
+ rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg);
+ next_sreg++;
+ }
+ }
+ if (attrs & DF_UB) {
+ if (attrs & DF_B_WIDE) {
+ rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg);
+ next_sreg+= 2;
+ } else {
+ rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg);
+ next_sreg++;
+ }
+ }
+ if (attrs & DF_UC) {
+ if (attrs & DF_C_WIDE) {
+ rl_src[next_loc++] = mir_graph_->GetSrcWide(mir, next_sreg);
+ } else {
+ rl_src[next_loc++] = mir_graph_->GetSrc(mir, next_sreg);
+ }
+ }
+ if (attrs & DF_DA) {
+ if (attrs & DF_A_WIDE) {
+ rl_dest = mir_graph_->GetDestWide(mir);
+ } else {
+ rl_dest = mir_graph_->GetDest(mir);
+ }
+ }
+ switch (opcode) {
+ case Instruction::NOP:
+ break;
+
+ case Instruction::MOVE_EXCEPTION:
+ GenMoveException(rl_dest);
+ break;
+
+ case Instruction::RETURN_VOID:
+ if (((cu_->access_flags & kAccConstructor) != 0) &&
+ cu_->compiler_driver->RequiresConstructorBarrier(Thread::Current(), cu_->dex_file,
+ cu_->class_def_idx)) {
+ GenMemBarrier(kStoreStore);
+ }
+ if (!mir_graph_->MethodIsLeaf()) {
+ GenSuspendTest(opt_flags);
+ }
+ break;
+
+ case Instruction::RETURN:
+ case Instruction::RETURN_OBJECT:
+ if (!mir_graph_->MethodIsLeaf()) {
+ GenSuspendTest(opt_flags);
+ }
+ StoreValue(GetReturn(cu_->shorty[0] == 'F'), rl_src[0]);
+ break;
+
+ case Instruction::RETURN_WIDE:
+ if (!mir_graph_->MethodIsLeaf()) {
+ GenSuspendTest(opt_flags);
+ }
+ StoreValueWide(GetReturnWide(cu_->shorty[0] == 'D'), rl_src[0]);
+ break;
+
+ case Instruction::MOVE_RESULT_WIDE:
+ if (opt_flags & MIR_INLINED)
+ break; // Nop - combined w/ previous invoke.
+ StoreValueWide(rl_dest, GetReturnWide(rl_dest.fp));
+ break;
+
+ case Instruction::MOVE_RESULT:
+ case Instruction::MOVE_RESULT_OBJECT:
+ if (opt_flags & MIR_INLINED)
+ break; // Nop - combined w/ previous invoke.
+ StoreValue(rl_dest, GetReturn(rl_dest.fp));
+ break;
+
+ case Instruction::MOVE:
+ case Instruction::MOVE_OBJECT:
+ case Instruction::MOVE_16:
+ case Instruction::MOVE_OBJECT_16:
+ case Instruction::MOVE_FROM16:
+ case Instruction::MOVE_OBJECT_FROM16:
+ StoreValue(rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::MOVE_WIDE:
+ case Instruction::MOVE_WIDE_16:
+ case Instruction::MOVE_WIDE_FROM16:
+ StoreValueWide(rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::CONST:
+ case Instruction::CONST_4:
+ case Instruction::CONST_16:
+ rl_result = EvalLoc(rl_dest, kAnyReg, true);
+ LoadConstantNoClobber(rl_result.low_reg, vB);
+ StoreValue(rl_dest, rl_result);
+ if (vB == 0) {
+ Workaround7250540(rl_dest, rl_result.low_reg);
+ }
+ break;
+
+ case Instruction::CONST_HIGH16:
+ rl_result = EvalLoc(rl_dest, kAnyReg, true);
+ LoadConstantNoClobber(rl_result.low_reg, vB << 16);
+ StoreValue(rl_dest, rl_result);
+ if (vB == 0) {
+ Workaround7250540(rl_dest, rl_result.low_reg);
+ }
+ break;
+
+ case Instruction::CONST_WIDE_16:
+ case Instruction::CONST_WIDE_32:
+ rl_result = EvalLoc(rl_dest, kAnyReg, true);
+ LoadConstantWide(rl_result.low_reg, rl_result.high_reg,
+ static_cast<int64_t>(static_cast<int32_t>(vB)));
+ StoreValueWide(rl_dest, rl_result);
+ break;
+
+ case Instruction::CONST_WIDE:
+ rl_result = EvalLoc(rl_dest, kAnyReg, true);
+ LoadConstantWide(rl_result.low_reg, rl_result.high_reg, mir->dalvikInsn.vB_wide);
+ StoreValueWide(rl_dest, rl_result);
+ break;
+
+ case Instruction::CONST_WIDE_HIGH16:
+ rl_result = EvalLoc(rl_dest, kAnyReg, true);
+ LoadConstantWide(rl_result.low_reg, rl_result.high_reg,
+ static_cast<int64_t>(vB) << 48);
+ StoreValueWide(rl_dest, rl_result);
+ break;
+
+ case Instruction::MONITOR_ENTER:
+ GenMonitorEnter(opt_flags, rl_src[0]);
+ break;
+
+ case Instruction::MONITOR_EXIT:
+ GenMonitorExit(opt_flags, rl_src[0]);
+ break;
+
+ case Instruction::CHECK_CAST: {
+ GenCheckCast(mir->offset, vB, rl_src[0]);
+ break;
+ }
+ case Instruction::INSTANCE_OF:
+ GenInstanceof(vC, rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::NEW_INSTANCE:
+ GenNewInstance(vB, rl_dest);
+ break;
+
+ case Instruction::THROW:
+ GenThrow(rl_src[0]);
+ break;
+
+ case Instruction::ARRAY_LENGTH:
+ int len_offset;
+ len_offset = mirror::Array::LengthOffset().Int32Value();
+ rl_src[0] = LoadValue(rl_src[0], kCoreReg);
+ GenNullCheck(rl_src[0].s_reg_low, rl_src[0].low_reg, opt_flags);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ LoadWordDisp(rl_src[0].low_reg, len_offset, rl_result.low_reg);
+ StoreValue(rl_dest, rl_result);
+ break;
+
+ case Instruction::CONST_STRING:
+ case Instruction::CONST_STRING_JUMBO:
+ GenConstString(vB, rl_dest);
+ break;
+
+ case Instruction::CONST_CLASS:
+ GenConstClass(vB, rl_dest);
+ break;
+
+ case Instruction::FILL_ARRAY_DATA:
+ GenFillArrayData(vB, rl_src[0]);
+ break;
+
+ case Instruction::FILLED_NEW_ARRAY:
+ GenFilledNewArray(mir_graph_->NewMemCallInfo(bb, mir, kStatic,
+ false /* not range */));
+ break;
+
+ case Instruction::FILLED_NEW_ARRAY_RANGE:
+ GenFilledNewArray(mir_graph_->NewMemCallInfo(bb, mir, kStatic,
+ true /* range */));
+ break;
+
+ case Instruction::NEW_ARRAY:
+ GenNewArray(vC, rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::GOTO:
+ case Instruction::GOTO_16:
+ case Instruction::GOTO_32:
+ if (bb->taken->start_offset <= mir->offset) {
+ GenSuspendTestAndBranch(opt_flags, &label_list[bb->taken->id]);
+ } else {
+ OpUnconditionalBranch(&label_list[bb->taken->id]);
+ }
+ break;
+
+ case Instruction::PACKED_SWITCH:
+ GenPackedSwitch(mir, vB, rl_src[0]);
+ break;
+
+ case Instruction::SPARSE_SWITCH:
+ GenSparseSwitch(mir, vB, rl_src[0]);
+ break;
+
+ case Instruction::CMPL_FLOAT:
+ case Instruction::CMPG_FLOAT:
+ case Instruction::CMPL_DOUBLE:
+ case Instruction::CMPG_DOUBLE:
+ GenCmpFP(opcode, rl_dest, rl_src[0], rl_src[1]);
+ break;
+
+ case Instruction::CMP_LONG:
+ GenCmpLong(rl_dest, rl_src[0], rl_src[1]);
+ break;
+
+ case Instruction::IF_EQ:
+ case Instruction::IF_NE:
+ case Instruction::IF_LT:
+ case Instruction::IF_GE:
+ case Instruction::IF_GT:
+ case Instruction::IF_LE: {
+ LIR* taken = &label_list[bb->taken->id];
+ LIR* fall_through = &label_list[bb->fall_through->id];
+ bool backward_branch;
+ backward_branch = (bb->taken->start_offset <= mir->offset);
+ // Result known at compile time?
+ if (rl_src[0].is_const && rl_src[1].is_const) {
+ bool is_taken = EvaluateBranch(opcode, mir_graph_->ConstantValue(rl_src[0].orig_sreg),
+ mir_graph_->ConstantValue(rl_src[1].orig_sreg));
+ if (is_taken && backward_branch) {
+ GenSuspendTest(opt_flags);
+ }
+ int id = is_taken ? bb->taken->id : bb->fall_through->id;
+ OpUnconditionalBranch(&label_list[id]);
+ } else {
+ if (backward_branch) {
+ GenSuspendTest(opt_flags);
+ }
+ GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken,
+ fall_through);
+ }
+ break;
+ }
+
+ case Instruction::IF_EQZ:
+ case Instruction::IF_NEZ:
+ case Instruction::IF_LTZ:
+ case Instruction::IF_GEZ:
+ case Instruction::IF_GTZ:
+ case Instruction::IF_LEZ: {
+ LIR* taken = &label_list[bb->taken->id];
+ LIR* fall_through = &label_list[bb->fall_through->id];
+ bool backward_branch;
+ backward_branch = (bb->taken->start_offset <= mir->offset);
+ // Result known at compile time?
+ if (rl_src[0].is_const) {
+ bool is_taken = EvaluateBranch(opcode, mir_graph_->ConstantValue(rl_src[0].orig_sreg), 0);
+ if (is_taken && backward_branch) {
+ GenSuspendTest(opt_flags);
+ }
+ int id = is_taken ? bb->taken->id : bb->fall_through->id;
+ OpUnconditionalBranch(&label_list[id]);
+ } else {
+ if (backward_branch) {
+ GenSuspendTest(opt_flags);
+ }
+ GenCompareZeroAndBranch(opcode, rl_src[0], taken, fall_through);
+ }
+ break;
+ }
+
+ case Instruction::AGET_WIDE:
+ GenArrayGet(opt_flags, kLong, rl_src[0], rl_src[1], rl_dest, 3);
+ break;
+ case Instruction::AGET:
+ case Instruction::AGET_OBJECT:
+ GenArrayGet(opt_flags, kWord, rl_src[0], rl_src[1], rl_dest, 2);
+ break;
+ case Instruction::AGET_BOOLEAN:
+ GenArrayGet(opt_flags, kUnsignedByte, rl_src[0], rl_src[1], rl_dest, 0);
+ break;
+ case Instruction::AGET_BYTE:
+ GenArrayGet(opt_flags, kSignedByte, rl_src[0], rl_src[1], rl_dest, 0);
+ break;
+ case Instruction::AGET_CHAR:
+ GenArrayGet(opt_flags, kUnsignedHalf, rl_src[0], rl_src[1], rl_dest, 1);
+ break;
+ case Instruction::AGET_SHORT:
+ GenArrayGet(opt_flags, kSignedHalf, rl_src[0], rl_src[1], rl_dest, 1);
+ break;
+ case Instruction::APUT_WIDE:
+ GenArrayPut(opt_flags, kLong, rl_src[1], rl_src[2], rl_src[0], 3);
+ break;
+ case Instruction::APUT:
+ GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2);
+ break;
+ case Instruction::APUT_OBJECT:
+ GenArrayObjPut(opt_flags, rl_src[1], rl_src[2], rl_src[0], 2);
+ break;
+ case Instruction::APUT_SHORT:
+ case Instruction::APUT_CHAR:
+ GenArrayPut(opt_flags, kUnsignedHalf, rl_src[1], rl_src[2], rl_src[0], 1);
+ break;
+ case Instruction::APUT_BYTE:
+ case Instruction::APUT_BOOLEAN:
+ GenArrayPut(opt_flags, kUnsignedByte, rl_src[1], rl_src[2],
+ rl_src[0], 0);
+ break;
+
+ case Instruction::IGET_OBJECT:
+ GenIGet(vC, opt_flags, kWord, rl_dest, rl_src[0], false, true);
+ break;
+
+ case Instruction::IGET_WIDE:
+ GenIGet(vC, opt_flags, kLong, rl_dest, rl_src[0], true, false);
+ break;
+
+ case Instruction::IGET:
+ GenIGet(vC, opt_flags, kWord, rl_dest, rl_src[0], false, false);
+ break;
+
+ case Instruction::IGET_CHAR:
+ GenIGet(vC, opt_flags, kUnsignedHalf, rl_dest, rl_src[0], false, false);
+ break;
+
+ case Instruction::IGET_SHORT:
+ GenIGet(vC, opt_flags, kSignedHalf, rl_dest, rl_src[0], false, false);
+ break;
+
+ case Instruction::IGET_BOOLEAN:
+ case Instruction::IGET_BYTE:
+ GenIGet(vC, opt_flags, kUnsignedByte, rl_dest, rl_src[0], false, false);
+ break;
+
+ case Instruction::IPUT_WIDE:
+ GenIPut(vC, opt_flags, kLong, rl_src[0], rl_src[1], true, false);
+ break;
+
+ case Instruction::IPUT_OBJECT:
+ GenIPut(vC, opt_flags, kWord, rl_src[0], rl_src[1], false, true);
+ break;
+
+ case Instruction::IPUT:
+ GenIPut(vC, opt_flags, kWord, rl_src[0], rl_src[1], false, false);
+ break;
+
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BYTE:
+ GenIPut(vC, opt_flags, kUnsignedByte, rl_src[0], rl_src[1], false, false);
+ break;
+
+ case Instruction::IPUT_CHAR:
+ GenIPut(vC, opt_flags, kUnsignedHalf, rl_src[0], rl_src[1], false, false);
+ break;
+
+ case Instruction::IPUT_SHORT:
+ GenIPut(vC, opt_flags, kSignedHalf, rl_src[0], rl_src[1], false, false);
+ break;
+
+ case Instruction::SGET_OBJECT:
+ GenSget(vB, rl_dest, false, true);
+ break;
+ case Instruction::SGET:
+ case Instruction::SGET_BOOLEAN:
+ case Instruction::SGET_BYTE:
+ case Instruction::SGET_CHAR:
+ case Instruction::SGET_SHORT:
+ GenSget(vB, rl_dest, false, false);
+ break;
+
+ case Instruction::SGET_WIDE:
+ GenSget(vB, rl_dest, true, false);
+ break;
+
+ case Instruction::SPUT_OBJECT:
+ GenSput(vB, rl_src[0], false, true);
+ break;
+
+ case Instruction::SPUT:
+ case Instruction::SPUT_BOOLEAN:
+ case Instruction::SPUT_BYTE:
+ case Instruction::SPUT_CHAR:
+ case Instruction::SPUT_SHORT:
+ GenSput(vB, rl_src[0], false, false);
+ break;
+
+ case Instruction::SPUT_WIDE:
+ GenSput(vB, rl_src[0], true, false);
+ break;
+
+ case Instruction::INVOKE_STATIC_RANGE:
+ GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kStatic, true));
+ break;
+ case Instruction::INVOKE_STATIC:
+ GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kStatic, false));
+ break;
+
+ case Instruction::INVOKE_DIRECT:
+ GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kDirect, false));
+ break;
+ case Instruction::INVOKE_DIRECT_RANGE:
+ GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kDirect, true));
+ break;
+
+ case Instruction::INVOKE_VIRTUAL:
+ GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kVirtual, false));
+ break;
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kVirtual, true));
+ break;
+
+ case Instruction::INVOKE_SUPER:
+ GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kSuper, false));
+ break;
+ case Instruction::INVOKE_SUPER_RANGE:
+ GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kSuper, true));
+ break;
+
+ case Instruction::INVOKE_INTERFACE:
+ GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kInterface, false));
+ break;
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ GenInvoke(mir_graph_->NewMemCallInfo(bb, mir, kInterface, true));
+ break;
+
+ case Instruction::NEG_INT:
+ case Instruction::NOT_INT:
+ GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[0]);
+ break;
+
+ case Instruction::NEG_LONG:
+ case Instruction::NOT_LONG:
+ GenArithOpLong(opcode, rl_dest, rl_src[0], rl_src[0]);
+ break;
+
+ case Instruction::NEG_FLOAT:
+ GenArithOpFloat(opcode, rl_dest, rl_src[0], rl_src[0]);
+ break;
+
+ case Instruction::NEG_DOUBLE:
+ GenArithOpDouble(opcode, rl_dest, rl_src[0], rl_src[0]);
+ break;
+
+ case Instruction::INT_TO_LONG:
+ GenIntToLong(rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::LONG_TO_INT:
+ rl_src[0] = UpdateLocWide(rl_src[0]);
+ rl_src[0] = WideToNarrow(rl_src[0]);
+ StoreValue(rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::INT_TO_BYTE:
+ case Instruction::INT_TO_SHORT:
+ case Instruction::INT_TO_CHAR:
+ GenIntNarrowing(opcode, rl_dest, rl_src[0]);
+ break;
+
+ case Instruction::INT_TO_FLOAT:
+ case Instruction::INT_TO_DOUBLE:
+ case Instruction::LONG_TO_FLOAT:
+ case Instruction::LONG_TO_DOUBLE:
+ case Instruction::FLOAT_TO_INT:
+ case Instruction::FLOAT_TO_LONG:
+ case Instruction::FLOAT_TO_DOUBLE:
+ case Instruction::DOUBLE_TO_INT:
+ case Instruction::DOUBLE_TO_LONG:
+ case Instruction::DOUBLE_TO_FLOAT:
+ GenConversion(opcode, rl_dest, rl_src[0]);
+ break;
+
+
+ case Instruction::ADD_INT:
+ case Instruction::ADD_INT_2ADDR:
+ case Instruction::MUL_INT:
+ case Instruction::MUL_INT_2ADDR:
+ case Instruction::AND_INT:
+ case Instruction::AND_INT_2ADDR:
+ case Instruction::OR_INT:
+ case Instruction::OR_INT_2ADDR:
+ case Instruction::XOR_INT:
+ case Instruction::XOR_INT_2ADDR:
+ if (rl_src[0].is_const &&
+ InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src[0]))) {
+ GenArithOpIntLit(opcode, rl_dest, rl_src[1],
+ mir_graph_->ConstantValue(rl_src[0].orig_sreg));
+ } else if (rl_src[1].is_const &&
+ InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src[1]))) {
+ GenArithOpIntLit(opcode, rl_dest, rl_src[0],
+ mir_graph_->ConstantValue(rl_src[1].orig_sreg));
+ } else {
+ GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[1]);
+ }
+ break;
+
+ case Instruction::SUB_INT:
+ case Instruction::SUB_INT_2ADDR:
+ case Instruction::DIV_INT:
+ case Instruction::DIV_INT_2ADDR:
+ case Instruction::REM_INT:
+ case Instruction::REM_INT_2ADDR:
+ case Instruction::SHL_INT:
+ case Instruction::SHL_INT_2ADDR:
+ case Instruction::SHR_INT:
+ case Instruction::SHR_INT_2ADDR:
+ case Instruction::USHR_INT:
+ case Instruction::USHR_INT_2ADDR:
+ if (rl_src[1].is_const &&
+ InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src[1]))) {
+ GenArithOpIntLit(opcode, rl_dest, rl_src[0], mir_graph_->ConstantValue(rl_src[1]));
+ } else {
+ GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[1]);
+ }
+ break;
+
+ case Instruction::ADD_LONG:
+ case Instruction::SUB_LONG:
+ case Instruction::AND_LONG:
+ case Instruction::OR_LONG:
+ case Instruction::XOR_LONG:
+ case Instruction::ADD_LONG_2ADDR:
+ case Instruction::SUB_LONG_2ADDR:
+ case Instruction::AND_LONG_2ADDR:
+ case Instruction::OR_LONG_2ADDR:
+ case Instruction::XOR_LONG_2ADDR:
+ if (rl_src[0].is_const || rl_src[1].is_const) {
+ GenArithImmOpLong(opcode, rl_dest, rl_src[0], rl_src[1]);
+ break;
+ }
+ // Note: intentional fallthrough.
+
+ case Instruction::MUL_LONG:
+ case Instruction::DIV_LONG:
+ case Instruction::REM_LONG:
+ case Instruction::MUL_LONG_2ADDR:
+ case Instruction::DIV_LONG_2ADDR:
+ case Instruction::REM_LONG_2ADDR:
+ GenArithOpLong(opcode, rl_dest, rl_src[0], rl_src[1]);
+ break;
+
+ case Instruction::SHL_LONG:
+ case Instruction::SHR_LONG:
+ case Instruction::USHR_LONG:
+ case Instruction::SHL_LONG_2ADDR:
+ case Instruction::SHR_LONG_2ADDR:
+ case Instruction::USHR_LONG_2ADDR:
+ if (rl_src[1].is_const) {
+ GenShiftImmOpLong(opcode, rl_dest, rl_src[0], rl_src[1]);
+ } else {
+ GenShiftOpLong(opcode, rl_dest, rl_src[0], rl_src[1]);
+ }
+ break;
+
+ case Instruction::ADD_FLOAT:
+ case Instruction::SUB_FLOAT:
+ case Instruction::MUL_FLOAT:
+ case Instruction::DIV_FLOAT:
+ case Instruction::REM_FLOAT:
+ case Instruction::ADD_FLOAT_2ADDR:
+ case Instruction::SUB_FLOAT_2ADDR:
+ case Instruction::MUL_FLOAT_2ADDR:
+ case Instruction::DIV_FLOAT_2ADDR:
+ case Instruction::REM_FLOAT_2ADDR:
+ GenArithOpFloat(opcode, rl_dest, rl_src[0], rl_src[1]);
+ break;
+
+ case Instruction::ADD_DOUBLE:
+ case Instruction::SUB_DOUBLE:
+ case Instruction::MUL_DOUBLE:
+ case Instruction::DIV_DOUBLE:
+ case Instruction::REM_DOUBLE:
+ case Instruction::ADD_DOUBLE_2ADDR:
+ case Instruction::SUB_DOUBLE_2ADDR:
+ case Instruction::MUL_DOUBLE_2ADDR:
+ case Instruction::DIV_DOUBLE_2ADDR:
+ case Instruction::REM_DOUBLE_2ADDR:
+ GenArithOpDouble(opcode, rl_dest, rl_src[0], rl_src[1]);
+ break;
+
+ case Instruction::RSUB_INT:
+ case Instruction::ADD_INT_LIT16:
+ case Instruction::MUL_INT_LIT16:
+ case Instruction::DIV_INT_LIT16:
+ case Instruction::REM_INT_LIT16:
+ case Instruction::AND_INT_LIT16:
+ case Instruction::OR_INT_LIT16:
+ case Instruction::XOR_INT_LIT16:
+ case Instruction::ADD_INT_LIT8:
+ case Instruction::RSUB_INT_LIT8:
+ case Instruction::MUL_INT_LIT8:
+ case Instruction::DIV_INT_LIT8:
+ case Instruction::REM_INT_LIT8:
+ case Instruction::AND_INT_LIT8:
+ case Instruction::OR_INT_LIT8:
+ case Instruction::XOR_INT_LIT8:
+ case Instruction::SHL_INT_LIT8:
+ case Instruction::SHR_INT_LIT8:
+ case Instruction::USHR_INT_LIT8:
+ GenArithOpIntLit(opcode, rl_dest, rl_src[0], vC);
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+}
+
+// Process extended MIR instructions
+void Mir2Lir::HandleExtendedMethodMIR(BasicBlock* bb, MIR* mir)
+{
+ switch (static_cast<ExtendedMIROpcode>(mir->dalvikInsn.opcode)) {
+ case kMirOpCopy: {
+ RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
+ RegLocation rl_dest = mir_graph_->GetDest(mir);
+ StoreValue(rl_dest, rl_src);
+ break;
+ }
+ case kMirOpFusedCmplFloat:
+ GenFusedFPCmpBranch(bb, mir, false /*gt bias*/, false /*double*/);
+ break;
+ case kMirOpFusedCmpgFloat:
+ GenFusedFPCmpBranch(bb, mir, true /*gt bias*/, false /*double*/);
+ break;
+ case kMirOpFusedCmplDouble:
+ GenFusedFPCmpBranch(bb, mir, false /*gt bias*/, true /*double*/);
+ break;
+ case kMirOpFusedCmpgDouble:
+ GenFusedFPCmpBranch(bb, mir, true /*gt bias*/, true /*double*/);
+ break;
+ case kMirOpFusedCmpLong:
+ GenFusedLongCmpBranch(bb, mir);
+ break;
+ case kMirOpSelect:
+ GenSelect(bb, mir);
+ break;
+ default:
+ break;
+ }
+}
+
+// Handle the content in each basic block.
+bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb)
+{
+ if (bb->block_type == kDead) return false;
+ current_dalvik_offset_ = bb->start_offset;
+ MIR* mir;
+ int block_id = bb->id;
+
+ block_label_list_[block_id].operands[0] = bb->start_offset;
+
+ // Insert the block label.
+ block_label_list_[block_id].opcode = kPseudoNormalBlockLabel;
+ AppendLIR(&block_label_list_[block_id]);
+
+ LIR* head_lir = NULL;
+
+ // If this is a catch block, export the start address.
+ if (bb->catch_entry) {
+ head_lir = NewLIR0(kPseudoExportedPC);
+ }
+
+ // Free temp registers and reset redundant store tracking.
+ ResetRegPool();
+ ResetDefTracking();
+
+ ClobberAllRegs();
+
+ if (bb->block_type == kEntryBlock) {
+ int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
+ GenEntrySequence(&mir_graph_->reg_location_[start_vreg],
+ mir_graph_->reg_location_[mir_graph_->GetMethodSReg()]);
+ } else if (bb->block_type == kExitBlock) {
+ GenExitSequence();
+ }
+
+ for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ ResetRegPool();
+ if (cu_->disable_opt & (1 << kTrackLiveTemps)) {
+ ClobberAllRegs();
+ }
+
+ if (cu_->disable_opt & (1 << kSuppressLoads)) {
+ ResetDefTracking();
+ }
+
+ // Reset temp tracking sanity check.
+ if (kIsDebugBuild) {
+ live_sreg_ = INVALID_SREG;
+ }
+
+ current_dalvik_offset_ = mir->offset;
+ int opcode = mir->dalvikInsn.opcode;
+ LIR* boundary_lir;
+
+ // Mark the beginning of a Dalvik instruction for line tracking.
+ char* inst_str = cu_->verbose ?
+ mir_graph_->GetDalvikDisassembly(mir) : NULL;
+ boundary_lir = MarkBoundary(mir->offset, inst_str);
+ // Remember the first LIR for this block.
+ if (head_lir == NULL) {
+ head_lir = boundary_lir;
+ // Set the first boundary_lir as a scheduling barrier.
+ head_lir->def_mask = ENCODE_ALL;
+ }
+
+ if (opcode == kMirOpCheck) {
+ // Combine check and work halves of throwing instruction.
+ MIR* work_half = mir->meta.throw_insn;
+ mir->dalvikInsn.opcode = work_half->dalvikInsn.opcode;
+ opcode = work_half->dalvikInsn.opcode;
+ SSARepresentation* ssa_rep = work_half->ssa_rep;
+ work_half->ssa_rep = mir->ssa_rep;
+ mir->ssa_rep = ssa_rep;
+ work_half->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpCheckPart2);
+ }
+
+ if (opcode >= kMirOpFirst) {
+ HandleExtendedMethodMIR(bb, mir);
+ continue;
+ }
+
+ CompileDalvikInstruction(mir, bb, block_label_list_);
+ }
+
+ if (head_lir) {
+ // Eliminate redundant loads/stores and delay stores into later slots.
+ ApplyLocalOptimizations(head_lir, last_lir_insn_);
+
+ // Generate an unconditional branch to the fallthrough block.
+ if (bb->fall_through) {
+ OpUnconditionalBranch(&block_label_list_[bb->fall_through->id]);
+ }
+ }
+ return false;
+}
+
+void Mir2Lir::SpecialMIR2LIR(SpecialCaseHandler special_case)
+{
+ // Find the first DalvikByteCode block.
+ int num_reachable_blocks = mir_graph_->GetNumReachableBlocks();
+ BasicBlock*bb = NULL;
+ for (int idx = 0; idx < num_reachable_blocks; idx++) {
+ // TODO: no direct access of growable lists.
+ int dfs_index = mir_graph_->GetDfsOrder()->Get(idx);
+ bb = mir_graph_->GetBasicBlock(dfs_index);
+ if (bb->block_type == kDalvikByteCode) {
+ break;
+ }
+ }
+ if (bb == NULL) {
+ return;
+ }
+ DCHECK_EQ(bb->start_offset, 0);
+ DCHECK(bb->first_mir_insn != NULL);
+
+ // Get the first instruction.
+ MIR* mir = bb->first_mir_insn;
+
+ // Free temp registers and reset redundant store tracking.
+ ResetRegPool();
+ ResetDefTracking();
+ ClobberAllRegs();
+
+ GenSpecialCase(bb, mir, special_case);
+}
+
+void Mir2Lir::MethodMIR2LIR()
+{
+ // Hold the labels of each block.
+ block_label_list_ =
+ static_cast<LIR*>(arena_->NewMem(sizeof(LIR) * mir_graph_->GetNumBlocks(), true,
+ ArenaAllocator::kAllocLIR));
+
+ PreOrderDfsIterator iter(mir_graph_, false /* not iterative */);
+ for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
+ MethodBlockCodeGen(bb);
+ }
+
+ HandleSuspendLaunchPads();
+
+ HandleThrowLaunchPads();
+
+ HandleIntrinsicLaunchPads();
+
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations))) {
+ RemoveRedundantBranches();
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
new file mode 100644
index 0000000..47514f7
--- /dev/null
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -0,0 +1,779 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_QUICK_MIR_TO_LIR_H_
+#define ART_SRC_COMPILER_DEX_QUICK_MIR_TO_LIR_H_
+
+#include "invoke_type.h"
+#include "compiled_method.h"
+#include "dex/compiler_enums.h"
+#include "dex/compiler_ir.h"
+#include "dex/backend.h"
+#include "dex/growable_array.h"
+#include "dex/arena_allocator.h"
+#include "driver/compiler_driver.h"
+#include "safe_map.h"
+
+namespace art {
+
+// Set to 1 to measure cost of suspend check.
+#define NO_SUSPEND 0
+
+#define IS_BINARY_OP (1ULL << kIsBinaryOp)
+#define IS_BRANCH (1ULL << kIsBranch)
+#define IS_IT (1ULL << kIsIT)
+#define IS_LOAD (1ULL << kMemLoad)
+#define IS_QUAD_OP (1ULL << kIsQuadOp)
+#define IS_QUIN_OP (1ULL << kIsQuinOp)
+#define IS_SEXTUPLE_OP (1ULL << kIsSextupleOp)
+#define IS_STORE (1ULL << kMemStore)
+#define IS_TERTIARY_OP (1ULL << kIsTertiaryOp)
+#define IS_UNARY_OP (1ULL << kIsUnaryOp)
+#define NEEDS_FIXUP (1ULL << kPCRelFixup)
+#define NO_OPERAND (1ULL << kNoOperand)
+#define REG_DEF0 (1ULL << kRegDef0)
+#define REG_DEF1 (1ULL << kRegDef1)
+#define REG_DEFA (1ULL << kRegDefA)
+#define REG_DEFD (1ULL << kRegDefD)
+#define REG_DEF_FPCS_LIST0 (1ULL << kRegDefFPCSList0)
+#define REG_DEF_FPCS_LIST2 (1ULL << kRegDefFPCSList2)
+#define REG_DEF_LIST0 (1ULL << kRegDefList0)
+#define REG_DEF_LIST1 (1ULL << kRegDefList1)
+#define REG_DEF_LR (1ULL << kRegDefLR)
+#define REG_DEF_SP (1ULL << kRegDefSP)
+#define REG_USE0 (1ULL << kRegUse0)
+#define REG_USE1 (1ULL << kRegUse1)
+#define REG_USE2 (1ULL << kRegUse2)
+#define REG_USE3 (1ULL << kRegUse3)
+#define REG_USE4 (1ULL << kRegUse4)
+#define REG_USEA (1ULL << kRegUseA)
+#define REG_USEC (1ULL << kRegUseC)
+#define REG_USED (1ULL << kRegUseD)
+#define REG_USE_FPCS_LIST0 (1ULL << kRegUseFPCSList0)
+#define REG_USE_FPCS_LIST2 (1ULL << kRegUseFPCSList2)
+#define REG_USE_LIST0 (1ULL << kRegUseList0)
+#define REG_USE_LIST1 (1ULL << kRegUseList1)
+#define REG_USE_LR (1ULL << kRegUseLR)
+#define REG_USE_PC (1ULL << kRegUsePC)
+#define REG_USE_SP (1ULL << kRegUseSP)
+#define SETS_CCODES (1ULL << kSetsCCodes)
+#define USES_CCODES (1ULL << kUsesCCodes)
+
+// Common combo register usage patterns.
+#define REG_DEF01 (REG_DEF0 | REG_DEF1)
+#define REG_DEF01_USE2 (REG_DEF0 | REG_DEF1 | REG_USE2)
+#define REG_DEF0_USE01 (REG_DEF0 | REG_USE01)
+#define REG_DEF0_USE0 (REG_DEF0 | REG_USE0)
+#define REG_DEF0_USE12 (REG_DEF0 | REG_USE12)
+#define REG_DEF0_USE1 (REG_DEF0 | REG_USE1)
+#define REG_DEF0_USE2 (REG_DEF0 | REG_USE2)
+#define REG_DEFAD_USEAD (REG_DEFAD_USEA | REG_USED)
+#define REG_DEFAD_USEA (REG_DEFA_USEA | REG_DEFD)
+#define REG_DEFA_USEA (REG_DEFA | REG_USEA)
+#define REG_USE012 (REG_USE01 | REG_USE2)
+#define REG_USE014 (REG_USE01 | REG_USE4)
+#define REG_USE01 (REG_USE0 | REG_USE1)
+#define REG_USE02 (REG_USE0 | REG_USE2)
+#define REG_USE12 (REG_USE1 | REG_USE2)
+#define REG_USE23 (REG_USE2 | REG_USE3)
+
+struct BasicBlock;
+struct CallInfo;
+struct CompilationUnit;
+struct MIR;
+struct RegLocation;
+struct RegisterInfo;
+class MIRGraph;
+class Mir2Lir;
+
+typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int,
+ const MethodReference& target_method,
+ uint32_t method_idx, uintptr_t direct_code,
+ uintptr_t direct_method, InvokeType type);
+
+typedef std::vector<uint8_t> CodeBuffer;
+
+
+struct LIR {
+ int offset; // Offset of this instruction.
+ int dalvik_offset; // Offset of Dalvik opcode.
+ LIR* next;
+ LIR* prev;
+ LIR* target;
+ int opcode;
+ int operands[5]; // [0..4] = [dest, src1, src2, extra, extra2].
+ struct {
+ bool is_nop:1; // LIR is optimized away.
+ bool pcRelFixup:1; // May need pc-relative fixup.
+ unsigned int size:5; // Note: size is in bytes.
+ unsigned int unused:25;
+ } flags;
+ int alias_info; // For Dalvik register & litpool disambiguation.
+ uint64_t use_mask; // Resource mask for use.
+ uint64_t def_mask; // Resource mask for def.
+};
+
+// Target-specific initialization.
+Mir2Lir* ArmCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena);
+Mir2Lir* MipsCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena);
+Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena);
+
+// Utility macros to traverse the LIR list.
+#define NEXT_LIR(lir) (lir->next)
+#define PREV_LIR(lir) (lir->prev)
+
+// Defines for alias_info (tracks Dalvik register references).
+#define DECODE_ALIAS_INFO_REG(X) (X & 0xffff)
+#define DECODE_ALIAS_INFO_WIDE_FLAG (0x80000000)
+#define DECODE_ALIAS_INFO_WIDE(X) ((X & DECODE_ALIAS_INFO_WIDE_FLAG) ? 1 : 0)
+#define ENCODE_ALIAS_INFO(REG, ISWIDE) (REG | (ISWIDE ? DECODE_ALIAS_INFO_WIDE_FLAG : 0))
+
+// Common resource macros.
+#define ENCODE_CCODE (1ULL << kCCode)
+#define ENCODE_FP_STATUS (1ULL << kFPStatus)
+
+// Abstract memory locations.
+#define ENCODE_DALVIK_REG (1ULL << kDalvikReg)
+#define ENCODE_LITERAL (1ULL << kLiteral)
+#define ENCODE_HEAP_REF (1ULL << kHeapRef)
+#define ENCODE_MUST_NOT_ALIAS (1ULL << kMustNotAlias)
+
+#define ENCODE_ALL (~0ULL)
+#define ENCODE_MEM (ENCODE_DALVIK_REG | ENCODE_LITERAL | \
+ ENCODE_HEAP_REF | ENCODE_MUST_NOT_ALIAS)
+//TODO: replace these macros
+#define SLOW_FIELD_PATH (cu_->enable_debug & (1 << kDebugSlowFieldPath))
+#define SLOW_INVOKE_PATH (cu_->enable_debug & (1 << kDebugSlowInvokePath))
+#define SLOW_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowStringPath))
+#define SLOW_TYPE_PATH (cu_->enable_debug & (1 << kDebugSlowTypePath))
+#define EXERCISE_SLOWEST_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowestStringPath))
+#define is_pseudo_opcode(opcode) (static_cast<int>(opcode) < 0)
+
+class Mir2Lir : public Backend {
+
+ public:
+ struct SwitchTable {
+ int offset;
+ const uint16_t* table; // Original dex table.
+ int vaddr; // Dalvik offset of switch opcode.
+ LIR* anchor; // Reference instruction for relative offsets.
+ LIR** targets; // Array of case targets.
+ };
+
+ struct FillArrayData {
+ int offset;
+ const uint16_t* table; // Original dex table.
+ int size;
+ int vaddr; // Dalvik offset of FILL_ARRAY_DATA opcode.
+ };
+
+ /* Static register use counts */
+ struct RefCounts {
+ int count;
+ int s_reg;
+ bool double_start; // Starting v_reg for a double
+ };
+
+ /*
+ * Data structure tracking the mapping between a Dalvik register (pair) and a
+ * native register (pair). The idea is to reuse the previously loaded value
+ * if possible, otherwise to keep the value in a native register as long as
+ * possible.
+ */
+ struct RegisterInfo {
+ int reg; // Reg number
+ bool in_use; // Has it been allocated?
+ bool is_temp; // Can allocate as temp?
+ bool pair; // Part of a register pair?
+ int partner; // If pair, other reg of pair.
+ bool live; // Is there an associated SSA name?
+ bool dirty; // If live, is it dirty?
+ int s_reg; // Name of live value.
+ LIR *def_start; // Starting inst in last def sequence.
+ LIR *def_end; // Ending inst in last def sequence.
+ };
+
+ struct RegisterPool {
+ int num_core_regs;
+ RegisterInfo *core_regs;
+ int next_core_reg;
+ int num_fp_regs;
+ RegisterInfo *FPRegs;
+ int next_fp_reg;
+ };
+
+ struct PromotionMap {
+ RegLocationType core_location:3;
+ uint8_t core_reg;
+ RegLocationType fp_location:3;
+ uint8_t FpReg;
+ bool first_in_pair;
+ };
+
+ virtual ~Mir2Lir(){};
+
+ int32_t s4FromSwitchData(const void* switch_data) {
+ return *reinterpret_cast<const int32_t*>(switch_data);
+ }
+
+ RegisterClass oat_reg_class_by_size(OpSize size) {
+ return (size == kUnsignedHalf || size == kSignedHalf || size == kUnsignedByte ||
+ size == kSignedByte ) ? kCoreReg : kAnyReg;
+ }
+
+ size_t CodeBufferSizeInBytes() {
+ return code_buffer_.size() / sizeof(code_buffer_[0]);
+ }
+
+ // Shared by all targets - implemented in codegen_util.cc
+ void AppendLIR(LIR* lir);
+ void InsertLIRBefore(LIR* current_lir, LIR* new_lir);
+ void InsertLIRAfter(LIR* current_lir, LIR* new_lir);
+
+ int ComputeFrameSize();
+ virtual void Materialize();
+ virtual CompiledMethod* GetCompiledMethod();
+ void MarkSafepointPC(LIR* inst);
+ bool FastInstance(uint32_t field_idx, int& field_offset, bool& is_volatile, bool is_put);
+ void SetupResourceMasks(LIR* lir);
+ void AssembleLIR();
+ void SetMemRefType(LIR* lir, bool is_load, int mem_type);
+ void AnnotateDalvikRegAccess(LIR* lir, int reg_id, bool is_load, bool is64bit);
+ void SetupRegMask(uint64_t* mask, int reg);
+ void DumpLIRInsn(LIR* arg, unsigned char* base_addr);
+ void DumpPromotionMap();
+ void CodegenDump();
+ LIR* RawLIR(int dalvik_offset, int opcode, int op0 = 0, int op1 = 0,
+ int op2 = 0, int op3 = 0, int op4 = 0, LIR* target = NULL);
+ LIR* NewLIR0(int opcode);
+ LIR* NewLIR1(int opcode, int dest);
+ LIR* NewLIR2(int opcode, int dest, int src1);
+ LIR* NewLIR3(int opcode, int dest, int src1, int src2);
+ LIR* NewLIR4(int opcode, int dest, int src1, int src2, int info);
+ LIR* NewLIR5(int opcode, int dest, int src1, int src2, int info1, int info2);
+ LIR* ScanLiteralPool(LIR* data_target, int value, unsigned int delta);
+ LIR* ScanLiteralPoolWide(LIR* data_target, int val_lo, int val_hi);
+ LIR* AddWordData(LIR* *constant_list_p, int value);
+ LIR* AddWideData(LIR* *constant_list_p, int val_lo, int val_hi);
+ void ProcessSwitchTables();
+ void DumpSparseSwitchTable(const uint16_t* table);
+ void DumpPackedSwitchTable(const uint16_t* table);
+ LIR* MarkBoundary(int offset, const char* inst_str);
+ void NopLIR(LIR* lir);
+ bool EvaluateBranch(Instruction::Code opcode, int src1, int src2);
+ bool IsInexpensiveConstant(RegLocation rl_src);
+ ConditionCode FlipComparisonOrder(ConditionCode before);
+ void DumpMappingTable(const char* table_name, const std::string& descriptor,
+ const std::string& name, const std::string& signature,
+ const std::vector<uint32_t>& v);
+ void InstallLiteralPools();
+ void InstallSwitchTables();
+ void InstallFillArrayData();
+ bool VerifyCatchEntries();
+ void CreateMappingTables();
+ void CreateNativeGcMap();
+ int AssignLiteralOffset(int offset);
+ int AssignSwitchTablesOffset(int offset);
+ int AssignFillArrayDataOffset(int offset);
+ int AssignInsnOffsets();
+ void AssignOffsets();
+ LIR* InsertCaseLabel(int vaddr, int keyVal);
+ void MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec);
+ void MarkSparseCaseLabels(Mir2Lir::SwitchTable *tab_rec);
+
+ // Shared by all targets - implemented in local_optimizations.cc
+ void ConvertMemOpIntoMove(LIR* orig_lir, int dest, int src);
+ void ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir);
+ void ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir);
+ void ApplyLocalOptimizations(LIR* head_lir, LIR* tail_lir);
+ void RemoveRedundantBranches();
+
+ // Shared by all targets - implemented in ralloc_util.cc
+ int GetSRegHi(int lowSreg);
+ bool oat_live_out(int s_reg);
+ int oatSSASrc(MIR* mir, int num);
+ void SimpleRegAlloc();
+ void ResetRegPool();
+ void CompilerInitPool(RegisterInfo* regs, int* reg_nums, int num);
+ void DumpRegPool(RegisterInfo* p, int num_regs);
+ void DumpCoreRegPool();
+ void DumpFpRegPool();
+ /* Mark a temp register as dead. Does not affect allocation state. */
+ void Clobber(int reg) {
+ ClobberBody(GetRegInfo(reg));
+ }
+ void ClobberSRegBody(RegisterInfo* p, int num_regs, int s_reg);
+ void ClobberSReg(int s_reg);
+ int SRegToPMap(int s_reg);
+ void RecordCorePromotion(int reg, int s_reg);
+ int AllocPreservedCoreReg(int s_reg);
+ void RecordFpPromotion(int reg, int s_reg);
+ int AllocPreservedSingle(int s_reg, bool even);
+ int AllocPreservedDouble(int s_reg);
+ int AllocPreservedFPReg(int s_reg, bool double_start);
+ int AllocTempBody(RegisterInfo* p, int num_regs, int* next_temp,
+ bool required);
+ int AllocTempDouble();
+ int AllocFreeTemp();
+ int AllocTemp();
+ int AllocTempFloat();
+ RegisterInfo* AllocLiveBody(RegisterInfo* p, int num_regs, int s_reg);
+ RegisterInfo* AllocLive(int s_reg, int reg_class);
+ void FreeTemp(int reg);
+ RegisterInfo* IsLive(int reg);
+ RegisterInfo* IsTemp(int reg);
+ RegisterInfo* IsPromoted(int reg);
+ bool IsDirty(int reg);
+ void LockTemp(int reg);
+ void ResetDef(int reg);
+ void NullifyRange(LIR *start, LIR *finish, int s_reg1, int s_reg2);
+ void MarkDef(RegLocation rl, LIR *start, LIR *finish);
+ void MarkDefWide(RegLocation rl, LIR *start, LIR *finish);
+ RegLocation WideToNarrow(RegLocation rl);
+ void ResetDefLoc(RegLocation rl);
+ void ResetDefLocWide(RegLocation rl);
+ void ResetDefTracking();
+ void ClobberAllRegs();
+ void FlushAllRegsBody(RegisterInfo* info, int num_regs);
+ void FlushAllRegs();
+ bool RegClassMatches(int reg_class, int reg);
+ void MarkLive(int reg, int s_reg);
+ void MarkTemp(int reg);
+ void UnmarkTemp(int reg);
+ void MarkPair(int low_reg, int high_reg);
+ void MarkClean(RegLocation loc);
+ void MarkDirty(RegLocation loc);
+ void MarkInUse(int reg);
+ void CopyRegInfo(int new_reg, int old_reg);
+ bool CheckCorePoolSanity();
+ RegLocation UpdateLoc(RegLocation loc);
+ RegLocation UpdateLocWide(RegLocation loc);
+ RegLocation UpdateRawLoc(RegLocation loc);
+ RegLocation EvalLocWide(RegLocation loc, int reg_class, bool update);
+ RegLocation EvalLoc(RegLocation loc, int reg_class, bool update);
+ void CountRefs(RefCounts* core_counts, RefCounts* fp_counts);
+ void DumpCounts(const RefCounts* arr, int size, const char* msg);
+ void DoPromotion();
+ int VRegOffset(int v_reg);
+ int SRegOffset(int s_reg);
+ RegLocation GetReturnWide(bool is_double);
+ RegLocation GetReturn(bool is_float);
+
+ // Shared by all targets - implemented in gen_common.cc.
+ bool HandleEasyDivide(Instruction::Code dalvik_opcode,
+ RegLocation rl_src, RegLocation rl_dest, int lit);
+ bool HandleEasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit);
+ void HandleSuspendLaunchPads();
+ void HandleIntrinsicLaunchPads();
+ void HandleThrowLaunchPads();
+ void GenBarrier();
+ LIR* GenCheck(ConditionCode c_code, ThrowKind kind);
+ LIR* GenImmedCheck(ConditionCode c_code, int reg, int imm_val,
+ ThrowKind kind);
+ LIR* GenNullCheck(int s_reg, int m_reg, int opt_flags);
+ LIR* GenRegRegCheck(ConditionCode c_code, int reg1, int reg2,
+ ThrowKind kind);
+ void GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1,
+ RegLocation rl_src2, LIR* taken, LIR* fall_through);
+ void GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src,
+ LIR* taken, LIR* fall_through);
+ void GenIntToLong(RegLocation rl_dest, RegLocation rl_src);
+ void GenIntNarrowing(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src);
+ void GenNewArray(uint32_t type_idx, RegLocation rl_dest,
+ RegLocation rl_src);
+ void GenFilledNewArray(CallInfo* info);
+ void GenSput(uint32_t field_idx, RegLocation rl_src,
+ bool is_long_or_double, bool is_object);
+ void GenSget(uint32_t field_idx, RegLocation rl_dest,
+ bool is_long_or_double, bool is_object);
+ void GenIGet(uint32_t field_idx, int opt_flags, OpSize size,
+ RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double, bool is_object);
+ void GenIPut(uint32_t field_idx, int opt_flags, OpSize size,
+ RegLocation rl_src, RegLocation rl_obj, bool is_long_or_double, bool is_object);
+ void GenConstClass(uint32_t type_idx, RegLocation rl_dest);
+ void GenConstString(uint32_t string_idx, RegLocation rl_dest);
+ void GenNewInstance(uint32_t type_idx, RegLocation rl_dest);
+ void GenThrow(RegLocation rl_src);
+ void GenInstanceof(uint32_t type_idx, RegLocation rl_dest,
+ RegLocation rl_src);
+ void GenCheckCast(uint32_t insn_idx, uint32_t type_idx,
+ RegLocation rl_src);
+ void GenLong3Addr(OpKind first_op, OpKind second_op, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_shift);
+ void GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenArithOpIntLit(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src, int lit);
+ void GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenConversionCall(int func_offset, RegLocation rl_dest,
+ RegLocation rl_src);
+ void GenSuspendTest(int opt_flags);
+ void GenSuspendTestAndBranch(int opt_flags, LIR* target);
+
+ // Shared by all targets - implemented in gen_invoke.cc.
+ int CallHelperSetup(int helper_offset);
+ LIR* CallHelper(int r_tgt, int helper_offset, bool safepoint_pc);
+ void CallRuntimeHelperImm(int helper_offset, int arg0, bool safepoint_pc);
+ void CallRuntimeHelperReg(int helper_offset, int arg0, bool safepoint_pc);
+ void CallRuntimeHelperRegLocation(int helper_offset, RegLocation arg0,
+ bool safepoint_pc);
+ void CallRuntimeHelperImmImm(int helper_offset, int arg0, int arg1,
+ bool safepoint_pc);
+ void CallRuntimeHelperImmRegLocation(int helper_offset, int arg0,
+ RegLocation arg1, bool safepoint_pc);
+ void CallRuntimeHelperRegLocationImm(int helper_offset, RegLocation arg0,
+ int arg1, bool safepoint_pc);
+ void CallRuntimeHelperImmReg(int helper_offset, int arg0, int arg1,
+ bool safepoint_pc);
+ void CallRuntimeHelperRegImm(int helper_offset, int arg0, int arg1,
+ bool safepoint_pc);
+ void CallRuntimeHelperImmMethod(int helper_offset, int arg0,
+ bool safepoint_pc);
+ void CallRuntimeHelperRegLocationRegLocation(int helper_offset,
+ RegLocation arg0, RegLocation arg1,
+ bool safepoint_pc);
+ void CallRuntimeHelperRegReg(int helper_offset, int arg0, int arg1,
+ bool safepoint_pc);
+ void CallRuntimeHelperRegRegImm(int helper_offset, int arg0, int arg1,
+ int arg2, bool safepoint_pc);
+ void CallRuntimeHelperImmMethodRegLocation(int helper_offset, int arg0,
+ RegLocation arg2, bool safepoint_pc);
+ void CallRuntimeHelperImmMethodImm(int helper_offset, int arg0, int arg2,
+ bool safepoint_pc);
+ void CallRuntimeHelperImmRegLocationRegLocation(int helper_offset,
+ int arg0, RegLocation arg1, RegLocation arg2,
+ bool safepoint_pc);
+ void GenInvoke(CallInfo* info);
+ void FlushIns(RegLocation* ArgLocs, RegLocation rl_method);
+ int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
+ NextCallInsn next_call_insn,
+ const MethodReference& target_method,
+ uint32_t vtable_idx,
+ uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
+ bool skip_this);
+ int GenDalvikArgsRange(CallInfo* info, int call_state, LIR** pcrLabel,
+ NextCallInsn next_call_insn,
+ const MethodReference& target_method,
+ uint32_t vtable_idx,
+ uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
+ bool skip_this);
+ RegLocation InlineTarget(CallInfo* info);
+ RegLocation InlineTargetWide(CallInfo* info);
+
+ bool GenInlinedCharAt(CallInfo* info);
+ bool GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty);
+ bool GenInlinedAbsInt(CallInfo* info);
+ bool GenInlinedAbsLong(CallInfo* info);
+ bool GenInlinedFloatCvt(CallInfo* info);
+ bool GenInlinedDoubleCvt(CallInfo* info);
+ bool GenInlinedIndexOf(CallInfo* info, bool zero_based);
+ bool GenInlinedStringCompareTo(CallInfo* info);
+ bool GenInlinedCurrentThread(CallInfo* info);
+ bool GenInlinedUnsafeGet(CallInfo* info, bool is_long, bool is_volatile);
+ bool GenInlinedUnsafePut(CallInfo* info, bool is_long, bool is_object,
+ bool is_volatile, bool is_ordered);
+ bool GenIntrinsic(CallInfo* info);
+ int LoadArgRegs(CallInfo* info, int call_state,
+ NextCallInsn next_call_insn,
+ const MethodReference& target_method,
+ uint32_t vtable_idx,
+ uintptr_t direct_code, uintptr_t direct_method, InvokeType type,
+ bool skip_this);
+
+ // Shared by all targets - implemented in gen_loadstore.cc.
+ RegLocation LoadCurrMethod();
+ void LoadCurrMethodDirect(int r_tgt);
+ LIR* LoadConstant(int r_dest, int value);
+ LIR* LoadWordDisp(int rBase, int displacement, int r_dest);
+ RegLocation LoadValue(RegLocation rl_src, RegisterClass op_kind);
+ RegLocation LoadValueWide(RegLocation rl_src, RegisterClass op_kind);
+ void LoadValueDirect(RegLocation rl_src, int r_dest);
+ void LoadValueDirectFixed(RegLocation rl_src, int r_dest);
+ void LoadValueDirectWide(RegLocation rl_src, int reg_lo, int reg_hi);
+ void LoadValueDirectWideFixed(RegLocation rl_src, int reg_lo, int reg_hi);
+ LIR* StoreWordDisp(int rBase, int displacement, int r_src);
+ void StoreValue(RegLocation rl_dest, RegLocation rl_src);
+ void StoreValueWide(RegLocation rl_dest, RegLocation rl_src);
+
+ // Shared by all targets - implemented in mir_to_lir.cc.
+ void CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list);
+ void HandleExtendedMethodMIR(BasicBlock* bb, MIR* mir);
+ bool MethodBlockCodeGen(BasicBlock* bb);
+ void SpecialMIR2LIR(SpecialCaseHandler special_case);
+ void MethodMIR2LIR();
+
+
+
+ // Required for target - codegen helpers.
+ virtual bool SmallLiteralDivide(Instruction::Code dalvik_opcode,
+ RegLocation rl_src, RegLocation rl_dest, int lit) = 0;
+ virtual int LoadHelper(int offset) = 0;
+ virtual LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg) = 0;
+ virtual LIR* LoadBaseDispWide(int rBase, int displacement, int r_dest_lo, int r_dest_hi,
+ int s_reg) = 0;
+ virtual LIR* LoadBaseIndexed(int rBase, int r_index, int r_dest, int scale, OpSize size) = 0;
+ virtual LIR* LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
+ int r_dest, int r_dest_hi, OpSize size, int s_reg) = 0;
+ virtual LIR* LoadConstantNoClobber(int r_dest, int value) = 0;
+ virtual LIR* LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value) = 0;
+ virtual LIR* StoreBaseDisp(int rBase, int displacement, int r_src, OpSize size) = 0;
+ virtual LIR* StoreBaseDispWide(int rBase, int displacement, int r_src_lo, int r_src_hi) = 0;
+ virtual LIR* StoreBaseIndexed(int rBase, int r_index, int r_src, int scale, OpSize size) = 0;
+ virtual LIR* StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
+ int r_src, int r_src_hi, OpSize size, int s_reg) = 0;
+ virtual void MarkGCCard(int val_reg, int tgt_addr_reg) = 0;
+
+ // Required for target - register utilities.
+ virtual bool IsFpReg(int reg) = 0;
+ virtual bool SameRegType(int reg1, int reg2) = 0;
+ virtual int AllocTypedTemp(bool fp_hint, int reg_class) = 0;
+ virtual int AllocTypedTempPair(bool fp_hint, int reg_class) = 0;
+ virtual int S2d(int low_reg, int high_reg) = 0;
+ virtual int TargetReg(SpecialTargetRegister reg) = 0;
+ virtual RegisterInfo* GetRegInfo(int reg) = 0;
+ virtual RegLocation GetReturnAlt() = 0;
+ virtual RegLocation GetReturnWideAlt() = 0;
+ virtual RegLocation LocCReturn() = 0;
+ virtual RegLocation LocCReturnDouble() = 0;
+ virtual RegLocation LocCReturnFloat() = 0;
+ virtual RegLocation LocCReturnWide() = 0;
+ virtual uint32_t FpRegMask() = 0;
+ virtual uint64_t GetRegMaskCommon(int reg) = 0;
+ virtual void AdjustSpillMask() = 0;
+ virtual void ClobberCalleeSave() = 0;
+ virtual void FlushReg(int reg) = 0;
+ virtual void FlushRegWide(int reg1, int reg2) = 0;
+ virtual void FreeCallTemps() = 0;
+ virtual void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free) = 0;
+ virtual void LockCallTemps() = 0;
+ virtual void MarkPreservedSingle(int v_reg, int reg) = 0;
+ virtual void CompilerInitializeRegAlloc() = 0;
+
+ // Required for target - miscellaneous.
+ virtual AssemblerStatus AssembleInstructions(uintptr_t start_addr) = 0;
+ virtual void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix) = 0;
+ virtual void SetupTargetResourceMasks(LIR* lir) = 0;
+ virtual const char* GetTargetInstFmt(int opcode) = 0;
+ virtual const char* GetTargetInstName(int opcode) = 0;
+ virtual std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr) = 0;
+ virtual uint64_t GetPCUseDefEncoding() = 0;
+ virtual uint64_t GetTargetInstFlags(int opcode) = 0;
+ virtual int GetInsnSize(LIR* lir) = 0;
+ virtual bool IsUnconditionalBranch(LIR* lir) = 0;
+
+ // Required for target - Dalvik-level generators.
+ virtual void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2) = 0;
+ virtual void GenMulLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2) = 0;
+ virtual void GenAddLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2) = 0;
+ virtual void GenAndLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2) = 0;
+ virtual void GenArithOpDouble(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2) = 0;
+ virtual void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2) = 0;
+ virtual void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2) = 0;
+ virtual void GenConversion(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src) = 0;
+ virtual bool GenInlinedCas32(CallInfo* info, bool need_write_barrier) = 0;
+ virtual bool GenInlinedMinMaxInt(CallInfo* info, bool is_min) = 0;
+ virtual bool GenInlinedSqrt(CallInfo* info) = 0;
+ virtual void GenNegLong(RegLocation rl_dest, RegLocation rl_src) = 0;
+ virtual void GenOrLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2) = 0;
+ virtual void GenSubLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2) = 0;
+ virtual void GenXorLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2) = 0;
+ virtual LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base,
+ int offset, ThrowKind kind) = 0;
+ virtual RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi,
+ bool is_div) = 0;
+ virtual RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit,
+ bool is_div) = 0;
+ virtual void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2) = 0;
+ virtual void GenDivZeroCheck(int reg_lo, int reg_hi) = 0;
+ virtual void GenEntrySequence(RegLocation* ArgLocs,
+ RegLocation rl_method) = 0;
+ virtual void GenExitSequence() = 0;
+ virtual void GenFillArrayData(uint32_t table_offset,
+ RegLocation rl_src) = 0;
+ virtual void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
+ bool is_double) = 0;
+ virtual void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) = 0;
+ virtual void GenSelect(BasicBlock* bb, MIR* mir) = 0;
+ virtual void GenMemBarrier(MemBarrierKind barrier_kind) = 0;
+ virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src) = 0;
+ virtual void GenMonitorExit(int opt_flags, RegLocation rl_src) = 0;
+ virtual void GenMoveException(RegLocation rl_dest) = 0;
+ virtual void GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
+ RegLocation rl_result, int lit, int first_bit,
+ int second_bit) = 0;
+ virtual void GenNegDouble(RegLocation rl_dest, RegLocation rl_src) = 0;
+ virtual void GenNegFloat(RegLocation rl_dest, RegLocation rl_src) = 0;
+ virtual void GenPackedSwitch(MIR* mir, uint32_t table_offset,
+ RegLocation rl_src) = 0;
+ virtual void GenSparseSwitch(MIR* mir, uint32_t table_offset,
+ RegLocation rl_src) = 0;
+ virtual void GenSpecialCase(BasicBlock* bb, MIR* mir,
+ SpecialCaseHandler special_case) = 0;
+ virtual void GenArrayObjPut(int opt_flags, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale) = 0;
+ virtual void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_dest, int scale) = 0;
+ virtual void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale) = 0;
+ virtual void GenShiftImmOpLong(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_shift) = 0;
+
+ // Required for target - single operation generators.
+ virtual LIR* OpUnconditionalBranch(LIR* target) = 0;
+ virtual LIR* OpCmpBranch(ConditionCode cond, int src1, int src2,
+ LIR* target) = 0;
+ virtual LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value,
+ LIR* target) = 0;
+ virtual LIR* OpCondBranch(ConditionCode cc, LIR* target) = 0;
+ virtual LIR* OpDecAndBranch(ConditionCode c_code, int reg,
+ LIR* target) = 0;
+ virtual LIR* OpFpRegCopy(int r_dest, int r_src) = 0;
+ virtual LIR* OpIT(ConditionCode cond, const char* guide) = 0;
+ virtual LIR* OpMem(OpKind op, int rBase, int disp) = 0;
+ virtual LIR* OpPcRelLoad(int reg, LIR* target) = 0;
+ virtual LIR* OpReg(OpKind op, int r_dest_src) = 0;
+ virtual LIR* OpRegCopy(int r_dest, int r_src) = 0;
+ virtual LIR* OpRegCopyNoInsert(int r_dest, int r_src) = 0;
+ virtual LIR* OpRegImm(OpKind op, int r_dest_src1, int value) = 0;
+ virtual LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset) = 0;
+ virtual LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2) = 0;
+ virtual LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) = 0;
+ virtual LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1,
+ int r_src2) = 0;
+ virtual LIR* OpTestSuspend(LIR* target) = 0;
+ virtual LIR* OpThreadMem(OpKind op, int thread_offset) = 0;
+ virtual LIR* OpVldm(int rBase, int count) = 0;
+ virtual LIR* OpVstm(int rBase, int count) = 0;
+ virtual void OpLea(int rBase, int reg1, int reg2, int scale,
+ int offset) = 0;
+ virtual void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo,
+ int src_hi) = 0;
+ virtual void OpTlsCmp(int offset, int val) = 0;
+ virtual bool InexpensiveConstantInt(int32_t value) = 0;
+ virtual bool InexpensiveConstantFloat(int32_t value) = 0;
+ virtual bool InexpensiveConstantLong(int64_t value) = 0;
+ virtual bool InexpensiveConstantDouble(int64_t value) = 0;
+
+ // Temp workaround
+ void Workaround7250540(RegLocation rl_dest, int value);
+
+ protected:
+ Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
+
+ CompilationUnit* GetCompilationUnit() {
+ return cu_;
+ }
+
+ private:
+ void GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, RegLocation rl_dest,
+ RegLocation rl_src);
+ void GenInstanceofCallingHelper(bool needs_access_check, bool type_known_final,
+ bool type_known_abstract, bool use_declaring_class,
+ bool can_assume_type_is_in_dex_cache,
+ uint32_t type_idx, RegLocation rl_dest,
+ RegLocation rl_src);
+
+ void ClobberBody(RegisterInfo* p);
+ void ResetDefBody(RegisterInfo* p) {
+ p->def_start = NULL;
+ p->def_end = NULL;
+ }
+
+ public:
+ // TODO: add accessors for these.
+ LIR* literal_list_; // Constants.
+ LIR* method_literal_list_; // Method literals requiring patching.
+ LIR* code_literal_list_; // Code literals requiring patching.
+
+ protected:
+ CompilationUnit* const cu_;
+ MIRGraph* const mir_graph_;
+ GrowableArray<SwitchTable*> switch_tables_;
+ GrowableArray<FillArrayData*> fill_array_data_;
+ GrowableArray<LIR*> throw_launchpads_;
+ GrowableArray<LIR*> suspend_launchpads_;
+ GrowableArray<LIR*> intrinsic_launchpads_;
+ SafeMap<unsigned int, LIR*> boundary_map_; // boundary lookup cache.
+ /*
+ * Holds mapping from native PC to dex PC for safepoints where we may deoptimize.
+ * Native PC is on the return address of the safepointed operation. Dex PC is for
+ * the instruction being executed at the safepoint.
+ */
+ std::vector<uint32_t> pc2dex_mapping_table_;
+ /*
+ * Holds mapping from Dex PC to native PC for catch entry points. Native PC and Dex PC
+ * immediately preceed the instruction.
+ */
+ std::vector<uint32_t> dex2pc_mapping_table_;
+ int data_offset_; // starting offset of literal pool.
+ int total_size_; // header + code size.
+ LIR* block_label_list_;
+ PromotionMap* promotion_map_;
+ /*
+ * TODO: The code generation utilities don't have a built-in
+ * mechanism to propagate the original Dalvik opcode address to the
+ * associated generated instructions. For the trace compiler, this wasn't
+ * necessary because the interpreter handled all throws and debugging
+ * requests. For now we'll handle this by placing the Dalvik offset
+ * in the CompilationUnit struct before codegen for each instruction.
+ * The low-level LIR creation utilites will pull it from here. Rework this.
+ */
+ int current_dalvik_offset_;
+ RegisterPool* reg_pool_;
+ /*
+ * Sanity checking for the register temp tracking. The same ssa
+ * name should never be associated with one temp register per
+ * instruction compilation.
+ */
+ int live_sreg_;
+ CodeBuffer code_buffer_;
+ std::vector<uint32_t> combined_mapping_table_;
+ std::vector<uint32_t> core_vmap_table_;
+ std::vector<uint32_t> fp_vmap_table_;
+ std::vector<uint8_t> native_gc_map_;
+ int num_core_spills_;
+ int num_fp_spills_;
+ int frame_size_;
+ unsigned int core_spill_mask_;
+ unsigned int fp_spill_mask_;
+ LIR* first_lir_insn_;
+ LIR* last_lir_insn_;
+}; // Class Mir2Lir
+
+} // namespace art
+
+#endif //ART_SRC_COMPILER_DEX_QUICK_MIR_TO_LIR_H_
diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc
new file mode 100644
index 0000000..8f43542
--- /dev/null
+++ b/compiler/dex/quick/ralloc_util.cc
@@ -0,0 +1,1237 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/* This file contains register alloction support. */
+
+#include "dex/compiler_ir.h"
+#include "dex/compiler_internals.h"
+#include "mir_to_lir-inl.h"
+
+namespace art {
+
+/*
+ * Free all allocated temps in the temp pools. Note that this does
+ * not affect the "liveness" of a temp register, which will stay
+ * live until it is either explicitly killed or reallocated.
+ */
+void Mir2Lir::ResetRegPool()
+{
+ int i;
+ for (i=0; i < reg_pool_->num_core_regs; i++) {
+ if (reg_pool_->core_regs[i].is_temp)
+ reg_pool_->core_regs[i].in_use = false;
+ }
+ for (i=0; i < reg_pool_->num_fp_regs; i++) {
+ if (reg_pool_->FPRegs[i].is_temp)
+ reg_pool_->FPRegs[i].in_use = false;
+ }
+ // Reset temp tracking sanity check.
+ if (kIsDebugBuild) {
+ live_sreg_ = INVALID_SREG;
+ }
+}
+
+ /*
+ * Set up temp & preserved register pools specialized by target.
+ * Note: num_regs may be zero.
+ */
+void Mir2Lir::CompilerInitPool(RegisterInfo* regs, int* reg_nums, int num)
+{
+ int i;
+ for (i=0; i < num; i++) {
+ regs[i].reg = reg_nums[i];
+ regs[i].in_use = false;
+ regs[i].is_temp = false;
+ regs[i].pair = false;
+ regs[i].live = false;
+ regs[i].dirty = false;
+ regs[i].s_reg = INVALID_SREG;
+ }
+}
+
+void Mir2Lir::DumpRegPool(RegisterInfo* p, int num_regs)
+{
+ LOG(INFO) << "================================================";
+ for (int i = 0; i < num_regs; i++) {
+ LOG(INFO) << StringPrintf(
+ "R[%d]: T:%d, U:%d, P:%d, p:%d, LV:%d, D:%d, SR:%d, ST:%x, EN:%x",
+ p[i].reg, p[i].is_temp, p[i].in_use, p[i].pair, p[i].partner,
+ p[i].live, p[i].dirty, p[i].s_reg, reinterpret_cast<uintptr_t>(p[i].def_start),
+ reinterpret_cast<uintptr_t>(p[i].def_end));
+ }
+ LOG(INFO) << "================================================";
+}
+
+void Mir2Lir::DumpCoreRegPool()
+{
+ DumpRegPool(reg_pool_->core_regs, reg_pool_->num_core_regs);
+}
+
+void Mir2Lir::DumpFpRegPool()
+{
+ DumpRegPool(reg_pool_->FPRegs, reg_pool_->num_fp_regs);
+}
+
+void Mir2Lir::ClobberSRegBody(RegisterInfo* p, int num_regs, int s_reg)
+{
+ int i;
+ for (i=0; i< num_regs; i++) {
+ if (p[i].s_reg == s_reg) {
+ if (p[i].is_temp) {
+ p[i].live = false;
+ }
+ p[i].def_start = NULL;
+ p[i].def_end = NULL;
+ }
+ }
+}
+
+/*
+ * Break the association between a Dalvik vreg and a physical temp register of either register
+ * class.
+ * TODO: Ideally, the public version of this code should not exist. Besides its local usage
+ * in the register utilities, is is also used by code gen routines to work around a deficiency in
+ * local register allocation, which fails to distinguish between the "in" and "out" identities
+ * of Dalvik vregs. This can result in useless register copies when the same Dalvik vreg
+ * is used both as the source and destination register of an operation in which the type
+ * changes (for example: INT_TO_FLOAT v1, v1). Revisit when improved register allocation is
+ * addressed.
+ */
+void Mir2Lir::ClobberSReg(int s_reg)
+{
+ /* Reset live temp tracking sanity checker */
+ if (kIsDebugBuild) {
+ if (s_reg == live_sreg_) {
+ live_sreg_ = INVALID_SREG;
+ }
+ }
+ ClobberSRegBody(reg_pool_->core_regs, reg_pool_->num_core_regs, s_reg);
+ ClobberSRegBody(reg_pool_->FPRegs, reg_pool_->num_fp_regs, s_reg);
+}
+
+/*
+ * SSA names associated with the initial definitions of Dalvik
+ * registers are the same as the Dalvik register number (and
+ * thus take the same position in the promotion_map. However,
+ * the special Method* and compiler temp resisters use negative
+ * v_reg numbers to distinguish them and can have an arbitrary
+ * ssa name (above the last original Dalvik register). This function
+ * maps SSA names to positions in the promotion_map array.
+ */
+int Mir2Lir::SRegToPMap(int s_reg)
+{
+ DCHECK_LT(s_reg, mir_graph_->GetNumSSARegs());
+ DCHECK_GE(s_reg, 0);
+ int v_reg = mir_graph_->SRegToVReg(s_reg);
+ if (v_reg >= 0) {
+ DCHECK_LT(v_reg, cu_->num_dalvik_registers);
+ return v_reg;
+ } else {
+ int pos = std::abs(v_reg) - std::abs(SSA_METHOD_BASEREG);
+ DCHECK_LE(pos, cu_->num_compiler_temps);
+ return cu_->num_dalvik_registers + pos;
+ }
+}
+
+void Mir2Lir::RecordCorePromotion(int reg, int s_reg)
+{
+ int p_map_idx = SRegToPMap(s_reg);
+ int v_reg = mir_graph_->SRegToVReg(s_reg);
+ GetRegInfo(reg)->in_use = true;
+ core_spill_mask_ |= (1 << reg);
+ // Include reg for later sort
+ core_vmap_table_.push_back(reg << VREG_NUM_WIDTH | (v_reg & ((1 << VREG_NUM_WIDTH) - 1)));
+ num_core_spills_++;
+ promotion_map_[p_map_idx].core_location = kLocPhysReg;
+ promotion_map_[p_map_idx].core_reg = reg;
+}
+
+/* Reserve a callee-save register. Return -1 if none available */
+int Mir2Lir::AllocPreservedCoreReg(int s_reg)
+{
+ int res = -1;
+ RegisterInfo* core_regs = reg_pool_->core_regs;
+ for (int i = 0; i < reg_pool_->num_core_regs; i++) {
+ if (!core_regs[i].is_temp && !core_regs[i].in_use) {
+ res = core_regs[i].reg;
+ RecordCorePromotion(res, s_reg);
+ break;
+ }
+ }
+ return res;
+}
+
+void Mir2Lir::RecordFpPromotion(int reg, int s_reg)
+{
+ int p_map_idx = SRegToPMap(s_reg);
+ int v_reg = mir_graph_->SRegToVReg(s_reg);
+ GetRegInfo(reg)->in_use = true;
+ MarkPreservedSingle(v_reg, reg);
+ promotion_map_[p_map_idx].fp_location = kLocPhysReg;
+ promotion_map_[p_map_idx].FpReg = reg;
+}
+
+/*
+ * Reserve a callee-save fp single register. Try to fullfill request for
+ * even/odd allocation, but go ahead and allocate anything if not
+ * available. If nothing's available, return -1.
+ */
+int Mir2Lir::AllocPreservedSingle(int s_reg, bool even)
+{
+ int res = -1;
+ RegisterInfo* FPRegs = reg_pool_->FPRegs;
+ for (int i = 0; i < reg_pool_->num_fp_regs; i++) {
+ if (!FPRegs[i].is_temp && !FPRegs[i].in_use &&
+ ((FPRegs[i].reg & 0x1) == 0) == even) {
+ res = FPRegs[i].reg;
+ RecordFpPromotion(res, s_reg);
+ break;
+ }
+ }
+ return res;
+}
+
+/*
+ * Somewhat messy code here. We want to allocate a pair of contiguous
+ * physical single-precision floating point registers starting with
+ * an even numbered reg. It is possible that the paired s_reg (s_reg+1)
+ * has already been allocated - try to fit if possible. Fail to
+ * allocate if we can't meet the requirements for the pair of
+ * s_reg<=sX[even] & (s_reg+1)<= sX+1.
+ */
+int Mir2Lir::AllocPreservedDouble(int s_reg)
+{
+ int res = -1; // Assume failure
+ int v_reg = mir_graph_->SRegToVReg(s_reg);
+ int p_map_idx = SRegToPMap(s_reg);
+ if (promotion_map_[p_map_idx+1].fp_location == kLocPhysReg) {
+ // Upper reg is already allocated. Can we fit?
+ int high_reg = promotion_map_[p_map_idx+1].FpReg;
+ if ((high_reg & 1) == 0) {
+ // High reg is even - fail.
+ return res;
+ }
+ // Is the low reg of the pair free?
+ RegisterInfo* p = GetRegInfo(high_reg-1);
+ if (p->in_use || p->is_temp) {
+ // Already allocated or not preserved - fail.
+ return res;
+ }
+ // OK - good to go.
+ res = p->reg;
+ p->in_use = true;
+ DCHECK_EQ((res & 1), 0);
+ MarkPreservedSingle(v_reg, res);
+ } else {
+ RegisterInfo* FPRegs = reg_pool_->FPRegs;
+ for (int i = 0; i < reg_pool_->num_fp_regs; i++) {
+ if (!FPRegs[i].is_temp && !FPRegs[i].in_use &&
+ ((FPRegs[i].reg & 0x1) == 0x0) &&
+ !FPRegs[i+1].is_temp && !FPRegs[i+1].in_use &&
+ ((FPRegs[i+1].reg & 0x1) == 0x1) &&
+ (FPRegs[i].reg + 1) == FPRegs[i+1].reg) {
+ res = FPRegs[i].reg;
+ FPRegs[i].in_use = true;
+ MarkPreservedSingle(v_reg, res);
+ FPRegs[i+1].in_use = true;
+ DCHECK_EQ(res + 1, FPRegs[i+1].reg);
+ MarkPreservedSingle(v_reg+1, res+1);
+ break;
+ }
+ }
+ }
+ if (res != -1) {
+ promotion_map_[p_map_idx].fp_location = kLocPhysReg;
+ promotion_map_[p_map_idx].FpReg = res;
+ promotion_map_[p_map_idx+1].fp_location = kLocPhysReg;
+ promotion_map_[p_map_idx+1].FpReg = res + 1;
+ }
+ return res;
+}
+
+
+/*
+ * Reserve a callee-save fp register. If this register can be used
+ * as the first of a double, attempt to allocate an even pair of fp
+ * single regs (but if can't still attempt to allocate a single, preferring
+ * first to allocate an odd register.
+ */
+int Mir2Lir::AllocPreservedFPReg(int s_reg, bool double_start)
+{
+ int res = -1;
+ if (double_start) {
+ res = AllocPreservedDouble(s_reg);
+ }
+ if (res == -1) {
+ res = AllocPreservedSingle(s_reg, false /* try odd # */);
+ }
+ if (res == -1)
+ res = AllocPreservedSingle(s_reg, true /* try even # */);
+ return res;
+}
+
+int Mir2Lir::AllocTempBody(RegisterInfo* p, int num_regs, int* next_temp,
+ bool required)
+{
+ int i;
+ int next = *next_temp;
+ for (i=0; i< num_regs; i++) {
+ if (next >= num_regs)
+ next = 0;
+ if (p[next].is_temp && !p[next].in_use && !p[next].live) {
+ Clobber(p[next].reg);
+ p[next].in_use = true;
+ p[next].pair = false;
+ *next_temp = next + 1;
+ return p[next].reg;
+ }
+ next++;
+ }
+ next = *next_temp;
+ for (i=0; i< num_regs; i++) {
+ if (next >= num_regs)
+ next = 0;
+ if (p[next].is_temp && !p[next].in_use) {
+ Clobber(p[next].reg);
+ p[next].in_use = true;
+ p[next].pair = false;
+ *next_temp = next + 1;
+ return p[next].reg;
+ }
+ next++;
+ }
+ if (required) {
+ CodegenDump();
+ DumpRegPool(reg_pool_->core_regs,
+ reg_pool_->num_core_regs);
+ LOG(FATAL) << "No free temp registers";
+ }
+ return -1; // No register available
+}
+
+//REDO: too many assumptions.
+int Mir2Lir::AllocTempDouble()
+{
+ RegisterInfo* p = reg_pool_->FPRegs;
+ int num_regs = reg_pool_->num_fp_regs;
+ /* Start looking at an even reg */
+ int next = reg_pool_->next_fp_reg & ~0x1;
+
+ // First try to avoid allocating live registers
+ for (int i=0; i < num_regs; i+=2) {
+ if (next >= num_regs)
+ next = 0;
+ if ((p[next].is_temp && !p[next].in_use && !p[next].live) &&
+ (p[next+1].is_temp && !p[next+1].in_use && !p[next+1].live)) {
+ Clobber(p[next].reg);
+ Clobber(p[next+1].reg);
+ p[next].in_use = true;
+ p[next+1].in_use = true;
+ DCHECK_EQ((p[next].reg+1), p[next+1].reg);
+ DCHECK_EQ((p[next].reg & 0x1), 0);
+ reg_pool_->next_fp_reg = next + 2;
+ if (reg_pool_->next_fp_reg >= num_regs) {
+ reg_pool_->next_fp_reg = 0;
+ }
+ return p[next].reg;
+ }
+ next += 2;
+ }
+ next = reg_pool_->next_fp_reg & ~0x1;
+
+ // No choice - find a pair and kill it.
+ for (int i=0; i < num_regs; i+=2) {
+ if (next >= num_regs)
+ next = 0;
+ if (p[next].is_temp && !p[next].in_use && p[next+1].is_temp &&
+ !p[next+1].in_use) {
+ Clobber(p[next].reg);
+ Clobber(p[next+1].reg);
+ p[next].in_use = true;
+ p[next+1].in_use = true;
+ DCHECK_EQ((p[next].reg+1), p[next+1].reg);
+ DCHECK_EQ((p[next].reg & 0x1), 0);
+ reg_pool_->next_fp_reg = next + 2;
+ if (reg_pool_->next_fp_reg >= num_regs) {
+ reg_pool_->next_fp_reg = 0;
+ }
+ return p[next].reg;
+ }
+ next += 2;
+ }
+ LOG(FATAL) << "No free temp registers (pair)";
+ return -1;
+}
+
+/* Return a temp if one is available, -1 otherwise */
+int Mir2Lir::AllocFreeTemp()
+{
+ return AllocTempBody(reg_pool_->core_regs,
+ reg_pool_->num_core_regs,
+ ®_pool_->next_core_reg, true);
+}
+
+int Mir2Lir::AllocTemp()
+{
+ return AllocTempBody(reg_pool_->core_regs,
+ reg_pool_->num_core_regs,
+ ®_pool_->next_core_reg, true);
+}
+
+int Mir2Lir::AllocTempFloat()
+{
+ return AllocTempBody(reg_pool_->FPRegs,
+ reg_pool_->num_fp_regs,
+ ®_pool_->next_fp_reg, true);
+}
+
+Mir2Lir::RegisterInfo* Mir2Lir::AllocLiveBody(RegisterInfo* p, int num_regs, int s_reg)
+{
+ int i;
+ if (s_reg == -1)
+ return NULL;
+ for (i=0; i < num_regs; i++) {
+ if (p[i].live && (p[i].s_reg == s_reg)) {
+ if (p[i].is_temp)
+ p[i].in_use = true;
+ return &p[i];
+ }
+ }
+ return NULL;
+}
+
+Mir2Lir::RegisterInfo* Mir2Lir::AllocLive(int s_reg, int reg_class)
+{
+ RegisterInfo* res = NULL;
+ switch (reg_class) {
+ case kAnyReg:
+ res = AllocLiveBody(reg_pool_->FPRegs,
+ reg_pool_->num_fp_regs, s_reg);
+ if (res)
+ break;
+ /* Intentional fallthrough */
+ case kCoreReg:
+ res = AllocLiveBody(reg_pool_->core_regs,
+ reg_pool_->num_core_regs, s_reg);
+ break;
+ case kFPReg:
+ res = AllocLiveBody(reg_pool_->FPRegs,
+ reg_pool_->num_fp_regs, s_reg);
+ break;
+ default:
+ LOG(FATAL) << "Invalid register type";
+ }
+ return res;
+}
+
+void Mir2Lir::FreeTemp(int reg)
+{
+ RegisterInfo* p = reg_pool_->core_regs;
+ int num_regs = reg_pool_->num_core_regs;
+ int i;
+ for (i=0; i< num_regs; i++) {
+ if (p[i].reg == reg) {
+ if (p[i].is_temp) {
+ p[i].in_use = false;
+ }
+ p[i].pair = false;
+ return;
+ }
+ }
+ p = reg_pool_->FPRegs;
+ num_regs = reg_pool_->num_fp_regs;
+ for (i=0; i< num_regs; i++) {
+ if (p[i].reg == reg) {
+ if (p[i].is_temp) {
+ p[i].in_use = false;
+ }
+ p[i].pair = false;
+ return;
+ }
+ }
+ LOG(FATAL) << "Tried to free a non-existant temp: r" << reg;
+}
+
+Mir2Lir::RegisterInfo* Mir2Lir::IsLive(int reg)
+{
+ RegisterInfo* p = reg_pool_->core_regs;
+ int num_regs = reg_pool_->num_core_regs;
+ int i;
+ for (i=0; i< num_regs; i++) {
+ if (p[i].reg == reg) {
+ return p[i].live ? &p[i] : NULL;
+ }
+ }
+ p = reg_pool_->FPRegs;
+ num_regs = reg_pool_->num_fp_regs;
+ for (i=0; i< num_regs; i++) {
+ if (p[i].reg == reg) {
+ return p[i].live ? &p[i] : NULL;
+ }
+ }
+ return NULL;
+}
+
+Mir2Lir::RegisterInfo* Mir2Lir::IsTemp(int reg)
+{
+ RegisterInfo* p = GetRegInfo(reg);
+ return (p->is_temp) ? p : NULL;
+}
+
+Mir2Lir::RegisterInfo* Mir2Lir::IsPromoted(int reg)
+{
+ RegisterInfo* p = GetRegInfo(reg);
+ return (p->is_temp) ? NULL : p;
+}
+
+bool Mir2Lir::IsDirty(int reg)
+{
+ RegisterInfo* p = GetRegInfo(reg);
+ return p->dirty;
+}
+
+/*
+ * Similar to AllocTemp(), but forces the allocation of a specific
+ * register. No check is made to see if the register was previously
+ * allocated. Use with caution.
+ */
+void Mir2Lir::LockTemp(int reg)
+{
+ RegisterInfo* p = reg_pool_->core_regs;
+ int num_regs = reg_pool_->num_core_regs;
+ int i;
+ for (i=0; i< num_regs; i++) {
+ if (p[i].reg == reg) {
+ DCHECK(p[i].is_temp);
+ p[i].in_use = true;
+ p[i].live = false;
+ return;
+ }
+ }
+ p = reg_pool_->FPRegs;
+ num_regs = reg_pool_->num_fp_regs;
+ for (i=0; i< num_regs; i++) {
+ if (p[i].reg == reg) {
+ DCHECK(p[i].is_temp);
+ p[i].in_use = true;
+ p[i].live = false;
+ return;
+ }
+ }
+ LOG(FATAL) << "Tried to lock a non-existant temp: r" << reg;
+}
+
+void Mir2Lir::ResetDef(int reg)
+{
+ ResetDefBody(GetRegInfo(reg));
+}
+
+void Mir2Lir::NullifyRange(LIR *start, LIR *finish, int s_reg1, int s_reg2)
+{
+ if (start && finish) {
+ LIR *p;
+ DCHECK_EQ(s_reg1, s_reg2);
+ for (p = start; ;p = p->next) {
+ NopLIR(p);
+ if (p == finish)
+ break;
+ }
+ }
+}
+
+/*
+ * Mark the beginning and end LIR of a def sequence. Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+void Mir2Lir::MarkDef(RegLocation rl, LIR *start, LIR *finish)
+{
+ DCHECK(!rl.wide);
+ DCHECK(start && start->next);
+ DCHECK(finish);
+ RegisterInfo* p = GetRegInfo(rl.low_reg);
+ p->def_start = start->next;
+ p->def_end = finish;
+}
+
+/*
+ * Mark the beginning and end LIR of a def sequence. Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+void Mir2Lir::MarkDefWide(RegLocation rl, LIR *start, LIR *finish)
+{
+ DCHECK(rl.wide);
+ DCHECK(start && start->next);
+ DCHECK(finish);
+ RegisterInfo* p = GetRegInfo(rl.low_reg);
+ ResetDef(rl.high_reg); // Only track low of pair
+ p->def_start = start->next;
+ p->def_end = finish;
+}
+
+RegLocation Mir2Lir::WideToNarrow(RegLocation rl)
+{
+ DCHECK(rl.wide);
+ if (rl.location == kLocPhysReg) {
+ RegisterInfo* info_lo = GetRegInfo(rl.low_reg);
+ RegisterInfo* info_hi = GetRegInfo(rl.high_reg);
+ if (info_lo->is_temp) {
+ info_lo->pair = false;
+ info_lo->def_start = NULL;
+ info_lo->def_end = NULL;
+ }
+ if (info_hi->is_temp) {
+ info_hi->pair = false;
+ info_hi->def_start = NULL;
+ info_hi->def_end = NULL;
+ }
+ }
+ rl.wide = false;
+ return rl;
+}
+
+void Mir2Lir::ResetDefLoc(RegLocation rl)
+{
+ DCHECK(!rl.wide);
+ RegisterInfo* p = IsTemp(rl.low_reg);
+ if (p && !(cu_->disable_opt & (1 << kSuppressLoads))) {
+ DCHECK(!p->pair);
+ NullifyRange(p->def_start, p->def_end, p->s_reg, rl.s_reg_low);
+ }
+ ResetDef(rl.low_reg);
+}
+
+void Mir2Lir::ResetDefLocWide(RegLocation rl)
+{
+ DCHECK(rl.wide);
+ RegisterInfo* p_low = IsTemp(rl.low_reg);
+ RegisterInfo* p_high = IsTemp(rl.high_reg);
+ if (p_low && !(cu_->disable_opt & (1 << kSuppressLoads))) {
+ DCHECK(p_low->pair);
+ NullifyRange(p_low->def_start, p_low->def_end, p_low->s_reg, rl.s_reg_low);
+ }
+ if (p_high && !(cu_->disable_opt & (1 << kSuppressLoads))) {
+ DCHECK(p_high->pair);
+ }
+ ResetDef(rl.low_reg);
+ ResetDef(rl.high_reg);
+}
+
+void Mir2Lir::ResetDefTracking()
+{
+ int i;
+ for (i=0; i< reg_pool_->num_core_regs; i++) {
+ ResetDefBody(®_pool_->core_regs[i]);
+ }
+ for (i=0; i< reg_pool_->num_fp_regs; i++) {
+ ResetDefBody(®_pool_->FPRegs[i]);
+ }
+}
+
+void Mir2Lir::ClobberAllRegs()
+{
+ int i;
+ for (i=0; i< reg_pool_->num_core_regs; i++) {
+ ClobberBody(®_pool_->core_regs[i]);
+ }
+ for (i=0; i< reg_pool_->num_fp_regs; i++) {
+ ClobberBody(®_pool_->FPRegs[i]);
+ }
+}
+
+// Make sure nothing is live and dirty
+void Mir2Lir::FlushAllRegsBody(RegisterInfo* info, int num_regs)
+{
+ int i;
+ for (i=0; i < num_regs; i++) {
+ if (info[i].live && info[i].dirty) {
+ if (info[i].pair) {
+ FlushRegWide(info[i].reg, info[i].partner);
+ } else {
+ FlushReg(info[i].reg);
+ }
+ }
+ }
+}
+
+void Mir2Lir::FlushAllRegs()
+{
+ FlushAllRegsBody(reg_pool_->core_regs,
+ reg_pool_->num_core_regs);
+ FlushAllRegsBody(reg_pool_->FPRegs,
+ reg_pool_->num_fp_regs);
+ ClobberAllRegs();
+}
+
+
+//TUNING: rewrite all of this reg stuff. Probably use an attribute table
+bool Mir2Lir::RegClassMatches(int reg_class, int reg)
+{
+ if (reg_class == kAnyReg) {
+ return true;
+ } else if (reg_class == kCoreReg) {
+ return !IsFpReg(reg);
+ } else {
+ return IsFpReg(reg);
+ }
+}
+
+void Mir2Lir::MarkLive(int reg, int s_reg)
+{
+ RegisterInfo* info = GetRegInfo(reg);
+ if ((info->reg == reg) && (info->s_reg == s_reg) && info->live) {
+ return; /* already live */
+ } else if (s_reg != INVALID_SREG) {
+ ClobberSReg(s_reg);
+ if (info->is_temp) {
+ info->live = true;
+ }
+ } else {
+ /* Can't be live if no associated s_reg */
+ DCHECK(info->is_temp);
+ info->live = false;
+ }
+ info->s_reg = s_reg;
+}
+
+void Mir2Lir::MarkTemp(int reg)
+{
+ RegisterInfo* info = GetRegInfo(reg);
+ info->is_temp = true;
+}
+
+void Mir2Lir::UnmarkTemp(int reg)
+{
+ RegisterInfo* info = GetRegInfo(reg);
+ info->is_temp = false;
+}
+
+void Mir2Lir::MarkPair(int low_reg, int high_reg)
+{
+ RegisterInfo* info_lo = GetRegInfo(low_reg);
+ RegisterInfo* info_hi = GetRegInfo(high_reg);
+ info_lo->pair = info_hi->pair = true;
+ info_lo->partner = high_reg;
+ info_hi->partner = low_reg;
+}
+
+void Mir2Lir::MarkClean(RegLocation loc)
+{
+ RegisterInfo* info = GetRegInfo(loc.low_reg);
+ info->dirty = false;
+ if (loc.wide) {
+ info = GetRegInfo(loc.high_reg);
+ info->dirty = false;
+ }
+}
+
+void Mir2Lir::MarkDirty(RegLocation loc)
+{
+ if (loc.home) {
+ // If already home, can't be dirty
+ return;
+ }
+ RegisterInfo* info = GetRegInfo(loc.low_reg);
+ info->dirty = true;
+ if (loc.wide) {
+ info = GetRegInfo(loc.high_reg);
+ info->dirty = true;
+ }
+}
+
+void Mir2Lir::MarkInUse(int reg)
+{
+ RegisterInfo* info = GetRegInfo(reg);
+ info->in_use = true;
+}
+
+void Mir2Lir::CopyRegInfo(int new_reg, int old_reg)
+{
+ RegisterInfo* new_info = GetRegInfo(new_reg);
+ RegisterInfo* old_info = GetRegInfo(old_reg);
+ // Target temp status must not change
+ bool is_temp = new_info->is_temp;
+ *new_info = *old_info;
+ // Restore target's temp status
+ new_info->is_temp = is_temp;
+ new_info->reg = new_reg;
+}
+
+bool Mir2Lir::CheckCorePoolSanity()
+{
+ for (static int i = 0; i < reg_pool_->num_core_regs; i++) {
+ if (reg_pool_->core_regs[i].pair) {
+ static int my_reg = reg_pool_->core_regs[i].reg;
+ static int my_sreg = reg_pool_->core_regs[i].s_reg;
+ static int partner_reg = reg_pool_->core_regs[i].partner;
+ static RegisterInfo* partner = GetRegInfo(partner_reg);
+ DCHECK(partner != NULL);
+ DCHECK(partner->pair);
+ DCHECK_EQ(my_reg, partner->partner);
+ static int partner_sreg = partner->s_reg;
+ if (my_sreg == INVALID_SREG) {
+ DCHECK_EQ(partner_sreg, INVALID_SREG);
+ } else {
+ int diff = my_sreg - partner_sreg;
+ DCHECK((diff == -1) || (diff == 1));
+ }
+ }
+ if (!reg_pool_->core_regs[i].live) {
+ DCHECK(reg_pool_->core_regs[i].def_start == NULL);
+ DCHECK(reg_pool_->core_regs[i].def_end == NULL);
+ }
+ }
+ return true;
+}
+
+/*
+ * Return an updated location record with current in-register status.
+ * If the value lives in live temps, reflect that fact. No code
+ * is generated. If the live value is part of an older pair,
+ * clobber both low and high.
+ * TUNING: clobbering both is a bit heavy-handed, but the alternative
+ * is a bit complex when dealing with FP regs. Examine code to see
+ * if it's worthwhile trying to be more clever here.
+ */
+
+RegLocation Mir2Lir::UpdateLoc(RegLocation loc)
+{
+ DCHECK(!loc.wide);
+ DCHECK(CheckCorePoolSanity());
+ if (loc.location != kLocPhysReg) {
+ DCHECK((loc.location == kLocDalvikFrame) ||
+ (loc.location == kLocCompilerTemp));
+ RegisterInfo* info_lo = AllocLive(loc.s_reg_low, kAnyReg);
+ if (info_lo) {
+ if (info_lo->pair) {
+ Clobber(info_lo->reg);
+ Clobber(info_lo->partner);
+ FreeTemp(info_lo->reg);
+ } else {
+ loc.low_reg = info_lo->reg;
+ loc.location = kLocPhysReg;
+ }
+ }
+ }
+
+ return loc;
+}
+
+/* see comments for update_loc */
+RegLocation Mir2Lir::UpdateLocWide(RegLocation loc)
+{
+ DCHECK(loc.wide);
+ DCHECK(CheckCorePoolSanity());
+ if (loc.location != kLocPhysReg) {
+ DCHECK((loc.location == kLocDalvikFrame) ||
+ (loc.location == kLocCompilerTemp));
+ // Are the dalvik regs already live in physical registers?
+ RegisterInfo* info_lo = AllocLive(loc.s_reg_low, kAnyReg);
+ RegisterInfo* info_hi = AllocLive(GetSRegHi(loc.s_reg_low), kAnyReg);
+ bool match = true;
+ match = match && (info_lo != NULL);
+ match = match && (info_hi != NULL);
+ // Are they both core or both FP?
+ match = match && (IsFpReg(info_lo->reg) == IsFpReg(info_hi->reg));
+ // If a pair of floating point singles, are they properly aligned?
+ if (match && IsFpReg(info_lo->reg)) {
+ match &= ((info_lo->reg & 0x1) == 0);
+ match &= ((info_hi->reg - info_lo->reg) == 1);
+ }
+ // If previously used as a pair, it is the same pair?
+ if (match && (info_lo->pair || info_hi->pair)) {
+ match = (info_lo->pair == info_hi->pair);
+ match &= ((info_lo->reg == info_hi->partner) &&
+ (info_hi->reg == info_lo->partner));
+ }
+ if (match) {
+ // Can reuse - update the register usage info
+ loc.low_reg = info_lo->reg;
+ loc.high_reg = info_hi->reg;
+ loc.location = kLocPhysReg;
+ MarkPair(loc.low_reg, loc.high_reg);
+ DCHECK(!IsFpReg(loc.low_reg) || ((loc.low_reg & 0x1) == 0));
+ return loc;
+ }
+ // Can't easily reuse - clobber and free any overlaps
+ if (info_lo) {
+ Clobber(info_lo->reg);
+ FreeTemp(info_lo->reg);
+ if (info_lo->pair)
+ Clobber(info_lo->partner);
+ }
+ if (info_hi) {
+ Clobber(info_hi->reg);
+ FreeTemp(info_hi->reg);
+ if (info_hi->pair)
+ Clobber(info_hi->partner);
+ }
+ }
+ return loc;
+}
+
+
+/* For use in cases we don't know (or care) width */
+RegLocation Mir2Lir::UpdateRawLoc(RegLocation loc)
+{
+ if (loc.wide)
+ return UpdateLocWide(loc);
+ else
+ return UpdateLoc(loc);
+}
+
+RegLocation Mir2Lir::EvalLocWide(RegLocation loc, int reg_class, bool update)
+{
+ DCHECK(loc.wide);
+ int new_regs;
+ int low_reg;
+ int high_reg;
+
+ loc = UpdateLocWide(loc);
+
+ /* If already in registers, we can assume proper form. Right reg class? */
+ if (loc.location == kLocPhysReg) {
+ DCHECK_EQ(IsFpReg(loc.low_reg), IsFpReg(loc.high_reg));
+ DCHECK(!IsFpReg(loc.low_reg) || ((loc.low_reg & 0x1) == 0));
+ if (!RegClassMatches(reg_class, loc.low_reg)) {
+ /* Wrong register class. Reallocate and copy */
+ new_regs = AllocTypedTempPair(loc.fp, reg_class);
+ low_reg = new_regs & 0xff;
+ high_reg = (new_regs >> 8) & 0xff;
+ OpRegCopyWide(low_reg, high_reg, loc.low_reg, loc.high_reg);
+ CopyRegInfo(low_reg, loc.low_reg);
+ CopyRegInfo(high_reg, loc.high_reg);
+ Clobber(loc.low_reg);
+ Clobber(loc.high_reg);
+ loc.low_reg = low_reg;
+ loc.high_reg = high_reg;
+ MarkPair(loc.low_reg, loc.high_reg);
+ DCHECK(!IsFpReg(loc.low_reg) || ((loc.low_reg & 0x1) == 0));
+ }
+ return loc;
+ }
+
+ DCHECK_NE(loc.s_reg_low, INVALID_SREG);
+ DCHECK_NE(GetSRegHi(loc.s_reg_low), INVALID_SREG);
+
+ new_regs = AllocTypedTempPair(loc.fp, reg_class);
+ loc.low_reg = new_regs & 0xff;
+ loc.high_reg = (new_regs >> 8) & 0xff;
+
+ MarkPair(loc.low_reg, loc.high_reg);
+ if (update) {
+ loc.location = kLocPhysReg;
+ MarkLive(loc.low_reg, loc.s_reg_low);
+ MarkLive(loc.high_reg, GetSRegHi(loc.s_reg_low));
+ }
+ DCHECK(!IsFpReg(loc.low_reg) || ((loc.low_reg & 0x1) == 0));
+ return loc;
+}
+
+RegLocation Mir2Lir::EvalLoc(RegLocation loc, int reg_class, bool update)
+{
+ int new_reg;
+
+ if (loc.wide)
+ return EvalLocWide(loc, reg_class, update);
+
+ loc = UpdateLoc(loc);
+
+ if (loc.location == kLocPhysReg) {
+ if (!RegClassMatches(reg_class, loc.low_reg)) {
+ /* Wrong register class. Realloc, copy and transfer ownership */
+ new_reg = AllocTypedTemp(loc.fp, reg_class);
+ OpRegCopy(new_reg, loc.low_reg);
+ CopyRegInfo(new_reg, loc.low_reg);
+ Clobber(loc.low_reg);
+ loc.low_reg = new_reg;
+ }
+ return loc;
+ }
+
+ DCHECK_NE(loc.s_reg_low, INVALID_SREG);
+
+ new_reg = AllocTypedTemp(loc.fp, reg_class);
+ loc.low_reg = new_reg;
+
+ if (update) {
+ loc.location = kLocPhysReg;
+ MarkLive(loc.low_reg, loc.s_reg_low);
+ }
+ return loc;
+}
+
+/* USE SSA names to count references of base Dalvik v_regs. */
+void Mir2Lir::CountRefs(RefCounts* core_counts, RefCounts* fp_counts) {
+ for (int i = 0; i < mir_graph_->GetNumSSARegs(); i++) {
+ RegLocation loc = mir_graph_->reg_location_[i];
+ RefCounts* counts = loc.fp ? fp_counts : core_counts;
+ int p_map_idx = SRegToPMap(loc.s_reg_low);
+ //Don't count easily regenerated immediates
+ if (loc.fp || !IsInexpensiveConstant(loc)) {
+ counts[p_map_idx].count += mir_graph_->GetUseCount(i);
+ }
+ if (loc.wide && loc.fp && !loc.high_word) {
+ counts[p_map_idx].double_start = true;
+ }
+ }
+}
+
+/* qsort callback function, sort descending */
+static int SortCounts(const void *val1, const void *val2)
+{
+ const Mir2Lir::RefCounts* op1 = reinterpret_cast<const Mir2Lir::RefCounts*>(val1);
+ const Mir2Lir::RefCounts* op2 = reinterpret_cast<const Mir2Lir::RefCounts*>(val2);
+ return (op1->count == op2->count) ? 0 : (op1->count < op2->count ? 1 : -1);
+}
+
+void Mir2Lir::DumpCounts(const RefCounts* arr, int size, const char* msg)
+{
+ LOG(INFO) << msg;
+ for (int i = 0; i < size; i++) {
+ LOG(INFO) << "s_reg[" << arr[i].s_reg << "]: " << arr[i].count;
+ }
+}
+
+/*
+ * Note: some portions of this code required even if the kPromoteRegs
+ * optimization is disabled.
+ */
+void Mir2Lir::DoPromotion()
+{
+ int reg_bias = cu_->num_compiler_temps + 1;
+ int dalvik_regs = cu_->num_dalvik_registers;
+ int num_regs = dalvik_regs + reg_bias;
+ const int promotion_threshold = 1;
+
+ // Allow target code to add any special registers
+ AdjustSpillMask();
+
+ /*
+ * Simple register promotion. Just do a static count of the uses
+ * of Dalvik registers. Note that we examine the SSA names, but
+ * count based on original Dalvik register name. Count refs
+ * separately based on type in order to give allocation
+ * preference to fp doubles - which must be allocated sequential
+ * physical single fp registers started with an even-numbered
+ * reg.
+ * TUNING: replace with linear scan once we have the ability
+ * to describe register live ranges for GC.
+ */
+ RefCounts *core_regs =
+ static_cast<RefCounts*>(arena_->NewMem(sizeof(RefCounts) * num_regs, true,
+ ArenaAllocator::kAllocRegAlloc));
+ RefCounts *FpRegs =
+ static_cast<RefCounts *>(arena_->NewMem(sizeof(RefCounts) * num_regs, true,
+ ArenaAllocator::kAllocRegAlloc));
+ // Set ssa names for original Dalvik registers
+ for (int i = 0; i < dalvik_regs; i++) {
+ core_regs[i].s_reg = FpRegs[i].s_reg = i;
+ }
+ // Set ssa name for Method*
+ core_regs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg();
+ FpRegs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg(); // For consistecy
+ // Set ssa names for compiler_temps
+ for (int i = 1; i <= cu_->num_compiler_temps; i++) {
+ CompilerTemp* ct = mir_graph_->compiler_temps_.Get(i);
+ core_regs[dalvik_regs + i].s_reg = ct->s_reg;
+ FpRegs[dalvik_regs + i].s_reg = ct->s_reg;
+ }
+
+ // Sum use counts of SSA regs by original Dalvik vreg.
+ CountRefs(core_regs, FpRegs);
+
+ /*
+ * Ideally, we'd allocate doubles starting with an even-numbered
+ * register. Bias the counts to try to allocate any vreg that's
+ * used as the start of a pair first.
+ */
+ for (int i = 0; i < num_regs; i++) {
+ if (FpRegs[i].double_start) {
+ FpRegs[i].count *= 2;
+ }
+ }
+
+ // Sort the count arrays
+ qsort(core_regs, num_regs, sizeof(RefCounts), SortCounts);
+ qsort(FpRegs, num_regs, sizeof(RefCounts), SortCounts);
+
+ if (cu_->verbose) {
+ DumpCounts(core_regs, num_regs, "Core regs after sort");
+ DumpCounts(FpRegs, num_regs, "Fp regs after sort");
+ }
+
+ if (!(cu_->disable_opt & (1 << kPromoteRegs))) {
+ // Promote FpRegs
+ for (int i = 0; (i < num_regs) &&
+ (FpRegs[i].count >= promotion_threshold ); i++) {
+ int p_map_idx = SRegToPMap(FpRegs[i].s_reg);
+ if (promotion_map_[p_map_idx].fp_location != kLocPhysReg) {
+ int reg = AllocPreservedFPReg(FpRegs[i].s_reg,
+ FpRegs[i].double_start);
+ if (reg < 0) {
+ break; // No more left
+ }
+ }
+ }
+
+ // Promote core regs
+ for (int i = 0; (i < num_regs) &&
+ (core_regs[i].count >= promotion_threshold); i++) {
+ int p_map_idx = SRegToPMap(core_regs[i].s_reg);
+ if (promotion_map_[p_map_idx].core_location !=
+ kLocPhysReg) {
+ int reg = AllocPreservedCoreReg(core_regs[i].s_reg);
+ if (reg < 0) {
+ break; // No more left
+ }
+ }
+ }
+ }
+
+ // Now, update SSA names to new home locations
+ for (int i = 0; i < mir_graph_->GetNumSSARegs(); i++) {
+ RegLocation *curr = &mir_graph_->reg_location_[i];
+ int p_map_idx = SRegToPMap(curr->s_reg_low);
+ if (!curr->wide) {
+ if (curr->fp) {
+ if (promotion_map_[p_map_idx].fp_location == kLocPhysReg) {
+ curr->location = kLocPhysReg;
+ curr->low_reg = promotion_map_[p_map_idx].FpReg;
+ curr->home = true;
+ }
+ } else {
+ if (promotion_map_[p_map_idx].core_location == kLocPhysReg) {
+ curr->location = kLocPhysReg;
+ curr->low_reg = promotion_map_[p_map_idx].core_reg;
+ curr->home = true;
+ }
+ }
+ curr->high_reg = INVALID_REG;
+ } else {
+ if (curr->high_word) {
+ continue;
+ }
+ if (curr->fp) {
+ if ((promotion_map_[p_map_idx].fp_location == kLocPhysReg) &&
+ (promotion_map_[p_map_idx+1].fp_location ==
+ kLocPhysReg)) {
+ int low_reg = promotion_map_[p_map_idx].FpReg;
+ int high_reg = promotion_map_[p_map_idx+1].FpReg;
+ // Doubles require pair of singles starting at even reg
+ if (((low_reg & 0x1) == 0) && ((low_reg + 1) == high_reg)) {
+ curr->location = kLocPhysReg;
+ curr->low_reg = low_reg;
+ curr->high_reg = high_reg;
+ curr->home = true;
+ }
+ }
+ } else {
+ if ((promotion_map_[p_map_idx].core_location == kLocPhysReg)
+ && (promotion_map_[p_map_idx+1].core_location ==
+ kLocPhysReg)) {
+ curr->location = kLocPhysReg;
+ curr->low_reg = promotion_map_[p_map_idx].core_reg;
+ curr->high_reg = promotion_map_[p_map_idx+1].core_reg;
+ curr->home = true;
+ }
+ }
+ }
+ }
+ if (cu_->verbose) {
+ DumpPromotionMap();
+ }
+}
+
+/* Returns sp-relative offset in bytes for a VReg */
+int Mir2Lir::VRegOffset(int v_reg)
+{
+ return StackVisitor::GetVRegOffset(cu_->code_item, core_spill_mask_,
+ fp_spill_mask_, frame_size_, v_reg);
+}
+
+/* Returns sp-relative offset in bytes for a SReg */
+int Mir2Lir::SRegOffset(int s_reg)
+{
+ return VRegOffset(mir_graph_->SRegToVReg(s_reg));
+}
+
+/* Mark register usage state and return long retloc */
+RegLocation Mir2Lir::GetReturnWide(bool is_double)
+{
+ RegLocation gpr_res = LocCReturnWide();
+ RegLocation fpr_res = LocCReturnDouble();
+ RegLocation res = is_double ? fpr_res : gpr_res;
+ Clobber(res.low_reg);
+ Clobber(res.high_reg);
+ LockTemp(res.low_reg);
+ LockTemp(res.high_reg);
+ MarkPair(res.low_reg, res.high_reg);
+ return res;
+}
+
+RegLocation Mir2Lir::GetReturn(bool is_float)
+{
+ RegLocation gpr_res = LocCReturn();
+ RegLocation fpr_res = LocCReturnFloat();
+ RegLocation res = is_float ? fpr_res : gpr_res;
+ Clobber(res.low_reg);
+ if (cu_->instruction_set == kMips) {
+ MarkInUse(res.low_reg);
+ } else {
+ LockTemp(res.low_reg);
+ }
+ return res;
+}
+
+void Mir2Lir::SimpleRegAlloc()
+{
+ DoPromotion();
+
+ if (cu_->verbose && !(cu_->disable_opt & (1 << kPromoteRegs))) {
+ LOG(INFO) << "After Promotion";
+ mir_graph_->DumpRegLocTable(mir_graph_->reg_location_, mir_graph_->GetNumSSARegs());
+ }
+
+ /* Set the frame size */
+ frame_size_ = ComputeFrameSize();
+}
+
+/*
+ * Get the "real" sreg number associated with an s_reg slot. In general,
+ * s_reg values passed through codegen are the SSA names created by
+ * dataflow analysis and refer to slot numbers in the mir_graph_->reg_location
+ * array. However, renaming is accomplished by simply replacing RegLocation
+ * entries in the reglocation[] array. Therefore, when location
+ * records for operands are first created, we need to ask the locRecord
+ * identified by the dataflow pass what it's new name is.
+ */
+int Mir2Lir::GetSRegHi(int lowSreg) {
+ return (lowSreg == INVALID_SREG) ? INVALID_SREG : lowSreg + 1;
+}
+
+bool Mir2Lir::oat_live_out(int s_reg) {
+ //For now.
+ return true;
+}
+
+int Mir2Lir::oatSSASrc(MIR* mir, int num) {
+ DCHECK_GT(mir->ssa_rep->num_uses, num);
+ return mir->ssa_rep->uses[num];
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
new file mode 100644
index 0000000..4aeda41
--- /dev/null
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -0,0 +1,1388 @@
+/*
+ * Copyright (C) 2012 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 "codegen_x86.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "x86_lir.h"
+
+namespace art {
+
+#define MAX_ASSEMBLER_RETRIES 50
+
+const X86EncodingMap X86Mir2Lir::EncodingMap[kX86Last] = {
+ { kX8632BitData, kData, IS_UNARY_OP, { 0, 0, 0x00, 0, 0, 0, 0, 4 }, "data", "0x!0d" },
+ { kX86Bkpt, kNullary, NO_OPERAND | IS_BRANCH, { 0, 0, 0xCC, 0, 0, 0, 0, 0 }, "int 3", "" },
+ { kX86Nop, kNop, IS_UNARY_OP, { 0, 0, 0x90, 0, 0, 0, 0, 0 }, "nop", "" },
+
+#define ENCODING_MAP(opname, mem_use, reg_def, uses_ccodes, \
+ rm8_r8, rm32_r32, \
+ r8_rm8, r32_rm32, \
+ ax8_i8, ax32_i32, \
+ rm8_i8, rm8_i8_modrm, \
+ rm32_i32, rm32_i32_modrm, \
+ rm32_i8, rm32_i8_modrm) \
+{ kX86 ## opname ## 8MR, kMemReg, mem_use | IS_TERTIARY_OP | REG_USE02 | SETS_CCODES | uses_ccodes, { 0, 0, rm8_r8, 0, 0, 0, 0, 0 }, #opname "8MR", "[!0r+!1d],!2r" }, \
+{ kX86 ## opname ## 8AR, kArrayReg, mem_use | IS_QUIN_OP | REG_USE014 | SETS_CCODES | uses_ccodes, { 0, 0, rm8_r8, 0, 0, 0, 0, 0 }, #opname "8AR", "[!0r+!1r<<!2d+!3d],!4r" }, \
+{ kX86 ## opname ## 8TR, kThreadReg, mem_use | IS_BINARY_OP | REG_USE1 | SETS_CCODES | uses_ccodes, { THREAD_PREFIX, 0, rm8_r8, 0, 0, 0, 0, 0 }, #opname "8TR", "fs:[!0d],!1r" }, \
+{ kX86 ## opname ## 8RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE01 | SETS_CCODES | uses_ccodes, { 0, 0, r8_rm8, 0, 0, 0, 0, 0 }, #opname "8RR", "!0r,!1r" }, \
+{ kX86 ## opname ## 8RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE01 | SETS_CCODES | uses_ccodes, { 0, 0, r8_rm8, 0, 0, 0, 0, 0 }, #opname "8RM", "!0r,[!1r+!2d]" }, \
+{ kX86 ## opname ## 8RA, kRegArray, IS_LOAD | IS_QUIN_OP | reg_def | REG_USE012 | SETS_CCODES | uses_ccodes, { 0, 0, r8_rm8, 0, 0, 0, 0, 0 }, #opname "8RA", "!0r,[!1r+!2r<<!3d+!4d]" }, \
+{ kX86 ## opname ## 8RT, kRegThread, IS_LOAD | IS_BINARY_OP | reg_def | REG_USE0 | SETS_CCODES | uses_ccodes, { THREAD_PREFIX, 0, r8_rm8, 0, 0, 0, 0, 0 }, #opname "8RT", "!0r,fs:[!1d]" }, \
+{ kX86 ## opname ## 8RI, kRegImm, IS_BINARY_OP | reg_def | REG_USE0 | SETS_CCODES | uses_ccodes, { 0, 0, rm8_i8, 0, 0, rm8_i8_modrm, ax8_i8, 1 }, #opname "8RI", "!0r,!1d" }, \
+{ kX86 ## opname ## 8MI, kMemImm, mem_use | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES | uses_ccodes, { 0, 0, rm8_i8, 0, 0, rm8_i8_modrm, 0, 1 }, #opname "8MI", "[!0r+!1d],!2d" }, \
+{ kX86 ## opname ## 8AI, kArrayImm, mem_use | IS_QUIN_OP | REG_USE01 | SETS_CCODES | uses_ccodes, { 0, 0, rm8_i8, 0, 0, rm8_i8_modrm, 0, 1 }, #opname "8AI", "[!0r+!1r<<!2d+!3d],!4d" }, \
+{ kX86 ## opname ## 8TI, kThreadImm, mem_use | IS_BINARY_OP | SETS_CCODES | uses_ccodes, { THREAD_PREFIX, 0, rm8_i8, 0, 0, rm8_i8_modrm, 0, 1 }, #opname "8TI", "fs:[!0d],!1d" }, \
+ \
+{ kX86 ## opname ## 16MR, kMemReg, mem_use | IS_TERTIARY_OP | REG_USE02 | SETS_CCODES | uses_ccodes, { 0x66, 0, rm32_r32, 0, 0, 0, 0, 0 }, #opname "16MR", "[!0r+!1d],!2r" }, \
+{ kX86 ## opname ## 16AR, kArrayReg, mem_use | IS_QUIN_OP | REG_USE014 | SETS_CCODES | uses_ccodes, { 0x66, 0, rm32_r32, 0, 0, 0, 0, 0 }, #opname "16AR", "[!0r+!1r<<!2d+!3d],!4r" }, \
+{ kX86 ## opname ## 16TR, kThreadReg, mem_use | IS_BINARY_OP | REG_USE1 | SETS_CCODES | uses_ccodes, { THREAD_PREFIX, 0x66, rm32_r32, 0, 0, 0, 0, 0 }, #opname "16TR", "fs:[!0d],!1r" }, \
+{ kX86 ## opname ## 16RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE01 | SETS_CCODES | uses_ccodes, { 0x66, 0, r32_rm32, 0, 0, 0, 0, 0 }, #opname "16RR", "!0r,!1r" }, \
+{ kX86 ## opname ## 16RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE01 | SETS_CCODES | uses_ccodes, { 0x66, 0, r32_rm32, 0, 0, 0, 0, 0 }, #opname "16RM", "!0r,[!1r+!2d]" }, \
+{ kX86 ## opname ## 16RA, kRegArray, IS_LOAD | IS_QUIN_OP | reg_def | REG_USE012 | SETS_CCODES | uses_ccodes, { 0x66, 0, r32_rm32, 0, 0, 0, 0, 0 }, #opname "16RA", "!0r,[!1r+!2r<<!3d+!4d]" }, \
+{ kX86 ## opname ## 16RT, kRegThread, IS_LOAD | IS_BINARY_OP | reg_def | REG_USE0 | SETS_CCODES | uses_ccodes, { THREAD_PREFIX, 0x66, r32_rm32, 0, 0, 0, 0, 0 }, #opname "16RT", "!0r,fs:[!1d]" }, \
+{ kX86 ## opname ## 16RI, kRegImm, IS_BINARY_OP | reg_def | REG_USE0 | SETS_CCODES | uses_ccodes, { 0x66, 0, rm32_i32, 0, 0, rm32_i32_modrm, ax32_i32, 2 }, #opname "16RI", "!0r,!1d" }, \
+{ kX86 ## opname ## 16MI, kMemImm, mem_use | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES | uses_ccodes, { 0x66, 0, rm32_i32, 0, 0, rm32_i32_modrm, 0, 2 }, #opname "16MI", "[!0r+!1d],!2d" }, \
+{ kX86 ## opname ## 16AI, kArrayImm, mem_use | IS_QUIN_OP | REG_USE01 | SETS_CCODES | uses_ccodes, { 0x66, 0, rm32_i32, 0, 0, rm32_i32_modrm, 0, 2 }, #opname "16AI", "[!0r+!1r<<!2d+!3d],!4d" }, \
+{ kX86 ## opname ## 16TI, kThreadImm, mem_use | IS_BINARY_OP | SETS_CCODES | uses_ccodes, { THREAD_PREFIX, 0x66, rm32_i32, 0, 0, rm32_i32_modrm, 0, 2 }, #opname "16TI", "fs:[!0d],!1d" }, \
+{ kX86 ## opname ## 16RI8, kRegImm, IS_BINARY_OP | reg_def | REG_USE0 | SETS_CCODES | uses_ccodes, { 0x66, 0, rm32_i8, 0, 0, rm32_i8_modrm, 0, 1 }, #opname "16RI8", "!0r,!1d" }, \
+{ kX86 ## opname ## 16MI8, kMemImm, mem_use | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES | uses_ccodes, { 0x66, 0, rm32_i8, 0, 0, rm32_i8_modrm, 0, 1 }, #opname "16MI8", "[!0r+!1d],!2d" }, \
+{ kX86 ## opname ## 16AI8, kArrayImm, mem_use | IS_QUIN_OP | REG_USE01 | SETS_CCODES | uses_ccodes, { 0x66, 0, rm32_i8, 0, 0, rm32_i8_modrm, 0, 1 }, #opname "16AI8", "[!0r+!1r<<!2d+!3d],!4d" }, \
+{ kX86 ## opname ## 16TI8, kThreadImm, mem_use | IS_BINARY_OP | SETS_CCODES | uses_ccodes, { THREAD_PREFIX, 0x66, rm32_i8, 0, 0, rm32_i8_modrm, 0, 1 }, #opname "16TI8", "fs:[!0d],!1d" }, \
+ \
+{ kX86 ## opname ## 32MR, kMemReg, mem_use | IS_TERTIARY_OP | REG_USE02 | SETS_CCODES | uses_ccodes, { 0, 0, rm32_r32, 0, 0, 0, 0, 0 }, #opname "32MR", "[!0r+!1d],!2r" }, \
+{ kX86 ## opname ## 32AR, kArrayReg, mem_use | IS_QUIN_OP | REG_USE014 | SETS_CCODES | uses_ccodes, { 0, 0, rm32_r32, 0, 0, 0, 0, 0 }, #opname "32AR", "[!0r+!1r<<!2d+!3d],!4r" }, \
+{ kX86 ## opname ## 32TR, kThreadReg, mem_use | IS_BINARY_OP | REG_USE1 | SETS_CCODES | uses_ccodes, { THREAD_PREFIX, 0, rm32_r32, 0, 0, 0, 0, 0 }, #opname "32TR", "fs:[!0d],!1r" }, \
+{ kX86 ## opname ## 32RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE01 | SETS_CCODES | uses_ccodes, { 0, 0, r32_rm32, 0, 0, 0, 0, 0 }, #opname "32RR", "!0r,!1r" }, \
+{ kX86 ## opname ## 32RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE01 | SETS_CCODES | uses_ccodes, { 0, 0, r32_rm32, 0, 0, 0, 0, 0 }, #opname "32RM", "!0r,[!1r+!2d]" }, \
+{ kX86 ## opname ## 32RA, kRegArray, IS_LOAD | IS_QUIN_OP | reg_def | REG_USE012 | SETS_CCODES | uses_ccodes, { 0, 0, r32_rm32, 0, 0, 0, 0, 0 }, #opname "32RA", "!0r,[!1r+!2r<<!3d+!4d]" }, \
+{ kX86 ## opname ## 32RT, kRegThread, IS_LOAD | IS_BINARY_OP | reg_def | REG_USE0 | SETS_CCODES | uses_ccodes, { THREAD_PREFIX, 0, r32_rm32, 0, 0, 0, 0, 0 }, #opname "32RT", "!0r,fs:[!1d]" }, \
+{ kX86 ## opname ## 32RI, kRegImm, IS_BINARY_OP | reg_def | REG_USE0 | SETS_CCODES | uses_ccodes, { 0, 0, rm32_i32, 0, 0, rm32_i32_modrm, ax32_i32, 4 }, #opname "32RI", "!0r,!1d" }, \
+{ kX86 ## opname ## 32MI, kMemImm, mem_use | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES | uses_ccodes, { 0, 0, rm32_i32, 0, 0, rm32_i32_modrm, 0, 4 }, #opname "32MI", "[!0r+!1d],!2d" }, \
+{ kX86 ## opname ## 32AI, kArrayImm, mem_use | IS_QUIN_OP | REG_USE01 | SETS_CCODES | uses_ccodes, { 0, 0, rm32_i32, 0, 0, rm32_i32_modrm, 0, 4 }, #opname "32AI", "[!0r+!1r<<!2d+!3d],!4d" }, \
+{ kX86 ## opname ## 32TI, kThreadImm, mem_use | IS_BINARY_OP | SETS_CCODES | uses_ccodes, { THREAD_PREFIX, 0, rm32_i32, 0, 0, rm32_i32_modrm, 0, 4 }, #opname "32TI", "fs:[!0d],!1d" }, \
+{ kX86 ## opname ## 32RI8, kRegImm, IS_BINARY_OP | reg_def | REG_USE0 | SETS_CCODES | uses_ccodes, { 0, 0, rm32_i8, 0, 0, rm32_i8_modrm, 0, 1 }, #opname "32RI8", "!0r,!1d" }, \
+{ kX86 ## opname ## 32MI8, kMemImm, mem_use | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES | uses_ccodes, { 0, 0, rm32_i8, 0, 0, rm32_i8_modrm, 0, 1 }, #opname "32MI8", "[!0r+!1d],!2d" }, \
+{ kX86 ## opname ## 32AI8, kArrayImm, mem_use | IS_QUIN_OP | REG_USE01 | SETS_CCODES | uses_ccodes, { 0, 0, rm32_i8, 0, 0, rm32_i8_modrm, 0, 1 }, #opname "32AI8", "[!0r+!1r<<!2d+!3d],!4d" }, \
+{ kX86 ## opname ## 32TI8, kThreadImm, mem_use | IS_BINARY_OP | SETS_CCODES | uses_ccodes, { THREAD_PREFIX, 0, rm32_i8, 0, 0, rm32_i8_modrm, 0, 1 }, #opname "32TI8", "fs:[!0d],!1d" }
+
+ENCODING_MAP(Add, IS_LOAD | IS_STORE, REG_DEF0, 0,
+ 0x00 /* RegMem8/Reg8 */, 0x01 /* RegMem32/Reg32 */,
+ 0x02 /* Reg8/RegMem8 */, 0x03 /* Reg32/RegMem32 */,
+ 0x04 /* Rax8/imm8 opcode */, 0x05 /* Rax32/imm32 */,
+ 0x80, 0x0 /* RegMem8/imm8 */,
+ 0x81, 0x0 /* RegMem32/imm32 */, 0x83, 0x0 /* RegMem32/imm8 */),
+ENCODING_MAP(Or, IS_LOAD | IS_STORE, REG_DEF0, 0,
+ 0x08 /* RegMem8/Reg8 */, 0x09 /* RegMem32/Reg32 */,
+ 0x0A /* Reg8/RegMem8 */, 0x0B /* Reg32/RegMem32 */,
+ 0x0C /* Rax8/imm8 opcode */, 0x0D /* Rax32/imm32 */,
+ 0x80, 0x1 /* RegMem8/imm8 */,
+ 0x81, 0x1 /* RegMem32/imm32 */, 0x83, 0x1 /* RegMem32/imm8 */),
+ENCODING_MAP(Adc, IS_LOAD | IS_STORE, REG_DEF0, USES_CCODES,
+ 0x10 /* RegMem8/Reg8 */, 0x11 /* RegMem32/Reg32 */,
+ 0x12 /* Reg8/RegMem8 */, 0x13 /* Reg32/RegMem32 */,
+ 0x14 /* Rax8/imm8 opcode */, 0x15 /* Rax32/imm32 */,
+ 0x80, 0x2 /* RegMem8/imm8 */,
+ 0x81, 0x2 /* RegMem32/imm32 */, 0x83, 0x2 /* RegMem32/imm8 */),
+ENCODING_MAP(Sbb, IS_LOAD | IS_STORE, REG_DEF0, USES_CCODES,
+ 0x18 /* RegMem8/Reg8 */, 0x19 /* RegMem32/Reg32 */,
+ 0x1A /* Reg8/RegMem8 */, 0x1B /* Reg32/RegMem32 */,
+ 0x1C /* Rax8/imm8 opcode */, 0x1D /* Rax32/imm32 */,
+ 0x80, 0x3 /* RegMem8/imm8 */,
+ 0x81, 0x3 /* RegMem32/imm32 */, 0x83, 0x3 /* RegMem32/imm8 */),
+ENCODING_MAP(And, IS_LOAD | IS_STORE, REG_DEF0, 0,
+ 0x20 /* RegMem8/Reg8 */, 0x21 /* RegMem32/Reg32 */,
+ 0x22 /* Reg8/RegMem8 */, 0x23 /* Reg32/RegMem32 */,
+ 0x24 /* Rax8/imm8 opcode */, 0x25 /* Rax32/imm32 */,
+ 0x80, 0x4 /* RegMem8/imm8 */,
+ 0x81, 0x4 /* RegMem32/imm32 */, 0x83, 0x4 /* RegMem32/imm8 */),
+ENCODING_MAP(Sub, IS_LOAD | IS_STORE, REG_DEF0, 0,
+ 0x28 /* RegMem8/Reg8 */, 0x29 /* RegMem32/Reg32 */,
+ 0x2A /* Reg8/RegMem8 */, 0x2B /* Reg32/RegMem32 */,
+ 0x2C /* Rax8/imm8 opcode */, 0x2D /* Rax32/imm32 */,
+ 0x80, 0x5 /* RegMem8/imm8 */,
+ 0x81, 0x5 /* RegMem32/imm32 */, 0x83, 0x5 /* RegMem32/imm8 */),
+ENCODING_MAP(Xor, IS_LOAD | IS_STORE, REG_DEF0, 0,
+ 0x30 /* RegMem8/Reg8 */, 0x31 /* RegMem32/Reg32 */,
+ 0x32 /* Reg8/RegMem8 */, 0x33 /* Reg32/RegMem32 */,
+ 0x34 /* Rax8/imm8 opcode */, 0x35 /* Rax32/imm32 */,
+ 0x80, 0x6 /* RegMem8/imm8 */,
+ 0x81, 0x6 /* RegMem32/imm32 */, 0x83, 0x6 /* RegMem32/imm8 */),
+ENCODING_MAP(Cmp, IS_LOAD, 0, 0,
+ 0x38 /* RegMem8/Reg8 */, 0x39 /* RegMem32/Reg32 */,
+ 0x3A /* Reg8/RegMem8 */, 0x3B /* Reg32/RegMem32 */,
+ 0x3C /* Rax8/imm8 opcode */, 0x3D /* Rax32/imm32 */,
+ 0x80, 0x7 /* RegMem8/imm8 */,
+ 0x81, 0x7 /* RegMem32/imm32 */, 0x83, 0x7 /* RegMem32/imm8 */),
+#undef ENCODING_MAP
+
+ { kX86Imul16RRI, kRegRegImm, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, { 0x66, 0, 0x69, 0, 0, 0, 0, 2 }, "Imul16RRI", "!0r,!1r,!2d" },
+ { kX86Imul16RMI, kRegMemImm, IS_LOAD | IS_QUAD_OP | REG_DEF0_USE1 | SETS_CCODES, { 0x66, 0, 0x69, 0, 0, 0, 0, 2 }, "Imul16RMI", "!0r,[!1r+!2d],!3d" },
+ { kX86Imul16RAI, kRegArrayImm, IS_LOAD | IS_SEXTUPLE_OP | REG_DEF0_USE12 | SETS_CCODES, { 0x66, 0, 0x69, 0, 0, 0, 0, 2 }, "Imul16RAI", "!0r,[!1r+!2r<<!3d+!4d],!5d" },
+
+ { kX86Imul32RRI, kRegRegImm, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, { 0, 0, 0x69, 0, 0, 0, 0, 4 }, "Imul32RRI", "!0r,!1r,!2d" },
+ { kX86Imul32RMI, kRegMemImm, IS_LOAD | IS_QUAD_OP | REG_DEF0_USE1 | SETS_CCODES, { 0, 0, 0x69, 0, 0, 0, 0, 4 }, "Imul32RMI", "!0r,[!1r+!2d],!3d" },
+ { kX86Imul32RAI, kRegArrayImm, IS_LOAD | IS_SEXTUPLE_OP | REG_DEF0_USE12 | SETS_CCODES, { 0, 0, 0x69, 0, 0, 0, 0, 4 }, "Imul32RAI", "!0r,[!1r+!2r<<!3d+!4d],!5d" },
+ { kX86Imul32RRI8, kRegRegImm, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES, { 0, 0, 0x6B, 0, 0, 0, 0, 1 }, "Imul32RRI8", "!0r,!1r,!2d" },
+ { kX86Imul32RMI8, kRegMemImm, IS_LOAD | IS_QUAD_OP | REG_DEF0_USE1 | SETS_CCODES, { 0, 0, 0x6B, 0, 0, 0, 0, 1 }, "Imul32RMI8", "!0r,[!1r+!2d],!3d" },
+ { kX86Imul32RAI8, kRegArrayImm, IS_LOAD | IS_SEXTUPLE_OP | REG_DEF0_USE12 | SETS_CCODES, { 0, 0, 0x6B, 0, 0, 0, 0, 1 }, "Imul32RAI8", "!0r,[!1r+!2r<<!3d+!4d],!5d" },
+
+ { kX86Mov8MR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0, 0, 0x88, 0, 0, 0, 0, 0 }, "Mov8MR", "[!0r+!1d],!2r" },
+ { kX86Mov8AR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { 0, 0, 0x88, 0, 0, 0, 0, 0 }, "Mov8AR", "[!0r+!1r<<!2d+!3d],!4r" },
+ { kX86Mov8TR, kThreadReg, IS_STORE | IS_BINARY_OP | REG_USE1, { THREAD_PREFIX, 0, 0x88, 0, 0, 0, 0, 0 }, "Mov8TR", "fs:[!0d],!1r" },
+ { kX86Mov8RR, kRegReg, IS_BINARY_OP | REG_DEF0_USE1, { 0, 0, 0x8A, 0, 0, 0, 0, 0 }, "Mov8RR", "!0r,!1r" },
+ { kX86Mov8RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | REG_DEF0_USE1, { 0, 0, 0x8A, 0, 0, 0, 0, 0 }, "Mov8RM", "!0r,[!1r+!2d]" },
+ { kX86Mov8RA, kRegArray, IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8A, 0, 0, 0, 0, 0 }, "Mov8RA", "!0r,[!1r+!2r<<!3d+!4d]" },
+ { kX86Mov8RT, kRegThread, IS_LOAD | IS_BINARY_OP | REG_DEF0, { THREAD_PREFIX, 0, 0x8A, 0, 0, 0, 0, 0 }, "Mov8RT", "!0r,fs:[!1d]" },
+ { kX86Mov8RI, kMovRegImm, IS_BINARY_OP | REG_DEF0, { 0, 0, 0xB0, 0, 0, 0, 0, 1 }, "Mov8RI", "!0r,!1d" },
+ { kX86Mov8MI, kMemImm, IS_STORE | IS_TERTIARY_OP | REG_USE0, { 0, 0, 0xC6, 0, 0, 0, 0, 1 }, "Mov8MI", "[!0r+!1d],!2d" },
+ { kX86Mov8AI, kArrayImm, IS_STORE | IS_QUIN_OP | REG_USE01, { 0, 0, 0xC6, 0, 0, 0, 0, 1 }, "Mov8AI", "[!0r+!1r<<!2d+!3d],!4d" },
+ { kX86Mov8TI, kThreadImm, IS_STORE | IS_BINARY_OP, { THREAD_PREFIX, 0, 0xC6, 0, 0, 0, 0, 1 }, "Mov8TI", "fs:[!0d],!1d" },
+
+ { kX86Mov16MR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0x66, 0, 0x89, 0, 0, 0, 0, 0 }, "Mov16MR", "[!0r+!1d],!2r" },
+ { kX86Mov16AR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { 0x66, 0, 0x89, 0, 0, 0, 0, 0 }, "Mov16AR", "[!0r+!1r<<!2d+!3d],!4r" },
+ { kX86Mov16TR, kThreadReg, IS_STORE | IS_BINARY_OP | REG_USE1, { THREAD_PREFIX, 0x66, 0x89, 0, 0, 0, 0, 0 }, "Mov16TR", "fs:[!0d],!1r" },
+ { kX86Mov16RR, kRegReg, IS_BINARY_OP | REG_DEF0_USE1, { 0x66, 0, 0x8B, 0, 0, 0, 0, 0 }, "Mov16RR", "!0r,!1r" },
+ { kX86Mov16RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | REG_DEF0_USE1, { 0x66, 0, 0x8B, 0, 0, 0, 0, 0 }, "Mov16RM", "!0r,[!1r+!2d]" },
+ { kX86Mov16RA, kRegArray, IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { 0x66, 0, 0x8B, 0, 0, 0, 0, 0 }, "Mov16RA", "!0r,[!1r+!2r<<!3d+!4d]" },
+ { kX86Mov16RT, kRegThread, IS_LOAD | IS_BINARY_OP | REG_DEF0, { THREAD_PREFIX, 0x66, 0x8B, 0, 0, 0, 0, 0 }, "Mov16RT", "!0r,fs:[!1d]" },
+ { kX86Mov16RI, kMovRegImm, IS_BINARY_OP | REG_DEF0, { 0x66, 0, 0xB8, 0, 0, 0, 0, 2 }, "Mov16RI", "!0r,!1d" },
+ { kX86Mov16MI, kMemImm, IS_STORE | IS_TERTIARY_OP | REG_USE0, { 0x66, 0, 0xC7, 0, 0, 0, 0, 2 }, "Mov16MI", "[!0r+!1d],!2d" },
+ { kX86Mov16AI, kArrayImm, IS_STORE | IS_QUIN_OP | REG_USE01, { 0x66, 0, 0xC7, 0, 0, 0, 0, 2 }, "Mov16AI", "[!0r+!1r<<!2d+!3d],!4d" },
+ { kX86Mov16TI, kThreadImm, IS_STORE | IS_BINARY_OP, { THREAD_PREFIX, 0x66, 0xC7, 0, 0, 0, 0, 2 }, "Mov16TI", "fs:[!0d],!1d" },
+
+ { kX86Mov32MR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0, 0, 0x89, 0, 0, 0, 0, 0 }, "Mov32MR", "[!0r+!1d],!2r" },
+ { kX86Mov32AR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { 0, 0, 0x89, 0, 0, 0, 0, 0 }, "Mov32AR", "[!0r+!1r<<!2d+!3d],!4r" },
+ { kX86Mov32TR, kThreadReg, IS_STORE | IS_BINARY_OP | REG_USE1, { THREAD_PREFIX, 0, 0x89, 0, 0, 0, 0, 0 }, "Mov32TR", "fs:[!0d],!1r" },
+ { kX86Mov32RR, kRegReg, IS_BINARY_OP | REG_DEF0_USE1, { 0, 0, 0x8B, 0, 0, 0, 0, 0 }, "Mov32RR", "!0r,!1r" },
+ { kX86Mov32RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | REG_DEF0_USE1, { 0, 0, 0x8B, 0, 0, 0, 0, 0 }, "Mov32RM", "!0r,[!1r+!2d]" },
+ { kX86Mov32RA, kRegArray, IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8B, 0, 0, 0, 0, 0 }, "Mov32RA", "!0r,[!1r+!2r<<!3d+!4d]" },
+ { kX86Mov32RT, kRegThread, IS_LOAD | IS_BINARY_OP | REG_DEF0, { THREAD_PREFIX, 0, 0x8B, 0, 0, 0, 0, 0 }, "Mov32RT", "!0r,fs:[!1d]" },
+ { kX86Mov32RI, kMovRegImm, IS_BINARY_OP | REG_DEF0, { 0, 0, 0xB8, 0, 0, 0, 0, 4 }, "Mov32RI", "!0r,!1d" },
+ { kX86Mov32MI, kMemImm, IS_STORE | IS_TERTIARY_OP | REG_USE0, { 0, 0, 0xC7, 0, 0, 0, 0, 4 }, "Mov32MI", "[!0r+!1d],!2d" },
+ { kX86Mov32AI, kArrayImm, IS_STORE | IS_QUIN_OP | REG_USE01, { 0, 0, 0xC7, 0, 0, 0, 0, 4 }, "Mov32AI", "[!0r+!1r<<!2d+!3d],!4d" },
+ { kX86Mov32TI, kThreadImm, IS_STORE | IS_BINARY_OP, { THREAD_PREFIX, 0, 0xC7, 0, 0, 0, 0, 4 }, "Mov32TI", "fs:[!0d],!1d" },
+
+ { kX86Lea32RA, kRegArray, IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8D, 0, 0, 0, 0, 0 }, "Lea32RA", "!0r,[!1r+!2r<<!3d+!4d]" },
+
+#define SHIFT_ENCODING_MAP(opname, modrm_opcode) \
+{ kX86 ## opname ## 8RI, kShiftRegImm, IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES, { 0, 0, 0xC0, 0, 0, modrm_opcode, 0xD1, 1 }, #opname "8RI", "!0r,!1d" }, \
+{ kX86 ## opname ## 8MI, kShiftMemImm, IS_LOAD | IS_STORE | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xC0, 0, 0, modrm_opcode, 0xD1, 1 }, #opname "8MI", "[!0r+!1d],!2d" }, \
+{ kX86 ## opname ## 8AI, kShiftArrayImm, IS_LOAD | IS_STORE | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0xC0, 0, 0, modrm_opcode, 0xD1, 1 }, #opname "8AI", "[!0r+!1r<<!2d+!3d],!4d" }, \
+{ kX86 ## opname ## 8RC, kShiftRegCl, IS_BINARY_OP | REG_DEF0_USE0 | REG_USEC | SETS_CCODES, { 0, 0, 0xD2, 0, 0, modrm_opcode, 0, 1 }, #opname "8RC", "!0r,cl" }, \
+{ kX86 ## opname ## 8MC, kShiftMemCl, IS_LOAD | IS_STORE | IS_TERTIARY_OP | REG_USE0 | REG_USEC | SETS_CCODES, { 0, 0, 0xD2, 0, 0, modrm_opcode, 0, 1 }, #opname "8MC", "[!0r+!1d],cl" }, \
+{ kX86 ## opname ## 8AC, kShiftArrayCl, IS_LOAD | IS_STORE | IS_QUIN_OP | REG_USE01 | REG_USEC | SETS_CCODES, { 0, 0, 0xD2, 0, 0, modrm_opcode, 0, 1 }, #opname "8AC", "[!0r+!1r<<!2d+!3d],cl" }, \
+ \
+{ kX86 ## opname ## 16RI, kShiftRegImm, IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES, { 0x66, 0, 0xC1, 0, 0, modrm_opcode, 0xD1, 1 }, #opname "16RI", "!0r,!1d" }, \
+{ kX86 ## opname ## 16MI, kShiftMemImm, IS_LOAD | IS_STORE | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0x66, 0, 0xC1, 0, 0, modrm_opcode, 0xD1, 1 }, #opname "16MI", "[!0r+!1d],!2d" }, \
+{ kX86 ## opname ## 16AI, kShiftArrayImm, IS_LOAD | IS_STORE | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0x66, 0, 0xC1, 0, 0, modrm_opcode, 0xD1, 1 }, #opname "16AI", "[!0r+!1r<<!2d+!3d],!4d" }, \
+{ kX86 ## opname ## 16RC, kShiftRegCl, IS_BINARY_OP | REG_DEF0_USE0 | REG_USEC | SETS_CCODES, { 0x66, 0, 0xD3, 0, 0, modrm_opcode, 0, 1 }, #opname "16RC", "!0r,cl" }, \
+{ kX86 ## opname ## 16MC, kShiftMemCl, IS_LOAD | IS_STORE | IS_TERTIARY_OP | REG_USE0 | REG_USEC | SETS_CCODES, { 0x66, 0, 0xD3, 0, 0, modrm_opcode, 0, 1 }, #opname "16MC", "[!0r+!1d],cl" }, \
+{ kX86 ## opname ## 16AC, kShiftArrayCl, IS_LOAD | IS_STORE | IS_QUIN_OP | REG_USE01 | REG_USEC | SETS_CCODES, { 0x66, 0, 0xD3, 0, 0, modrm_opcode, 0, 1 }, #opname "16AC", "[!0r+!1r<<!2d+!3d],cl" }, \
+ \
+{ kX86 ## opname ## 32RI, kShiftRegImm, IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES, { 0, 0, 0xC1, 0, 0, modrm_opcode, 0xD1, 1 }, #opname "32RI", "!0r,!1d" }, \
+{ kX86 ## opname ## 32MI, kShiftMemImm, IS_LOAD | IS_STORE | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xC1, 0, 0, modrm_opcode, 0xD1, 1 }, #opname "32MI", "[!0r+!1d],!2d" }, \
+{ kX86 ## opname ## 32AI, kShiftArrayImm, IS_LOAD | IS_STORE | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0xC1, 0, 0, modrm_opcode, 0xD1, 1 }, #opname "32AI", "[!0r+!1r<<!2d+!3d],!4d" }, \
+{ kX86 ## opname ## 32RC, kShiftRegCl, IS_BINARY_OP | REG_DEF0_USE0 | REG_USEC | SETS_CCODES, { 0, 0, 0xD3, 0, 0, modrm_opcode, 0, 0 }, #opname "32RC", "!0r,cl" }, \
+{ kX86 ## opname ## 32MC, kShiftMemCl, IS_LOAD | IS_STORE | IS_TERTIARY_OP | REG_USE0 | REG_USEC | SETS_CCODES, { 0, 0, 0xD3, 0, 0, modrm_opcode, 0, 0 }, #opname "32MC", "[!0r+!1d],cl" }, \
+{ kX86 ## opname ## 32AC, kShiftArrayCl, IS_LOAD | IS_STORE | IS_QUIN_OP | REG_USE01 | REG_USEC | SETS_CCODES, { 0, 0, 0xD3, 0, 0, modrm_opcode, 0, 0 }, #opname "32AC", "[!0r+!1r<<!2d+!3d],cl" }
+
+ SHIFT_ENCODING_MAP(Rol, 0x0),
+ SHIFT_ENCODING_MAP(Ror, 0x1),
+ SHIFT_ENCODING_MAP(Rcl, 0x2),
+ SHIFT_ENCODING_MAP(Rcr, 0x3),
+ SHIFT_ENCODING_MAP(Sal, 0x4),
+ SHIFT_ENCODING_MAP(Shr, 0x5),
+ SHIFT_ENCODING_MAP(Sar, 0x7),
+#undef SHIFT_ENCODING_MAP
+
+ { kX86Cmc, kNullary, NO_OPERAND, { 0, 0, 0xF5, 0, 0, 0, 0, 0}, "Cmc", "" },
+
+ { kX86Test8RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1}, "Test8RI", "!0r,!1d" },
+ { kX86Test8MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1}, "Test8MI", "[!0r+!1d],!2d" },
+ { kX86Test8AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1}, "Test8AI", "[!0r+!1r<<!2d+!3d],!4d" },
+ { kX86Test16RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2}, "Test16RI", "!0r,!1d" },
+ { kX86Test16MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2}, "Test16MI", "[!0r+!1d],!2d" },
+ { kX86Test16AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2}, "Test16AI", "[!0r+!1r<<!2d+!3d],!4d" },
+ { kX86Test32RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4}, "Test32RI", "!0r,!1d" },
+ { kX86Test32MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4}, "Test32MI", "[!0r+!1d],!2d" },
+ { kX86Test32AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4}, "Test32AI", "[!0r+!1r<<!2d+!3d],!4d" },
+ { kX86Test32RR, kRegReg, IS_BINARY_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0x85, 0, 0, 0, 0, 0}, "Test32RR", "!0r,!1r" },
+
+#define UNARY_ENCODING_MAP(opname, modrm, is_store, sets_ccodes, \
+ reg, reg_kind, reg_flags, \
+ mem, mem_kind, mem_flags, \
+ arr, arr_kind, arr_flags, imm, \
+ b_flags, hw_flags, w_flags, \
+ b_format, hw_format, w_format) \
+{ kX86 ## opname ## 8 ## reg, reg_kind, reg_flags | b_flags | sets_ccodes, { 0, 0, 0xF6, 0, 0, modrm, 0, imm << 0}, #opname "8" #reg, #b_format "!0r" }, \
+{ kX86 ## opname ## 8 ## mem, mem_kind, IS_LOAD | is_store | mem_flags | b_flags | sets_ccodes, { 0, 0, 0xF6, 0, 0, modrm, 0, imm << 0}, #opname "8" #mem, #b_format "[!0r+!1d]" }, \
+{ kX86 ## opname ## 8 ## arr, arr_kind, IS_LOAD | is_store | arr_flags | b_flags | sets_ccodes, { 0, 0, 0xF6, 0, 0, modrm, 0, imm << 0}, #opname "8" #arr, #b_format "[!0r+!1r<<!2d+!3d]" }, \
+{ kX86 ## opname ## 16 ## reg, reg_kind, reg_flags | hw_flags | sets_ccodes, { 0x66, 0, 0xF7, 0, 0, modrm, 0, imm << 1}, #opname "16" #reg, #hw_format "!0r" }, \
+{ kX86 ## opname ## 16 ## mem, mem_kind, IS_LOAD | is_store | mem_flags | hw_flags | sets_ccodes, { 0x66, 0, 0xF7, 0, 0, modrm, 0, imm << 1}, #opname "16" #mem, #hw_format "[!0r+!1d]" }, \
+{ kX86 ## opname ## 16 ## arr, arr_kind, IS_LOAD | is_store | arr_flags | hw_flags | sets_ccodes, { 0x66, 0, 0xF7, 0, 0, modrm, 0, imm << 1}, #opname "16" #arr, #hw_format "[!0r+!1r<<!2d+!3d]" }, \
+{ kX86 ## opname ## 32 ## reg, reg_kind, reg_flags | w_flags | sets_ccodes, { 0, 0, 0xF7, 0, 0, modrm, 0, imm << 2}, #opname "32" #reg, #w_format "!0r" }, \
+{ kX86 ## opname ## 32 ## mem, mem_kind, IS_LOAD | is_store | mem_flags | w_flags | sets_ccodes, { 0, 0, 0xF7, 0, 0, modrm, 0, imm << 2}, #opname "32" #mem, #w_format "[!0r+!1d]" }, \
+{ kX86 ## opname ## 32 ## arr, arr_kind, IS_LOAD | is_store | arr_flags | w_flags | sets_ccodes, { 0, 0, 0xF7, 0, 0, modrm, 0, imm << 2}, #opname "32" #arr, #w_format "[!0r+!1r<<!2d+!3d]" }
+
+ UNARY_ENCODING_MAP(Not, 0x2, IS_STORE, 0, R, kReg, IS_UNARY_OP | REG_DEF0_USE0, M, kMem, IS_BINARY_OP | REG_USE0, A, kArray, IS_QUAD_OP | REG_USE01, 0, 0, 0, 0, "", "", ""),
+ UNARY_ENCODING_MAP(Neg, 0x3, IS_STORE, SETS_CCODES, R, kReg, IS_UNARY_OP | REG_DEF0_USE0, M, kMem, IS_BINARY_OP | REG_USE0, A, kArray, IS_QUAD_OP | REG_USE01, 0, 0, 0, 0, "", "", ""),
+
+ UNARY_ENCODING_MAP(Mul, 0x4, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEA, REG_DEFAD_USEA, "ax,al,", "dx:ax,ax,", "edx:eax,eax,"),
+ UNARY_ENCODING_MAP(Imul, 0x5, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEA, REG_DEFAD_USEA, "ax,al,", "dx:ax,ax,", "edx:eax,eax,"),
+ UNARY_ENCODING_MAP(Divmod, 0x6, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEAD, REG_DEFAD_USEAD, "ah:al,ax,", "dx:ax,dx:ax,", "edx:eax,edx:eax,"),
+ UNARY_ENCODING_MAP(Idivmod, 0x7, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEAD, REG_DEFAD_USEAD, "ah:al,ax,", "dx:ax,dx:ax,", "edx:eax,edx:eax,"),
+#undef UNARY_ENCODING_MAP
+
+#define EXT_0F_ENCODING_MAP(opname, prefix, opcode, reg_def) \
+{ kX86 ## opname ## RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE01, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RR", "!0r,!1r" }, \
+{ kX86 ## opname ## RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE01, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RM", "!0r,[!1r+!2d]" }, \
+{ kX86 ## opname ## RA, kRegArray, IS_LOAD | IS_QUIN_OP | reg_def | REG_USE012, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RA", "!0r,[!1r+!2r<<!3d+!4d]" }
+
+ EXT_0F_ENCODING_MAP(Movsd, 0xF2, 0x10, REG_DEF0),
+ { kX86MovsdMR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0xF2, 0, 0x0F, 0x11, 0, 0, 0, 0 }, "MovsdMR", "[!0r+!1d],!2r" },
+ { kX86MovsdAR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { 0xF2, 0, 0x0F, 0x11, 0, 0, 0, 0 }, "MovsdAR", "[!0r+!1r<<!2d+!3d],!4r" },
+
+ EXT_0F_ENCODING_MAP(Movss, 0xF3, 0x10, REG_DEF0),
+ { kX86MovssMR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0xF3, 0, 0x0F, 0x11, 0, 0, 0, 0 }, "MovssMR", "[!0r+!1d],!2r" },
+ { kX86MovssAR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { 0xF3, 0, 0x0F, 0x11, 0, 0, 0, 0 }, "MovssAR", "[!0r+!1r<<!2d+!3d],!4r" },
+
+ EXT_0F_ENCODING_MAP(Cvtsi2sd, 0xF2, 0x2A, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Cvtsi2ss, 0xF3, 0x2A, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Cvttsd2si, 0xF2, 0x2C, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Cvttss2si, 0xF3, 0x2C, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Cvtsd2si, 0xF2, 0x2D, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Cvtss2si, 0xF3, 0x2D, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Ucomisd, 0x66, 0x2E, SETS_CCODES),
+ EXT_0F_ENCODING_MAP(Ucomiss, 0x00, 0x2E, SETS_CCODES),
+ EXT_0F_ENCODING_MAP(Comisd, 0x66, 0x2F, SETS_CCODES),
+ EXT_0F_ENCODING_MAP(Comiss, 0x00, 0x2F, SETS_CCODES),
+ EXT_0F_ENCODING_MAP(Orps, 0x00, 0x56, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Xorps, 0x00, 0x57, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Addsd, 0xF2, 0x58, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Addss, 0xF3, 0x58, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Mulsd, 0xF2, 0x59, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Mulss, 0xF3, 0x59, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Cvtsd2ss, 0xF2, 0x5A, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Cvtss2sd, 0xF3, 0x5A, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Subsd, 0xF2, 0x5C, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Subss, 0xF3, 0x5C, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Divsd, 0xF2, 0x5E, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Divss, 0xF3, 0x5E, REG_DEF0),
+
+ { kX86PsrlqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 2, 0, 1 }, "PsrlqRI", "!0r,!1d" },
+ { kX86PsllqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 6, 0, 1 }, "PsllqRI", "!0r,!1d" },
+
+ EXT_0F_ENCODING_MAP(Movdxr, 0x66, 0x6E, REG_DEF0),
+ { kX86MovdrxRR, kRegRegStore, IS_BINARY_OP | REG_DEF0 | REG_USE01, { 0x66, 0, 0x0F, 0x7E, 0, 0, 0, 0 }, "MovdrxRR", "!0r,!1r" },
+ { kX86MovdrxMR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0x66, 0, 0x0F, 0x7E, 0, 0, 0, 0 }, "MovdrxMR", "[!0r+!1d],!2r" },
+ { kX86MovdrxAR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { 0x66, 0, 0x0F, 0x7E, 0, 0, 0, 0 }, "MovdrxAR", "[!0r+!1r<<!2d+!3d],!4r" },
+
+ { kX86Set8R, kRegCond, IS_BINARY_OP | REG_DEF0 | USES_CCODES, { 0, 0, 0x0F, 0x90, 0, 0, 0, 0 }, "Set8R", "!1c !0r" },
+ { kX86Set8M, kMemCond, IS_STORE | IS_TERTIARY_OP | REG_USE0 | USES_CCODES, { 0, 0, 0x0F, 0x90, 0, 0, 0, 0 }, "Set8M", "!2c [!0r+!1d]" },
+ { kX86Set8A, kArrayCond, IS_STORE | IS_QUIN_OP | REG_USE01 | USES_CCODES, { 0, 0, 0x0F, 0x90, 0, 0, 0, 0 }, "Set8A", "!4c [!0r+!1r<<!2d+!3d]" },
+
+ // TODO: load/store?
+ // Encode the modrm opcode as an extra opcode byte to avoid computation during assembly.
+ { kX86Mfence, kReg, NO_OPERAND, { 0, 0, 0x0F, 0xAE, 0, 6, 0, 0 }, "Mfence", "" },
+
+ EXT_0F_ENCODING_MAP(Imul16, 0x66, 0xAF, REG_DEF0 | SETS_CCODES),
+ EXT_0F_ENCODING_MAP(Imul32, 0x00, 0xAF, REG_DEF0 | SETS_CCODES),
+
+ { kX86CmpxchgRR, kRegRegStore, IS_BINARY_OP | REG_DEF0 | REG_USE01 | REG_DEFA_USEA | SETS_CCODES, { 0, 0, 0x0F, 0xB1, 0, 0, 0, 0 }, "Cmpxchg", "!0r,!1r" },
+ { kX86CmpxchgMR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02 | REG_DEFA_USEA | SETS_CCODES, { 0, 0, 0x0F, 0xB1, 0, 0, 0, 0 }, "Cmpxchg", "[!0r+!1d],!2r" },
+ { kX86CmpxchgAR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014 | REG_DEFA_USEA | SETS_CCODES, { 0, 0, 0x0F, 0xB1, 0, 0, 0, 0 }, "Cmpxchg", "[!0r+!1r<<!2d+!3d],!4r" },
+ { kX86LockCmpxchgRR, kRegRegStore, IS_BINARY_OP | REG_DEF0 | REG_USE01 | REG_DEFA_USEA | SETS_CCODES, { 0xF0, 0, 0x0F, 0xB1, 0, 0, 0, 0 }, "Lock Cmpxchg", "!0r,!1r" },
+ { kX86LockCmpxchgMR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02 | REG_DEFA_USEA | SETS_CCODES, { 0xF0, 0, 0x0F, 0xB1, 0, 0, 0, 0 }, "Lock Cmpxchg", "[!0r+!1d],!2r" },
+ { kX86LockCmpxchgAR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014 | REG_DEFA_USEA | SETS_CCODES, { 0xF0, 0, 0x0F, 0xB1, 0, 0, 0, 0 }, "Lock Cmpxchg", "[!0r+!1r<<!2d+!3d],!4r" },
+
+ EXT_0F_ENCODING_MAP(Movzx8, 0x00, 0xB6, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Movzx16, 0x00, 0xB7, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Movsx8, 0x00, 0xBE, REG_DEF0),
+ EXT_0F_ENCODING_MAP(Movsx16, 0x00, 0xBF, REG_DEF0),
+#undef EXT_0F_ENCODING_MAP
+
+ { kX86Jcc8, kJcc, IS_BINARY_OP | IS_BRANCH | NEEDS_FIXUP | USES_CCODES, { 0, 0, 0x70, 0, 0, 0, 0, 0 }, "Jcc8", "!1c !0t" },
+ { kX86Jcc32, kJcc, IS_BINARY_OP | IS_BRANCH | NEEDS_FIXUP | USES_CCODES, { 0, 0, 0x0F, 0x80, 0, 0, 0, 0 }, "Jcc32", "!1c !0t" },
+ { kX86Jmp8, kJmp, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP, { 0, 0, 0xEB, 0, 0, 0, 0, 0 }, "Jmp8", "!0t" },
+ { kX86Jmp32, kJmp, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP, { 0, 0, 0xE9, 0, 0, 0, 0, 0 }, "Jmp32", "!0t" },
+ { kX86JmpR, kJmp, IS_UNARY_OP | IS_BRANCH | REG_USE0, { 0, 0, 0xFF, 0, 0, 4, 0, 0 }, "JmpR", "!0r" },
+ { kX86CallR, kCall, IS_UNARY_OP | IS_BRANCH | REG_USE0, { 0, 0, 0xE8, 0, 0, 0, 0, 0 }, "CallR", "!0r" },
+ { kX86CallM, kCall, IS_BINARY_OP | IS_BRANCH | IS_LOAD | REG_USE0, { 0, 0, 0xFF, 0, 0, 2, 0, 0 }, "CallM", "[!0r+!1d]" },
+ { kX86CallA, kCall, IS_QUAD_OP | IS_BRANCH | IS_LOAD | REG_USE01, { 0, 0, 0xFF, 0, 0, 2, 0, 0 }, "CallA", "[!0r+!1r<<!2d+!3d]" },
+ { kX86CallT, kCall, IS_UNARY_OP | IS_BRANCH | IS_LOAD, { THREAD_PREFIX, 0, 0xFF, 0, 0, 2, 0, 0 }, "CallT", "fs:[!0d]" },
+ { kX86Ret, kNullary,NO_OPERAND | IS_BRANCH, { 0, 0, 0xC3, 0, 0, 0, 0, 0 }, "Ret", "" },
+
+ { kX86StartOfMethod, kMacro, IS_UNARY_OP | SETS_CCODES, { 0, 0, 0, 0, 0, 0, 0, 0 }, "StartOfMethod", "!0r" },
+ { kX86PcRelLoadRA, kPcRel, IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8B, 0, 0, 0, 0, 0 }, "PcRelLoadRA", "!0r,[!1r+!2r<<!3d+!4p]" },
+ { kX86PcRelAdr, kPcRel, IS_LOAD | IS_BINARY_OP | REG_DEF0, { 0, 0, 0xB8, 0, 0, 0, 0, 4 }, "PcRelAdr", "!0r,!1d" },
+};
+
+static size_t ComputeSize(const X86EncodingMap* entry, int base, int displacement, bool has_sib) {
+ size_t size = 0;
+ if (entry->skeleton.prefix1 > 0) {
+ ++size;
+ if (entry->skeleton.prefix2 > 0) {
+ ++size;
+ }
+ }
+ ++size; // opcode
+ if (entry->skeleton.opcode == 0x0F) {
+ ++size;
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
+ ++size;
+ }
+ }
+ ++size; // modrm
+ if (has_sib || base == rX86_SP) {
+ // SP requires a SIB byte.
+ ++size;
+ }
+ if (displacement != 0 || base == rBP) {
+ // BP requires an explicit displacement, even when it's 0.
+ if (entry->opcode != kX86Lea32RA) {
+ DCHECK_NE(entry->flags & (IS_LOAD | IS_STORE), 0ULL) << entry->name;
+ }
+ size += IS_SIMM8(displacement) ? 1 : 4;
+ }
+ size += entry->skeleton.immediate_bytes;
+ return size;
+}
+
+int X86Mir2Lir::GetInsnSize(LIR* lir) {
+ const X86EncodingMap* entry = &X86Mir2Lir::EncodingMap[lir->opcode];
+ switch (entry->kind) {
+ case kData:
+ return 4; // 4 bytes of data
+ case kNop:
+ return lir->operands[0]; // length of nop is sole operand
+ case kNullary:
+ return 1; // 1 byte of opcode
+ case kReg: // lir operands - 0: reg
+ return ComputeSize(entry, 0, 0, false);
+ case kMem: // lir operands - 0: base, 1: disp
+ return ComputeSize(entry, lir->operands[0], lir->operands[1], false);
+ case kArray: // lir operands - 0: base, 1: index, 2: scale, 3: disp
+ return ComputeSize(entry, lir->operands[0], lir->operands[3], true);
+ case kMemReg: // lir operands - 0: base, 1: disp, 2: reg
+ return ComputeSize(entry, lir->operands[0], lir->operands[1], false);
+ case kArrayReg: // lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: reg
+ return ComputeSize(entry, lir->operands[0], lir->operands[3], true);
+ case kThreadReg: // lir operands - 0: disp, 1: reg
+ return ComputeSize(entry, 0, lir->operands[0], false);
+ case kRegReg:
+ return ComputeSize(entry, 0, 0, false);
+ case kRegRegStore:
+ return ComputeSize(entry, 0, 0, false);
+ case kRegMem: // lir operands - 0: reg, 1: base, 2: disp
+ return ComputeSize(entry, lir->operands[1], lir->operands[2], false);
+ case kRegArray: // lir operands - 0: reg, 1: base, 2: index, 3: scale, 4: disp
+ return ComputeSize(entry, lir->operands[1], lir->operands[4], true);
+ case kRegThread: // lir operands - 0: reg, 1: disp
+ return ComputeSize(entry, 0, 0x12345678, false); // displacement size is always 32bit
+ case kRegImm: { // lir operands - 0: reg, 1: immediate
+ size_t size = ComputeSize(entry, 0, 0, false);
+ if (entry->skeleton.ax_opcode == 0) {
+ return size;
+ } else {
+ // AX opcodes don't require the modrm byte.
+ int reg = lir->operands[0];
+ return size - (reg == rAX ? 1 : 0);
+ }
+ }
+ case kMemImm: // lir operands - 0: base, 1: disp, 2: immediate
+ return ComputeSize(entry, lir->operands[0], lir->operands[1], false);
+ case kArrayImm: // lir operands - 0: base, 1: index, 2: scale, 3: disp 4: immediate
+ return ComputeSize(entry, lir->operands[0], lir->operands[3], true);
+ case kThreadImm: // lir operands - 0: disp, 1: imm
+ return ComputeSize(entry, 0, 0x12345678, false); // displacement size is always 32bit
+ case kRegRegImm: // lir operands - 0: reg, 1: reg, 2: imm
+ return ComputeSize(entry, 0, 0, false);
+ case kRegMemImm: // lir operands - 0: reg, 1: base, 2: disp, 3: imm
+ return ComputeSize(entry, lir->operands[1], lir->operands[2], false);
+ case kRegArrayImm: // lir operands - 0: reg, 1: base, 2: index, 3: scale, 4: disp, 5: imm
+ return ComputeSize(entry, lir->operands[1], lir->operands[4], true);
+ case kMovRegImm: // lir operands - 0: reg, 1: immediate
+ return 1 + entry->skeleton.immediate_bytes;
+ case kShiftRegImm: // lir operands - 0: reg, 1: immediate
+ // Shift by immediate one has a shorter opcode.
+ return ComputeSize(entry, 0, 0, false) - (lir->operands[1] == 1 ? 1 : 0);
+ case kShiftMemImm: // lir operands - 0: base, 1: disp, 2: immediate
+ // Shift by immediate one has a shorter opcode.
+ return ComputeSize(entry, lir->operands[0], lir->operands[1], false) -
+ (lir->operands[2] == 1 ? 1 : 0);
+ case kShiftArrayImm: // lir operands - 0: base, 1: index, 2: scale, 3: disp 4: immediate
+ // Shift by immediate one has a shorter opcode.
+ return ComputeSize(entry, lir->operands[0], lir->operands[3], true) -
+ (lir->operands[4] == 1 ? 1 : 0);
+ case kShiftRegCl:
+ return ComputeSize(entry, 0, 0, false);
+ case kShiftMemCl: // lir operands - 0: base, 1: disp, 2: cl
+ return ComputeSize(entry, lir->operands[0], lir->operands[1], false);
+ case kShiftArrayCl: // lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: reg
+ return ComputeSize(entry, lir->operands[0], lir->operands[3], true);
+ case kRegCond: // lir operands - 0: reg, 1: cond
+ return ComputeSize(entry, 0, 0, false);
+ case kMemCond: // lir operands - 0: base, 1: disp, 2: cond
+ return ComputeSize(entry, lir->operands[0], lir->operands[1], false);
+ case kArrayCond: // lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: cond
+ return ComputeSize(entry, lir->operands[0], lir->operands[3], true);
+ case kJcc:
+ if (lir->opcode == kX86Jcc8) {
+ return 2; // opcode + rel8
+ } else {
+ DCHECK(lir->opcode == kX86Jcc32);
+ return 6; // 2 byte opcode + rel32
+ }
+ case kJmp:
+ if (lir->opcode == kX86Jmp8) {
+ return 2; // opcode + rel8
+ } else if (lir->opcode == kX86Jmp32) {
+ return 5; // opcode + rel32
+ } else {
+ DCHECK(lir->opcode == kX86JmpR);
+ return 2; // opcode + modrm
+ }
+ case kCall:
+ switch (lir->opcode) {
+ case kX86CallR: return 2; // opcode modrm
+ case kX86CallM: // lir operands - 0: base, 1: disp
+ return ComputeSize(entry, lir->operands[0], lir->operands[1], false);
+ case kX86CallA: // lir operands - 0: base, 1: index, 2: scale, 3: disp
+ return ComputeSize(entry, lir->operands[0], lir->operands[3], true);
+ case kX86CallT: // lir operands - 0: disp
+ return ComputeSize(entry, 0, 0x12345678, false); // displacement size is always 32bit
+ default:
+ break;
+ }
+ break;
+ case kPcRel:
+ if (entry->opcode == kX86PcRelLoadRA) {
+ // lir operands - 0: reg, 1: base, 2: index, 3: scale, 4: table
+ return ComputeSize(entry, lir->operands[1], 0x12345678, true);
+ } else {
+ DCHECK(entry->opcode == kX86PcRelAdr);
+ return 5; // opcode with reg + 4 byte immediate
+ }
+ case kMacro:
+ DCHECK_EQ(lir->opcode, static_cast<int>(kX86StartOfMethod));
+ return 5 /* call opcode + 4 byte displacement */ + 1 /* pop reg */ +
+ ComputeSize(&X86Mir2Lir::EncodingMap[kX86Sub32RI], 0, 0, false) -
+ (lir->operands[0] == rAX ? 1 : 0); // shorter ax encoding
+ default:
+ break;
+ }
+ UNIMPLEMENTED(FATAL) << "Unimplemented size encoding for: " << entry->name;
+ return 0;
+}
+
+static uint8_t ModrmForDisp(int base, int disp) {
+ // BP requires an explicit disp, so do not omit it in the 0 case
+ if (disp == 0 && base != rBP) {
+ return 0;
+ } else if (IS_SIMM8(disp)) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+
+void X86Mir2Lir::EmitDisp(int base, int disp) {
+ // BP requires an explicit disp, so do not omit it in the 0 case
+ if (disp == 0 && base != rBP) {
+ return;
+ } else if (IS_SIMM8(disp)) {
+ code_buffer_.push_back(disp & 0xFF);
+ } else {
+ code_buffer_.push_back(disp & 0xFF);
+ code_buffer_.push_back((disp >> 8) & 0xFF);
+ code_buffer_.push_back((disp >> 16) & 0xFF);
+ code_buffer_.push_back((disp >> 24) & 0xFF);
+ }
+}
+
+void X86Mir2Lir::EmitOpReg(const X86EncodingMap* entry, uint8_t reg) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ if (X86_FPREG(reg)) {
+ reg = reg & X86_FP_REG_MASK;
+ }
+ if (reg >= 4) {
+ DCHECK(strchr(entry->name, '8') == NULL) << entry->name << " " << static_cast<int>(reg)
+ << " in " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
+ }
+ DCHECK_LT(reg, 8);
+ uint8_t modrm = (3 << 6) | (entry->skeleton.modrm_opcode << 3) | reg;
+ code_buffer_.push_back(modrm);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
+void X86Mir2Lir::EmitOpMem(const X86EncodingMap* entry, uint8_t base, int disp) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ DCHECK_LT(entry->skeleton.modrm_opcode, 8);
+ DCHECK_LT(base, 8);
+ uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (entry->skeleton.modrm_opcode << 3) | base;
+ code_buffer_.push_back(modrm);
+ EmitDisp(base, disp);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
+void X86Mir2Lir::EmitMemReg(const X86EncodingMap* entry,
+ uint8_t base, int disp, uint8_t reg) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ if (X86_FPREG(reg)) {
+ reg = reg & X86_FP_REG_MASK;
+ }
+ if (reg >= 4) {
+ DCHECK(strchr(entry->name, '8') == NULL) << entry->name << " " << static_cast<int>(reg)
+ << " in " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
+ }
+ DCHECK_LT(reg, 8);
+ DCHECK_LT(base, 8);
+ uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (reg << 3) | base;
+ code_buffer_.push_back(modrm);
+ if (base == rX86_SP) {
+ // Special SIB for SP base
+ code_buffer_.push_back(0 << 6 | (rX86_SP << 3) | rX86_SP);
+ }
+ EmitDisp(base, disp);
+ DCHECK_EQ(0, entry->skeleton.modrm_opcode);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
+void X86Mir2Lir::EmitRegMem(const X86EncodingMap* entry,
+ uint8_t reg, uint8_t base, int disp) {
+ // Opcode will flip operands.
+ EmitMemReg(entry, base, disp, reg);
+}
+
+void X86Mir2Lir::EmitRegArray(const X86EncodingMap* entry, uint8_t reg, uint8_t base, uint8_t index,
+ int scale, int disp) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ if (X86_FPREG(reg)) {
+ reg = reg & X86_FP_REG_MASK;
+ }
+ DCHECK_LT(reg, 8);
+ uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (reg << 3) | rX86_SP;
+ code_buffer_.push_back(modrm);
+ DCHECK_LT(scale, 4);
+ DCHECK_LT(index, 8);
+ DCHECK_LT(base, 8);
+ uint8_t sib = (scale << 6) | (index << 3) | base;
+ code_buffer_.push_back(sib);
+ EmitDisp(base, disp);
+ DCHECK_EQ(0, entry->skeleton.modrm_opcode);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
+void X86Mir2Lir::EmitArrayReg(const X86EncodingMap* entry, uint8_t base, uint8_t index, int scale, int disp,
+ uint8_t reg) {
+ // Opcode will flip operands.
+ EmitRegArray(entry, reg, base, index, scale, disp);
+}
+
+void X86Mir2Lir::EmitRegThread(const X86EncodingMap* entry, uint8_t reg, int disp) {
+ DCHECK_NE(entry->skeleton.prefix1, 0);
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ if (X86_FPREG(reg)) {
+ reg = reg & X86_FP_REG_MASK;
+ }
+ if (reg >= 4) {
+ DCHECK(strchr(entry->name, '8') == NULL) << entry->name << " " << static_cast<int>(reg)
+ << " in " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
+ }
+ DCHECK_LT(reg, 8);
+ uint8_t modrm = (0 << 6) | (reg << 3) | rBP;
+ code_buffer_.push_back(modrm);
+ code_buffer_.push_back(disp & 0xFF);
+ code_buffer_.push_back((disp >> 8) & 0xFF);
+ code_buffer_.push_back((disp >> 16) & 0xFF);
+ code_buffer_.push_back((disp >> 24) & 0xFF);
+ DCHECK_EQ(0, entry->skeleton.modrm_opcode);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
+void X86Mir2Lir::EmitRegReg(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ if (X86_FPREG(reg1)) {
+ reg1 = reg1 & X86_FP_REG_MASK;
+ }
+ if (X86_FPREG(reg2)) {
+ reg2 = reg2 & X86_FP_REG_MASK;
+ }
+ DCHECK_LT(reg1, 8);
+ DCHECK_LT(reg2, 8);
+ uint8_t modrm = (3 << 6) | (reg1 << 3) | reg2;
+ code_buffer_.push_back(modrm);
+ DCHECK_EQ(0, entry->skeleton.modrm_opcode);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
+void X86Mir2Lir::EmitRegRegImm(const X86EncodingMap* entry,
+ uint8_t reg1, uint8_t reg2, int32_t imm) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ if (X86_FPREG(reg1)) {
+ reg1 = reg1 & X86_FP_REG_MASK;
+ }
+ if (X86_FPREG(reg2)) {
+ reg2 = reg2 & X86_FP_REG_MASK;
+ }
+ DCHECK_LT(reg1, 8);
+ DCHECK_LT(reg2, 8);
+ uint8_t modrm = (3 << 6) | (reg1 << 3) | reg2;
+ code_buffer_.push_back(modrm);
+ DCHECK_EQ(0, entry->skeleton.modrm_opcode);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ switch (entry->skeleton.immediate_bytes) {
+ case 1:
+ DCHECK(IS_SIMM8(imm));
+ code_buffer_.push_back(imm & 0xFF);
+ break;
+ case 2:
+ DCHECK(IS_SIMM16(imm));
+ code_buffer_.push_back(imm & 0xFF);
+ code_buffer_.push_back((imm >> 8) & 0xFF);
+ break;
+ case 4:
+ code_buffer_.push_back(imm & 0xFF);
+ code_buffer_.push_back((imm >> 8) & 0xFF);
+ code_buffer_.push_back((imm >> 16) & 0xFF);
+ code_buffer_.push_back((imm >> 24) & 0xFF);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected immediate bytes (" << entry->skeleton.immediate_bytes
+ << ") for instruction: " << entry->name;
+ break;
+ }
+}
+
+void X86Mir2Lir::EmitRegImm(const X86EncodingMap* entry, uint8_t reg, int imm) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ if (reg == rAX && entry->skeleton.ax_opcode != 0) {
+ code_buffer_.push_back(entry->skeleton.ax_opcode);
+ } else {
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ if (X86_FPREG(reg)) {
+ reg = reg & X86_FP_REG_MASK;
+ }
+ uint8_t modrm = (3 << 6) | (entry->skeleton.modrm_opcode << 3) | reg;
+ code_buffer_.push_back(modrm);
+ }
+ switch (entry->skeleton.immediate_bytes) {
+ case 1:
+ DCHECK(IS_SIMM8(imm));
+ code_buffer_.push_back(imm & 0xFF);
+ break;
+ case 2:
+ DCHECK(IS_SIMM16(imm));
+ code_buffer_.push_back(imm & 0xFF);
+ code_buffer_.push_back((imm >> 8) & 0xFF);
+ break;
+ case 4:
+ code_buffer_.push_back(imm & 0xFF);
+ code_buffer_.push_back((imm >> 8) & 0xFF);
+ code_buffer_.push_back((imm >> 16) & 0xFF);
+ code_buffer_.push_back((imm >> 24) & 0xFF);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected immediate bytes (" << entry->skeleton.immediate_bytes
+ << ") for instruction: " << entry->name;
+ break;
+ }
+}
+
+void X86Mir2Lir::EmitThreadImm(const X86EncodingMap* entry, int disp, int imm) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ uint8_t modrm = (0 << 6) | (entry->skeleton.modrm_opcode << 3) | rBP;
+ code_buffer_.push_back(modrm);
+ code_buffer_.push_back(disp & 0xFF);
+ code_buffer_.push_back((disp >> 8) & 0xFF);
+ code_buffer_.push_back((disp >> 16) & 0xFF);
+ code_buffer_.push_back((disp >> 24) & 0xFF);
+ switch (entry->skeleton.immediate_bytes) {
+ case 1:
+ DCHECK(IS_SIMM8(imm));
+ code_buffer_.push_back(imm & 0xFF);
+ break;
+ case 2:
+ DCHECK(IS_SIMM16(imm));
+ code_buffer_.push_back(imm & 0xFF);
+ code_buffer_.push_back((imm >> 8) & 0xFF);
+ break;
+ case 4:
+ code_buffer_.push_back(imm & 0xFF);
+ code_buffer_.push_back((imm >> 8) & 0xFF);
+ code_buffer_.push_back((imm >> 16) & 0xFF);
+ code_buffer_.push_back((imm >> 24) & 0xFF);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected immediate bytes (" << entry->skeleton.immediate_bytes
+ << ") for instruction: " << entry->name;
+ break;
+ }
+ DCHECK_EQ(entry->skeleton.ax_opcode, 0);
+}
+
+void X86Mir2Lir::EmitMovRegImm(const X86EncodingMap* entry, uint8_t reg, int imm) {
+ DCHECK_LT(reg, 8);
+ code_buffer_.push_back(0xB8 + reg);
+ code_buffer_.push_back(imm & 0xFF);
+ code_buffer_.push_back((imm >> 8) & 0xFF);
+ code_buffer_.push_back((imm >> 16) & 0xFF);
+ code_buffer_.push_back((imm >> 24) & 0xFF);
+}
+
+void X86Mir2Lir::EmitShiftRegImm(const X86EncodingMap* entry, uint8_t reg, int imm) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ if (imm != 1) {
+ code_buffer_.push_back(entry->skeleton.opcode);
+ } else {
+ // Shorter encoding for 1 bit shift
+ code_buffer_.push_back(entry->skeleton.ax_opcode);
+ }
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ if (reg >= 4) {
+ DCHECK(strchr(entry->name, '8') == NULL) << entry->name << " " << static_cast<int>(reg)
+ << " in " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
+ }
+ DCHECK_LT(reg, 8);
+ uint8_t modrm = (3 << 6) | (entry->skeleton.modrm_opcode << 3) | reg;
+ code_buffer_.push_back(modrm);
+ if (imm != 1) {
+ DCHECK_EQ(entry->skeleton.immediate_bytes, 1);
+ DCHECK(IS_SIMM8(imm));
+ code_buffer_.push_back(imm & 0xFF);
+ }
+}
+
+void X86Mir2Lir::EmitShiftRegCl(const X86EncodingMap* entry, uint8_t reg, uint8_t cl) {
+ DCHECK_EQ(cl, static_cast<uint8_t>(rCX));
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ DCHECK_LT(reg, 8);
+ uint8_t modrm = (3 << 6) | (entry->skeleton.modrm_opcode << 3) | reg;
+ code_buffer_.push_back(modrm);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
+void X86Mir2Lir::EmitRegCond(const X86EncodingMap* entry, uint8_t reg, uint8_t condition) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0x0F, entry->skeleton.opcode);
+ code_buffer_.push_back(0x0F);
+ DCHECK_EQ(0x90, entry->skeleton.extra_opcode1);
+ code_buffer_.push_back(0x90 | condition);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ DCHECK_LT(reg, 8);
+ uint8_t modrm = (3 << 6) | (entry->skeleton.modrm_opcode << 3) | reg;
+ code_buffer_.push_back(modrm);
+ DCHECK_EQ(entry->skeleton.immediate_bytes, 0);
+}
+
+void X86Mir2Lir::EmitJmp(const X86EncodingMap* entry, int rel) {
+ if (entry->opcode == kX86Jmp8) {
+ DCHECK(IS_SIMM8(rel));
+ code_buffer_.push_back(0xEB);
+ code_buffer_.push_back(rel & 0xFF);
+ } else if (entry->opcode == kX86Jmp32) {
+ code_buffer_.push_back(0xE9);
+ code_buffer_.push_back(rel & 0xFF);
+ code_buffer_.push_back((rel >> 8) & 0xFF);
+ code_buffer_.push_back((rel >> 16) & 0xFF);
+ code_buffer_.push_back((rel >> 24) & 0xFF);
+ } else {
+ DCHECK(entry->opcode == kX86JmpR);
+ code_buffer_.push_back(entry->skeleton.opcode);
+ uint8_t reg = static_cast<uint8_t>(rel);
+ DCHECK_LT(reg, 8);
+ uint8_t modrm = (3 << 6) | (entry->skeleton.modrm_opcode << 3) | reg;
+ code_buffer_.push_back(modrm);
+ }
+}
+
+void X86Mir2Lir::EmitJcc(const X86EncodingMap* entry, int rel, uint8_t cc) {
+ DCHECK_LT(cc, 16);
+ if (entry->opcode == kX86Jcc8) {
+ DCHECK(IS_SIMM8(rel));
+ code_buffer_.push_back(0x70 | cc);
+ code_buffer_.push_back(rel & 0xFF);
+ } else {
+ DCHECK(entry->opcode == kX86Jcc32);
+ code_buffer_.push_back(0x0F);
+ code_buffer_.push_back(0x80 | cc);
+ code_buffer_.push_back(rel & 0xFF);
+ code_buffer_.push_back((rel >> 8) & 0xFF);
+ code_buffer_.push_back((rel >> 16) & 0xFF);
+ code_buffer_.push_back((rel >> 24) & 0xFF);
+ }
+}
+
+void X86Mir2Lir::EmitCallMem(const X86EncodingMap* entry, uint8_t base, int disp) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ uint8_t modrm = (ModrmForDisp(base, disp) << 6) | (entry->skeleton.modrm_opcode << 3) | base;
+ code_buffer_.push_back(modrm);
+ if (base == rX86_SP) {
+ // Special SIB for SP base
+ code_buffer_.push_back(0 << 6 | (rX86_SP << 3) | rX86_SP);
+ }
+ EmitDisp(base, disp);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
+void X86Mir2Lir::EmitCallThread(const X86EncodingMap* entry, int disp) {
+ DCHECK_NE(entry->skeleton.prefix1, 0);
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ uint8_t modrm = (0 << 6) | (entry->skeleton.modrm_opcode << 3) | rBP;
+ code_buffer_.push_back(modrm);
+ code_buffer_.push_back(disp & 0xFF);
+ code_buffer_.push_back((disp >> 8) & 0xFF);
+ code_buffer_.push_back((disp >> 16) & 0xFF);
+ code_buffer_.push_back((disp >> 24) & 0xFF);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
+void X86Mir2Lir::EmitPcRel(const X86EncodingMap* entry, uint8_t reg,
+ int base_or_table, uint8_t index, int scale, int table_or_disp) {
+ int disp;
+ if (entry->opcode == kX86PcRelLoadRA) {
+ Mir2Lir::SwitchTable *tab_rec = reinterpret_cast<Mir2Lir::SwitchTable*>(table_or_disp);
+ disp = tab_rec->offset;
+ } else {
+ DCHECK(entry->opcode == kX86PcRelAdr);
+ Mir2Lir::FillArrayData *tab_rec = reinterpret_cast<Mir2Lir::FillArrayData*>(base_or_table);
+ disp = tab_rec->offset;
+ }
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ if (X86_FPREG(reg)) {
+ reg = reg & X86_FP_REG_MASK;
+ }
+ DCHECK_LT(reg, 8);
+ if (entry->opcode == kX86PcRelLoadRA) {
+ code_buffer_.push_back(entry->skeleton.opcode);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ uint8_t modrm = (2 << 6) | (reg << 3) | rX86_SP;
+ code_buffer_.push_back(modrm);
+ DCHECK_LT(scale, 4);
+ DCHECK_LT(index, 8);
+ DCHECK_LT(base_or_table, 8);
+ uint8_t base = static_cast<uint8_t>(base_or_table);
+ uint8_t sib = (scale << 6) | (index << 3) | base;
+ code_buffer_.push_back(sib);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+ } else {
+ code_buffer_.push_back(entry->skeleton.opcode + reg);
+ }
+ code_buffer_.push_back(disp & 0xFF);
+ code_buffer_.push_back((disp >> 8) & 0xFF);
+ code_buffer_.push_back((disp >> 16) & 0xFF);
+ code_buffer_.push_back((disp >> 24) & 0xFF);
+ DCHECK_EQ(0, entry->skeleton.modrm_opcode);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+}
+
+void X86Mir2Lir::EmitMacro(const X86EncodingMap* entry, uint8_t reg, int offset) {
+ DCHECK(entry->opcode == kX86StartOfMethod) << entry->name;
+ code_buffer_.push_back(0xE8); // call +0
+ code_buffer_.push_back(0);
+ code_buffer_.push_back(0);
+ code_buffer_.push_back(0);
+ code_buffer_.push_back(0);
+
+ DCHECK_LT(reg, 8);
+ code_buffer_.push_back(0x58 + reg); // pop reg
+
+ EmitRegImm(&X86Mir2Lir::EncodingMap[kX86Sub32RI], reg, offset + 5 /* size of call +0 */);
+}
+
+void X86Mir2Lir::EmitUnimplemented(const X86EncodingMap* entry, LIR* lir) {
+ UNIMPLEMENTED(WARNING) << "encoding kind for " << entry->name << " "
+ << BuildInsnString(entry->fmt, lir, 0);
+ for (int i = 0; i < GetInsnSize(lir); ++i) {
+ code_buffer_.push_back(0xCC); // push breakpoint instruction - int 3
+ }
+}
+
+/*
+ * Assemble the LIR into binary instruction format. Note that we may
+ * discover that pc-relative displacements may not fit the selected
+ * instruction. In those cases we will try to substitute a new code
+ * sequence or request that the trace be shortened and retried.
+ */
+AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) {
+ LIR *lir;
+ AssemblerStatus res = kSuccess; // Assume success
+
+ const bool kVerbosePcFixup = false;
+ for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
+ if (lir->opcode < 0) {
+ continue;
+ }
+
+ if (lir->flags.is_nop) {
+ continue;
+ }
+
+ if (lir->flags.pcRelFixup) {
+ switch (lir->opcode) {
+ case kX86Jcc8: {
+ LIR *target_lir = lir->target;
+ DCHECK(target_lir != NULL);
+ int delta = 0;
+ uintptr_t pc;
+ if (IS_SIMM8(lir->operands[0])) {
+ pc = lir->offset + 2 /* opcode + rel8 */;
+ } else {
+ pc = lir->offset + 6 /* 2 byte opcode + rel32 */;
+ }
+ uintptr_t target = target_lir->offset;
+ delta = target - pc;
+ if (IS_SIMM8(delta) != IS_SIMM8(lir->operands[0])) {
+ if (kVerbosePcFixup) {
+ LOG(INFO) << "Retry for JCC growth at " << lir->offset
+ << " delta: " << delta << " old delta: " << lir->operands[0];
+ }
+ lir->opcode = kX86Jcc32;
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ }
+ if (kVerbosePcFixup) {
+ LOG(INFO) << "Source:";
+ DumpLIRInsn(lir, 0);
+ LOG(INFO) << "Target:";
+ DumpLIRInsn(target_lir, 0);
+ LOG(INFO) << "Delta " << delta;
+ }
+ lir->operands[0] = delta;
+ break;
+ }
+ case kX86Jcc32: {
+ LIR *target_lir = lir->target;
+ DCHECK(target_lir != NULL);
+ uintptr_t pc = lir->offset + 6 /* 2 byte opcode + rel32 */;
+ uintptr_t target = target_lir->offset;
+ int delta = target - pc;
+ if (kVerbosePcFixup) {
+ LOG(INFO) << "Source:";
+ DumpLIRInsn(lir, 0);
+ LOG(INFO) << "Target:";
+ DumpLIRInsn(target_lir, 0);
+ LOG(INFO) << "Delta " << delta;
+ }
+ lir->operands[0] = delta;
+ break;
+ }
+ case kX86Jmp8: {
+ LIR *target_lir = lir->target;
+ DCHECK(target_lir != NULL);
+ int delta = 0;
+ uintptr_t pc;
+ if (IS_SIMM8(lir->operands[0])) {
+ pc = lir->offset + 2 /* opcode + rel8 */;
+ } else {
+ pc = lir->offset + 5 /* opcode + rel32 */;
+ }
+ uintptr_t target = target_lir->offset;
+ delta = target - pc;
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && delta == 0) {
+ // Useless branch
+ lir->flags.is_nop = true;
+ if (kVerbosePcFixup) {
+ LOG(INFO) << "Retry for useless branch at " << lir->offset;
+ }
+ res = kRetryAll;
+ } else if (IS_SIMM8(delta) != IS_SIMM8(lir->operands[0])) {
+ if (kVerbosePcFixup) {
+ LOG(INFO) << "Retry for JMP growth at " << lir->offset;
+ }
+ lir->opcode = kX86Jmp32;
+ SetupResourceMasks(lir);
+ res = kRetryAll;
+ }
+ lir->operands[0] = delta;
+ break;
+ }
+ case kX86Jmp32: {
+ LIR *target_lir = lir->target;
+ DCHECK(target_lir != NULL);
+ uintptr_t pc = lir->offset + 5 /* opcode + rel32 */;
+ uintptr_t target = target_lir->offset;
+ int delta = target - pc;
+ lir->operands[0] = delta;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /*
+ * If one of the pc-relative instructions expanded we'll have
+ * to make another pass. Don't bother to fully assemble the
+ * instruction.
+ */
+ if (res != kSuccess) {
+ continue;
+ }
+ CHECK_EQ(static_cast<size_t>(lir->offset), code_buffer_.size());
+ const X86EncodingMap *entry = &X86Mir2Lir::EncodingMap[lir->opcode];
+ size_t starting_cbuf_size = code_buffer_.size();
+ switch (entry->kind) {
+ case kData: // 4 bytes of data
+ code_buffer_.push_back(lir->operands[0]);
+ break;
+ case kNullary: // 1 byte of opcode
+ DCHECK_EQ(0, entry->skeleton.prefix1);
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.extra_opcode1 != 0) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ if (entry->skeleton.extra_opcode2 != 0) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ DCHECK_EQ(0, entry->skeleton.modrm_opcode);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+ break;
+ case kReg: // lir operands - 0: reg
+ EmitOpReg(entry, lir->operands[0]);
+ break;
+ case kMem: // lir operands - 0: base, 1: disp
+ EmitOpMem(entry, lir->operands[0], lir->operands[1]);
+ break;
+ case kMemReg: // lir operands - 0: base, 1: disp, 2: reg
+ EmitMemReg(entry, lir->operands[0], lir->operands[1], lir->operands[2]);
+ break;
+ case kArrayReg: // lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: reg
+ EmitArrayReg(entry, lir->operands[0], lir->operands[1], lir->operands[2],
+ lir->operands[3], lir->operands[4]);
+ break;
+ case kRegMem: // lir operands - 0: reg, 1: base, 2: disp
+ EmitRegMem(entry, lir->operands[0], lir->operands[1], lir->operands[2]);
+ break;
+ case kRegArray: // lir operands - 0: reg, 1: base, 2: index, 3: scale, 4: disp
+ EmitRegArray(entry, lir->operands[0], lir->operands[1], lir->operands[2],
+ lir->operands[3], lir->operands[4]);
+ break;
+ case kRegThread: // lir operands - 0: reg, 1: disp
+ EmitRegThread(entry, lir->operands[0], lir->operands[1]);
+ break;
+ case kRegReg: // lir operands - 0: reg1, 1: reg2
+ EmitRegReg(entry, lir->operands[0], lir->operands[1]);
+ break;
+ case kRegRegStore: // lir operands - 0: reg2, 1: reg1
+ EmitRegReg(entry, lir->operands[1], lir->operands[0]);
+ break;
+ case kRegRegImm:
+ EmitRegRegImm(entry, lir->operands[0], lir->operands[1], lir->operands[2]);
+ break;
+ case kRegImm: // lir operands - 0: reg, 1: immediate
+ EmitRegImm(entry, lir->operands[0], lir->operands[1]);
+ break;
+ case kThreadImm: // lir operands - 0: disp, 1: immediate
+ EmitThreadImm(entry, lir->operands[0], lir->operands[1]);
+ break;
+ case kMovRegImm: // lir operands - 0: reg, 1: immediate
+ EmitMovRegImm(entry, lir->operands[0], lir->operands[1]);
+ break;
+ case kShiftRegImm: // lir operands - 0: reg, 1: immediate
+ EmitShiftRegImm(entry, lir->operands[0], lir->operands[1]);
+ break;
+ case kShiftRegCl: // lir operands - 0: reg, 1: cl
+ EmitShiftRegCl(entry, lir->operands[0], lir->operands[1]);
+ break;
+ case kRegCond: // lir operands - 0: reg, 1: condition
+ EmitRegCond(entry, lir->operands[0], lir->operands[1]);
+ break;
+ case kJmp: // lir operands - 0: rel
+ EmitJmp(entry, lir->operands[0]);
+ break;
+ case kJcc: // lir operands - 0: rel, 1: CC, target assigned
+ EmitJcc(entry, lir->operands[0], lir->operands[1]);
+ break;
+ case kCall:
+ switch (entry->opcode) {
+ case kX86CallM: // lir operands - 0: base, 1: disp
+ EmitCallMem(entry, lir->operands[0], lir->operands[1]);
+ break;
+ case kX86CallT: // lir operands - 0: disp
+ EmitCallThread(entry, lir->operands[0]);
+ break;
+ default:
+ EmitUnimplemented(entry, lir);
+ break;
+ }
+ break;
+ case kPcRel: // lir operands - 0: reg, 1: base, 2: index, 3: scale, 4: table
+ EmitPcRel(entry, lir->operands[0], lir->operands[1], lir->operands[2],
+ lir->operands[3], lir->operands[4]);
+ break;
+ case kMacro:
+ EmitMacro(entry, lir->operands[0], lir->offset);
+ break;
+ default:
+ EmitUnimplemented(entry, lir);
+ break;
+ }
+ CHECK_EQ(static_cast<size_t>(GetInsnSize(lir)),
+ code_buffer_.size() - starting_cbuf_size)
+ << "Instruction size mismatch for entry: " << X86Mir2Lir::EncodingMap[lir->opcode].name;
+ }
+ return res;
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
new file mode 100644
index 0000000..d60be72
--- /dev/null
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/* This file contains codegen for the X86 ISA */
+
+#include "codegen_x86.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "x86_lir.h"
+
+namespace art {
+
+void X86Mir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
+ SpecialCaseHandler special_case)
+{
+ // TODO
+}
+
+/*
+ * The sparse table in the literal pool is an array of <key,displacement>
+ * pairs.
+ */
+void X86Mir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
+ RegLocation rl_src)
+{
+ const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ if (cu_->verbose) {
+ DumpSparseSwitchTable(table);
+ }
+ int entries = table[1];
+ const int* keys = reinterpret_cast<const int*>(&table[2]);
+ const int* targets = &keys[entries];
+ rl_src = LoadValue(rl_src, kCoreReg);
+ for (int i = 0; i < entries; i++) {
+ int key = keys[i];
+ BasicBlock* case_block =
+ mir_graph_->FindBlock(current_dalvik_offset_ + targets[i]);
+ OpCmpImmBranch(kCondEq, rl_src.low_reg, key,
+ &block_label_list_[case_block->id]);
+ }
+}
+
+/*
+ * Code pattern will look something like:
+ *
+ * mov r_val, ..
+ * call 0
+ * pop r_start_of_method
+ * sub r_start_of_method, ..
+ * mov r_key_reg, r_val
+ * sub r_key_reg, low_key
+ * cmp r_key_reg, size-1 ; bound check
+ * ja done
+ * mov r_disp, [r_start_of_method + r_key_reg * 4 + table_offset]
+ * add r_start_of_method, r_disp
+ * jmp r_start_of_method
+ * done:
+ */
+void X86Mir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
+ RegLocation rl_src)
+{
+ const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ if (cu_->verbose) {
+ DumpPackedSwitchTable(table);
+ }
+ // Add the table to the list - we'll process it later
+ SwitchTable *tab_rec =
+ static_cast<SwitchTable *>(arena_->NewMem(sizeof(SwitchTable), true,
+ ArenaAllocator::kAllocData));
+ tab_rec->table = table;
+ tab_rec->vaddr = current_dalvik_offset_;
+ int size = table[1];
+ tab_rec->targets = static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true,
+ ArenaAllocator::kAllocLIR));
+ switch_tables_.Insert(tab_rec);
+
+ // Get the switch value
+ rl_src = LoadValue(rl_src, kCoreReg);
+ int start_of_method_reg = AllocTemp();
+ // Materialize a pointer to the switch table
+ //NewLIR0(kX86Bkpt);
+ NewLIR1(kX86StartOfMethod, start_of_method_reg);
+ int low_key = s4FromSwitchData(&table[2]);
+ int keyReg;
+ // Remove the bias, if necessary
+ if (low_key == 0) {
+ keyReg = rl_src.low_reg;
+ } else {
+ keyReg = AllocTemp();
+ OpRegRegImm(kOpSub, keyReg, rl_src.low_reg, low_key);
+ }
+ // Bounds check - if < 0 or >= size continue following switch
+ OpRegImm(kOpCmp, keyReg, size-1);
+ LIR* branch_over = OpCondBranch(kCondHi, NULL);
+
+ // Load the displacement from the switch table
+ int disp_reg = AllocTemp();
+ NewLIR5(kX86PcRelLoadRA, disp_reg, start_of_method_reg, keyReg, 2,
+ reinterpret_cast<uintptr_t>(tab_rec));
+ // Add displacement to start of method
+ OpRegReg(kOpAdd, start_of_method_reg, disp_reg);
+ // ..and go!
+ LIR* switch_branch = NewLIR1(kX86JmpR, start_of_method_reg);
+ tab_rec->anchor = switch_branch;
+
+ /* branch_over target here */
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ branch_over->target = target;
+}
+
+/*
+ * Array data table format:
+ * ushort ident = 0x0300 magic value
+ * ushort width width of each element in the table
+ * uint size number of elements in the table
+ * ubyte data[size*width] table of data values (may contain a single-byte
+ * padding at the end)
+ *
+ * Total size is 4+(width * size + 1)/2 16-bit code units.
+ */
+void X86Mir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src)
+{
+ const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ // Add the table to the list - we'll process it later
+ FillArrayData *tab_rec =
+ static_cast<FillArrayData*>(arena_->NewMem(sizeof(FillArrayData), true,
+ ArenaAllocator::kAllocData));
+ tab_rec->table = table;
+ tab_rec->vaddr = current_dalvik_offset_;
+ uint16_t width = tab_rec->table[1];
+ uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
+ tab_rec->size = (size * width) + 8;
+
+ fill_array_data_.Insert(tab_rec);
+
+ // Making a call - use explicit registers
+ FlushAllRegs(); /* Everything to home location */
+ LoadValueDirectFixed(rl_src, rX86_ARG0);
+ // Materialize a pointer to the fill data image
+ NewLIR1(kX86StartOfMethod, rX86_ARG2);
+ NewLIR2(kX86PcRelAdr, rX86_ARG1, reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR2(kX86Add32RR, rX86_ARG1, rX86_ARG2);
+ CallRuntimeHelperRegReg(ENTRYPOINT_OFFSET(pHandleFillArrayDataFromCode), rX86_ARG0,
+ rX86_ARG1, true);
+}
+
+void X86Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src)
+{
+ FlushAllRegs();
+ LoadValueDirectFixed(rl_src, rCX); // Get obj
+ LockCallTemps(); // Prepare for explicit register usage
+ GenNullCheck(rl_src.s_reg_low, rCX, opt_flags);
+ // If lock is unheld, try to grab it quickly with compare and exchange
+ // TODO: copy and clear hash state?
+ NewLIR2(kX86Mov32RT, rDX, Thread::ThinLockIdOffset().Int32Value());
+ NewLIR2(kX86Sal32RI, rDX, LW_LOCK_OWNER_SHIFT);
+ NewLIR2(kX86Xor32RR, rAX, rAX);
+ NewLIR3(kX86LockCmpxchgMR, rCX, mirror::Object::MonitorOffset().Int32Value(), rDX);
+ LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondEq);
+ // If lock is held, go the expensive route - artLockObjectFromCode(self, obj);
+ CallRuntimeHelperReg(ENTRYPOINT_OFFSET(pLockObjectFromCode), rCX, true);
+ branch->target = NewLIR0(kPseudoTargetLabel);
+}
+
+void X86Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src)
+{
+ FlushAllRegs();
+ LoadValueDirectFixed(rl_src, rAX); // Get obj
+ LockCallTemps(); // Prepare for explicit register usage
+ GenNullCheck(rl_src.s_reg_low, rAX, opt_flags);
+ // If lock is held by the current thread, clear it to quickly release it
+ // TODO: clear hash state?
+ NewLIR2(kX86Mov32RT, rDX, Thread::ThinLockIdOffset().Int32Value());
+ NewLIR2(kX86Sal32RI, rDX, LW_LOCK_OWNER_SHIFT);
+ NewLIR3(kX86Mov32RM, rCX, rAX, mirror::Object::MonitorOffset().Int32Value());
+ OpRegReg(kOpSub, rCX, rDX);
+ LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+ NewLIR3(kX86Mov32MR, rAX, mirror::Object::MonitorOffset().Int32Value(), rCX);
+ LIR* branch2 = NewLIR1(kX86Jmp8, 0);
+ branch->target = NewLIR0(kPseudoTargetLabel);
+ // Otherwise, go the expensive route - UnlockObjectFromCode(obj);
+ CallRuntimeHelperReg(ENTRYPOINT_OFFSET(pUnlockObjectFromCode), rAX, true);
+ branch2->target = NewLIR0(kPseudoTargetLabel);
+}
+
+void X86Mir2Lir::GenMoveException(RegLocation rl_dest)
+{
+ int ex_offset = Thread::ExceptionOffset().Int32Value();
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ NewLIR2(kX86Mov32RT, rl_result.low_reg, ex_offset);
+ NewLIR2(kX86Mov32TI, ex_offset, 0);
+ StoreValue(rl_dest, rl_result);
+}
+
+/*
+ * Mark garbage collection card. Skip if the value we're storing is null.
+ */
+void X86Mir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg)
+{
+ int reg_card_base = AllocTemp();
+ int reg_card_no = AllocTemp();
+ LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL);
+ NewLIR2(kX86Mov32RT, reg_card_base, Thread::CardTableOffset().Int32Value());
+ OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
+ StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0,
+ kUnsignedByte);
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ branch_over->target = target;
+ FreeTemp(reg_card_base);
+ FreeTemp(reg_card_no);
+}
+
+void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method)
+{
+ /*
+ * On entry, rX86_ARG0, rX86_ARG1, rX86_ARG2 are live. Let the register
+ * allocation mechanism know so it doesn't try to use any of them when
+ * expanding the frame or flushing. This leaves the utility
+ * code with no spare temps.
+ */
+ LockTemp(rX86_ARG0);
+ LockTemp(rX86_ARG1);
+ LockTemp(rX86_ARG2);
+
+ /* Build frame, return address already on stack */
+ OpRegImm(kOpSub, rX86_SP, frame_size_ - 4);
+
+ /*
+ * We can safely skip the stack overflow check if we're
+ * a leaf *and* our frame size < fudge factor.
+ */
+ bool skip_overflow_check = (mir_graph_->MethodIsLeaf() &&
+ (static_cast<size_t>(frame_size_) <
+ Thread::kStackOverflowReservedBytes));
+ NewLIR0(kPseudoMethodEntry);
+ /* Spill core callee saves */
+ SpillCoreRegs();
+ /* NOTE: promotion of FP regs currently unsupported, thus no FP spill */
+ DCHECK_EQ(num_fp_spills_, 0);
+ if (!skip_overflow_check) {
+ // cmp rX86_SP, fs:[stack_end_]; jcc throw_launchpad
+ LIR* tgt = RawLIR(0, kPseudoThrowTarget, kThrowStackOverflow, 0, 0, 0, 0);
+ OpRegThreadMem(kOpCmp, rX86_SP, Thread::StackEndOffset().Int32Value());
+ OpCondBranch(kCondUlt, tgt);
+ // Remember branch target - will process later
+ throw_launchpads_.Insert(tgt);
+ }
+
+ FlushIns(ArgLocs, rl_method);
+
+ FreeTemp(rX86_ARG0);
+ FreeTemp(rX86_ARG1);
+ FreeTemp(rX86_ARG2);
+}
+
+void X86Mir2Lir::GenExitSequence() {
+ /*
+ * In the exit path, rX86_RET0/rX86_RET1 are live - make sure they aren't
+ * allocated by the register utilities as temps.
+ */
+ LockTemp(rX86_RET0);
+ LockTemp(rX86_RET1);
+
+ NewLIR0(kPseudoMethodExit);
+ UnSpillCoreRegs();
+ /* Remove frame except for return address */
+ OpRegImm(kOpAdd, rX86_SP, frame_size_ - 4);
+ NewLIR0(kX86Ret);
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
new file mode 100644
index 0000000..3e30141
--- /dev/null
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_QUICK_X86_CODEGENX86_H_
+#define ART_SRC_COMPILER_DEX_QUICK_X86_CODEGENX86_H_
+
+#include "dex/compiler_internals.h"
+#include "x86_lir.h"
+
+namespace art {
+
+class X86Mir2Lir : public Mir2Lir {
+ public:
+
+ X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
+
+ // Required for target - codegen helpers.
+ bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src,
+ RegLocation rl_dest, int lit);
+ int LoadHelper(int offset);
+ LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg);
+ LIR* LoadBaseDispWide(int rBase, int displacement, int r_dest_lo, int r_dest_hi,
+ int s_reg);
+ LIR* LoadBaseIndexed(int rBase, int r_index, int r_dest, int scale, OpSize size);
+ LIR* LoadBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
+ int r_dest, int r_dest_hi, OpSize size, int s_reg);
+ LIR* LoadConstantNoClobber(int r_dest, int value);
+ LIR* LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value);
+ LIR* StoreBaseDisp(int rBase, int displacement, int r_src, OpSize size);
+ LIR* StoreBaseDispWide(int rBase, int displacement, int r_src_lo, int r_src_hi);
+ LIR* StoreBaseIndexed(int rBase, int r_index, int r_src, int scale, OpSize size);
+ LIR* StoreBaseIndexedDisp(int rBase, int r_index, int scale, int displacement,
+ int r_src, int r_src_hi, OpSize size, int s_reg);
+ void MarkGCCard(int val_reg, int tgt_addr_reg);
+
+ // Required for target - register utilities.
+ bool IsFpReg(int reg);
+ bool SameRegType(int reg1, int reg2);
+ int AllocTypedTemp(bool fp_hint, int reg_class);
+ int AllocTypedTempPair(bool fp_hint, int reg_class);
+ int S2d(int low_reg, int high_reg);
+ int TargetReg(SpecialTargetRegister reg);
+ RegisterInfo* GetRegInfo(int reg);
+ RegLocation GetReturnAlt();
+ RegLocation GetReturnWideAlt();
+ RegLocation LocCReturn();
+ RegLocation LocCReturnDouble();
+ RegLocation LocCReturnFloat();
+ RegLocation LocCReturnWide();
+ uint32_t FpRegMask();
+ uint64_t GetRegMaskCommon(int reg);
+ void AdjustSpillMask();
+ void ClobberCalleeSave();
+ void FlushReg(int reg);
+ void FlushRegWide(int reg1, int reg2);
+ void FreeCallTemps();
+ void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free);
+ void LockCallTemps();
+ void MarkPreservedSingle(int v_reg, int reg);
+ void CompilerInitializeRegAlloc();
+
+ // Required for target - miscellaneous.
+ AssemblerStatus AssembleInstructions(uintptr_t start_addr);
+ void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix);
+ void SetupTargetResourceMasks(LIR* lir);
+ const char* GetTargetInstFmt(int opcode);
+ const char* GetTargetInstName(int opcode);
+ std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr);
+ uint64_t GetPCUseDefEncoding();
+ uint64_t GetTargetInstFlags(int opcode);
+ int GetInsnSize(LIR* lir);
+ bool IsUnconditionalBranch(LIR* lir);
+
+ // Required for target - Dalvik-level generators.
+ void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenArrayObjPut(int opt_flags, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale);
+ void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_dest, int scale);
+ void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale);
+ void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_shift);
+ void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2);
+ void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2);
+ void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src);
+ bool GenInlinedCas32(CallInfo* info, bool need_write_barrier);
+ bool GenInlinedMinMaxInt(CallInfo* info, bool is_min);
+ bool GenInlinedSqrt(CallInfo* info);
+ void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
+ void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset,
+ ThrowKind kind);
+ RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div);
+ RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div);
+ void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
+ void GenDivZeroCheck(int reg_lo, int reg_hi);
+ void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
+ void GenExitSequence();
+ void GenFillArrayData(uint32_t table_offset, RegLocation rl_src);
+ void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
+ void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
+ void GenSelect(BasicBlock* bb, MIR* mir);
+ void GenMemBarrier(MemBarrierKind barrier_kind);
+ void GenMonitorEnter(int opt_flags, RegLocation rl_src);
+ void GenMonitorExit(int opt_flags, RegLocation rl_src);
+ void GenMoveException(RegLocation rl_dest);
+ void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result,
+ int lit, int first_bit, int second_bit);
+ void GenNegDouble(RegLocation rl_dest, RegLocation rl_src);
+ void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
+ void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
+ void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
+ void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case);
+
+ // Single operation generators.
+ LIR* OpUnconditionalBranch(LIR* target);
+ LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target);
+ LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target);
+ LIR* OpCondBranch(ConditionCode cc, LIR* target);
+ LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target);
+ LIR* OpFpRegCopy(int r_dest, int r_src);
+ LIR* OpIT(ConditionCode cond, const char* guide);
+ LIR* OpMem(OpKind op, int rBase, int disp);
+ LIR* OpPcRelLoad(int reg, LIR* target);
+ LIR* OpReg(OpKind op, int r_dest_src);
+ LIR* OpRegCopy(int r_dest, int r_src);
+ LIR* OpRegCopyNoInsert(int r_dest, int r_src);
+ LIR* OpRegImm(OpKind op, int r_dest_src1, int value);
+ LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset);
+ LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2);
+ LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value);
+ LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2);
+ LIR* OpTestSuspend(LIR* target);
+ LIR* OpThreadMem(OpKind op, int thread_offset);
+ LIR* OpVldm(int rBase, int count);
+ LIR* OpVstm(int rBase, int count);
+ void OpLea(int rBase, int reg1, int reg2, int scale, int offset);
+ void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi);
+ void OpTlsCmp(int offset, int val);
+
+ void OpRegThreadMem(OpKind op, int r_dest, int thread_offset);
+ void SpillCoreRegs();
+ void UnSpillCoreRegs();
+ static const X86EncodingMap EncodingMap[kX86Last];
+ bool InexpensiveConstantInt(int32_t value);
+ bool InexpensiveConstantFloat(int32_t value);
+ bool InexpensiveConstantLong(int64_t value);
+ bool InexpensiveConstantDouble(int64_t value);
+
+ private:
+ void EmitDisp(int base, int disp);
+ void EmitOpReg(const X86EncodingMap* entry, uint8_t reg);
+ void EmitOpMem(const X86EncodingMap* entry, uint8_t base, int disp);
+ void EmitMemReg(const X86EncodingMap* entry, uint8_t base, int disp, uint8_t reg);
+ void EmitRegMem(const X86EncodingMap* entry, uint8_t reg, uint8_t base, int disp);
+ void EmitRegArray(const X86EncodingMap* entry, uint8_t reg, uint8_t base, uint8_t index,
+ int scale, int disp);
+ void EmitArrayReg(const X86EncodingMap* entry, uint8_t base, uint8_t index, int scale, int disp,
+ uint8_t reg);
+ void EmitRegThread(const X86EncodingMap* entry, uint8_t reg, int disp);
+ void EmitRegReg(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2);
+ void EmitRegRegImm(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2, int32_t imm);
+ void EmitRegImm(const X86EncodingMap* entry, uint8_t reg, int imm);
+ void EmitThreadImm(const X86EncodingMap* entry, int disp, int imm);
+ void EmitMovRegImm(const X86EncodingMap* entry, uint8_t reg, int imm);
+ void EmitShiftRegImm(const X86EncodingMap* entry, uint8_t reg, int imm);
+ void EmitShiftRegCl(const X86EncodingMap* entry, uint8_t reg, uint8_t cl);
+ void EmitRegCond(const X86EncodingMap* entry, uint8_t reg, uint8_t condition);
+ void EmitJmp(const X86EncodingMap* entry, int rel);
+ void EmitJcc(const X86EncodingMap* entry, int rel, uint8_t cc);
+ void EmitCallMem(const X86EncodingMap* entry, uint8_t base, int disp);
+ void EmitCallThread(const X86EncodingMap* entry, int disp);
+ void EmitPcRel(const X86EncodingMap* entry, uint8_t reg, int base_or_table, uint8_t index,
+ int scale, int table_or_disp);
+ void EmitMacro(const X86EncodingMap* entry, uint8_t reg, int offset);
+ void EmitUnimplemented(const X86EncodingMap* entry, LIR* lir);
+};
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_QUICK_X86_CODEGENX86_H_
diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc
new file mode 100644
index 0000000..906b4cc
--- /dev/null
+++ b/compiler/dex/quick/x86/fp_x86.cc
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2012 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 "codegen_x86.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "x86_lir.h"
+
+namespace art {
+
+void X86Mir2Lir::GenArithOpFloat(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
+ X86OpCode op = kX86Nop;
+ RegLocation rl_result;
+
+ /*
+ * Don't attempt to optimize register usage since these opcodes call out to
+ * the handlers.
+ */
+ switch (opcode) {
+ case Instruction::ADD_FLOAT_2ADDR:
+ case Instruction::ADD_FLOAT:
+ op = kX86AddssRR;
+ break;
+ case Instruction::SUB_FLOAT_2ADDR:
+ case Instruction::SUB_FLOAT:
+ op = kX86SubssRR;
+ break;
+ case Instruction::DIV_FLOAT_2ADDR:
+ case Instruction::DIV_FLOAT:
+ op = kX86DivssRR;
+ break;
+ case Instruction::MUL_FLOAT_2ADDR:
+ case Instruction::MUL_FLOAT:
+ op = kX86MulssRR;
+ break;
+ case Instruction::REM_FLOAT_2ADDR:
+ case Instruction::REM_FLOAT:
+ FlushAllRegs(); // Send everything to home location
+ CallRuntimeHelperRegLocationRegLocation(ENTRYPOINT_OFFSET(pFmodf), rl_src1, rl_src2, false);
+ rl_result = GetReturn(true);
+ StoreValue(rl_dest, rl_result);
+ return;
+ case Instruction::NEG_FLOAT:
+ GenNegFloat(rl_dest, rl_src1);
+ return;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+ rl_src1 = LoadValue(rl_src1, kFPReg);
+ rl_src2 = LoadValue(rl_src2, kFPReg);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ int r_dest = rl_result.low_reg;
+ int r_src1 = rl_src1.low_reg;
+ int r_src2 = rl_src2.low_reg;
+ if (r_dest == r_src2) {
+ r_src2 = AllocTempFloat();
+ OpRegCopy(r_src2, r_dest);
+ }
+ OpRegCopy(r_dest, r_src1);
+ NewLIR2(op, r_dest, r_src2);
+ StoreValue(rl_dest, rl_result);
+}
+
+void X86Mir2Lir::GenArithOpDouble(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) {
+ X86OpCode op = kX86Nop;
+ RegLocation rl_result;
+
+ switch (opcode) {
+ case Instruction::ADD_DOUBLE_2ADDR:
+ case Instruction::ADD_DOUBLE:
+ op = kX86AddsdRR;
+ break;
+ case Instruction::SUB_DOUBLE_2ADDR:
+ case Instruction::SUB_DOUBLE:
+ op = kX86SubsdRR;
+ break;
+ case Instruction::DIV_DOUBLE_2ADDR:
+ case Instruction::DIV_DOUBLE:
+ op = kX86DivsdRR;
+ break;
+ case Instruction::MUL_DOUBLE_2ADDR:
+ case Instruction::MUL_DOUBLE:
+ op = kX86MulsdRR;
+ break;
+ case Instruction::REM_DOUBLE_2ADDR:
+ case Instruction::REM_DOUBLE:
+ FlushAllRegs(); // Send everything to home location
+ CallRuntimeHelperRegLocationRegLocation(ENTRYPOINT_OFFSET(pFmod), rl_src1, rl_src2, false);
+ rl_result = GetReturnWide(true);
+ StoreValueWide(rl_dest, rl_result);
+ return;
+ case Instruction::NEG_DOUBLE:
+ GenNegDouble(rl_dest, rl_src1);
+ return;
+ default:
+ LOG(FATAL) << "Unexpected opcode: " << opcode;
+ }
+ rl_src1 = LoadValueWide(rl_src1, kFPReg);
+ DCHECK(rl_src1.wide);
+ rl_src2 = LoadValueWide(rl_src2, kFPReg);
+ DCHECK(rl_src2.wide);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ DCHECK(rl_dest.wide);
+ DCHECK(rl_result.wide);
+ int r_dest = S2d(rl_result.low_reg, rl_result.high_reg);
+ int r_src1 = S2d(rl_src1.low_reg, rl_src1.high_reg);
+ int r_src2 = S2d(rl_src2.low_reg, rl_src2.high_reg);
+ if (r_dest == r_src2) {
+ r_src2 = AllocTempDouble() | X86_FP_DOUBLE;
+ OpRegCopy(r_src2, r_dest);
+ }
+ OpRegCopy(r_dest, r_src1);
+ NewLIR2(op, r_dest, r_src2);
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void X86Mir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src) {
+ RegisterClass rcSrc = kFPReg;
+ X86OpCode op = kX86Nop;
+ int src_reg;
+ RegLocation rl_result;
+ switch (opcode) {
+ case Instruction::INT_TO_FLOAT:
+ rcSrc = kCoreReg;
+ op = kX86Cvtsi2ssRR;
+ break;
+ case Instruction::DOUBLE_TO_FLOAT:
+ rcSrc = kFPReg;
+ op = kX86Cvtsd2ssRR;
+ break;
+ case Instruction::FLOAT_TO_DOUBLE:
+ rcSrc = kFPReg;
+ op = kX86Cvtss2sdRR;
+ break;
+ case Instruction::INT_TO_DOUBLE:
+ rcSrc = kCoreReg;
+ op = kX86Cvtsi2sdRR;
+ break;
+ case Instruction::FLOAT_TO_INT: {
+ rl_src = LoadValue(rl_src, kFPReg);
+ src_reg = rl_src.low_reg;
+ // In case result vreg is also src vreg, break association to avoid useless copy by EvalLoc()
+ ClobberSReg(rl_dest.s_reg_low);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int temp_reg = AllocTempFloat();
+
+ LoadConstant(rl_result.low_reg, 0x7fffffff);
+ NewLIR2(kX86Cvtsi2ssRR, temp_reg, rl_result.low_reg);
+ NewLIR2(kX86ComissRR, src_reg, temp_reg);
+ LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondA);
+ LIR* branch_na_n = NewLIR2(kX86Jcc8, 0, kX86CondP);
+ NewLIR2(kX86Cvttss2siRR, rl_result.low_reg, src_reg);
+ LIR* branch_normal = NewLIR1(kX86Jmp8, 0);
+ branch_na_n->target = NewLIR0(kPseudoTargetLabel);
+ NewLIR2(kX86Xor32RR, rl_result.low_reg, rl_result.low_reg);
+ branch_pos_overflow->target = NewLIR0(kPseudoTargetLabel);
+ branch_normal->target = NewLIR0(kPseudoTargetLabel);
+ StoreValue(rl_dest, rl_result);
+ return;
+ }
+ case Instruction::DOUBLE_TO_INT: {
+ rl_src = LoadValueWide(rl_src, kFPReg);
+ src_reg = rl_src.low_reg;
+ // In case result vreg is also src vreg, break association to avoid useless copy by EvalLoc()
+ ClobberSReg(rl_dest.s_reg_low);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ int temp_reg = AllocTempDouble() | X86_FP_DOUBLE;
+
+ LoadConstant(rl_result.low_reg, 0x7fffffff);
+ NewLIR2(kX86Cvtsi2sdRR, temp_reg, rl_result.low_reg);
+ NewLIR2(kX86ComisdRR, src_reg, temp_reg);
+ LIR* branch_pos_overflow = NewLIR2(kX86Jcc8, 0, kX86CondA);
+ LIR* branch_na_n = NewLIR2(kX86Jcc8, 0, kX86CondP);
+ NewLIR2(kX86Cvttsd2siRR, rl_result.low_reg, src_reg);
+ LIR* branch_normal = NewLIR1(kX86Jmp8, 0);
+ branch_na_n->target = NewLIR0(kPseudoTargetLabel);
+ NewLIR2(kX86Xor32RR, rl_result.low_reg, rl_result.low_reg);
+ branch_pos_overflow->target = NewLIR0(kPseudoTargetLabel);
+ branch_normal->target = NewLIR0(kPseudoTargetLabel);
+ StoreValue(rl_dest, rl_result);
+ return;
+ }
+ case Instruction::LONG_TO_DOUBLE:
+ GenConversionCall(ENTRYPOINT_OFFSET(pL2d), rl_dest, rl_src);
+ return;
+ case Instruction::LONG_TO_FLOAT:
+ // TODO: inline by using memory as a 64-bit source. Be careful about promoted registers.
+ GenConversionCall(ENTRYPOINT_OFFSET(pL2f), rl_dest, rl_src);
+ return;
+ case Instruction::FLOAT_TO_LONG:
+ GenConversionCall(ENTRYPOINT_OFFSET(pF2l), rl_dest, rl_src);
+ return;
+ case Instruction::DOUBLE_TO_LONG:
+ GenConversionCall(ENTRYPOINT_OFFSET(pD2l), rl_dest, rl_src);
+ return;
+ default:
+ LOG(INFO) << "Unexpected opcode: " << opcode;
+ }
+ if (rl_src.wide) {
+ rl_src = LoadValueWide(rl_src, rcSrc);
+ src_reg = S2d(rl_src.low_reg, rl_src.high_reg);
+ } else {
+ rl_src = LoadValue(rl_src, rcSrc);
+ src_reg = rl_src.low_reg;
+ }
+ if (rl_dest.wide) {
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(op, S2d(rl_result.low_reg, rl_result.high_reg), src_reg);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(op, rl_result.low_reg, src_reg);
+ StoreValue(rl_dest, rl_result);
+ }
+}
+
+void X86Mir2Lir::GenCmpFP(Instruction::Code code, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2) {
+ bool single = (code == Instruction::CMPL_FLOAT) || (code == Instruction::CMPG_FLOAT);
+ bool unordered_gt = (code == Instruction::CMPG_DOUBLE) || (code == Instruction::CMPG_FLOAT);
+ int src_reg1;
+ int src_reg2;
+ if (single) {
+ rl_src1 = LoadValue(rl_src1, kFPReg);
+ src_reg1 = rl_src1.low_reg;
+ rl_src2 = LoadValue(rl_src2, kFPReg);
+ src_reg2 = rl_src2.low_reg;
+ } else {
+ rl_src1 = LoadValueWide(rl_src1, kFPReg);
+ src_reg1 = S2d(rl_src1.low_reg, rl_src1.high_reg);
+ rl_src2 = LoadValueWide(rl_src2, kFPReg);
+ src_reg2 = S2d(rl_src2.low_reg, rl_src2.high_reg);
+ }
+ // In case result vreg is also src vreg, break association to avoid useless copy by EvalLoc()
+ ClobberSReg(rl_dest.s_reg_low);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ LoadConstantNoClobber(rl_result.low_reg, unordered_gt ? 1 : 0);
+ if (single) {
+ NewLIR2(kX86UcomissRR, src_reg1, src_reg2);
+ } else {
+ NewLIR2(kX86UcomisdRR, src_reg1, src_reg2);
+ }
+ LIR* branch = NULL;
+ if (unordered_gt) {
+ branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
+ }
+ // If the result reg can't be byte accessed, use a jump and move instead of a set.
+ if (rl_result.low_reg >= 4) {
+ LIR* branch2 = NULL;
+ if (unordered_gt) {
+ branch2 = NewLIR2(kX86Jcc8, 0, kX86CondA);
+ NewLIR2(kX86Mov32RI, rl_result.low_reg, 0x0);
+ } else {
+ branch2 = NewLIR2(kX86Jcc8, 0, kX86CondBe);
+ NewLIR2(kX86Mov32RI, rl_result.low_reg, 0x1);
+ }
+ branch2->target = NewLIR0(kPseudoTargetLabel);
+ } else {
+ NewLIR2(kX86Set8R, rl_result.low_reg, kX86CondA /* above - unsigned > */);
+ }
+ NewLIR2(kX86Sbb32RI, rl_result.low_reg, 0);
+ if (unordered_gt) {
+ branch->target = NewLIR0(kPseudoTargetLabel);
+ }
+ StoreValue(rl_dest, rl_result);
+}
+
+void X86Mir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
+ bool is_double) {
+ LIR* taken = &block_label_list_[bb->taken->id];
+ LIR* not_taken = &block_label_list_[bb->fall_through->id];
+ LIR* branch = NULL;
+ RegLocation rl_src1;
+ RegLocation rl_src2;
+ if (is_double) {
+ rl_src1 = mir_graph_->GetSrcWide(mir, 0);
+ rl_src2 = mir_graph_->GetSrcWide(mir, 2);
+ rl_src1 = LoadValueWide(rl_src1, kFPReg);
+ rl_src2 = LoadValueWide(rl_src2, kFPReg);
+ NewLIR2(kX86UcomisdRR, S2d(rl_src1.low_reg, rl_src1.high_reg),
+ S2d(rl_src2.low_reg, rl_src2.high_reg));
+ } else {
+ rl_src1 = mir_graph_->GetSrc(mir, 0);
+ rl_src2 = mir_graph_->GetSrc(mir, 1);
+ rl_src1 = LoadValue(rl_src1, kFPReg);
+ rl_src2 = LoadValue(rl_src2, kFPReg);
+ NewLIR2(kX86UcomissRR, rl_src1.low_reg, rl_src2.low_reg);
+ }
+ ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
+ switch (ccode) {
+ case kCondEq:
+ if (!gt_bias) {
+ branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
+ branch->target = not_taken;
+ }
+ break;
+ case kCondNe:
+ if (!gt_bias) {
+ branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
+ branch->target = taken;
+ }
+ break;
+ case kCondLt:
+ if (gt_bias) {
+ branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
+ branch->target = not_taken;
+ }
+ ccode = kCondCs;
+ break;
+ case kCondLe:
+ if (gt_bias) {
+ branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
+ branch->target = not_taken;
+ }
+ ccode = kCondLs;
+ break;
+ case kCondGt:
+ if (gt_bias) {
+ branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
+ branch->target = taken;
+ }
+ ccode = kCondHi;
+ break;
+ case kCondGe:
+ if (gt_bias) {
+ branch = NewLIR2(kX86Jcc8, 0, kX86CondPE);
+ branch->target = taken;
+ }
+ ccode = kCondCc;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected ccode: " << ccode;
+ }
+ OpCondBranch(ccode, taken);
+}
+
+void X86Mir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src)
+{
+ RegLocation rl_result;
+ rl_src = LoadValue(rl_src, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegRegImm(kOpAdd, rl_result.low_reg, rl_src.low_reg, 0x80000000);
+ StoreValue(rl_dest, rl_result);
+}
+
+void X86Mir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src)
+{
+ RegLocation rl_result;
+ rl_src = LoadValueWide(rl_src, kCoreReg);
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegRegImm(kOpAdd, rl_result.high_reg, rl_src.high_reg, 0x80000000);
+ OpRegCopy(rl_result.low_reg, rl_src.low_reg);
+ StoreValueWide(rl_dest, rl_result);
+}
+
+bool X86Mir2Lir::GenInlinedSqrt(CallInfo* info) {
+ DCHECK_NE(cu_->instruction_set, kThumb2);
+ return false;
+}
+
+
+
+} // namespace art
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
new file mode 100644
index 0000000..97d9d2d
--- /dev/null
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -0,0 +1,606 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/* This file contains codegen for the X86 ISA */
+
+#include "codegen_x86.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "mirror/array.h"
+#include "x86_lir.h"
+
+namespace art {
+
+/*
+ * Perform register memory operation.
+ */
+LIR* X86Mir2Lir::GenRegMemCheck(ConditionCode c_code,
+ int reg1, int base, int offset, ThrowKind kind)
+{
+ LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind,
+ current_dalvik_offset_, reg1, base, offset);
+ OpRegMem(kOpCmp, reg1, base, offset);
+ LIR* branch = OpCondBranch(c_code, tgt);
+ // Remember branch target - will process later
+ throw_launchpads_.Insert(tgt);
+ return branch;
+}
+
+/*
+ * Compare two 64-bit values
+ * x = y return 0
+ * x < y return -1
+ * x > y return 1
+ */
+void X86Mir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ FlushAllRegs();
+ LockCallTemps(); // Prepare for explicit register usage
+ LoadValueDirectWideFixed(rl_src1, r0, r1);
+ LoadValueDirectWideFixed(rl_src2, r2, r3);
+ // Compute (r1:r0) = (r1:r0) - (r3:r2)
+ OpRegReg(kOpSub, r0, r2); // r0 = r0 - r2
+ OpRegReg(kOpSbc, r1, r3); // r1 = r1 - r3 - CF
+ NewLIR2(kX86Set8R, r2, kX86CondL); // r2 = (r1:r0) < (r3:r2) ? 1 : 0
+ NewLIR2(kX86Movzx8RR, r2, r2);
+ OpReg(kOpNeg, r2); // r2 = -r2
+ OpRegReg(kOpOr, r0, r1); // r0 = high | low - sets ZF
+ NewLIR2(kX86Set8R, r0, kX86CondNz); // r0 = (r1:r0) != (r3:r2) ? 1 : 0
+ NewLIR2(kX86Movzx8RR, r0, r0);
+ OpRegReg(kOpOr, r0, r2); // r0 = r0 | r2
+ RegLocation rl_result = LocCReturn();
+ StoreValue(rl_dest, rl_result);
+}
+
+X86ConditionCode X86ConditionEncoding(ConditionCode cond) {
+ switch (cond) {
+ case kCondEq: return kX86CondEq;
+ case kCondNe: return kX86CondNe;
+ case kCondCs: return kX86CondC;
+ case kCondCc: return kX86CondNc;
+ case kCondMi: return kX86CondS;
+ case kCondPl: return kX86CondNs;
+ case kCondVs: return kX86CondO;
+ case kCondVc: return kX86CondNo;
+ case kCondHi: return kX86CondA;
+ case kCondLs: return kX86CondBe;
+ case kCondGe: return kX86CondGe;
+ case kCondLt: return kX86CondL;
+ case kCondGt: return kX86CondG;
+ case kCondLe: return kX86CondLe;
+ case kCondAl:
+ case kCondNv: LOG(FATAL) << "Should not reach here";
+ }
+ return kX86CondO;
+}
+
+LIR* X86Mir2Lir::OpCmpBranch(ConditionCode cond, int src1, int src2,
+ LIR* target)
+{
+ NewLIR2(kX86Cmp32RR, src1, src2);
+ X86ConditionCode cc = X86ConditionEncoding(cond);
+ LIR* branch = NewLIR2(kX86Jcc8, 0 /* lir operand for Jcc offset */ ,
+ cc);
+ branch->target = target;
+ return branch;
+}
+
+LIR* X86Mir2Lir::OpCmpImmBranch(ConditionCode cond, int reg,
+ int check_value, LIR* target)
+{
+ if ((check_value == 0) && (cond == kCondEq || cond == kCondNe)) {
+ // TODO: when check_value == 0 and reg is rCX, use the jcxz/nz opcode
+ NewLIR2(kX86Test32RR, reg, reg);
+ } else {
+ NewLIR2(IS_SIMM8(check_value) ? kX86Cmp32RI8 : kX86Cmp32RI, reg, check_value);
+ }
+ X86ConditionCode cc = X86ConditionEncoding(cond);
+ LIR* branch = NewLIR2(kX86Jcc8, 0 /* lir operand for Jcc offset */ , cc);
+ branch->target = target;
+ return branch;
+}
+
+LIR* X86Mir2Lir::OpRegCopyNoInsert(int r_dest, int r_src)
+{
+ if (X86_FPREG(r_dest) || X86_FPREG(r_src))
+ return OpFpRegCopy(r_dest, r_src);
+ LIR* res = RawLIR(current_dalvik_offset_, kX86Mov32RR,
+ r_dest, r_src);
+ if (r_dest == r_src) {
+ res->flags.is_nop = true;
+ }
+ return res;
+}
+
+LIR* X86Mir2Lir::OpRegCopy(int r_dest, int r_src)
+{
+ LIR *res = OpRegCopyNoInsert(r_dest, r_src);
+ AppendLIR(res);
+ return res;
+}
+
+void X86Mir2Lir::OpRegCopyWide(int dest_lo, int dest_hi,
+ int src_lo, int src_hi)
+{
+ bool dest_fp = X86_FPREG(dest_lo) && X86_FPREG(dest_hi);
+ bool src_fp = X86_FPREG(src_lo) && X86_FPREG(src_hi);
+ assert(X86_FPREG(src_lo) == X86_FPREG(src_hi));
+ assert(X86_FPREG(dest_lo) == X86_FPREG(dest_hi));
+ if (dest_fp) {
+ if (src_fp) {
+ OpRegCopy(S2d(dest_lo, dest_hi), S2d(src_lo, src_hi));
+ } else {
+ // TODO: Prevent this from happening in the code. The result is often
+ // unused or could have been loaded more easily from memory.
+ NewLIR2(kX86MovdxrRR, dest_lo, src_lo);
+ NewLIR2(kX86MovdxrRR, dest_hi, src_hi);
+ NewLIR2(kX86PsllqRI, dest_hi, 32);
+ NewLIR2(kX86OrpsRR, dest_lo, dest_hi);
+ }
+ } else {
+ if (src_fp) {
+ NewLIR2(kX86MovdrxRR, dest_lo, src_lo);
+ NewLIR2(kX86PsrlqRI, src_lo, 32);
+ NewLIR2(kX86MovdrxRR, dest_hi, src_lo);
+ } else {
+ // Handle overlap
+ if (src_hi == dest_lo) {
+ OpRegCopy(dest_hi, src_hi);
+ OpRegCopy(dest_lo, src_lo);
+ } else {
+ OpRegCopy(dest_lo, src_lo);
+ OpRegCopy(dest_hi, src_hi);
+ }
+ }
+ }
+}
+
+void X86Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir)
+{
+ UNIMPLEMENTED(FATAL) << "Need codegen for GenSelect";
+}
+
+void X86Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
+ LIR* taken = &block_label_list_[bb->taken->id];
+ RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
+ RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
+ FlushAllRegs();
+ LockCallTemps(); // Prepare for explicit register usage
+ LoadValueDirectWideFixed(rl_src1, r0, r1);
+ LoadValueDirectWideFixed(rl_src2, r2, r3);
+ ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
+ // Swap operands and condition code to prevent use of zero flag.
+ if (ccode == kCondLe || ccode == kCondGt) {
+ // Compute (r3:r2) = (r3:r2) - (r1:r0)
+ OpRegReg(kOpSub, r2, r0); // r2 = r2 - r0
+ OpRegReg(kOpSbc, r3, r1); // r3 = r3 - r1 - CF
+ } else {
+ // Compute (r1:r0) = (r1:r0) - (r3:r2)
+ OpRegReg(kOpSub, r0, r2); // r0 = r0 - r2
+ OpRegReg(kOpSbc, r1, r3); // r1 = r1 - r3 - CF
+ }
+ switch (ccode) {
+ case kCondEq:
+ case kCondNe:
+ OpRegReg(kOpOr, r0, r1); // r0 = r0 | r1
+ break;
+ case kCondLe:
+ ccode = kCondGe;
+ break;
+ case kCondGt:
+ ccode = kCondLt;
+ break;
+ case kCondLt:
+ case kCondGe:
+ break;
+ default:
+ LOG(FATAL) << "Unexpected ccode: " << ccode;
+ }
+ OpCondBranch(ccode, taken);
+}
+
+RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest, int reg_lo,
+ int lit, bool is_div)
+{
+ LOG(FATAL) << "Unexpected use of GenDivRemLit for x86";
+ return rl_dest;
+}
+
+RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, int reg_lo,
+ int reg_hi, bool is_div)
+{
+ LOG(FATAL) << "Unexpected use of GenDivRem for x86";
+ return rl_dest;
+}
+
+bool X86Mir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min)
+{
+ DCHECK_EQ(cu_->instruction_set, kX86);
+ RegLocation rl_src1 = info->args[0];
+ RegLocation rl_src2 = info->args[1];
+ rl_src1 = LoadValue(rl_src1, kCoreReg);
+ rl_src2 = LoadValue(rl_src2, kCoreReg);
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegReg(kOpCmp, rl_src1.low_reg, rl_src2.low_reg);
+ DCHECK_EQ(cu_->instruction_set, kX86);
+ LIR* branch = NewLIR2(kX86Jcc8, 0, is_min ? kX86CondG : kX86CondL);
+ OpRegReg(kOpMov, rl_result.low_reg, rl_src1.low_reg);
+ LIR* branch2 = NewLIR1(kX86Jmp8, 0);
+ branch->target = NewLIR0(kPseudoTargetLabel);
+ OpRegReg(kOpMov, rl_result.low_reg, rl_src2.low_reg);
+ branch2->target = NewLIR0(kPseudoTargetLabel);
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+void X86Mir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset)
+{
+ NewLIR5(kX86Lea32RA, rBase, reg1, reg2, scale, offset);
+}
+
+void X86Mir2Lir::OpTlsCmp(int offset, int val)
+{
+ NewLIR2(kX86Cmp16TI8, offset, val);
+}
+
+bool X86Mir2Lir::GenInlinedCas32(CallInfo* info, bool need_write_barrier) {
+ DCHECK_NE(cu_->instruction_set, kThumb2);
+ return false;
+}
+
+LIR* X86Mir2Lir::OpPcRelLoad(int reg, LIR* target) {
+ LOG(FATAL) << "Unexpected use of OpPcRelLoad for x86";
+ return NULL;
+}
+
+LIR* X86Mir2Lir::OpVldm(int rBase, int count)
+{
+ LOG(FATAL) << "Unexpected use of OpVldm for x86";
+ return NULL;
+}
+
+LIR* X86Mir2Lir::OpVstm(int rBase, int count)
+{
+ LOG(FATAL) << "Unexpected use of OpVstm for x86";
+ return NULL;
+}
+
+void X86Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
+ RegLocation rl_result, int lit,
+ int first_bit, int second_bit)
+{
+ int t_reg = AllocTemp();
+ OpRegRegImm(kOpLsl, t_reg, rl_src.low_reg, second_bit - first_bit);
+ OpRegRegReg(kOpAdd, rl_result.low_reg, rl_src.low_reg, t_reg);
+ FreeTemp(t_reg);
+ if (first_bit != 0) {
+ OpRegRegImm(kOpLsl, rl_result.low_reg, rl_result.low_reg, first_bit);
+ }
+}
+
+void X86Mir2Lir::GenDivZeroCheck(int reg_lo, int reg_hi)
+{
+ int t_reg = AllocTemp();
+ OpRegRegReg(kOpOr, t_reg, reg_lo, reg_hi);
+ GenImmedCheck(kCondEq, t_reg, 0, kThrowDivZero);
+ FreeTemp(t_reg);
+}
+
+// Test suspend flag, return target of taken suspend branch
+LIR* X86Mir2Lir::OpTestSuspend(LIR* target)
+{
+ OpTlsCmp(Thread::ThreadFlagsOffset().Int32Value(), 0);
+ return OpCondBranch((target == NULL) ? kCondNe : kCondEq, target);
+}
+
+// Decrement register and branch on condition
+LIR* X86Mir2Lir::OpDecAndBranch(ConditionCode c_code, int reg, LIR* target)
+{
+ OpRegImm(kOpSub, reg, 1);
+ return OpCmpImmBranch(c_code, reg, 0, target);
+}
+
+bool X86Mir2Lir::SmallLiteralDivide(Instruction::Code dalvik_opcode,
+ RegLocation rl_src, RegLocation rl_dest, int lit)
+{
+ LOG(FATAL) << "Unexpected use of smallLiteralDive in x86";
+ return false;
+}
+
+LIR* X86Mir2Lir::OpIT(ConditionCode cond, const char* guide)
+{
+ LOG(FATAL) << "Unexpected use of OpIT in x86";
+ return NULL;
+}
+
+void X86Mir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ LOG(FATAL) << "Unexpected use of GenX86Long for x86";
+}
+void X86Mir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
+ // enough.
+ FlushAllRegs();
+ LockCallTemps(); // Prepare for explicit register usage
+ LoadValueDirectWideFixed(rl_src1, r0, r1);
+ LoadValueDirectWideFixed(rl_src2, r2, r3);
+ // Compute (r1:r0) = (r1:r0) + (r2:r3)
+ OpRegReg(kOpAdd, r0, r2); // r0 = r0 + r2
+ OpRegReg(kOpAdc, r1, r3); // r1 = r1 + r3 + CF
+ RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
+ INVALID_SREG, INVALID_SREG};
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void X86Mir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
+ // enough.
+ FlushAllRegs();
+ LockCallTemps(); // Prepare for explicit register usage
+ LoadValueDirectWideFixed(rl_src1, r0, r1);
+ LoadValueDirectWideFixed(rl_src2, r2, r3);
+ // Compute (r1:r0) = (r1:r0) + (r2:r3)
+ OpRegReg(kOpSub, r0, r2); // r0 = r0 - r2
+ OpRegReg(kOpSbc, r1, r3); // r1 = r1 - r3 - CF
+ RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
+ INVALID_SREG, INVALID_SREG};
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void X86Mir2Lir::GenAndLong(RegLocation rl_dest, RegLocation rl_src1,
+ RegLocation rl_src2)
+{
+ // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
+ // enough.
+ FlushAllRegs();
+ LockCallTemps(); // Prepare for explicit register usage
+ LoadValueDirectWideFixed(rl_src1, r0, r1);
+ LoadValueDirectWideFixed(rl_src2, r2, r3);
+ // Compute (r1:r0) = (r1:r0) & (r2:r3)
+ OpRegReg(kOpAnd, r0, r2); // r0 = r0 & r2
+ OpRegReg(kOpAnd, r1, r3); // r1 = r1 & r3
+ RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
+ INVALID_SREG, INVALID_SREG};
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void X86Mir2Lir::GenOrLong(RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2)
+{
+ // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
+ // enough.
+ FlushAllRegs();
+ LockCallTemps(); // Prepare for explicit register usage
+ LoadValueDirectWideFixed(rl_src1, r0, r1);
+ LoadValueDirectWideFixed(rl_src2, r2, r3);
+ // Compute (r1:r0) = (r1:r0) | (r2:r3)
+ OpRegReg(kOpOr, r0, r2); // r0 = r0 | r2
+ OpRegReg(kOpOr, r1, r3); // r1 = r1 | r3
+ RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
+ INVALID_SREG, INVALID_SREG};
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void X86Mir2Lir::GenXorLong(RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_src2)
+{
+ // TODO: fixed register usage here as we only have 4 temps and temporary allocation isn't smart
+ // enough.
+ FlushAllRegs();
+ LockCallTemps(); // Prepare for explicit register usage
+ LoadValueDirectWideFixed(rl_src1, r0, r1);
+ LoadValueDirectWideFixed(rl_src2, r2, r3);
+ // Compute (r1:r0) = (r1:r0) ^ (r2:r3)
+ OpRegReg(kOpXor, r0, r2); // r0 = r0 ^ r2
+ OpRegReg(kOpXor, r1, r3); // r1 = r1 ^ r3
+ RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
+ INVALID_SREG, INVALID_SREG};
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void X86Mir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src)
+{
+ FlushAllRegs();
+ LockCallTemps(); // Prepare for explicit register usage
+ LoadValueDirectWideFixed(rl_src, r0, r1);
+ // Compute (r1:r0) = -(r1:r0)
+ OpRegReg(kOpNeg, r0, r0); // r0 = -r0
+ OpRegImm(kOpAdc, r1, 0); // r1 = r1 + CF
+ OpRegReg(kOpNeg, r1, r1); // r1 = -r1
+ RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
+ INVALID_SREG, INVALID_SREG};
+ StoreValueWide(rl_dest, rl_result);
+}
+
+void X86Mir2Lir::OpRegThreadMem(OpKind op, int r_dest, int thread_offset) {
+ X86OpCode opcode = kX86Bkpt;
+ switch (op) {
+ case kOpCmp: opcode = kX86Cmp32RT; break;
+ case kOpMov: opcode = kX86Mov32RT; break;
+ default:
+ LOG(FATAL) << "Bad opcode: " << op;
+ break;
+ }
+ NewLIR2(opcode, r_dest, thread_offset);
+}
+
+/*
+ * Generate array load
+ */
+void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_dest, int scale)
+{
+ RegisterClass reg_class = oat_reg_class_by_size(size);
+ int len_offset = mirror::Array::LengthOffset().Int32Value();
+ int data_offset;
+ RegLocation rl_result;
+ rl_array = LoadValue(rl_array, kCoreReg);
+ rl_index = LoadValue(rl_index, kCoreReg);
+
+ if (size == kLong || size == kDouble) {
+ data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
+ } else {
+ data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
+ }
+
+ /* null object? */
+ GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
+
+ if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
+ /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */
+ GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
+ len_offset, kThrowArrayBounds);
+ }
+ if ((size == kLong) || (size == kDouble)) {
+ int reg_addr = AllocTemp();
+ OpLea(reg_addr, rl_array.low_reg, rl_index.low_reg, scale, data_offset);
+ FreeTemp(rl_array.low_reg);
+ FreeTemp(rl_index.low_reg);
+ rl_result = EvalLoc(rl_dest, reg_class, true);
+ LoadBaseIndexedDisp(reg_addr, INVALID_REG, 0, 0, rl_result.low_reg,
+ rl_result.high_reg, size, INVALID_SREG);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ rl_result = EvalLoc(rl_dest, reg_class, true);
+
+ LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale,
+ data_offset, rl_result.low_reg, INVALID_REG, size,
+ INVALID_SREG);
+
+ StoreValue(rl_dest, rl_result);
+ }
+}
+
+/*
+ * Generate array store
+ *
+ */
+void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale)
+{
+ RegisterClass reg_class = oat_reg_class_by_size(size);
+ int len_offset = mirror::Array::LengthOffset().Int32Value();
+ int data_offset;
+
+ if (size == kLong || size == kDouble) {
+ data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
+ } else {
+ data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
+ }
+
+ rl_array = LoadValue(rl_array, kCoreReg);
+ rl_index = LoadValue(rl_index, kCoreReg);
+
+ /* null object? */
+ GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
+
+ if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
+ /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */
+ GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg, len_offset, kThrowArrayBounds);
+ }
+ if ((size == kLong) || (size == kDouble)) {
+ rl_src = LoadValueWide(rl_src, reg_class);
+ } else {
+ rl_src = LoadValue(rl_src, reg_class);
+ }
+ // If the src reg can't be byte accessed, move it to a temp first.
+ if ((size == kSignedByte || size == kUnsignedByte) && rl_src.low_reg >= 4) {
+ int temp = AllocTemp();
+ OpRegCopy(temp, rl_src.low_reg);
+ StoreBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, temp,
+ INVALID_REG, size, INVALID_SREG);
+ } else {
+ StoreBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_src.low_reg,
+ rl_src.high_reg, size, INVALID_SREG);
+ }
+}
+
+/*
+ * Generate array store
+ *
+ */
+void X86Mir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array,
+ RegLocation rl_index, RegLocation rl_src, int scale)
+{
+ int len_offset = mirror::Array::LengthOffset().Int32Value();
+ int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
+
+ FlushAllRegs(); // Use explicit registers
+ LockCallTemps();
+
+ int r_value = TargetReg(kArg0); // Register holding value
+ int r_array_class = TargetReg(kArg1); // Register holding array's Class
+ int r_array = TargetReg(kArg2); // Register holding array
+ int r_index = TargetReg(kArg3); // Register holding index into array
+
+ LoadValueDirectFixed(rl_array, r_array); // Grab array
+ LoadValueDirectFixed(rl_src, r_value); // Grab value
+ LoadValueDirectFixed(rl_index, r_index); // Grab index
+
+ GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE?
+
+ // Store of null?
+ LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL);
+
+ // Get the array's class.
+ LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
+ CallRuntimeHelperRegReg(ENTRYPOINT_OFFSET(pCanPutArrayElementFromCode), r_value,
+ r_array_class, true);
+ // Redo LoadValues in case they didn't survive the call.
+ LoadValueDirectFixed(rl_array, r_array); // Reload array
+ LoadValueDirectFixed(rl_index, r_index); // Reload index
+ LoadValueDirectFixed(rl_src, r_value); // Reload value
+ r_array_class = INVALID_REG;
+
+ // Branch here if value to be stored == null
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ null_value_check->target = target;
+
+ // make an extra temp available for card mark below
+ FreeTemp(TargetReg(kArg1));
+ if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
+ /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */
+ GenRegMemCheck(kCondUge, r_index, r_array, len_offset, kThrowArrayBounds);
+ }
+ StoreBaseIndexedDisp(r_array, r_index, scale,
+ data_offset, r_value, INVALID_REG, kWord, INVALID_SREG);
+ FreeTemp(r_index);
+ if (!mir_graph_->IsConstantNullRef(rl_src)) {
+ MarkGCCard(r_value, r_array);
+ }
+}
+
+void X86Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
+ RegLocation rl_src1, RegLocation rl_shift)
+{
+ // Default implementation is just to ignore the constant case.
+ GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift);
+}
+
+void X86Mir2Lir::GenArithImmOpLong(Instruction::Code opcode,
+ RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2)
+{
+ // Default - bail to non-const handler.
+ GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2);
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
new file mode 100644
index 0000000..c421ef3
--- /dev/null
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2012 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 "codegen_x86.h"
+#include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "x86_lir.h"
+
+#include <string>
+
+namespace art {
+
+//FIXME: restore "static" when usage uncovered
+/*static*/ int core_regs[] = {
+ rAX, rCX, rDX, rBX, rX86_SP, rBP, rSI, rDI
+#ifdef TARGET_REX_SUPPORT
+ r8, r9, r10, r11, r12, r13, r14, 15
+#endif
+};
+/*static*/ int ReservedRegs[] = {rX86_SP};
+/*static*/ int core_temps[] = {rAX, rCX, rDX, rBX};
+/*static*/ int FpRegs[] = {
+ fr0, fr1, fr2, fr3, fr4, fr5, fr6, fr7,
+#ifdef TARGET_REX_SUPPORT
+ fr8, fr9, fr10, fr11, fr12, fr13, fr14, fr15
+#endif
+};
+/*static*/ int fp_temps[] = {
+ fr0, fr1, fr2, fr3, fr4, fr5, fr6, fr7,
+#ifdef TARGET_REX_SUPPORT
+ fr8, fr9, fr10, fr11, fr12, fr13, fr14, fr15
+#endif
+};
+
+RegLocation X86Mir2Lir::LocCReturn()
+{
+ RegLocation res = X86_LOC_C_RETURN;
+ return res;
+}
+
+RegLocation X86Mir2Lir::LocCReturnWide()
+{
+ RegLocation res = X86_LOC_C_RETURN_WIDE;
+ return res;
+}
+
+RegLocation X86Mir2Lir::LocCReturnFloat()
+{
+ RegLocation res = X86_LOC_C_RETURN_FLOAT;
+ return res;
+}
+
+RegLocation X86Mir2Lir::LocCReturnDouble()
+{
+ RegLocation res = X86_LOC_C_RETURN_DOUBLE;
+ return res;
+}
+
+// Return a target-dependent special register.
+int X86Mir2Lir::TargetReg(SpecialTargetRegister reg) {
+ int res = INVALID_REG;
+ switch (reg) {
+ case kSelf: res = rX86_SELF; break;
+ case kSuspend: res = rX86_SUSPEND; break;
+ case kLr: res = rX86_LR; break;
+ case kPc: res = rX86_PC; break;
+ case kSp: res = rX86_SP; break;
+ case kArg0: res = rX86_ARG0; break;
+ case kArg1: res = rX86_ARG1; break;
+ case kArg2: res = rX86_ARG2; break;
+ case kArg3: res = rX86_ARG3; break;
+ case kFArg0: res = rX86_FARG0; break;
+ case kFArg1: res = rX86_FARG1; break;
+ case kFArg2: res = rX86_FARG2; break;
+ case kFArg3: res = rX86_FARG3; break;
+ case kRet0: res = rX86_RET0; break;
+ case kRet1: res = rX86_RET1; break;
+ case kInvokeTgt: res = rX86_INVOKE_TGT; break;
+ case kCount: res = rX86_COUNT; break;
+ }
+ return res;
+}
+
+// Create a double from a pair of singles.
+int X86Mir2Lir::S2d(int low_reg, int high_reg)
+{
+ return X86_S2D(low_reg, high_reg);
+}
+
+// Return mask to strip off fp reg flags and bias.
+uint32_t X86Mir2Lir::FpRegMask()
+{
+ return X86_FP_REG_MASK;
+}
+
+// True if both regs single, both core or both double.
+bool X86Mir2Lir::SameRegType(int reg1, int reg2)
+{
+ return (X86_REGTYPE(reg1) == X86_REGTYPE(reg2));
+}
+
+/*
+ * Decode the register id.
+ */
+uint64_t X86Mir2Lir::GetRegMaskCommon(int reg)
+{
+ uint64_t seed;
+ int shift;
+ int reg_id;
+
+ reg_id = reg & 0xf;
+ /* Double registers in x86 are just a single FP register */
+ seed = 1;
+ /* FP register starts at bit position 16 */
+ shift = X86_FPREG(reg) ? kX86FPReg0 : 0;
+ /* Expand the double register id into single offset */
+ shift += reg_id;
+ return (seed << shift);
+}
+
+uint64_t X86Mir2Lir::GetPCUseDefEncoding()
+{
+ /*
+ * FIXME: might make sense to use a virtual resource encoding bit for pc. Might be
+ * able to clean up some of the x86/Arm_Mips differences
+ */
+ LOG(FATAL) << "Unexpected call to GetPCUseDefEncoding for x86";
+ return 0ULL;
+}
+
+void X86Mir2Lir::SetupTargetResourceMasks(LIR* lir)
+{
+ DCHECK_EQ(cu_->instruction_set, kX86);
+
+ // X86-specific resource map setup here.
+ uint64_t flags = X86Mir2Lir::EncodingMap[lir->opcode].flags;
+
+ if (flags & REG_USE_SP) {
+ lir->use_mask |= ENCODE_X86_REG_SP;
+ }
+
+ if (flags & REG_DEF_SP) {
+ lir->def_mask |= ENCODE_X86_REG_SP;
+ }
+
+ if (flags & REG_DEFA) {
+ SetupRegMask(&lir->def_mask, rAX);
+ }
+
+ if (flags & REG_DEFD) {
+ SetupRegMask(&lir->def_mask, rDX);
+ }
+ if (flags & REG_USEA) {
+ SetupRegMask(&lir->use_mask, rAX);
+ }
+
+ if (flags & REG_USEC) {
+ SetupRegMask(&lir->use_mask, rCX);
+ }
+
+ if (flags & REG_USED) {
+ SetupRegMask(&lir->use_mask, rDX);
+ }
+}
+
+/* For dumping instructions */
+static const char* x86RegName[] = {
+ "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
+};
+
+static const char* x86CondName[] = {
+ "O",
+ "NO",
+ "B/NAE/C",
+ "NB/AE/NC",
+ "Z/EQ",
+ "NZ/NE",
+ "BE/NA",
+ "NBE/A",
+ "S",
+ "NS",
+ "P/PE",
+ "NP/PO",
+ "L/NGE",
+ "NL/GE",
+ "LE/NG",
+ "NLE/G"
+};
+
+/*
+ * Interpret a format string and build a string no longer than size
+ * See format key in Assemble.cc.
+ */
+std::string X86Mir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned char* base_addr) {
+ std::string buf;
+ size_t i = 0;
+ size_t fmt_len = strlen(fmt);
+ while (i < fmt_len) {
+ if (fmt[i] != '!') {
+ buf += fmt[i];
+ i++;
+ } else {
+ i++;
+ DCHECK_LT(i, fmt_len);
+ char operand_number_ch = fmt[i];
+ i++;
+ if (operand_number_ch == '!') {
+ buf += "!";
+ } else {
+ int operand_number = operand_number_ch - '0';
+ DCHECK_LT(operand_number, 6); // Expect upto 6 LIR operands.
+ DCHECK_LT(i, fmt_len);
+ int operand = lir->operands[operand_number];
+ switch (fmt[i]) {
+ case 'c':
+ DCHECK_LT(static_cast<size_t>(operand), sizeof(x86CondName));
+ buf += x86CondName[operand];
+ break;
+ case 'd':
+ buf += StringPrintf("%d", operand);
+ break;
+ case 'p': {
+ SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(operand);
+ buf += StringPrintf("0x%08x", tab_rec->offset);
+ break;
+ }
+ case 'r':
+ if (X86_FPREG(operand) || X86_DOUBLEREG(operand)) {
+ int fp_reg = operand & X86_FP_REG_MASK;
+ buf += StringPrintf("xmm%d", fp_reg);
+ } else {
+ DCHECK_LT(static_cast<size_t>(operand), sizeof(x86RegName));
+ buf += x86RegName[operand];
+ }
+ break;
+ case 't':
+ buf += StringPrintf("0x%08x (L%p)",
+ reinterpret_cast<uint32_t>(base_addr)
+ + lir->offset + operand, lir->target);
+ break;
+ default:
+ buf += StringPrintf("DecodeError '%c'", fmt[i]);
+ break;
+ }
+ i++;
+ }
+ }
+ }
+ return buf;
+}
+
+void X86Mir2Lir::DumpResourceMask(LIR *x86LIR, uint64_t mask, const char *prefix)
+{
+ char buf[256];
+ buf[0] = 0;
+
+ if (mask == ENCODE_ALL) {
+ strcpy(buf, "all");
+ } else {
+ char num[8];
+ int i;
+
+ for (i = 0; i < kX86RegEnd; i++) {
+ if (mask & (1ULL << i)) {
+ sprintf(num, "%d ", i);
+ strcat(buf, num);
+ }
+ }
+
+ if (mask & ENCODE_CCODE) {
+ strcat(buf, "cc ");
+ }
+ /* Memory bits */
+ if (x86LIR && (mask & ENCODE_DALVIK_REG)) {
+ sprintf(buf + strlen(buf), "dr%d%s", x86LIR->alias_info & 0xffff,
+ (x86LIR->alias_info & 0x80000000) ? "(+1)" : "");
+ }
+ if (mask & ENCODE_LITERAL) {
+ strcat(buf, "lit ");
+ }
+
+ if (mask & ENCODE_HEAP_REF) {
+ strcat(buf, "heap ");
+ }
+ if (mask & ENCODE_MUST_NOT_ALIAS) {
+ strcat(buf, "noalias ");
+ }
+ }
+ if (buf[0]) {
+ LOG(INFO) << prefix << ": " << buf;
+ }
+}
+
+void X86Mir2Lir::AdjustSpillMask() {
+ // Adjustment for LR spilling, x86 has no LR so nothing to do here
+ core_spill_mask_ |= (1 << rRET);
+ num_core_spills_++;
+}
+
+/*
+ * Mark a callee-save fp register as promoted. Note that
+ * vpush/vpop uses contiguous register lists so we must
+ * include any holes in the mask. Associate holes with
+ * Dalvik register INVALID_VREG (0xFFFFU).
+ */
+void X86Mir2Lir::MarkPreservedSingle(int v_reg, int reg)
+{
+ UNIMPLEMENTED(WARNING) << "MarkPreservedSingle";
+#if 0
+ LOG(FATAL) << "No support yet for promoted FP regs";
+#endif
+}
+
+void X86Mir2Lir::FlushRegWide(int reg1, int reg2)
+{
+ RegisterInfo* info1 = GetRegInfo(reg1);
+ RegisterInfo* info2 = GetRegInfo(reg2);
+ DCHECK(info1 && info2 && info1->pair && info2->pair &&
+ (info1->partner == info2->reg) &&
+ (info2->partner == info1->reg));
+ if ((info1->live && info1->dirty) || (info2->live && info2->dirty)) {
+ if (!(info1->is_temp && info2->is_temp)) {
+ /* Should not happen. If it does, there's a problem in eval_loc */
+ LOG(FATAL) << "Long half-temp, half-promoted";
+ }
+
+ info1->dirty = false;
+ info2->dirty = false;
+ if (mir_graph_->SRegToVReg(info2->s_reg) < mir_graph_->SRegToVReg(info1->s_reg))
+ info1 = info2;
+ int v_reg = mir_graph_->SRegToVReg(info1->s_reg);
+ StoreBaseDispWide(rX86_SP, VRegOffset(v_reg), info1->reg, info1->partner);
+ }
+}
+
+void X86Mir2Lir::FlushReg(int reg)
+{
+ RegisterInfo* info = GetRegInfo(reg);
+ if (info->live && info->dirty) {
+ info->dirty = false;
+ int v_reg = mir_graph_->SRegToVReg(info->s_reg);
+ StoreBaseDisp(rX86_SP, VRegOffset(v_reg), reg, kWord);
+ }
+}
+
+/* Give access to the target-dependent FP register encoding to common code */
+bool X86Mir2Lir::IsFpReg(int reg) {
+ return X86_FPREG(reg);
+}
+
+/* Clobber all regs that might be used by an external C call */
+void X86Mir2Lir::ClobberCalleeSave()
+{
+ Clobber(rAX);
+ Clobber(rCX);
+ Clobber(rDX);
+}
+
+RegLocation X86Mir2Lir::GetReturnWideAlt() {
+ RegLocation res = LocCReturnWide();
+ CHECK(res.low_reg == rAX);
+ CHECK(res.high_reg == rDX);
+ Clobber(rAX);
+ Clobber(rDX);
+ MarkInUse(rAX);
+ MarkInUse(rDX);
+ MarkPair(res.low_reg, res.high_reg);
+ return res;
+}
+
+RegLocation X86Mir2Lir::GetReturnAlt()
+{
+ RegLocation res = LocCReturn();
+ res.low_reg = rDX;
+ Clobber(rDX);
+ MarkInUse(rDX);
+ return res;
+}
+
+X86Mir2Lir::RegisterInfo* X86Mir2Lir::GetRegInfo(int reg)
+{
+ return X86_FPREG(reg) ? ®_pool_->FPRegs[reg & X86_FP_REG_MASK]
+ : ®_pool_->core_regs[reg];
+}
+
+/* To be used when explicitly managing register use */
+void X86Mir2Lir::LockCallTemps()
+{
+ LockTemp(rX86_ARG0);
+ LockTemp(rX86_ARG1);
+ LockTemp(rX86_ARG2);
+ LockTemp(rX86_ARG3);
+}
+
+/* To be used when explicitly managing register use */
+void X86Mir2Lir::FreeCallTemps()
+{
+ FreeTemp(rX86_ARG0);
+ FreeTemp(rX86_ARG1);
+ FreeTemp(rX86_ARG2);
+ FreeTemp(rX86_ARG3);
+}
+
+void X86Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind)
+{
+#if ANDROID_SMP != 0
+ // TODO: optimize fences
+ NewLIR0(kX86Mfence);
+#endif
+}
+/*
+ * Alloc a pair of core registers, or a double. Low reg in low byte,
+ * high reg in next byte.
+ */
+int X86Mir2Lir::AllocTypedTempPair(bool fp_hint,
+ int reg_class)
+{
+ int high_reg;
+ int low_reg;
+ int res = 0;
+
+ if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg)) {
+ low_reg = AllocTempDouble();
+ high_reg = low_reg + 1;
+ res = (low_reg & 0xff) | ((high_reg & 0xff) << 8);
+ return res;
+ }
+
+ low_reg = AllocTemp();
+ high_reg = AllocTemp();
+ res = (low_reg & 0xff) | ((high_reg & 0xff) << 8);
+ return res;
+}
+
+int X86Mir2Lir::AllocTypedTemp(bool fp_hint, int reg_class) {
+ if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg)) {
+ return AllocTempFloat();
+ }
+ return AllocTemp();
+}
+
+void X86Mir2Lir::CompilerInitializeRegAlloc() {
+ int num_regs = sizeof(core_regs)/sizeof(*core_regs);
+ int num_reserved = sizeof(ReservedRegs)/sizeof(*ReservedRegs);
+ int num_temps = sizeof(core_temps)/sizeof(*core_temps);
+ int num_fp_regs = sizeof(FpRegs)/sizeof(*FpRegs);
+ int num_fp_temps = sizeof(fp_temps)/sizeof(*fp_temps);
+ reg_pool_ = static_cast<RegisterPool*>(arena_->NewMem(sizeof(*reg_pool_), true,
+ ArenaAllocator::kAllocRegAlloc));
+ reg_pool_->num_core_regs = num_regs;
+ reg_pool_->core_regs =
+ static_cast<RegisterInfo*>(arena_->NewMem(num_regs * sizeof(*reg_pool_->core_regs), true,
+ ArenaAllocator::kAllocRegAlloc));
+ reg_pool_->num_fp_regs = num_fp_regs;
+ reg_pool_->FPRegs =
+ static_cast<RegisterInfo *>(arena_->NewMem(num_fp_regs * sizeof(*reg_pool_->FPRegs), true,
+ ArenaAllocator::kAllocRegAlloc));
+ CompilerInitPool(reg_pool_->core_regs, core_regs, reg_pool_->num_core_regs);
+ CompilerInitPool(reg_pool_->FPRegs, FpRegs, reg_pool_->num_fp_regs);
+ // Keep special registers from being allocated
+ for (int i = 0; i < num_reserved; i++) {
+ MarkInUse(ReservedRegs[i]);
+ }
+ // Mark temp regs - all others not in use can be used for promotion
+ for (int i = 0; i < num_temps; i++) {
+ MarkTemp(core_temps[i]);
+ }
+ for (int i = 0; i < num_fp_temps; i++) {
+ MarkTemp(fp_temps[i]);
+ }
+}
+
+void X86Mir2Lir::FreeRegLocTemps(RegLocation rl_keep,
+ RegLocation rl_free)
+{
+ if ((rl_free.low_reg != rl_keep.low_reg) && (rl_free.low_reg != rl_keep.high_reg) &&
+ (rl_free.high_reg != rl_keep.low_reg) && (rl_free.high_reg != rl_keep.high_reg)) {
+ // No overlap, free both
+ FreeTemp(rl_free.low_reg);
+ FreeTemp(rl_free.high_reg);
+ }
+}
+
+void X86Mir2Lir::SpillCoreRegs() {
+ if (num_core_spills_ == 0) {
+ return;
+ }
+ // Spill mask not including fake return address register
+ uint32_t mask = core_spill_mask_ & ~(1 << rRET);
+ int offset = frame_size_ - (4 * num_core_spills_);
+ for (int reg = 0; mask; mask >>= 1, reg++) {
+ if (mask & 0x1) {
+ StoreWordDisp(rX86_SP, offset, reg);
+ offset += 4;
+ }
+ }
+}
+
+void X86Mir2Lir::UnSpillCoreRegs() {
+ if (num_core_spills_ == 0) {
+ return;
+ }
+ // Spill mask not including fake return address register
+ uint32_t mask = core_spill_mask_ & ~(1 << rRET);
+ int offset = frame_size_ - (4 * num_core_spills_);
+ for (int reg = 0; mask; mask >>= 1, reg++) {
+ if (mask & 0x1) {
+ LoadWordDisp(rX86_SP, offset, reg);
+ offset += 4;
+ }
+ }
+}
+
+bool X86Mir2Lir::IsUnconditionalBranch(LIR* lir)
+{
+ return (lir->opcode == kX86Jmp8 || lir->opcode == kX86Jmp32);
+}
+
+X86Mir2Lir::X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
+ : Mir2Lir(cu, mir_graph, arena) {
+ for (int i = 0; i < kX86Last; i++) {
+ if (X86Mir2Lir::EncodingMap[i].opcode != i) {
+ LOG(FATAL) << "Encoding order for " << X86Mir2Lir::EncodingMap[i].name
+ << " is wrong: expecting " << i << ", seeing "
+ << static_cast<int>(X86Mir2Lir::EncodingMap[i].opcode);
+ }
+ }
+}
+
+Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena) {
+ return new X86Mir2Lir(cu, mir_graph, arena);
+}
+
+// Not used in x86
+int X86Mir2Lir::LoadHelper(int offset)
+{
+ LOG(FATAL) << "Unexpected use of LoadHelper in x86";
+ return INVALID_REG;
+}
+
+uint64_t X86Mir2Lir::GetTargetInstFlags(int opcode)
+{
+ return X86Mir2Lir::EncodingMap[opcode].flags;
+}
+
+const char* X86Mir2Lir::GetTargetInstName(int opcode)
+{
+ return X86Mir2Lir::EncodingMap[opcode].name;
+}
+
+const char* X86Mir2Lir::GetTargetInstFmt(int opcode)
+{
+ return X86Mir2Lir::EncodingMap[opcode].fmt;
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc
new file mode 100644
index 0000000..fb07ff1
--- /dev/null
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2012 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 "codegen_x86.h"
+#include "dex/quick/mir_to_lir-inl.h"
+#include "x86_lir.h"
+
+namespace art {
+
+/* This file contains codegen for the X86 ISA */
+
+LIR* X86Mir2Lir::OpFpRegCopy(int r_dest, int r_src)
+{
+ int opcode;
+ /* must be both DOUBLE or both not DOUBLE */
+ DCHECK_EQ(X86_DOUBLEREG(r_dest), X86_DOUBLEREG(r_src));
+ if (X86_DOUBLEREG(r_dest)) {
+ opcode = kX86MovsdRR;
+ } else {
+ if (X86_SINGLEREG(r_dest)) {
+ if (X86_SINGLEREG(r_src)) {
+ opcode = kX86MovssRR;
+ } else { // Fpr <- Gpr
+ opcode = kX86MovdxrRR;
+ }
+ } else { // Gpr <- Fpr
+ DCHECK(X86_SINGLEREG(r_src));
+ opcode = kX86MovdrxRR;
+ }
+ }
+ DCHECK_NE((EncodingMap[opcode].flags & IS_BINARY_OP), 0ULL);
+ LIR* res = RawLIR(current_dalvik_offset_, opcode, r_dest, r_src);
+ if (r_dest == r_src) {
+ res->flags.is_nop = true;
+ }
+ return res;
+}
+
+bool X86Mir2Lir::InexpensiveConstantInt(int32_t value)
+{
+ return true;
+}
+
+bool X86Mir2Lir::InexpensiveConstantFloat(int32_t value)
+{
+ return false;
+}
+
+bool X86Mir2Lir::InexpensiveConstantLong(int64_t value)
+{
+ return true;
+}
+
+bool X86Mir2Lir::InexpensiveConstantDouble(int64_t value)
+{
+ return false; // TUNING
+}
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool. If target is
+ * a high register, build constant into a low register and copy.
+ *
+ * No additional register clobbering operation performed. Use this version when
+ * 1) r_dest is freshly returned from AllocTemp or
+ * 2) The codegen is under fixed register usage
+ */
+LIR* X86Mir2Lir::LoadConstantNoClobber(int r_dest, int value)
+{
+ int r_dest_save = r_dest;
+ if (X86_FPREG(r_dest)) {
+ if (value == 0) {
+ return NewLIR2(kX86XorpsRR, r_dest, r_dest);
+ }
+ DCHECK(X86_SINGLEREG(r_dest));
+ r_dest = AllocTemp();
+ }
+
+ LIR *res;
+ if (value == 0) {
+ res = NewLIR2(kX86Xor32RR, r_dest, r_dest);
+ } else {
+ // Note, there is no byte immediate form of a 32 bit immediate move.
+ res = NewLIR2(kX86Mov32RI, r_dest, value);
+ }
+
+ if (X86_FPREG(r_dest_save)) {
+ NewLIR2(kX86MovdxrRR, r_dest_save, r_dest);
+ FreeTemp(r_dest);
+ }
+
+ return res;
+}
+
+LIR* X86Mir2Lir::OpUnconditionalBranch(LIR* target)
+{
+ LIR* res = NewLIR1(kX86Jmp8, 0 /* offset to be patched during assembly*/ );
+ res->target = target;
+ return res;
+}
+
+LIR* X86Mir2Lir::OpCondBranch(ConditionCode cc, LIR* target)
+{
+ LIR* branch = NewLIR2(kX86Jcc8, 0 /* offset to be patched */,
+ X86ConditionEncoding(cc));
+ branch->target = target;
+ return branch;
+}
+
+LIR* X86Mir2Lir::OpReg(OpKind op, int r_dest_src)
+{
+ X86OpCode opcode = kX86Bkpt;
+ switch (op) {
+ case kOpNeg: opcode = kX86Neg32R; break;
+ case kOpNot: opcode = kX86Not32R; break;
+ case kOpBlx: opcode = kX86CallR; break;
+ default:
+ LOG(FATAL) << "Bad case in OpReg " << op;
+ }
+ return NewLIR1(opcode, r_dest_src);
+}
+
+LIR* X86Mir2Lir::OpRegImm(OpKind op, int r_dest_src1, int value)
+{
+ X86OpCode opcode = kX86Bkpt;
+ bool byte_imm = IS_SIMM8(value);
+ DCHECK(!X86_FPREG(r_dest_src1));
+ switch (op) {
+ case kOpLsl: opcode = kX86Sal32RI; break;
+ case kOpLsr: opcode = kX86Shr32RI; break;
+ case kOpAsr: opcode = kX86Sar32RI; break;
+ case kOpAdd: opcode = byte_imm ? kX86Add32RI8 : kX86Add32RI; break;
+ case kOpOr: opcode = byte_imm ? kX86Or32RI8 : kX86Or32RI; break;
+ case kOpAdc: opcode = byte_imm ? kX86Adc32RI8 : kX86Adc32RI; break;
+ //case kOpSbb: opcode = kX86Sbb32RI; break;
+ case kOpAnd: opcode = byte_imm ? kX86And32RI8 : kX86And32RI; break;
+ case kOpSub: opcode = byte_imm ? kX86Sub32RI8 : kX86Sub32RI; break;
+ case kOpXor: opcode = byte_imm ? kX86Xor32RI8 : kX86Xor32RI; break;
+ case kOpCmp: opcode = byte_imm ? kX86Cmp32RI8 : kX86Cmp32RI; break;
+ case kOpMov: return LoadConstantNoClobber(r_dest_src1, value);
+ case kOpMul:
+ opcode = byte_imm ? kX86Imul32RRI8 : kX86Imul32RRI;
+ return NewLIR3(opcode, r_dest_src1, r_dest_src1, value);
+ default:
+ LOG(FATAL) << "Bad case in OpRegImm " << op;
+ }
+ return NewLIR2(opcode, r_dest_src1, value);
+}
+
+LIR* X86Mir2Lir::OpRegReg(OpKind op, int r_dest_src1, int r_src2)
+{
+ X86OpCode opcode = kX86Nop;
+ bool src2_must_be_cx = false;
+ switch (op) {
+ // X86 unary opcodes
+ case kOpMvn:
+ OpRegCopy(r_dest_src1, r_src2);
+ return OpReg(kOpNot, r_dest_src1);
+ case kOpNeg:
+ OpRegCopy(r_dest_src1, r_src2);
+ return OpReg(kOpNeg, r_dest_src1);
+ // X86 binary opcodes
+ case kOpSub: opcode = kX86Sub32RR; break;
+ case kOpSbc: opcode = kX86Sbb32RR; break;
+ case kOpLsl: opcode = kX86Sal32RC; src2_must_be_cx = true; break;
+ case kOpLsr: opcode = kX86Shr32RC; src2_must_be_cx = true; break;
+ case kOpAsr: opcode = kX86Sar32RC; src2_must_be_cx = true; break;
+ case kOpMov: opcode = kX86Mov32RR; break;
+ case kOpCmp: opcode = kX86Cmp32RR; break;
+ case kOpAdd: opcode = kX86Add32RR; break;
+ case kOpAdc: opcode = kX86Adc32RR; break;
+ case kOpAnd: opcode = kX86And32RR; break;
+ case kOpOr: opcode = kX86Or32RR; break;
+ case kOpXor: opcode = kX86Xor32RR; break;
+ case kOp2Byte:
+ // Use shifts instead of a byte operand if the source can't be byte accessed.
+ if (r_src2 >= 4) {
+ NewLIR2(kX86Mov32RR, r_dest_src1, r_src2);
+ NewLIR2(kX86Sal32RI, r_dest_src1, 24);
+ return NewLIR2(kX86Sar32RI, r_dest_src1, 24);
+ } else {
+ opcode = kX86Movsx8RR;
+ }
+ break;
+ case kOp2Short: opcode = kX86Movsx16RR; break;
+ case kOp2Char: opcode = kX86Movzx16RR; break;
+ case kOpMul: opcode = kX86Imul32RR; break;
+ default:
+ LOG(FATAL) << "Bad case in OpRegReg " << op;
+ break;
+ }
+ CHECK(!src2_must_be_cx || r_src2 == rCX);
+ return NewLIR2(opcode, r_dest_src1, r_src2);
+}
+
+LIR* X86Mir2Lir::OpRegMem(OpKind op, int r_dest, int rBase,
+ int offset)
+{
+ X86OpCode opcode = kX86Nop;
+ switch (op) {
+ // X86 binary opcodes
+ case kOpSub: opcode = kX86Sub32RM; break;
+ case kOpMov: opcode = kX86Mov32RM; break;
+ case kOpCmp: opcode = kX86Cmp32RM; break;
+ case kOpAdd: opcode = kX86Add32RM; break;
+ case kOpAnd: opcode = kX86And32RM; break;
+ case kOpOr: opcode = kX86Or32RM; break;
+ case kOpXor: opcode = kX86Xor32RM; break;
+ case kOp2Byte: opcode = kX86Movsx8RM; break;
+ case kOp2Short: opcode = kX86Movsx16RM; break;
+ case kOp2Char: opcode = kX86Movzx16RM; break;
+ case kOpMul:
+ default:
+ LOG(FATAL) << "Bad case in OpRegMem " << op;
+ break;
+ }
+ return NewLIR3(opcode, r_dest, rBase, offset);
+}
+
+LIR* X86Mir2Lir::OpRegRegReg(OpKind op, int r_dest, int r_src1,
+ int r_src2)
+{
+ if (r_dest != r_src1 && r_dest != r_src2) {
+ if (op == kOpAdd) { // lea special case, except can't encode rbp as base
+ if (r_src1 == r_src2) {
+ OpRegCopy(r_dest, r_src1);
+ return OpRegImm(kOpLsl, r_dest, 1);
+ } else if (r_src1 != rBP) {
+ return NewLIR5(kX86Lea32RA, r_dest, r_src1 /* base */,
+ r_src2 /* index */, 0 /* scale */, 0 /* disp */);
+ } else {
+ return NewLIR5(kX86Lea32RA, r_dest, r_src2 /* base */,
+ r_src1 /* index */, 0 /* scale */, 0 /* disp */);
+ }
+ } else {
+ OpRegCopy(r_dest, r_src1);
+ return OpRegReg(op, r_dest, r_src2);
+ }
+ } else if (r_dest == r_src1) {
+ return OpRegReg(op, r_dest, r_src2);
+ } else { // r_dest == r_src2
+ switch (op) {
+ case kOpSub: // non-commutative
+ OpReg(kOpNeg, r_dest);
+ op = kOpAdd;
+ break;
+ case kOpSbc:
+ case kOpLsl: case kOpLsr: case kOpAsr: case kOpRor: {
+ int t_reg = AllocTemp();
+ OpRegCopy(t_reg, r_src1);
+ OpRegReg(op, t_reg, r_src2);
+ LIR* res = OpRegCopy(r_dest, t_reg);
+ FreeTemp(t_reg);
+ return res;
+ }
+ case kOpAdd: // commutative
+ case kOpOr:
+ case kOpAdc:
+ case kOpAnd:
+ case kOpXor:
+ break;
+ default:
+ LOG(FATAL) << "Bad case in OpRegRegReg " << op;
+ }
+ return OpRegReg(op, r_dest, r_src1);
+ }
+}
+
+LIR* X86Mir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src,
+ int value)
+{
+ if (op == kOpMul) {
+ X86OpCode opcode = IS_SIMM8(value) ? kX86Imul32RRI8 : kX86Imul32RRI;
+ return NewLIR3(opcode, r_dest, r_src, value);
+ } else if (op == kOpAnd) {
+ if (value == 0xFF && r_src < 4) {
+ return NewLIR2(kX86Movzx8RR, r_dest, r_src);
+ } else if (value == 0xFFFF) {
+ return NewLIR2(kX86Movzx16RR, r_dest, r_src);
+ }
+ }
+ if (r_dest != r_src) {
+ if (false && op == kOpLsl && value >= 0 && value <= 3) { // lea shift special case
+ // TODO: fix bug in LEA encoding when disp == 0
+ return NewLIR5(kX86Lea32RA, r_dest, r5sib_no_base /* base */,
+ r_src /* index */, value /* scale */, 0 /* disp */);
+ } else if (op == kOpAdd) { // lea add special case
+ return NewLIR5(kX86Lea32RA, r_dest, r_src /* base */,
+ r4sib_no_index /* index */, 0 /* scale */, value /* disp */);
+ }
+ OpRegCopy(r_dest, r_src);
+ }
+ return OpRegImm(op, r_dest, value);
+}
+
+LIR* X86Mir2Lir::OpThreadMem(OpKind op, int thread_offset)
+{
+ X86OpCode opcode = kX86Bkpt;
+ switch (op) {
+ case kOpBlx: opcode = kX86CallT; break;
+ default:
+ LOG(FATAL) << "Bad opcode: " << op;
+ break;
+ }
+ return NewLIR1(opcode, thread_offset);
+}
+
+LIR* X86Mir2Lir::OpMem(OpKind op, int rBase, int disp)
+{
+ X86OpCode opcode = kX86Bkpt;
+ switch (op) {
+ case kOpBlx: opcode = kX86CallM; break;
+ default:
+ LOG(FATAL) << "Bad opcode: " << op;
+ break;
+ }
+ return NewLIR2(opcode, rBase, disp);
+}
+
+LIR* X86Mir2Lir::LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value)
+{
+ int32_t val_lo = Low32Bits(value);
+ int32_t val_hi = High32Bits(value);
+ LIR *res;
+ if (X86_FPREG(r_dest_lo)) {
+ DCHECK(X86_FPREG(r_dest_hi)); // ignore r_dest_hi
+ if (value == 0) {
+ return NewLIR2(kX86XorpsRR, r_dest_lo, r_dest_lo);
+ } else {
+ if (val_lo == 0) {
+ res = NewLIR2(kX86XorpsRR, r_dest_lo, r_dest_lo);
+ } else {
+ res = LoadConstantNoClobber(r_dest_lo, val_lo);
+ }
+ if (val_hi != 0) {
+ LoadConstantNoClobber(r_dest_hi, val_hi);
+ NewLIR2(kX86PsllqRI, r_dest_hi, 32);
+ NewLIR2(kX86OrpsRR, r_dest_lo, r_dest_hi);
+ }
+ }
+ } else {
+ res = LoadConstantNoClobber(r_dest_lo, val_lo);
+ LoadConstantNoClobber(r_dest_hi, val_hi);
+ }
+ return res;
+}
+
+LIR* X86Mir2Lir::LoadBaseIndexedDisp(int rBase, int r_index, int scale,
+ int displacement, int r_dest, int r_dest_hi, OpSize size,
+ int s_reg) {
+ LIR *load = NULL;
+ LIR *load2 = NULL;
+ bool is_array = r_index != INVALID_REG;
+ bool pair = false;
+ bool is64bit = false;
+ X86OpCode opcode = kX86Nop;
+ switch (size) {
+ case kLong:
+ case kDouble:
+ is64bit = true;
+ if (X86_FPREG(r_dest)) {
+ opcode = is_array ? kX86MovsdRA : kX86MovsdRM;
+ if (X86_SINGLEREG(r_dest)) {
+ DCHECK(X86_FPREG(r_dest_hi));
+ DCHECK_EQ(r_dest, (r_dest_hi - 1));
+ r_dest = S2d(r_dest, r_dest_hi);
+ }
+ r_dest_hi = r_dest + 1;
+ } else {
+ pair = true;
+ opcode = is_array ? kX86Mov32RA : kX86Mov32RM;
+ }
+ // TODO: double store is to unaligned address
+ DCHECK_EQ((displacement & 0x3), 0);
+ break;
+ case kWord:
+ case kSingle:
+ opcode = is_array ? kX86Mov32RA : kX86Mov32RM;
+ if (X86_FPREG(r_dest)) {
+ opcode = is_array ? kX86MovssRA : kX86MovssRM;
+ DCHECK(X86_SINGLEREG(r_dest));
+ }
+ DCHECK_EQ((displacement & 0x3), 0);
+ break;
+ case kUnsignedHalf:
+ opcode = is_array ? kX86Movzx16RA : kX86Movzx16RM;
+ DCHECK_EQ((displacement & 0x1), 0);
+ break;
+ case kSignedHalf:
+ opcode = is_array ? kX86Movsx16RA : kX86Movsx16RM;
+ DCHECK_EQ((displacement & 0x1), 0);
+ break;
+ case kUnsignedByte:
+ opcode = is_array ? kX86Movzx8RA : kX86Movzx8RM;
+ break;
+ case kSignedByte:
+ opcode = is_array ? kX86Movsx8RA : kX86Movsx8RM;
+ break;
+ default:
+ LOG(FATAL) << "Bad case in LoadBaseIndexedDispBody";
+ }
+
+ if (!is_array) {
+ if (!pair) {
+ load = NewLIR3(opcode, r_dest, rBase, displacement + LOWORD_OFFSET);
+ } else {
+ if (rBase == r_dest) {
+ load2 = NewLIR3(opcode, r_dest_hi, rBase,
+ displacement + HIWORD_OFFSET);
+ load = NewLIR3(opcode, r_dest, rBase, displacement + LOWORD_OFFSET);
+ } else {
+ load = NewLIR3(opcode, r_dest, rBase, displacement + LOWORD_OFFSET);
+ load2 = NewLIR3(opcode, r_dest_hi, rBase,
+ displacement + HIWORD_OFFSET);
+ }
+ }
+ if (rBase == rX86_SP) {
+ AnnotateDalvikRegAccess(load, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
+ true /* is_load */, is64bit);
+ if (pair) {
+ AnnotateDalvikRegAccess(load2, (displacement + HIWORD_OFFSET) >> 2,
+ true /* is_load */, is64bit);
+ }
+ }
+ } else {
+ if (!pair) {
+ load = NewLIR5(opcode, r_dest, rBase, r_index, scale,
+ displacement + LOWORD_OFFSET);
+ } else {
+ if (rBase == r_dest) {
+ load2 = NewLIR5(opcode, r_dest_hi, rBase, r_index, scale,
+ displacement + HIWORD_OFFSET);
+ load = NewLIR5(opcode, r_dest, rBase, r_index, scale,
+ displacement + LOWORD_OFFSET);
+ } else {
+ load = NewLIR5(opcode, r_dest, rBase, r_index, scale,
+ displacement + LOWORD_OFFSET);
+ load2 = NewLIR5(opcode, r_dest_hi, rBase, r_index, scale,
+ displacement + HIWORD_OFFSET);
+ }
+ }
+ }
+
+ return load;
+}
+
+/* Load value from base + scaled index. */
+LIR* X86Mir2Lir::LoadBaseIndexed(int rBase,
+ int r_index, int r_dest, int scale, OpSize size) {
+ return LoadBaseIndexedDisp(rBase, r_index, scale, 0,
+ r_dest, INVALID_REG, size, INVALID_SREG);
+}
+
+LIR* X86Mir2Lir::LoadBaseDisp(int rBase, int displacement,
+ int r_dest, OpSize size, int s_reg) {
+ return LoadBaseIndexedDisp(rBase, INVALID_REG, 0, displacement,
+ r_dest, INVALID_REG, size, s_reg);
+}
+
+LIR* X86Mir2Lir::LoadBaseDispWide(int rBase, int displacement,
+ int r_dest_lo, int r_dest_hi, int s_reg) {
+ return LoadBaseIndexedDisp(rBase, INVALID_REG, 0, displacement,
+ r_dest_lo, r_dest_hi, kLong, s_reg);
+}
+
+LIR* X86Mir2Lir::StoreBaseIndexedDisp(int rBase, int r_index, int scale,
+ int displacement, int r_src, int r_src_hi, OpSize size,
+ int s_reg) {
+ LIR *store = NULL;
+ LIR *store2 = NULL;
+ bool is_array = r_index != INVALID_REG;
+ bool pair = false;
+ bool is64bit = false;
+ X86OpCode opcode = kX86Nop;
+ switch (size) {
+ case kLong:
+ case kDouble:
+ is64bit = true;
+ if (X86_FPREG(r_src)) {
+ opcode = is_array ? kX86MovsdAR : kX86MovsdMR;
+ if (X86_SINGLEREG(r_src)) {
+ DCHECK(X86_FPREG(r_src_hi));
+ DCHECK_EQ(r_src, (r_src_hi - 1));
+ r_src = S2d(r_src, r_src_hi);
+ }
+ r_src_hi = r_src + 1;
+ } else {
+ pair = true;
+ opcode = is_array ? kX86Mov32AR : kX86Mov32MR;
+ }
+ // TODO: double store is to unaligned address
+ DCHECK_EQ((displacement & 0x3), 0);
+ break;
+ case kWord:
+ case kSingle:
+ opcode = is_array ? kX86Mov32AR : kX86Mov32MR;
+ if (X86_FPREG(r_src)) {
+ opcode = is_array ? kX86MovssAR : kX86MovssMR;
+ DCHECK(X86_SINGLEREG(r_src));
+ }
+ DCHECK_EQ((displacement & 0x3), 0);
+ break;
+ case kUnsignedHalf:
+ case kSignedHalf:
+ opcode = is_array ? kX86Mov16AR : kX86Mov16MR;
+ DCHECK_EQ((displacement & 0x1), 0);
+ break;
+ case kUnsignedByte:
+ case kSignedByte:
+ opcode = is_array ? kX86Mov8AR : kX86Mov8MR;
+ break;
+ default:
+ LOG(FATAL) << "Bad case in LoadBaseIndexedDispBody";
+ }
+
+ if (!is_array) {
+ if (!pair) {
+ store = NewLIR3(opcode, rBase, displacement + LOWORD_OFFSET, r_src);
+ } else {
+ store = NewLIR3(opcode, rBase, displacement + LOWORD_OFFSET, r_src);
+ store2 = NewLIR3(opcode, rBase, displacement + HIWORD_OFFSET, r_src_hi);
+ }
+ if (rBase == rX86_SP) {
+ AnnotateDalvikRegAccess(store, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
+ false /* is_load */, is64bit);
+ if (pair) {
+ AnnotateDalvikRegAccess(store2, (displacement + HIWORD_OFFSET) >> 2,
+ false /* is_load */, is64bit);
+ }
+ }
+ } else {
+ if (!pair) {
+ store = NewLIR5(opcode, rBase, r_index, scale,
+ displacement + LOWORD_OFFSET, r_src);
+ } else {
+ store = NewLIR5(opcode, rBase, r_index, scale,
+ displacement + LOWORD_OFFSET, r_src);
+ store2 = NewLIR5(opcode, rBase, r_index, scale,
+ displacement + HIWORD_OFFSET, r_src_hi);
+ }
+ }
+
+ return store;
+}
+
+/* store value base base + scaled index. */
+LIR* X86Mir2Lir::StoreBaseIndexed(int rBase, int r_index, int r_src,
+ int scale, OpSize size)
+{
+ return StoreBaseIndexedDisp(rBase, r_index, scale, 0,
+ r_src, INVALID_REG, size, INVALID_SREG);
+}
+
+LIR* X86Mir2Lir::StoreBaseDisp(int rBase, int displacement,
+ int r_src, OpSize size)
+{
+ return StoreBaseIndexedDisp(rBase, INVALID_REG, 0,
+ displacement, r_src, INVALID_REG, size,
+ INVALID_SREG);
+}
+
+LIR* X86Mir2Lir::StoreBaseDispWide(int rBase, int displacement,
+ int r_src_lo, int r_src_hi)
+{
+ return StoreBaseIndexedDisp(rBase, INVALID_REG, 0, displacement,
+ r_src_lo, r_src_hi, kLong, INVALID_SREG);
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
new file mode 100644
index 0000000..600bd03
--- /dev/null
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ART_SRC_COMPILER_DEX_QUICK_X86_X86LIR_H_
+#define ART_SRC_COMPILER_DEX_QUICK_X86_X86LIR_H_
+
+#include "dex/compiler_internals.h"
+
+namespace art {
+
+/*
+ * Runtime register conventions. We consider both x86, x86-64 and x32 (32bit mode x86-64), although
+ * we currently only target x86. The ABI has different conventions and we hope to have a single
+ * convention to simplify code generation. Changing something that is callee save and making it
+ * caller save places a burden on up-calls to save/restore the callee save register, however, there
+ * are few registers that are callee save in the ABI. Changing something that is caller save and
+ * making it callee save places a burden on down-calls to save/restore the callee save register.
+ * For these reasons we aim to match native conventions for caller and callee save. The first 4
+ * registers can be used for byte operations, for this reason they are preferred for temporary
+ * scratch registers.
+ *
+ * General Purpose Register:
+ * Native: x86 | x86-64 / x32 | ART
+ * r0/eax: caller save | caller save | caller, Method*, scratch, return value
+ * r1/ecx: caller save | caller save, arg4 | caller, arg1, scratch
+ * r2/edx: caller save | caller save, arg3 | caller, arg2, scratch, high half of long return
+ * r3/ebx: callEE save | callEE save | callER, arg3, scratch
+ * r4/esp: stack pointer
+ * r5/ebp: callee save | callee save | callee, available for dalvik register promotion
+ * r6/esi: callEE save | callER save, arg2 | callee, available for dalvik register promotion
+ * r7/edi: callEE save | callER save, arg1 | callee, available for dalvik register promotion
+ * --- x86-64/x32 registers
+ * Native: x86-64 / x32 | ART
+ * r8: caller save, arg5 | caller, scratch
+ * r9: caller save, arg6 | caller, scratch
+ * r10: caller save | caller, scratch
+ * r11: caller save | caller, scratch
+ * r12: callee save | callee, available for dalvik register promotion
+ * r13: callee save | callee, available for dalvik register promotion
+ * r14: callee save | callee, available for dalvik register promotion
+ * r15: callee save | callee, available for dalvik register promotion
+ *
+ * There is no rSELF, instead on x86 fs: has a base address of Thread::Current, whereas on
+ * x86-64/x32 gs: holds it.
+ *
+ * For floating point we don't support CPUs without SSE2 support (ie newer than PIII):
+ * Native: x86 | x86-64 / x32 | ART
+ * XMM0: caller save |caller save, arg1 | caller, float/double return value (except for native x86 code)
+ * XMM1: caller save |caller save, arg2 | caller, scratch
+ * XMM2: caller save |caller save, arg3 | caller, scratch
+ * XMM3: caller save |caller save, arg4 | caller, scratch
+ * XMM4: caller save |caller save, arg5 | caller, scratch
+ * XMM5: caller save |caller save, arg6 | caller, scratch
+ * XMM6: caller save |caller save, arg7 | caller, scratch
+ * XMM7: caller save |caller save, arg8 | caller, scratch
+ * --- x86-64/x32 registers
+ * XMM8 .. 15: caller save
+ *
+ * X87 is a necessary evil outside of ART code:
+ * ST0: x86 float/double native return value, caller save
+ * ST1 .. ST7: caller save
+ *
+ * Stack frame diagram (stack grows down, higher addresses at top):
+ *
+ * +------------------------+
+ * | IN[ins-1] | {Note: resides in caller's frame}
+ * | . |
+ * | IN[0] |
+ * | caller's Method* |
+ * +========================+ {Note: start of callee's frame}
+ * | return address | {pushed by call}
+ * | spill region | {variable sized}
+ * +------------------------+
+ * | ...filler word... | {Note: used as 2nd word of V[locals-1] if long]
+ * +------------------------+
+ * | V[locals-1] |
+ * | V[locals-2] |
+ * | . |
+ * | . |
+ * | V[1] |
+ * | V[0] |
+ * +------------------------+
+ * | 0 to 3 words padding |
+ * +------------------------+
+ * | OUT[outs-1] |
+ * | OUT[outs-2] |
+ * | . |
+ * | OUT[0] |
+ * | cur_method* | <<== sp w/ 16-byte alignment
+ * +========================+
+ */
+
+// Offset to distingish FP regs.
+#define X86_FP_REG_OFFSET 32
+// Offset to distinguish DP FP regs.
+#define X86_FP_DOUBLE (X86_FP_REG_OFFSET + 16)
+// Offset to distingish the extra regs.
+#define X86_EXTRA_REG_OFFSET (X86_FP_DOUBLE + 16)
+// Reg types.
+#define X86_REGTYPE(x) (x & (X86_FP_REG_OFFSET | X86_FP_DOUBLE))
+#define X86_FPREG(x) ((x & X86_FP_REG_OFFSET) == X86_FP_REG_OFFSET)
+#define X86_EXTRAREG(x) ((x & X86_EXTRA_REG_OFFSET) == X86_EXTRA_REG_OFFSET)
+#define X86_DOUBLEREG(x) ((x & X86_FP_DOUBLE) == X86_FP_DOUBLE)
+#define X86_SINGLEREG(x) (X86_FPREG(x) && !X86_DOUBLEREG(x))
+
+/*
+ * Note: the low register of a floating point pair is sufficient to
+ * create the name of a double, but require both names to be passed to
+ * allow for asserts to verify that the pair is consecutive if significant
+ * rework is done in this area. Also, it is a good reminder in the calling
+ * code that reg locations always describe doubles as a pair of singles.
+ */
+#define X86_S2D(x,y) ((x) | X86_FP_DOUBLE)
+/* Mask to strip off fp flags */
+#define X86_FP_REG_MASK 0xF
+
+// RegisterLocation templates return values (rAX, rAX/rDX or XMM0).
+// location, wide, defined, const, fp, core, ref, high_word, home, low_reg, high_reg, s_reg_low
+#define X86_LOC_C_RETURN {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, rAX, INVALID_REG, INVALID_SREG, INVALID_SREG}
+#define X86_LOC_C_RETURN_WIDE {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, rAX, rDX, INVALID_SREG, INVALID_SREG}
+#define X86_LOC_C_RETURN_FLOAT {kLocPhysReg, 0, 0, 0, 1, 0, 0, 0, 1, fr0, INVALID_REG, INVALID_SREG, INVALID_SREG}
+#define X86_LOC_C_RETURN_DOUBLE {kLocPhysReg, 1, 0, 0, 1, 0, 0, 0, 1, fr0, fr1, INVALID_SREG, INVALID_SREG}
+
+enum X86ResourceEncodingPos {
+ kX86GPReg0 = 0,
+ kX86RegSP = 4,
+ kX86FPReg0 = 16, // xmm0 .. xmm7/xmm15.
+ kX86FPRegEnd = 32,
+ kX86RegEnd = kX86FPRegEnd,
+};
+
+#define ENCODE_X86_REG_LIST(N) (static_cast<uint64_t>(N))
+#define ENCODE_X86_REG_SP (1ULL << kX86RegSP)
+
+enum X86NativeRegisterPool {
+ r0 = 0,
+ rAX = r0,
+ r1 = 1,
+ rCX = r1,
+ r2 = 2,
+ rDX = r2,
+ r3 = 3,
+ rBX = r3,
+ r4sp = 4,
+ rX86_SP = r4sp,
+ r4sib_no_index = r4sp,
+ r5 = 5,
+ rBP = r5,
+ r5sib_no_base = r5,
+ r6 = 6,
+ rSI = r6,
+ r7 = 7,
+ rDI = r7,
+#ifndef TARGET_REX_SUPPORT
+ rRET = 8, // fake return address register for core spill mask.
+#else
+ r8 = 8,
+ r9 = 9,
+ r10 = 10,
+ r11 = 11,
+ r12 = 12,
+ r13 = 13,
+ r14 = 14,
+ r15 = 15,
+ rRET = 16, // fake return address register for core spill mask.
+#endif
+ fr0 = 0 + X86_FP_REG_OFFSET,
+ fr1 = 1 + X86_FP_REG_OFFSET,
+ fr2 = 2 + X86_FP_REG_OFFSET,
+ fr3 = 3 + X86_FP_REG_OFFSET,
+ fr4 = 4 + X86_FP_REG_OFFSET,
+ fr5 = 5 + X86_FP_REG_OFFSET,
+ fr6 = 6 + X86_FP_REG_OFFSET,
+ fr7 = 7 + X86_FP_REG_OFFSET,
+ fr8 = 8 + X86_FP_REG_OFFSET,
+ fr9 = 9 + X86_FP_REG_OFFSET,
+ fr10 = 10 + X86_FP_REG_OFFSET,
+ fr11 = 11 + X86_FP_REG_OFFSET,
+ fr12 = 12 + X86_FP_REG_OFFSET,
+ fr13 = 13 + X86_FP_REG_OFFSET,
+ fr14 = 14 + X86_FP_REG_OFFSET,
+ fr15 = 15 + X86_FP_REG_OFFSET,
+};
+
+#define rX86_ARG0 rAX
+#define rX86_ARG1 rCX
+#define rX86_ARG2 rDX
+#define rX86_ARG3 rBX
+#define rX86_FARG0 rAX
+#define rX86_FARG1 rCX
+#define rX86_FARG2 rDX
+#define rX86_FARG3 rBX
+#define rX86_RET0 rAX
+#define rX86_RET1 rDX
+#define rX86_INVOKE_TGT rAX
+#define rX86_LR INVALID_REG
+#define rX86_SUSPEND INVALID_REG
+#define rX86_SELF INVALID_REG
+#define rX86_COUNT rCX
+#define rX86_PC INVALID_REG
+
+/*
+ * The following enum defines the list of supported X86 instructions by the
+ * assembler. Their corresponding EncodingMap positions will be defined in
+ * Assemble.cc.
+ */
+enum X86OpCode {
+ kX86First = 0,
+ kX8632BitData = kX86First, // data [31..0].
+ kX86Bkpt,
+ kX86Nop,
+ // Define groups of binary operations
+ // MR - Memory Register - opcode [base + disp], reg
+ // - lir operands - 0: base, 1: disp, 2: reg
+ // AR - Array Register - opcode [base + index * scale + disp], reg
+ // - lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: reg
+ // TR - Thread Register - opcode fs:[disp], reg - where fs: is equal to Thread::Current()
+ // - lir operands - 0: disp, 1: reg
+ // RR - Register Register - opcode reg1, reg2
+ // - lir operands - 0: reg1, 1: reg2
+ // RM - Register Memory - opcode reg, [base + disp]
+ // - lir operands - 0: reg, 1: base, 2: disp
+ // RA - Register Array - opcode reg, [base + index * scale + disp]
+ // - lir operands - 0: reg, 1: base, 2: index, 3: scale, 4: disp
+ // RT - Register Thread - opcode reg, fs:[disp] - where fs: is equal to Thread::Current()
+ // - lir operands - 0: reg, 1: disp
+ // RI - Register Immediate - opcode reg, #immediate
+ // - lir operands - 0: reg, 1: immediate
+ // MI - Memory Immediate - opcode [base + disp], #immediate
+ // - lir operands - 0: base, 1: disp, 2: immediate
+ // AI - Array Immediate - opcode [base + index * scale + disp], #immediate
+ // - lir operands - 0: base, 1: index, 2: scale, 3: disp 4: immediate
+ // TI - Thread Register - opcode fs:[disp], imm - where fs: is equal to Thread::Current()
+ // - lir operands - 0: disp, 1: imm
+#define BinaryOpCode(opcode) \
+ opcode ## 8MR, opcode ## 8AR, opcode ## 8TR, \
+ opcode ## 8RR, opcode ## 8RM, opcode ## 8RA, opcode ## 8RT, \
+ opcode ## 8RI, opcode ## 8MI, opcode ## 8AI, opcode ## 8TI, \
+ opcode ## 16MR, opcode ## 16AR, opcode ## 16TR, \
+ opcode ## 16RR, opcode ## 16RM, opcode ## 16RA, opcode ## 16RT, \
+ opcode ## 16RI, opcode ## 16MI, opcode ## 16AI, opcode ## 16TI, \
+ opcode ## 16RI8, opcode ## 16MI8, opcode ## 16AI8, opcode ## 16TI8, \
+ opcode ## 32MR, opcode ## 32AR, opcode ## 32TR, \
+ opcode ## 32RR, opcode ## 32RM, opcode ## 32RA, opcode ## 32RT, \
+ opcode ## 32RI, opcode ## 32MI, opcode ## 32AI, opcode ## 32TI, \
+ opcode ## 32RI8, opcode ## 32MI8, opcode ## 32AI8, opcode ## 32TI8
+ BinaryOpCode(kX86Add),
+ BinaryOpCode(kX86Or),
+ BinaryOpCode(kX86Adc),
+ BinaryOpCode(kX86Sbb),
+ BinaryOpCode(kX86And),
+ BinaryOpCode(kX86Sub),
+ BinaryOpCode(kX86Xor),
+ BinaryOpCode(kX86Cmp),
+#undef BinaryOpCode
+ kX86Imul16RRI, kX86Imul16RMI, kX86Imul16RAI,
+ kX86Imul32RRI, kX86Imul32RMI, kX86Imul32RAI,
+ kX86Imul32RRI8, kX86Imul32RMI8, kX86Imul32RAI8,
+ kX86Mov8MR, kX86Mov8AR, kX86Mov8TR,
+ kX86Mov8RR, kX86Mov8RM, kX86Mov8RA, kX86Mov8RT,
+ kX86Mov8RI, kX86Mov8MI, kX86Mov8AI, kX86Mov8TI,
+ kX86Mov16MR, kX86Mov16AR, kX86Mov16TR,
+ kX86Mov16RR, kX86Mov16RM, kX86Mov16RA, kX86Mov16RT,
+ kX86Mov16RI, kX86Mov16MI, kX86Mov16AI, kX86Mov16TI,
+ kX86Mov32MR, kX86Mov32AR, kX86Mov32TR,
+ kX86Mov32RR, kX86Mov32RM, kX86Mov32RA, kX86Mov32RT,
+ kX86Mov32RI, kX86Mov32MI, kX86Mov32AI, kX86Mov32TI,
+ kX86Lea32RA,
+ // RC - Register CL - opcode reg, CL
+ // - lir operands - 0: reg, 1: CL
+ // MC - Memory CL - opcode [base + disp], CL
+ // - lir operands - 0: base, 1: disp, 2: CL
+ // AC - Array CL - opcode [base + index * scale + disp], CL
+ // - lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: CL
+#define BinaryShiftOpCode(opcode) \
+ opcode ## 8RI, opcode ## 8MI, opcode ## 8AI, \
+ opcode ## 8RC, opcode ## 8MC, opcode ## 8AC, \
+ opcode ## 16RI, opcode ## 16MI, opcode ## 16AI, \
+ opcode ## 16RC, opcode ## 16MC, opcode ## 16AC, \
+ opcode ## 32RI, opcode ## 32MI, opcode ## 32AI, \
+ opcode ## 32RC, opcode ## 32MC, opcode ## 32AC
+ BinaryShiftOpCode(kX86Rol),
+ BinaryShiftOpCode(kX86Ror),
+ BinaryShiftOpCode(kX86Rcl),
+ BinaryShiftOpCode(kX86Rcr),
+ BinaryShiftOpCode(kX86Sal),
+ BinaryShiftOpCode(kX86Shr),
+ BinaryShiftOpCode(kX86Sar),
+#undef BinaryShiftOpcode
+ kX86Cmc,
+#define UnaryOpcode(opcode, reg, mem, array) \
+ opcode ## 8 ## reg, opcode ## 8 ## mem, opcode ## 8 ## array, \
+ opcode ## 16 ## reg, opcode ## 16 ## mem, opcode ## 16 ## array, \
+ opcode ## 32 ## reg, opcode ## 32 ## mem, opcode ## 32 ## array
+ UnaryOpcode(kX86Test, RI, MI, AI),
+ kX86Test32RR,
+ UnaryOpcode(kX86Not, R, M, A),
+ UnaryOpcode(kX86Neg, R, M, A),
+ UnaryOpcode(kX86Mul, DaR, DaM, DaA),
+ UnaryOpcode(kX86Imul, DaR, DaM, DaA),
+ UnaryOpcode(kX86Divmod, DaR, DaM, DaA),
+ UnaryOpcode(kX86Idivmod, DaR, DaM, DaA),
+#undef UnaryOpcode
+#define Binary0fOpCode(opcode) \
+ opcode ## RR, opcode ## RM, opcode ## RA
+ Binary0fOpCode(kX86Movsd),
+ kX86MovsdMR,
+ kX86MovsdAR,
+ Binary0fOpCode(kX86Movss),
+ kX86MovssMR,
+ kX86MovssAR,
+ Binary0fOpCode(kX86Cvtsi2sd), // int to double
+ Binary0fOpCode(kX86Cvtsi2ss), // int to float
+ Binary0fOpCode(kX86Cvttsd2si),// truncating double to int
+ Binary0fOpCode(kX86Cvttss2si),// truncating float to int
+ Binary0fOpCode(kX86Cvtsd2si), // rounding double to int
+ Binary0fOpCode(kX86Cvtss2si), // rounding float to int
+ Binary0fOpCode(kX86Ucomisd), // unordered double compare
+ Binary0fOpCode(kX86Ucomiss), // unordered float compare
+ Binary0fOpCode(kX86Comisd), // double compare
+ Binary0fOpCode(kX86Comiss), // float compare
+ Binary0fOpCode(kX86Orps), // or of floating point registers
+ Binary0fOpCode(kX86Xorps), // xor of floating point registers
+ Binary0fOpCode(kX86Addsd), // double add
+ Binary0fOpCode(kX86Addss), // float add
+ Binary0fOpCode(kX86Mulsd), // double multiply
+ Binary0fOpCode(kX86Mulss), // float multiply
+ Binary0fOpCode(kX86Cvtsd2ss), // double to float
+ Binary0fOpCode(kX86Cvtss2sd), // float to double
+ Binary0fOpCode(kX86Subsd), // double subtract
+ Binary0fOpCode(kX86Subss), // float subtract
+ Binary0fOpCode(kX86Divsd), // double divide
+ Binary0fOpCode(kX86Divss), // float divide
+ kX86PsrlqRI, // right shift of floating point registers
+ kX86PsllqRI, // left shift of floating point registers
+ Binary0fOpCode(kX86Movdxr), // move into xmm from gpr
+ kX86MovdrxRR, kX86MovdrxMR, kX86MovdrxAR,// move into reg from xmm
+ kX86Set8R, kX86Set8M, kX86Set8A,// set byte depending on condition operand
+ kX86Mfence, // memory barrier
+ Binary0fOpCode(kX86Imul16), // 16bit multiply
+ Binary0fOpCode(kX86Imul32), // 32bit multiply
+ kX86CmpxchgRR, kX86CmpxchgMR, kX86CmpxchgAR,// compare and exchange
+ kX86LockCmpxchgRR, kX86LockCmpxchgMR, kX86LockCmpxchgAR,// locked compare and exchange
+ Binary0fOpCode(kX86Movzx8), // zero-extend 8-bit value
+ Binary0fOpCode(kX86Movzx16), // zero-extend 16-bit value
+ Binary0fOpCode(kX86Movsx8), // sign-extend 8-bit value
+ Binary0fOpCode(kX86Movsx16), // sign-extend 16-bit value
+#undef Binary0fOpCode
+ kX86Jcc8, kX86Jcc32, // jCC rel8/32; lir operands - 0: rel, 1: CC, target assigned
+ kX86Jmp8, kX86Jmp32, // jmp rel8/32; lir operands - 0: rel, target assigned
+ kX86JmpR, // jmp reg; lir operands - 0: reg
+ kX86CallR, // call reg; lir operands - 0: reg
+ kX86CallM, // call [base + disp]; lir operands - 0: base, 1: disp
+ kX86CallA, // call [base + index * scale + disp]
+ // lir operands - 0: base, 1: index, 2: scale, 3: disp
+ kX86CallT, // call fs:[disp]; fs: is equal to Thread::Current(); lir operands - 0: disp
+ kX86Ret, // ret; no lir operands
+ kX86StartOfMethod, // call 0; pop reg; sub reg, # - generate start of method into reg
+ // lir operands - 0: reg
+ kX86PcRelLoadRA, // mov reg, [base + index * scale + PC relative displacement]
+ // lir operands - 0: reg, 1: base, 2: index, 3: scale, 4: table
+ kX86PcRelAdr, // mov reg, PC relative displacement; lir operands - 0: reg, 1: table
+ kX86Last
+};
+
+/* Instruction assembly field_loc kind */
+enum X86EncodingKind {
+ kData, // Special case for raw data.
+ kNop, // Special case for variable length nop.
+ kNullary, // Opcode that takes no arguments.
+ kReg, kMem, kArray, // R, M and A instruction kinds.
+ kMemReg, kArrayReg, kThreadReg, // MR, AR and TR instruction kinds.
+ kRegReg, kRegMem, kRegArray, kRegThread, // RR, RM, RA and RT instruction kinds.
+ kRegRegStore, // RR following the store modrm reg-reg encoding rather than the load.
+ kRegImm, kMemImm, kArrayImm, kThreadImm, // RI, MI, AI and TI instruction kinds.
+ kRegRegImm, kRegMemImm, kRegArrayImm, // RRI, RMI and RAI instruction kinds.
+ kMovRegImm, // Shorter form move RI.
+ kShiftRegImm, kShiftMemImm, kShiftArrayImm, // Shift opcode with immediate.
+ kShiftRegCl, kShiftMemCl, kShiftArrayCl, // Shift opcode with register CL.
+ kRegRegReg, kRegRegMem, kRegRegArray, // RRR, RRM, RRA instruction kinds.
+ kRegCond, kMemCond, kArrayCond, // R, M, A instruction kinds following by a condition.
+ kJmp, kJcc, kCall, // Branch instruction kinds.
+ kPcRel, // Operation with displacement that is PC relative
+ kMacro, // An instruction composing multiple others
+ kUnimplemented // Encoding used when an instruction isn't yet implemented.
+};
+
+/* Struct used to define the EncodingMap positions for each X86 opcode */
+struct X86EncodingMap {
+ X86OpCode opcode; // e.g. kOpAddRI
+ X86EncodingKind kind; // Used to discriminate in the union below
+ uint64_t flags;
+ struct {
+ uint8_t prefix1; // non-zero => a prefix byte
+ uint8_t prefix2; // non-zero => a second prefix byte
+ uint8_t opcode; // 1 byte opcode
+ uint8_t extra_opcode1; // possible extra opcode byte
+ uint8_t extra_opcode2; // possible second extra opcode byte
+ // 3bit opcode that gets encoded in the register bits of the modrm byte, use determined by the
+ // encoding kind
+ uint8_t modrm_opcode;
+ uint8_t ax_opcode; // non-zero => shorter encoding for AX as a destination
+ uint8_t immediate_bytes; // number of bytes of immediate
+ } skeleton;
+ const char *name;
+ const char* fmt;
+};
+
+
+// FIXME: mem barrier type - what do we do for x86?
+#define kSY 0
+#define kST 0
+
+// Offsets of high and low halves of a 64bit value.
+#define LOWORD_OFFSET 0
+#define HIWORD_OFFSET 4
+
+// Segment override instruction prefix used for quick TLS access to Thread::Current().
+#define THREAD_PREFIX 0x64
+
+#define IS_SIMM8(v) ((-128 <= (v)) && ((v) <= 127))
+#define IS_SIMM16(v) ((-32768 <= (v)) && ((v) <= 32767))
+
+extern X86EncodingMap EncodingMap[kX86Last];
+extern X86ConditionCode X86ConditionEncoding(ConditionCode cond);
+
+} // namespace art
+
+#endif // ART_SRC_COMPILER_DEX_QUICK_X86_X86LIR_H_
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
new file mode 100644
index 0000000..4182072
--- /dev/null
+++ b/compiler/dex/ssa_transformation.cc
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2011 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 "compiler_internals.h"
+#include "dataflow_iterator-inl.h"
+
+#define NOTVISITED (-1)
+
+namespace art {
+
+void MIRGraph::ClearAllVisitedFlags() {
+ AllNodesIterator iter(this, false /* not iterative */);
+ for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
+ bb->visited = false;
+ }
+}
+
+BasicBlock* MIRGraph::NeedsVisit(BasicBlock* bb) {
+ if (bb != NULL) {
+ if (bb->visited || bb->hidden) {
+ bb = NULL;
+ }
+ }
+ return bb;
+}
+
+BasicBlock* MIRGraph::NextUnvisitedSuccessor(BasicBlock* bb)
+{
+ BasicBlock* res = NeedsVisit(bb->fall_through);
+ if (res == NULL) {
+ res = NeedsVisit(bb->taken);
+ if (res == NULL) {
+ if (bb->successor_block_list.block_list_type != kNotUsed) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_block_list.blocks);
+ while (true) {
+ SuccessorBlockInfo *sbi = iterator.Next();
+ if (sbi == NULL) break;
+ res = NeedsVisit(sbi->block);
+ if (res != NULL) break;
+ }
+ }
+ }
+ }
+ return res;
+}
+
+void MIRGraph::MarkPreOrder(BasicBlock* block)
+{
+ block->visited = true;
+ /* Enqueue the pre_order block id */
+ dfs_order_->Insert(block->id);
+}
+
+void MIRGraph::RecordDFSOrders(BasicBlock* block)
+{
+ std::vector<BasicBlock*> succ;
+ MarkPreOrder(block);
+ succ.push_back(block);
+ while (!succ.empty()) {
+ BasicBlock* curr = succ.back();
+ BasicBlock* next_successor = NextUnvisitedSuccessor(curr);
+ if (next_successor != NULL) {
+ MarkPreOrder(next_successor);
+ succ.push_back(next_successor);
+ continue;
+ }
+ curr->dfs_id = dfs_post_order_->Size();
+ dfs_post_order_->Insert(curr->id);
+ succ.pop_back();
+ }
+}
+
+/* Sort the blocks by the Depth-First-Search */
+void MIRGraph::ComputeDFSOrders()
+{
+ /* Initialize or reset the DFS pre_order list */
+ if (dfs_order_ == NULL) {
+ dfs_order_ = new (arena_) GrowableArray<int>(arena_, GetNumBlocks(), kGrowableArrayDfsOrder);
+ } else {
+ /* Just reset the used length on the counter */
+ dfs_order_->Reset();
+ }
+
+ /* Initialize or reset the DFS post_order list */
+ if (dfs_post_order_ == NULL) {
+ dfs_post_order_ = new (arena_) GrowableArray<int>(arena_, GetNumBlocks(), kGrowableArrayDfsPostOrder);
+ } else {
+ /* Just reset the used length on the counter */
+ dfs_post_order_->Reset();
+ }
+
+ // Reset visited flags from all nodes
+ ClearAllVisitedFlags();
+
+ // Record dfs orders
+ RecordDFSOrders(GetEntryBlock());
+
+ num_reachable_blocks_ = dfs_order_->Size();
+}
+
+/*
+ * Mark block bit on the per-Dalvik register vector to denote that Dalvik
+ * register idx is defined in BasicBlock bb.
+ */
+bool MIRGraph::FillDefBlockMatrix(BasicBlock* bb)
+{
+ if (bb->data_flow_info == NULL) return false;
+
+ ArenaBitVector::Iterator iterator(bb->data_flow_info->def_v);
+ while (true) {
+ int idx = iterator.Next();
+ if (idx == -1) break;
+ /* Block bb defines register idx */
+ def_block_matrix_[idx]->SetBit(bb->id);
+ }
+ return true;
+}
+
+void MIRGraph::ComputeDefBlockMatrix()
+{
+ int num_registers = cu_->num_dalvik_registers;
+ /* Allocate num_dalvik_registers bit vector pointers */
+ def_block_matrix_ = static_cast<ArenaBitVector**>
+ (arena_->NewMem(sizeof(ArenaBitVector *) * num_registers, true,
+ ArenaAllocator::kAllocDFInfo));
+ int i;
+
+ /* Initialize num_register vectors with num_blocks bits each */
+ for (i = 0; i < num_registers; i++) {
+ def_block_matrix_[i] =
+ new (arena_) ArenaBitVector(arena_, GetNumBlocks(), false, kBitMapBMatrix);
+ }
+ AllNodesIterator iter(this, false /* not iterative */);
+ for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
+ FindLocalLiveIn(bb);
+ }
+ AllNodesIterator iter2(this, false /* not iterative */);
+ for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
+ FillDefBlockMatrix(bb);
+ }
+
+ /*
+ * Also set the incoming parameters as defs in the entry block.
+ * Only need to handle the parameters for the outer method.
+ */
+ int num_regs = cu_->num_dalvik_registers;
+ int in_reg = num_regs - cu_->num_ins;
+ for (; in_reg < num_regs; in_reg++) {
+ def_block_matrix_[in_reg]->SetBit(GetEntryBlock()->id);
+ }
+}
+
+void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) {
+ if (dom_post_order_traversal_ == NULL) {
+ // First time - create the array.
+ dom_post_order_traversal_ =
+ new (arena_) GrowableArray<int>(arena_, num_reachable_blocks_,
+ kGrowableArrayDomPostOrderTraversal);
+ } else {
+ dom_post_order_traversal_->Reset();
+ }
+ ClearAllVisitedFlags();
+ std::vector<std::pair<BasicBlock*, ArenaBitVector::Iterator*> > work_stack;
+ bb->visited = true;
+ work_stack.push_back(std::make_pair(bb, new (arena_) ArenaBitVector::Iterator(bb->i_dominated)));
+ while (!work_stack.empty()) {
+ std::pair<BasicBlock*, ArenaBitVector::Iterator*> curr = work_stack.back();
+ BasicBlock* curr_bb = curr.first;
+ ArenaBitVector::Iterator* curr_idom_iter = curr.second;
+ int bb_idx = curr_idom_iter->Next();
+ while ((bb_idx != -1) && (NeedsVisit(GetBasicBlock(bb_idx)) == NULL)) {
+ bb_idx = curr_idom_iter->Next();
+ }
+ if (bb_idx != -1) {
+ BasicBlock* new_bb = GetBasicBlock(bb_idx);
+ new_bb->visited = true;
+ work_stack.push_back(
+ std::make_pair(new_bb, new (arena_) ArenaBitVector::Iterator(new_bb->i_dominated)));
+ } else {
+ // no successor/next
+ dom_post_order_traversal_->Insert(curr_bb->id);
+ work_stack.pop_back();
+
+ /* hacky loop detection */
+ if (curr_bb->taken && curr_bb->dominators->IsBitSet(curr_bb->taken->id)) {
+ attributes_ |= METHOD_HAS_LOOP;
+ }
+ }
+ }
+}
+
+void MIRGraph::CheckForDominanceFrontier(BasicBlock* dom_bb,
+ const BasicBlock* succ_bb)
+{
+ /*
+ * TODO - evaluate whether phi will ever need to be inserted into exit
+ * blocks.
+ */
+ if (succ_bb->i_dom != dom_bb &&
+ succ_bb->block_type == kDalvikByteCode &&
+ succ_bb->hidden == false) {
+ dom_bb->dom_frontier->SetBit(succ_bb->id);
+ }
+}
+
+/* Worker function to compute the dominance frontier */
+bool MIRGraph::ComputeDominanceFrontier(BasicBlock* bb)
+{
+ /* Calculate DF_local */
+ if (bb->taken) {
+ CheckForDominanceFrontier(bb, bb->taken);
+ }
+ if (bb->fall_through) {
+ CheckForDominanceFrontier(bb, bb->fall_through);
+ }
+ if (bb->successor_block_list.block_list_type != kNotUsed) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_block_list.blocks);
+ while (true) {
+ SuccessorBlockInfo *successor_block_info = iterator.Next();
+ if (successor_block_info == NULL) break;
+ BasicBlock* succ_bb = successor_block_info->block;
+ CheckForDominanceFrontier(bb, succ_bb);
+ }
+ }
+
+ /* Calculate DF_up */
+ ArenaBitVector::Iterator bv_iterator(bb->i_dominated);
+ while (true) {
+ //TUNING: hot call to BitVectorIteratorNext
+ int dominated_idx = bv_iterator.Next();
+ if (dominated_idx == -1) break;
+ BasicBlock* dominated_bb = GetBasicBlock(dominated_idx);
+ ArenaBitVector::Iterator df_iterator(dominated_bb->dom_frontier);
+ while (true) {
+ //TUNING: hot call to BitVectorIteratorNext
+ int df_up_idx = df_iterator.Next();
+ if (df_up_idx == -1) break;
+ BasicBlock* df_up_block = GetBasicBlock(df_up_idx);
+ CheckForDominanceFrontier(bb, df_up_block);
+ }
+ }
+
+ return true;
+}
+
+/* Worker function for initializing domination-related data structures */
+void MIRGraph::InitializeDominationInfo(BasicBlock* bb)
+{
+ int num_total_blocks = GetBasicBlockListCount();
+
+ if (bb->dominators == NULL ) {
+ bb->dominators = new (arena_) ArenaBitVector(arena_, num_total_blocks,
+ false /* expandable */, kBitMapDominators);
+ bb->i_dominated = new (arena_) ArenaBitVector(arena_, num_total_blocks,
+ false /* expandable */, kBitMapIDominated);
+ bb->dom_frontier = new (arena_) ArenaBitVector(arena_, num_total_blocks,
+ false /* expandable */, kBitMapDomFrontier);
+ } else {
+ bb->dominators->ClearAllBits();
+ bb->i_dominated->ClearAllBits();
+ bb->dom_frontier->ClearAllBits();
+ }
+ /* Set all bits in the dominator vector */
+ bb->dominators->SetInitialBits(num_total_blocks);
+
+ return;
+}
+
+/*
+ * Walk through the ordered i_dom list until we reach common parent.
+ * Given the ordering of i_dom_list, this common parent represents the
+ * last element of the intersection of block1 and block2 dominators.
+ */
+int MIRGraph::FindCommonParent(int block1, int block2)
+{
+ while (block1 != block2) {
+ while (block1 < block2) {
+ block1 = i_dom_list_[block1];
+ DCHECK_NE(block1, NOTVISITED);
+ }
+ while (block2 < block1) {
+ block2 = i_dom_list_[block2];
+ DCHECK_NE(block2, NOTVISITED);
+ }
+ }
+ return block1;
+}
+
+/* Worker function to compute each block's immediate dominator */
+bool MIRGraph::ComputeblockIDom(BasicBlock* bb)
+{
+ /* Special-case entry block */
+ if (bb == GetEntryBlock()) {
+ return false;
+ }
+
+ /* Iterate through the predecessors */
+ GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors);
+
+ /* Find the first processed predecessor */
+ int idom = -1;
+ while (true) {
+ BasicBlock* pred_bb = iter.Next();
+ CHECK(pred_bb != NULL);
+ if (i_dom_list_[pred_bb->dfs_id] != NOTVISITED) {
+ idom = pred_bb->dfs_id;
+ break;
+ }
+ }
+
+ /* Scan the rest of the predecessors */
+ while (true) {
+ BasicBlock* pred_bb = iter.Next();
+ if (!pred_bb) break;
+ if (i_dom_list_[pred_bb->dfs_id] == NOTVISITED) {
+ continue;
+ } else {
+ idom = FindCommonParent(pred_bb->dfs_id, idom);
+ }
+ }
+
+ DCHECK_NE(idom, NOTVISITED);
+
+ /* Did something change? */
+ if (i_dom_list_[bb->dfs_id] != idom) {
+ i_dom_list_[bb->dfs_id] = idom;
+ return true;
+ }
+ return false;
+}
+
+/* Worker function to compute each block's domintors */
+bool MIRGraph::ComputeBlockDominators(BasicBlock* bb)
+{
+ if (bb == GetEntryBlock()) {
+ bb->dominators->ClearAllBits();
+ } else {
+ bb->dominators->Copy(bb->i_dom->dominators);
+ }
+ bb->dominators->SetBit(bb->id);
+ return false;
+}
+
+bool MIRGraph::SetDominators(BasicBlock* bb)
+{
+ if (bb != GetEntryBlock()) {
+ int idom_dfs_idx = i_dom_list_[bb->dfs_id];
+ DCHECK_NE(idom_dfs_idx, NOTVISITED);
+ int i_dom_idx = dfs_post_order_->Get(idom_dfs_idx);
+ BasicBlock* i_dom = GetBasicBlock(i_dom_idx);
+ bb->i_dom = i_dom;
+ /* Add bb to the i_dominated set of the immediate dominator block */
+ i_dom->i_dominated->SetBit(bb->id);
+ }
+ return false;
+}
+
+/* Compute dominators, immediate dominator, and dominance fronter */
+void MIRGraph::ComputeDominators()
+{
+ int num_reachable_blocks = num_reachable_blocks_;
+ int num_total_blocks = GetBasicBlockListCount();
+
+ /* Initialize domination-related data structures */
+ ReachableNodesIterator iter(this, false /* not iterative */);
+ for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
+ InitializeDominationInfo(bb);
+ }
+
+ /* Initalize & Clear i_dom_list */
+ if (i_dom_list_ == NULL) {
+ i_dom_list_ = static_cast<int*>(arena_->NewMem(sizeof(int) * num_reachable_blocks, false,
+ ArenaAllocator::kAllocDFInfo));
+ }
+ for (int i = 0; i < num_reachable_blocks; i++) {
+ i_dom_list_[i] = NOTVISITED;
+ }
+
+ /* For post-order, last block is entry block. Set its i_dom to istelf */
+ DCHECK_EQ(GetEntryBlock()->dfs_id, num_reachable_blocks-1);
+ i_dom_list_[GetEntryBlock()->dfs_id] = GetEntryBlock()->dfs_id;
+
+ /* Compute the immediate dominators */
+ ReversePostOrderDfsIterator iter2(this, true /* iterative */);
+ bool change = false;
+ for (BasicBlock* bb = iter2.Next(false); bb != NULL; bb = iter2.Next(change)) {
+ change = ComputeblockIDom(bb);
+ }
+
+ /* Set the dominator for the root node */
+ GetEntryBlock()->dominators->ClearAllBits();
+ GetEntryBlock()->dominators->SetBit(GetEntryBlock()->id);
+
+ if (temp_block_v_ == NULL) {
+ temp_block_v_ = new (arena_) ArenaBitVector(arena_, num_total_blocks,
+ false /* expandable */, kBitMapTmpBlockV);
+ } else {
+ temp_block_v_->ClearAllBits();
+ }
+ GetEntryBlock()->i_dom = NULL;
+
+ ReachableNodesIterator iter3(this, false /* not iterative */);
+ for (BasicBlock* bb = iter3.Next(); bb != NULL; bb = iter3.Next()) {
+ SetDominators(bb);
+ }
+
+ ReversePostOrderDfsIterator iter4(this, false /* not iterative */);
+ for (BasicBlock* bb = iter4.Next(); bb != NULL; bb = iter4.Next()) {
+ ComputeBlockDominators(bb);
+ }
+
+ // Compute the dominance frontier for each block.
+ ComputeDomPostOrderTraversal(GetEntryBlock());
+ PostOrderDOMIterator iter5(this, false /* not iterative */);
+ for (BasicBlock* bb = iter5.Next(); bb != NULL; bb = iter5.Next()) {
+ ComputeDominanceFrontier(bb);
+ }
+}
+
+/*
+ * Perform dest U= src1 ^ ~src2
+ * This is probably not general enough to be placed in BitVector.[ch].
+ */
+void MIRGraph::ComputeSuccLineIn(ArenaBitVector* dest, const ArenaBitVector* src1,
+ const ArenaBitVector* src2)
+{
+ if (dest->GetStorageSize() != src1->GetStorageSize() ||
+ dest->GetStorageSize() != src2->GetStorageSize() ||
+ dest->IsExpandable() != src1->IsExpandable() ||
+ dest->IsExpandable() != src2->IsExpandable()) {
+ LOG(FATAL) << "Incompatible set properties";
+ }
+
+ unsigned int idx;
+ for (idx = 0; idx < dest->GetStorageSize(); idx++) {
+ dest->GetRawStorage()[idx] |= src1->GetRawStorageWord(idx) & ~(src2->GetRawStorageWord(idx));
+ }
+}
+
+/*
+ * Iterate through all successor blocks and propagate up the live-in sets.
+ * The calculated result is used for phi-node pruning - where we only need to
+ * insert a phi node if the variable is live-in to the block.
+ */
+bool MIRGraph::ComputeBlockLiveIns(BasicBlock* bb)
+{
+ ArenaBitVector* temp_dalvik_register_v = temp_dalvik_register_v_;
+
+ if (bb->data_flow_info == NULL) return false;
+ temp_dalvik_register_v->Copy(bb->data_flow_info->live_in_v);
+ if (bb->taken && bb->taken->data_flow_info)
+ ComputeSuccLineIn(temp_dalvik_register_v, bb->taken->data_flow_info->live_in_v,
+ bb->data_flow_info->def_v);
+ if (bb->fall_through && bb->fall_through->data_flow_info)
+ ComputeSuccLineIn(temp_dalvik_register_v, bb->fall_through->data_flow_info->live_in_v,
+ bb->data_flow_info->def_v);
+ if (bb->successor_block_list.block_list_type != kNotUsed) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_block_list.blocks);
+ while (true) {
+ SuccessorBlockInfo *successor_block_info = iterator.Next();
+ if (successor_block_info == NULL) break;
+ BasicBlock* succ_bb = successor_block_info->block;
+ if (succ_bb->data_flow_info) {
+ ComputeSuccLineIn(temp_dalvik_register_v, succ_bb->data_flow_info->live_in_v,
+ bb->data_flow_info->def_v);
+ }
+ }
+ }
+ if (!temp_dalvik_register_v->Equal(bb->data_flow_info->live_in_v)) {
+ bb->data_flow_info->live_in_v->Copy(temp_dalvik_register_v);
+ return true;
+ }
+ return false;
+}
+
+/* Insert phi nodes to for each variable to the dominance frontiers */
+void MIRGraph::InsertPhiNodes()
+{
+ int dalvik_reg;
+ ArenaBitVector* phi_blocks =
+ new (arena_) ArenaBitVector(arena_, GetNumBlocks(), false, kBitMapPhi);
+ ArenaBitVector* tmp_blocks =
+ new (arena_) ArenaBitVector(arena_, GetNumBlocks(), false, kBitMapTmpBlocks);
+ ArenaBitVector* input_blocks =
+ new (arena_) ArenaBitVector(arena_, GetNumBlocks(), false, kBitMapInputBlocks);
+
+ temp_dalvik_register_v_ =
+ new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapRegisterV);
+
+ PostOrderDfsIterator iter(this, true /* iterative */);
+ bool change = false;
+ for (BasicBlock* bb = iter.Next(false); bb != NULL; bb = iter.Next(change)) {
+ change = ComputeBlockLiveIns(bb);
+ }
+
+ /* Iterate through each Dalvik register */
+ for (dalvik_reg = cu_->num_dalvik_registers - 1; dalvik_reg >= 0; dalvik_reg--) {
+ bool change;
+
+ input_blocks->Copy(def_block_matrix_[dalvik_reg]);
+ phi_blocks->ClearAllBits();
+
+ /* Calculate the phi blocks for each Dalvik register */
+ do {
+ change = false;
+ tmp_blocks->ClearAllBits();
+ ArenaBitVector::Iterator iterator(input_blocks);
+
+ while (true) {
+ int idx = iterator.Next();
+ if (idx == -1) break;
+ BasicBlock* def_bb = GetBasicBlock(idx);
+
+ /* Merge the dominance frontier to tmp_blocks */
+ //TUNING: hot call to Union().
+ if (def_bb->dom_frontier != NULL) {
+ tmp_blocks->Union(def_bb->dom_frontier);
+ }
+ }
+ if (!phi_blocks->Equal(tmp_blocks)) {
+ change = true;
+ phi_blocks->Copy(tmp_blocks);
+
+ /*
+ * Iterate through the original blocks plus the new ones in
+ * the dominance frontier.
+ */
+ input_blocks->Copy(phi_blocks);
+ input_blocks->Union(def_block_matrix_[dalvik_reg]);
+ }
+ } while (change);
+
+ /*
+ * Insert a phi node for dalvik_reg in the phi_blocks if the Dalvik
+ * register is in the live-in set.
+ */
+ ArenaBitVector::Iterator iterator(phi_blocks);
+ while (true) {
+ int idx = iterator.Next();
+ if (idx == -1) break;
+ BasicBlock* phi_bb = GetBasicBlock(idx);
+ /* Variable will be clobbered before being used - no need for phi */
+ if (!phi_bb->data_flow_info->live_in_v->IsBitSet(dalvik_reg)) continue;
+ MIR *phi =
+ static_cast<MIR*>(arena_->NewMem(sizeof(MIR), true, ArenaAllocator::kAllocDFInfo));
+ phi->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpPhi);
+ phi->dalvikInsn.vA = dalvik_reg;
+ phi->offset = phi_bb->start_offset;
+ phi->m_unit_index = 0; // Arbitrarily assign all Phi nodes to outermost method.
+ PrependMIR(phi_bb, phi);
+ }
+ }
+}
+
+/*
+ * Worker function to insert phi-operands with latest SSA names from
+ * predecessor blocks
+ */
+bool MIRGraph::InsertPhiNodeOperands(BasicBlock* bb)
+{
+ MIR *mir;
+ std::vector<int> uses;
+ std::vector<int> incoming_arc;
+
+ /* Phi nodes are at the beginning of each block */
+ for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ if (mir->dalvikInsn.opcode != static_cast<Instruction::Code>(kMirOpPhi))
+ return true;
+ int ssa_reg = mir->ssa_rep->defs[0];
+ DCHECK_GE(ssa_reg, 0); // Shouldn't see compiler temps here
+ int v_reg = SRegToVReg(ssa_reg);
+
+ uses.clear();
+ incoming_arc.clear();
+
+ /* Iterate through the predecessors */
+ GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors);
+ while (true) {
+ BasicBlock* pred_bb = iter.Next();
+ if (!pred_bb) break;
+ int ssa_reg = pred_bb->data_flow_info->vreg_to_ssa_map[v_reg];
+ uses.push_back(ssa_reg);
+ incoming_arc.push_back(pred_bb->id);
+ }
+
+ /* Count the number of SSA registers for a Dalvik register */
+ int num_uses = uses.size();
+ mir->ssa_rep->num_uses = num_uses;
+ mir->ssa_rep->uses =
+ static_cast<int*>(arena_->NewMem(sizeof(int) * num_uses, false,
+ ArenaAllocator::kAllocDFInfo));
+ mir->ssa_rep->fp_use =
+ static_cast<bool*>(arena_->NewMem(sizeof(bool) * num_uses, true,
+ ArenaAllocator::kAllocDFInfo));
+ int* incoming =
+ static_cast<int*>(arena_->NewMem(sizeof(int) * num_uses, false,
+ ArenaAllocator::kAllocDFInfo));
+ // TODO: Ugly, rework (but don't burden each MIR/LIR for Phi-only needs)
+ mir->dalvikInsn.vB = reinterpret_cast<uintptr_t>(incoming);
+
+ /* Set the uses array for the phi node */
+ int *use_ptr = mir->ssa_rep->uses;
+ for (int i = 0; i < num_uses; i++) {
+ *use_ptr++ = uses[i];
+ *incoming++ = incoming_arc[i];
+ }
+ }
+
+ return true;
+}
+
+void MIRGraph::DoDFSPreOrderSSARename(BasicBlock* block)
+{
+
+ if (block->visited || block->hidden) return;
+ block->visited = true;
+
+ /* Process this block */
+ DoSSAConversion(block);
+ int map_size = sizeof(int) * cu_->num_dalvik_registers;
+
+ /* Save SSA map snapshot */
+ int* saved_ssa_map =
+ static_cast<int*>(arena_->NewMem(map_size, false, ArenaAllocator::kAllocDalvikToSSAMap));
+ memcpy(saved_ssa_map, vreg_to_ssa_map_, map_size);
+
+ if (block->fall_through) {
+ DoDFSPreOrderSSARename(block->fall_through);
+ /* Restore SSA map snapshot */
+ memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
+ }
+ if (block->taken) {
+ DoDFSPreOrderSSARename(block->taken);
+ /* Restore SSA map snapshot */
+ memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
+ }
+ if (block->successor_block_list.block_list_type != kNotUsed) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(block->successor_block_list.blocks);
+ while (true) {
+ SuccessorBlockInfo *successor_block_info = iterator.Next();
+ if (successor_block_info == NULL) break;
+ BasicBlock* succ_bb = successor_block_info->block;
+ DoDFSPreOrderSSARename(succ_bb);
+ /* Restore SSA map snapshot */
+ memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
+ }
+ }
+ vreg_to_ssa_map_ = saved_ssa_map;
+ return;
+}
+
+/* Perform SSA transformation for the whole method */
+void MIRGraph::SSATransformation()
+{
+ /* Compute the DFS order */
+ ComputeDFSOrders();
+
+ /* Compute the dominator info */
+ ComputeDominators();
+
+ /* Allocate data structures in preparation for SSA conversion */
+ CompilerInitializeSSAConversion();
+
+ /* Find out the "Dalvik reg def x block" relation */
+ ComputeDefBlockMatrix();
+
+ /* Insert phi nodes to dominance frontiers for all variables */
+ InsertPhiNodes();
+
+ /* Rename register names by local defs and phi nodes */
+ ClearAllVisitedFlags();
+ DoDFSPreOrderSSARename(GetEntryBlock());
+
+ /*
+ * Shared temp bit vector used by each block to count the number of defs
+ * from all the predecessor blocks.
+ */
+ temp_ssa_register_v_ =
+ new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false, kBitMapTempSSARegisterV);
+
+ /* Insert phi-operands with latest SSA names from predecessor blocks */
+ ReachableNodesIterator iter2(this, false /* not iterative */);
+ for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
+ InsertPhiNodeOperands(bb);
+ }
+
+ if (cu_->enable_debug & (1 << kDebugDumpCFG)) {
+ DumpCFG("/sdcard/3_post_ssa_cfg/", false);
+ }
+ if (cu_->enable_debug & (1 << kDebugVerifyDataflow)) {
+ VerifyDataflow();
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc
new file mode 100644
index 0000000..adbda5c
--- /dev/null
+++ b/compiler/dex/vreg_analysis.cc
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2011 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 "compiler_internals.h"
+#include "dex/dataflow_iterator-inl.h"
+
+namespace art {
+
+bool MIRGraph::SetFp(int index, bool is_fp) {
+ bool change = false;
+ if (is_fp && !reg_location_[index].fp) {
+ reg_location_[index].fp = true;
+ reg_location_[index].defined = true;
+ change = true;
+ }
+ return change;
+}
+
+bool MIRGraph::SetCore(int index, bool is_core) {
+ bool change = false;
+ if (is_core && !reg_location_[index].defined) {
+ reg_location_[index].core = true;
+ reg_location_[index].defined = true;
+ change = true;
+ }
+ return change;
+}
+
+bool MIRGraph::SetRef(int index, bool is_ref) {
+ bool change = false;
+ if (is_ref && !reg_location_[index].defined) {
+ reg_location_[index].ref = true;
+ reg_location_[index].defined = true;
+ change = true;
+ }
+ return change;
+}
+
+bool MIRGraph::SetWide(int index, bool is_wide) {
+ bool change = false;
+ if (is_wide && !reg_location_[index].wide) {
+ reg_location_[index].wide = true;
+ change = true;
+ }
+ return change;
+}
+
+bool MIRGraph::SetHigh(int index, bool is_high) {
+ bool change = false;
+ if (is_high && !reg_location_[index].high_word) {
+ reg_location_[index].high_word = true;
+ change = true;
+ }
+ return change;
+}
+
+/*
+ * Infer types and sizes. We don't need to track change on sizes,
+ * as it doesn't propagate. We're guaranteed at least one pass through
+ * the cfg.
+ */
+bool MIRGraph::InferTypeAndSize(BasicBlock* bb)
+{
+ MIR *mir;
+ bool changed = false; // Did anything change?
+
+ if (bb->data_flow_info == NULL) return false;
+ if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock)
+ return false;
+
+ for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ SSARepresentation *ssa_rep = mir->ssa_rep;
+ if (ssa_rep) {
+ int attrs = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
+
+ // Handle defs
+ if (attrs & DF_DA) {
+ if (attrs & DF_CORE_A) {
+ changed |= SetCore(ssa_rep->defs[0], true);
+ }
+ if (attrs & DF_REF_A) {
+ changed |= SetRef(ssa_rep->defs[0], true);
+ }
+ if (attrs & DF_A_WIDE) {
+ reg_location_[ssa_rep->defs[0]].wide = true;
+ reg_location_[ssa_rep->defs[1]].wide = true;
+ reg_location_[ssa_rep->defs[1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(ssa_rep->defs[0])+1,
+ SRegToVReg(ssa_rep->defs[1]));
+ }
+ }
+
+ // Handles uses
+ int next = 0;
+ if (attrs & DF_UA) {
+ if (attrs & DF_CORE_A) {
+ changed |= SetCore(ssa_rep->uses[next], true);
+ }
+ if (attrs & DF_REF_A) {
+ changed |= SetRef(ssa_rep->uses[next], true);
+ }
+ if (attrs & DF_A_WIDE) {
+ reg_location_[ssa_rep->uses[next]].wide = true;
+ reg_location_[ssa_rep->uses[next + 1]].wide = true;
+ reg_location_[ssa_rep->uses[next + 1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(ssa_rep->uses[next])+1,
+ SRegToVReg(ssa_rep->uses[next + 1]));
+ next += 2;
+ } else {
+ next++;
+ }
+ }
+ if (attrs & DF_UB) {
+ if (attrs & DF_CORE_B) {
+ changed |= SetCore(ssa_rep->uses[next], true);
+ }
+ if (attrs & DF_REF_B) {
+ changed |= SetRef(ssa_rep->uses[next], true);
+ }
+ if (attrs & DF_B_WIDE) {
+ reg_location_[ssa_rep->uses[next]].wide = true;
+ reg_location_[ssa_rep->uses[next + 1]].wide = true;
+ reg_location_[ssa_rep->uses[next + 1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(ssa_rep->uses[next])+1,
+ SRegToVReg(ssa_rep->uses[next + 1]));
+ next += 2;
+ } else {
+ next++;
+ }
+ }
+ if (attrs & DF_UC) {
+ if (attrs & DF_CORE_C) {
+ changed |= SetCore(ssa_rep->uses[next], true);
+ }
+ if (attrs & DF_REF_C) {
+ changed |= SetRef(ssa_rep->uses[next], true);
+ }
+ if (attrs & DF_C_WIDE) {
+ reg_location_[ssa_rep->uses[next]].wide = true;
+ reg_location_[ssa_rep->uses[next + 1]].wide = true;
+ reg_location_[ssa_rep->uses[next + 1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(ssa_rep->uses[next])+1,
+ SRegToVReg(ssa_rep->uses[next + 1]));
+ }
+ }
+
+ // Special-case return handling
+ if ((mir->dalvikInsn.opcode == Instruction::RETURN) ||
+ (mir->dalvikInsn.opcode == Instruction::RETURN_WIDE) ||
+ (mir->dalvikInsn.opcode == Instruction::RETURN_OBJECT)) {
+ switch(cu_->shorty[0]) {
+ case 'I':
+ changed |= SetCore(ssa_rep->uses[0], true);
+ break;
+ case 'J':
+ changed |= SetCore(ssa_rep->uses[0], true);
+ changed |= SetCore(ssa_rep->uses[1], true);
+ reg_location_[ssa_rep->uses[0]].wide = true;
+ reg_location_[ssa_rep->uses[1]].wide = true;
+ reg_location_[ssa_rep->uses[1]].high_word = true;
+ break;
+ case 'F':
+ changed |= SetFp(ssa_rep->uses[0], true);
+ break;
+ case 'D':
+ changed |= SetFp(ssa_rep->uses[0], true);
+ changed |= SetFp(ssa_rep->uses[1], true);
+ reg_location_[ssa_rep->uses[0]].wide = true;
+ reg_location_[ssa_rep->uses[1]].wide = true;
+ reg_location_[ssa_rep->uses[1]].high_word = true;
+ break;
+ case 'L':
+ changed |= SetRef(ssa_rep->uses[0], true);
+ break;
+ default: break;
+ }
+ }
+
+ // Special-case handling for format 35c/3rc invokes
+ Instruction::Code opcode = mir->dalvikInsn.opcode;
+ int flags = (static_cast<int>(opcode) >= kNumPackedOpcodes)
+ ? 0 : Instruction::FlagsOf(mir->dalvikInsn.opcode);
+ if ((flags & Instruction::kInvoke) &&
+ (attrs & (DF_FORMAT_35C | DF_FORMAT_3RC))) {
+ DCHECK_EQ(next, 0);
+ int target_idx = mir->dalvikInsn.vB;
+ const char* shorty = GetShortyFromTargetIdx(target_idx);
+ // Handle result type if floating point
+ if ((shorty[0] == 'F') || (shorty[0] == 'D')) {
+ MIR* move_result_mir = FindMoveResult(bb, mir);
+ // Result might not be used at all, so no move-result
+ if (move_result_mir && (move_result_mir->dalvikInsn.opcode !=
+ Instruction::MOVE_RESULT_OBJECT)) {
+ SSARepresentation* tgt_rep = move_result_mir->ssa_rep;
+ DCHECK(tgt_rep != NULL);
+ tgt_rep->fp_def[0] = true;
+ changed |= SetFp(tgt_rep->defs[0], true);
+ if (shorty[0] == 'D') {
+ tgt_rep->fp_def[1] = true;
+ changed |= SetFp(tgt_rep->defs[1], true);
+ }
+ }
+ }
+ int num_uses = mir->dalvikInsn.vA;
+ // If this is a non-static invoke, mark implicit "this"
+ if (((mir->dalvikInsn.opcode != Instruction::INVOKE_STATIC) &&
+ (mir->dalvikInsn.opcode != Instruction::INVOKE_STATIC_RANGE))) {
+ reg_location_[ssa_rep->uses[next]].defined = true;
+ reg_location_[ssa_rep->uses[next]].ref = true;
+ next++;
+ }
+ uint32_t cpos = 1;
+ if (strlen(shorty) > 1) {
+ for (int i = next; i < num_uses;) {
+ DCHECK_LT(cpos, strlen(shorty));
+ switch (shorty[cpos++]) {
+ case 'D':
+ ssa_rep->fp_use[i] = true;
+ ssa_rep->fp_use[i+1] = true;
+ reg_location_[ssa_rep->uses[i]].wide = true;
+ reg_location_[ssa_rep->uses[i+1]].wide = true;
+ reg_location_[ssa_rep->uses[i+1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(ssa_rep->uses[i])+1, SRegToVReg(ssa_rep->uses[i+1]));
+ i++;
+ break;
+ case 'J':
+ reg_location_[ssa_rep->uses[i]].wide = true;
+ reg_location_[ssa_rep->uses[i+1]].wide = true;
+ reg_location_[ssa_rep->uses[i+1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(ssa_rep->uses[i])+1, SRegToVReg(ssa_rep->uses[i+1]));
+ changed |= SetCore(ssa_rep->uses[i],true);
+ i++;
+ break;
+ case 'F':
+ ssa_rep->fp_use[i] = true;
+ break;
+ case 'L':
+ changed |= SetRef(ssa_rep->uses[i], true);
+ break;
+ default:
+ changed |= SetCore(ssa_rep->uses[i], true);
+ break;
+ }
+ i++;
+ }
+ }
+ }
+
+ for (int i=0; ssa_rep->fp_use && i< ssa_rep->num_uses; i++) {
+ if (ssa_rep->fp_use[i])
+ changed |= SetFp(ssa_rep->uses[i], true);
+ }
+ for (int i=0; ssa_rep->fp_def && i< ssa_rep->num_defs; i++) {
+ if (ssa_rep->fp_def[i])
+ changed |= SetFp(ssa_rep->defs[i], true);
+ }
+ // Special-case handling for moves & Phi
+ if (attrs & (DF_IS_MOVE | DF_NULL_TRANSFER_N)) {
+ /*
+ * If any of our inputs or outputs is defined, set all.
+ * Some ugliness related to Phi nodes and wide values.
+ * The Phi set will include all low words or all high
+ * words, so we have to treat them specially.
+ */
+ bool is_phi = (static_cast<int>(mir->dalvikInsn.opcode) ==
+ kMirOpPhi);
+ RegLocation rl_temp = reg_location_[ssa_rep->defs[0]];
+ bool defined_fp = rl_temp.defined && rl_temp.fp;
+ bool defined_core = rl_temp.defined && rl_temp.core;
+ bool defined_ref = rl_temp.defined && rl_temp.ref;
+ bool is_wide = rl_temp.wide || ((attrs & DF_A_WIDE) != 0);
+ bool is_high = is_phi && rl_temp.wide && rl_temp.high_word;
+ for (int i = 0; i < ssa_rep->num_uses;i++) {
+ rl_temp = reg_location_[ssa_rep->uses[i]];
+ defined_fp |= rl_temp.defined && rl_temp.fp;
+ defined_core |= rl_temp.defined && rl_temp.core;
+ defined_ref |= rl_temp.defined && rl_temp.ref;
+ is_wide |= rl_temp.wide;
+ is_high |= is_phi && rl_temp.wide && rl_temp.high_word;
+ }
+ /*
+ * We don't normally expect to see a Dalvik register definition used both as a
+ * floating point and core value, though technically it could happen with constants.
+ * Until we have proper typing, detect this situation and disable register promotion
+ * (which relies on the distinction between core a fp usages).
+ */
+ if ((defined_fp && (defined_core | defined_ref)) &&
+ ((cu_->disable_opt & (1 << kPromoteRegs)) == 0)) {
+ LOG(WARNING) << PrettyMethod(cu_->method_idx, *cu_->dex_file)
+ << " op at block " << bb->id
+ << " has both fp and core/ref uses for same def.";
+ cu_->disable_opt |= (1 << kPromoteRegs);
+ }
+ changed |= SetFp(ssa_rep->defs[0], defined_fp);
+ changed |= SetCore(ssa_rep->defs[0], defined_core);
+ changed |= SetRef(ssa_rep->defs[0], defined_ref);
+ changed |= SetWide(ssa_rep->defs[0], is_wide);
+ changed |= SetHigh(ssa_rep->defs[0], is_high);
+ if (attrs & DF_A_WIDE) {
+ changed |= SetWide(ssa_rep->defs[1], true);
+ changed |= SetHigh(ssa_rep->defs[1], true);
+ }
+ for (int i = 0; i < ssa_rep->num_uses; i++) {
+ changed |= SetFp(ssa_rep->uses[i], defined_fp);
+ changed |= SetCore(ssa_rep->uses[i], defined_core);
+ changed |= SetRef(ssa_rep->uses[i], defined_ref);
+ changed |= SetWide(ssa_rep->uses[i], is_wide);
+ changed |= SetHigh(ssa_rep->uses[i], is_high);
+ }
+ if (attrs & DF_A_WIDE) {
+ DCHECK_EQ(ssa_rep->num_uses, 2);
+ changed |= SetWide(ssa_rep->uses[1], true);
+ changed |= SetHigh(ssa_rep->uses[1], true);
+ }
+ }
+ }
+ }
+ return changed;
+}
+
+static const char* storage_name[] = {" Frame ", "PhysReg", " Spill "};
+
+void MIRGraph::DumpRegLocTable(RegLocation* table, int count)
+{
+ //FIXME: Quick-specific. Move to Quick (and make a generic version for MIRGraph?
+ Mir2Lir* cg = static_cast<Mir2Lir*>(cu_->cg.get());
+ if (cg != NULL) {
+ for (int i = 0; i < count; i++) {
+ LOG(INFO) << StringPrintf("Loc[%02d] : %s, %c %c %c %c %c %c %c%d %c%d S%d",
+ table[i].orig_sreg, storage_name[table[i].location],
+ table[i].wide ? 'W' : 'N', table[i].defined ? 'D' : 'U',
+ table[i].fp ? 'F' : table[i].ref ? 'R' :'C',
+ table[i].is_const ? 'c' : 'n',
+ table[i].high_word ? 'H' : 'L', table[i].home ? 'h' : 't',
+ cg->IsFpReg(table[i].low_reg) ? 's' : 'r',
+ table[i].low_reg & cg->FpRegMask(),
+ cg->IsFpReg(table[i].high_reg) ? 's' : 'r',
+ table[i].high_reg & cg->FpRegMask(), table[i].s_reg_low);
+ }
+ } else {
+ // Either pre-regalloc or Portable.
+ for (int i = 0; i < count; i++) {
+ LOG(INFO) << StringPrintf("Loc[%02d] : %s, %c %c %c %c %c %c S%d",
+ table[i].orig_sreg, storage_name[table[i].location],
+ table[i].wide ? 'W' : 'N', table[i].defined ? 'D' : 'U',
+ table[i].fp ? 'F' : table[i].ref ? 'R' :'C',
+ table[i].is_const ? 'c' : 'n',
+ table[i].high_word ? 'H' : 'L', table[i].home ? 'h' : 't',
+ table[i].s_reg_low);
+ }
+ }
+}
+
+static const RegLocation fresh_loc = {kLocDalvikFrame, 0, 0, 0, 0, 0, 0, 0, 0,
+ INVALID_REG, INVALID_REG, INVALID_SREG,
+ INVALID_SREG};
+
+/*
+ * Simple register allocation. Some Dalvik virtual registers may
+ * be promoted to physical registers. Most of the work for temp
+ * allocation is done on the fly. We also do some initialization and
+ * type inference here.
+ */
+void MIRGraph::BuildRegLocations()
+{
+ int i;
+ RegLocation* loc;
+
+ /* Allocate the location map */
+ loc = static_cast<RegLocation*>(arena_->NewMem(GetNumSSARegs() * sizeof(*loc), true,
+ ArenaAllocator::kAllocRegAlloc));
+ for (i=0; i < GetNumSSARegs(); i++) {
+ loc[i] = fresh_loc;
+ loc[i].s_reg_low = i;
+ loc[i].is_const = is_constant_v_->IsBitSet(i);
+ }
+
+ /* Patch up the locations for Method* and the compiler temps */
+ loc[method_sreg_].location = kLocCompilerTemp;
+ loc[method_sreg_].defined = true;
+ for (i = 0; i < cu_->num_compiler_temps; i++) {
+ CompilerTemp* ct = compiler_temps_.Get(i);
+ loc[ct->s_reg].location = kLocCompilerTemp;
+ loc[ct->s_reg].defined = true;
+ }
+
+ reg_location_ = loc;
+
+ int num_regs = cu_->num_dalvik_registers;
+
+ /* Add types of incoming arguments based on signature */
+ int num_ins = cu_->num_ins;
+ if (num_ins > 0) {
+ int s_reg = num_regs - num_ins;
+ if ((cu_->access_flags & kAccStatic) == 0) {
+ // For non-static, skip past "this"
+ reg_location_[s_reg].defined = true;
+ reg_location_[s_reg].ref = true;
+ s_reg++;
+ }
+ const char* shorty = cu_->shorty;
+ int shorty_len = strlen(shorty);
+ for (int i = 1; i < shorty_len; i++) {
+ switch (shorty[i]) {
+ case 'D':
+ reg_location_[s_reg].wide = true;
+ reg_location_[s_reg+1].high_word = true;
+ reg_location_[s_reg+1].fp = true;
+ DCHECK_EQ(SRegToVReg(s_reg)+1, SRegToVReg(s_reg+1));
+ reg_location_[s_reg].fp = true;
+ reg_location_[s_reg].defined = true;
+ s_reg++;
+ break;
+ case 'J':
+ reg_location_[s_reg].wide = true;
+ reg_location_[s_reg+1].high_word = true;
+ DCHECK_EQ(SRegToVReg(s_reg)+1, SRegToVReg(s_reg+1));
+ reg_location_[s_reg].core = true;
+ reg_location_[s_reg].defined = true;
+ s_reg++;
+ break;
+ case 'F':
+ reg_location_[s_reg].fp = true;
+ reg_location_[s_reg].defined = true;
+ break;
+ case 'L':
+ reg_location_[s_reg].ref = true;
+ reg_location_[s_reg].defined = true;
+ break;
+ default:
+ reg_location_[s_reg].core = true;
+ reg_location_[s_reg].defined = true;
+ break;
+ }
+ s_reg++;
+ }
+ }
+
+ /* Do type & size inference pass */
+ PreOrderDfsIterator iter(this, true /* iterative */);
+ bool change = false;
+ for (BasicBlock* bb = iter.Next(false); bb != NULL; bb = iter.Next(change)) {
+ change = InferTypeAndSize(bb);
+ }
+
+ /*
+ * Set the s_reg_low field to refer to the pre-SSA name of the
+ * base Dalvik virtual register. Once we add a better register
+ * allocator, remove this remapping.
+ */
+ for (i=0; i < GetNumSSARegs(); i++) {
+ if (reg_location_[i].location != kLocCompilerTemp) {
+ int orig_sreg = reg_location_[i].s_reg_low;
+ reg_location_[i].orig_sreg = orig_sreg;
+ reg_location_[i].s_reg_low = SRegToVReg(orig_sreg);
+ }
+ }
+}
+
+} // namespace art