summaryrefslogtreecommitdiffstats
path: root/compiler/dex/quick/codegen_util.cc
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/dex/quick/codegen_util.cc')
-rw-r--r--compiler/dex/quick/codegen_util.cc1109
1 files changed, 1109 insertions, 0 deletions
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