Merge "Fix for incorrect parse of PEXTRW instruction"
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 8f00298..c60e75b 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -135,6 +135,22 @@
# Enable warning of converting ints to void*.
art_clang_cflags += -Wint-to-void-pointer-cast
+# Enable warning of wrong unused annotations.
+art_clang_cflags += -Wused-but-marked-unused
+
+# Enable warning for deprecated language features.
+art_clang_cflags += -Wdeprecated
+
+# Enable warning for unreachable break & return.
+art_clang_cflags += -Wunreachable-code-break -Wunreachable-code-return
+
+# Enable missing-noreturn only on non-Mac. As lots of things are not implemented for Apple, it's
+# a pain.
+ifneq ($(HOST_OS),darwin)
+ art_clang_cflags += -Wmissing-noreturn
+endif
+
+
# GCC-only warnings.
art_gcc_cflags := -Wunused-but-set-parameter
# Suggest const: too many false positives, but good for a trial run.
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 1e7988a..10bb90b 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -188,6 +188,7 @@
compiler/dex/local_value_numbering_test.cc \
compiler/dex/mir_graph_test.cc \
compiler/dex/mir_optimization_test.cc \
+ compiler/dex/quick/quick_cfi_test.cc \
compiler/dwarf/dwarf_test.cc \
compiler/driver/compiler_driver_test.cc \
compiler/elf_writer_test.cc \
@@ -257,6 +258,7 @@
LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk
$(eval $(call set-target-local-clang-vars))
$(eval $(call set-target-local-cflags-vars,debug))
+LOCAL_CLANG_CFLAGS += -Wno-used-but-marked-unused -Wno-deprecated -Wno-missing-noreturn # gtest issue
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
@@ -271,6 +273,7 @@
LOCAL_LDLIBS += -ldl -lpthread
LOCAL_MULTILIB := both
LOCAL_CLANG := $(ART_HOST_CLANG)
+LOCAL_CLANG_CFLAGS += -Wno-used-but-marked-unused -Wno-deprecated -Wno-missing-noreturn # gtest issue
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.gtest.mk
include $(BUILD_HOST_SHARED_LIBRARY)
@@ -403,7 +406,7 @@
LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION)
LOCAL_SRC_FILES := $$(art_gtest_filename)
LOCAL_C_INCLUDES += $$(ART_C_INCLUDES) art/runtime $$(art_gtest_extra_c_includes)
- LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) libart-gtest
+ LOCAL_SHARED_LIBRARIES += libartd $$(art_gtest_extra_shared_libraries) libart-gtest libart-disassembler
LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
@@ -423,6 +426,7 @@
LOCAL_MODULE_PATH_32 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_32)
LOCAL_MODULE_PATH_64 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_64)
LOCAL_MULTILIB := both
+ LOCAL_CLANG_CFLAGS += -Wno-used-but-marked-unused -Wno-deprecated -Wno-missing-noreturn # gtest issue
include $$(BUILD_EXECUTABLE)
library_path :=
2nd_library_path :=
@@ -461,6 +465,7 @@
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := $$(art_gtest_name)32
LOCAL_MODULE_STEM_64 := $$(art_gtest_name)64
+ LOCAL_CLANG_CFLAGS += -Wno-used-but-marked-unused -Wno-deprecated -Wno-missing-noreturn # gtest issue
include $$(BUILD_HOST_EXECUTABLE)
ART_TEST_HOST_GTEST_$$(art_gtest_name)_RULES :=
diff --git a/cmdline/unit.h b/cmdline/unit.h
index 6b53b18..ad6a03d 100644
--- a/cmdline/unit.h
+++ b/cmdline/unit.h
@@ -24,6 +24,7 @@
// Avoid 'Conditional jump or move depends on uninitialised value(s)' errors
// when running valgrind by specifying a user-defined constructor.
Unit() {}
+ Unit(const Unit&) = default;
~Unit() {}
bool operator==(Unit) const {
return true;
diff --git a/compiler/Android.mk b/compiler/Android.mk
index eaea031..94322a8 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -41,6 +41,7 @@
dex/quick/gen_common.cc \
dex/quick/gen_invoke.cc \
dex/quick/gen_loadstore.cc \
+ dex/quick/lazy_debug_frame_opcode_writer.cc \
dex/quick/local_optimizations.cc \
dex/quick/mips/assemble_mips.cc \
dex/quick/mips/call_mips.cc \
@@ -103,6 +104,7 @@
optimizing/code_generator_arm64.cc \
optimizing/code_generator_x86.cc \
optimizing/code_generator_x86_64.cc \
+ optimizing/code_generator_utils.cc \
optimizing/constant_folding.cc \
optimizing/dead_code_elimination.cc \
optimizing/graph_checker.cc \
@@ -138,7 +140,6 @@
utils/arm64/assembler_arm64.cc \
utils/arm64/managed_register_arm64.cc \
utils/assembler.cc \
- utils/dwarf_cfi.cc \
utils/mips/assembler_mips.cc \
utils/mips/managed_register_mips.cc \
utils/mips64/assembler_mips64.cc \
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
new file mode 100644
index 0000000..f550395
--- /dev/null
+++ b/compiler/cfi_test.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 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_COMPILER_CFI_TEST_H_
+#define ART_COMPILER_CFI_TEST_H_
+
+#include <vector>
+#include <memory>
+#include <sstream>
+
+#include "arch/instruction_set.h"
+#include "dwarf/debug_frame_writer.h"
+#include "dwarf/dwarf_test.h"
+#include "disassembler/disassembler.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+class CFITest : public dwarf::DwarfTest {
+ public:
+ void GenerateExpected(FILE* f, InstructionSet isa, const char* isa_str,
+ const std::vector<uint8_t>& actual_asm,
+ const std::vector<uint8_t>& actual_cfi) {
+ std::vector<std::string> lines;
+ // Print the raw bytes.
+ fprintf(f, "static constexpr uint8_t expected_asm_%s[] = {", isa_str);
+ HexDump(f, actual_asm);
+ fprintf(f, "\n};\n");
+ fprintf(f, "static constexpr uint8_t expected_cfi_%s[] = {", isa_str);
+ HexDump(f, actual_cfi);
+ fprintf(f, "\n};\n");
+ // Pretty-print CFI opcodes.
+ dwarf::DebugFrameWriter<> eh_frame(&eh_frame_data_, false);
+ eh_frame.WriteCIE(dwarf::Reg(8), {});
+ eh_frame.WriteFDE(0, actual_asm.size(), actual_cfi.data(), actual_cfi.size());
+ ReformatCfi(Objdump(false, "-W"), &lines);
+ // Pretty-print assembly.
+ auto* opts = new DisassemblerOptions(false, actual_asm.data(), true);
+ std::unique_ptr<Disassembler> disasm(Disassembler::Create(isa, opts));
+ std::stringstream stream;
+ const uint8_t* base = actual_asm.data() + (isa == kThumb2 ? 1 : 0);
+ disasm->Dump(stream, base, base + actual_asm.size());
+ ReformatAsm(&stream, &lines);
+ // Print CFI and assembly interleaved.
+ std::stable_sort(lines.begin(), lines.end(), CompareByAddress);
+ for (const std::string& line : lines) {
+ fprintf(f, "// %s\n", line.c_str());
+ }
+ fprintf(f, "\n");
+ }
+
+ private:
+ // Helper - get offset just past the end of given string.
+ static size_t FindEndOf(const std::string& str, const char* substr) {
+ size_t pos = str.find(substr);
+ CHECK_NE(std::string::npos, pos);
+ return pos + strlen(substr);
+ }
+
+ // Spit to lines and remove raw instruction bytes.
+ static void ReformatAsm(std::stringstream* stream,
+ std::vector<std::string>* output) {
+ std::string line;
+ while (std::getline(*stream, line)) {
+ line = line.substr(0, FindEndOf(line, ": ")) +
+ line.substr(FindEndOf(line, "\t"));
+ size_t pos;
+ while ((pos = line.find(" ")) != std::string::npos) {
+ line = line.replace(pos, 2, " ");
+ }
+ while (!line.empty() && line.back() == ' ') {
+ line.pop_back();
+ }
+ output->push_back(line);
+ }
+ }
+
+ // Find interesting parts of objdump output and prefix the lines with address.
+ static void ReformatCfi(const std::vector<std::string>& lines,
+ std::vector<std::string>* output) {
+ std::string address;
+ for (const std::string& line : lines) {
+ if (line.find("DW_CFA_nop") != std::string::npos) {
+ // Ignore.
+ } else if (line.find("DW_CFA_advance_loc") != std::string::npos) {
+ // The last 8 characters are the address.
+ address = "0x" + line.substr(line.size() - 8);
+ } else if (line.find("DW_CFA_") != std::string::npos) {
+ std::string new_line(line);
+ // "bad register" warning is caused by always using host (x86) objdump.
+ const char* bad_reg = "bad register: ";
+ size_t pos;
+ if ((pos = new_line.find(bad_reg)) != std::string::npos) {
+ new_line = new_line.replace(pos, strlen(bad_reg), "");
+ }
+ // Remove register names in parentheses since they have x86 names.
+ if ((pos = new_line.find(" (")) != std::string::npos) {
+ new_line = new_line.replace(pos, FindEndOf(new_line, ")") - pos, "");
+ }
+ // Use the .cfi_ prefix.
+ new_line = ".cfi_" + new_line.substr(FindEndOf(new_line, "DW_CFA_"));
+ output->push_back(address + ": " + new_line);
+ }
+ }
+ }
+
+ // Compare strings by the address prefix.
+ static bool CompareByAddress(const std::string& lhs, const std::string& rhs) {
+ EXPECT_EQ(lhs[10], ':');
+ EXPECT_EQ(rhs[10], ':');
+ return strncmp(lhs.c_str(), rhs.c_str(), 10) < 0;
+ }
+
+ // Pretty-print byte array. 12 bytes per line.
+ static void HexDump(FILE* f, const std::vector<uint8_t>& data) {
+ for (size_t i = 0; i < data.size(); i++) {
+ fprintf(f, i % 12 == 0 ? "\n " : " "); // Whitespace.
+ fprintf(f, "0x%02X,", data[i]);
+ }
+ }
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_CFI_TEST_H_
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index 03370db..eeed877 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -132,7 +132,7 @@
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& native_gc_map,
const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<LinkerPatch>& patches)
+ const ArrayRef<const LinkerPatch>& patches)
: CompiledCode(driver, instruction_set, quick_code, !driver->DedupeEnabled()),
owns_arrays_(!driver->DedupeEnabled()),
frame_size_in_bytes_(frame_size_in_bytes), core_spill_mask_(core_spill_mask),
@@ -179,7 +179,7 @@
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& native_gc_map,
const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<LinkerPatch>& patches) {
+ const ArrayRef<const LinkerPatch>& patches) {
SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
CompiledMethod* ret = alloc.allocate(1);
alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
@@ -200,7 +200,8 @@
CompiledMethod* ret = alloc.allocate(1);
alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
fp_spill_mask, nullptr, ArrayRef<const uint8_t>(), stack_map,
- ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(), ArrayRef<LinkerPatch>());
+ ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(),
+ ArrayRef<const LinkerPatch>());
return ret;
}
@@ -217,7 +218,7 @@
alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
fp_spill_mask, nullptr, ArrayRef<const uint8_t>(),
ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(),
- cfi_info, ArrayRef<LinkerPatch>());
+ cfi_info, ArrayRef<const LinkerPatch>());
return ret;
}
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 7497b17..506b47b 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -320,7 +320,7 @@
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& native_gc_map,
const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<LinkerPatch>& patches = ArrayRef<LinkerPatch>());
+ const ArrayRef<const LinkerPatch>& patches = ArrayRef<const LinkerPatch>());
virtual ~CompiledMethod();
@@ -336,7 +336,7 @@
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& native_gc_map,
const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<LinkerPatch>& patches = ArrayRef<LinkerPatch>());
+ const ArrayRef<const LinkerPatch>& patches = ArrayRef<const LinkerPatch>());
static CompiledMethod* SwapAllocCompiledMethodStackMap(
CompilerDriver* driver,
@@ -391,8 +391,8 @@
return cfi_info_;
}
- const SwapVector<LinkerPatch>& GetPatches() const {
- return patches_;
+ ArrayRef<const LinkerPatch> GetPatches() const {
+ return ArrayRef<const LinkerPatch>(patches_);
}
private:
@@ -417,7 +417,7 @@
// For quick code, a FDE entry for the debug_frame section.
SwapVector<uint8_t>* cfi_info_;
// For quick code, linker patches needed by the method.
- SwapVector<LinkerPatch> patches_;
+ const SwapVector<LinkerPatch> patches_;
};
} // namespace art
diff --git a/compiler/compiler.h b/compiler/compiler.h
index 6ec39f9..a04641e 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -107,6 +107,9 @@
return driver_;
}
+ // Whether to produce 64-bit ELF files for 64-bit targets. Leave this off for now.
+ static constexpr bool kProduce64BitELFFiles = false;
+
private:
CompilerDriver* const driver_;
const uint64_t maximum_compilation_time_before_warning_;
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h
index 93d83c6..0850f42 100644
--- a/compiler/dex/bb_optimizations.h
+++ b/compiler/dex/bb_optimizations.h
@@ -403,13 +403,6 @@
DCHECK(bb != nullptr);
return c_unit->mir_graph->EliminateSuspendChecks(bb);
}
-
- void End(PassDataHolder* data) const {
- DCHECK(data != nullptr);
- CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
- DCHECK(c_unit != nullptr);
- c_unit->mir_graph->EliminateSuspendChecksEnd();
- }
};
} // namespace art
diff --git a/compiler/dex/gvn_dead_code_elimination.cc b/compiler/dex/gvn_dead_code_elimination.cc
index 2d4c18f..ec12221 100644
--- a/compiler/dex/gvn_dead_code_elimination.cc
+++ b/compiler/dex/gvn_dead_code_elimination.cc
@@ -1357,7 +1357,6 @@
default:
LOG(FATAL) << "Unexpected opcode: " << opcode;
UNREACHABLE();
- break;
}
if (mir->ssa_rep->num_defs != 0) {
diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc
index dc222b5..cdf5e38 100644
--- a/compiler/dex/local_value_numbering.cc
+++ b/compiler/dex/local_value_numbering.cc
@@ -166,9 +166,9 @@
return gvn->LookupValue(kAliasingArrayOp, type, location, memory_version);
}
- static uint16_t LookupMergeValue(GlobalValueNumbering* gvn ATTRIBUTE_UNUSED,
+ static uint16_t LookupMergeValue(GlobalValueNumbering* gvn,
const LocalValueNumbering* lvn,
- uint16_t type ATTRIBUTE_UNUSED, uint16_t location) {
+ uint16_t type, uint16_t location) {
// If the location is non-aliasing in lvn, use the non-aliasing value.
uint16_t array = gvn->GetArrayLocationBase(location);
if (lvn->IsNonAliasingArray(array, type)) {
@@ -182,8 +182,6 @@
static bool HasNewBaseVersion(GlobalValueNumbering* gvn ATTRIBUTE_UNUSED,
const LocalValueNumbering* lvn,
uint16_t type ATTRIBUTE_UNUSED) {
- UNUSED(gvn);
- UNUSED(type);
return lvn->global_memory_version_ == lvn->merge_new_memory_version_;
}
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
index f638b0b..2a920a4 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -1396,6 +1396,13 @@
InitializeBasicBlockDataFlow();
}
+uint32_t MIRGraph::GetUseCountWeight(BasicBlock* bb) const {
+ // Each level of nesting adds *100 to count, up to 3 levels deep.
+ uint32_t depth = std::min(3U, static_cast<uint32_t>(bb->nesting_depth));
+ uint32_t weight = std::max(1U, depth * 100);
+ return weight;
+}
+
/*
* Count uses, weighting by loop nesting depth. This code only
* counts explicitly used s_regs. A later phase will add implicit
@@ -1405,9 +1412,7 @@
if (bb->block_type != kDalvikByteCode) {
return;
}
- // Each level of nesting adds *100 to count, up to 3 levels deep.
- uint32_t depth = std::min(3U, static_cast<uint32_t>(bb->nesting_depth));
- uint32_t weight = std::max(1U, depth * 100);
+ uint32_t weight = GetUseCountWeight(bb);
for (MIR* mir = bb->first_mir_insn; (mir != NULL); mir = mir->next) {
if (mir->ssa_rep == NULL) {
continue;
@@ -1417,23 +1422,6 @@
raw_use_counts_[s_reg] += 1u;
use_counts_[s_reg] += weight;
}
- if (!(cu_->disable_opt & (1 << kPromoteCompilerTemps))) {
- uint64_t df_attributes = GetDataFlowAttributes(mir);
- // 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. This is a relatively expensive
- * operation, so should only be done once.
- * TODO: refactor InvokeUsesMethodStar() to perform check at parse time,
- * and save results for both here and GenInvoke. For now, go ahead
- * and assume all invokes use method*.
- */
- raw_use_counts_[method_sreg_] += 1u;
- use_counts_[method_sreg_] += weight;
- }
- }
}
}
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 58f12c9..4d34038 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -1609,8 +1609,8 @@
}
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
+ // TODO: This value is needed for 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.
int vreg = SRegToVReg(ssa_reg);
if (vreg >= static_cast<int>(GetFirstTempVR())) {
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 3298af1..85b1344 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -960,6 +960,12 @@
*/
CompilerTemp* GetNewCompilerTemp(CompilerTempType ct_type, bool wide);
+ /**
+ * @brief Used to remove last created compiler temporary when it's not needed.
+ * @param temp the temporary to remove.
+ */
+ void RemoveLastCompilerTemp(CompilerTempType ct_type, bool wide, CompilerTemp* temp);
+
bool MethodIsLeaf() {
return attributes_ & METHOD_IS_LEAF;
}
@@ -1079,7 +1085,6 @@
void EliminateDeadCodeEnd();
bool EliminateSuspendChecksGate();
bool EliminateSuspendChecks(BasicBlock* bb);
- void EliminateSuspendChecksEnd();
uint16_t GetGvnIFieldId(MIR* mir) const {
DCHECK(IsInstructionIGetOrIPut(mir->dalvikInsn.opcode));
@@ -1185,6 +1190,12 @@
void DoConstantPropagation(BasicBlock* bb);
/**
+ * @brief Get use count weight for a given block.
+ * @param bb the BasicBlock.
+ */
+ uint32_t GetUseCountWeight(BasicBlock* bb) const;
+
+ /**
* @brief Count the uses in the BasicBlock
* @param bb the BasicBlock
*/
@@ -1396,10 +1407,6 @@
uint16_t* sfield_ids; // Ditto.
GvnDeadCodeElimination* dce;
} gvn;
- // Suspend check elimination.
- struct {
- DexFileMethodInliner* inliner;
- } sce;
} temp_;
static const int kInvalidEntry = -1;
ArenaVector<BasicBlock*> block_list_;
@@ -1451,6 +1458,7 @@
friend class GvnDeadCodeEliminationTest;
friend class LocalValueNumberingTest;
friend class TopologicalSortOrderTest;
+ friend class QuickCFITest;
};
} // namespace art
diff --git a/compiler/dex/mir_method_info.cc b/compiler/dex/mir_method_info.cc
index 831ad42..0c84b82 100644
--- a/compiler/dex/mir_method_info.cc
+++ b/compiler/dex/mir_method_info.cc
@@ -16,6 +16,8 @@
# include "mir_method_info.h"
+#include "dex/quick/dex_file_method_inliner.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex/verified_method.h"
#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
@@ -64,6 +66,9 @@
const DexFile* const dex_file = mUnit->GetDexFile();
const bool use_jit = runtime->UseJit();
const VerifiedMethod* const verified_method = mUnit->GetVerifiedMethod();
+ DexFileToMethodInlinerMap* inliner_map = compiler_driver->GetMethodInlinerMap();
+ DexFileMethodInliner* default_inliner =
+ (inliner_map != nullptr) ? inliner_map->GetMethodInliner(dex_file) : nullptr;
for (auto it = method_infos, end = method_infos + count; it != end; ++it) {
// For quickened invokes, the dex method idx is actually the mir offset.
@@ -122,6 +127,7 @@
if (UNLIKELY(resolved_method == nullptr)) {
continue;
}
+
compiler_driver->GetResolvedMethodDexFileLocation(resolved_method,
&it->declaring_dex_file_, &it->declaring_class_idx_, &it->declaring_method_idx_);
if (!it->IsQuickened()) {
@@ -133,6 +139,7 @@
it->vtable_idx_ =
compiler_driver->GetResolvedMethodVTableIndex(resolved_method, invoke_type);
}
+
MethodReference target_method(it->target_dex_file_, it->target_method_idx_);
int fast_path_flags = compiler_driver->IsFastInvoke(
soa, current_dex_cache, class_loader, mUnit, referrer_class.Get(), resolved_method,
@@ -140,10 +147,23 @@
const bool is_referrers_class = referrer_class.Get() == resolved_method->GetDeclaringClass();
const bool is_class_initialized =
compiler_driver->IsMethodsClassInitialized(referrer_class.Get(), resolved_method);
+
+ // Check if the target method is intrinsic or special.
+ InlineMethodFlags is_intrinsic_or_special = kNoInlineMethodFlags;
+ if (inliner_map != nullptr) {
+ auto* inliner = (target_method.dex_file == dex_file)
+ ? default_inliner
+ : inliner_map->GetMethodInliner(target_method.dex_file);
+ is_intrinsic_or_special = inliner->IsIntrinsicOrSpecial(target_method.dex_method_index);
+ }
+
uint16_t other_flags = it->flags_ &
- ~(kFlagFastPath | kFlagClassIsInitialized | (kInvokeTypeMask << kBitSharpTypeBegin));
+ ~(kFlagFastPath | kFlagIsIntrinsic | kFlagIsSpecial | kFlagClassIsInitialized |
+ (kInvokeTypeMask << kBitSharpTypeBegin));
it->flags_ = other_flags |
(fast_path_flags != 0 ? kFlagFastPath : 0u) |
+ ((is_intrinsic_or_special & kInlineIntrinsic) != 0 ? kFlagIsIntrinsic : 0u) |
+ ((is_intrinsic_or_special & kInlineSpecial) != 0 ? kFlagIsSpecial : 0u) |
(static_cast<uint16_t>(invoke_type) << kBitSharpTypeBegin) |
(is_referrers_class ? kFlagIsReferrersClass : 0u) |
(is_class_initialized ? kFlagClassIsInitialized : 0u);
diff --git a/compiler/dex/mir_method_info.h b/compiler/dex/mir_method_info.h
index e131c96..7230c46 100644
--- a/compiler/dex/mir_method_info.h
+++ b/compiler/dex/mir_method_info.h
@@ -127,6 +127,14 @@
return (flags_ & kFlagFastPath) != 0u;
}
+ bool IsIntrinsic() const {
+ return (flags_ & kFlagIsIntrinsic) != 0u;
+ }
+
+ bool IsSpecial() const {
+ return (flags_ & kFlagIsSpecial) != 0u;
+ }
+
bool IsReferrersClass() const {
return (flags_ & kFlagIsReferrersClass) != 0;
}
@@ -188,9 +196,11 @@
private:
enum {
kBitFastPath = kMethodInfoBitEnd,
+ kBitIsIntrinsic,
+ kBitIsSpecial,
kBitInvokeTypeBegin,
kBitInvokeTypeEnd = kBitInvokeTypeBegin + 3, // 3 bits for invoke type.
- kBitSharpTypeBegin,
+ kBitSharpTypeBegin = kBitInvokeTypeEnd,
kBitSharpTypeEnd = kBitSharpTypeBegin + 3, // 3 bits for sharp type.
kBitIsReferrersClass = kBitSharpTypeEnd,
kBitClassIsInitialized,
@@ -199,6 +209,8 @@
};
static_assert(kMethodLoweringInfoBitEnd <= 16, "Too many flags");
static constexpr uint16_t kFlagFastPath = 1u << kBitFastPath;
+ static constexpr uint16_t kFlagIsIntrinsic = 1u << kBitIsIntrinsic;
+ static constexpr uint16_t kFlagIsSpecial = 1u << kBitIsSpecial;
static constexpr uint16_t kFlagIsReferrersClass = 1u << kBitIsReferrersClass;
static constexpr uint16_t kFlagClassIsInitialized = 1u << kBitClassIsInitialized;
static constexpr uint16_t kFlagQuickened = 1u << kBitQuickened;
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index c85c3b6..9d7b4b4 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -318,9 +318,11 @@
// Since VR temps cannot be requested once the BE temps are requested, we
// allow reservation of VR temps as well for BE. We
size_t available_temps = reserved_temps_for_backend_ + GetNumAvailableVRTemps();
- if (available_temps <= 0 || (available_temps <= 1 && wide)) {
+ size_t needed_temps = wide ? 2u : 1u;
+ if (available_temps < needed_temps) {
if (verbose) {
- LOG(INFO) << "CompilerTemps: Not enough temp(s) of type " << ct_type_str << " are available.";
+ LOG(INFO) << "CompilerTemps: Not enough temp(s) of type " << ct_type_str
+ << " are available.";
}
return nullptr;
}
@@ -328,12 +330,8 @@
// Update the remaining reserved temps since we have now used them.
// Note that the code below is actually subtracting to remove them from reserve
// once they have been claimed. It is careful to not go below zero.
- if (reserved_temps_for_backend_ >= 1) {
- reserved_temps_for_backend_--;
- }
- if (wide && reserved_temps_for_backend_ >= 1) {
- reserved_temps_for_backend_--;
- }
+ reserved_temps_for_backend_ =
+ std::max(reserved_temps_for_backend_, needed_temps) - needed_temps;
// The new non-special compiler temp must receive a unique v_reg.
compiler_temp->v_reg = GetFirstNonSpecialTempVR() + num_non_special_compiler_temps_;
@@ -407,6 +405,36 @@
return compiler_temp;
}
+void MIRGraph::RemoveLastCompilerTemp(CompilerTempType ct_type, bool wide, CompilerTemp* temp) {
+ // Once the compiler temps have been committed, it's too late for any modifications.
+ DCHECK_EQ(compiler_temps_committed_, false);
+
+ size_t used_temps = wide ? 2u : 1u;
+
+ if (ct_type == kCompilerTempBackend) {
+ DCHECK(requested_backend_temp_);
+
+ // Make the temps available to backend again.
+ reserved_temps_for_backend_ += used_temps;
+ } else if (ct_type == kCompilerTempVR) {
+ DCHECK(!requested_backend_temp_);
+ } else {
+ UNIMPLEMENTED(FATAL) << "No handling for compiler temp type " << static_cast<int>(ct_type);
+ }
+
+ // Reduce the number of non-special compiler temps.
+ DCHECK_LE(used_temps, num_non_special_compiler_temps_);
+ num_non_special_compiler_temps_ -= used_temps;
+
+ // Check that this was really the last temp.
+ DCHECK_EQ(static_cast<size_t>(temp->v_reg),
+ GetFirstNonSpecialTempVR() + num_non_special_compiler_temps_);
+
+ if (cu_->verbose) {
+ LOG(INFO) << "Last temporary has been removed.";
+ }
+}
+
static bool EvaluateBranch(Instruction::Code opcode, int32_t src1, int32_t src2) {
bool is_taken;
switch (opcode) {
@@ -1489,7 +1517,7 @@
continue;
}
const MirMethodLoweringInfo& method_info = GetMethodLoweringInfo(mir);
- if (!method_info.FastPath()) {
+ if (!method_info.FastPath() || !method_info.IsSpecial()) {
continue;
}
@@ -1631,10 +1659,6 @@
!HasInvokes()) { // No invokes to actually eliminate any suspend checks.
return false;
}
- if (cu_->compiler_driver != nullptr && cu_->compiler_driver->GetMethodInlinerMap() != nullptr) {
- temp_.sce.inliner =
- cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file);
- }
suspend_checks_in_loops_ = arena_->AllocArray<uint32_t>(GetNumBlocks(), kArenaAllocMisc);
return true;
}
@@ -1652,9 +1676,9 @@
uint32_t suspend_checks_in_loops = (1u << bb->nesting_depth) - 1u; // Start with all loop heads.
bool found_invoke = false;
for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
- if (IsInstructionInvoke(mir->dalvikInsn.opcode) &&
- (temp_.sce.inliner == nullptr ||
- !temp_.sce.inliner->IsIntrinsic(mir->dalvikInsn.vB, nullptr))) {
+ if ((IsInstructionInvoke(mir->dalvikInsn.opcode) ||
+ IsInstructionQuickInvoke(mir->dalvikInsn.opcode)) &&
+ !GetMethodLoweringInfo(mir).IsIntrinsic()) {
// Non-intrinsic invoke, rely on a suspend point in the invoked method.
found_invoke = true;
break;
@@ -1717,10 +1741,6 @@
return true;
}
-void MIRGraph::EliminateSuspendChecksEnd() {
- temp_.sce.inliner = nullptr;
-}
-
bool MIRGraph::CanThrow(MIR* mir) const {
if ((mir->dalvikInsn.FlagsOf() & Instruction::kThrow) == 0) {
return false;
diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc
index 9ce5ebb..10a4337 100644
--- a/compiler/dex/mir_optimization_test.cc
+++ b/compiler/dex/mir_optimization_test.cc
@@ -474,7 +474,6 @@
for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
change = cu_.mir_graph->EliminateSuspendChecks(bb);
}
- cu_.mir_graph->EliminateSuspendChecksEnd();
}
SuspendCheckEliminationTest()
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index e6158c3..3d18af6 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -29,6 +29,7 @@
#include "mirror/object_array-inl.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "utils.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
namespace art {
@@ -354,7 +355,16 @@
FreeTemp(reg_card_no);
}
+static dwarf::Reg DwarfCoreReg(int num) {
+ return dwarf::Reg::ArmCore(num);
+}
+
+static dwarf::Reg DwarfFpReg(int num) {
+ return dwarf::Reg::ArmFp(num);
+}
+
void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
+ DCHECK_EQ(cfi_.GetCurrentCFAOffset(), 0); // empty stack.
int spill_count = num_core_spills_ + num_fp_spills_;
/*
* On entry, r0, r1, r2 & r3 are live. Let the register allocation
@@ -402,28 +412,32 @@
}
}
/* Spill core callee saves */
- if (core_spill_mask_ == 0u) {
- // Nothing to spill.
- } else if ((core_spill_mask_ & ~(0xffu | (1u << rs_rARM_LR.GetRegNum()))) == 0u) {
- // Spilling only low regs and/or LR, use 16-bit PUSH.
- constexpr int lr_bit_shift = rs_rARM_LR.GetRegNum() - 8;
- NewLIR1(kThumbPush,
- (core_spill_mask_ & ~(1u << rs_rARM_LR.GetRegNum())) |
- ((core_spill_mask_ & (1u << rs_rARM_LR.GetRegNum())) >> lr_bit_shift));
- } else if (IsPowerOfTwo(core_spill_mask_)) {
- // kThumb2Push cannot be used to spill a single register.
- NewLIR1(kThumb2Push1, CTZ(core_spill_mask_));
- } else {
- NewLIR1(kThumb2Push, core_spill_mask_);
+ if (core_spill_mask_ != 0u) {
+ if ((core_spill_mask_ & ~(0xffu | (1u << rs_rARM_LR.GetRegNum()))) == 0u) {
+ // Spilling only low regs and/or LR, use 16-bit PUSH.
+ constexpr int lr_bit_shift = rs_rARM_LR.GetRegNum() - 8;
+ NewLIR1(kThumbPush,
+ (core_spill_mask_ & ~(1u << rs_rARM_LR.GetRegNum())) |
+ ((core_spill_mask_ & (1u << rs_rARM_LR.GetRegNum())) >> lr_bit_shift));
+ } else if (IsPowerOfTwo(core_spill_mask_)) {
+ // kThumb2Push cannot be used to spill a single register.
+ NewLIR1(kThumb2Push1, CTZ(core_spill_mask_));
+ } else {
+ NewLIR1(kThumb2Push, core_spill_mask_);
+ }
+ cfi_.AdjustCFAOffset(num_core_spills_ * kArmPointerSize);
+ cfi_.RelOffsetForMany(DwarfCoreReg(0), 0, core_spill_mask_, kArmPointerSize);
}
/* Need to spill any FP regs? */
- if (num_fp_spills_) {
+ if (num_fp_spills_ != 0u) {
/*
* 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_);
+ cfi_.AdjustCFAOffset(num_fp_spills_ * kArmPointerSize);
+ cfi_.RelOffsetForMany(DwarfFpReg(0), 0, fp_spill_mask_, kArmPointerSize);
}
const int spill_size = spill_count * 4;
@@ -444,12 +458,14 @@
m2l_->LoadWordDisp(rs_rARM_SP, sp_displace_ - 4, rs_rARM_LR);
}
m2l_->OpRegImm(kOpAdd, rs_rARM_SP, sp_displace_);
+ m2l_->cfi().AdjustCFAOffset(-sp_displace_);
m2l_->ClobberCallerSave();
ThreadOffset<4> func_offset = QUICK_ENTRYPOINT_OFFSET(4, pThrowStackOverflow);
// Load the entrypoint directly into the pc instead of doing a load + branch. Assumes
// codegen and target are in thumb2 mode.
// NOTE: native pointer.
m2l_->LoadWordDisp(rs_rARM_SELF, func_offset.Int32Value(), rs_rARM_PC);
+ m2l_->cfi().AdjustCFAOffset(sp_displace_);
}
private:
@@ -464,6 +480,7 @@
// Need to restore LR since we used it as a temp.
AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, true, spill_size));
OpRegCopy(rs_rARM_SP, rs_rARM_LR); // Establish stack
+ cfi_.AdjustCFAOffset(frame_size_without_spills);
} else {
/*
* If the frame is small enough we are guaranteed to have enough space that remains to
@@ -474,6 +491,7 @@
MarkTemp(rs_rARM_LR);
FreeTemp(rs_rARM_LR);
OpRegRegImm(kOpSub, rs_rARM_SP, rs_rARM_SP, frame_size_without_spills);
+ cfi_.AdjustCFAOffset(frame_size_without_spills);
Clobber(rs_rARM_LR);
UnmarkTemp(rs_rARM_LR);
LIR* branch = OpCmpBranch(kCondUlt, rs_rARM_SP, rs_r12, nullptr);
@@ -483,13 +501,23 @@
// Implicit stack overflow check has already been done. Just make room on the
// stack for the frame now.
OpRegImm(kOpSub, rs_rARM_SP, frame_size_without_spills);
+ cfi_.AdjustCFAOffset(frame_size_without_spills);
}
} else {
OpRegImm(kOpSub, rs_rARM_SP, frame_size_without_spills);
+ cfi_.AdjustCFAOffset(frame_size_without_spills);
}
FlushIns(ArgLocs, rl_method);
+ // We can promote a PC-relative reference to dex cache arrays to a register
+ // if it's used at least twice. Without investigating where we should lazily
+ // load the reference, we conveniently load it after flushing inputs.
+ if (dex_cache_arrays_base_reg_.Valid()) {
+ OpPcRelDexCacheArrayAddr(cu_->dex_file, dex_cache_arrays_min_offset_,
+ dex_cache_arrays_base_reg_);
+ }
+
FreeTemp(rs_r0);
FreeTemp(rs_r1);
FreeTemp(rs_r2);
@@ -498,7 +526,9 @@
}
void ArmMir2Lir::GenExitSequence() {
+ cfi_.RememberState();
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.
@@ -506,34 +536,47 @@
LockTemp(rs_r0);
LockTemp(rs_r1);
- OpRegImm(kOpAdd, rs_rARM_SP, frame_size_ - (spill_count * 4));
+ int adjust = frame_size_ - (spill_count * kArmPointerSize);
+ OpRegImm(kOpAdd, rs_rARM_SP, adjust);
+ cfi_.AdjustCFAOffset(-adjust);
/* Need to restore any FP callee saves? */
if (num_fp_spills_) {
NewLIR1(kThumb2VPopCS, num_fp_spills_);
+ cfi_.AdjustCFAOffset(-num_fp_spills_ * kArmPointerSize);
+ cfi_.RestoreMany(DwarfFpReg(0), fp_spill_mask_);
}
- if ((core_spill_mask_ & (1 << rs_rARM_LR.GetRegNum())) != 0) {
- /* Unspill rARM_LR to rARM_PC */
+ bool unspill_LR_to_PC = (core_spill_mask_ & (1 << rs_rARM_LR.GetRegNum())) != 0;
+ if (unspill_LR_to_PC) {
core_spill_mask_ &= ~(1 << rs_rARM_LR.GetRegNum());
core_spill_mask_ |= (1 << rs_rARM_PC.GetRegNum());
}
- if (core_spill_mask_ == 0u) {
- // Nothing to unspill.
- } else if ((core_spill_mask_ & ~(0xffu | (1u << rs_rARM_PC.GetRegNum()))) == 0u) {
- // Unspilling only low regs and/or PC, use 16-bit POP.
- constexpr int pc_bit_shift = rs_rARM_PC.GetRegNum() - 8;
- NewLIR1(kThumbPop,
- (core_spill_mask_ & ~(1u << rs_rARM_PC.GetRegNum())) |
- ((core_spill_mask_ & (1u << rs_rARM_PC.GetRegNum())) >> pc_bit_shift));
- } else if (IsPowerOfTwo(core_spill_mask_)) {
- // kThumb2Pop cannot be used to unspill a single register.
- NewLIR1(kThumb2Pop1, CTZ(core_spill_mask_));
- } else {
- NewLIR1(kThumb2Pop, core_spill_mask_);
+ if (core_spill_mask_ != 0u) {
+ if ((core_spill_mask_ & ~(0xffu | (1u << rs_rARM_PC.GetRegNum()))) == 0u) {
+ // Unspilling only low regs and/or PC, use 16-bit POP.
+ constexpr int pc_bit_shift = rs_rARM_PC.GetRegNum() - 8;
+ NewLIR1(kThumbPop,
+ (core_spill_mask_ & ~(1u << rs_rARM_PC.GetRegNum())) |
+ ((core_spill_mask_ & (1u << rs_rARM_PC.GetRegNum())) >> pc_bit_shift));
+ } else if (IsPowerOfTwo(core_spill_mask_)) {
+ // kThumb2Pop cannot be used to unspill a single register.
+ NewLIR1(kThumb2Pop1, CTZ(core_spill_mask_));
+ } else {
+ NewLIR1(kThumb2Pop, core_spill_mask_);
+ }
+ // If we pop to PC, there is no further epilogue code.
+ if (!unspill_LR_to_PC) {
+ cfi_.AdjustCFAOffset(-num_core_spills_ * kArmPointerSize);
+ cfi_.RestoreMany(DwarfCoreReg(0), core_spill_mask_);
+ DCHECK_EQ(cfi_.GetCurrentCFAOffset(), 0); // empty stack.
+ }
}
- if ((core_spill_mask_ & (1 << rs_rARM_PC.GetRegNum())) == 0) {
+ if (!unspill_LR_to_PC) {
/* We didn't pop to rARM_PC, so must do a bv rARM_LR */
NewLIR1(kThumbBx, rs_rARM_LR.GetReg());
}
+ // The CFI should be restored for any code that follows the exit block.
+ cfi_.RestoreState();
+ cfi_.DefCFAOffset(frame_size_);
}
void ArmMir2Lir::GenSpecialExitSequence() {
@@ -555,11 +598,16 @@
NewLIR1(kThumbPush, (1u << rs_r0.GetRegNum()) | // ArtMethod*
(core_spill_mask_ & ~(1u << rs_rARM_LR.GetRegNum())) | // Spills other than LR.
(1u << 8)); // LR encoded for 16-bit push.
+ cfi_.AdjustCFAOffset(frame_size_);
+ // Do not generate CFI for scratch register r0.
+ cfi_.RelOffsetForMany(DwarfCoreReg(0), 4, core_spill_mask_, kArmPointerSize);
}
void ArmMir2Lir::GenSpecialExitForSuspend() {
// Pop the frame. (ArtMethod* no longer needed but restore it anyway.)
NewLIR1(kThumb2Pop, (1u << rs_r0.GetRegNum()) | core_spill_mask_); // 32-bit because of LR.
+ cfi_.AdjustCFAOffset(-frame_size_);
+ cfi_.RestoreMany(DwarfCoreReg(0), core_spill_mask_);
}
static bool ArmUseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) {
@@ -571,12 +619,12 @@
* Bit of a hack here - in the absence of a real scheduling pass,
* emit the next instruction in static & direct invoke sequences.
*/
-static int ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED,
- int state, const MethodReference& target_method,
- uint32_t unused_idx ATTRIBUTE_UNUSED,
- uintptr_t direct_code, uintptr_t direct_method,
- InvokeType type) {
- Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+int ArmMir2Lir::ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED,
+ int state, const MethodReference& target_method,
+ uint32_t unused_idx ATTRIBUTE_UNUSED,
+ uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type) {
+ ArmMir2Lir* cg = static_cast<ArmMir2Lir*>(cu->cg.get());
if (direct_code != 0 && direct_method != 0) {
switch (state) {
case 0: // Get the current Method* [sets kArg0]
@@ -597,17 +645,24 @@
return -1;
}
} else {
+ bool use_pc_rel = cg->CanUseOpPcRelDexCacheArrayLoad();
RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
switch (state) {
case 0: // Get the current Method* [sets kArg0]
// TUNING: we can save a reg copy if Method* has been promoted.
- cg->LoadCurrMethodDirect(arg0_ref);
- break;
+ if (!use_pc_rel) {
+ cg->LoadCurrMethodDirect(arg0_ref);
+ break;
+ }
+ ++state;
+ FALLTHROUGH_INTENDED;
case 1: // Get method->dex_cache_resolved_methods_
- cg->LoadRefDisp(arg0_ref,
- mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
- arg0_ref,
- kNotVolatile);
+ if (!use_pc_rel) {
+ cg->LoadRefDisp(arg0_ref,
+ mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
+ arg0_ref,
+ kNotVolatile);
+ }
// Set up direct code if known.
if (direct_code != 0) {
if (direct_code != static_cast<uintptr_t>(-1)) {
@@ -619,14 +674,23 @@
cg->LoadCodeAddress(target_method, type, kInvokeTgt);
}
}
- break;
+ if (!use_pc_rel || direct_code != 0) {
+ break;
+ }
+ ++state;
+ FALLTHROUGH_INTENDED;
case 2: // Grab target method*
CHECK_EQ(cu->dex_file, target_method.dex_file);
- cg->LoadRefDisp(arg0_ref,
- mirror::ObjectArray<mirror::Object>::OffsetOfElement(
- target_method.dex_method_index).Int32Value(),
- arg0_ref,
- kNotVolatile);
+ if (!use_pc_rel) {
+ cg->LoadRefDisp(arg0_ref,
+ mirror::ObjectArray<mirror::Object>::OffsetOfElement(
+ target_method.dex_method_index).Int32Value(),
+ arg0_ref,
+ kNotVolatile);
+ } else {
+ size_t offset = cg->dex_cache_arrays_layout_.MethodOffset(target_method.dex_method_index);
+ cg->OpPcRelDexCacheArrayLoad(cu->dex_file, offset, arg0_ref);
+ }
break;
case 3: // Grab the code from the method*
if (direct_code == 0) {
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index 4141bcf..83b27df 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -82,6 +82,9 @@
/// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage)
void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE;
+ bool CanUseOpPcRelDexCacheArrayLoad() const OVERRIDE;
+ void OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset, RegStorage r_dest) OVERRIDE;
+
// Required for target - register utilities.
RegStorage TargetReg(SpecialTargetRegister reg) OVERRIDE;
RegStorage TargetReg(SpecialTargetRegister reg, WideKind wide_kind) OVERRIDE {
@@ -257,6 +260,9 @@
*/
LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) OVERRIDE;
+ void CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs) OVERRIDE;
+ void DoPromotion() OVERRIDE;
+
/*
* @brief Handle ARM specific literals.
*/
@@ -300,6 +306,13 @@
ArenaVector<LIR*> call_method_insns_;
+ // Instructions needing patching with PC relative code addresses.
+ ArenaVector<LIR*> dex_cache_access_insns_;
+
+ // Register with a reference to the dex cache arrays at dex_cache_arrays_min_offset_,
+ // if promoted.
+ RegStorage dex_cache_arrays_base_reg_;
+
/**
* @brief Given float register pair, returns Solo64 float register.
* @param reg #RegStorage containing a float register pair (e.g. @c s2 and @c s3).
@@ -329,6 +342,14 @@
}
int GenDalvikArgsBulkCopy(CallInfo* info, int first, int count) OVERRIDE;
+
+ static int ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED,
+ int state, const MethodReference& target_method,
+ uint32_t unused_idx ATTRIBUTE_UNUSED,
+ uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type);
+
+ void OpPcRelDexCacheArrayAddr(const DexFile* dex_file, int offset, RegStorage r_dest);
};
} // namespace art
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 9193e1b..47669db 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -1087,6 +1087,36 @@
lir->target = target;
}
+bool ArmMir2Lir::CanUseOpPcRelDexCacheArrayLoad() const {
+ return dex_cache_arrays_layout_.Valid();
+}
+
+void ArmMir2Lir::OpPcRelDexCacheArrayAddr(const DexFile* dex_file, int offset, RegStorage r_dest) {
+ LIR* movw = NewLIR2(kThumb2MovImm16, r_dest.GetReg(), 0);
+ LIR* movt = NewLIR2(kThumb2MovImm16H, r_dest.GetReg(), 0);
+ ArmOpcode add_pc_opcode = (r_dest.GetRegNum() < 8) ? kThumbAddRRLH : kThumbAddRRHH;
+ LIR* add_pc = NewLIR2(add_pc_opcode, r_dest.GetReg(), rs_rARM_PC.GetReg());
+ add_pc->flags.fixup = kFixupLabel;
+ movw->operands[2] = WrapPointer(dex_file);
+ movw->operands[3] = offset;
+ movw->operands[4] = WrapPointer(add_pc);
+ movt->operands[2] = movw->operands[2];
+ movt->operands[3] = movw->operands[3];
+ movt->operands[4] = movw->operands[4];
+ dex_cache_access_insns_.push_back(movw);
+ dex_cache_access_insns_.push_back(movt);
+}
+
+void ArmMir2Lir::OpPcRelDexCacheArrayLoad(const DexFile* dex_file, int offset, RegStorage r_dest) {
+ if (dex_cache_arrays_base_reg_.Valid()) {
+ LoadRefDisp(dex_cache_arrays_base_reg_, offset - dex_cache_arrays_min_offset_,
+ r_dest, kNotVolatile);
+ } else {
+ OpPcRelDexCacheArrayAddr(dex_file, offset, r_dest);
+ LoadRefDisp(r_dest, 0, r_dest, kNotVolatile);
+ }
+}
+
LIR* ArmMir2Lir::OpVldm(RegStorage r_base, int count) {
return NewLIR3(kThumb2Vldms, r_base.GetReg(), rs_fr0.GetReg(), count);
}
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 9812d9f..5f27338 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -575,7 +575,9 @@
ArmMir2Lir::ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
: Mir2Lir(cu, mir_graph, arena),
- call_method_insns_(arena->Adapter()) {
+ call_method_insns_(arena->Adapter()),
+ dex_cache_access_insns_(arena->Adapter()),
+ dex_cache_arrays_base_reg_(RegStorage::InvalidReg()) {
call_method_insns_.reserve(100);
// Sanity check - make sure encoding map lines up.
for (int i = 0; i < kArmLast; i++) {
@@ -901,14 +903,28 @@
}
void ArmMir2Lir::InstallLiteralPools() {
+ patches_.reserve(call_method_insns_.size() + dex_cache_access_insns_.size());
+
// PC-relative calls to methods.
- patches_.reserve(call_method_insns_.size());
for (LIR* p : call_method_insns_) {
- DCHECK_EQ(p->opcode, kThumb2Bl);
- uint32_t target_method_idx = p->operands[1];
- const DexFile* target_dex_file = UnwrapPointer<DexFile>(p->operands[2]);
- patches_.push_back(LinkerPatch::RelativeCodePatch(p->offset,
- target_dex_file, target_method_idx));
+ DCHECK_EQ(p->opcode, kThumb2Bl);
+ uint32_t target_method_idx = p->operands[1];
+ const DexFile* target_dex_file = UnwrapPointer<DexFile>(p->operands[2]);
+ patches_.push_back(LinkerPatch::RelativeCodePatch(p->offset,
+ target_dex_file, target_method_idx));
+ }
+
+ // PC-relative dex cache array accesses.
+ for (LIR* p : dex_cache_access_insns_) {
+ DCHECK(p->opcode = kThumb2MovImm16 || p->opcode == kThumb2MovImm16H);
+ const LIR* add_pc = UnwrapPointer<LIR>(p->operands[4]);
+ DCHECK(add_pc->opcode == kThumbAddRRLH || add_pc->opcode == kThumbAddRRHH);
+ const DexFile* dex_file = UnwrapPointer<DexFile>(p->operands[2]);
+ uint32_t offset = p->operands[3];
+ DCHECK(!p->flags.is_nop);
+ DCHECK(!add_pc->flags.is_nop);
+ patches_.push_back(LinkerPatch::DexCacheArrayPatch(p->offset,
+ dex_file, add_pc->offset, offset));
}
// And do the normal processing.
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
index e4bd2a3..c3371cf 100644
--- a/compiler/dex/quick/arm/utility_arm.cc
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -19,6 +19,7 @@
#include "arch/arm/instruction_set_features_arm.h"
#include "arm_lir.h"
#include "base/logging.h"
+#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
#include "driver/compiler_driver.h"
@@ -1266,4 +1267,38 @@
return offset;
}
+void ArmMir2Lir::CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs) {
+ // Start with the default counts.
+ Mir2Lir::CountRefs(core_counts, fp_counts, num_regs);
+
+ if (pc_rel_temp_ != nullptr) {
+ // Now, if the dex cache array base temp is used only once outside any loops (weight = 1),
+ // avoid the promotion, otherwise boost the weight by factor 4 because the full PC-relative
+ // load sequence is 4 instructions long.
+ int p_map_idx = SRegToPMap(pc_rel_temp_->s_reg_low);
+ if (core_counts[p_map_idx].count == 1) {
+ core_counts[p_map_idx].count = 0;
+ } else {
+ core_counts[p_map_idx].count *= 4;
+ }
+ }
+}
+
+void ArmMir2Lir::DoPromotion() {
+ if (CanUseOpPcRelDexCacheArrayLoad()) {
+ pc_rel_temp_ = mir_graph_->GetNewCompilerTemp(kCompilerTempBackend, false);
+ }
+
+ Mir2Lir::DoPromotion();
+
+ if (pc_rel_temp_ != nullptr) {
+ // Now, if the dex cache array base temp is promoted, remember the register but
+ // always remove the temp's stack location to avoid unnecessarily bloating the stack.
+ dex_cache_arrays_base_reg_ = mir_graph_->reg_location_[pc_rel_temp_->s_reg_low].reg;
+ DCHECK(!dex_cache_arrays_base_reg_.Valid() || !dex_cache_arrays_base_reg_.IsFloat());
+ mir_graph_->RemoveLastCompilerTemp(kCompilerTempBackend, false, pc_rel_temp_);
+ pc_rel_temp_ = nullptr;
+ }
+}
+
} // namespace art
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 6b47bba..4abbd77 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -282,7 +282,13 @@
FreeTemp(reg_card_no);
}
+static dwarf::Reg DwarfCoreReg(int num) {
+ return dwarf::Reg::Arm64Core(num);
+}
+
void Arm64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
+ DCHECK_EQ(cfi_.GetCurrentCFAOffset(), 0); // empty stack.
+
/*
* On entry, x0 to x7 are live. Let the register allocation
* mechanism know so it doesn't try to use any of them when
@@ -345,6 +351,7 @@
if (spilled_already != frame_size_) {
OpRegImm(kOpSub, rs_sp, frame_size_without_spills);
+ cfi_.AdjustCFAOffset(frame_size_without_spills);
}
if (!skip_overflow_check) {
@@ -361,12 +368,14 @@
GenerateTargetLabel(kPseudoThrowTarget);
// Unwinds stack.
m2l_->OpRegImm(kOpAdd, rs_sp, sp_displace_);
+ m2l_->cfi().AdjustCFAOffset(-sp_displace_);
m2l_->ClobberCallerSave();
ThreadOffset<8> func_offset = QUICK_ENTRYPOINT_OFFSET(8, pThrowStackOverflow);
m2l_->LockTemp(rs_xIP0);
m2l_->LoadWordDisp(rs_xSELF, func_offset.Int32Value(), rs_xIP0);
m2l_->NewLIR1(kA64Br1x, rs_xIP0.GetReg());
m2l_->FreeTemp(rs_xIP0);
+ m2l_->cfi().AdjustCFAOffset(sp_displace_);
}
private:
@@ -393,6 +402,7 @@
}
void Arm64Mir2Lir::GenExitSequence() {
+ cfi_.RememberState();
/*
* In the exit path, r0/r1 are live - make sure they aren't
* allocated by the register utilities as temps.
@@ -403,6 +413,9 @@
// Finally return.
NewLIR0(kA64Ret);
+ // The CFI should be restored for any code that follows the exit block.
+ cfi_.RestoreState();
+ cfi_.DefCFAOffset(frame_size_);
}
void Arm64Mir2Lir::GenSpecialExitSequence() {
@@ -419,11 +432,16 @@
core_vmap_table_.clear();
fp_vmap_table_.clear();
NewLIR4(WIDE(kA64StpPre4rrXD), rs_x0.GetReg(), rs_xLR.GetReg(), rs_sp.GetReg(), -frame_size_ / 8);
+ cfi_.AdjustCFAOffset(frame_size_);
+ // Do not generate CFI for scratch register x0.
+ cfi_.RelOffset(DwarfCoreReg(rxLR), 8);
}
void Arm64Mir2Lir::GenSpecialExitForSuspend() {
// Pop the frame. (ArtMethod* no longer needed but restore it anyway.)
NewLIR4(WIDE(kA64LdpPost4rrXD), rs_x0.GetReg(), rs_xLR.GetReg(), rs_sp.GetReg(), frame_size_ / 8);
+ cfi_.AdjustCFAOffset(-frame_size_);
+ cfi_.Restore(DwarfCoreReg(rxLR));
}
static bool Arm64UseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) {
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index a9d9f3d..20f61f2 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -1458,6 +1458,14 @@
return reg_mask;
}
+static dwarf::Reg DwarfCoreReg(int num) {
+ return dwarf::Reg::Arm64Core(num);
+}
+
+static dwarf::Reg DwarfFpReg(int num) {
+ return dwarf::Reg::Arm64Fp(num);
+}
+
static void SpillCoreRegs(Arm64Mir2Lir* m2l, RegStorage base, int offset, uint32_t reg_mask) {
int reg1 = -1, reg2 = -1;
const int reg_log2_size = 3;
@@ -1466,9 +1474,12 @@
reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
if (UNLIKELY(reg2 < 0)) {
m2l->NewLIR3(WIDE(kA64Str3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
+ m2l->cfi().RelOffset(DwarfCoreReg(reg1), offset << reg_log2_size);
} else {
m2l->NewLIR4(WIDE(kA64Stp4rrXD), RegStorage::Solo64(reg2).GetReg(),
RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
+ m2l->cfi().RelOffset(DwarfCoreReg(reg2), offset << reg_log2_size);
+ m2l->cfi().RelOffset(DwarfCoreReg(reg1), (offset + 1) << reg_log2_size);
}
}
}
@@ -1483,9 +1494,12 @@
if (UNLIKELY(reg2 < 0)) {
m2l->NewLIR3(WIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
offset);
+ m2l->cfi().RelOffset(DwarfFpReg(reg1), offset << reg_log2_size);
} else {
m2l->NewLIR4(WIDE(kA64Stp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
+ m2l->cfi().RelOffset(DwarfFpReg(reg2), offset << reg_log2_size);
+ m2l->cfi().RelOffset(DwarfFpReg(reg1), (offset + 1) << reg_log2_size);
}
}
}
@@ -1493,6 +1507,7 @@
static int SpillRegsPreSub(Arm64Mir2Lir* m2l, uint32_t core_reg_mask, uint32_t fp_reg_mask,
int frame_size) {
m2l->OpRegRegImm(kOpSub, rs_sp, rs_sp, frame_size);
+ m2l->cfi().AdjustCFAOffset(frame_size);
int core_count = POPCOUNT(core_reg_mask);
@@ -1552,11 +1567,15 @@
RegStorage::FloatSolo64(reg1).GetReg(),
RegStorage::FloatSolo64(reg1).GetReg(),
base.GetReg(), -all_offset);
+ m2l->cfi().AdjustCFAOffset(all_offset * kArm64PointerSize);
+ m2l->cfi().RelOffset(DwarfFpReg(reg1), kArm64PointerSize);
} else {
m2l->NewLIR4(WIDE(kA64StpPre4ffXD),
RegStorage::FloatSolo64(reg1).GetReg(),
RegStorage::FloatSolo64(reg1).GetReg(),
base.GetReg(), -all_offset);
+ m2l->cfi().AdjustCFAOffset(all_offset * kArm64PointerSize);
+ m2l->cfi().RelOffset(DwarfFpReg(reg1), 0);
cur_offset = 0; // That core reg needs to go into the upper half.
}
} else {
@@ -1564,10 +1583,15 @@
fp_reg_mask = GenPairWise(fp_reg_mask, ®1, ®2);
m2l->NewLIR4(WIDE(kA64StpPre4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), -all_offset);
+ m2l->cfi().AdjustCFAOffset(all_offset * kArm64PointerSize);
+ m2l->cfi().RelOffset(DwarfFpReg(reg2), 0);
+ m2l->cfi().RelOffset(DwarfFpReg(reg1), kArm64PointerSize);
} else {
fp_reg_mask = ExtractReg(fp_reg_mask, ®1);
m2l->NewLIR4(WIDE(kA64StpPre4ffXD), rs_d0.GetReg(), RegStorage::FloatSolo64(reg1).GetReg(),
base.GetReg(), -all_offset);
+ m2l->cfi().AdjustCFAOffset(all_offset * kArm64PointerSize);
+ m2l->cfi().RelOffset(DwarfFpReg(reg1), kArm64PointerSize);
}
}
} else {
@@ -1580,12 +1604,19 @@
core_reg_mask = ExtractReg(core_reg_mask, ®1);
m2l->NewLIR4(WIDE(kA64StpPre4rrXD), rs_xzr.GetReg(),
RegStorage::Solo64(reg1).GetReg(), base.GetReg(), -all_offset);
+ m2l->cfi().AdjustCFAOffset(all_offset * kArm64PointerSize);
+ m2l->cfi().RelOffset(DwarfCoreReg(reg1), kArm64PointerSize);
} else {
core_reg_mask = GenPairWise(core_reg_mask, ®1, ®2);
m2l->NewLIR4(WIDE(kA64StpPre4rrXD), RegStorage::Solo64(reg2).GetReg(),
RegStorage::Solo64(reg1).GetReg(), base.GetReg(), -all_offset);
+ m2l->cfi().AdjustCFAOffset(all_offset * kArm64PointerSize);
+ m2l->cfi().RelOffset(DwarfCoreReg(reg2), 0);
+ m2l->cfi().RelOffset(DwarfCoreReg(reg1), kArm64PointerSize);
}
}
+ DCHECK_EQ(m2l->cfi().GetCurrentCFAOffset(),
+ static_cast<int>(all_offset * kArm64PointerSize));
if (fp_count != 0) {
for (; fp_reg_mask != 0;) {
@@ -1594,10 +1625,13 @@
if (UNLIKELY(reg2 < 0)) {
m2l->NewLIR3(WIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
cur_offset);
+ m2l->cfi().RelOffset(DwarfFpReg(reg1), cur_offset * kArm64PointerSize);
// Do not increment offset here, as the second half will be filled by a core reg.
} else {
m2l->NewLIR4(WIDE(kA64Stp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), cur_offset);
+ m2l->cfi().RelOffset(DwarfFpReg(reg2), cur_offset * kArm64PointerSize);
+ m2l->cfi().RelOffset(DwarfFpReg(reg1), (cur_offset + 1) * kArm64PointerSize);
cur_offset += 2;
}
}
@@ -1610,6 +1644,7 @@
core_reg_mask = ExtractReg(core_reg_mask, ®1);
m2l->NewLIR3(WIDE(kA64Str3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(),
cur_offset + 1);
+ m2l->cfi().RelOffset(DwarfCoreReg(reg1), (cur_offset + 1) * kArm64PointerSize);
cur_offset += 2; // Half-slot filled now.
}
}
@@ -1620,6 +1655,8 @@
core_reg_mask = GenPairWise(core_reg_mask, ®1, ®2);
m2l->NewLIR4(WIDE(kA64Stp4rrXD), RegStorage::Solo64(reg2).GetReg(),
RegStorage::Solo64(reg1).GetReg(), base.GetReg(), cur_offset);
+ m2l->cfi().RelOffset(DwarfCoreReg(reg2), cur_offset * kArm64PointerSize);
+ m2l->cfi().RelOffset(DwarfCoreReg(reg1), (cur_offset + 1) * kArm64PointerSize);
}
DCHECK_EQ(cur_offset, all_offset);
@@ -1650,10 +1687,13 @@
reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
if (UNLIKELY(reg2 < 0)) {
m2l->NewLIR3(WIDE(kA64Ldr3rXD), RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
+ m2l->cfi().Restore(DwarfCoreReg(reg1));
} else {
DCHECK_LE(offset, 63);
m2l->NewLIR4(WIDE(kA64Ldp4rrXD), RegStorage::Solo64(reg2).GetReg(),
RegStorage::Solo64(reg1).GetReg(), base.GetReg(), offset);
+ m2l->cfi().Restore(DwarfCoreReg(reg2));
+ m2l->cfi().Restore(DwarfCoreReg(reg1));
}
}
}
@@ -1667,9 +1707,12 @@
if (UNLIKELY(reg2 < 0)) {
m2l->NewLIR3(WIDE(kA64Ldr3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
offset);
+ m2l->cfi().Restore(DwarfFpReg(reg1));
} else {
m2l->NewLIR4(WIDE(kA64Ldp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(), offset);
+ m2l->cfi().Restore(DwarfFpReg(reg2));
+ m2l->cfi().Restore(DwarfFpReg(reg1));
}
}
}
@@ -1711,6 +1754,7 @@
early_drop = RoundDown(early_drop, 16);
OpRegImm64(kOpAdd, rs_sp, early_drop);
+ cfi_.AdjustCFAOffset(-early_drop);
}
// Unspill.
@@ -1724,7 +1768,9 @@
}
// Drop the (rest of) the frame.
- OpRegImm64(kOpAdd, rs_sp, frame_size - early_drop);
+ int adjust = frame_size - early_drop;
+ OpRegImm64(kOpAdd, rs_sp, adjust);
+ cfi_.AdjustCFAOffset(-adjust);
}
bool Arm64Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc
index f48290d..e9ad8ba 100644
--- a/compiler/dex/quick/arm64/utility_arm64.cc
+++ b/compiler/dex/quick/arm64/utility_arm64.cc
@@ -589,13 +589,11 @@
DCHECK_EQ(shift, 0);
// Binary, but rm is encoded twice.
return NewLIR2(kA64Rev2rr | wide, r_dest_src1.GetReg(), r_src2.GetReg());
- break;
case kOpRevsh:
// Binary, but rm is encoded twice.
NewLIR2(kA64Rev162rr | wide, r_dest_src1.GetReg(), r_src2.GetReg());
// "sxth r1, r2" is "sbfm r1, r2, #0, #15"
return NewLIR4(kA64Sbfm4rrdd | wide, r_dest_src1.GetReg(), r_dest_src1.GetReg(), 0, 15);
- break;
case kOp2Byte:
DCHECK_EQ(shift, ENCODE_NO_SHIFT);
// "sbfx r1, r2, #imm1, #imm2" is "sbfm r1, r2, #imm1, #(imm1 + imm2 - 1)".
@@ -645,10 +643,9 @@
// Note: intentional fallthrough
case kOpSub:
return OpRegRegRegExtend(op, r_dest_src1, r_dest_src1, r_src2, ext, amount);
- break;
default:
LOG(FATAL) << "Bad Opcode: " << opcode;
- break;
+ UNREACHABLE();
}
DCHECK(!IsPseudoLirOp(opcode));
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 483a5d0..ff5f735 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -1070,6 +1070,11 @@
mask_cache_(arena),
safepoints_(arena->Adapter()),
dex_cache_arrays_layout_(cu->compiler_driver->GetDexCacheArraysLayout(cu->dex_file)),
+ pc_rel_temp_(nullptr),
+ dex_cache_arrays_min_offset_(std::numeric_limits<uint32_t>::max()),
+ cfi_(&last_lir_insn_,
+ cu->compiler_driver->GetCompilerOptions().GetGenerateGDBInformation(),
+ arena),
in_to_reg_storage_mapping_(arena) {
switch_tables_.reserve(4);
fill_array_data_.reserve(4);
@@ -1154,14 +1159,6 @@
return lhs.LiteralOffset() < rhs.LiteralOffset();
});
- std::unique_ptr<std::vector<uint8_t>> cfi_info(
- cu_->compiler_driver->GetCompilerOptions().GetGenerateGDBInformation() ?
- ReturnFrameDescriptionEntry() :
- nullptr);
- ArrayRef<const uint8_t> cfi_ref;
- if (cfi_info.get() != nullptr) {
- cfi_ref = ArrayRef<const uint8_t>(*cfi_info);
- }
return CompiledMethod::SwapAllocCompiledMethod(
cu_->compiler_driver, cu_->instruction_set,
ArrayRef<const uint8_t>(code_buffer_),
@@ -1170,8 +1167,8 @@
ArrayRef<const uint8_t>(encoded_mapping_table_),
ArrayRef<const uint8_t>(vmap_encoder.GetData()),
ArrayRef<const uint8_t>(native_gc_map_),
- cfi_ref,
- ArrayRef<LinkerPatch>(patches_));
+ ArrayRef<const uint8_t>(*cfi_.Patch(code_buffer_.size())),
+ ArrayRef<const LinkerPatch>(patches_));
}
size_t Mir2Lir::GetMaxPossibleCompilerTemps() const {
@@ -1332,11 +1329,6 @@
UNREACHABLE();
}
-std::vector<uint8_t>* Mir2Lir::ReturnFrameDescriptionEntry() {
- // Default case is to do nothing.
- return nullptr;
-}
-
RegLocation Mir2Lir::NarrowRegLoc(RegLocation loc) {
if (loc.location == kLocPhysReg) {
DCHECK(!loc.reg.Is32Bit());
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 8e3f4ef..4ac6c0c 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -413,6 +413,17 @@
return success && AddInlineMethod(verifier->GetMethodReference().dex_method_index, method);
}
+InlineMethodFlags DexFileMethodInliner::IsIntrinsicOrSpecial(uint32_t method_index) {
+ ReaderMutexLock mu(Thread::Current(), lock_);
+ auto it = inline_methods_.find(method_index);
+ if (it != inline_methods_.end()) {
+ DCHECK_NE(it->second.flags & (kInlineIntrinsic | kInlineSpecial), 0);
+ return it->second.flags;
+ } else {
+ return kNoInlineMethodFlags;
+ }
+}
+
bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index, InlineMethod* intrinsic) {
ReaderMutexLock mu(Thread::Current(), lock_);
auto it = inline_methods_.find(method_index);
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index cb521da..d1e5621 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -65,6 +65,11 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_);
/**
+ * Check whether a particular method index corresponds to an intrinsic or special function.
+ */
+ InlineMethodFlags IsIntrinsicOrSpecial(uint32_t method_index) LOCKS_EXCLUDED(lock_);
+
+ /**
* Check whether a particular method index corresponds to an intrinsic function.
*/
bool IsIntrinsic(uint32_t method_index, InlineMethod* intrinsic) LOCKS_EXCLUDED(lock_);
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 1813e09..b132c4c 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -94,6 +94,97 @@
r_method, r_result));
}
+RegStorage Mir2Lir::GenGetOtherTypeForSgetSput(const MirSFieldLoweringInfo& field_info,
+ int opt_flags) {
+ DCHECK_NE(field_info.StorageIndex(), DexFile::kDexNoIndex);
+ // May do runtime call so everything to home locations.
+ FlushAllRegs();
+ RegStorage r_base = TargetReg(kArg0, kRef);
+ LockTemp(r_base);
+ RegStorage r_method = RegStorage::InvalidReg(); // Loaded lazily, maybe in the slow-path.
+ if (CanUseOpPcRelDexCacheArrayLoad()) {
+ uint32_t offset = dex_cache_arrays_layout_.TypeOffset(field_info.StorageIndex());
+ OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, r_base);
+ } else {
+ // Using fixed register to sync with possible call to runtime support.
+ r_method = LoadCurrMethodWithHint(TargetReg(kArg1, kRef));
+ LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base,
+ kNotVolatile);
+ int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value();
+ LoadRefDisp(r_base, offset_of_field, r_base, kNotVolatile);
+ }
+ // r_base now points at static storage (Class*) or nullptr if the type is not yet resolved.
+ LIR* unresolved_branch = nullptr;
+ if (!field_info.IsClassInDexCache() && (opt_flags & MIR_CLASS_IS_IN_DEX_CACHE) == 0) {
+ // Check if r_base is nullptr.
+ unresolved_branch = OpCmpImmBranch(kCondEq, r_base, 0, nullptr);
+ }
+ LIR* uninit_branch = nullptr;
+ if (!field_info.IsClassInitialized() && (opt_flags & MIR_CLASS_IS_INITIALIZED) == 0) {
+ // Check if r_base is not yet initialized class.
+ RegStorage r_tmp = TargetReg(kArg2, kNotWide);
+ LockTemp(r_tmp);
+ uninit_branch = OpCmpMemImmBranch(kCondLt, r_tmp, r_base,
+ mirror::Class::StatusOffset().Int32Value(),
+ mirror::Class::kStatusInitialized, nullptr, nullptr);
+ FreeTemp(r_tmp);
+ }
+ if (unresolved_branch != nullptr || uninit_branch != nullptr) {
+ //
+ // Slow path to ensure a class is initialized for sget/sput.
+ //
+ class StaticFieldSlowPath : public Mir2Lir::LIRSlowPath {
+ public:
+ // There are up to two branches to the static field slow path, the "unresolved" when the type
+ // entry in the dex cache is nullptr, and the "uninit" when the class is not yet initialized.
+ // At least one will be non-nullptr here, otherwise we wouldn't generate the slow path.
+ StaticFieldSlowPath(Mir2Lir* m2l, LIR* unresolved, LIR* uninit, LIR* cont, int storage_index,
+ RegStorage r_base_in, RegStorage r_method_in)
+ : LIRSlowPath(m2l, unresolved != nullptr ? unresolved : uninit, cont),
+ second_branch_(unresolved != nullptr ? uninit : nullptr),
+ storage_index_(storage_index), r_base_(r_base_in), r_method_(r_method_in) {
+ }
+
+ void Compile() {
+ LIR* target = GenerateTargetLabel();
+ if (second_branch_ != nullptr) {
+ second_branch_->target = target;
+ }
+ if (r_method_.Valid()) {
+ // ArtMethod* was loaded in normal path - use it.
+ m2l_->CallRuntimeHelperImmReg(kQuickInitializeStaticStorage, storage_index_, r_method_,
+ true);
+ } else {
+ // ArtMethod* wasn't loaded in normal path - use a helper that loads it.
+ m2l_->CallRuntimeHelperImmMethod(kQuickInitializeStaticStorage, storage_index_, true);
+ }
+ // Copy helper's result into r_base, a no-op on all but MIPS.
+ m2l_->OpRegCopy(r_base_, m2l_->TargetReg(kRet0, kRef));
+
+ m2l_->OpUnconditionalBranch(cont_);
+ }
+
+ private:
+ // Second branch to the slow path, or nullptr if there's only one branch.
+ LIR* const second_branch_;
+
+ const int storage_index_;
+ const RegStorage r_base_;
+ RegStorage r_method_;
+ };
+
+ // The slow path is invoked if the r_base is nullptr or the class pointed
+ // to by it is not initialized.
+ LIR* cont = NewLIR0(kPseudoTargetLabel);
+ AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont,
+ field_info.StorageIndex(), r_base, r_method));
+ }
+ if (IsTemp(r_method)) {
+ FreeTemp(r_method);
+ }
+ return r_base;
+}
+
/*
* Generate a kPseudoBarrier marker to indicate the boundary of special
* blocks.
@@ -609,41 +700,6 @@
CallRuntimeHelperImmRegLocation(kQuickHandleFillArrayData, table_offset_from_start, rl_src, true);
}
-//
-// Slow path to ensure a class is initialized for sget/sput.
-//
-class StaticFieldSlowPath : public Mir2Lir::LIRSlowPath {
- public:
- // There are up to two branches to the static field slow path, the "unresolved" when the type
- // entry in the dex cache is null, and the "uninit" when the class is not yet initialized.
- // At least one will be non-null here, otherwise we wouldn't generate the slow path.
- StaticFieldSlowPath(Mir2Lir* m2l, LIR* unresolved, LIR* uninit, LIR* cont, int storage_index,
- RegStorage r_base)
- : LIRSlowPath(m2l, unresolved != nullptr ? unresolved : uninit, cont),
- second_branch_(unresolved != nullptr ? uninit : nullptr),
- storage_index_(storage_index), r_base_(r_base) {
- }
-
- void Compile() {
- LIR* target = GenerateTargetLabel();
- if (second_branch_ != nullptr) {
- second_branch_->target = target;
- }
- m2l_->CallRuntimeHelperImm(kQuickInitializeStaticStorage, storage_index_, true);
- // Copy helper's result into r_base, a no-op on all but MIPS.
- m2l_->OpRegCopy(r_base_, m2l_->TargetReg(kRet0, kRef));
-
- m2l_->OpUnconditionalBranch(cont_);
- }
-
- private:
- // Second branch to the slow path, or null if there's only one branch.
- LIR* const second_branch_;
-
- const int storage_index_;
- const RegStorage r_base_;
-};
-
void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, OpSize size) {
const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir);
DCHECK_EQ(SPutMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType());
@@ -653,65 +709,23 @@
RegStorage r_base;
if (field_info.IsReferrersClass()) {
// Fast path, static storage base is this method's class
- RegLocation rl_method = LoadCurrMethod();
r_base = AllocTempRef();
- LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base,
+ RegStorage r_method = LoadCurrMethodWithHint(r_base);
+ LoadRefDisp(r_method, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base,
kNotVolatile);
- if (IsTemp(rl_method.reg)) {
- FreeTemp(rl_method.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_NE(field_info.StorageIndex(), DexFile::kDexNoIndex);
- // May do runtime call so everything to home locations.
- FlushAllRegs();
- // Using fixed register to sync with possible call to runtime support.
- RegStorage r_method = TargetReg(kArg1, kRef);
- LockTemp(r_method);
- LoadCurrMethodDirect(r_method);
- r_base = TargetReg(kArg0, kRef);
- LockTemp(r_base);
- LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base,
- kNotVolatile);
- int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value();
- LoadRefDisp(r_base, offset_of_field, r_base, kNotVolatile);
- // r_base now points at static storage (Class*) or NULL if the type is not yet resolved.
- LIR* unresolved_branch = nullptr;
- if (!field_info.IsClassInDexCache() &&
- (mir->optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) == 0) {
- // Check if r_base is NULL.
- unresolved_branch = OpCmpImmBranch(kCondEq, r_base, 0, NULL);
- }
- LIR* uninit_branch = nullptr;
+ r_base = GenGetOtherTypeForSgetSput(field_info, mir->optimization_flags);
if (!field_info.IsClassInitialized() &&
(mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0) {
- // Check if r_base is not yet initialized class.
- RegStorage r_tmp = TargetReg(kArg2, kNotWide);
- LockTemp(r_tmp);
- uninit_branch = OpCmpMemImmBranch(kCondLt, r_tmp, r_base,
- mirror::Class::StatusOffset().Int32Value(),
- mirror::Class::kStatusInitialized, nullptr, nullptr);
- FreeTemp(r_tmp);
+ // Ensure load of status and store of value don't re-order.
+ // TODO: Presumably the actual value store is control-dependent on the status load,
+ // and will thus not be reordered in any case, since stores are never speculated.
+ // Does later code "know" that the class is now initialized? If so, we still
+ // need the barrier to guard later static loads.
+ GenMemBarrier(kLoadAny);
}
- if (unresolved_branch != nullptr || uninit_branch != nullptr) {
- // The slow path is invoked if the r_base is NULL or the class pointed
- // to by it is not initialized.
- LIR* cont = NewLIR0(kPseudoTargetLabel);
- AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont,
- field_info.StorageIndex(), r_base));
-
- if (uninit_branch != nullptr) {
- // Ensure load of status and store of value don't re-order.
- // TODO: Presumably the actual value store is control-dependent on the status load,
- // and will thus not be reordered in any case, since stores are never speculated.
- // Does later code "know" that the class is now initialized? If so, we still
- // need the barrier to guard later static loads.
- GenMemBarrier(kLoadAny);
- }
- }
- FreeTemp(r_method);
}
// rBase now holds static storage base
RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
@@ -773,57 +787,19 @@
RegStorage r_base;
if (field_info.IsReferrersClass()) {
// Fast path, static storage base is this method's class
- RegLocation rl_method = LoadCurrMethod();
r_base = AllocTempRef();
- LoadRefDisp(rl_method.reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base,
+ RegStorage r_method = LoadCurrMethodWithHint(r_base);
+ LoadRefDisp(r_method, mirror::ArtMethod::DeclaringClassOffset().Int32Value(), r_base,
kNotVolatile);
} else {
// Medium path, static storage base in a different class which requires checks that the other
// class is initialized
- DCHECK_NE(field_info.StorageIndex(), DexFile::kDexNoIndex);
- // May do runtime call so everything to home locations.
- FlushAllRegs();
- // Using fixed register to sync with possible call to runtime support.
- RegStorage r_method = TargetReg(kArg1, kRef);
- LockTemp(r_method);
- LoadCurrMethodDirect(r_method);
- r_base = TargetReg(kArg0, kRef);
- LockTemp(r_base);
- LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base,
- kNotVolatile);
- int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value();
- LoadRefDisp(r_base, offset_of_field, r_base, kNotVolatile);
- // r_base now points at static storage (Class*) or NULL if the type is not yet resolved.
- LIR* unresolved_branch = nullptr;
- if (!field_info.IsClassInDexCache() &&
- (mir->optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) == 0) {
- // Check if r_base is NULL.
- unresolved_branch = OpCmpImmBranch(kCondEq, r_base, 0, NULL);
- }
- LIR* uninit_branch = nullptr;
+ r_base = GenGetOtherTypeForSgetSput(field_info, mir->optimization_flags);
if (!field_info.IsClassInitialized() &&
(mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0) {
- // Check if r_base is not yet initialized class.
- RegStorage r_tmp = TargetReg(kArg2, kNotWide);
- LockTemp(r_tmp);
- uninit_branch = OpCmpMemImmBranch(kCondLt, r_tmp, r_base,
- mirror::Class::StatusOffset().Int32Value(),
- mirror::Class::kStatusInitialized, nullptr, nullptr);
- FreeTemp(r_tmp);
+ // Ensure load of status and load of value don't re-order.
+ GenMemBarrier(kLoadAny);
}
- if (unresolved_branch != nullptr || uninit_branch != nullptr) {
- // The slow path is invoked if the r_base is NULL or the class pointed
- // to by it is not initialized.
- LIR* cont = NewLIR0(kPseudoTargetLabel);
- AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont,
- field_info.StorageIndex(), r_base));
-
- if (uninit_branch != nullptr) {
- // Ensure load of status and load of value don't re-order.
- GenMemBarrier(kLoadAny);
- }
- }
- FreeTemp(r_method);
}
// r_base now holds static storage base
RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index e747239..db7095d 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -1435,10 +1435,12 @@
void Mir2Lir::GenInvoke(CallInfo* info) {
DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
- const DexFile* dex_file = info->method_ref.dex_file;
- if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(dex_file)
- ->GenIntrinsic(this, info)) {
- return;
+ if (mir_graph_->GetMethodLoweringInfo(info->mir).IsIntrinsic()) {
+ const DexFile* dex_file = info->method_ref.dex_file;
+ auto* inliner = cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(dex_file);
+ if (inliner->GenIntrinsic(this, info)) {
+ return;
+ }
}
GenInvokeNoInline(info);
}
diff --git a/compiler/dex/quick/lazy_debug_frame_opcode_writer.cc b/compiler/dex/quick/lazy_debug_frame_opcode_writer.cc
new file mode 100644
index 0000000..03cf4be
--- /dev/null
+++ b/compiler/dex/quick/lazy_debug_frame_opcode_writer.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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 "lazy_debug_frame_opcode_writer.h"
+#include "mir_to_lir.h"
+
+namespace art {
+namespace dwarf {
+
+const ArenaVector<uint8_t>* LazyDebugFrameOpCodeWriter::Patch(size_t code_size) {
+ if (!enable_writes_) {
+ DCHECK(this->data()->empty());
+ return this->data();
+ }
+ if (!patched_) {
+ patched_ = true;
+ // Move our data buffer to temporary variable.
+ ArenaVector<uint8_t> old_opcodes(this->opcodes_.get_allocator());
+ old_opcodes.swap(this->opcodes_);
+ // Refill our data buffer with patched opcodes.
+ this->opcodes_.reserve(old_opcodes.size() + advances_.size() + 4);
+ size_t pos = 0;
+ for (auto advance : advances_) {
+ DCHECK_GE(advance.pos, pos);
+ // Copy old data up to the point when advance was issued.
+ this->opcodes_.insert(this->opcodes_.end(),
+ old_opcodes.begin() + pos,
+ old_opcodes.begin() + advance.pos);
+ pos = advance.pos;
+ // This may be null if there is no slow-path code after return.
+ LIR* next_lir = NEXT_LIR(advance.last_lir_insn);
+ // Insert the advance command with its final offset.
+ Base::AdvancePC(next_lir != nullptr ? next_lir->offset : code_size);
+ }
+ // Copy the final segment.
+ this->opcodes_.insert(this->opcodes_.end(),
+ old_opcodes.begin() + pos,
+ old_opcodes.end());
+ Base::AdvancePC(code_size);
+ }
+ return this->data();
+}
+
+} // namespace dwarf
+} // namespace art
diff --git a/compiler/dex/quick/lazy_debug_frame_opcode_writer.h b/compiler/dex/quick/lazy_debug_frame_opcode_writer.h
new file mode 100644
index 0000000..d71a87d
--- /dev/null
+++ b/compiler/dex/quick/lazy_debug_frame_opcode_writer.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 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_COMPILER_DEX_QUICK_LAZY_DEBUG_FRAME_OPCODE_WRITER_H_
+#define ART_COMPILER_DEX_QUICK_LAZY_DEBUG_FRAME_OPCODE_WRITER_H_
+
+#include "base/arena_allocator.h"
+#include "base/arena_containers.h"
+#include "dwarf/debug_frame_opcode_writer.h"
+
+namespace art {
+struct LIR;
+namespace dwarf {
+
+// When we are generating the CFI code, we do not know the instuction offsets,
+// this class stores the LIR references and patches the instruction stream later.
+class LazyDebugFrameOpCodeWriter FINAL
+ : private DebugFrameOpCodeWriter<ArenaAllocatorAdapter<uint8_t>> {
+ typedef DebugFrameOpCodeWriter<ArenaAllocatorAdapter<uint8_t>> Base;
+ public:
+ // This method is implicitely called the by opcode writers.
+ virtual void ImplicitlyAdvancePC() OVERRIDE {
+ DCHECK_EQ(patched_, false);
+ DCHECK_EQ(this->current_pc_, 0);
+ advances_.push_back({this->data()->size(), *last_lir_insn_});
+ }
+
+ // The register was unspilled.
+ void Restore(Reg reg) {
+ if (enable_writes_) {
+ Base::Restore(reg);
+ }
+ }
+
+ // Custom alias - unspill many registers based on bitmask.
+ void RestoreMany(Reg reg_base, uint32_t reg_mask) {
+ if (enable_writes_) {
+ Base::RestoreMany(reg_base, reg_mask);
+ }
+ }
+
+ // Remember the state of register spills.
+ void RememberState() {
+ if (enable_writes_) {
+ Base::RememberState();
+ }
+ }
+
+ // Restore the state of register spills.
+ void RestoreState() {
+ if (enable_writes_) {
+ Base::RestoreState();
+ }
+ }
+
+ // Set the frame pointer (CFA) to (stack_pointer + offset).
+ void DefCFAOffset(int offset) {
+ if (enable_writes_) {
+ Base::DefCFAOffset(offset);
+ }
+ this->current_cfa_offset_ = offset;
+ }
+
+ // The stack size was increased by given delta.
+ void AdjustCFAOffset(int delta) {
+ DefCFAOffset(this->current_cfa_offset_ + delta);
+ }
+
+ // The register was spilled to (stack_pointer + offset).
+ void RelOffset(Reg reg, int offset) {
+ if (enable_writes_) {
+ Base::RelOffset(reg, offset);
+ }
+ }
+
+ // Custom alias - spill many registers based on bitmask.
+ void RelOffsetForMany(Reg reg_base, int offset, uint32_t reg_mask, int reg_size) {
+ if (enable_writes_) {
+ Base::RelOffsetForMany(reg_base, offset, reg_mask, reg_size);
+ }
+ }
+
+ using Base::GetCurrentCFAOffset;
+ using Base::SetCurrentCFAOffset;
+ using Base::GetCurrentPC;
+
+ const ArenaVector<uint8_t>* Patch(size_t code_size);
+
+ explicit LazyDebugFrameOpCodeWriter(LIR** last_lir_insn, bool enable_writes,
+ ArenaAllocator* allocator)
+ : Base(allocator->Adapter()),
+ last_lir_insn_(last_lir_insn),
+ enable_writes_(enable_writes),
+ advances_(allocator->Adapter()),
+ patched_(false) {
+ }
+
+ private:
+ typedef struct {
+ size_t pos;
+ LIR* last_lir_insn;
+ } Advance;
+
+ LIR** last_lir_insn_;
+ bool enable_writes_;
+ ArenaVector<Advance> advances_;
+ bool patched_;
+
+ DISALLOW_COPY_AND_ASSIGN(LazyDebugFrameOpCodeWriter);
+};
+
+} // namespace dwarf
+} // namespace art
+
+#endif // ART_COMPILER_DEX_QUICK_LAZY_DEBUG_FRAME_OPCODE_WRITER_H_
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index c932df6..7d4f20e 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -238,7 +238,12 @@
FreeTemp(reg_card_no);
}
+static dwarf::Reg DwarfCoreReg(int num) {
+ return dwarf::Reg::MipsCore(num);
+}
+
void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
+ DCHECK_EQ(cfi_.GetCurrentCFAOffset(), 0);
int spill_count = num_core_spills_ + num_fp_spills_;
/*
* On entry, A0, A1, A2 & A3 are live. On Mips64, A4, A5, A6 & A7 are also live.
@@ -304,10 +309,12 @@
// RA is offset 0 since we push in reverse order.
m2l_->LoadWordDisp(m2l_->TargetPtrReg(kSp), 0, m2l_->TargetPtrReg(kLr));
m2l_->OpRegImm(kOpAdd, m2l_->TargetPtrReg(kSp), sp_displace_);
+ m2l_->cfi().AdjustCFAOffset(-sp_displace_);
m2l_->ClobberCallerSave();
RegStorage r_tgt = m2l_->CallHelperSetup(kQuickThrowStackOverflow); // Doesn't clobber LR.
m2l_->CallHelper(r_tgt, kQuickThrowStackOverflow, false /* MarkSafepointPC */,
false /* UseLink */);
+ m2l_->cfi().AdjustCFAOffset(sp_displace_);
}
private:
@@ -318,8 +325,10 @@
AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, spill_count * ptr_size));
// TODO: avoid copy for small frame sizes.
OpRegCopy(rs_sp, new_sp); // Establish stack.
+ cfi_.AdjustCFAOffset(frame_sub);
} else {
OpRegImm(kOpSub, rs_sp, frame_sub);
+ cfi_.AdjustCFAOffset(frame_sub);
}
FlushIns(ArgLocs, rl_method);
@@ -337,6 +346,7 @@
}
void MipsMir2Lir::GenExitSequence() {
+ cfi_.RememberState();
/*
* In the exit path, rMIPS_RET0/rMIPS_RET1 are live - make sure they aren't
* allocated by the register utilities as temps.
@@ -346,6 +356,9 @@
UnSpillCoreRegs();
OpReg(kOpBx, TargetPtrReg(kLr));
+ // The CFI should be restored for any code that follows the exit block.
+ cfi_.RestoreState();
+ cfi_.DefCFAOffset(frame_size_);
}
void MipsMir2Lir::GenSpecialExitSequence() {
@@ -364,15 +377,20 @@
fp_vmap_table_.clear();
const RegStorage rs_sp = TargetPtrReg(kSp);
OpRegImm(kOpSub, rs_sp, frame_size_);
+ cfi_.AdjustCFAOffset(frame_size_);
StoreWordDisp(rs_sp, frame_size_ - (cu_->target64 ? 8 : 4), TargetPtrReg(kLr));
+ cfi_.RelOffset(DwarfCoreReg(rRA), frame_size_ - (cu_->target64 ? 8 : 4));
StoreWordDisp(rs_sp, 0, TargetPtrReg(kArg0));
+ // Do not generate CFI for scratch register A0.
}
void MipsMir2Lir::GenSpecialExitForSuspend() {
// Pop the frame. Don't pop ArtMethod*, it's no longer needed.
const RegStorage rs_sp = TargetPtrReg(kSp);
LoadWordDisp(rs_sp, frame_size_ - (cu_->target64 ? 8 : 4), TargetPtrReg(kLr));
+ cfi_.Restore(DwarfCoreReg(rRA));
OpRegImm(kOpAdd, rs_sp, frame_size_);
+ cfi_.AdjustCFAOffset(-frame_size_);
}
/*
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index a94fad7..4c0bd83 100644
--- a/compiler/dex/quick/mips/target_mips.cc
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -830,6 +830,10 @@
return OpReg(kOpBlx, r_tgt);
}
+static dwarf::Reg DwarfCoreReg(int num) {
+ return dwarf::Reg::MipsCore(num);
+}
+
void MipsMir2Lir::SpillCoreRegs() {
if (num_core_spills_ == 0) {
return;
@@ -839,11 +843,13 @@
int offset = num_core_spills_ * ptr_size;
const RegStorage rs_sp = TargetPtrReg(kSp);
OpRegImm(kOpSub, rs_sp, offset);
+ cfi_.AdjustCFAOffset(offset);
for (int reg = 0; mask; mask >>= 1, reg++) {
if (mask & 0x1) {
offset -= ptr_size;
StoreWordDisp(rs_sp, offset,
cu_->target64 ? RegStorage::Solo64(reg) : RegStorage::Solo32(reg));
+ cfi_.RelOffset(DwarfCoreReg(reg), offset);
}
}
}
@@ -861,9 +867,11 @@
offset -= ptr_size;
LoadWordDisp(rs_sp, offset,
cu_->target64 ? RegStorage::Solo64(reg) : RegStorage::Solo32(reg));
+ cfi_.Restore(DwarfCoreReg(reg));
}
}
OpRegImm(kOpAdd, rs_sp, frame_size_);
+ cfi_.AdjustCFAOffset(-frame_size_);
}
bool MipsMir2Lir::IsUnconditionalBranch(LIR* lir) {
diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc
index bf0e0fc..8ab5422 100644
--- a/compiler/dex/quick/mips/utility_mips.cc
+++ b/compiler/dex/quick/mips/utility_mips.cc
@@ -283,9 +283,9 @@
break;
case kOpBx:
return NewLIR2(kMipsJalr, rZERO, r_dest_src.GetReg());
- break;
default:
LOG(FATAL) << "Bad case in OpReg";
+ UNREACHABLE();
}
return NewLIR2(opcode, cu_->target64 ? rRAd : rRA, r_dest_src.GetReg());
}
@@ -295,8 +295,8 @@
return OpRegRegImm(op, r_dest_src1, r_dest_src1, value);
} else {
LOG(FATAL) << "Bad case in OpRegImm";
+ UNREACHABLE();
}
- UNREACHABLE();
}
LIR* MipsMir2Lir::OpRegRegReg(OpKind op, RegStorage r_dest, RegStorage r_src1, RegStorage r_src2) {
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index ed8e21e..961cd4f 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -1253,11 +1253,14 @@
AppendLIR(NewLIR0(kPseudoPrologueBegin));
GenEntrySequence(&mir_graph_->reg_location_[start_vreg], mir_graph_->GetMethodLoc());
AppendLIR(NewLIR0(kPseudoPrologueEnd));
+ DCHECK_EQ(cfi_.GetCurrentCFAOffset(), frame_size_);
} else if (bb->block_type == kExitBlock) {
ResetRegPool();
+ DCHECK_EQ(cfi_.GetCurrentCFAOffset(), frame_size_);
AppendLIR(NewLIR0(kPseudoEpilogueBegin));
GenExitSequence();
AppendLIR(NewLIR0(kPseudoEpilogueEnd));
+ DCHECK_EQ(cfi_.GetCurrentCFAOffset(), frame_size_);
}
for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index bb8fbae..5995f33 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -29,6 +29,7 @@
#include "dex/quick/resource_mask.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "invoke_type.h"
+#include "lazy_debug_frame_opcode_writer.h"
#include "leb128.h"
#include "safe_map.h"
#include "utils/array_ref.h"
@@ -135,6 +136,7 @@
class BitVector;
struct CallInfo;
struct CompilationUnit;
+struct CompilerTemp;
struct InlineMethod;
class MIR;
struct LIR;
@@ -142,6 +144,7 @@
class DexFileMethodInliner;
class MIRGraph;
class MirMethodLoweringInfo;
+class MirSFieldLoweringInfo;
typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int,
const MethodReference& target_method,
@@ -774,9 +777,10 @@
*/
virtual RegLocation EvalLoc(RegLocation loc, int reg_class, bool update);
- void CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs);
+ void AnalyzeMIR(RefCounts* core_counts, MIR* mir, uint32_t weight);
+ virtual void CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs);
void DumpCounts(const RefCounts* arr, int size, const char* msg);
- void DoPromotion();
+ virtual void DoPromotion();
int VRegOffset(int v_reg);
int SRegOffset(int s_reg);
RegLocation GetReturnWide(RegisterClass reg_class);
@@ -1505,6 +1509,12 @@
return 0;
}
+ /**
+ * @brief Buffer of DWARF's Call Frame Information opcodes.
+ * @details It is used by debuggers and other tools to unwind the call stack.
+ */
+ dwarf::LazyDebugFrameOpCodeWriter& cfi() { return cfi_; }
+
protected:
Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
@@ -1570,11 +1580,6 @@
bool can_assume_type_is_in_dex_cache,
uint32_t type_idx, RegLocation rl_dest,
RegLocation rl_src);
- /*
- * @brief Generate the eh_frame FDE information if possible.
- * @returns pointer to vector containg FDE information, or NULL.
- */
- virtual std::vector<uint8_t>* ReturnFrameDescriptionEntry();
/**
* @brief Used to insert marker that can be used to associate MIR with LIR.
@@ -1692,6 +1697,13 @@
void GenIfNullUseHelperImmMethod(
RegStorage r_result, QuickEntrypointEnum trampoline, int imm, RegStorage r_method);
+ /**
+ * @brief Generate code to retrieve Class* for another type to be used by SGET/SPUT.
+ * @param field_info information about the field to be accessed.
+ * @param opt_flags the optimization flags of the MIR.
+ */
+ RegStorage GenGetOtherTypeForSgetSput(const MirSFieldLoweringInfo& field_info, int opt_flags);
+
void AddDivZeroCheckSlowPath(LIR* branch);
// Copy arg0 and arg1 to kArg0 and kArg1 safely, possibly using
@@ -1765,6 +1777,13 @@
// Update references from prev_mir to mir.
void UpdateReferenceVRegs(MIR* mir, MIR* prev_mir, BitVector* references);
+ /**
+ * Returns true if the frame spills the given core register.
+ */
+ bool CoreSpillMaskContains(int reg) {
+ return (core_spill_mask_ & (1u << reg)) != 0;
+ }
+
public:
// TODO: add accessors for these.
LIR* literal_list_; // Constants.
@@ -1841,6 +1860,20 @@
// The layout of the cu_->dex_file's dex cache arrays for PC-relative addressing.
const DexCacheArraysLayout dex_cache_arrays_layout_;
+ // For architectures that don't have true PC-relative addressing, we can promote
+ // a PC of an instruction (or another PC-relative address such as a pointer to
+ // the dex cache arrays if supported) to a register. This is indicated to the
+ // register promotion by allocating a backend temp.
+ CompilerTemp* pc_rel_temp_;
+
+ // For architectures that don't have true PC-relative addressing (see pc_rel_temp_
+ // above) and also have a limited range of offsets for loads, it's be useful to
+ // know the minimum offset into the dex cache arrays, so we calculate that as well
+ // if pc_rel_temp_ isn't nullptr.
+ uint32_t dex_cache_arrays_min_offset_;
+
+ dwarf::LazyDebugFrameOpCodeWriter cfi_;
+
// ABI support
class ShortyArg {
public:
@@ -1900,6 +1933,8 @@
private:
static bool SizeMatchesTypeForEntrypoint(OpSize size, Primitive::Type type);
+
+ friend class QuickCFITest;
}; // Class Mir2Lir
} // namespace art
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
new file mode 100644
index 0000000..0540a8c
--- /dev/null
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2015 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 <vector>
+#include <memory>
+
+#include "arch/instruction_set.h"
+#include "arch/instruction_set_features.h"
+#include "cfi_test.h"
+#include "dex/compiler_ir.h"
+#include "dex/mir_graph.h"
+#include "dex/pass_manager.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "dex/quick/quick_compiler.h"
+#include "dex/quick/mir_to_lir.h"
+#include "dex/verification_results.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
+#include "gtest/gtest.h"
+
+#include "dex/quick/quick_cfi_test_expected.inc"
+
+namespace art {
+
+// Run the tests only on host.
+#ifndef HAVE_ANDROID_OS
+
+class QuickCFITest : public CFITest {
+ public:
+ // Enable this flag to generate the expected outputs.
+ static constexpr bool kGenerateExpected = false;
+
+ void TestImpl(InstructionSet isa, const char* isa_str,
+ const std::vector<uint8_t>& expected_asm,
+ const std::vector<uint8_t>& expected_cfi) {
+ // Setup simple compiler context.
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ CompilerOptions compiler_options(
+ CompilerOptions::kDefaultCompilerFilter,
+ CompilerOptions::kDefaultHugeMethodThreshold,
+ CompilerOptions::kDefaultLargeMethodThreshold,
+ CompilerOptions::kDefaultSmallMethodThreshold,
+ CompilerOptions::kDefaultTinyMethodThreshold,
+ CompilerOptions::kDefaultNumDexMethodsThreshold,
+ true, // generate_gdb_information.
+ false,
+ CompilerOptions::kDefaultTopKProfileThreshold,
+ false,
+ true, // include_debug_symbols.
+ false,
+ false,
+ false,
+ false,
+ nullptr,
+ new PassManagerOptions(),
+ nullptr,
+ false);
+ VerificationResults verification_results(&compiler_options);
+ DexFileToMethodInlinerMap method_inliner_map;
+ std::unique_ptr<const InstructionSetFeatures> isa_features;
+ std::string error;
+ isa_features.reset(InstructionSetFeatures::FromVariant(isa, "default", &error));
+ CompilerDriver driver(&compiler_options, &verification_results, &method_inliner_map,
+ Compiler::kQuick, isa, isa_features.get(),
+ false, 0, 0, 0, false, false, "", 0, -1, "");
+ ClassLinker* linker = nullptr;
+ CompilationUnit cu(&pool, isa, &driver, linker);
+ DexFile::CodeItem code_item { 0, 0, 0, 0, 0, 0, { 0 } }; // NOLINT
+ cu.mir_graph.reset(new MIRGraph(&cu, &arena));
+ cu.mir_graph->current_code_item_ = &code_item;
+
+ // Generate empty method with some spills.
+ Mir2Lir* m2l = QuickCompiler::GetCodeGenerator(&cu, NULL);
+ m2l->frame_size_ = 64u;
+ m2l->CompilerInitializeRegAlloc();
+ for (const auto& info : m2l->reg_pool_->core_regs_) {
+ if (m2l->num_core_spills_ < 2 && !info->IsTemp() && !info->InUse()) {
+ m2l->core_spill_mask_ |= 1 << info->GetReg().GetReg();
+ m2l->num_core_spills_++;
+ }
+ }
+ for (const auto& info : m2l->reg_pool_->sp_regs_) {
+ if (m2l->num_fp_spills_ < 2 && !info->IsTemp() && !info->InUse()) {
+ m2l->fp_spill_mask_ |= 1 << info->GetReg().GetReg();
+ m2l->num_fp_spills_++;
+ }
+ }
+ m2l->AdjustSpillMask();
+ m2l->GenEntrySequence(NULL, m2l->LocCReturnRef());
+ m2l->GenExitSequence();
+ m2l->HandleSlowPaths();
+ m2l->AssembleLIR();
+ std::vector<uint8_t> actual_asm(m2l->code_buffer_.begin(), m2l->code_buffer_.end());
+ auto const& cfi_data = m2l->cfi().Patch(actual_asm.size());
+ std::vector<uint8_t> actual_cfi(cfi_data->begin(), cfi_data->end());
+ EXPECT_EQ(m2l->cfi().GetCurrentPC(), static_cast<int>(actual_asm.size()));
+
+ if (kGenerateExpected) {
+ GenerateExpected(stdout, isa, isa_str, actual_asm, actual_cfi);
+ } else {
+ EXPECT_EQ(expected_asm, actual_asm);
+ EXPECT_EQ(expected_cfi, actual_cfi);
+ }
+ }
+};
+
+#define TEST_ISA(isa) \
+ TEST_F(QuickCFITest, isa) { \
+ std::vector<uint8_t> expected_asm(expected_asm_##isa, \
+ expected_asm_##isa + arraysize(expected_asm_##isa)); \
+ std::vector<uint8_t> expected_cfi(expected_cfi_##isa, \
+ expected_cfi_##isa + arraysize(expected_cfi_##isa)); \
+ TestImpl(isa, #isa, expected_asm, expected_cfi); \
+ }
+
+TEST_ISA(kThumb2)
+TEST_ISA(kArm64)
+TEST_ISA(kX86)
+TEST_ISA(kX86_64)
+TEST_ISA(kMips)
+TEST_ISA(kMips64)
+
+#endif // HAVE_ANDROID_OS
+
+} // namespace art
diff --git a/compiler/dex/quick/quick_cfi_test_expected.inc b/compiler/dex/quick/quick_cfi_test_expected.inc
new file mode 100644
index 0000000..634fdee
--- /dev/null
+++ b/compiler/dex/quick/quick_cfi_test_expected.inc
@@ -0,0 +1,217 @@
+static constexpr uint8_t expected_asm_kThumb2[] = {
+ 0x60, 0xB5, 0x2D, 0xED, 0x02, 0x8A, 0x8B, 0xB0, 0x00, 0x90, 0x0B, 0xB0,
+ 0xBD, 0xEC, 0x02, 0x8A, 0x60, 0xBD, 0x00, 0x00,
+};
+static constexpr uint8_t expected_cfi_kThumb2[] = {
+ 0x42, 0x0E, 0x0C, 0x85, 0x03, 0x86, 0x02, 0x8E, 0x01, 0x44, 0x0E, 0x14,
+ 0x05, 0x50, 0x05, 0x05, 0x51, 0x04, 0x42, 0x0E, 0x40, 0x42, 0x0A, 0x42,
+ 0x0E, 0x14, 0x44, 0x0E, 0x0C, 0x06, 0x50, 0x06, 0x51, 0x44, 0x0B, 0x0E,
+ 0x40,
+};
+// 0x00000000: push {r5, r6, lr}
+// 0x00000002: .cfi_def_cfa_offset: 12
+// 0x00000002: .cfi_offset: r5 at cfa-12
+// 0x00000002: .cfi_offset: r6 at cfa-8
+// 0x00000002: .cfi_offset: r14 at cfa-4
+// 0x00000002: vpush.f32 {s16-s17}
+// 0x00000006: .cfi_def_cfa_offset: 20
+// 0x00000006: .cfi_offset_extended: r80 at cfa-20
+// 0x00000006: .cfi_offset_extended: r81 at cfa-16
+// 0x00000006: sub sp, sp, #44
+// 0x00000008: .cfi_def_cfa_offset: 64
+// 0x00000008: str r0, [sp, #0]
+// 0x0000000a: .cfi_remember_state
+// 0x0000000a: add sp, sp, #44
+// 0x0000000c: .cfi_def_cfa_offset: 20
+// 0x0000000c: vpop.f32 {s16-s17}
+// 0x00000010: .cfi_def_cfa_offset: 12
+// 0x00000010: .cfi_restore_extended: r80
+// 0x00000010: .cfi_restore_extended: r81
+// 0x00000010: pop {r5, r6, pc}
+// 0x00000012: lsls r0, r0, #0
+// 0x00000014: .cfi_restore_state
+// 0x00000014: .cfi_def_cfa_offset: 64
+
+static constexpr uint8_t expected_asm_kArm64[] = {
+ 0xFF, 0x03, 0x01, 0xD1, 0xE8, 0xA7, 0x01, 0x6D, 0xF4, 0xD7, 0x02, 0xA9,
+ 0xFE, 0x1F, 0x00, 0xF9, 0xE0, 0x03, 0x00, 0xB9, 0xE8, 0xA7, 0x41, 0x6D,
+ 0xF4, 0xD7, 0x42, 0xA9, 0xFE, 0x1F, 0x40, 0xF9, 0xFF, 0x03, 0x01, 0x91,
+ 0xC0, 0x03, 0x5F, 0xD6,
+};
+static constexpr uint8_t expected_cfi_kArm64[] = {
+ 0x44, 0x0E, 0x40, 0x44, 0x05, 0x48, 0x0A, 0x05, 0x49, 0x08, 0x44, 0x94,
+ 0x06, 0x95, 0x04, 0x44, 0x9E, 0x02, 0x44, 0x0A, 0x44, 0x06, 0x48, 0x06,
+ 0x49, 0x44, 0xD4, 0xD5, 0x44, 0xDE, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E,
+ 0x40,
+};
+// 0x00000000: sub sp, sp, #0x40 (64)
+// 0x00000004: .cfi_def_cfa_offset: 64
+// 0x00000004: stp d8, d9, [sp, #24]
+// 0x00000008: .cfi_offset_extended: r72 at cfa-40
+// 0x00000008: .cfi_offset_extended: r73 at cfa-32
+// 0x00000008: stp x20, x21, [sp, #40]
+// 0x0000000c: .cfi_offset: r20 at cfa-24
+// 0x0000000c: .cfi_offset: r21 at cfa-16
+// 0x0000000c: str lr, [sp, #56]
+// 0x00000010: .cfi_offset: r30 at cfa-8
+// 0x00000010: str w0, [sp]
+// 0x00000014: .cfi_remember_state
+// 0x00000014: ldp d8, d9, [sp, #24]
+// 0x00000018: .cfi_restore_extended: r72
+// 0x00000018: .cfi_restore_extended: r73
+// 0x00000018: ldp x20, x21, [sp, #40]
+// 0x0000001c: .cfi_restore: r20
+// 0x0000001c: .cfi_restore: r21
+// 0x0000001c: ldr lr, [sp, #56]
+// 0x00000020: .cfi_restore: r30
+// 0x00000020: add sp, sp, #0x40 (64)
+// 0x00000024: .cfi_def_cfa_offset: 0
+// 0x00000024: ret
+// 0x00000028: .cfi_restore_state
+// 0x00000028: .cfi_def_cfa_offset: 64
+
+static constexpr uint8_t expected_asm_kX86[] = {
+ 0x83, 0xEC, 0x3C, 0x89, 0x6C, 0x24, 0x34, 0x89, 0x74, 0x24, 0x38, 0x89,
+ 0x04, 0x24, 0x8B, 0x6C, 0x24, 0x34, 0x8B, 0x74, 0x24, 0x38, 0x83, 0xC4,
+ 0x3C, 0xC3, 0x00, 0x00,
+};
+static constexpr uint8_t expected_cfi_kX86[] = {
+ 0x43, 0x0E, 0x40, 0x44, 0x85, 0x03, 0x44, 0x86, 0x02, 0x43, 0x0A, 0x44,
+ 0xC5, 0x44, 0xC6, 0x43, 0x0E, 0x04, 0x43, 0x0B, 0x0E, 0x40,
+};
+// 0x00000000: sub esp, 60
+// 0x00000003: .cfi_def_cfa_offset: 64
+// 0x00000003: mov [esp + 52], ebp
+// 0x00000007: .cfi_offset: r5 at cfa-12
+// 0x00000007: mov [esp + 56], esi
+// 0x0000000b: .cfi_offset: r6 at cfa-8
+// 0x0000000b: mov [esp], eax
+// 0x0000000e: .cfi_remember_state
+// 0x0000000e: mov ebp, [esp + 52]
+// 0x00000012: .cfi_restore: r5
+// 0x00000012: mov esi, [esp + 56]
+// 0x00000016: .cfi_restore: r6
+// 0x00000016: add esp, 60
+// 0x00000019: .cfi_def_cfa_offset: 4
+// 0x00000019: ret
+// 0x0000001a: addb [eax], al
+// 0x0000001c: .cfi_restore_state
+// 0x0000001c: .cfi_def_cfa_offset: 64
+
+static constexpr uint8_t expected_asm_kX86_64[] = {
+ 0x48, 0x83, 0xEC, 0x38, 0x48, 0x89, 0x5C, 0x24, 0x28, 0x48, 0x89, 0x6C,
+ 0x24, 0x30, 0xF2, 0x44, 0x0F, 0x11, 0x64, 0x24, 0x18, 0xF2, 0x44, 0x0F,
+ 0x11, 0x6C, 0x24, 0x20, 0x48, 0x8B, 0xC7, 0x89, 0x3C, 0x24, 0x48, 0x8B,
+ 0x5C, 0x24, 0x28, 0x48, 0x8B, 0x6C, 0x24, 0x30, 0xF2, 0x44, 0x0F, 0x10,
+ 0x64, 0x24, 0x18, 0xF2, 0x44, 0x0F, 0x10, 0x6C, 0x24, 0x20, 0x48, 0x83,
+ 0xC4, 0x38, 0xC3, 0x00,
+};
+static constexpr uint8_t expected_cfi_kX86_64[] = {
+ 0x44, 0x0E, 0x40, 0x45, 0x83, 0x06, 0x45, 0x86, 0x04, 0x47, 0x9D, 0x0A,
+ 0x47, 0x9E, 0x08, 0x46, 0x0A, 0x45, 0xC3, 0x45, 0xC6, 0x47, 0xDD, 0x47,
+ 0xDE, 0x44, 0x0E, 0x08, 0x42, 0x0B, 0x0E, 0x40,
+};
+// 0x00000000: subq rsp, 56
+// 0x00000004: .cfi_def_cfa_offset: 64
+// 0x00000004: movq [rsp + 40], rbx
+// 0x00000009: .cfi_offset: r3 at cfa-24
+// 0x00000009: movq [rsp + 48], rbp
+// 0x0000000e: .cfi_offset: r6 at cfa-16
+// 0x0000000e: movsd [rsp + 24], xmm12
+// 0x00000015: .cfi_offset: r29 at cfa-40
+// 0x00000015: movsd [rsp + 32], xmm13
+// 0x0000001c: .cfi_offset: r30 at cfa-32
+// 0x0000001c: movq rax, rdi
+// 0x0000001f: mov [rsp], edi
+// 0x00000022: .cfi_remember_state
+// 0x00000022: movq rbx, [rsp + 40]
+// 0x00000027: .cfi_restore: r3
+// 0x00000027: movq rbp, [rsp + 48]
+// 0x0000002c: .cfi_restore: r6
+// 0x0000002c: movsd xmm12, [rsp + 24]
+// 0x00000033: .cfi_restore: r29
+// 0x00000033: movsd xmm13, [rsp + 32]
+// 0x0000003a: .cfi_restore: r30
+// 0x0000003a: addq rsp, 56
+// 0x0000003e: .cfi_def_cfa_offset: 8
+// 0x0000003e: ret
+// 0x0000003f: addb al, al
+// 0x00000040: .cfi_restore_state
+// 0x00000040: .cfi_def_cfa_offset: 64
+
+static constexpr uint8_t expected_asm_kMips[] = {
+ 0xF4, 0xFF, 0xBD, 0x27, 0x08, 0x00, 0xB2, 0xAF, 0x04, 0x00, 0xB3, 0xAF,
+ 0x00, 0x00, 0xBF, 0xAF, 0xCC, 0xFF, 0xBD, 0x27, 0x25, 0x10, 0x80, 0x00,
+ 0x00, 0x00, 0xA4, 0xAF, 0x3C, 0x00, 0xB2, 0x8F, 0x38, 0x00, 0xB3, 0x8F,
+ 0x34, 0x00, 0xBF, 0x8F, 0x40, 0x00, 0xBD, 0x27, 0x09, 0x00, 0xE0, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+};
+static constexpr uint8_t expected_cfi_kMips[] = {
+ 0x44, 0x0E, 0x0C, 0x44, 0x92, 0x01, 0x44, 0x93, 0x02, 0x44, 0x9F, 0x03,
+ 0x44, 0x0E, 0x40, 0x48, 0x0A, 0x44, 0xD2, 0x44, 0xD3, 0x44, 0xDF, 0x44,
+ 0x0E, 0x00, 0x48, 0x0B, 0x0E, 0x40,
+};
+// 0x00000000: addiu r29, r29, -12
+// 0x00000004: .cfi_def_cfa_offset: 12
+// 0x00000004: sw r18, +8(r29)
+// 0x00000008: .cfi_offset: r18 at cfa-4
+// 0x00000008: sw r19, +4(r29)
+// 0x0000000c: .cfi_offset: r19 at cfa-8
+// 0x0000000c: sw r31, +0(r29)
+// 0x00000010: .cfi_offset: r31 at cfa-12
+// 0x00000010: addiu r29, r29, -52
+// 0x00000014: .cfi_def_cfa_offset: 64
+// 0x00000014: or r2, r4, r0
+// 0x00000018: sw r4, +0(r29)
+// 0x0000001c: .cfi_remember_state
+// 0x0000001c: lw r18, +60(r29)
+// 0x00000020: .cfi_restore: r18
+// 0x00000020: lw r19, +56(r29)
+// 0x00000024: .cfi_restore: r19
+// 0x00000024: lw r31, +52(r29)
+// 0x00000028: .cfi_restore: r31
+// 0x00000028: addiu r29, r29, 64
+// 0x0000002c: .cfi_def_cfa_offset: 0
+// 0x0000002c: jalr r0, r31
+// 0x00000030: nop
+// 0x00000034: .cfi_restore_state
+// 0x00000034: .cfi_def_cfa_offset: 64
+
+static constexpr uint8_t expected_asm_kMips64[] = {
+ 0xE8, 0xFF, 0xBD, 0x67, 0x10, 0x00, 0xB2, 0xFF, 0x08, 0x00, 0xB3, 0xFF,
+ 0x00, 0x00, 0xBF, 0xFF, 0xD8, 0xFF, 0xBD, 0x67, 0x25, 0x10, 0x80, 0x00,
+ 0x00, 0x00, 0xA4, 0xAF, 0x38, 0x00, 0xB2, 0xDF, 0x30, 0x00, 0xB3, 0xDF,
+ 0x28, 0x00, 0xBF, 0xDF, 0x40, 0x00, 0xBD, 0x67, 0x09, 0x00, 0xE0, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+};
+static constexpr uint8_t expected_cfi_kMips64[] = {
+ 0x44, 0x0E, 0x18, 0x44, 0x92, 0x02, 0x44, 0x93, 0x04, 0x44, 0x9F, 0x06,
+ 0x44, 0x0E, 0x40, 0x48, 0x0A, 0x44, 0xD2, 0x44, 0xD3, 0x44, 0xDF, 0x44,
+ 0x0E, 0x00, 0x48, 0x0B, 0x0E, 0x40,
+};
+// 0x00000000: daddiu r29, r29, -24
+// 0x00000004: .cfi_def_cfa_offset: 24
+// 0x00000004: sd r18, +16(r29)
+// 0x00000008: .cfi_offset: r18 at cfa-8
+// 0x00000008: sd r19, +8(r29)
+// 0x0000000c: .cfi_offset: r19 at cfa-16
+// 0x0000000c: sd r31, +0(r29)
+// 0x00000010: .cfi_offset: r31 at cfa-24
+// 0x00000010: daddiu r29, r29, -40
+// 0x00000014: .cfi_def_cfa_offset: 64
+// 0x00000014: or r2, r4, r0
+// 0x00000018: sw r4, +0(r29)
+// 0x0000001c: .cfi_remember_state
+// 0x0000001c: ld r18, +56(r29)
+// 0x00000020: .cfi_restore: r18
+// 0x00000020: ld r19, +48(r29)
+// 0x00000024: .cfi_restore: r19
+// 0x00000024: ld r31, +40(r29)
+// 0x00000028: .cfi_restore: r31
+// 0x00000028: daddiu r29, r29, 64
+// 0x0000002c: .cfi_def_cfa_offset: 0
+// 0x0000002c: jr r31
+// 0x00000030: nop
+// 0x00000034: .cfi_restore_state
+// 0x00000034: .cfi_def_cfa_offset: 64
+
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 8baafc7..2c0bd47 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -798,11 +798,16 @@
const std::vector<const art::DexFile*>& dex_files,
const std::string& android_root,
bool is_host) const {
- return art::ElfWriterQuick32::Create(file, oat_writer, dex_files, android_root, is_host,
- *GetCompilerDriver());
+ if (kProduce64BitELFFiles && Is64BitInstructionSet(GetCompilerDriver()->GetInstructionSet())) {
+ return art::ElfWriterQuick64::Create(file, oat_writer, dex_files, android_root, is_host,
+ *GetCompilerDriver());
+ } else {
+ return art::ElfWriterQuick32::Create(file, oat_writer, dex_files, android_root, is_host,
+ *GetCompilerDriver());
+ }
}
-Mir2Lir* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
+Mir2Lir* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) {
UNUSED(compilation_unit);
Mir2Lir* mir_to_lir = nullptr;
switch (cu->instruction_set) {
diff --git a/compiler/dex/quick/quick_compiler.h b/compiler/dex/quick/quick_compiler.h
index 5153a9e..09b08ac 100644
--- a/compiler/dex/quick/quick_compiler.h
+++ b/compiler/dex/quick/quick_compiler.h
@@ -60,7 +60,7 @@
OVERRIDE
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- Mir2Lir* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const;
+ static Mir2Lir* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit);
void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;
diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc
index 741657b..e779479 100644
--- a/compiler/dex/quick/ralloc_util.cc
+++ b/compiler/dex/quick/ralloc_util.cc
@@ -19,9 +19,11 @@
#include "mir_to_lir-inl.h"
#include "dex/compiler_ir.h"
+#include "dex/dataflow_iterator-inl.h"
#include "dex/mir_graph.h"
#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
namespace art {
@@ -1128,6 +1130,152 @@
return loc;
}
+void Mir2Lir::AnalyzeMIR(RefCounts* core_counts, MIR* mir, uint32_t weight) {
+ // NOTE: This should be in sync with functions that actually generate code for
+ // the opcodes below. However, if we get this wrong, the generated code will
+ // still be correct even if it may be sub-optimal.
+ int opcode = mir->dalvikInsn.opcode;
+ bool uses_method = false;
+ bool uses_pc_rel_load = false;
+ uint32_t dex_cache_array_offset = std::numeric_limits<uint32_t>::max();
+ switch (opcode) {
+ case Instruction::CHECK_CAST:
+ case Instruction::INSTANCE_OF: {
+ if ((opcode == Instruction::CHECK_CAST) &&
+ (mir->optimization_flags & MIR_IGNORE_CHECK_CAST) != 0) {
+ break; // No code generated.
+ }
+ uint32_t type_idx =
+ (opcode == Instruction::CHECK_CAST) ? mir->dalvikInsn.vB : mir->dalvikInsn.vC;
+ 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);
+ if (opcode == Instruction::CHECK_CAST && !needs_access_check &&
+ cu_->compiler_driver->IsSafeCast(
+ mir_graph_->GetCurrentDexCompilationUnit(), mir->offset)) {
+ break; // No code generated.
+ }
+ if (!needs_access_check && !use_declaring_class && CanUseOpPcRelDexCacheArrayLoad()) {
+ uses_pc_rel_load = true; // And ignore method use in slow path.
+ dex_cache_array_offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
+ } else {
+ uses_method = true;
+ }
+ break;
+ }
+
+ case Instruction::CONST_CLASS:
+ if (CanUseOpPcRelDexCacheArrayLoad() &&
+ cu_->compiler_driver->CanAccessTypeWithoutChecks(cu_->method_idx, *cu_->dex_file,
+ mir->dalvikInsn.vB)) {
+ uses_pc_rel_load = true; // And ignore method use in slow path.
+ dex_cache_array_offset = dex_cache_arrays_layout_.TypeOffset(mir->dalvikInsn.vB);
+ } else {
+ uses_method = true;
+ }
+ break;
+
+ case Instruction::CONST_STRING:
+ case Instruction::CONST_STRING_JUMBO:
+ if (CanUseOpPcRelDexCacheArrayLoad()) {
+ uses_pc_rel_load = true; // And ignore method use in slow path.
+ dex_cache_array_offset = dex_cache_arrays_layout_.StringOffset(mir->dalvikInsn.vB);
+ } else {
+ uses_method = true;
+ }
+ break;
+
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_SUPER:
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ case Instruction::INVOKE_STATIC_RANGE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ case Instruction::INVOKE_VIRTUAL_QUICK:
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
+ const MirMethodLoweringInfo& info = mir_graph_->GetMethodLoweringInfo(mir);
+ InvokeType sharp_type = info.GetSharpType();
+ if (info.IsIntrinsic()) {
+ // Nothing to do, if an intrinsic uses ArtMethod* it's in the slow-path - don't count it.
+ } else if (!info.FastPath() || (sharp_type != kStatic && sharp_type != kDirect)) {
+ // Nothing to do, the generated code or entrypoint uses method from the stack.
+ } else if (info.DirectCode() != 0 && info.DirectMethod() != 0) {
+ // Nothing to do, the generated code uses method from the stack.
+ } else if (CanUseOpPcRelDexCacheArrayLoad()) {
+ uses_pc_rel_load = true;
+ dex_cache_array_offset = dex_cache_arrays_layout_.MethodOffset(mir->dalvikInsn.vB);
+ } else {
+ uses_method = true;
+ }
+ break;
+ }
+
+ case Instruction::NEW_INSTANCE:
+ case Instruction::NEW_ARRAY:
+ case Instruction::FILLED_NEW_ARRAY:
+ case Instruction::FILLED_NEW_ARRAY_RANGE:
+ uses_method = true;
+ break;
+ case Instruction::FILL_ARRAY_DATA:
+ // Nothing to do, the entrypoint uses method from the stack.
+ break;
+ case Instruction::THROW:
+ // Nothing to do, the entrypoint uses method from the stack.
+ break;
+
+ case Instruction::SGET:
+ case Instruction::SGET_WIDE:
+ case Instruction::SGET_OBJECT:
+ case Instruction::SGET_BOOLEAN:
+ case Instruction::SGET_BYTE:
+ case Instruction::SGET_CHAR:
+ case Instruction::SGET_SHORT:
+ case Instruction::SPUT:
+ case Instruction::SPUT_WIDE:
+ case Instruction::SPUT_OBJECT:
+ case Instruction::SPUT_BOOLEAN:
+ case Instruction::SPUT_BYTE:
+ case Instruction::SPUT_CHAR:
+ case Instruction::SPUT_SHORT: {
+ const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir);
+ bool fast = IsInstructionSGet(static_cast<Instruction::Code>(opcode))
+ ? field_info.FastGet()
+ : field_info.FastPut();
+ if (fast && (cu_->enable_debug & (1 << kDebugSlowFieldPath)) == 0) {
+ if (!field_info.IsReferrersClass() && CanUseOpPcRelDexCacheArrayLoad()) {
+ uses_pc_rel_load = true; // And ignore method use in slow path.
+ dex_cache_array_offset = dex_cache_arrays_layout_.TypeOffset(field_info.StorageIndex());
+ } else {
+ uses_method = true;
+ }
+ } else {
+ // Nothing to do, the entrypoint uses method from the stack.
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ if (uses_method) {
+ core_counts[SRegToPMap(mir_graph_->GetMethodLoc().s_reg_low)].count += weight;
+ }
+ if (uses_pc_rel_load) {
+ if (pc_rel_temp_ != nullptr) {
+ core_counts[SRegToPMap(pc_rel_temp_->s_reg_low)].count += weight;
+ DCHECK_NE(dex_cache_array_offset, std::numeric_limits<uint32_t>::max());
+ dex_cache_arrays_min_offset_ = std::min(dex_cache_arrays_min_offset_, dex_cache_array_offset);
+ } else {
+ // Nothing to do, using PC-relative addressing without promoting base PC to register.
+ }
+ }
+}
+
/* USE SSA names to count references of base Dalvik v_regs. */
void Mir2Lir::CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs) {
for (int i = 0; i < mir_graph_->GetNumSSARegs(); i++) {
@@ -1157,6 +1305,22 @@
}
}
}
+
+ // Now analyze the ArtMethod* and pc_rel_temp_ uses.
+ DCHECK_EQ(core_counts[SRegToPMap(mir_graph_->GetMethodLoc().s_reg_low)].count, 0);
+ if (pc_rel_temp_ != nullptr) {
+ DCHECK_EQ(core_counts[SRegToPMap(pc_rel_temp_->s_reg_low)].count, 0);
+ }
+ PreOrderDfsIterator iter(mir_graph_);
+ for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
+ if (bb->block_type == kDead) {
+ continue;
+ }
+ uint32_t weight = mir_graph_->GetUseCountWeight(bb);
+ for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
+ AnalyzeMIR(core_counts, mir, weight);
+ }
+ }
}
/* qsort callback function, sort descending */
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index fd23692..7f42536 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -150,6 +150,10 @@
FreeTemp(reg_card_no);
}
+static dwarf::Reg DwarfCoreReg(bool is_x86_64, int num) {
+ return is_x86_64 ? dwarf::Reg::X86_64Core(num) : dwarf::Reg::X86Core(num);
+}
+
void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
/*
* On entry, rX86_ARG0, rX86_ARG1, rX86_ARG2 are live. Let the register
@@ -184,8 +188,9 @@
}
/* Build frame, return address already on stack */
- stack_decrement_ = OpRegImm(kOpSub, rs_rSP, frame_size_ -
- GetInstructionSetPointerSize(cu_->instruction_set));
+ cfi_.SetCurrentCFAOffset(GetInstructionSetPointerSize(cu_->instruction_set));
+ OpRegImm(kOpSub, rs_rSP, frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set));
+ cfi_.DefCFAOffset(frame_size_);
/* Spill core callee saves */
SpillCoreRegs();
@@ -202,10 +207,12 @@
GenerateTargetLabel(kPseudoThrowTarget);
const RegStorage local_rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
m2l_->OpRegImm(kOpAdd, local_rs_rSP, sp_displace_);
+ m2l_->cfi().AdjustCFAOffset(-sp_displace_);
m2l_->ClobberCallerSave();
// Assumes codegen and target are in thumb2 mode.
m2l_->CallHelper(RegStorage::InvalidReg(), kQuickThrowStackOverflow,
false /* MarkSafepointPC */, false /* UseLink */);
+ m2l_->cfi().AdjustCFAOffset(sp_displace_);
}
private:
@@ -252,6 +259,7 @@
}
void X86Mir2Lir::GenExitSequence() {
+ cfi_.RememberState();
/*
* In the exit path, rX86_RET0/rX86_RET1 are live - make sure they aren't
* allocated by the register utilities as temps.
@@ -263,9 +271,14 @@
UnSpillFPRegs();
/* Remove frame except for return address */
const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
- stack_increment_ = OpRegImm(kOpAdd, rs_rSP,
- frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set));
+ int adjust = frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set);
+ OpRegImm(kOpAdd, rs_rSP, adjust);
+ cfi_.AdjustCFAOffset(-adjust);
+ // There is only the return PC on the stack now.
NewLIR0(kX86Ret);
+ // The CFI should be restored for any code that follows the exit block.
+ cfi_.RestoreState();
+ cfi_.DefCFAOffset(frame_size_);
}
void X86Mir2Lir::GenSpecialExitSequence() {
@@ -276,6 +289,8 @@
// Keep 16-byte stack alignment, there's already the return address, so
// - for 32-bit push EAX, i.e. ArtMethod*, ESI, EDI,
// - for 64-bit push RAX, i.e. ArtMethod*.
+ const int kRegSize = cu_->target64 ? 8 : 4;
+ cfi_.SetCurrentCFAOffset(kRegSize); // Return address.
if (!cu_->target64) {
DCHECK(!IsTemp(rs_rSI));
DCHECK(!IsTemp(rs_rDI));
@@ -293,17 +308,29 @@
fp_vmap_table_.clear();
if (!cu_->target64) {
NewLIR1(kX86Push32R, rs_rDI.GetReg());
+ cfi_.AdjustCFAOffset(kRegSize);
+ cfi_.RelOffset(DwarfCoreReg(cu_->target64, rs_rDI.GetRegNum()), 0);
NewLIR1(kX86Push32R, rs_rSI.GetReg());
+ cfi_.AdjustCFAOffset(kRegSize);
+ cfi_.RelOffset(DwarfCoreReg(cu_->target64, rs_rSI.GetRegNum()), 0);
}
NewLIR1(kX86Push32R, TargetReg(kArg0, kRef).GetReg()); // ArtMethod*
+ cfi_.AdjustCFAOffset(kRegSize);
+ // Do not generate CFI for scratch register.
}
void X86Mir2Lir::GenSpecialExitForSuspend() {
+ const int kRegSize = cu_->target64 ? 8 : 4;
// Pop the frame. (ArtMethod* no longer needed but restore it anyway.)
NewLIR1(kX86Pop32R, TargetReg(kArg0, kRef).GetReg()); // ArtMethod*
+ cfi_.AdjustCFAOffset(-kRegSize);
if (!cu_->target64) {
NewLIR1(kX86Pop32R, rs_rSI.GetReg());
+ cfi_.AdjustCFAOffset(-kRegSize);
+ cfi_.Restore(DwarfCoreReg(cu_->target64, rs_rSI.GetRegNum()));
NewLIR1(kX86Pop32R, rs_rDI.GetReg());
+ cfi_.AdjustCFAOffset(-kRegSize);
+ cfi_.Restore(DwarfCoreReg(cu_->target64, rs_rDI.GetRegNum()));
}
}
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 758684e..a98a99e 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -380,12 +380,6 @@
*/
void InstallLiteralPools() OVERRIDE;
- /*
- * @brief Generate the debug_frame FDE information.
- * @returns pointer to vector containing CFE information
- */
- std::vector<uint8_t>* ReturnFrameDescriptionEntry() OVERRIDE;
-
LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE;
protected:
@@ -958,12 +952,6 @@
// Instructions needing patching with PC relative code addresses.
ArenaVector<LIR*> dex_cache_access_insns_;
- // Prologue decrement of stack pointer.
- LIR* stack_decrement_;
-
- // Epilogue increment of stack pointer.
- LIR* stack_increment_;
-
// The list of const vector literals.
LIR* const_vectors_;
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 5def5c8..931294e 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -830,6 +830,10 @@
return rl_result;
}
+static dwarf::Reg DwarfCoreReg(bool is_x86_64, int num) {
+ return is_x86_64 ? dwarf::Reg::X86_64Core(num) : dwarf::Reg::X86Core(num);
+}
+
bool X86Mir2Lir::GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) {
DCHECK(cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64);
@@ -928,6 +932,7 @@
// Do we have a free register for intermediate calculations?
RegStorage tmp = AllocTemp(false);
+ const int kRegSize = cu_->target64 ? 8 : 4;
if (tmp == RegStorage::InvalidReg()) {
/*
* No, will use 'edi'.
@@ -946,6 +951,11 @@
IsTemp(rl_result.reg.GetHigh()));
tmp = rs_rDI;
NewLIR1(kX86Push32R, tmp.GetReg());
+ cfi_.AdjustCFAOffset(kRegSize);
+ // Record cfi only if it is not already spilled.
+ if (!CoreSpillMaskContains(tmp.GetReg())) {
+ cfi_.RelOffset(DwarfCoreReg(cu_->target64, tmp.GetReg()), 0);
+ }
}
// Now we are ready to do calculations.
@@ -957,6 +967,10 @@
// Let's put pop 'edi' here to break a bit the dependency chain.
if (tmp == rs_rDI) {
NewLIR1(kX86Pop32R, tmp.GetReg());
+ cfi_.AdjustCFAOffset(-kRegSize);
+ if (!CoreSpillMaskContains(tmp.GetReg())) {
+ cfi_.Restore(DwarfCoreReg(cu_->target64, tmp.GetReg()));
+ }
} else {
FreeTemp(tmp);
}
@@ -1104,6 +1118,7 @@
// If is_long, high half is in info->args[5]
RegLocation rl_src_new_value = info->args[is_long ? 6 : 5]; // int, long or Object
// If is_long, high half is in info->args[7]
+ const int kRegSize = cu_->target64 ? 8 : 4;
if (is_long && cu_->target64) {
// RAX must hold expected for CMPXCHG. Neither rl_new_value, nor r_ptr may be in RAX.
@@ -1125,7 +1140,6 @@
FreeTemp(rs_r0q);
} else if (is_long) {
// TODO: avoid unnecessary loads of SI and DI when the values are in registers.
- // TODO: CFI support.
FlushAllRegs();
LockCallTemps();
RegStorage r_tmp1 = RegStorage::MakeRegPair(rs_rAX, rs_rDX);
@@ -1148,11 +1162,21 @@
NewLIR1(kX86Push32R, rs_rDI.GetReg());
MarkTemp(rs_rDI);
LockTemp(rs_rDI);
+ cfi_.AdjustCFAOffset(kRegSize);
+ // Record cfi only if it is not already spilled.
+ if (!CoreSpillMaskContains(rs_rDI.GetReg())) {
+ cfi_.RelOffset(DwarfCoreReg(cu_->target64, rs_rDI.GetReg()), 0);
+ }
}
if (push_si) {
NewLIR1(kX86Push32R, rs_rSI.GetReg());
MarkTemp(rs_rSI);
LockTemp(rs_rSI);
+ cfi_.AdjustCFAOffset(kRegSize);
+ // Record cfi only if it is not already spilled.
+ if (!CoreSpillMaskContains(rs_rSI.GetReg())) {
+ cfi_.RelOffset(DwarfCoreReg(cu_->target64, rs_rSI.GetReg()), 0);
+ }
}
ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
const size_t push_offset = (push_si ? 4u : 0u) + (push_di ? 4u : 0u);
@@ -1183,11 +1207,19 @@
FreeTemp(rs_rSI);
UnmarkTemp(rs_rSI);
NewLIR1(kX86Pop32R, rs_rSI.GetReg());
+ cfi_.AdjustCFAOffset(-kRegSize);
+ if (!CoreSpillMaskContains(rs_rSI.GetReg())) {
+ cfi_.Restore(DwarfCoreReg(cu_->target64, rs_rSI.GetRegNum()));
+ }
}
if (push_di) {
FreeTemp(rs_rDI);
UnmarkTemp(rs_rDI);
NewLIR1(kX86Pop32R, rs_rDI.GetReg());
+ cfi_.AdjustCFAOffset(-kRegSize);
+ if (!CoreSpillMaskContains(rs_rDI.GetReg())) {
+ cfi_.Restore(DwarfCoreReg(cu_->target64, rs_rDI.GetRegNum()));
+ }
}
FreeCallTemps();
} else {
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index cad82a1..926b75e 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -32,7 +32,6 @@
#include "mirror/string.h"
#include "oat.h"
#include "x86_lir.h"
-#include "utils/dwarf_cfi.h"
namespace art {
@@ -725,6 +724,14 @@
return long_or_fp ? num_vector_temps - 2 : num_vector_temps - 1;
}
+static dwarf::Reg DwarfCoreReg(bool is_x86_64, int num) {
+ return is_x86_64 ? dwarf::Reg::X86_64Core(num) : dwarf::Reg::X86Core(num);
+}
+
+static dwarf::Reg DwarfFpReg(bool is_x86_64, int num) {
+ return is_x86_64 ? dwarf::Reg::X86_64Fp(num) : dwarf::Reg::X86Fp(num);
+}
+
void X86Mir2Lir::SpillCoreRegs() {
if (num_core_spills_ == 0) {
return;
@@ -735,11 +742,11 @@
frame_size_ - (GetInstructionSetPointerSize(cu_->instruction_set) * num_core_spills_);
OpSize size = cu_->target64 ? k64 : k32;
const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
- for (int reg = 0; mask; mask >>= 1, reg++) {
- if (mask & 0x1) {
- StoreBaseDisp(rs_rSP, offset,
- cu_->target64 ? RegStorage::Solo64(reg) : RegStorage::Solo32(reg),
- size, kNotVolatile);
+ for (int reg = 0; mask != 0u; mask >>= 1, reg++) {
+ if ((mask & 0x1) != 0u) {
+ RegStorage r_src = cu_->target64 ? RegStorage::Solo64(reg) : RegStorage::Solo32(reg);
+ StoreBaseDisp(rs_rSP, offset, r_src, size, kNotVolatile);
+ cfi_.RelOffset(DwarfCoreReg(cu_->target64, reg), offset);
offset += GetInstructionSetPointerSize(cu_->instruction_set);
}
}
@@ -754,10 +761,11 @@
int offset = frame_size_ - (GetInstructionSetPointerSize(cu_->instruction_set) * num_core_spills_);
OpSize size = cu_->target64 ? k64 : k32;
const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
- for (int reg = 0; mask; mask >>= 1, reg++) {
- if (mask & 0x1) {
- LoadBaseDisp(rs_rSP, offset, cu_->target64 ? RegStorage::Solo64(reg) : RegStorage::Solo32(reg),
- size, kNotVolatile);
+ for (int reg = 0; mask != 0u; mask >>= 1, reg++) {
+ if ((mask & 0x1) != 0u) {
+ RegStorage r_dest = cu_->target64 ? RegStorage::Solo64(reg) : RegStorage::Solo32(reg);
+ LoadBaseDisp(rs_rSP, offset, r_dest, size, kNotVolatile);
+ cfi_.Restore(DwarfCoreReg(cu_->target64, reg));
offset += GetInstructionSetPointerSize(cu_->instruction_set);
}
}
@@ -771,9 +779,10 @@
int offset = frame_size_ -
(GetInstructionSetPointerSize(cu_->instruction_set) * (num_fp_spills_ + num_core_spills_));
const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
- for (int reg = 0; mask; mask >>= 1, reg++) {
- if (mask & 0x1) {
+ for (int reg = 0; mask != 0u; mask >>= 1, reg++) {
+ if ((mask & 0x1) != 0u) {
StoreBaseDisp(rs_rSP, offset, RegStorage::FloatSolo64(reg), k64, kNotVolatile);
+ cfi_.RelOffset(DwarfFpReg(cu_->target64, reg), offset);
offset += sizeof(double);
}
}
@@ -786,10 +795,11 @@
int offset = frame_size_ -
(GetInstructionSetPointerSize(cu_->instruction_set) * (num_fp_spills_ + num_core_spills_));
const RegStorage rs_rSP = cu_->target64 ? rs_rX86_SP_64 : rs_rX86_SP_32;
- for (int reg = 0; mask; mask >>= 1, reg++) {
- if (mask & 0x1) {
+ for (int reg = 0; mask != 0u; mask >>= 1, reg++) {
+ if ((mask & 0x1) != 0u) {
LoadBaseDisp(rs_rSP, offset, RegStorage::FloatSolo64(reg),
k64, kNotVolatile);
+ cfi_.Restore(DwarfFpReg(cu_->target64, reg));
offset += sizeof(double);
}
}
@@ -830,7 +840,6 @@
class_type_address_insns_(arena->Adapter()),
call_method_insns_(arena->Adapter()),
dex_cache_access_insns_(arena->Adapter()),
- stack_decrement_(nullptr), stack_increment_(nullptr),
const_vectors_(nullptr) {
method_address_insns_.reserve(100);
class_type_address_insns_.reserve(100);
@@ -1317,6 +1326,11 @@
if (!cu_->target64) {
// EDI is promotable in 32-bit mode.
NewLIR1(kX86Push32R, rs_rDI.GetReg());
+ cfi_.AdjustCFAOffset(4);
+ // Record cfi only if it is not already spilled.
+ if (!CoreSpillMaskContains(rs_rDI.GetReg())) {
+ cfi_.RelOffset(DwarfCoreReg(cu_->target64, rs_rDI.GetReg()), 0);
+ }
}
if (zero_based) {
@@ -1412,8 +1426,13 @@
// And join up at the end.
all_done->target = NewLIR0(kPseudoTargetLabel);
- if (!cu_->target64)
+ if (!cu_->target64) {
NewLIR1(kX86Pop32R, rs_rDI.GetReg());
+ cfi_.AdjustCFAOffset(-4);
+ if (!CoreSpillMaskContains(rs_rDI.GetReg())) {
+ cfi_.Restore(DwarfCoreReg(cu_->target64, rs_rDI.GetReg()));
+ }
+ }
// Out of line code returns here.
if (slowpath_branch != nullptr) {
@@ -1426,100 +1445,6 @@
return true;
}
-static bool ARTRegIDToDWARFRegID(bool is_x86_64, int art_reg_id, int* dwarf_reg_id) {
- if (is_x86_64) {
- switch (art_reg_id) {
- case 3 : *dwarf_reg_id = 3; return true; // %rbx
- // This is the only discrepancy between ART & DWARF register numbering.
- case 5 : *dwarf_reg_id = 6; return true; // %rbp
- case 12: *dwarf_reg_id = 12; return true; // %r12
- case 13: *dwarf_reg_id = 13; return true; // %r13
- case 14: *dwarf_reg_id = 14; return true; // %r14
- case 15: *dwarf_reg_id = 15; return true; // %r15
- default: return false; // Should not get here
- }
- } else {
- switch (art_reg_id) {
- case 5: *dwarf_reg_id = 5; return true; // %ebp
- case 6: *dwarf_reg_id = 6; return true; // %esi
- case 7: *dwarf_reg_id = 7; return true; // %edi
- default: return false; // Should not get here
- }
- }
-}
-
-std::vector<uint8_t>* X86Mir2Lir::ReturnFrameDescriptionEntry() {
- std::vector<uint8_t>* cfi_info = new std::vector<uint8_t>;
-
- // Generate the FDE for the method.
- DCHECK_NE(data_offset_, 0U);
-
- WriteFDEHeader(cfi_info, cu_->target64);
- WriteFDEAddressRange(cfi_info, data_offset_, cu_->target64);
-
- // The instructions in the FDE.
- if (stack_decrement_ != nullptr) {
- // Advance LOC to just past the stack decrement.
- uint32_t pc = NEXT_LIR(stack_decrement_)->offset;
- DW_CFA_advance_loc(cfi_info, pc);
-
- // Now update the offset to the call frame: DW_CFA_def_cfa_offset frame_size.
- DW_CFA_def_cfa_offset(cfi_info, frame_size_);
-
- // Handle register spills
- const uint32_t kSpillInstLen = (cu_->target64) ? 5 : 4;
- const int kDataAlignmentFactor = (cu_->target64) ? -8 : -4;
- uint32_t mask = core_spill_mask_ & ~(1 << rs_rRET.GetRegNum());
- int offset = -(GetInstructionSetPointerSize(cu_->instruction_set) * num_core_spills_);
- for (int reg = 0; mask; mask >>= 1, reg++) {
- if (mask & 0x1) {
- pc += kSpillInstLen;
-
- // Advance LOC to pass this instruction
- DW_CFA_advance_loc(cfi_info, kSpillInstLen);
-
- int dwarf_reg_id;
- if (ARTRegIDToDWARFRegID(cu_->target64, reg, &dwarf_reg_id)) {
- // DW_CFA_offset_extended_sf reg offset
- DW_CFA_offset_extended_sf(cfi_info, dwarf_reg_id, offset / kDataAlignmentFactor);
- }
-
- offset += GetInstructionSetPointerSize(cu_->instruction_set);
- }
- }
-
- // We continue with that stack until the epilogue.
- if (stack_increment_ != nullptr) {
- uint32_t new_pc = NEXT_LIR(stack_increment_)->offset;
- DW_CFA_advance_loc(cfi_info, new_pc - pc);
-
- // We probably have code snippets after the epilogue, so save the
- // current state: DW_CFA_remember_state.
- DW_CFA_remember_state(cfi_info);
-
- // We have now popped the stack: DW_CFA_def_cfa_offset 4/8.
- // There is only the return PC on the stack now.
- DW_CFA_def_cfa_offset(cfi_info, GetInstructionSetPointerSize(cu_->instruction_set));
-
- // Everything after that is the same as before the epilogue.
- // Stack bump was followed by RET instruction.
- LIR *post_ret_insn = NEXT_LIR(NEXT_LIR(stack_increment_));
- if (post_ret_insn != nullptr) {
- pc = new_pc;
- new_pc = post_ret_insn->offset;
- DW_CFA_advance_loc(cfi_info, new_pc - pc);
- // Restore the state: DW_CFA_restore_state.
- DW_CFA_restore_state(cfi_info);
- }
- }
- }
-
- PadCFI(cfi_info);
- WriteCFILength(cfi_info, cu_->target64);
-
- return cfi_info;
-}
-
void X86Mir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) {
switch (static_cast<ExtendedMIROpcode>(mir->dalvikInsn.opcode)) {
case kMirOpReserveVectorRegisters:
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 670efee..c2b8375 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -350,6 +350,7 @@
verification_results_(verification_results),
method_inliner_map_(method_inliner_map),
compiler_(Compiler::Create(this, compiler_kind)),
+ compiler_kind_(compiler_kind),
instruction_set_(instruction_set),
instruction_set_features_(instruction_set_features),
freezing_constructor_lock_("freezing constructor lock"),
@@ -2214,10 +2215,8 @@
InstructionSetHasGenericJniStub(instruction_set_)) {
// Leaving this empty will trigger the generic JNI version
} else {
- if (instruction_set_ != kMips64) { // Use generic JNI for Mips64 (temporarily).
- compiled_method = compiler_->JniCompile(access_flags, method_idx, dex_file);
- CHECK(compiled_method != nullptr);
- }
+ compiled_method = compiler_->JniCompile(access_flags, method_idx, dex_file);
+ CHECK(compiled_method != nullptr);
}
} else if ((access_flags & kAccAbstract) != 0) {
// Abstract methods don't have code.
@@ -2272,8 +2271,11 @@
DCHECK(GetCompiledMethod(method_ref) != nullptr) << PrettyMethod(method_idx, dex_file);
}
- // Done compiling, delete the verified method to reduce native memory usage.
- verification_results_->RemoveVerifiedMethod(method_ref);
+ // Done compiling, delete the verified method to reduce native memory usage. Do not delete in
+ // optimizing compiler, which may need the verified method again for inlining.
+ if (compiler_kind_ != Compiler::kOptimizing) {
+ verification_results_->RemoveVerifiedMethod(method_ref);
+ }
if (self->IsExceptionPending()) {
ScopedObjectAccess soa(self);
@@ -2368,44 +2370,6 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return compiler_->WriteElf(file, oat_writer, dex_files, android_root, is_host);
}
-void CompilerDriver::InstructionSetToLLVMTarget(InstructionSet instruction_set,
- std::string* target_triple,
- std::string* target_cpu,
- std::string* target_attr) {
- switch (instruction_set) {
- case kThumb2:
- *target_triple = "thumb-none-linux-gnueabi";
- *target_cpu = "cortex-a9";
- *target_attr = "+thumb2,+neon,+neonfp,+vfp3,+db";
- break;
-
- case kArm:
- *target_triple = "armv7-none-linux-gnueabi";
- // TODO: Fix for Nexus S.
- *target_cpu = "cortex-a9";
- // TODO: Fix for Xoom.
- *target_attr = "+v7,+neon,+neonfp,+vfp3,+db";
- break;
-
- case kX86:
- *target_triple = "i386-pc-linux-gnu";
- *target_attr = "";
- break;
-
- case kX86_64:
- *target_triple = "x86_64-pc-linux-gnu";
- *target_attr = "";
- break;
-
- case kMips:
- *target_triple = "mipsel-unknown-linux";
- *target_attr = "mips32r2";
- break;
-
- default:
- LOG(FATAL) << "Unknown instruction set: " << instruction_set;
- }
- }
bool CompilerDriver::SkipCompilation(const std::string& method_name) {
if (!profile_present_) {
@@ -2447,7 +2411,7 @@
gc::Heap* const heap = runtime->GetHeap();
oss << "arena alloc=" << PrettySize(arena_pool->GetBytesAllocated());
oss << " java alloc=" << PrettySize(heap->GetBytesAllocated());
-#ifdef HAVE_MALLOC_H
+#if defined(__BIONIC__) || defined(__GLIBC__)
struct mallinfo info = mallinfo();
const size_t allocated_space = static_cast<size_t>(info.uordblks);
const size_t free_space = static_cast<size_t>(info.fordblks);
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index efcaae4..a6ed559 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -385,12 +385,6 @@
OatWriter* oat_writer,
File* file);
- // TODO: move to a common home for llvm helpers once quick/portable are merged.
- static void InstructionSetToLLVMTarget(InstructionSet instruction_set,
- std::string* target_triple,
- std::string* target_cpu,
- std::string* target_attr);
-
void SetCompilerContext(void* compiler_context) {
compiler_context_ = compiler_context;
}
@@ -557,6 +551,7 @@
DexFileToMethodInlinerMap* const method_inliner_map_;
std::unique_ptr<Compiler> compiler_;
+ Compiler::Kind compiler_kind_;
const InstructionSet instruction_set_;
const InstructionSetFeatures* const instruction_set_features_;
diff --git a/compiler/dwarf/debug_frame_opcode_writer.h b/compiler/dwarf/debug_frame_opcode_writer.h
index cc4ef8f..85186bb 100644
--- a/compiler/dwarf/debug_frame_opcode_writer.h
+++ b/compiler/dwarf/debug_frame_opcode_writer.h
@@ -150,7 +150,7 @@
}
void RememberState() {
- // Note that we do not need to advance the PC.
+ ImplicitlyAdvancePC();
this->PushUint8(DW_CFA_remember_state);
}
@@ -236,6 +236,10 @@
this->PushData(expr, expr_size);
}
+ int GetCurrentPC() const {
+ return current_pc_;
+ }
+
int GetCurrentCFAOffset() const {
return current_cfa_offset_;
}
diff --git a/compiler/dwarf/debug_frame_writer.h b/compiler/dwarf/debug_frame_writer.h
index 6de45f5..b104cc9 100644
--- a/compiler/dwarf/debug_frame_writer.h
+++ b/compiler/dwarf/debug_frame_writer.h
@@ -33,8 +33,15 @@
int initial_opcodes_size) {
DCHECK(cie_header_start_ == ~0u);
cie_header_start_ = this->data()->size();
- this->PushUint32(0); // Length placeholder.
- this->PushUint32(0); // CIE id.
+ if (use_64bit_address_) {
+ // TODO: This is not related to being 64bit.
+ this->PushUint32(0xffffffff);
+ this->PushUint64(0); // Length placeholder.
+ this->PushUint64(0); // CIE id.
+ } else {
+ this->PushUint32(0); // Length placeholder.
+ this->PushUint32(0); // CIE id.
+ }
this->PushUint8(1); // Version.
this->PushString("zR");
this->PushUleb128(DebugFrameOpCodeWriter<Allocator>::kCodeAlignmentFactor);
@@ -48,7 +55,11 @@
}
this->PushData(initial_opcodes, initial_opcodes_size);
this->Pad(use_64bit_address_ ? 8 : 4);
- this->UpdateUint32(cie_header_start_, this->data()->size() - cie_header_start_ - 4);
+ if (use_64bit_address_) {
+ this->UpdateUint64(cie_header_start_ + 4, this->data()->size() - cie_header_start_ - 12);
+ } else {
+ this->UpdateUint32(cie_header_start_, this->data()->size() - cie_header_start_ - 4);
+ }
}
void WriteCIE(Reg return_address_register,
@@ -62,8 +73,15 @@
int unwind_opcodes_size) {
DCHECK(cie_header_start_ != ~0u);
size_t fde_header_start = this->data()->size();
- this->PushUint32(0); // Length placeholder.
- this->PushUint32(this->data()->size() - cie_header_start_); // 'CIE_pointer'
+ if (use_64bit_address_) {
+ // TODO: This is not related to being 64bit.
+ this->PushUint32(0xffffffff);
+ this->PushUint64(0); // Length placeholder.
+ this->PushUint64(this->data()->size() - cie_header_start_); // 'CIE_pointer'
+ } else {
+ this->PushUint32(0); // Length placeholder.
+ this->PushUint32(this->data()->size() - cie_header_start_); // 'CIE_pointer'
+ }
if (use_64bit_address_) {
this->PushUint64(initial_address);
this->PushUint64(address_range);
@@ -74,7 +92,11 @@
this->PushUleb128(0); // Augmentation data size.
this->PushData(unwind_opcodes, unwind_opcodes_size);
this->Pad(use_64bit_address_ ? 8 : 4);
- this->UpdateUint32(fde_header_start, this->data()->size() - fde_header_start - 4);
+ if (use_64bit_address_) {
+ this->UpdateUint64(fde_header_start + 4, this->data()->size() - fde_header_start - 12);
+ } else {
+ this->UpdateUint32(fde_header_start, this->data()->size() - fde_header_start - 4);
+ }
}
DebugFrameWriter(std::vector<uint8_t, Allocator>* buffer, bool use_64bit_address)
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
index f3553bc..2b051c9 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -127,7 +127,8 @@
CheckObjdumpOutput(is64bit, "-W");
}
-TEST_F(DwarfTest, DebugFrame64) {
+// TODO: objdump seems to have trouble with 64bit CIE length.
+TEST_F(DwarfTest, DISABLED_DebugFrame64) {
const bool is64bit = true;
DebugFrameWriter<> eh_frame(&eh_frame_data_, is64bit);
DebugFrameOpCodeWriter<> no_opcodes;
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 9ab3602..124ed03 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -40,6 +40,7 @@
section_.sh_addralign = align;
section_.sh_entsize = entsize;
}
+ ElfSectionBuilder(const ElfSectionBuilder&) = default;
~ElfSectionBuilder() {}
@@ -144,6 +145,7 @@
: ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, flags, link, info, align,
entsize) {
}
+ ElfRawSectionBuilder(const ElfRawSectionBuilder&) = default;
~ElfRawSectionBuilder() {}
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 3ce19ab..354c71e 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -89,114 +89,126 @@
return elf_writer.Write(oat_writer, dex_files, android_root, is_host);
}
-std::vector<uint8_t>* ConstructCIEFrameX86(bool is_x86_64) {
- std::vector<uint8_t>* cfi_info = new std::vector<uint8_t>;
-
- // Length (will be filled in later in this routine).
- if (is_x86_64) {
- Push32(cfi_info, 0xffffffff); // Indicates 64bit
- Push32(cfi_info, 0);
- Push32(cfi_info, 0);
- } else {
- Push32(cfi_info, 0);
- }
-
- // CIE id: always 0.
- if (is_x86_64) {
- Push32(cfi_info, 0);
- Push32(cfi_info, 0);
- } else {
- Push32(cfi_info, 0);
- }
-
- // Version: always 1.
- cfi_info->push_back(0x01);
-
- // Augmentation: 'zR\0'
- cfi_info->push_back(0x7a);
- cfi_info->push_back(0x52);
- cfi_info->push_back(0x0);
-
- // Code alignment: 1.
- EncodeUnsignedLeb128(1, cfi_info);
-
- // Data alignment.
- if (is_x86_64) {
- EncodeSignedLeb128(-8, cfi_info);
- } else {
- EncodeSignedLeb128(-4, cfi_info);
- }
-
- // Return address register.
- if (is_x86_64) {
- // R16(RIP)
- cfi_info->push_back(0x10);
- } else {
- // R8(EIP)
- cfi_info->push_back(0x08);
- }
-
- // Augmentation length: 1.
- cfi_info->push_back(1);
-
- // Augmentation data.
- if (is_x86_64) {
- // 0x04 ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata8).
- cfi_info->push_back(0x04);
- } else {
- // 0x03 ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4).
- cfi_info->push_back(0x03);
- }
-
- // Initial instructions.
- if (is_x86_64) {
- // DW_CFA_def_cfa R7(RSP) 8.
- cfi_info->push_back(0x0c);
- cfi_info->push_back(0x07);
- cfi_info->push_back(0x08);
-
- // DW_CFA_offset R16(RIP) 1 (* -8).
- cfi_info->push_back(0x90);
- cfi_info->push_back(0x01);
- } else {
- // DW_CFA_def_cfa R4(ESP) 4.
- cfi_info->push_back(0x0c);
- cfi_info->push_back(0x04);
- cfi_info->push_back(0x04);
-
- // DW_CFA_offset R8(EIP) 1 (* -4).
- cfi_info->push_back(0x88);
- cfi_info->push_back(0x01);
- }
-
- // Padding to a multiple of 4
- while ((cfi_info->size() & 3) != 0) {
- // DW_CFA_nop is encoded as 0.
- cfi_info->push_back(0);
- }
-
- // Set the length of the CIE inside the generated bytes.
- if (is_x86_64) {
- uint32_t length = cfi_info->size() - 12;
- UpdateWord(cfi_info, 4, length);
- } else {
- uint32_t length = cfi_info->size() - 4;
- UpdateWord(cfi_info, 0, length);
- }
- return cfi_info;
-}
-
-std::vector<uint8_t>* ConstructCIEFrame(InstructionSet isa) {
+void WriteCIE(dwarf::DebugFrameWriter<>* cfi, InstructionSet isa) {
+ // Scratch registers should be marked as undefined. This tells the
+ // debugger that its value in the previous frame is not recoverable.
switch (isa) {
- case kX86:
- return ConstructCIEFrameX86(false);
- case kX86_64:
- return ConstructCIEFrameX86(true);
-
- default:
- // Not implemented.
- return nullptr;
+ case kArm:
+ case kThumb2: {
+ dwarf::DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(dwarf::Reg::ArmCore(13), 0); // R13(SP).
+ // core registers.
+ for (int reg = 0; reg < 13; reg++) {
+ if (reg < 4 || reg == 12) {
+ opcodes.Undefined(dwarf::Reg::ArmCore(reg));
+ } else {
+ opcodes.SameValue(dwarf::Reg::ArmCore(reg));
+ }
+ }
+ // fp registers.
+ for (int reg = 0; reg < 32; reg++) {
+ if (reg < 16) {
+ opcodes.Undefined(dwarf::Reg::ArmFp(reg));
+ } else {
+ opcodes.SameValue(dwarf::Reg::ArmFp(reg));
+ }
+ }
+ auto return_address_reg = dwarf::Reg::ArmCore(14); // R14(LR).
+ cfi->WriteCIE(return_address_reg, opcodes);
+ return;
+ }
+ case kArm64: {
+ dwarf::DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(dwarf::Reg::Arm64Core(31), 0); // R31(SP).
+ // core registers.
+ for (int reg = 0; reg < 30; reg++) {
+ if (reg < 8 || reg == 16 || reg == 17) {
+ opcodes.Undefined(dwarf::Reg::Arm64Core(reg));
+ } else {
+ opcodes.SameValue(dwarf::Reg::Arm64Core(reg));
+ }
+ }
+ // fp registers.
+ for (int reg = 0; reg < 32; reg++) {
+ if (reg < 8 || reg >= 16) {
+ opcodes.Undefined(dwarf::Reg::Arm64Fp(reg));
+ } else {
+ opcodes.SameValue(dwarf::Reg::Arm64Fp(reg));
+ }
+ }
+ auto return_address_reg = dwarf::Reg::Arm64Core(30); // R30(LR).
+ cfi->WriteCIE(return_address_reg, opcodes);
+ return;
+ }
+ case kMips:
+ case kMips64: {
+ dwarf::DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(dwarf::Reg::MipsCore(29), 0); // R29(SP).
+ // core registers.
+ for (int reg = 1; reg < 26; reg++) {
+ if (reg < 16 || reg == 24 || reg == 25) { // AT, V*, A*, T*.
+ opcodes.Undefined(dwarf::Reg::MipsCore(reg));
+ } else {
+ opcodes.SameValue(dwarf::Reg::MipsCore(reg));
+ }
+ }
+ auto return_address_reg = dwarf::Reg::MipsCore(31); // R31(RA).
+ cfi->WriteCIE(return_address_reg, opcodes);
+ return;
+ }
+ case kX86: {
+ dwarf::DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(dwarf::Reg::X86Core(4), 4); // R4(ESP).
+ opcodes.Offset(dwarf::Reg::X86Core(8), -4); // R8(EIP).
+ // core registers.
+ for (int reg = 0; reg < 8; reg++) {
+ if (reg <= 3) {
+ opcodes.Undefined(dwarf::Reg::X86Core(reg));
+ } else if (reg == 4) {
+ // Stack pointer.
+ } else {
+ opcodes.SameValue(dwarf::Reg::X86Core(reg));
+ }
+ }
+ // fp registers.
+ for (int reg = 0; reg < 8; reg++) {
+ opcodes.Undefined(dwarf::Reg::X86Fp(reg));
+ }
+ auto return_address_reg = dwarf::Reg::X86Core(8); // R8(EIP).
+ cfi->WriteCIE(return_address_reg, opcodes);
+ return;
+ }
+ case kX86_64: {
+ dwarf::DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(dwarf::Reg::X86_64Core(4), 8); // R4(RSP).
+ opcodes.Offset(dwarf::Reg::X86_64Core(16), -8); // R16(RIP).
+ // core registers.
+ for (int reg = 0; reg < 16; reg++) {
+ if (reg == 4) {
+ // Stack pointer.
+ } else if (reg < 12 && reg != 3 && reg != 5) { // except EBX and EBP.
+ opcodes.Undefined(dwarf::Reg::X86_64Core(reg));
+ } else {
+ opcodes.SameValue(dwarf::Reg::X86_64Core(reg));
+ }
+ }
+ // fp registers.
+ for (int reg = 0; reg < 16; reg++) {
+ if (reg < 12) {
+ opcodes.Undefined(dwarf::Reg::X86_64Fp(reg));
+ } else {
+ opcodes.SameValue(dwarf::Reg::X86_64Fp(reg));
+ }
+ }
+ auto return_address_reg = dwarf::Reg::X86_64Core(16); // R16(RIP).
+ cfi->WriteCIE(return_address_reg, opcodes);
+ return;
+ }
+ case kNone:
+ break;
}
+ LOG(FATAL) << "Can not write CIE frame for ISA " << isa;
+ UNREACHABLE();
}
class OatWriterWrapper FINAL : public CodeOutput {
@@ -490,14 +502,11 @@
int code_factor_bits_ = 0;
int isa = -1;
switch (oat_writer->GetOatHeader().GetInstructionSet()) {
+ case kArm: // arm actually means thumb2.
case kThumb2:
code_factor_bits_ = 1; // 16-bit instuctions
isa = 1; // DW_ISA_ARM_thumb.
break;
- case kArm:
- code_factor_bits_ = 1; // 16-bit instructions
- isa = 2; // DW_ISA_ARM_arm.
- break;
case kArm64:
case kMips:
case kMips64:
@@ -624,8 +633,10 @@
ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
OatWriter* oat_writer) {
- std::unique_ptr<std::vector<uint8_t>> cfi_info(
- ConstructCIEFrame(compiler_driver->GetInstructionSet()));
+ std::vector<uint8_t> cfi_data;
+ bool is_64bit = Is64BitInstructionSet(compiler_driver->GetInstructionSet());
+ dwarf::DebugFrameWriter<> cfi(&cfi_data, is_64bit);
+ WriteCIE(&cfi, compiler_driver->GetInstructionSet());
Elf_Addr text_section_address = builder->GetTextBuilder().GetSection()->sh_addr;
@@ -647,62 +658,17 @@
0, STB_LOCAL, STT_NOTYPE);
}
- // Include CFI for compiled method, if possible.
- if (cfi_info.get() != nullptr) {
- DCHECK(it->compiled_method_ != nullptr);
-
- // Copy in the FDE, if present
- const SwapVector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
- if (fde != nullptr) {
- // Copy the information into cfi_info and then fix the address in the new copy.
- int cur_offset = cfi_info->size();
- cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
-
- bool is_64bit = *(reinterpret_cast<const uint32_t*>(fde->data())) == 0xffffffff;
-
- // Set the 'CIE_pointer' field.
- uint64_t CIE_pointer = cur_offset + (is_64bit ? 12 : 4);
- uint64_t offset_to_update = CIE_pointer;
- if (is_64bit) {
- (*cfi_info)[offset_to_update+0] = CIE_pointer;
- (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
- (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
- (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
- (*cfi_info)[offset_to_update+4] = CIE_pointer >> 32;
- (*cfi_info)[offset_to_update+5] = CIE_pointer >> 40;
- (*cfi_info)[offset_to_update+6] = CIE_pointer >> 48;
- (*cfi_info)[offset_to_update+7] = CIE_pointer >> 56;
- } else {
- (*cfi_info)[offset_to_update+0] = CIE_pointer;
- (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
- (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
- (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
- }
-
- // Set the 'initial_location' field.
- offset_to_update += is_64bit ? 8 : 4;
- if (is_64bit) {
- const uint64_t quick_code_start = it->low_pc_ + text_section_address;
- (*cfi_info)[offset_to_update+0] = quick_code_start;
- (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
- (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
- (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
- (*cfi_info)[offset_to_update+4] = quick_code_start >> 32;
- (*cfi_info)[offset_to_update+5] = quick_code_start >> 40;
- (*cfi_info)[offset_to_update+6] = quick_code_start >> 48;
- (*cfi_info)[offset_to_update+7] = quick_code_start >> 56;
- } else {
- const uint32_t quick_code_start = it->low_pc_ + text_section_address;
- (*cfi_info)[offset_to_update+0] = quick_code_start;
- (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
- (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
- (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
- }
- }
+ // Include FDE for compiled method, if possible.
+ DCHECK(it->compiled_method_ != nullptr);
+ const SwapVector<uint8_t>* unwind_opcodes = it->compiled_method_->GetCFIInfo();
+ if (unwind_opcodes != nullptr) {
+ // TUNING: The headers take a lot of space. Can we have 1 FDE per file?
+ // TUNING: Some tools support compressed DWARF sections (.zdebug_*).
+ cfi.WriteFDE(text_section_address + it->low_pc_, it->high_pc_ - it->low_pc_,
+ unwind_opcodes->data(), unwind_opcodes->size());
}
}
- bool hasCFI = (cfi_info.get() != nullptr);
bool hasLineInfo = false;
for (auto& dbg_info : oat_writer->GetCFIMethodInfo()) {
if (dbg_info.dbgstream_ != nullptr &&
@@ -712,7 +678,8 @@
}
}
- if (hasLineInfo || hasCFI) {
+ if (!method_info.empty() &&
+ compiler_driver->GetCompilerOptions().GetGenerateGDBInformation()) {
ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_info(".debug_info",
SHT_PROGBITS,
0, nullptr, 0, 1, 0);
@@ -734,14 +701,12 @@
builder->RegisterRawSection(debug_info);
builder->RegisterRawSection(debug_abbrev);
- if (hasCFI) {
- ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> eh_frame(".eh_frame",
- SHT_PROGBITS,
- SHF_ALLOC,
- nullptr, 0, 4, 0);
- eh_frame.SetBuffer(std::move(*cfi_info.get()));
- builder->RegisterRawSection(eh_frame);
- }
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> eh_frame(".eh_frame",
+ SHT_PROGBITS,
+ SHF_ALLOC,
+ nullptr, 0, 4, 0);
+ eh_frame.SetBuffer(std::move(cfi_data));
+ builder->RegisterRawSection(eh_frame);
if (hasLineInfo) {
builder->RegisterRawSection(debug_line);
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 2d9e03a..45e2fd0 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -93,7 +93,6 @@
// Assembler that holds generated instructions
std::unique_ptr<Assembler> jni_asm(Assembler::Create(instruction_set));
- jni_asm->InitializeFrameDescriptionEntry();
// Offsets into data structures
// TODO: if cross compiling these offsets are for the host not the target
@@ -432,19 +431,14 @@
std::vector<uint8_t> managed_code(cs);
MemoryRegion code(&managed_code[0], managed_code.size());
__ FinalizeInstructions(code);
- jni_asm->FinalizeFrameDescriptionEntry();
- std::vector<uint8_t>* fde(jni_asm->GetFrameDescriptionEntry());
- ArrayRef<const uint8_t> cfi_ref;
- if (fde != nullptr) {
- cfi_ref = ArrayRef<const uint8_t>(*fde);
- }
+
return CompiledMethod::SwapAllocCompiledMethodCFI(driver,
instruction_set,
ArrayRef<const uint8_t>(managed_code),
frame_size,
main_jni_conv->CoreSpillMask(),
main_jni_conv->FpSpillMask(),
- cfi_ref);
+ ArrayRef<const uint8_t>());
}
// Copy a single parameter from the managed to the JNI calling convention
diff --git a/compiler/jni/quick/mips64/calling_convention_mips64.cc b/compiler/jni/quick/mips64/calling_convention_mips64.cc
index 17325d6..d446867 100644
--- a/compiler/jni/quick/mips64/calling_convention_mips64.cc
+++ b/compiler/jni/quick/mips64/calling_convention_mips64.cc
@@ -126,25 +126,20 @@
Mips64JniCallingConvention::Mips64JniCallingConvention(bool is_static, bool is_synchronized,
const char* shorty)
: JniCallingConvention(is_static, is_synchronized, shorty, kFramePointerSize) {
- callee_save_regs_.push_back(Mips64ManagedRegister::FromGpuRegister(S0));
- callee_save_regs_.push_back(Mips64ManagedRegister::FromGpuRegister(S1));
callee_save_regs_.push_back(Mips64ManagedRegister::FromGpuRegister(S2));
callee_save_regs_.push_back(Mips64ManagedRegister::FromGpuRegister(S3));
callee_save_regs_.push_back(Mips64ManagedRegister::FromGpuRegister(S4));
callee_save_regs_.push_back(Mips64ManagedRegister::FromGpuRegister(S5));
callee_save_regs_.push_back(Mips64ManagedRegister::FromGpuRegister(S6));
callee_save_regs_.push_back(Mips64ManagedRegister::FromGpuRegister(S7));
-
callee_save_regs_.push_back(Mips64ManagedRegister::FromGpuRegister(GP));
- callee_save_regs_.push_back(Mips64ManagedRegister::FromGpuRegister(SP));
callee_save_regs_.push_back(Mips64ManagedRegister::FromGpuRegister(S8));
}
uint32_t Mips64JniCallingConvention::CoreSpillMask() const {
// Compute spill mask to agree with callee saves initialized in the constructor
uint32_t result = 0;
- result = 1 << S0 | 1 << S1 | 1 << S2 | 1 << S3 | 1 << S4 | 1 << S5 | 1 << S6 |
- 1 << S7 | 1 << GP | 1 << SP | 1 << S8;
+ result = 1 << S2 | 1 << S3 | 1 << S4 | 1 << S5 | 1 << S6 | 1 << S7 | 1 << GP | 1 << S8 | 1 << RA;
return result;
}
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
index 4267743..b17cbca 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -48,22 +48,30 @@
uint32_t value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | imm11;
value |= 0xf000d000; // BL
- uint8_t* addr = &(*code)[literal_offset];
// Check that we're just overwriting an existing BL.
- DCHECK_EQ(addr[1] & 0xf8, 0xf0);
- DCHECK_EQ(addr[3] & 0xd0, 0xd0);
+ DCHECK_EQ(GetInsn32(code, literal_offset) & 0xf800d000, 0xf000d000);
// Write the new BL.
- addr[0] = (value >> 16) & 0xff;
- addr[1] = (value >> 24) & 0xff;
- addr[2] = (value >> 0) & 0xff;
- addr[3] = (value >> 8) & 0xff;
+ SetInsn32(code, literal_offset, value);
}
-void Thumb2RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
- const LinkerPatch& patch ATTRIBUTE_UNUSED,
- uint32_t patch_offset ATTRIBUTE_UNUSED,
- uint32_t target_offset ATTRIBUTE_UNUSED) {
- LOG(FATAL) << "Unexpected relative dex cache array patch.";
+void Thumb2RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
+ uint32_t literal_offset = patch.LiteralOffset();
+ uint32_t pc_literal_offset = patch.PcInsnOffset();
+ uint32_t pc_base = patch_offset + (pc_literal_offset - literal_offset) + 4u /* PC adjustment */;
+ uint32_t diff = target_offset - pc_base;
+
+ uint32_t insn = GetInsn32(code, literal_offset);
+ DCHECK_EQ(insn & 0xff7ff0ffu, 0xf2400000u); // MOVW/MOVT, unpatched (imm16 == 0).
+ uint32_t diff16 = ((insn & 0x00800000u) != 0u) ? (diff >> 16) : (diff & 0xffffu);
+ uint32_t imm4 = (diff16 >> 12) & 0xfu;
+ uint32_t imm = (diff16 >> 11) & 0x1u;
+ uint32_t imm3 = (diff16 >> 8) & 0x7u;
+ uint32_t imm8 = diff16 & 0xffu;
+ insn = (insn & 0xfbf08f00u) | (imm << 26) | (imm4 << 16) | (imm3 << 12) | imm8;
+ SetInsn32(code, literal_offset, insn);
}
std::vector<uint8_t> Thumb2RelativePatcher::CompileThunkCode() {
@@ -80,5 +88,31 @@
return thunk_code;
}
+void Thumb2RelativePatcher::SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value) {
+ DCHECK_LE(offset + 4u, code->size());
+ DCHECK_EQ(offset & 1u, 0u);
+ uint8_t* addr = &(*code)[offset];
+ addr[0] = (value >> 16) & 0xff;
+ addr[1] = (value >> 24) & 0xff;
+ addr[2] = (value >> 0) & 0xff;
+ addr[3] = (value >> 8) & 0xff;
+}
+
+uint32_t Thumb2RelativePatcher::GetInsn32(ArrayRef<const uint8_t> code, uint32_t offset) {
+ DCHECK_LE(offset + 4u, code.size());
+ DCHECK_EQ(offset & 1u, 0u);
+ const uint8_t* addr = &code[offset];
+ return
+ (static_cast<uint32_t>(addr[0]) << 16) +
+ (static_cast<uint32_t>(addr[1]) << 24) +
+ (static_cast<uint32_t>(addr[2]) << 0)+
+ (static_cast<uint32_t>(addr[3]) << 8);
+}
+
+template <typename Alloc>
+uint32_t Thumb2RelativePatcher::GetInsn32(std::vector<uint8_t, Alloc>* code, uint32_t offset) {
+ return GetInsn32(ArrayRef<const uint8_t>(*code), offset);
+}
+
} // namespace linker
} // namespace art
diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h
index 5611303..2d474c2 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.h
+++ b/compiler/linker/arm/relative_patcher_thumb2.h
@@ -34,6 +34,12 @@
private:
static std::vector<uint8_t> CompileThunkCode();
+ void SetInsn32(std::vector<uint8_t>* code, uint32_t offset, uint32_t value);
+ static uint32_t GetInsn32(ArrayRef<const uint8_t> code, uint32_t offset);
+
+ template <typename Alloc>
+ static uint32_t GetInsn32(std::vector<uint8_t, Alloc>* code, uint32_t offset);
+
// PC displacement from patch location; Thumb2 PC is always at instruction address + 4.
static constexpr int32_t kPcDisplacement = 4;
diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc
index abdfd6d..a057a4c 100644
--- a/compiler/linker/arm/relative_patcher_thumb2_test.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc
@@ -39,14 +39,14 @@
static constexpr uint32_t kBlMinusMax = 0xf400d000;
bool Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
- const ArrayRef<LinkerPatch>& method1_patches,
+ const ArrayRef<const LinkerPatch>& method1_patches,
const ArrayRef<const uint8_t>& method3_code,
- const ArrayRef<LinkerPatch>& method3_patches,
+ const ArrayRef<const LinkerPatch>& method3_patches,
uint32_t distance_without_thunks) {
CHECK_EQ(distance_without_thunks % kArmAlignment, 0u);
const uint32_t method1_offset =
CompiledCode::AlignCode(kTrampolineSize, kThumb2) + sizeof(OatQuickMethodHeader);
- AddCompiledMethod(MethodRef(1u), method1_code, ArrayRef<LinkerPatch>(method1_patches));
+ AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
// We want to put the method3 at a very precise offset.
const uint32_t method3_offset = method1_offset + distance_without_thunks;
@@ -59,7 +59,7 @@
const uint32_t method2_size = (method3_offset - sizeof(OatQuickMethodHeader) - method2_offset);
std::vector<uint8_t> method2_raw_code(method2_size);
ArrayRef<const uint8_t> method2_code(method2_raw_code);
- AddCompiledMethod(MethodRef(2u), method2_code, ArrayRef<LinkerPatch>());
+ AddCompiledMethod(MethodRef(2u), method2_code, ArrayRef<const LinkerPatch>());
AddCompiledMethod(MethodRef(3u), method3_code, method3_patches);
@@ -121,6 +121,48 @@
result.push_back(static_cast<uint8_t>(bl >> 8));
return result;
}
+
+ void TestDexCachereference(uint32_t dex_cache_arrays_begin, uint32_t element_offset) {
+ dex_cache_arrays_begin_ = dex_cache_arrays_begin;
+ static const uint8_t raw_code[] = {
+ 0x40, 0xf2, 0x00, 0x00, // MOVW r0, #0 (placeholder)
+ 0xc0, 0xf2, 0x00, 0x00, // MOVT r0, #0 (placeholder)
+ 0x78, 0x44, // ADD r0, pc
+ };
+ constexpr uint32_t pc_insn_offset = 8u;
+ const ArrayRef<const uint8_t> code(raw_code);
+ LinkerPatch patches[] = {
+ LinkerPatch::DexCacheArrayPatch(0u, nullptr, pc_insn_offset, element_offset),
+ LinkerPatch::DexCacheArrayPatch(4u, nullptr, pc_insn_offset, element_offset),
+ };
+ AddCompiledMethod(MethodRef(1u), code, ArrayRef<const LinkerPatch>(patches));
+ Link();
+
+ uint32_t method1_offset = GetMethodOffset(1u);
+ uint32_t pc_base_offset = method1_offset + pc_insn_offset + 4u /* PC adjustment */;
+ uint32_t diff = dex_cache_arrays_begin_ + element_offset - pc_base_offset;
+ // Distribute the bits of the diff between the MOVW and MOVT:
+ uint32_t diffw = diff & 0xffffu;
+ uint32_t difft = diff >> 16;
+ uint32_t movw = 0xf2400000u | // MOVW r0, #0 (placeholder),
+ ((diffw & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19,
+ ((diffw & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26,
+ ((diffw & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14,
+ ((diffw & 0x00ffu)); // keep imm8 at bits 0-7.
+ uint32_t movt = 0xf2c00000u | // MOVT r0, #0 (placeholder),
+ ((difft & 0xf000u) << (16 - 12)) | // move imm4 from bits 12-15 to bits 16-19,
+ ((difft & 0x0800u) << (26 - 11)) | // move imm from bit 11 to bit 26,
+ ((difft & 0x0700u) << (12 - 8)) | // move imm3 from bits 8-10 to bits 12-14,
+ ((difft & 0x00ffu)); // keep imm8 at bits 0-7.
+ const uint8_t expected_code[] = {
+ static_cast<uint8_t>(movw >> 16), static_cast<uint8_t>(movw >> 24),
+ static_cast<uint8_t>(movw >> 0), static_cast<uint8_t>(movw >> 8),
+ static_cast<uint8_t>(movt >> 16), static_cast<uint8_t>(movt >> 24),
+ static_cast<uint8_t>(movt >> 0), static_cast<uint8_t>(movt >> 8),
+ 0x78, 0x44,
+ };
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
+ }
};
const uint8_t Thumb2RelativePatcherTest::kCallRawCode[] = {
@@ -139,7 +181,7 @@
LinkerPatch patches[] = {
LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
};
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
Link();
static const uint8_t expected_code[] = {
@@ -152,11 +194,11 @@
LinkerPatch method1_patches[] = {
LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
};
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<LinkerPatch>(method1_patches));
+ AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
LinkerPatch method2_patches[] = {
LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
};
- AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<LinkerPatch>(method2_patches));
+ AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
Link();
uint32_t method1_offset = GetMethodOffset(1u);
@@ -179,7 +221,7 @@
LinkerPatch patches[] = {
LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
};
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
Link();
uint32_t method1_offset = GetMethodOffset(1u);
@@ -201,7 +243,7 @@
constexpr uint32_t max_positive_disp = 16 * MB - 2u + 4u /* PC adjustment */;
bool thunk_in_gap = Create2MethodsWithGap(method1_code, method1_patches,
- kNopCode, ArrayRef<LinkerPatch>(),
+ kNopCode, ArrayRef<const LinkerPatch>(),
bl_offset_in_method1 + max_positive_disp);
ASSERT_FALSE(thunk_in_gap); // There should be no thunk.
@@ -220,7 +262,7 @@
};
constexpr uint32_t just_over_max_negative_disp = 16 * MB - 4u /* PC adjustment */;
- bool thunk_in_gap = Create2MethodsWithGap(kNopCode, ArrayRef<LinkerPatch>(),
+ bool thunk_in_gap = Create2MethodsWithGap(kNopCode, ArrayRef<const LinkerPatch>(),
method3_code, method3_patches,
just_over_max_negative_disp - bl_offset_in_method3);
ASSERT_FALSE(thunk_in_gap); // There should be no thunk.
@@ -241,7 +283,7 @@
constexpr uint32_t just_over_max_positive_disp = 16 * MB + 4u /* PC adjustment */;
bool thunk_in_gap = Create2MethodsWithGap(method1_code, method1_patches,
- kNopCode, ArrayRef<LinkerPatch>(),
+ kNopCode, ArrayRef<const LinkerPatch>(),
bl_offset_in_method1 + just_over_max_positive_disp);
ASSERT_TRUE(thunk_in_gap);
@@ -269,7 +311,7 @@
};
constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
- bool thunk_in_gap = Create2MethodsWithGap(kNopCode, ArrayRef<LinkerPatch>(),
+ bool thunk_in_gap = Create2MethodsWithGap(kNopCode, ArrayRef<const LinkerPatch>(),
method3_code, method3_patches,
just_over_max_negative_disp - bl_offset_in_method3);
ASSERT_FALSE(thunk_in_gap); // There should be a thunk but it should be after the method2.
@@ -285,5 +327,25 @@
EXPECT_TRUE(CheckThunk(thunk_offset));
}
+TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm8) {
+ TestDexCachereference(0x00ff0000u, 0x00fcu);
+ ASSERT_LT(GetMethodOffset(1u), 0xfcu);
+}
+
+TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm3) {
+ TestDexCachereference(0x02ff0000u, 0x05fcu);
+ ASSERT_LT(GetMethodOffset(1u), 0xfcu);
+}
+
+TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceImm) {
+ TestDexCachereference(0x08ff0000u, 0x08fcu);
+ ASSERT_LT(GetMethodOffset(1u), 0xfcu);
+}
+
+TEST_F(Thumb2RelativePatcherTest, DexCacheReferenceimm4) {
+ TestDexCachereference(0xd0ff0000u, 0x60fcu);
+ ASSERT_LT(GetMethodOffset(1u), 0xfcu);
+}
+
} // namespace linker
} // namespace art
diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc
index b039936..b36e6d0 100644
--- a/compiler/linker/arm64/relative_patcher_arm64_test.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64_test.cc
@@ -43,14 +43,14 @@
static constexpr uint32_t kLdurInsn = 0xf840405fu;
uint32_t Create2MethodsWithGap(const ArrayRef<const uint8_t>& method1_code,
- const ArrayRef<LinkerPatch>& method1_patches,
+ const ArrayRef<const LinkerPatch>& method1_patches,
const ArrayRef<const uint8_t>& last_method_code,
- const ArrayRef<LinkerPatch>& last_method_patches,
+ const ArrayRef<const LinkerPatch>& last_method_patches,
uint32_t distance_without_thunks) {
CHECK_EQ(distance_without_thunks % kArm64Alignment, 0u);
const uint32_t method1_offset =
CompiledCode::AlignCode(kTrampolineSize, kArm64) + sizeof(OatQuickMethodHeader);
- AddCompiledMethod(MethodRef(1u), method1_code, ArrayRef<LinkerPatch>(method1_patches));
+ AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
const uint32_t gap_start =
CompiledCode::AlignCode(method1_offset + method1_code.size(), kArm64);
@@ -70,13 +70,13 @@
uint32_t chunk_code_size = kSmallChunkSize - sizeof(OatQuickMethodHeader);
gap_code.resize(chunk_code_size, 0u);
AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(gap_code),
- ArrayRef<LinkerPatch>());
+ ArrayRef<const LinkerPatch>());
method_idx += 1u;
}
uint32_t chunk_code_size = gap_size - sizeof(OatQuickMethodHeader);
gap_code.resize(chunk_code_size, 0u);
AddCompiledMethod(MethodRef(method_idx), ArrayRef<const uint8_t>(gap_code),
- ArrayRef<LinkerPatch>());
+ ArrayRef<const LinkerPatch>());
method_idx += 1u;
// Add the last method and link
@@ -174,7 +174,8 @@
LinkerPatch::DexCacheArrayPatch(num_nops * 4u , nullptr, num_nops * 4u, element_offset),
LinkerPatch::DexCacheArrayPatch(num_nops * 4u + 4u, nullptr, num_nops * 4u, element_offset),
};
- AddCompiledMethod(MethodRef(1u), ArrayRef<const uint8_t>(code), ArrayRef<LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), ArrayRef<const uint8_t>(code),
+ ArrayRef<const LinkerPatch>(patches));
Link();
uint32_t method1_offset = GetMethodOffset(1u);
@@ -202,7 +203,8 @@
LinkerPatch::DexCacheArrayPatch(num_nops * 4u , nullptr, num_nops * 4u, element_offset),
LinkerPatch::DexCacheArrayPatch(num_nops * 4u + 8u, nullptr, num_nops * 4u, element_offset),
};
- AddCompiledMethod(MethodRef(1u), ArrayRef<const uint8_t>(code), ArrayRef<LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), ArrayRef<const uint8_t>(code),
+ ArrayRef<const LinkerPatch>(patches));
Link();
}
@@ -300,7 +302,7 @@
LinkerPatch patches[] = {
LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
};
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
Link();
static const uint8_t expected_code[] = {
@@ -313,11 +315,11 @@
LinkerPatch method1_patches[] = {
LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
};
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<LinkerPatch>(method1_patches));
+ AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
LinkerPatch method2_patches[] = {
LinkerPatch::RelativeCodePatch(0u, nullptr, 1u),
};
- AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<LinkerPatch>(method2_patches));
+ AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
Link();
uint32_t method1_offset = GetMethodOffset(1u);
@@ -340,7 +342,7 @@
LinkerPatch patches[] = {
LinkerPatch::RelativeCodePatch(0u, nullptr, 2u),
};
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
Link();
uint32_t method1_offset = GetMethodOffset(1u);
@@ -363,7 +365,7 @@
constexpr uint32_t max_positive_disp = 128 * MB - 4u;
uint32_t last_method_idx = Create2MethodsWithGap(method1_code, method1_patches,
- kNopCode, ArrayRef<LinkerPatch>(),
+ kNopCode, ArrayRef<const LinkerPatch>(),
bl_offset_in_method1 + max_positive_disp);
ASSERT_EQ(expected_last_method_idx, last_method_idx);
@@ -386,7 +388,7 @@
};
constexpr uint32_t max_negative_disp = 128 * MB;
- uint32_t last_method_idx = Create2MethodsWithGap(kNopCode, ArrayRef<LinkerPatch>(),
+ uint32_t last_method_idx = Create2MethodsWithGap(kNopCode, ArrayRef<const LinkerPatch>(),
last_method_code, last_method_patches,
max_negative_disp - bl_offset_in_last_method);
uint32_t method1_offset = GetMethodOffset(1u);
@@ -411,7 +413,7 @@
constexpr uint32_t just_over_max_positive_disp = 128 * MB;
uint32_t last_method_idx = Create2MethodsWithGap(
- method1_code, method1_patches, kNopCode, ArrayRef<LinkerPatch>(),
+ method1_code, method1_patches, kNopCode, ArrayRef<const LinkerPatch>(),
bl_offset_in_method1 + just_over_max_positive_disp);
ASSERT_EQ(expected_last_method_idx, last_method_idx);
@@ -440,7 +442,7 @@
constexpr uint32_t just_over_max_negative_disp = 128 * MB + 4;
uint32_t last_method_idx = Create2MethodsWithGap(
- kNopCode, ArrayRef<LinkerPatch>(), last_method_code, last_method_patches,
+ kNopCode, ArrayRef<const LinkerPatch>(), last_method_code, last_method_patches,
just_over_max_negative_disp - bl_offset_in_last_method);
uint32_t method1_offset = GetMethodOffset(1u);
uint32_t last_method_offset = GetMethodOffset(last_method_idx);
diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc
index 8ee87aa..89aed95 100644
--- a/compiler/linker/relative_patcher.cc
+++ b/compiler/linker/relative_patcher.cc
@@ -67,22 +67,17 @@
switch (instruction_set) {
case kX86:
return std::unique_ptr<RelativePatcher>(new X86RelativePatcher());
- break;
case kX86_64:
return std::unique_ptr<RelativePatcher>(new X86_64RelativePatcher());
- break;
case kArm:
// Fall through: we generate Thumb2 code for "arm".
case kThumb2:
return std::unique_ptr<RelativePatcher>(new Thumb2RelativePatcher(provider));
- break;
case kArm64:
return std::unique_ptr<RelativePatcher>(
new Arm64RelativePatcher(provider, features->AsArm64InstructionSetFeatures()));
- break;
default:
return std::unique_ptr<RelativePatcher>(new RelativePatcherNone);
- break;
}
}
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index 08167b3..70630f3 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -69,7 +69,7 @@
void AddCompiledMethod(MethodReference method_ref,
const ArrayRef<const uint8_t>& code,
- const ArrayRef<LinkerPatch>& patches) {
+ const ArrayRef<const LinkerPatch>& patches) {
compiled_method_refs_.push_back(method_ref);
compiled_methods_.emplace_back(new CompiledMethod(
&driver_, instruction_set_, code,
diff --git a/compiler/linker/x86/relative_patcher_x86_test.cc b/compiler/linker/x86/relative_patcher_x86_test.cc
index c18a743..15ac47e 100644
--- a/compiler/linker/x86/relative_patcher_x86_test.cc
+++ b/compiler/linker/x86/relative_patcher_x86_test.cc
@@ -45,7 +45,7 @@
LinkerPatch patches[] = {
LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u),
};
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
Link();
static const uint8_t expected_code[] = {
@@ -58,11 +58,11 @@
LinkerPatch method1_patches[] = {
LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 2u),
};
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<LinkerPatch>(method1_patches));
+ AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
LinkerPatch method2_patches[] = {
LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u),
};
- AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<LinkerPatch>(method2_patches));
+ AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
Link();
uint32_t method1_offset = GetMethodOffset(1u);
@@ -87,7 +87,7 @@
LinkerPatch patches[] = {
LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 2u),
};
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
Link();
auto result = method_offset_map_.FindMethodOffset(MethodRef(1));
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64_test.cc b/compiler/linker/x86_64/relative_patcher_x86_64_test.cc
index 9d9529c..36e0f01 100644
--- a/compiler/linker/x86_64/relative_patcher_x86_64_test.cc
+++ b/compiler/linker/x86_64/relative_patcher_x86_64_test.cc
@@ -55,7 +55,7 @@
LinkerPatch patches[] = {
LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u),
};
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
Link();
static const uint8_t expected_code[] = {
@@ -68,11 +68,11 @@
LinkerPatch method1_patches[] = {
LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 2u),
};
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<LinkerPatch>(method1_patches));
+ AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(method1_patches));
LinkerPatch method2_patches[] = {
LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 1u),
};
- AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<LinkerPatch>(method2_patches));
+ AddCompiledMethod(MethodRef(2u), kCallCode, ArrayRef<const LinkerPatch>(method2_patches));
Link();
uint32_t method1_offset = GetMethodOffset(1u);
@@ -97,7 +97,7 @@
LinkerPatch patches[] = {
LinkerPatch::RelativeCodePatch(kCallCode.size() - 4u, nullptr, 2u),
};
- AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), kCallCode, ArrayRef<const LinkerPatch>(patches));
Link();
auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
@@ -117,7 +117,7 @@
LinkerPatch patches[] = {
LinkerPatch::DexCacheArrayPatch(kDexCacheLoadCode.size() - 4u, nullptr, 0u, kElementOffset),
};
- AddCompiledMethod(MethodRef(1u), kDexCacheLoadCode, ArrayRef<LinkerPatch>(patches));
+ AddCompiledMethod(MethodRef(1u), kDexCacheLoadCode, ArrayRef<const LinkerPatch>(patches));
Link();
auto result = method_offset_map_.FindMethodOffset(MethodRef(1u));
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 01f7e91..6511120 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -239,7 +239,6 @@
*underflow = true;
return Min();
}
- return ValueBound(instruction_, new_constant);
}
private:
@@ -1011,7 +1010,7 @@
HDeoptimize(cond, bounds_check->GetDexPc());
block->InsertInstructionBefore(cond, bounds_check);
block->InsertInstructionBefore(deoptimize, bounds_check);
- deoptimize->SetEnvironment(bounds_check->GetEnvironment());
+ deoptimize->CopyEnvironmentFrom(bounds_check->GetEnvironment());
}
void AddComparesWithDeoptimization(HBasicBlock* block) {
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 9b1ef17..da28dc7 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -132,7 +132,6 @@
}
LOG(FATAL) << "Could not find a register in baseline register allocator";
UNREACHABLE();
- return -1;
}
size_t CodeGenerator::FindTwoFreeConsecutiveAlignedEntries(bool* array, size_t length) {
@@ -145,7 +144,6 @@
}
LOG(FATAL) << "Could not find a register in baseline register allocator";
UNREACHABLE();
- return -1;
}
void CodeGenerator::InitializeCodeGeneration(size_t number_of_spill_slots,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index f5e4df1..cfc798a 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -562,7 +562,6 @@
case Primitive::kPrimLong:
case Primitive::kPrimDouble:
return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
- break;
case Primitive::kPrimInt:
case Primitive::kPrimNot:
@@ -575,10 +574,11 @@
case Primitive::kPrimShort:
case Primitive::kPrimVoid:
LOG(FATAL) << "Unexpected type " << load->GetType();
+ UNREACHABLE();
}
LOG(FATAL) << "Unreachable";
- return Location();
+ UNREACHABLE();
}
Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) {
@@ -683,7 +683,6 @@
return Location();
}
UNREACHABLE();
- return Location();
}
void CodeGeneratorARM::Move32(Location destination, Location source) {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 439e85c..5cb0217 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1659,11 +1659,26 @@
Register lhs = InputRegisterAt(condition, 0);
Operand rhs = InputOperandAt(condition, 1);
Condition arm64_cond = ARM64Condition(condition->GetCondition());
- if ((arm64_cond == eq || arm64_cond == ne) && rhs.IsImmediate() && (rhs.immediate() == 0)) {
- if (arm64_cond == eq) {
- __ Cbz(lhs, true_target);
- } else {
- __ Cbnz(lhs, true_target);
+ if ((arm64_cond != gt && arm64_cond != le) && rhs.IsImmediate() && (rhs.immediate() == 0)) {
+ switch (arm64_cond) {
+ case eq:
+ __ Cbz(lhs, true_target);
+ break;
+ case ne:
+ __ Cbnz(lhs, true_target);
+ break;
+ case lt:
+ // Test the sign bit and branch accordingly.
+ __ Tbnz(lhs, (lhs.IsX() ? kXRegSize : kWRegSize) - 1, true_target);
+ break;
+ case ge:
+ // Test the sign bit and branch accordingly.
+ __ Tbz(lhs, (lhs.IsX() ? kXRegSize : kWRegSize) - 1, true_target);
+ break;
+ default:
+ // Without the `static_cast` the compiler throws an error for
+ // `-Werror=sign-promo`.
+ LOG(FATAL) << "Unexpected condition: " << static_cast<int>(arm64_cond);
}
} else {
__ Cmp(lhs, rhs);
diff --git a/compiler/optimizing/code_generator_utils.cc b/compiler/optimizing/code_generator_utils.cc
new file mode 100644
index 0000000..26cab2f
--- /dev/null
+++ b/compiler/optimizing/code_generator_utils.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 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 "code_generator_utils.h"
+
+#include "base/logging.h"
+
+void CalculateMagicAndShiftForDivRem(int64_t divisor, bool is_long,
+ int64_t* magic, int* shift) {
+ // It does not make sense to calculate magic and shift for zero divisor.
+ DCHECK_NE(divisor, 0);
+
+ /* According to implementation from H.S.Warren's "Hacker's Delight" (Addison Wesley, 2002)
+ * Chapter 10 and T,Grablund, P.L.Montogomery's "Division by Invariant Integers Using
+ * Multiplication" (PLDI 1994).
+ * The magic number M and shift S can be calculated in the following way:
+ * Let nc be the most positive value of numerator(n) such that nc = kd - 1,
+ * where divisor(d) >= 2.
+ * Let nc be the most negative value of numerator(n) such that nc = kd + 1,
+ * where divisor(d) <= -2.
+ * Thus nc can be calculated like:
+ * nc = exp + exp % d - 1, where d >= 2 and exp = 2^31 for int or 2^63 for long
+ * nc = -exp + (exp + 1) % d, where d >= 2 and exp = 2^31 for int or 2^63 for long
+ *
+ * So the shift p is the smallest p satisfying
+ * 2^p > nc * (d - 2^p % d), where d >= 2
+ * 2^p > nc * (d + 2^p % d), where d <= -2.
+ *
+ * The magic number M is calcuated by
+ * M = (2^p + d - 2^p % d) / d, where d >= 2
+ * M = (2^p - d - 2^p % d) / d, where d <= -2.
+ *
+ * Notice that p is always bigger than or equal to 32 (resp. 64), so we just return 32-p
+ * (resp. 64 - p) as the shift number S.
+ */
+
+ int64_t p = is_long ? 63 : 31;
+ const uint64_t exp = is_long ? (UINT64_C(1) << 63) : (UINT32_C(1) << 31);
+
+ // Initialize the computations.
+ uint64_t abs_d = (divisor >= 0) ? divisor : -divisor;
+ uint64_t tmp = exp + (is_long ? static_cast<uint64_t>(divisor) >> 63 :
+ static_cast<uint32_t>(divisor) >> 31);
+ uint64_t abs_nc = tmp - 1 - tmp % abs_d;
+ uint64_t quotient1 = exp / abs_nc;
+ uint64_t remainder1 = exp % abs_nc;
+ uint64_t quotient2 = exp / abs_d;
+ uint64_t remainder2 = exp % abs_d;
+
+ /*
+ * To avoid handling both positive and negative divisor, "Hacker's Delight"
+ * introduces a method to handle these 2 cases together to avoid duplication.
+ */
+ uint64_t delta;
+ do {
+ p++;
+ quotient1 = 2 * quotient1;
+ remainder1 = 2 * remainder1;
+ if (remainder1 >= abs_nc) {
+ quotient1++;
+ remainder1 = remainder1 - abs_nc;
+ }
+ quotient2 = 2 * quotient2;
+ remainder2 = 2 * remainder2;
+ if (remainder2 >= abs_d) {
+ quotient2++;
+ remainder2 = remainder2 - abs_d;
+ }
+ delta = abs_d - remainder2;
+ } while (quotient1 < delta || (quotient1 == delta && remainder1 == 0));
+
+ *magic = (divisor > 0) ? (quotient2 + 1) : (-quotient2 - 1);
+
+ if (!is_long) {
+ *magic = static_cast<int>(*magic);
+ }
+
+ *shift = is_long ? p - 64 : p - 32;
+}
+
diff --git a/compiler/optimizing/code_generator_utils.h b/compiler/optimizing/code_generator_utils.h
new file mode 100644
index 0000000..742d675
--- /dev/null
+++ b/compiler/optimizing/code_generator_utils.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 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_COMPILER_OPTIMIZING_CODE_GENERATOR_UTILS_H_
+#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_UTILS_H_
+
+#include <cstdint>
+
+// Computes the magic number and the shift needed in the div/rem by constant algorithm
+void CalculateMagicAndShiftForDivRem(int64_t divisor, bool is_long, int64_t* magic, int* shift);
+
+#endif // ART_COMPILER_OPTIMIZING_CODE_GENERATOR_UTILS_H_
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index f79dbc3..c4fbc1d 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -16,6 +16,7 @@
#include "code_generator_x86.h"
+#include "code_generator_utils.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "gc/accounting/card_table.h"
@@ -514,7 +515,6 @@
case Primitive::kPrimLong:
case Primitive::kPrimDouble:
return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
- break;
case Primitive::kPrimInt:
case Primitive::kPrimNot:
@@ -527,10 +527,11 @@
case Primitive::kPrimShort:
case Primitive::kPrimVoid:
LOG(FATAL) << "Unexpected type " << load->GetType();
+ UNREACHABLE();
}
LOG(FATAL) << "Unreachable";
- return Location();
+ UNREACHABLE();
}
Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) {
@@ -2278,6 +2279,134 @@
__ addl(ESP, Immediate(2 * elem_size));
}
+
+void InstructionCodeGeneratorX86::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+
+ LocationSummary* locations = instruction->GetLocations();
+ DCHECK(locations->InAt(1).IsConstant());
+
+ Register out_register = locations->Out().AsRegister<Register>();
+ Register input_register = locations->InAt(0).AsRegister<Register>();
+ int imm = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+
+ DCHECK(imm == 1 || imm == -1);
+
+ if (instruction->IsRem()) {
+ __ xorl(out_register, out_register);
+ } else {
+ __ movl(out_register, input_register);
+ if (imm == -1) {
+ __ negl(out_register);
+ }
+ }
+}
+
+
+void InstructionCodeGeneratorX86::DivByPowerOfTwo(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv());
+
+ LocationSummary* locations = instruction->GetLocations();
+
+ Register out_register = locations->Out().AsRegister<Register>();
+ Register input_register = locations->InAt(0).AsRegister<Register>();
+ int imm = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+
+ DCHECK(instruction->IsDiv() && IsPowerOfTwo(std::abs(imm)));
+ Register num = locations->GetTemp(0).AsRegister<Register>();
+
+ __ leal(num, Address(input_register, std::abs(imm) - 1));
+ __ testl(input_register, input_register);
+ __ cmovl(kGreaterEqual, num, input_register);
+ int shift = CTZ(imm);
+ __ sarl(num, Immediate(shift));
+
+ if (imm < 0) {
+ __ negl(num);
+ }
+
+ __ movl(out_register, num);
+}
+
+void InstructionCodeGeneratorX86::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+
+ LocationSummary* locations = instruction->GetLocations();
+ int imm = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+
+ Register eax = locations->InAt(0).AsRegister<Register>();
+ Register out = locations->Out().AsRegister<Register>();
+ Register num;
+ Register edx;
+
+ if (instruction->IsDiv()) {
+ edx = locations->GetTemp(0).AsRegister<Register>();
+ num = locations->GetTemp(1).AsRegister<Register>();
+ } else {
+ edx = locations->Out().AsRegister<Register>();
+ num = locations->GetTemp(0).AsRegister<Register>();
+ }
+
+ DCHECK_EQ(EAX, eax);
+ DCHECK_EQ(EDX, edx);
+ if (instruction->IsDiv()) {
+ DCHECK_EQ(EAX, out);
+ } else {
+ DCHECK_EQ(EDX, out);
+ }
+
+ int64_t magic;
+ int shift;
+ CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
+
+ Label ndiv;
+ Label end;
+ // If numerator is 0, the result is 0, no computation needed.
+ __ testl(eax, eax);
+ __ j(kNotEqual, &ndiv);
+
+ __ xorl(out, out);
+ __ jmp(&end);
+
+ __ Bind(&ndiv);
+
+ // Save the numerator.
+ __ movl(num, eax);
+
+ // EAX = magic
+ __ movl(eax, Immediate(magic));
+
+ // EDX:EAX = magic * numerator
+ __ imull(num);
+
+ if (imm > 0 && magic < 0) {
+ // EDX += num
+ __ addl(edx, num);
+ } else if (imm < 0 && magic > 0) {
+ __ subl(edx, num);
+ }
+
+ // Shift if needed.
+ if (shift != 0) {
+ __ sarl(edx, Immediate(shift));
+ }
+
+ // EDX += 1 if EDX < 0
+ __ movl(eax, edx);
+ __ shrl(edx, Immediate(31));
+ __ addl(edx, eax);
+
+ if (instruction->IsRem()) {
+ __ movl(eax, num);
+ __ imull(edx, Immediate(imm));
+ __ subl(eax, edx);
+ __ movl(edx, eax);
+ } else {
+ __ movl(eax, edx);
+ }
+ __ Bind(&end);
+}
+
void InstructionCodeGeneratorX86::GenerateDivRemIntegral(HBinaryOperation* instruction) {
DCHECK(instruction->IsDiv() || instruction->IsRem());
@@ -2289,28 +2418,42 @@
switch (instruction->GetResultType()) {
case Primitive::kPrimInt: {
- Register second_reg = second.AsRegister<Register>();
DCHECK_EQ(EAX, first.AsRegister<Register>());
DCHECK_EQ(is_div ? EAX : EDX, out.AsRegister<Register>());
- SlowPathCodeX86* slow_path =
+ if (second.IsConstant()) {
+ int imm = second.GetConstant()->AsIntConstant()->GetValue();
+
+ if (imm == 0) {
+ // Do not generate anything for 0. DivZeroCheck would forbid any generated code.
+ } else if (imm == 1 || imm == -1) {
+ DivRemOneOrMinusOne(instruction);
+ } else if (is_div && IsPowerOfTwo(std::abs(imm))) {
+ DivByPowerOfTwo(instruction);
+ } else {
+ DCHECK(imm <= -2 || imm >= 2);
+ GenerateDivRemWithAnyConstant(instruction);
+ }
+ } else {
+ SlowPathCodeX86* slow_path =
new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86(out.AsRegister<Register>(),
- is_div);
- codegen_->AddSlowPath(slow_path);
+ is_div);
+ codegen_->AddSlowPath(slow_path);
- // 0x80000000/-1 triggers an arithmetic exception!
- // Dividing by -1 is actually negation and -0x800000000 = 0x80000000 so
- // it's safe to just use negl instead of more complex comparisons.
+ Register second_reg = second.AsRegister<Register>();
+ // 0x80000000/-1 triggers an arithmetic exception!
+ // Dividing by -1 is actually negation and -0x800000000 = 0x80000000 so
+ // it's safe to just use negl instead of more complex comparisons.
- __ cmpl(second_reg, Immediate(-1));
- __ j(kEqual, slow_path->GetEntryLabel());
+ __ cmpl(second_reg, Immediate(-1));
+ __ j(kEqual, slow_path->GetEntryLabel());
- // edx:eax <- sign-extended of eax
- __ cdq();
- // eax = quotient, edx = remainder
- __ idivl(second_reg);
-
- __ Bind(slow_path->GetExitLabel());
+ // edx:eax <- sign-extended of eax
+ __ cdq();
+ // eax = quotient, edx = remainder
+ __ idivl(second_reg);
+ __ Bind(slow_path->GetExitLabel());
+ }
break;
}
@@ -2350,10 +2493,16 @@
switch (div->GetResultType()) {
case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RegisterLocation(EAX));
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1)));
locations->SetOut(Location::SameAsFirstInput());
// Intel uses edx:eax as the dividend.
locations->AddTemp(Location::RegisterLocation(EDX));
+ // We need to save the numerator while we tweak eax and edx. As we are using imul in a way
+ // which enforces results to be in EAX and EDX, things are simpler if we use EAX also as
+ // output and request another temp.
+ if (div->InputAt(1)->IsConstant()) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
break;
}
case Primitive::kPrimLong: {
@@ -2411,6 +2560,7 @@
void LocationsBuilderX86::VisitRem(HRem* rem) {
Primitive::Type type = rem->GetResultType();
+
LocationSummary::CallKind call_kind = (rem->GetResultType() == Primitive::kPrimLong)
? LocationSummary::kCall
: LocationSummary::kNoCall;
@@ -2419,8 +2569,14 @@
switch (type) {
case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RegisterLocation(EAX));
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1)));
locations->SetOut(Location::RegisterLocation(EDX));
+ // We need to save the numerator while we tweak eax and edx. As we are using imul in a way
+ // which enforces results to be in EAX and EDX, things are simpler if we use EDX also as
+ // output and request another temp.
+ if (rem->InputAt(1)->IsConstant()) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
break;
}
case Primitive::kPrimLong: {
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 0cc3c65..20f14fb 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -163,6 +163,9 @@
void GenerateClassInitializationCheck(SlowPathCodeX86* slow_path, Register class_reg);
void HandleBitwiseOperation(HBinaryOperation* instruction);
void GenerateDivRemIntegral(HBinaryOperation* instruction);
+ void DivRemOneOrMinusOne(HBinaryOperation* instruction);
+ void DivByPowerOfTwo(HBinaryOperation* instruction);
+ void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
void GenerateRemFP(HRem *rem);
void HandleShift(HBinaryOperation* instruction);
void GenerateShlLong(const Location& loc, Register shifter);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 9958451..9e08558 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -16,6 +16,7 @@
#include "code_generator_x86_64.h"
+#include "code_generator_utils.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
#include "intrinsics.h"
@@ -555,7 +556,6 @@
case Primitive::kPrimLong:
case Primitive::kPrimDouble:
return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
- break;
case Primitive::kPrimInt:
case Primitive::kPrimNot:
@@ -568,10 +568,11 @@
case Primitive::kPrimShort:
case Primitive::kPrimVoid:
LOG(FATAL) << "Unexpected type " << load->GetType();
+ UNREACHABLE();
}
LOG(FATAL) << "Unreachable";
- return Location();
+ UNREACHABLE();
}
void CodeGeneratorX86_64::Move(Location destination, Location source) {
@@ -2259,6 +2260,228 @@
__ addq(CpuRegister(RSP), Immediate(2 * elem_size));
}
+void InstructionCodeGeneratorX86_64::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+ DCHECK(second.IsConstant());
+
+ CpuRegister output_register = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister input_register = locations->InAt(0).AsRegister<CpuRegister>();
+ int64_t imm;
+ if (second.GetConstant()->IsLongConstant()) {
+ imm = second.GetConstant()->AsLongConstant()->GetValue();
+ } else {
+ imm = second.GetConstant()->AsIntConstant()->GetValue();
+ }
+
+ DCHECK(imm == 1 || imm == -1);
+
+ switch (instruction->GetResultType()) {
+ case Primitive::kPrimInt: {
+ if (instruction->IsRem()) {
+ __ xorl(output_register, output_register);
+ } else {
+ __ movl(output_register, input_register);
+ if (imm == -1) {
+ __ negl(output_register);
+ }
+ }
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ if (instruction->IsRem()) {
+ __ xorq(output_register, output_register);
+ } else {
+ __ movq(output_register, input_register);
+ if (imm == -1) {
+ __ negq(output_register);
+ }
+ }
+ break;
+ }
+
+ default:
+ LOG(FATAL) << "Unreachable";
+ }
+}
+
+void InstructionCodeGeneratorX86_64::DivByPowerOfTwo(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv());
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+
+ CpuRegister output_register = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister numerator = locations->InAt(0).AsRegister<CpuRegister>();
+
+ int64_t imm;
+ if (instruction->GetResultType() == Primitive::kPrimLong) {
+ imm = second.GetConstant()->AsLongConstant()->GetValue();
+ } else {
+ imm = second.GetConstant()->AsIntConstant()->GetValue();
+ }
+
+ DCHECK(IsPowerOfTwo(std::abs(imm)));
+
+ CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>();
+
+ if (instruction->GetResultType() == Primitive::kPrimInt) {
+ __ leal(tmp, Address(numerator, std::abs(imm) - 1));
+ __ testl(numerator, numerator);
+ __ cmov(kGreaterEqual, tmp, numerator);
+ int shift = CTZ(imm);
+ __ sarl(tmp, Immediate(shift));
+
+ if (imm < 0) {
+ __ negl(tmp);
+ }
+
+ __ movl(output_register, tmp);
+ } else {
+ DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
+ CpuRegister rdx = locations->GetTemp(0).AsRegister<CpuRegister>();
+
+ __ movq(rdx, Immediate(std::abs(imm) - 1));
+ __ addq(rdx, numerator);
+ __ testq(numerator, numerator);
+ __ cmov(kGreaterEqual, rdx, numerator);
+ int shift = CTZ(imm);
+ __ sarq(rdx, Immediate(shift));
+
+ if (imm < 0) {
+ __ negq(rdx);
+ }
+
+ __ movq(output_register, rdx);
+ }
+}
+
+void InstructionCodeGeneratorX86_64::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+
+ CpuRegister numerator = instruction->IsDiv() ? locations->GetTemp(1).AsRegister<CpuRegister>()
+ : locations->GetTemp(0).AsRegister<CpuRegister>();
+ CpuRegister eax = locations->InAt(0).AsRegister<CpuRegister>();
+ CpuRegister edx = instruction->IsDiv() ? locations->GetTemp(0).AsRegister<CpuRegister>()
+ : locations->Out().AsRegister<CpuRegister>();
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+
+ DCHECK_EQ(RAX, eax.AsRegister());
+ DCHECK_EQ(RDX, edx.AsRegister());
+ if (instruction->IsDiv()) {
+ DCHECK_EQ(RAX, out.AsRegister());
+ } else {
+ DCHECK_EQ(RDX, out.AsRegister());
+ }
+
+ int64_t magic;
+ int shift;
+
+ // TODO: can these branch be written as one?
+ if (instruction->GetResultType() == Primitive::kPrimInt) {
+ int imm = second.GetConstant()->AsIntConstant()->GetValue();
+
+ CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
+
+ __ movl(numerator, eax);
+
+ Label no_div;
+ Label end;
+ __ testl(eax, eax);
+ __ j(kNotEqual, &no_div);
+
+ __ xorl(out, out);
+ __ jmp(&end);
+
+ __ Bind(&no_div);
+
+ __ movl(eax, Immediate(magic));
+ __ imull(numerator);
+
+ if (imm > 0 && magic < 0) {
+ __ addl(edx, numerator);
+ } else if (imm < 0 && magic > 0) {
+ __ subl(edx, numerator);
+ }
+
+ if (shift != 0) {
+ __ sarl(edx, Immediate(shift));
+ }
+
+ __ movl(eax, edx);
+ __ shrl(edx, Immediate(31));
+ __ addl(edx, eax);
+
+ if (instruction->IsRem()) {
+ __ movl(eax, numerator);
+ __ imull(edx, Immediate(imm));
+ __ subl(eax, edx);
+ __ movl(edx, eax);
+ } else {
+ __ movl(eax, edx);
+ }
+ __ Bind(&end);
+ } else {
+ int64_t imm = second.GetConstant()->AsLongConstant()->GetValue();
+
+ DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
+
+ CpuRegister rax = eax;
+ CpuRegister rdx = edx;
+
+ CalculateMagicAndShiftForDivRem(imm, true /* is_long */, &magic, &shift);
+
+ // Save the numerator.
+ __ movq(numerator, rax);
+
+ // RAX = magic
+ __ movq(rax, Immediate(magic));
+
+ // RDX:RAX = magic * numerator
+ __ imulq(numerator);
+
+ if (imm > 0 && magic < 0) {
+ // RDX += numeratorerator
+ __ addq(rdx, numerator);
+ } else if (imm < 0 && magic > 0) {
+ // RDX -= numerator
+ __ subq(rdx, numerator);
+ }
+
+ // Shift if needed.
+ if (shift != 0) {
+ __ sarq(rdx, Immediate(shift));
+ }
+
+ // RDX += 1 if RDX < 0
+ __ movq(rax, rdx);
+ __ shrq(rdx, Immediate(63));
+ __ addq(rdx, rax);
+
+ if (instruction->IsRem()) {
+ __ movq(rax, numerator);
+
+ if (IsInt<32>(imm)) {
+ __ imulq(rdx, Immediate(static_cast<int32_t>(imm)));
+ } else {
+ __ movq(numerator, Immediate(imm));
+ __ imulq(rdx, numerator);
+ }
+
+ __ subq(rax, rdx);
+ __ movq(rdx, rax);
+ } else {
+ __ movq(rax, rdx);
+ }
+ }
+}
+
void InstructionCodeGeneratorX86_64::GenerateDivRemIntegral(HBinaryOperation* instruction) {
DCHECK(instruction->IsDiv() || instruction->IsRem());
Primitive::Type type = instruction->GetResultType();
@@ -2267,37 +2490,57 @@
bool is_div = instruction->IsDiv();
LocationSummary* locations = instruction->GetLocations();
- CpuRegister out_reg = locations->Out().AsRegister<CpuRegister>();
- CpuRegister second_reg = locations->InAt(1).AsRegister<CpuRegister>();
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ Location second = locations->InAt(1);
DCHECK_EQ(RAX, locations->InAt(0).AsRegister<CpuRegister>().AsRegister());
- DCHECK_EQ(is_div ? RAX : RDX, out_reg.AsRegister());
+ DCHECK_EQ(is_div ? RAX : RDX, out.AsRegister());
- SlowPathCodeX86_64* slow_path =
- new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86_64(
- out_reg.AsRegister(), type, is_div);
- codegen_->AddSlowPath(slow_path);
+ if (second.IsConstant()) {
+ int64_t imm;
+ if (second.GetConstant()->AsLongConstant()) {
+ imm = second.GetConstant()->AsLongConstant()->GetValue();
+ } else {
+ imm = second.GetConstant()->AsIntConstant()->GetValue();
+ }
- // 0x80000000(00000000)/-1 triggers an arithmetic exception!
- // Dividing by -1 is actually negation and -0x800000000(00000000) = 0x80000000(00000000)
- // so it's safe to just use negl instead of more complex comparisons.
- if (type == Primitive::kPrimInt) {
- __ cmpl(second_reg, Immediate(-1));
- __ j(kEqual, slow_path->GetEntryLabel());
- // edx:eax <- sign-extended of eax
- __ cdq();
- // eax = quotient, edx = remainder
- __ idivl(second_reg);
+ if (imm == 0) {
+ // Do not generate anything. DivZeroCheck would prevent any code to be executed.
+ } else if (imm == 1 || imm == -1) {
+ DivRemOneOrMinusOne(instruction);
+ } else if (instruction->IsDiv() && IsPowerOfTwo(std::abs(imm))) {
+ DivByPowerOfTwo(instruction);
+ } else {
+ DCHECK(imm <= -2 || imm >= 2);
+ GenerateDivRemWithAnyConstant(instruction);
+ }
} else {
- __ cmpq(second_reg, Immediate(-1));
- __ j(kEqual, slow_path->GetEntryLabel());
- // rdx:rax <- sign-extended of rax
- __ cqo();
- // rax = quotient, rdx = remainder
- __ idivq(second_reg);
- }
+ SlowPathCodeX86_64* slow_path =
+ new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86_64(
+ out.AsRegister(), type, is_div);
+ codegen_->AddSlowPath(slow_path);
- __ Bind(slow_path->GetExitLabel());
+ CpuRegister second_reg = second.AsRegister<CpuRegister>();
+ // 0x80000000(00000000)/-1 triggers an arithmetic exception!
+ // Dividing by -1 is actually negation and -0x800000000(00000000) = 0x80000000(00000000)
+ // so it's safe to just use negl instead of more complex comparisons.
+ if (type == Primitive::kPrimInt) {
+ __ cmpl(second_reg, Immediate(-1));
+ __ j(kEqual, slow_path->GetEntryLabel());
+ // edx:eax <- sign-extended of eax
+ __ cdq();
+ // eax = quotient, edx = remainder
+ __ idivl(second_reg);
+ } else {
+ __ cmpq(second_reg, Immediate(-1));
+ __ j(kEqual, slow_path->GetEntryLabel());
+ // rdx:rax <- sign-extended of rax
+ __ cqo();
+ // rax = quotient, rdx = remainder
+ __ idivq(second_reg);
+ }
+ __ Bind(slow_path->GetExitLabel());
+ }
}
void LocationsBuilderX86_64::VisitDiv(HDiv* div) {
@@ -2307,10 +2550,16 @@
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RegisterLocation(RAX));
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1)));
locations->SetOut(Location::SameAsFirstInput());
// Intel uses edx:eax as the dividend.
locations->AddTemp(Location::RegisterLocation(RDX));
+ // We need to save the numerator while we tweak rax and rdx. As we are using imul in a way
+ // which enforces results to be in RAX and RDX, things are simpler if we use RDX also as
+ // output and request another temp.
+ if (div->InputAt(1)->IsConstant()) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
break;
}
@@ -2365,9 +2614,15 @@
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RegisterLocation(RAX));
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1)));
// Intel uses rdx:rax as the dividend and puts the remainder in rdx
locations->SetOut(Location::RegisterLocation(RDX));
+ // We need to save the numerator while we tweak eax and edx. As we are using imul in a way
+ // which enforces results to be in RAX and RDX, things are simpler if we use EAX also as
+ // output and request another temp.
+ if (rem->InputAt(1)->IsConstant()) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
break;
}
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 375c0b0..be2a79e 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -173,6 +173,9 @@
void GenerateClassInitializationCheck(SlowPathCodeX86_64* slow_path, CpuRegister class_reg);
void HandleBitwiseOperation(HBinaryOperation* operation);
void GenerateRemFP(HRem *rem);
+ void DivRemOneOrMinusOne(HBinaryOperation* instruction);
+ void DivByPowerOfTwo(HBinaryOperation* instruction);
+ void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
void GenerateDivRemIntegral(HBinaryOperation* instruction);
void HandleShift(HBinaryOperation* operation);
void GenerateMemoryBarrier(MemBarrierKind kind);
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 49c0d38..4c28378 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -337,13 +337,11 @@
HGraphVisualizer::HGraphVisualizer(std::ostream* output,
HGraph* graph,
- const CodeGenerator& codegen,
- const char* method_name)
- : output_(output), graph_(graph), codegen_(codegen) {
- if (output == nullptr) {
- return;
- }
+ const CodeGenerator& codegen)
+ : output_(output), graph_(graph), codegen_(codegen) {}
+void HGraphVisualizer::PrintHeader(const char* method_name) const {
+ DCHECK(output_ != nullptr);
HGraphVisualizerPrinter printer(graph_, *output_, "", true, codegen_);
printer.StartTag("compilation");
printer.PrintProperty("name", method_name);
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index bc553ae..513bceb 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -35,9 +35,9 @@
public:
HGraphVisualizer(std::ostream* output,
HGraph* graph,
- const CodeGenerator& codegen,
- const char* method_name);
+ const CodeGenerator& codegen);
+ void PrintHeader(const char* method_name) const;
void DumpGraph(const char* pass_name, bool is_after_pass = true) const;
private:
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 628a844..20aa45f 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -90,7 +90,6 @@
LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
UNREACHABLE();
}
- break;
case kIntrinsicReverseBytes:
switch (GetType(method.d.data, true)) {
case Primitive::kPrimShort:
@@ -103,7 +102,6 @@
LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
UNREACHABLE();
}
- break;
// Abs.
case kIntrinsicAbsDouble:
@@ -166,7 +164,6 @@
LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
UNREACHABLE();
}
- break;
// Memory.poke.
case kIntrinsicPoke:
@@ -183,7 +180,6 @@
LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
UNREACHABLE();
}
- break;
// String.
case kIntrinsicCharAt:
@@ -211,7 +207,6 @@
LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
UNREACHABLE();
}
- break;
case kIntrinsicUnsafeGet: {
const bool is_volatile = (method.d.data & kIntrinsicFlagIsVolatile);
switch (GetType(method.d.data, false)) {
@@ -225,7 +220,6 @@
LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
UNREACHABLE();
}
- break;
}
case kIntrinsicUnsafePut: {
enum Sync { kNoSync, kVolatile, kOrdered };
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 33176f0..94e27e9 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -776,10 +776,10 @@
__ mov(out, ShifterOperand(0), CC);
}
-void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke ATTRIBUTE_UNUSED) {
+void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke) {
CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
}
-void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke ATTRIBUTE_UNUSED) {
+void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) {
CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
}
void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 0740471..aec2d19 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -158,6 +158,7 @@
if (invoke_->IsInvokeStaticOrDirect()) {
codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), EAX);
+ RecordPcInfo(codegen, invoke_, invoke_->GetDexPc());
} else {
UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
UNREACHABLE();
@@ -319,6 +320,27 @@
GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
}
+void IntrinsicLocationsBuilderX86::VisitLongReverseBytes(HInvoke* invoke) {
+ CreateLongToLongLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitLongReverseBytes(HInvoke* invoke) {
+ LocationSummary* locations = invoke->GetLocations();
+ Location input = locations->InAt(0);
+ Register input_lo = input.AsRegisterPairLow<Register>();
+ Register input_hi = input.AsRegisterPairHigh<Register>();
+ Location output = locations->Out();
+ Register output_lo = output.AsRegisterPairLow<Register>();
+ Register output_hi = output.AsRegisterPairHigh<Register>();
+
+ X86Assembler* assembler = GetAssembler();
+ // Assign the inputs to the outputs, mixing low/high.
+ __ movl(output_lo, input_hi);
+ __ movl(output_hi, input_lo);
+ __ bswapl(output_lo);
+ __ bswapl(output_hi);
+}
+
void IntrinsicLocationsBuilderX86::VisitShortReverseBytes(HInvoke* invoke) {
CreateIntToIntLocations(arena_, invoke);
}
@@ -730,6 +752,7 @@
DCHECK(invoke->IsInvokeStaticOrDirect());
codegen->GenerateStaticOrDirectCall(invoke->AsInvokeStaticOrDirect(), EAX);
+ codegen->RecordPcInfo(invoke, invoke->GetDexPc());
// Copy the result back to the expected output.
Location out = invoke->GetLocations()->Out();
@@ -1328,6 +1351,181 @@
GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, codegen_);
}
+static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, Primitive::Type type,
+ HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ // Offset is a long, but in 32 bit mode, we only need the low word.
+ // Can we update the invoke here to remove a TypeConvert to Long?
+ locations->SetInAt(2, Location::RequiresRegister());
+ // Expected value must be in EAX or EDX:EAX.
+ // For long, new value must be in ECX:EBX.
+ if (type == Primitive::kPrimLong) {
+ locations->SetInAt(3, Location::RegisterPairLocation(EAX, EDX));
+ locations->SetInAt(4, Location::RegisterPairLocation(EBX, ECX));
+ } else {
+ locations->SetInAt(3, Location::RegisterLocation(EAX));
+ locations->SetInAt(4, Location::RequiresRegister());
+ }
+
+ // Force a byte register for the output.
+ locations->SetOut(Location::RegisterLocation(EAX));
+ if (type == Primitive::kPrimNot) {
+ // Need temp registers for card-marking.
+ locations->AddTemp(Location::RequiresRegister());
+ // Need a byte register for marking.
+ locations->AddTemp(Location::RegisterLocation(ECX));
+ }
+}
+
+void IntrinsicLocationsBuilderX86::VisitUnsafeCASInt(HInvoke* invoke) {
+ CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimInt, invoke);
+}
+
+void IntrinsicLocationsBuilderX86::VisitUnsafeCASLong(HInvoke* invoke) {
+ CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimLong, invoke);
+}
+
+void IntrinsicLocationsBuilderX86::VisitUnsafeCASObject(HInvoke* invoke) {
+ CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimNot, invoke);
+}
+
+static void GenCAS(Primitive::Type type, HInvoke* invoke, CodeGeneratorX86* codegen) {
+ X86Assembler* assembler =
+ reinterpret_cast<X86Assembler*>(codegen->GetAssembler());
+ LocationSummary* locations = invoke->GetLocations();
+
+ Register base = locations->InAt(1).AsRegister<Register>();
+ Register offset = locations->InAt(2).AsRegisterPairLow<Register>();
+ Location out = locations->Out();
+ DCHECK_EQ(out.AsRegister<Register>(), EAX);
+
+ if (type == Primitive::kPrimLong) {
+ DCHECK_EQ(locations->InAt(3).AsRegisterPairLow<Register>(), EAX);
+ DCHECK_EQ(locations->InAt(3).AsRegisterPairHigh<Register>(), EDX);
+ DCHECK_EQ(locations->InAt(4).AsRegisterPairLow<Register>(), EBX);
+ DCHECK_EQ(locations->InAt(4).AsRegisterPairHigh<Register>(), ECX);
+ __ LockCmpxchg8b(Address(base, offset, TIMES_1, 0));
+ } else {
+ // Integer or object.
+ DCHECK_EQ(locations->InAt(3).AsRegister<Register>(), EAX);
+ Register value = locations->InAt(4).AsRegister<Register>();
+ if (type == Primitive::kPrimNot) {
+ // Mark card for object assuming new value is stored.
+ codegen->MarkGCCard(locations->GetTemp(0).AsRegister<Register>(),
+ locations->GetTemp(1).AsRegister<Register>(),
+ base,
+ value);
+ }
+
+ __ LockCmpxchgl(Address(base, offset, TIMES_1, 0), value);
+ }
+
+ // locked cmpxchg has full barrier semantics, and we don't need scheduling
+ // barriers at this time.
+
+ // Convert ZF into the boolean result.
+ __ setb(kZero, out.AsRegister<Register>());
+ __ movzxb(out.AsRegister<Register>(), out.AsRegister<ByteRegister>());
+}
+
+void IntrinsicCodeGeneratorX86::VisitUnsafeCASInt(HInvoke* invoke) {
+ GenCAS(Primitive::kPrimInt, invoke, codegen_);
+}
+
+void IntrinsicCodeGeneratorX86::VisitUnsafeCASLong(HInvoke* invoke) {
+ GenCAS(Primitive::kPrimLong, invoke, codegen_);
+}
+
+void IntrinsicCodeGeneratorX86::VisitUnsafeCASObject(HInvoke* invoke) {
+ GenCAS(Primitive::kPrimNot, invoke, codegen_);
+}
+
+void IntrinsicLocationsBuilderX86::VisitIntegerReverse(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+static void SwapBits(Register reg, Register temp, int32_t shift, int32_t mask,
+ X86Assembler* assembler) {
+ Immediate imm_shift(shift);
+ Immediate imm_mask(mask);
+ __ movl(temp, reg);
+ __ shrl(reg, imm_shift);
+ __ andl(temp, imm_mask);
+ __ andl(reg, imm_mask);
+ __ shll(temp, imm_shift);
+ __ orl(reg, temp);
+}
+
+void IntrinsicCodeGeneratorX86::VisitIntegerReverse(HInvoke* invoke) {
+ X86Assembler* assembler =
+ reinterpret_cast<X86Assembler*>(codegen_->GetAssembler());
+ LocationSummary* locations = invoke->GetLocations();
+
+ Register reg = locations->InAt(0).AsRegister<Register>();
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+
+ /*
+ * Use one bswap instruction to reverse byte order first and then use 3 rounds of
+ * swapping bits to reverse bits in a number x. Using bswap to save instructions
+ * compared to generic luni implementation which has 5 rounds of swapping bits.
+ * x = bswap x
+ * x = (x & 0x55555555) << 1 | (x >> 1) & 0x55555555;
+ * x = (x & 0x33333333) << 2 | (x >> 2) & 0x33333333;
+ * x = (x & 0x0F0F0F0F) << 4 | (x >> 4) & 0x0F0F0F0F;
+ */
+ __ bswapl(reg);
+ SwapBits(reg, temp, 1, 0x55555555, assembler);
+ SwapBits(reg, temp, 2, 0x33333333, assembler);
+ SwapBits(reg, temp, 4, 0x0f0f0f0f, assembler);
+}
+
+void IntrinsicLocationsBuilderX86::VisitLongReverse(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorX86::VisitLongReverse(HInvoke* invoke) {
+ X86Assembler* assembler =
+ reinterpret_cast<X86Assembler*>(codegen_->GetAssembler());
+ LocationSummary* locations = invoke->GetLocations();
+
+ Register reg_low = locations->InAt(0).AsRegisterPairLow<Register>();
+ Register reg_high = locations->InAt(0).AsRegisterPairHigh<Register>();
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+
+ // We want to swap high/low, then bswap each one, and then do the same
+ // as a 32 bit reverse.
+ // Exchange high and low.
+ __ movl(temp, reg_low);
+ __ movl(reg_low, reg_high);
+ __ movl(reg_high, temp);
+
+ // bit-reverse low
+ __ bswapl(reg_low);
+ SwapBits(reg_low, temp, 1, 0x55555555, assembler);
+ SwapBits(reg_low, temp, 2, 0x33333333, assembler);
+ SwapBits(reg_low, temp, 4, 0x0f0f0f0f, assembler);
+
+ // bit-reverse high
+ __ bswapl(reg_high);
+ SwapBits(reg_high, temp, 1, 0x55555555, assembler);
+ SwapBits(reg_high, temp, 2, 0x33333333, assembler);
+ SwapBits(reg_high, temp, 4, 0x0f0f0f0f, assembler);
+}
+
// Unimplemented intrinsics.
#define UNIMPLEMENTED_INTRINSIC(Name) \
@@ -1336,16 +1534,10 @@
void IntrinsicCodeGeneratorX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
}
-UNIMPLEMENTED_INTRINSIC(IntegerReverse)
-UNIMPLEMENTED_INTRINSIC(LongReverse)
-UNIMPLEMENTED_INTRINSIC(LongReverseBytes)
UNIMPLEMENTED_INTRINSIC(MathRoundDouble)
UNIMPLEMENTED_INTRINSIC(StringIndexOf)
UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
-UNIMPLEMENTED_INTRINSIC(UnsafeCASInt)
-UNIMPLEMENTED_INTRINSIC(UnsafeCASLong)
-UNIMPLEMENTED_INTRINSIC(UnsafeCASObject)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
} // namespace x86
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index f6fa013..5122a00 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1202,6 +1202,175 @@
GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, codegen_);
}
+static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, Primitive::Type type,
+ HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ // expected value must be in EAX/RAX.
+ locations->SetInAt(3, Location::RegisterLocation(RAX));
+ locations->SetInAt(4, Location::RequiresRegister());
+
+ locations->SetOut(Location::RequiresRegister());
+ if (type == Primitive::kPrimNot) {
+ // Need temp registers for card-marking.
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeCASInt(HInvoke* invoke) {
+ CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimInt, invoke);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeCASLong(HInvoke* invoke) {
+ CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimLong, invoke);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitUnsafeCASObject(HInvoke* invoke) {
+ CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimNot, invoke);
+}
+
+static void GenCAS(Primitive::Type type, HInvoke* invoke, CodeGeneratorX86_64* codegen) {
+ X86_64Assembler* assembler =
+ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler());
+ LocationSummary* locations = invoke->GetLocations();
+
+ CpuRegister base = locations->InAt(1).AsRegister<CpuRegister>();
+ CpuRegister offset = locations->InAt(2).AsRegister<CpuRegister>();
+ CpuRegister expected = locations->InAt(3).AsRegister<CpuRegister>();
+ DCHECK_EQ(expected.AsRegister(), RAX);
+ CpuRegister value = locations->InAt(4).AsRegister<CpuRegister>();
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+
+ if (type == Primitive::kPrimLong) {
+ __ LockCmpxchgq(Address(base, offset, TIMES_1, 0), value);
+ } else {
+ // Integer or object.
+ if (type == Primitive::kPrimNot) {
+ // Mark card for object assuming new value is stored.
+ codegen->MarkGCCard(locations->GetTemp(0).AsRegister<CpuRegister>(),
+ locations->GetTemp(1).AsRegister<CpuRegister>(),
+ base,
+ value);
+ }
+
+ __ LockCmpxchgl(Address(base, offset, TIMES_1, 0), value);
+ }
+
+ // locked cmpxchg has full barrier semantics, and we don't need scheduling
+ // barriers at this time.
+
+ // Convert ZF into the boolean result.
+ __ setcc(kZero, out);
+ __ movzxb(out, out);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASInt(HInvoke* invoke) {
+ GenCAS(Primitive::kPrimInt, invoke, codegen_);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASLong(HInvoke* invoke) {
+ GenCAS(Primitive::kPrimLong, invoke, codegen_);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASObject(HInvoke* invoke) {
+ GenCAS(Primitive::kPrimNot, invoke, codegen_);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitIntegerReverse(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+static void SwapBits(CpuRegister reg, CpuRegister temp, int32_t shift, int32_t mask,
+ X86_64Assembler* assembler) {
+ Immediate imm_shift(shift);
+ Immediate imm_mask(mask);
+ __ movl(temp, reg);
+ __ shrl(reg, imm_shift);
+ __ andl(temp, imm_mask);
+ __ andl(reg, imm_mask);
+ __ shll(temp, imm_shift);
+ __ orl(reg, temp);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitIntegerReverse(HInvoke* invoke) {
+ X86_64Assembler* assembler =
+ reinterpret_cast<X86_64Assembler*>(codegen_->GetAssembler());
+ LocationSummary* locations = invoke->GetLocations();
+
+ CpuRegister reg = locations->InAt(0).AsRegister<CpuRegister>();
+ CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
+
+ /*
+ * Use one bswap instruction to reverse byte order first and then use 3 rounds of
+ * swapping bits to reverse bits in a number x. Using bswap to save instructions
+ * compared to generic luni implementation which has 5 rounds of swapping bits.
+ * x = bswap x
+ * x = (x & 0x55555555) << 1 | (x >> 1) & 0x55555555;
+ * x = (x & 0x33333333) << 2 | (x >> 2) & 0x33333333;
+ * x = (x & 0x0F0F0F0F) << 4 | (x >> 4) & 0x0F0F0F0F;
+ */
+ __ bswapl(reg);
+ SwapBits(reg, temp, 1, 0x55555555, assembler);
+ SwapBits(reg, temp, 2, 0x33333333, assembler);
+ SwapBits(reg, temp, 4, 0x0f0f0f0f, assembler);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitLongReverse(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+static void SwapBits64(CpuRegister reg, CpuRegister temp, CpuRegister temp_mask,
+ int32_t shift, int64_t mask, X86_64Assembler* assembler) {
+ Immediate imm_shift(shift);
+ __ movq(temp_mask, Immediate(mask));
+ __ movq(temp, reg);
+ __ shrq(reg, imm_shift);
+ __ andq(temp, temp_mask);
+ __ andq(reg, temp_mask);
+ __ shlq(temp, imm_shift);
+ __ orq(reg, temp);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitLongReverse(HInvoke* invoke) {
+ X86_64Assembler* assembler =
+ reinterpret_cast<X86_64Assembler*>(codegen_->GetAssembler());
+ LocationSummary* locations = invoke->GetLocations();
+
+ CpuRegister reg = locations->InAt(0).AsRegister<CpuRegister>();
+ CpuRegister temp1 = locations->GetTemp(0).AsRegister<CpuRegister>();
+ CpuRegister temp2 = locations->GetTemp(1).AsRegister<CpuRegister>();
+
+ /*
+ * Use one bswap instruction to reverse byte order first and then use 3 rounds of
+ * swapping bits to reverse bits in a long number x. Using bswap to save instructions
+ * compared to generic luni implementation which has 5 rounds of swapping bits.
+ * x = bswap x
+ * x = (x & 0x5555555555555555) << 1 | (x >> 1) & 0x5555555555555555;
+ * x = (x & 0x3333333333333333) << 2 | (x >> 2) & 0x3333333333333333;
+ * x = (x & 0x0F0F0F0F0F0F0F0F) << 4 | (x >> 4) & 0x0F0F0F0F0F0F0F0F;
+ */
+ __ bswapq(reg);
+ SwapBits64(reg, temp1, temp2, 1, INT64_C(0x5555555555555555), assembler);
+ SwapBits64(reg, temp1, temp2, 2, INT64_C(0x3333333333333333), assembler);
+ SwapBits64(reg, temp1, temp2, 4, INT64_C(0x0f0f0f0f0f0f0f0f), assembler);
+}
+
// Unimplemented intrinsics.
#define UNIMPLEMENTED_INTRINSIC(Name) \
@@ -1210,14 +1379,9 @@
void IntrinsicCodeGeneratorX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
}
-UNIMPLEMENTED_INTRINSIC(IntegerReverse)
-UNIMPLEMENTED_INTRINSIC(LongReverse)
UNIMPLEMENTED_INTRINSIC(StringIndexOf)
UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
-UNIMPLEMENTED_INTRINSIC(UnsafeCASInt)
-UNIMPLEMENTED_INTRINSIC(UnsafeCASLong)
-UNIMPLEMENTED_INTRINSIC(UnsafeCASObject)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
} // namespace x86_64
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 6827cd0..f764eb4 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1192,7 +1192,17 @@
bool HasEnvironment() const { return environment_ != nullptr; }
HEnvironment* GetEnvironment() const { return environment_; }
- void SetEnvironment(HEnvironment* environment) { environment_ = environment; }
+ // Set the `environment_` field. Raw because this method does not
+ // update the uses lists.
+ void SetRawEnvironment(HEnvironment* environment) { environment_ = environment; }
+
+ // Set the environment of this instruction, copying it from `environment`. While
+ // copying, the uses lists are being updated.
+ void CopyEnvironmentFrom(HEnvironment* environment) {
+ ArenaAllocator* allocator = GetBlock()->GetGraph()->GetArena();
+ environment_ = new (allocator) HEnvironment(allocator, environment->Size());
+ environment_->CopyFrom(environment);
+ }
// Returns the number of entries in the environment. Typically, that is the
// number of dex registers in a method. It could be more in case of inlining.
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 4cf22d3..4e83ce5 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -50,7 +50,7 @@
exit_block->AddInstruction(new (&allocator) HExit());
HEnvironment* environment = new (&allocator) HEnvironment(&allocator, 1);
- null_check->SetEnvironment(environment);
+ null_check->SetRawEnvironment(environment);
environment->SetRawEnvAt(0, parameter);
parameter->AddEnvUseAt(null_check->GetEnvironment(), 0);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index e474c49..12798ed 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -96,10 +96,13 @@
timing_logger_enabled_(compiler_driver->GetDumpPasses()),
timing_logger_(method_name, true, true),
visualizer_enabled_(!compiler_driver->GetDumpCfgFileName().empty()),
- visualizer_(visualizer_output, graph, codegen, method_name_) {
+ visualizer_(visualizer_output, graph, codegen) {
if (strstr(method_name, kStringFilter) == nullptr) {
timing_logger_enabled_ = visualizer_enabled_ = false;
}
+ if (visualizer_enabled_) {
+ visualizer_.PrintHeader(method_name_);
+ }
}
~PassInfoPrinter() {
@@ -201,8 +204,13 @@
const std::vector<const art::DexFile*>& dex_files,
const std::string& android_root,
bool is_host) const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return art::ElfWriterQuick32::Create(file, oat_writer, dex_files, android_root, is_host,
- *GetCompilerDriver());
+ if (kProduce64BitELFFiles && Is64BitInstructionSet(GetCompilerDriver()->GetInstructionSet())) {
+ return art::ElfWriterQuick64::Create(file, oat_writer, dex_files, android_root, is_host,
+ *GetCompilerDriver());
+ } else {
+ return art::ElfWriterQuick32::Create(file, oat_writer, dex_files, android_root, is_host,
+ *GetCompilerDriver());
+ }
}
void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index fcc4e69..e154ea4 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -487,7 +487,7 @@
HEnvironment* environment = new (GetGraph()->GetArena()) HEnvironment(
GetGraph()->GetArena(), current_locals_->Size());
environment->CopyFrom(current_locals_);
- instruction->SetEnvironment(environment);
+ instruction->SetRawEnvironment(environment);
}
void SsaBuilder::VisitTemporary(HTemporary* temp) {
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index a02191b..8059289 100644
--- a/compiler/utils/arm/assembler_arm.cc
+++ b/compiler/utils/arm/assembler_arm.cc
@@ -89,7 +89,6 @@
} else {
return immed_;
}
- break;
case kRegister:
if (is_shift_) {
uint32_t shift_type;
@@ -121,7 +120,6 @@
// Simple register
return static_cast<uint32_t>(rm_);
}
- break;
default:
// Can't get here.
LOG(FATAL) << "Invalid shifter operand for ARM";
@@ -156,13 +154,11 @@
// Simple register
return static_cast<uint32_t>(rm_);
}
- break;
default:
// Can't get here.
LOG(FATAL) << "Invalid shifter operand for thumb";
- return 0;
+ UNREACHABLE();
}
- return 0;
}
uint32_t Address::encodingArm() const {
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index a894319..3b42f63 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -373,24 +373,34 @@
void Thumb2Assembler::ldrd(Register rd, const Address& ad, Condition cond) {
+ ldrd(rd, Register(rd + 1), ad, cond);
+}
+
+
+void Thumb2Assembler::ldrd(Register rd, Register rd2, const Address& ad, Condition cond) {
CheckCondition(cond);
- CHECK_EQ(rd % 2, 0);
+ // Encoding T1.
// This is different from other loads. The encoding is like ARM.
int32_t encoding = B31 | B30 | B29 | B27 | B22 | B20 |
static_cast<int32_t>(rd) << 12 |
- (static_cast<int32_t>(rd) + 1) << 8 |
+ static_cast<int32_t>(rd2) << 8 |
ad.encodingThumbLdrdStrd();
Emit32(encoding);
}
void Thumb2Assembler::strd(Register rd, const Address& ad, Condition cond) {
+ strd(rd, Register(rd + 1), ad, cond);
+}
+
+
+void Thumb2Assembler::strd(Register rd, Register rd2, const Address& ad, Condition cond) {
CheckCondition(cond);
- CHECK_EQ(rd % 2, 0);
+ // Encoding T1.
// This is different from other loads. The encoding is like ARM.
int32_t encoding = B31 | B30 | B29 | B27 | B22 |
static_cast<int32_t>(rd) << 12 |
- (static_cast<int32_t>(rd) + 1) << 8 |
+ static_cast<int32_t>(rd2) << 8 |
ad.encodingThumbLdrdStrd();
Emit32(encoding);
}
@@ -683,7 +693,7 @@
bool Thumb2Assembler::Is32BitDataProcessing(Condition cond ATTRIBUTE_UNUSED,
Opcode opcode,
- bool set_cc ATTRIBUTE_UNUSED,
+ bool set_cc,
Register rn,
Register rd,
const ShifterOperand& so) {
@@ -749,7 +759,6 @@
break;
case TEQ:
return true;
- break;
case ADD:
case SUB:
break;
@@ -2614,14 +2623,16 @@
Register tmp_reg = kNoRegister;
if (!Address::CanHoldStoreOffsetThumb(type, offset)) {
CHECK_NE(base, IP);
- if (reg != IP) {
+ if (reg != IP &&
+ (type != kStoreWordPair || reg + 1 != IP)) {
tmp_reg = IP;
} else {
- // Be careful not to use IP twice (for `reg` and to build the
- // Address object used by the store instruction(s) below).
- // Instead, save R5 on the stack (or R6 if R5 is not available),
- // use it as secondary temporary register, and restore it after
- // the store instruction has been emitted.
+ // Be careful not to use IP twice (for `reg` (or `reg` + 1 in
+ // the case of a word-pair store)) and to build the Address
+ // object used by the store instruction(s) below). Instead,
+ // save R5 on the stack (or R6 if R5 is not available), use it
+ // as secondary temporary register, and restore it after the
+ // store instruction has been emitted.
tmp_reg = base != R5 ? R5 : R6;
Push(tmp_reg);
if (base == SP) {
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 81dd138..e33c240 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -135,9 +135,17 @@
void ldrsb(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
void ldrsh(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
+ // Load/store register dual instructions using registers `rd` and `rd` + 1.
void ldrd(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
void strd(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
+ // Load/store register dual instructions using registers `rd` and `rd2`.
+ // Note that contrary to the ARM A1 encoding, the Thumb-2 T1 encoding
+ // does not require `rd` to be even, nor `rd2' to be equal to `rd` + 1.
+ void ldrd(Register rd, Register rd2, const Address& ad, Condition cond);
+ void strd(Register rd, Register rd2, const Address& ad, Condition cond);
+
+
void ldm(BlockAddressMode am, Register base,
RegList regs, Condition cond = AL) OVERRIDE;
void stm(BlockAddressMode am, Register base,
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 813996b..5f5561a 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -247,4 +247,103 @@
DriverStr(expected, "add");
}
+TEST_F(AssemblerThumb2Test, StoreWordToThumbOffset) {
+ arm::StoreOperandType type = arm::kStoreWord;
+ int32_t offset = 4092;
+ ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
+
+ __ StoreToOffset(type, arm::R0, arm::SP, offset);
+ __ StoreToOffset(type, arm::IP, arm::SP, offset);
+ __ StoreToOffset(type, arm::IP, arm::R5, offset);
+
+ const char* expected =
+ "str r0, [sp, #4092]\n"
+ "str ip, [sp, #4092]\n"
+ "str ip, [r5, #4092]\n";
+ DriverStr(expected, "StoreWordToThumbOffset");
+}
+
+TEST_F(AssemblerThumb2Test, StoreWordToNonThumbOffset) {
+ arm::StoreOperandType type = arm::kStoreWord;
+ int32_t offset = 4096;
+ ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
+
+ __ StoreToOffset(type, arm::R0, arm::SP, offset);
+ __ StoreToOffset(type, arm::IP, arm::SP, offset);
+ __ StoreToOffset(type, arm::IP, arm::R5, offset);
+
+ const char* expected =
+ "mov ip, #4096\n" // LoadImmediate(ip, 4096)
+ "add ip, ip, sp\n"
+ "str r0, [ip, #0]\n"
+
+ "str r5, [sp, #-4]!\n" // Push(r5)
+ "movw r5, #4100\n" // LoadImmediate(r5, 4096 + kRegisterSize)
+ "add r5, r5, sp\n"
+ "str ip, [r5, #0]\n"
+ "ldr r5, [sp], #4\n" // Pop(r5)
+
+ "str r6, [sp, #-4]!\n" // Push(r6)
+ "mov r6, #4096\n" // LoadImmediate(r6, 4096)
+ "add r6, r6, r5\n"
+ "str ip, [r6, #0]\n"
+ "ldr r6, [sp], #4\n"; // Pop(r6)
+ DriverStr(expected, "StoreWordToNonThumbOffset");
+}
+
+TEST_F(AssemblerThumb2Test, StoreWordPairToThumbOffset) {
+ arm::StoreOperandType type = arm::kStoreWordPair;
+ int32_t offset = 1020;
+ ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
+
+ __ StoreToOffset(type, arm::R0, arm::SP, offset);
+ // We cannot use IP (i.e. R12) as first source register, as it would
+ // force us to use SP (i.e. R13) as second source register, which
+ // would have an "unpredictable" effect according to the ARMv7
+ // specification (the T1 encoding describes the result as
+ // UNPREDICTABLE when of the source registers is R13).
+ //
+ // So we use (R11, IP) (e.g. (R11, R12)) as source registers in the
+ // following instructions.
+ __ StoreToOffset(type, arm::R11, arm::SP, offset);
+ __ StoreToOffset(type, arm::R11, arm::R5, offset);
+
+ const char* expected =
+ "strd r0, r1, [sp, #1020]\n"
+ "strd r11, ip, [sp, #1020]\n"
+ "strd r11, ip, [r5, #1020]\n";
+ DriverStr(expected, "StoreWordPairToThumbOffset");
+}
+
+TEST_F(AssemblerThumb2Test, StoreWordPairToNonThumbOffset) {
+ arm::StoreOperandType type = arm::kStoreWordPair;
+ int32_t offset = 1024;
+ ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
+
+ __ StoreToOffset(type, arm::R0, arm::SP, offset);
+ // Same comment as in AssemblerThumb2Test.StoreWordPairToThumbOffset
+ // regarding the use of (R11, IP) (e.g. (R11, R12)) as source
+ // registers in the following instructions.
+ __ StoreToOffset(type, arm::R11, arm::SP, offset);
+ __ StoreToOffset(type, arm::R11, arm::R5, offset);
+
+ const char* expected =
+ "mov ip, #1024\n" // LoadImmediate(ip, 1024)
+ "add ip, ip, sp\n"
+ "strd r0, r1, [ip, #0]\n"
+
+ "str r5, [sp, #-4]!\n" // Push(r5)
+ "movw r5, #1028\n" // LoadImmediate(r5, 1024 + kRegisterSize)
+ "add r5, r5, sp\n"
+ "strd r11, ip, [r5, #0]\n"
+ "ldr r5, [sp], #4\n" // Pop(r5)
+
+ "str r6, [sp, #-4]!\n" // Push(r6)
+ "mov r6, #1024\n" // LoadImmediate(r6, 1024)
+ "add r6, r6, r5\n"
+ "strd r11, ip, [r6, #0]\n"
+ "ldr r6, [sp], #4\n"; // Pop(r6)
+ DriverStr(expected, "StoreWordPairToNonThumbOffset");
+}
+
} // namespace art
diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h
index 2031fe4..8973b9c 100644
--- a/compiler/utils/arm64/assembler_arm64.h
+++ b/compiler/utils/arm64/assembler_arm64.h
@@ -30,7 +30,9 @@
// TODO: make vixl clean wrt -Wshadow.
#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wmissing-noreturn"
#include "vixl/a64/macro-assembler-a64.h"
#include "vixl/a64/disasm-a64.h"
#pragma GCC diagnostic pop
diff --git a/compiler/utils/array_ref.h b/compiler/utils/array_ref.h
index b1b0ee5..ff5a77c 100644
--- a/compiler/utils/array_ref.h
+++ b/compiler/utils/array_ref.h
@@ -89,6 +89,8 @@
: array_(v.data()), size_(v.size()) {
}
+ ArrayRef(const ArrayRef&) = default;
+
// Assignment operators.
ArrayRef& operator=(const ArrayRef& other) {
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index 923ecdb..323f93c 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -504,12 +504,6 @@
// and branch to a ExceptionSlowPath if it is.
virtual void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust) = 0;
- virtual void InitializeFrameDescriptionEntry() {}
- virtual void FinalizeFrameDescriptionEntry() {}
- // Give a vector containing FDE data, or null if not used. Note: the assembler must take care
- // of handling the lifecycle.
- virtual std::vector<uint8_t>* GetFrameDescriptionEntry() { return nullptr; }
-
virtual ~Assembler() {}
protected:
diff --git a/compiler/utils/dwarf_cfi.cc b/compiler/utils/dwarf_cfi.cc
deleted file mode 100644
index a7e09c6..0000000
--- a/compiler/utils/dwarf_cfi.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "leb128.h"
-#include "utils.h"
-
-#include "dwarf_cfi.h"
-
-namespace art {
-
-void DW_CFA_advance_loc(std::vector<uint8_t>* buf, uint32_t increment) {
- if (increment < 64) {
- // Encoding in opcode.
- buf->push_back(0x1 << 6 | increment);
- } else if (increment < 256) {
- // Single byte delta.
- buf->push_back(0x02);
- buf->push_back(increment);
- } else if (increment < 256 * 256) {
- // Two byte delta.
- buf->push_back(0x03);
- buf->push_back(increment & 0xff);
- buf->push_back((increment >> 8) & 0xff);
- } else {
- // Four byte delta.
- buf->push_back(0x04);
- Push32(buf, increment);
- }
-}
-
-void DW_CFA_offset_extended_sf(std::vector<uint8_t>* buf, int reg, int32_t offset) {
- buf->push_back(0x11);
- EncodeUnsignedLeb128(reg, buf);
- EncodeSignedLeb128(offset, buf);
-}
-
-void DW_CFA_offset(std::vector<uint8_t>* buf, int reg, uint32_t offset) {
- buf->push_back((0x2 << 6) | reg);
- EncodeUnsignedLeb128(offset, buf);
-}
-
-void DW_CFA_def_cfa_offset(std::vector<uint8_t>* buf, int32_t offset) {
- buf->push_back(0x0e);
- EncodeUnsignedLeb128(offset, buf);
-}
-
-void DW_CFA_remember_state(std::vector<uint8_t>* buf) {
- buf->push_back(0x0a);
-}
-
-void DW_CFA_restore_state(std::vector<uint8_t>* buf) {
- buf->push_back(0x0b);
-}
-
-void WriteFDEHeader(std::vector<uint8_t>* buf, bool is_64bit) {
- // 'length' (filled in by other functions).
- if (is_64bit) {
- Push32(buf, 0xffffffff); // Indicates 64bit
- Push32(buf, 0);
- Push32(buf, 0);
- } else {
- Push32(buf, 0);
- }
-
- // 'CIE_pointer' (filled in by linker).
- if (is_64bit) {
- Push32(buf, 0);
- Push32(buf, 0);
- } else {
- Push32(buf, 0);
- }
-
- // 'initial_location' (filled in by linker).
- if (is_64bit) {
- Push32(buf, 0);
- Push32(buf, 0);
- } else {
- Push32(buf, 0);
- }
-
- // 'address_range' (filled in by other functions).
- if (is_64bit) {
- Push32(buf, 0);
- Push32(buf, 0);
- } else {
- Push32(buf, 0);
- }
-
- // Augmentation length: 0
- buf->push_back(0);
-}
-
-void WriteFDEAddressRange(std::vector<uint8_t>* buf, uint64_t data, bool is_64bit) {
- const size_t kOffsetOfAddressRange = is_64bit? 28 : 12;
- CHECK(buf->size() >= kOffsetOfAddressRange + (is_64bit? 8 : 4));
-
- uint8_t *p = buf->data() + kOffsetOfAddressRange;
- if (is_64bit) {
- p[0] = data;
- p[1] = data >> 8;
- p[2] = data >> 16;
- p[3] = data >> 24;
- p[4] = data >> 32;
- p[5] = data >> 40;
- p[6] = data >> 48;
- p[7] = data >> 56;
- } else {
- p[0] = data;
- p[1] = data >> 8;
- p[2] = data >> 16;
- p[3] = data >> 24;
- }
-}
-
-void WriteCFILength(std::vector<uint8_t>* buf, bool is_64bit) {
- uint64_t length = is_64bit ? buf->size() - 12 : buf->size() - 4;
- DCHECK_EQ((length & 0x3), 0U);
-
- uint8_t *p = is_64bit? buf->data() + 4 : buf->data();
- if (is_64bit) {
- p[0] = length;
- p[1] = length >> 8;
- p[2] = length >> 16;
- p[3] = length >> 24;
- p[4] = length >> 32;
- p[5] = length >> 40;
- p[6] = length >> 48;
- p[7] = length >> 56;
- } else {
- p[0] = length;
- p[1] = length >> 8;
- p[2] = length >> 16;
- p[3] = length >> 24;
- }
-}
-
-void PadCFI(std::vector<uint8_t>* buf) {
- while (buf->size() & 0x3) {
- buf->push_back(0);
- }
-}
-
-} // namespace art
diff --git a/compiler/utils/dwarf_cfi.h b/compiler/utils/dwarf_cfi.h
deleted file mode 100644
index 0c8b151..0000000
--- a/compiler/utils/dwarf_cfi.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_UTILS_DWARF_CFI_H_
-#define ART_COMPILER_UTILS_DWARF_CFI_H_
-
-#include <vector>
-
-namespace art {
-
-/**
- * @brief Enter a 'DW_CFA_advance_loc' into an FDE buffer
- * @param buf FDE buffer.
- * @param increment Amount by which to increase the current location.
- */
-void DW_CFA_advance_loc(std::vector<uint8_t>* buf, uint32_t increment);
-
-/**
- * @brief Enter a 'DW_CFA_offset_extended_sf' into an FDE buffer
- * @param buf FDE buffer.
- * @param reg Register number.
- * @param offset Offset of register address from CFA.
- */
-void DW_CFA_offset_extended_sf(std::vector<uint8_t>* buf, int reg, int32_t offset);
-
-/**
- * @brief Enter a 'DW_CFA_offset' into an FDE buffer
- * @param buf FDE buffer.
- * @param reg Register number.
- * @param offset Offset of register address from CFA.
- */
-void DW_CFA_offset(std::vector<uint8_t>* buf, int reg, uint32_t offset);
-
-/**
- * @brief Enter a 'DW_CFA_def_cfa_offset' into an FDE buffer
- * @param buf FDE buffer.
- * @param offset New offset of CFA.
- */
-void DW_CFA_def_cfa_offset(std::vector<uint8_t>* buf, int32_t offset);
-
-/**
- * @brief Enter a 'DW_CFA_remember_state' into an FDE buffer
- * @param buf FDE buffer.
- */
-void DW_CFA_remember_state(std::vector<uint8_t>* buf);
-
-/**
- * @brief Enter a 'DW_CFA_restore_state' into an FDE buffer
- * @param buf FDE buffer.
- */
-void DW_CFA_restore_state(std::vector<uint8_t>* buf);
-
-/**
- * @brief Write FDE header into an FDE buffer
- * @param buf FDE buffer.
- * @param is_64bit If FDE is for 64bit application.
- */
-void WriteFDEHeader(std::vector<uint8_t>* buf, bool is_64bit);
-
-/**
- * @brief Set 'address_range' field of an FDE buffer
- * @param buf FDE buffer.
- * @param data Data value.
- * @param is_64bit If FDE is for 64bit application.
- */
-void WriteFDEAddressRange(std::vector<uint8_t>* buf, uint64_t data, bool is_64bit);
-
-/**
- * @brief Set 'length' field of an FDE buffer
- * @param buf FDE buffer.
- * @param is_64bit If FDE is for 64bit application.
- */
-void WriteCFILength(std::vector<uint8_t>* buf, bool is_64bit);
-
-/**
- * @brief Pad an FDE buffer with 0 until its size is a multiple of 4
- * @param buf FDE buffer.
- */
-void PadCFI(std::vector<uint8_t>* buf);
-} // namespace art
-
-#endif // ART_COMPILER_UTILS_DWARF_CFI_H_
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 233ae7d..388d274 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -1025,7 +1025,7 @@
__ Move(A0, scratch_.AsGpuRegister());
// Set up call to Thread::Current()->pDeliverException
__ LoadFromOffset(kLoadDoubleword, T9, S1,
- QUICK_ENTRYPOINT_OFFSET(4, pDeliverException).Int32Value());
+ QUICK_ENTRYPOINT_OFFSET(8, pDeliverException).Int32Value());
__ Jr(T9);
// Call never returns
__ Break();
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index b3a1376..4cca529 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -20,7 +20,6 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "memory_region.h"
#include "thread.h"
-#include "utils/dwarf_cfi.h"
namespace art {
namespace x86 {
@@ -1467,6 +1466,15 @@
EmitOperand(reg, address);
}
+
+void X86Assembler::cmpxchg8b(const Address& address) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x0F);
+ EmitUint8(0xC7);
+ EmitOperand(1, address);
+}
+
+
void X86Assembler::mfence() {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x0F);
@@ -1631,69 +1639,32 @@
EmitOperand(reg_or_opcode, Operand(operand));
}
-void X86Assembler::InitializeFrameDescriptionEntry() {
- WriteFDEHeader(&cfi_info_, false /* is_64bit */);
-}
-
-void X86Assembler::FinalizeFrameDescriptionEntry() {
- WriteFDEAddressRange(&cfi_info_, buffer_.Size(), false /* is_64bit */);
- PadCFI(&cfi_info_);
- WriteCFILength(&cfi_info_, false /* is_64bit */);
-}
-
constexpr size_t kFramePointerSize = 4;
void X86Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
const std::vector<ManagedRegister>& spill_regs,
const ManagedRegisterEntrySpills& entry_spills) {
- cfi_cfa_offset_ = kFramePointerSize; // Only return address on stack
- cfi_pc_ = buffer_.Size(); // Nothing emitted yet
- DCHECK_EQ(cfi_pc_, 0U);
-
- uint32_t reg_offset = 1;
+ DCHECK_EQ(buffer_.Size(), 0U); // Nothing emitted yet.
CHECK_ALIGNED(frame_size, kStackAlignment);
int gpr_count = 0;
for (int i = spill_regs.size() - 1; i >= 0; --i) {
- x86::X86ManagedRegister spill = spill_regs.at(i).AsX86();
- DCHECK(spill.IsCpuRegister());
- pushl(spill.AsCpuRegister());
+ Register spill = spill_regs.at(i).AsX86().AsCpuRegister();
+ pushl(spill);
gpr_count++;
-
- // DW_CFA_advance_loc
- DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
- cfi_pc_ = buffer_.Size();
- // DW_CFA_def_cfa_offset
- cfi_cfa_offset_ += kFramePointerSize;
- DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
- // DW_CFA_offset reg offset
- reg_offset++;
- DW_CFA_offset(&cfi_info_, spill_regs.at(i).AsX86().DWARFRegId(), reg_offset);
}
- // return address then method on stack
+ // return address then method on stack.
int32_t adjust = frame_size - (gpr_count * kFramePointerSize) -
sizeof(StackReference<mirror::ArtMethod>) /*method*/ -
kFramePointerSize /*return address*/;
addl(ESP, Immediate(-adjust));
- // DW_CFA_advance_loc
- DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
- cfi_pc_ = buffer_.Size();
- // DW_CFA_def_cfa_offset
- cfi_cfa_offset_ += adjust;
- DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
-
pushl(method_reg.AsX86().AsCpuRegister());
- // DW_CFA_advance_loc
- DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
- cfi_pc_ = buffer_.Size();
- // DW_CFA_def_cfa_offset
- cfi_cfa_offset_ += kFramePointerSize;
- DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
for (size_t i = 0; i < entry_spills.size(); ++i) {
ManagedRegisterSpill spill = entry_spills.at(i);
if (spill.AsX86().IsCpuRegister()) {
- movl(Address(ESP, frame_size + spill.getSpillOffset()), spill.AsX86().AsCpuRegister());
+ int offset = frame_size + spill.getSpillOffset();
+ movl(Address(ESP, offset), spill.AsX86().AsCpuRegister());
} else {
DCHECK(spill.AsX86().IsXmmRegister());
if (spill.getSize() == 8) {
@@ -1709,8 +1680,9 @@
void X86Assembler::RemoveFrame(size_t frame_size,
const std::vector<ManagedRegister>& spill_regs) {
CHECK_ALIGNED(frame_size, kStackAlignment);
- addl(ESP, Immediate(frame_size - (spill_regs.size() * kFramePointerSize) -
- sizeof(StackReference<mirror::ArtMethod>)));
+ int adjust = frame_size - (spill_regs.size() * kFramePointerSize) -
+ sizeof(StackReference<mirror::ArtMethod>);
+ addl(ESP, Immediate(adjust));
for (size_t i = 0; i < spill_regs.size(); ++i) {
x86::X86ManagedRegister spill = spill_regs.at(i).AsX86();
DCHECK(spill.IsCpuRegister());
@@ -1722,12 +1694,6 @@
void X86Assembler::IncreaseFrameSize(size_t adjust) {
CHECK_ALIGNED(adjust, kStackAlignment);
addl(ESP, Immediate(-adjust));
- // DW_CFA_advance_loc
- DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
- cfi_pc_ = buffer_.Size();
- // DW_CFA_def_cfa_offset
- cfi_cfa_offset_ += adjust;
- DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
}
void X86Assembler::DecreaseFrameSize(size_t adjust) {
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index bdf8843..f3675ae 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -205,7 +205,7 @@
class X86Assembler FINAL : public Assembler {
public:
- explicit X86Assembler() : cfi_cfa_offset_(0), cfi_pc_(0) {}
+ explicit X86Assembler() {}
virtual ~X86Assembler() {}
/*
@@ -457,6 +457,7 @@
X86Assembler* lock();
void cmpxchgl(const Address& address, Register reg);
+ void cmpxchg8b(const Address& address);
void mfence();
@@ -476,6 +477,10 @@
lock()->cmpxchgl(address, reg);
}
+ void LockCmpxchg8b(const Address& address) {
+ lock()->cmpxchg8b(address);
+ }
+
//
// Misc. functionality
//
@@ -599,12 +604,6 @@
// and branch to a ExceptionSlowPath if it is.
void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust) OVERRIDE;
- void InitializeFrameDescriptionEntry() OVERRIDE;
- void FinalizeFrameDescriptionEntry() OVERRIDE;
- std::vector<uint8_t>* GetFrameDescriptionEntry() OVERRIDE {
- return &cfi_info_;
- }
-
private:
inline void EmitUint8(uint8_t value);
inline void EmitInt32(int32_t value);
@@ -623,9 +622,6 @@
void EmitGenericShift(int rm, Register reg, const Immediate& imm);
void EmitGenericShift(int rm, Register operand, Register shifter);
- std::vector<uint8_t> cfi_info_;
- uint32_t cfi_cfa_offset_, cfi_pc_;
-
DISALLOW_COPY_AND_ASSIGN(X86Assembler);
};
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index fccb510..dba3b6b 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -127,4 +127,49 @@
DriverStr(expected, "LoadLongConstant");
}
+TEST_F(AssemblerX86Test, LockCmpxchgl) {
+ GetAssembler()->LockCmpxchgl(x86::Address(
+ x86::Register(x86::EDI), x86::Register(x86::EBX), x86::TIMES_4, 12),
+ x86::Register(x86::ESI));
+ GetAssembler()->LockCmpxchgl(x86::Address(
+ x86::Register(x86::EDI), x86::Register(x86::ESI), x86::TIMES_4, 12),
+ x86::Register(x86::ESI));
+ GetAssembler()->LockCmpxchgl(x86::Address(
+ x86::Register(x86::EDI), x86::Register(x86::ESI), x86::TIMES_4, 12),
+ x86::Register(x86::EDI));
+ GetAssembler()->LockCmpxchgl(x86::Address(
+ x86::Register(x86::EBP), 0), x86::Register(x86::ESI));
+ GetAssembler()->LockCmpxchgl(x86::Address(
+ x86::Register(x86::EBP), x86::Register(x86::ESI), x86::TIMES_1, 0),
+ x86::Register(x86::ESI));
+ const char* expected =
+ "lock cmpxchgl %ESI, 0xc(%EDI,%EBX,4)\n"
+ "lock cmpxchgl %ESI, 0xc(%EDI,%ESI,4)\n"
+ "lock cmpxchgl %EDI, 0xc(%EDI,%ESI,4)\n"
+ "lock cmpxchgl %ESI, (%EBP)\n"
+ "lock cmpxchgl %ESI, (%EBP,%ESI,1)\n";
+
+ DriverStr(expected, "lock_cmpxchgl");
+}
+
+TEST_F(AssemblerX86Test, LockCmpxchg8b) {
+ GetAssembler()->LockCmpxchg8b(x86::Address(
+ x86::Register(x86::EDI), x86::Register(x86::EBX), x86::TIMES_4, 12));
+ GetAssembler()->LockCmpxchg8b(x86::Address(
+ x86::Register(x86::EDI), x86::Register(x86::ESI), x86::TIMES_4, 12));
+ GetAssembler()->LockCmpxchg8b(x86::Address(
+ x86::Register(x86::EDI), x86::Register(x86::ESI), x86::TIMES_4, 12));
+ GetAssembler()->LockCmpxchg8b(x86::Address(x86::Register(x86::EBP), 0));
+ GetAssembler()->LockCmpxchg8b(x86::Address(
+ x86::Register(x86::EBP), x86::Register(x86::ESI), x86::TIMES_1, 0));
+ const char* expected =
+ "lock cmpxchg8b 0xc(%EDI,%EBX,4)\n"
+ "lock cmpxchg8b 0xc(%EDI,%ESI,4)\n"
+ "lock cmpxchg8b 0xc(%EDI,%ESI,4)\n"
+ "lock cmpxchg8b (%EBP)\n"
+ "lock cmpxchg8b (%EBP,%ESI,1)\n";
+
+ DriverStr(expected, "lock_cmpxchg8b");
+}
+
} // namespace art
diff --git a/compiler/utils/x86/managed_register_x86.h b/compiler/utils/x86/managed_register_x86.h
index 5d46ee2..09d2b49 100644
--- a/compiler/utils/x86/managed_register_x86.h
+++ b/compiler/utils/x86/managed_register_x86.h
@@ -88,14 +88,6 @@
// There is a one-to-one mapping between ManagedRegister and register id.
class X86ManagedRegister : public ManagedRegister {
public:
- int DWARFRegId() const {
- CHECK(IsCpuRegister());
- // For all the X86 registers we care about:
- // EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
- // DWARF register id is the same as id_.
- return static_cast<int>(id_);
- }
-
ByteRegister AsByteRegister() const {
CHECK(IsCpuRegister());
CHECK_LT(AsCpuRegister(), ESP); // ESP, EBP, ESI and EDI cannot be encoded as byte registers.
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index e82d90c..30e8218 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -20,7 +20,6 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "memory_region.h"
#include "thread.h"
-#include "utils/dwarf_cfi.h"
namespace art {
namespace x86_64 {
@@ -1621,6 +1620,14 @@
}
+void X86_64Assembler::imulq(CpuRegister reg) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitRex64(reg);
+ EmitUint8(0xF7);
+ EmitOperand(5, Operand(reg));
+}
+
+
void X86_64Assembler::imull(const Address& address) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalRex32(address);
@@ -1854,11 +1861,22 @@
void X86_64Assembler::cmpxchgl(const Address& address, CpuRegister reg) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitOptionalRex32(reg, address);
EmitUint8(0x0F);
EmitUint8(0xB1);
EmitOperand(reg.LowBits(), address);
}
+
+void X86_64Assembler::cmpxchgq(const Address& address, CpuRegister reg) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitRex64(reg, address);
+ EmitUint8(0x0F);
+ EmitUint8(0xB1);
+ EmitOperand(reg.LowBits(), address);
+}
+
+
void X86_64Assembler::mfence() {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x0F);
@@ -2168,26 +2186,12 @@
}
}
-void X86_64Assembler::InitializeFrameDescriptionEntry() {
- WriteFDEHeader(&cfi_info_, true /* is_64bit */);
-}
-
-void X86_64Assembler::FinalizeFrameDescriptionEntry() {
- WriteFDEAddressRange(&cfi_info_, buffer_.Size(), true /* is_64bit */);
- PadCFI(&cfi_info_);
- WriteCFILength(&cfi_info_, true /* is_64bit */);
-}
-
constexpr size_t kFramePointerSize = 8;
void X86_64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
const std::vector<ManagedRegister>& spill_regs,
const ManagedRegisterEntrySpills& entry_spills) {
- cfi_cfa_offset_ = kFramePointerSize; // Only return address on stack
- cfi_pc_ = buffer_.Size(); // Nothing emitted yet
- DCHECK_EQ(cfi_pc_, 0U);
-
- uint32_t reg_offset = 1;
+ DCHECK_EQ(buffer_.Size(), 0U); // Nothing emitted yet.
CHECK_ALIGNED(frame_size, kStackAlignment);
int gpr_count = 0;
for (int i = spill_regs.size() - 1; i >= 0; --i) {
@@ -2195,29 +2199,13 @@
if (spill.IsCpuRegister()) {
pushq(spill.AsCpuRegister());
gpr_count++;
-
- // DW_CFA_advance_loc
- DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
- cfi_pc_ = buffer_.Size();
- // DW_CFA_def_cfa_offset
- cfi_cfa_offset_ += kFramePointerSize;
- DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
- // DW_CFA_offset reg offset
- reg_offset++;
- DW_CFA_offset(&cfi_info_, spill.DWARFRegId(), reg_offset);
}
}
- // return address then method on stack
+ // return address then method on stack.
int64_t rest_of_frame = static_cast<int64_t>(frame_size)
- (gpr_count * kFramePointerSize)
- kFramePointerSize /*return address*/;
subq(CpuRegister(RSP), Immediate(rest_of_frame));
- // DW_CFA_advance_loc
- DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
- cfi_pc_ = buffer_.Size();
- // DW_CFA_def_cfa_offset
- cfi_cfa_offset_ += rest_of_frame;
- DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
// spill xmms
int64_t offset = rest_of_frame;
@@ -2282,12 +2270,6 @@
void X86_64Assembler::IncreaseFrameSize(size_t adjust) {
CHECK_ALIGNED(adjust, kStackAlignment);
addq(CpuRegister(RSP), Immediate(-static_cast<int64_t>(adjust)));
- // DW_CFA_advance_loc
- DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
- cfi_pc_ = buffer_.Size();
- // DW_CFA_def_cfa_offset
- cfi_cfa_offset_ += adjust;
- DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
}
void X86_64Assembler::DecreaseFrameSize(size_t adjust) {
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 39f781c..d357a81 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -244,7 +244,7 @@
class X86_64Assembler FINAL : public Assembler {
public:
- X86_64Assembler() : cfi_cfa_offset_(0), cfi_pc_(0) {}
+ X86_64Assembler() {}
virtual ~X86_64Assembler() {}
/*
@@ -468,6 +468,7 @@
void imull(CpuRegister reg, const Immediate& imm);
void imull(CpuRegister reg, const Address& address);
+ void imulq(CpuRegister src);
void imulq(CpuRegister dst, CpuRegister src);
void imulq(CpuRegister reg, const Immediate& imm);
void imulq(CpuRegister reg, const Address& address);
@@ -517,6 +518,7 @@
X86_64Assembler* lock();
void cmpxchgl(const Address& address, CpuRegister reg);
+ void cmpxchgq(const Address& address, CpuRegister reg);
void mfence();
@@ -539,6 +541,10 @@
lock()->cmpxchgl(address, reg);
}
+ void LockCmpxchgq(const Address& address, CpuRegister reg) {
+ lock()->cmpxchgq(address, reg);
+ }
+
//
// Misc. functionality
//
@@ -663,12 +669,6 @@
// and branch to a ExceptionSlowPath if it is.
void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust) OVERRIDE;
- void InitializeFrameDescriptionEntry() OVERRIDE;
- void FinalizeFrameDescriptionEntry() OVERRIDE;
- std::vector<uint8_t>* GetFrameDescriptionEntry() OVERRIDE {
- return &cfi_info_;
- }
-
private:
void EmitUint8(uint8_t value);
void EmitInt32(int32_t value);
@@ -714,9 +714,6 @@
void EmitOptionalByteRegNormalizingRex32(CpuRegister dst, CpuRegister src);
void EmitOptionalByteRegNormalizingRex32(CpuRegister dst, const Operand& operand);
- std::vector<uint8_t> cfi_info_;
- uint32_t cfi_cfa_offset_, cfi_pc_;
-
DISALLOW_COPY_AND_ASSIGN(X86_64Assembler);
};
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 4402dfc..86bdf20 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -269,6 +269,10 @@
DriverStr(Repeatri(&x86_64::X86_64Assembler::addl, 4U, "add ${imm}, %{reg}"), "addli");
}
+TEST_F(AssemblerX86_64Test, ImulqReg1) {
+ DriverStr(RepeatR(&x86_64::X86_64Assembler::imulq, "imulq %{reg}"), "imulq");
+}
+
TEST_F(AssemblerX86_64Test, ImulqRegs) {
DriverStr(RepeatRR(&x86_64::X86_64Assembler::imulq, "imulq %{reg2}, %{reg1}"), "imulq");
}
@@ -539,6 +543,56 @@
// DriverStr(Repeatrr(&x86_64::X86_64Assembler::xchgl, "xchgl %{reg2}, %{reg1}"), "xchgl");
}
+TEST_F(AssemblerX86_64Test, LockCmpxchgl) {
+ GetAssembler()->LockCmpxchgl(x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12),
+ x86_64::CpuRegister(x86_64::RSI));
+ GetAssembler()->LockCmpxchgl(x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12),
+ x86_64::CpuRegister(x86_64::RSI));
+ GetAssembler()->LockCmpxchgl(x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12),
+ x86_64::CpuRegister(x86_64::R8));
+ GetAssembler()->LockCmpxchgl(x86_64::Address(
+ x86_64::CpuRegister(x86_64::R13), 0), x86_64::CpuRegister(x86_64::RSI));
+ GetAssembler()->LockCmpxchgl(x86_64::Address(
+ x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0),
+ x86_64::CpuRegister(x86_64::RSI));
+ const char* expected =
+ "lock cmpxchgl %ESI, 0xc(%RDI,%RBX,4)\n"
+ "lock cmpxchgl %ESI, 0xc(%RDI,%R9,4)\n"
+ "lock cmpxchgl %R8d, 0xc(%RDI,%R9,4)\n"
+ "lock cmpxchgl %ESI, (%R13)\n"
+ "lock cmpxchgl %ESI, (%R13,%R9,1)\n";
+
+ DriverStr(expected, "lock_cmpxchgl");
+}
+
+TEST_F(AssemblerX86_64Test, LockCmpxchgq) {
+ GetAssembler()->LockCmpxchgq(x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12),
+ x86_64::CpuRegister(x86_64::RSI));
+ GetAssembler()->LockCmpxchgq(x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12),
+ x86_64::CpuRegister(x86_64::RSI));
+ GetAssembler()->LockCmpxchgq(x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12),
+ x86_64::CpuRegister(x86_64::R8));
+ GetAssembler()->LockCmpxchgq(x86_64::Address(
+ x86_64::CpuRegister(x86_64::R13), 0), x86_64::CpuRegister(x86_64::RSI));
+ GetAssembler()->LockCmpxchgq(x86_64::Address(
+ x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0),
+ x86_64::CpuRegister(x86_64::RSI));
+ const char* expected =
+ "lock cmpxchg %RSI, 0xc(%RDI,%RBX,4)\n"
+ "lock cmpxchg %RSI, 0xc(%RDI,%R9,4)\n"
+ "lock cmpxchg %R8, 0xc(%RDI,%R9,4)\n"
+ "lock cmpxchg %RSI, (%R13)\n"
+ "lock cmpxchg %RSI, (%R13,%R9,1)\n";
+
+ DriverStr(expected, "lock_cmpxchg");
+}
+
TEST_F(AssemblerX86_64Test, Movl) {
GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::Address(
x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12));
diff --git a/compiler/utils/x86_64/managed_register_x86_64.h b/compiler/utils/x86_64/managed_register_x86_64.h
index 3a96ad0..822659f 100644
--- a/compiler/utils/x86_64/managed_register_x86_64.h
+++ b/compiler/utils/x86_64/managed_register_x86_64.h
@@ -87,21 +87,6 @@
// There is a one-to-one mapping between ManagedRegister and register id.
class X86_64ManagedRegister : public ManagedRegister {
public:
- int DWARFRegId() const {
- CHECK(IsCpuRegister());
- switch (id_) {
- case RAX: return 0;
- case RDX: return 1;
- case RCX: return 2;
- case RBX: return 3;
- case RSI: return 4;
- case RDI: return 5;
- case RBP: return 6;
- case RSP: return 7;
- default: return static_cast<int>(id_); // R8 ~ R15
- }
- }
-
CpuRegister AsCpuRegister() const {
CHECK(IsCpuRegister());
return CpuRegister(static_cast<Register>(id_));
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
index c9aa8c8..1cfd45a 100644
--- a/disassembler/Android.mk
+++ b/disassembler/Android.mk
@@ -81,6 +81,8 @@
endif
LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
+ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+ LOCAL_MULTILIB := both
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index b27b555..e2b7341 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -228,7 +228,6 @@
}
continue; // No ", ".
}
- break;
case 'I': // Upper 16-bit immediate.
args << reinterpret_cast<void*>((instruction & 0xffff) << 16);
break;
diff --git a/disassembler/disassembler_mips64.cc b/disassembler/disassembler_mips64.cc
index 7b289d0..f1c7d8e 100644
--- a/disassembler/disassembler_mips64.cc
+++ b/disassembler/disassembler_mips64.cc
@@ -185,7 +185,7 @@
return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
}
-static void DumpMips64(std::ostream& os, const uint8_t* instr_ptr) {
+size_t DisassemblerMips64::Dump(std::ostream& os, const uint8_t* instr_ptr) {
uint32_t instruction = ReadU32(instr_ptr);
uint32_t rs = (instruction >> 21) & 0x1f; // I-type, R-type.
@@ -233,7 +233,6 @@
}
continue; // No ", ".
}
- break;
case 'I': // Upper 16-bit immediate.
args << reinterpret_cast<void*>((instruction & 0xffff) << 16);
break;
@@ -273,19 +272,16 @@
}
}
- os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str())
+ os << FormatInstructionPointer(instr_ptr)
+ << StringPrintf(": %08x\t%-7s ", instruction, opcode.c_str())
<< args.str() << '\n';
-}
-
-size_t DisassemblerMips64::Dump(std::ostream& os, const uint8_t* begin) {
- DumpMips64(os, begin);
return 4;
}
void DisassemblerMips64::Dump(std::ostream& os, const uint8_t* begin,
const uint8_t* end) {
for (const uint8_t* cur = begin; cur < end; cur += 4) {
- DumpMips64(os, cur);
+ Dump(os, cur);
}
}
diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index 325b283..3e8b367 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -95,6 +95,13 @@
// Need to work out the size of the instruction that caused the exception.
uint8_t* ptr = reinterpret_cast<uint8_t*>(sc->arm_pc);
VLOG(signals) << "pc: " << std::hex << static_cast<void*>(ptr);
+
+ if (ptr == nullptr) {
+ // Somebody jumped to 0x0. Definitely not ours, and will definitely segfault below.
+ *out_method = nullptr;
+ return;
+ }
+
uint32_t instr_size = GetInstructionSize(ptr);
*out_return_pc = (sc->arm_pc + instr_size) | 1;
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index f8a9f9d..1f2ce02 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -25,7 +25,7 @@
namespace art {
const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromVariant(
- const std::string& variant ATTRIBUTE_UNUSED, std::string* error_msg ATTRIBUTE_UNUSED) {
+ const std::string& variant, std::string* error_msg) {
const bool smp = true; // Conservative default.
// Look for variants that need a fix for a53 erratum 835769.
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index ff57603..b4de879 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -32,7 +32,7 @@
// xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] .
THIS_LOAD_REQUIRES_READ_BARRIER
- ldr xIP0, [xIP0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET ]
+ ldr wIP0, [xIP0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET ]
sub sp, sp, #176
.cfi_adjust_cfa_offset 176
@@ -97,7 +97,7 @@
// xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] .
THIS_LOAD_REQUIRES_READ_BARRIER
- ldr xIP0, [xIP0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET ]
+ ldr wIP0, [xIP0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET ]
sub sp, sp, #96
.cfi_adjust_cfa_offset 96
@@ -266,7 +266,7 @@
// xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] .
THIS_LOAD_REQUIRES_READ_BARRIER
- ldr xIP0, [xIP0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET ]
+ ldr wIP0, [xIP0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET ]
SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL
diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc
index db4b0b1..898f83a 100644
--- a/runtime/arch/instruction_set_features.cc
+++ b/runtime/arch/instruction_set_features.cc
@@ -288,10 +288,10 @@
return down_cast<const X86_64InstructionSetFeatures*>(this);
}
-bool InstructionSetFeatures::FindVariantInArray(const char* variants[], size_t num_variants,
+bool InstructionSetFeatures::FindVariantInArray(const char* const variants[], size_t num_variants,
const std::string& variant) {
- const char** begin = variants;
- const char** end = begin + num_variants;
+ const char* const * begin = variants;
+ const char* const * end = begin + num_variants;
return std::find(begin, end, variant) != end;
}
diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h
index e4513ef..d10ae21 100644
--- a/runtime/arch/instruction_set_features.h
+++ b/runtime/arch/instruction_set_features.h
@@ -103,7 +103,7 @@
explicit InstructionSetFeatures(bool smp) : smp_(smp) {}
// Returns true if variant appears in the array variants.
- static bool FindVariantInArray(const char* variants[], size_t num_variants,
+ static bool FindVariantInArray(const char* const variants[], size_t num_variants,
const std::string& variant);
// Add architecture specific features in sub-classes.
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.cc b/runtime/arch/mips64/instruction_set_features_mips64.cc
index 26478cb..5c0c914 100644
--- a/runtime/arch/mips64/instruction_set_features_mips64.cc
+++ b/runtime/arch/mips64/instruction_set_features_mips64.cc
@@ -26,9 +26,7 @@
const Mips64InstructionSetFeatures* Mips64InstructionSetFeatures::FromVariant(
const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED) {
- // TODO: r6 variant.
- if (variant != "default") {
- std::ostringstream os;
+ if (variant != "default" && variant != "mips64r6") {
LOG(WARNING) << "Unexpected CPU variant for Mips64 using defaults: " << variant;
}
bool smp = true; // Conservative default.
diff --git a/runtime/arch/mips64/jni_entrypoints_mips64.S b/runtime/arch/mips64/jni_entrypoints_mips64.S
index 90fd3ee..1085666 100644
--- a/runtime/arch/mips64/jni_entrypoints_mips64.S
+++ b/runtime/arch/mips64/jni_entrypoints_mips64.S
@@ -28,21 +28,21 @@
.cfi_adjust_cfa_offset 80
sd $ra, 64($sp)
.cfi_rel_offset 31, 64
- sw $a7, 56($sp)
+ sd $a7, 56($sp)
.cfi_rel_offset 11, 56
- sw $a6, 48($sp)
+ sd $a6, 48($sp)
.cfi_rel_offset 10, 48
- sw $a5, 40($sp)
+ sd $a5, 40($sp)
.cfi_rel_offset 9, 40
- sw $a4, 32($sp)
+ sd $a4, 32($sp)
.cfi_rel_offset 8, 32
- sw $a3, 24($sp)
+ sd $a3, 24($sp)
.cfi_rel_offset 7, 24
- sw $a2, 16($sp)
+ sd $a2, 16($sp)
.cfi_rel_offset 6, 16
- sw $a1, 8($sp)
+ sd $a1, 8($sp)
.cfi_rel_offset 5, 8
- sw $a0, 0($sp)
+ sd $a0, 0($sp)
.cfi_rel_offset 4, 0
jal artFindNativeMethod # (Thread*)
move $a0, $s1 # pass Thread::Current()
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 697bf00..3d502e6 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -77,7 +77,7 @@
ld $v0, %got(_ZN3art7Runtime9instance_E)($gp)
ld $v0, 0($v0)
THIS_LOAD_REQUIRES_READ_BARRIER
- ld $v0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET($v0)
+ lwu $v0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET($v0)
sw $v0, 0($sp) # Place Method* at bottom of stack.
sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame.
.endm
@@ -120,7 +120,7 @@
ld $v0, %got(_ZN3art7Runtime9instance_E)($gp)
ld $v0, 0($v0)
THIS_LOAD_REQUIRES_READ_BARRIER
- ld $v0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($v0)
+ lwu $v0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($v0)
sw $v0, 0($sp) # Place Method* at bottom of stack.
sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame.
.endm
@@ -237,7 +237,7 @@
ld $v0, %got(_ZN3art7Runtime9instance_E)($gp)
ld $v0, 0($v0)
THIS_LOAD_REQUIRES_READ_BARRIER
- ld $v0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($v0)
+ lwu $v0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($v0)
sw $v0, 0($sp) # Place Method* at bottom of stack.
sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame.
.endm
@@ -248,7 +248,7 @@
ld $v0, %got(_ZN3art7Runtime9instance_E)($gp)
ld $v0, 0($v0)
THIS_LOAD_REQUIRES_READ_BARRIER
- ld $v0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET($v0)
+ lwu $v0, RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET($v0)
sw $v0, 0($sp) # Place Method* at bottom of stack.
sd $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # Place sp in Thread::Current()->top_quick_frame.
.endm
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index ad962e2..27a4adf 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -275,6 +275,12 @@
uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP);
VLOG(signals) << HexDump(pc, 32, true, "PC ");
+ if (pc == nullptr) {
+ // Somebody jumped to 0x0. Definitely not ours, and will definitely segfault below.
+ *out_method = nullptr;
+ return;
+ }
+
uint32_t instr_size = GetInstructionSize(pc);
if (instr_size == 0) {
// Unknown instruction, tell caller it's not ours.
diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc
index a12773d..ef39999 100644
--- a/runtime/arch/x86/instruction_set_features_x86.cc
+++ b/runtime/arch/x86/instruction_set_features_x86.cc
@@ -25,22 +25,44 @@
namespace art {
+// Feature-support arrays.
+
+static constexpr const char* x86_known_variants[] = {
+ "atom",
+ "silvermont",
+};
+
+static constexpr const char* x86_variants_with_ssse3[] = {
+ "atom",
+ "silvermont",
+};
+
+static constexpr const char* x86_variants_with_sse4_1[] = {
+ "silvermont",
+};
+
+static constexpr const char* x86_variants_with_sse4_2[] = {
+ "silvermont",
+};
+
const X86InstructionSetFeatures* X86InstructionSetFeatures::FromVariant(
- const std::string& variant ATTRIBUTE_UNUSED, std::string* error_msg ATTRIBUTE_UNUSED,
+ const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED,
bool x86_64) {
- bool known_variant = false;
bool smp = true; // Conservative default.
- static const char* x86_variants_with_ssse3[] = {
- "atom"
- };
bool has_SSSE3 = FindVariantInArray(x86_variants_with_ssse3, arraysize(x86_variants_with_ssse3),
variant);
- bool has_SSE4_1 = false;
- bool has_SSE4_2 = false;
+ bool has_SSE4_1 = FindVariantInArray(x86_variants_with_sse4_1,
+ arraysize(x86_variants_with_sse4_1),
+ variant);
+ bool has_SSE4_2 = FindVariantInArray(x86_variants_with_sse4_2,
+ arraysize(x86_variants_with_sse4_2),
+ variant);
bool has_AVX = false;
bool has_AVX2 = false;
+
+ bool known_variant = FindVariantInArray(x86_known_variants, arraysize(x86_known_variants),
+ variant);
if (!known_variant && variant != "default") {
- std::ostringstream os;
LOG(WARNING) << "Unexpected CPU variant for X86 using defaults: " << variant;
}
diff --git a/runtime/arch/x86/instruction_set_features_x86_test.cc b/runtime/arch/x86/instruction_set_features_x86_test.cc
index d231beb..25a406b 100644
--- a/runtime/arch/x86/instruction_set_features_x86_test.cc
+++ b/runtime/arch/x86/instruction_set_features_x86_test.cc
@@ -67,4 +67,40 @@
EXPECT_FALSE(x86_features->Equals(x86_default_features.get()));
}
+TEST(X86InstructionSetFeaturesTest, X86FeaturesFromSilvermontVariant) {
+ // Build features for a 32-bit x86 silvermont processor.
+ std::string error_msg;
+ std::unique_ptr<const InstructionSetFeatures> x86_features(
+ InstructionSetFeatures::FromVariant(kX86, "silvermont", &error_msg));
+ ASSERT_TRUE(x86_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_features->GetInstructionSet(), kX86);
+ EXPECT_TRUE(x86_features->Equals(x86_features.get()));
+ EXPECT_STREQ("smp,ssse3,sse4.1,sse4.2,-avx,-avx2", x86_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_features->AsBitmap(), 15U);
+
+ // Build features for a 32-bit x86 default processor.
+ std::unique_ptr<const InstructionSetFeatures> x86_default_features(
+ InstructionSetFeatures::FromVariant(kX86, "default", &error_msg));
+ ASSERT_TRUE(x86_default_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_default_features->GetInstructionSet(), kX86);
+ EXPECT_TRUE(x86_default_features->Equals(x86_default_features.get()));
+ EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2",
+ x86_default_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_default_features->AsBitmap(), 1U);
+
+ // Build features for a 64-bit x86-64 silvermont processor.
+ std::unique_ptr<const InstructionSetFeatures> x86_64_features(
+ InstructionSetFeatures::FromVariant(kX86_64, "silvermont", &error_msg));
+ ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_64_features->GetInstructionSet(), kX86_64);
+ EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get()));
+ EXPECT_STREQ("smp,ssse3,sse4.1,sse4.2,-avx,-avx2",
+ x86_64_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_64_features->AsBitmap(), 15U);
+
+ EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
+ EXPECT_FALSE(x86_64_features->Equals(x86_default_features.get()));
+ EXPECT_FALSE(x86_features->Equals(x86_default_features.get()));
+}
+
} // namespace art
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 3a448a5..ce21f01 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -67,7 +67,7 @@
movq %xmm15, 32(%rsp)
// R10 := ArtMethod* for save all callee save frame method.
THIS_LOAD_REQUIRES_READ_BARRIER
- movq RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
+ movl RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10d
// Store ArtMethod* to bottom of stack.
movq %r10, 0(%rsp)
// Store rsp as the top quick frame.
@@ -110,7 +110,7 @@
movq %xmm15, 32(%rsp)
// R10 := ArtMethod* for refs only callee save frame method.
THIS_LOAD_REQUIRES_READ_BARRIER
- movq RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
+ movl RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10d
// Store ArtMethod* to bottom of stack.
movq %r10, 0(%rsp)
// Store rsp as the stop quick frame.
@@ -170,7 +170,7 @@
CFI_ADJUST_CFA_OFFSET(80 + 4 * 8)
// R10 := ArtMethod* for ref and args callee save frame method.
THIS_LOAD_REQUIRES_READ_BARRIER
- movq RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
+ movl RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10d
// Save FPRs.
movq %xmm0, 16(%rsp)
movq %xmm1, 24(%rsp)
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 0d0017d..dba4af8 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -57,6 +57,11 @@
#define STACK_REFERENCE_SIZE 4
ADD_TEST_EQ(static_cast<size_t>(STACK_REFERENCE_SIZE), sizeof(art::StackReference<art::mirror::Object>))
+// Size of heap references
+#define COMPRESSED_REFERENCE_SIZE 4
+ADD_TEST_EQ(static_cast<size_t>(COMPRESSED_REFERENCE_SIZE),
+ sizeof(art::mirror::CompressedReference<art::mirror::Object>))
+
// Note: these callee save methods loads require read barriers.
// Offset of field Runtime::callee_save_methods_[kSaveAll]
#define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 0
@@ -64,12 +69,12 @@
art::Runtime::GetCalleeSaveMethodOffset(art::Runtime::kSaveAll))
// Offset of field Runtime::callee_save_methods_[kRefsOnly]
-#define RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET __SIZEOF_POINTER__
+#define RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET COMPRESSED_REFERENCE_SIZE
ADD_TEST_EQ(static_cast<size_t>(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET),
art::Runtime::GetCalleeSaveMethodOffset(art::Runtime::kRefsOnly))
// Offset of field Runtime::callee_save_methods_[kRefsAndArgs]
-#define RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET (2 * __SIZEOF_POINTER__)
+#define RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET (2 * COMPRESSED_REFERENCE_SIZE)
ADD_TEST_EQ(static_cast<size_t>(RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET),
art::Runtime::GetCalleeSaveMethodOffset(art::Runtime::kRefsAndArgs))
diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h
index 2d67c8b..07daa7e 100644
--- a/runtime/base/allocator.h
+++ b/runtime/base/allocator.h
@@ -114,12 +114,12 @@
// Used internally by STL data structures.
template <class U>
- TrackingAllocatorImpl(const TrackingAllocatorImpl<U, kTag>& alloc) throw() {
+ TrackingAllocatorImpl(const TrackingAllocatorImpl<U, kTag>& alloc) noexcept {
UNUSED(alloc);
}
// Used internally by STL data structures.
- TrackingAllocatorImpl() throw() {
+ TrackingAllocatorImpl() noexcept {
static_assert(kTag < kAllocatorTagCount, "kTag must be less than kAllocatorTagCount");
}
diff --git a/runtime/base/arena_containers.h b/runtime/base/arena_containers.h
index e6fe6c0..d6c4a54 100644
--- a/runtime/base/arena_containers.h
+++ b/runtime/base/arena_containers.h
@@ -67,6 +67,7 @@
public:
// Not tracking allocations, ignore the supplied kind and arbitrarily provide kArenaAllocSTL.
explicit ArenaAllocatorAdapterKindImpl(ArenaAllocKind kind ATTRIBUTE_UNUSED) {}
+ ArenaAllocatorAdapterKindImpl(const ArenaAllocatorAdapterKindImpl&) = default;
ArenaAllocatorAdapterKindImpl& operator=(const ArenaAllocatorAdapterKindImpl&) = default;
ArenaAllocKind Kind() { return kArenaAllocSTL; }
};
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index 3a9de5f..6c33232 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -66,7 +66,7 @@
// A macro to disallow new and delete operators for a class. It goes in the private: declarations.
#define DISALLOW_ALLOCATION() \
public: \
- ALWAYS_INLINE void operator delete(void*, size_t) { UNREACHABLE(); } \
+ NO_RETURN ALWAYS_INLINE void operator delete(void*, size_t) { UNREACHABLE(); } \
private: \
void* operator new(size_t) = delete
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 6e7b04f..af00834 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -97,6 +97,7 @@
kAllocTrackerLock,
kDeoptimizationLock,
kProfilerLock,
+ kJdwpShutdownLock,
kJdwpEventListLock,
kJdwpAttachLock,
kJdwpStartLock,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index a89196d..12fa546 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -242,7 +242,10 @@
quick_generic_jni_trampoline_(nullptr),
quick_to_interpreter_bridge_trampoline_(nullptr),
image_pointer_size_(sizeof(void*)) {
- memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
+ CHECK(intern_table_ != nullptr);
+ for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
+ find_array_class_cache_[i] = GcRoot<mirror::Class>(nullptr);
+ }
}
void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path) {
@@ -693,35 +696,6 @@
return *oat_file;
}
-const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFileForDexFile(const DexFile& dex_file) {
- const char* dex_location = dex_file.GetLocation().c_str();
- uint32_t dex_location_checksum = dex_file.GetLocationChecksum();
- return FindOpenedOatDexFile(nullptr, dex_location, &dex_location_checksum);
-}
-
-const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFile(const char* oat_location,
- const char* dex_location,
- const uint32_t* dex_location_checksum) {
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- for (const OatFile* oat_file : oat_files_) {
- DCHECK(oat_file != nullptr);
-
- if (oat_location != nullptr) {
- if (oat_file->GetLocation() != oat_location) {
- continue;
- }
- }
-
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
- dex_location_checksum,
- false);
- if (oat_dex_file != nullptr) {
- return oat_dex_file;
- }
- }
- return nullptr;
-}
-
std::vector<std::unique_ptr<const DexFile>> ClassLinker::OpenDexFilesFromOat(
const char* dex_location, const char* oat_location,
std::vector<std::string>* error_msgs) {
@@ -937,19 +911,21 @@
VLOG(startup) << "ClassLinker::InitFromImage exiting";
}
-void ClassLinker::VisitClassRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
+void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) {
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
if ((flags & kVisitRootFlagAllRoots) != 0) {
+ BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
+ visitor, RootInfo(kRootStickyClass));
for (GcRoot<mirror::Class>& root : class_table_) {
- root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
+ buffered_visitor.VisitRoot(root);
}
for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
- root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
+ buffered_visitor.VisitRoot(root);
}
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_class_roots_) {
mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
- root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
+ root.VisitRoot(visitor, RootInfo(kRootStickyClass));
mirror::Class* new_ref = root.Read<kWithoutReadBarrier>();
if (UNLIKELY(new_ref != old_ref)) {
// Uh ohes, GC moved a root in the log. Need to search the class_table and update the
@@ -976,18 +952,18 @@
// Keep in sync with InitCallback. Anything we visit, we need to
// reinit references to when reinitializing a ClassLinker from a
// mapped image.
-void ClassLinker::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
- class_roots_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
- Thread* self = Thread::Current();
+void ClassLinker::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) {
+ class_roots_.VisitRoot(visitor, RootInfo(kRootVMInternal));
+ Thread* const self = Thread::Current();
{
ReaderMutexLock mu(self, dex_lock_);
if ((flags & kVisitRootFlagAllRoots) != 0) {
for (GcRoot<mirror::DexCache>& dex_cache : dex_caches_) {
- dex_cache.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
+ dex_cache.VisitRoot(visitor, RootInfo(kRootVMInternal));
}
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (size_t index : new_dex_cache_roots_) {
- dex_caches_[index].VisitRoot(callback, arg, RootInfo(kRootVMInternal));
+ dex_caches_[index].VisitRoot(visitor, RootInfo(kRootVMInternal));
}
}
if ((flags & kVisitRootFlagClearRootLog) != 0) {
@@ -999,11 +975,10 @@
log_new_dex_caches_roots_ = false;
}
}
- VisitClassRoots(callback, arg, flags);
- array_iftable_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
- DCHECK(!array_iftable_.IsNull());
+ VisitClassRoots(visitor, flags);
+ array_iftable_.VisitRoot(visitor, RootInfo(kRootVMInternal));
for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
- find_array_class_cache_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+ find_array_class_cache_[i].VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
}
}
@@ -1600,7 +1575,7 @@
OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx,
bool* found) {
DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16);
- const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFileForDexFile(dex_file);
+ const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
if (oat_dex_file == nullptr) {
*found = false;
return OatFile::OatClass::Invalid();
@@ -2813,7 +2788,7 @@
}
}
- const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFileForDexFile(dex_file);
+ const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
// In case we run without an image there won't be a backing oat file.
if (oat_dex_file == nullptr) {
return false;
@@ -3411,7 +3386,7 @@
// so we need to throw it again now.
VLOG(compiler) << "Return from class initializer of " << PrettyDescriptor(klass.Get())
<< " without exception while transaction was aborted: re-throw it now.";
- Runtime::Current()->ThrowInternalErrorForAbortedTransaction(self);
+ Runtime::Current()->ThrowTransactionAbortError(self);
mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
success = false;
} else {
@@ -3845,9 +3820,19 @@
// Now comes the expensive part: things can be broken if (a) the klass' dex file has a
// definition for the super-class, and (b) the files are in separate oat files. The oat files
// are referenced from the dex file, so do (b) first. Only relevant if we have oat files.
- const OatFile* class_oat_file = dex_file.GetOatFile();
+ const OatDexFile* class_oat_dex_file = dex_file.GetOatDexFile();
+ const OatFile* class_oat_file = nullptr;
+ if (class_oat_dex_file != nullptr) {
+ class_oat_file = class_oat_dex_file->GetOatFile();
+ }
+
if (class_oat_file != nullptr) {
- const OatFile* loaded_super_oat_file = super_class->GetDexFile().GetOatFile();
+ const OatDexFile* loaded_super_oat_dex_file = super_class->GetDexFile().GetOatDexFile();
+ const OatFile* loaded_super_oat_file = nullptr;
+ if (loaded_super_oat_dex_file != nullptr) {
+ loaded_super_oat_file = loaded_super_oat_dex_file->GetOatFile();
+ }
+
if (loaded_super_oat_file != nullptr && class_oat_file != loaded_super_oat_file) {
// Now check (a).
const DexFile::ClassDef* super_class_def = dex_file.FindClassDef(class_def.superclass_idx_);
@@ -5293,9 +5278,8 @@
if (m->IsPrivate()) {
// The method can only be called inside its own oat file. Therefore it won't be called using
// its direct code if the oat file has been compiled in PIC mode.
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
const DexFile& dex_file = m->GetDeclaringClass()->GetDexFile();
- const OatFile::OatDexFile* oat_dex_file = class_linker->FindOpenedOatDexFileForDexFile(dex_file);
+ const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
if (oat_dex_file == nullptr) {
// No oat file: the method has not been compiled.
return false;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index ec984cb..577fec2 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -299,10 +299,10 @@
void VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitClassRoots(RootCallback* callback, void* arg, VisitRootFlags flags)
+ void VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags)
LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags)
+ void VisitRoots(RootVisitor* visitor, VisitRootFlags flags)
LOCKS_EXCLUDED(dex_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -600,17 +600,6 @@
}
mirror::DexCache* GetDexCache(size_t idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, dex_lock_);
- const OatFile::OatDexFile* FindOpenedOatDexFileForDexFile(const DexFile& dex_file)
- LOCKS_EXCLUDED(dex_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- // Find an opened oat dex file that contains dex_location. If oat_location is not nullptr,
- // the file must have that location, else any oat location is accepted.
- const OatFile::OatDexFile* FindOpenedOatDexFile(const char* oat_location,
- const char* dex_location,
- const uint32_t* dex_location_checksum)
- LOCKS_EXCLUDED(dex_lock_);
-
const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location)
LOCKS_EXCLUDED(dex_lock_);
@@ -739,8 +728,6 @@
friend class ImageWriter; // for GetClassRoots
friend class ImageDumper; // for FindOpenedOatFileFromOatLocation
friend class JniCompilerTest; // for GetRuntimeQuickGenericJniStub
- friend class NoDex2OatTest; // for FindOpenedOatFileForDexFile
- friend class NoPatchoatTest; // for FindOpenedOatFileForDexFile
ART_FRIEND_TEST(mirror::DexCacheTest, Open); // for AllocDexCache
DISALLOW_COPY_AND_ASSIGN(ClassLinker);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 3e727e7..3f6c5a0 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -358,7 +358,8 @@
const char* descriptor = dex.GetTypeDescriptor(type_id);
AssertDexFileClass(class_loader, descriptor);
}
- class_linker_->VisitRoots(TestRootVisitor, nullptr, kVisitRootFlagAllRoots);
+ TestRootVisitor visitor;
+ class_linker_->VisitRoots(&visitor, kVisitRootFlagAllRoots);
// Verify the dex cache has resolution methods in all resolved method slots
mirror::DexCache* dex_cache = class_linker_->FindDexCache(dex);
mirror::ObjectArray<mirror::ArtMethod>* resolved_methods = dex_cache->GetResolvedMethods();
@@ -367,9 +368,12 @@
}
}
- static void TestRootVisitor(mirror::Object** root, void*, const RootInfo&) {
- EXPECT_TRUE(*root != nullptr);
- }
+ class TestRootVisitor : public SingleRootVisitor {
+ public:
+ void VisitRoot(mirror::Object* root, const RootInfo& info ATTRIBUTE_UNUSED) OVERRIDE {
+ EXPECT_TRUE(root != nullptr);
+ }
+ };
};
struct CheckOffset {
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index a767cf0..a909a1a 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -307,7 +307,6 @@
// Runtime JDWP state.
static JDWP::JdwpState* gJdwpState = nullptr;
static bool gDebuggerConnected; // debugger or DDMS is connected.
-static bool gDisposed; // debugger called VirtualMachine.Dispose, so we should drop the connection.
static bool gDdmThreadNotification = false;
@@ -319,6 +318,7 @@
static Dbg::HpsgWhat gDdmNhsgWhat;
bool Dbg::gDebuggerActive = false;
+bool Dbg::gDisposed = false;
ObjectRegistry* Dbg::gRegistry = nullptr;
// Recent allocation tracking.
@@ -344,16 +344,14 @@
// Breakpoints.
static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
-void DebugInvokeReq::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
- receiver.VisitRootIfNonNull(callback, arg, root_info); // null for static method call.
- klass.VisitRoot(callback, arg, root_info);
- method.VisitRoot(callback, arg, root_info);
+void DebugInvokeReq::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
+ receiver.VisitRootIfNonNull(visitor, root_info); // null for static method call.
+ klass.VisitRoot(visitor, root_info);
+ method.VisitRoot(visitor, root_info);
}
-void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
- if (method_ != nullptr) {
- callback(reinterpret_cast<mirror::Object**>(&method_), arg, root_info);
- }
+void SingleStepControl::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
+ visitor->VisitRootIfNonNull(reinterpret_cast<mirror::Object**>(&method_), root_info);
}
void SingleStepControl::AddDexPc(uint32_t dex_pc) {
@@ -553,7 +551,7 @@
gJdwpState->PostVMDeath();
}
// Prevent the JDWP thread from processing JDWP incoming packets after we close the connection.
- Disposed();
+ Dispose();
delete gJdwpState;
gJdwpState = nullptr;
delete gRegistry;
@@ -601,14 +599,6 @@
gDisposed = false;
}
-void Dbg::Disposed() {
- gDisposed = true;
-}
-
-bool Dbg::IsDisposed() {
- return gDisposed;
-}
-
bool Dbg::RequiresDeoptimization() {
// We don't need deoptimization if everything runs with interpreter after
// enabling -Xint mode.
@@ -1285,18 +1275,37 @@
return JDWP::ERR_NONE;
}
-JDWP::ObjectId Dbg::CreateString(const std::string& str) {
- return gRegistry->Add(mirror::String::AllocFromModifiedUtf8(Thread::Current(), str.c_str()));
+JDWP::JdwpError Dbg::CreateString(const std::string& str, JDWP::ObjectId* new_string_id) {
+ Thread* self = Thread::Current();
+ mirror::String* new_string = mirror::String::AllocFromModifiedUtf8(self, str.c_str());
+ if (new_string == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ LOG(ERROR) << "Could not allocate string";
+ *new_string_id = 0;
+ return JDWP::ERR_OUT_OF_MEMORY;
+ }
+ *new_string_id = gRegistry->Add(new_string);
+ return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId* new_object) {
+JDWP::JdwpError Dbg::CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId* new_object_id) {
JDWP::JdwpError error;
mirror::Class* c = DecodeClass(class_id, &error);
if (c == nullptr) {
- *new_object = 0;
+ *new_object_id = 0;
return error;
}
- *new_object = gRegistry->Add(c->AllocObject(Thread::Current()));
+ Thread* self = Thread::Current();
+ mirror::Object* new_object = c->AllocObject(self);
+ if (new_object == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ LOG(ERROR) << "Could not allocate object of type " << PrettyDescriptor(c);
+ *new_object_id = 0;
+ return JDWP::ERR_OUT_OF_MEMORY;
+ }
+ *new_object_id = gRegistry->Add(new_object);
return JDWP::ERR_NONE;
}
@@ -1304,16 +1313,26 @@
* Used by Eclipse's "Display" view to evaluate "new byte[5]" to get "(byte[]) [0, 0, 0, 0, 0]".
*/
JDWP::JdwpError Dbg::CreateArrayObject(JDWP::RefTypeId array_class_id, uint32_t length,
- JDWP::ObjectId* new_array) {
+ JDWP::ObjectId* new_array_id) {
JDWP::JdwpError error;
mirror::Class* c = DecodeClass(array_class_id, &error);
if (c == nullptr) {
- *new_array = 0;
+ *new_array_id = 0;
return error;
}
- *new_array = gRegistry->Add(mirror::Array::Alloc<true>(Thread::Current(), c, length,
- c->GetComponentSizeShift(),
- Runtime::Current()->GetHeap()->GetCurrentAllocator()));
+ Thread* self = Thread::Current();
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ mirror::Array* new_array = mirror::Array::Alloc<true>(self, c, length,
+ c->GetComponentSizeShift(),
+ heap->GetCurrentAllocator());
+ if (new_array == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ LOG(ERROR) << "Could not allocate array of type " << PrettyDescriptor(c);
+ *new_array_id = 0;
+ return JDWP::ERR_OUT_OF_MEMORY;
+ }
+ *new_array_id = gRegistry->Add(new_array);
return JDWP::ERR_NONE;
}
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 4f4a781..dd7f9c5 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -81,7 +81,7 @@
Mutex lock DEFAULT_MUTEX_ACQUIRED_AFTER;
ConditionVariable cond GUARDED_BY(lock);
- void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
+ void VisitRoots(RootVisitor* visitor, const RootInfo& root_info)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
@@ -117,7 +117,7 @@
return dex_pcs_;
}
- void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
+ void VisitRoots(RootVisitor* visitor, const RootInfo& root_info)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void AddDexPc(uint32_t dex_pc);
@@ -239,7 +239,9 @@
static void GoActive()
LOCKS_EXCLUDED(Locks::breakpoint_lock_, Locks::deoptimization_lock_, Locks::mutator_lock_);
static void Disconnected() LOCKS_EXCLUDED(Locks::deoptimization_lock_, Locks::mutator_lock_);
- static void Disposed();
+ static void Dispose() {
+ gDisposed = true;
+ }
// Returns true if we're actually debugging with a real debugger, false if it's
// just DDMS (or nothing at all).
@@ -255,9 +257,12 @@
// Returns true if a method has any breakpoints.
static bool MethodHasAnyBreakpoints(mirror::ArtMethod* method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::breakpoint_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(Locks::breakpoint_lock_);
- static bool IsDisposed();
+ static bool IsDisposed() {
+ return gDisposed;
+ }
/*
* Time, in milliseconds, since the last debugger activity. Does not
@@ -313,12 +318,12 @@
JDWP::Request* request)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static JDWP::ObjectId CreateString(const std::string& str)
+ static JDWP::JdwpError CreateString(const std::string& str, JDWP::ObjectId* new_string_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static JDWP::JdwpError CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId* new_object)
+ static JDWP::JdwpError CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId* new_object_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError CreateArrayObject(JDWP::RefTypeId array_class_id, uint32_t length,
- JDWP::ObjectId* new_array)
+ JDWP::ObjectId* new_array_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
//
@@ -648,7 +653,7 @@
static void DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void VisitRoots(RootCallback* callback, void* arg)
+ static void VisitRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
@@ -756,6 +761,10 @@
// Indicates whether the debugger is making requests.
static bool gDebuggerActive;
+ // Indicates whether we should drop the JDWP connection because the runtime stops or the
+ // debugger called VirtualMachine.Dispose.
+ static bool gDisposed;
+
// The registry mapping objects to JDWP ids.
static ObjectRegistry* gRegistry;
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index edd8bfe..8685d8e 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -340,11 +340,11 @@
const std::string& location,
uint32_t location_checksum,
MemMap* mem_map,
- const OatFile* oat_file,
+ const OatDexFile* oat_dex_file,
std::string* error_msg) {
CHECK_ALIGNED(base, 4); // various dex file structures must be word aligned
std::unique_ptr<DexFile> dex_file(
- new DexFile(base, size, location, location_checksum, mem_map, oat_file));
+ new DexFile(base, size, location, location_checksum, mem_map, oat_dex_file));
if (!dex_file->Init(error_msg)) {
dex_file.reset();
}
@@ -355,7 +355,7 @@
const std::string& location,
uint32_t location_checksum,
MemMap* mem_map,
- const OatFile* oat_file)
+ const OatDexFile* oat_dex_file)
: begin_(base),
size_(size),
location_(location),
@@ -370,7 +370,7 @@
class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
find_class_def_misses_(0),
class_def_index_(nullptr),
- oat_file_(oat_file) {
+ oat_dex_file_(oat_dex_file) {
CHECK(begin_ != NULL) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
}
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index da39573..8e2d6c2 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -44,7 +44,7 @@
} // namespace mirror
class ClassLinker;
class MemMap;
-class OatFile;
+class OatDexFile;
class Signature;
template<class T> class Handle;
class StringPiece;
@@ -392,9 +392,9 @@
static std::unique_ptr<const DexFile> Open(const uint8_t* base, size_t size,
const std::string& location,
uint32_t location_checksum,
- const OatFile* oat_file,
+ const OatDexFile* oat_dex_file,
std::string* error_msg) {
- return OpenMemory(base, size, location, location_checksum, NULL, oat_file, error_msg);
+ return OpenMemory(base, size, location, location_checksum, NULL, oat_dex_file, error_msg);
}
// Open all classesXXX.dex files from a zip archive.
@@ -904,8 +904,8 @@
// the dex_location where it's file name part has been made canonical.
static std::string GetDexCanonicalLocation(const char* dex_location);
- const OatFile* GetOatFile() const {
- return oat_file_;
+ const OatDexFile* GetOatDexFile() const {
+ return oat_dex_file_;
}
private:
@@ -944,14 +944,14 @@
const std::string& location,
uint32_t location_checksum,
MemMap* mem_map,
- const OatFile* oat_file,
+ const OatDexFile* oat_dex_file,
std::string* error_msg);
DexFile(const uint8_t* base, size_t size,
const std::string& location,
uint32_t location_checksum,
MemMap* mem_map,
- const OatFile* oat_file);
+ const OatDexFile* oat_dex_file);
// Top-level initializer that calls other Init methods.
bool Init(std::string* error_msg);
@@ -1035,9 +1035,10 @@
typedef HashMap<const char*, const ClassDef*, UTF16EmptyFn, UTF16HashCmp, UTF16HashCmp> Index;
mutable Atomic<Index*> class_def_index_;
- // The oat file this dex file was loaded from. May be null in case the dex file is not coming
- // from an oat file, e.g., directly from an apk.
- const OatFile* oat_file_;
+ // If this dex file was loaded from an oat file, oat_dex_file_ contains a
+ // pointer to the OatDexFile it was loaded from. Otherwise oat_dex_file_ is
+ // null.
+ const OatDexFile* oat_dex_file_;
};
struct DexFileReference {
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index bc5cf9b..411ec43 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -1630,8 +1630,10 @@
return frame->CIE_pointer != 0;
}
-static bool FixupEHFrame(off_t base_address_delta,
- uint8_t* eh_frame, size_t eh_frame_size) {
+template <typename Elf_SOff>
+static bool FixupEHFrame(Elf_SOff base_address_delta, uint8_t* eh_frame, size_t eh_frame_size) {
+ // TODO: Check the spec whether this is really data-dependent, or whether it's clear from the
+ // ELF file whether we should expect 32-bit or 64-bit.
if (*(reinterpret_cast<uint32_t*>(eh_frame)) == 0xffffffff) {
FDE64* last_frame = reinterpret_cast<FDE64*>(eh_frame + eh_frame_size);
FDE64* frame = NextFDE(reinterpret_cast<FDE64*>(eh_frame));
@@ -1643,6 +1645,7 @@
}
return true;
} else {
+ CHECK(IsInt<32>(base_address_delta));
FDE32* last_frame = reinterpret_cast<FDE32*>(eh_frame + eh_frame_size);
FDE32* frame = NextFDE(reinterpret_cast<FDE32*>(eh_frame));
for (; frame < last_frame; frame = NextFDE(frame)) {
@@ -1772,7 +1775,9 @@
uint8_t* current_instruction_;
};
-static bool FixupDebugLine(off_t base_offset_delta, DebugLineInstructionIterator* iter) {
+template <typename Elf_SOff>
+static bool FixupDebugLine(Elf_SOff base_offset_delta, DebugLineInstructionIterator* iter) {
+ CHECK(IsInt<32>(base_offset_delta));
for (; iter->GetInstruction(); iter->Next()) {
if (iter->IsExtendedOpcode() && iter->GetOpcode() == dwarf::DW_LNE_set_address) {
*reinterpret_cast<uint32_t*>(iter->GetArguments()) += base_offset_delta;
@@ -2044,7 +2049,9 @@
DebugTag* current_tag_;
};
-static bool FixupDebugInfo(off_t base_address_delta, DebugInfoIterator* iter) {
+template <typename Elf_SOff>
+static bool FixupDebugInfo(Elf_SOff base_address_delta, DebugInfoIterator* iter) {
+ CHECK(IsInt<32>(base_address_delta));
do {
if (iter->GetCurrentTag()->GetAttrSize(dwarf::DW_AT_low_pc) != sizeof(int32_t) ||
iter->GetCurrentTag()->GetAttrSize(dwarf::DW_AT_high_pc) != sizeof(int32_t)) {
@@ -2066,7 +2073,7 @@
typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
- ::FixupDebugSections(off_t base_address_delta) {
+ ::FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta) {
const Elf_Shdr* debug_info = FindSectionByName(".debug_info");
const Elf_Shdr* debug_abbrev = FindSectionByName(".debug_abbrev");
const Elf_Shdr* eh_frame = FindSectionByName(".eh_frame");
@@ -2280,7 +2287,7 @@
typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
- ::Fixup(uintptr_t base_address) {
+ ::Fixup(Elf_Addr base_address) {
if (!FixupDynamic(base_address)) {
LOG(WARNING) << "Failed to fixup .dynamic in " << file_->GetPath();
return false;
@@ -2305,7 +2312,8 @@
LOG(WARNING) << "Failed to fixup .rel.dyn in " << file_->GetPath();
return false;
}
- if (!FixupDebugSections(base_address)) {
+ static_assert(sizeof(Elf_Off) >= sizeof(base_address), "Potentially losing precision.");
+ if (!FixupDebugSections(static_cast<Elf_Off>(base_address))) {
LOG(WARNING) << "Failed to fixup debug sections in " << file_->GetPath();
return false;
}
@@ -2317,7 +2325,7 @@
typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
- ::FixupDynamic(uintptr_t base_address) {
+ ::FixupDynamic(Elf_Addr base_address) {
for (Elf_Word i = 0; i < GetDynamicNum(); i++) {
Elf_Dyn& elf_dyn = GetDynamic(i);
Elf_Word d_tag = elf_dyn.d_tag;
@@ -2341,7 +2349,7 @@
typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
- ::FixupSectionHeaders(uintptr_t base_address) {
+ ::FixupSectionHeaders(Elf_Addr base_address) {
for (Elf_Word i = 0; i < GetSectionHeaderNum(); i++) {
Elf_Shdr* sh = GetSectionHeader(i);
CHECK(sh != nullptr);
@@ -2365,7 +2373,7 @@
typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
- ::FixupProgramHeaders(uintptr_t base_address) {
+ ::FixupProgramHeaders(Elf_Addr base_address) {
// TODO: ELFObjectFile doesn't have give to Elf_Phdr, so we do that ourselves for now.
for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) {
Elf_Phdr* ph = GetProgramHeader(i);
@@ -2392,7 +2400,7 @@
typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
- ::FixupSymbols(uintptr_t base_address, bool dynamic) {
+ ::FixupSymbols(Elf_Addr base_address, bool dynamic) {
Elf_Word section_type = dynamic ? SHT_DYNSYM : SHT_SYMTAB;
// TODO: Unfortunate ELFObjectFile has protected symbol access, so use ElfFile
Elf_Shdr* symbol_section = FindSectionByType(section_type);
@@ -2422,7 +2430,7 @@
typename Elf_Rela, typename Elf_Dyn, typename Elf_Off>
bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word,
Elf_Sword, Elf_Addr, Elf_Sym, Elf_Rel, Elf_Rela, Elf_Dyn, Elf_Off>
- ::FixupRelocations(uintptr_t base_address) {
+ ::FixupRelocations(Elf_Addr base_address) {
for (Elf_Word i = 0; i < GetSectionHeaderNum(); i++) {
Elf_Shdr* sh = GetSectionHeader(i);
CHECK(sh != nullptr);
@@ -2622,7 +2630,14 @@
return elf_file->elf32_->Strip(error_msg);
}
-bool ElfFile::Fixup(uintptr_t base_address) {
+bool ElfFile::Fixup(uint64_t base_address) {
+ if (elf64_.get() != nullptr) {
+ return elf64_->Fixup(static_cast<Elf64_Addr>(base_address));
+ } else {
+ DCHECK(elf32_.get() != nullptr);
+ CHECK(IsUint<32>(base_address)) << std::hex << base_address;
+ return elf32_->Fixup(static_cast<Elf32_Addr>(base_address));
+ }
DELEGATE_TO_IMPL(Fixup, base_address);
}
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 41c54bc..286c2a6 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -78,9 +78,9 @@
// Fixup an ELF file so that that oat header will be loaded at oat_begin.
// Returns true on success, false on failure.
- static bool Fixup(File* file, uintptr_t oat_data_begin);
+ static bool Fixup(File* file, uint64_t oat_data_begin);
- bool Fixup(uintptr_t base_address);
+ bool Fixup(uint64_t base_address);
bool Is64Bit() const {
return elf64_.get() != nullptr;
diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h
index a70fa17..16d3857 100644
--- a/runtime/elf_file_impl.h
+++ b/runtime/elf_file_impl.h
@@ -19,6 +19,7 @@
#include <map>
#include <memory>
+#include <type_traits>
#include <vector>
// Explicitly include our own elf.h to avoid Linux and other dependencies.
@@ -102,13 +103,13 @@
// executable is true at run time, false at compile time.
bool Load(bool executable, std::string* error_msg);
- bool Fixup(uintptr_t base_address);
- bool FixupDynamic(uintptr_t base_address);
- bool FixupSectionHeaders(uintptr_t base_address);
- bool FixupProgramHeaders(uintptr_t base_address);
- bool FixupSymbols(uintptr_t base_address, bool dynamic);
- bool FixupRelocations(uintptr_t base_address);
- bool FixupDebugSections(off_t base_address_delta);
+ bool Fixup(Elf_Addr base_address);
+ bool FixupDynamic(Elf_Addr base_address);
+ bool FixupSectionHeaders(Elf_Addr base_address);
+ bool FixupProgramHeaders(Elf_Addr base_address);
+ bool FixupSymbols(Elf_Addr base_address, bool dynamic);
+ bool FixupRelocations(Elf_Addr base_address);
+ bool FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta);
bool Strip(std::string* error_msg);
diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
index d88d262..6a8aaf2 100644
--- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
@@ -27,7 +27,7 @@
namespace art {
-extern "C" void artDeoptimize(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+extern "C" NO_RETURN void artDeoptimize(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
self->SetException(Thread::GetDeoptimizationException());
self->QuickDeliverException();
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index 70317bb..9644b98 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -24,14 +24,14 @@
namespace art {
// Deliver an exception that's pending on thread helping set up a callee save frame on the way.
-extern "C" void artDeliverPendingExceptionFromCode(Thread* self)
+extern "C" NO_RETURN void artDeliverPendingExceptionFromCode(Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
self->QuickDeliverException();
}
// Called by generated call to throw an exception.
-extern "C" void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self)
+extern "C" NO_RETURN void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
/*
* exception may be NULL, in which case this routine should
@@ -50,7 +50,7 @@
}
// Called by generated call to throw a NPE exception.
-extern "C" void artThrowNullPointerExceptionFromCode(Thread* self)
+extern "C" NO_RETURN void artThrowNullPointerExceptionFromCode(Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
self->NoteSignalBeingHandled();
@@ -60,7 +60,7 @@
}
// Called by generated call to throw an arithmetic divide by zero exception.
-extern "C" void artThrowDivZeroFromCode(Thread* self)
+extern "C" NO_RETURN void artThrowDivZeroFromCode(Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ThrowArithmeticExceptionDivideByZero();
@@ -68,14 +68,14 @@
}
// Called by generated call to throw an array index out of bounds exception.
-extern "C" void artThrowArrayBoundsFromCode(int index, int length, Thread* self)
+extern "C" NO_RETURN void artThrowArrayBoundsFromCode(int index, int length, Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ThrowArrayIndexOutOfBoundsException(index, length);
self->QuickDeliverException();
}
-extern "C" void artThrowStackOverflowFromCode(Thread* self)
+extern "C" NO_RETURN void artThrowStackOverflowFromCode(Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
self->NoteSignalBeingHandled();
@@ -84,15 +84,16 @@
self->QuickDeliverException();
}
-extern "C" void artThrowNoSuchMethodFromCode(int32_t method_idx, Thread* self)
+extern "C" NO_RETURN void artThrowNoSuchMethodFromCode(int32_t method_idx, Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ThrowNoSuchMethodError(method_idx);
self->QuickDeliverException();
}
-extern "C" void artThrowClassCastException(mirror::Class* dest_type, mirror::Class* src_type,
- Thread* self)
+extern "C" NO_RETURN void artThrowClassCastException(mirror::Class* dest_type,
+ mirror::Class* src_type,
+ Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
DCHECK(!dest_type->IsAssignableFrom(src_type));
@@ -100,8 +101,8 @@
self->QuickDeliverException();
}
-extern "C" void artThrowArrayStoreException(mirror::Object* array, mirror::Object* value,
- Thread* self)
+extern "C" NO_RETURN void artThrowArrayStoreException(mirror::Object* array, mirror::Object* value,
+ Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ThrowArrayStoreException(value->GetClass(), array->GetClass());
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index db7a4ef..6a68880 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -83,6 +83,9 @@
LOG(INFO) << "Verifying no from-space refs";
}
VerifyNoFromSpaceReferences();
+ if (kVerboseMode) {
+ LOG(INFO) << "Done verifying no from-space refs";
+ }
CheckEmptyMarkQueue();
}
{
@@ -174,7 +177,7 @@
thread->RevokeThreadLocalAllocationStack();
}
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
- thread->VisitRoots(ConcurrentCopying::ProcessRootCallback, concurrent_copying_);
+ thread->VisitRoots(concurrent_copying_);
concurrent_copying_->GetBarrier().Pass(self);
}
@@ -208,7 +211,7 @@
if (UNLIKELY(Runtime::Current()->IsActiveTransaction())) {
CHECK(Runtime::Current()->IsAotCompiler());
TimingLogger::ScopedTiming split2("(Paused)VisitTransactionRoots", cc->GetTimings());
- Runtime::Current()->VisitTransactionRoots(ConcurrentCopying::ProcessRootCallback, cc);
+ Runtime::Current()->VisitTransactionRoots(cc);
}
}
@@ -332,22 +335,20 @@
}
{
TimingLogger::ScopedTiming split2("VisitConstantRoots", GetTimings());
- Runtime::Current()->VisitConstantRoots(ProcessRootCallback, this);
+ Runtime::Current()->VisitConstantRoots(this);
}
{
TimingLogger::ScopedTiming split3("VisitInternTableRoots", GetTimings());
- Runtime::Current()->GetInternTable()->VisitRoots(ProcessRootCallback,
- this, kVisitRootFlagAllRoots);
+ Runtime::Current()->GetInternTable()->VisitRoots(this, kVisitRootFlagAllRoots);
}
{
TimingLogger::ScopedTiming split4("VisitClassLinkerRoots", GetTimings());
- Runtime::Current()->GetClassLinker()->VisitRoots(ProcessRootCallback,
- this, kVisitRootFlagAllRoots);
+ Runtime::Current()->GetClassLinker()->VisitRoots(this, kVisitRootFlagAllRoots);
}
{
// TODO: don't visit the transaction roots if it's not active.
TimingLogger::ScopedTiming split5("VisitNonThreadRoots", GetTimings());
- Runtime::Current()->VisitNonThreadRoots(ProcessRootCallback, this);
+ Runtime::Current()->VisitNonThreadRoots(this);
}
// Immune spaces.
@@ -486,7 +487,7 @@
// The following visitors are that used to verify that there's no
// references to the from-space left after marking.
-class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor {
+class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor : public SingleRootVisitor {
public:
explicit ConcurrentCopyingVerifyNoFromSpaceRefsVisitor(ConcurrentCopying* collector)
: collector_(collector) {}
@@ -516,16 +517,14 @@
}
}
- static void RootCallback(mirror::Object** root, void *arg, const RootInfo& /*root_info*/)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg);
- ConcurrentCopyingVerifyNoFromSpaceRefsVisitor visitor(collector);
+ void VisitRoot(mirror::Object* root, const RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(root != nullptr);
- visitor(*root);
+ operator()(root);
}
private:
- ConcurrentCopying* collector_;
+ ConcurrentCopying* const collector_;
};
class ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor {
@@ -594,8 +593,8 @@
// Roots.
{
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
- Runtime::Current()->VisitRoots(
- ConcurrentCopyingVerifyNoFromSpaceRefsVisitor::RootCallback, this);
+ ConcurrentCopyingVerifyNoFromSpaceRefsVisitor ref_visitor(this);
+ Runtime::Current()->VisitRoots(&ref_visitor);
}
// The to-space.
region_space_->WalkToSpace(ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor::ObjectCallback,
@@ -808,6 +807,9 @@
public:
explicit ConcurrentCopyingClearBlackPtrsVisitor(ConcurrentCopying* cc)
: collector_(cc) {}
+#ifndef USE_BAKER_OR_BROOKS_READ_BARRIER
+ NO_RETURN
+#endif
void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
DCHECK(obj != nullptr);
@@ -1087,11 +1089,6 @@
}
}
-void ConcurrentCopying::ProcessRootCallback(mirror::Object** root, void* arg,
- const RootInfo& /*root_info*/) {
- reinterpret_cast<ConcurrentCopying*>(arg)->Process(root);
-}
-
// Used to scan ref fields of an object.
class ConcurrentCopyingRefFieldsVisitor {
public:
@@ -1144,25 +1141,54 @@
offset, expected_ref, new_ref));
}
-// Process a root.
-void ConcurrentCopying::Process(mirror::Object** root) {
- mirror::Object* ref = *root;
- if (ref == nullptr || region_space_->IsInToSpace(ref)) {
- return;
- }
- mirror::Object* to_ref = Mark(ref);
- if (to_ref == ref) {
- return;
- }
- Atomic<mirror::Object*>* addr = reinterpret_cast<Atomic<mirror::Object*>*>(root);
- mirror::Object* expected_ref = ref;
- mirror::Object* new_ref = to_ref;
- do {
- if (expected_ref != addr->LoadRelaxed()) {
- // It was updated by the mutator.
- break;
+// Process some roots.
+void ConcurrentCopying::VisitRoots(
+ mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) {
+ for (size_t i = 0; i < count; ++i) {
+ mirror::Object** root = roots[i];
+ mirror::Object* ref = *root;
+ if (ref == nullptr || region_space_->IsInToSpace(ref)) {
+ continue;
}
- } while (!addr->CompareExchangeWeakSequentiallyConsistent(expected_ref, new_ref));
+ mirror::Object* to_ref = Mark(ref);
+ if (to_ref == ref) {
+ continue;
+ }
+ Atomic<mirror::Object*>* addr = reinterpret_cast<Atomic<mirror::Object*>*>(root);
+ mirror::Object* expected_ref = ref;
+ mirror::Object* new_ref = to_ref;
+ do {
+ if (expected_ref != addr->LoadRelaxed()) {
+ // It was updated by the mutator.
+ break;
+ }
+ } while (!addr->CompareExchangeWeakSequentiallyConsistent(expected_ref, new_ref));
+ }
+}
+
+void ConcurrentCopying::VisitRoots(
+ mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info ATTRIBUTE_UNUSED) {
+ for (size_t i = 0; i < count; ++i) {
+ mirror::CompressedReference<mirror::Object>* root = roots[i];
+ mirror::Object* ref = root->AsMirrorPtr();
+ if (ref == nullptr || region_space_->IsInToSpace(ref)) {
+ continue;
+ }
+ mirror::Object* to_ref = Mark(ref);
+ if (to_ref == ref) {
+ continue;
+ }
+ auto* addr = reinterpret_cast<Atomic<mirror::CompressedReference<mirror::Object>>*>(root);
+ auto expected_ref = mirror::CompressedReference<mirror::Object>::FromMirrorPtr(ref);
+ auto new_ref = mirror::CompressedReference<mirror::Object>::FromMirrorPtr(to_ref);
+ do {
+ if (ref != addr->LoadRelaxed().AsMirrorPtr()) {
+ // It was updated by the mutator.
+ break;
+ }
+ } while (!addr->CompareExchangeWeakSequentiallyConsistent(expected_ref, new_ref));
+ }
}
// Fill the given memory block with a dummy object. Used to fill in a
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index bbb551a..93de035 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -192,9 +192,11 @@
void Scan(mirror::Object* to_ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void Process(mirror::Object* obj, MemberOffset offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void Process(mirror::Object** root) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void ProcessRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+ virtual void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info)
+ OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ virtual void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info)
+ OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void VerifyNoFromSpaceReferences() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
accounting::ObjectStack* GetAllocationStack();
accounting::ObjectStack* GetLiveStack();
@@ -230,7 +232,7 @@
bool IsOnAllocStack(mirror::Object* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::Object* GetFwdPtr(mirror::Object* from_ref)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void FlipThreadRoots() LOCKS_EXCLUDED(Locks::mutator_lock_);;
+ void FlipThreadRoots() LOCKS_EXCLUDED(Locks::mutator_lock_);
void SwapStacks(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void RecordLiveStackFreezeSize(Thread* self);
void ComputeUnevacFromSpaceLiveRatio();
diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h
index ed5207a..c5a8d5d 100644
--- a/runtime/gc/collector/garbage_collector.h
+++ b/runtime/gc/collector/garbage_collector.h
@@ -22,6 +22,7 @@
#include "base/timing_logger.h"
#include "gc/collector_type.h"
#include "gc/gc_cause.h"
+#include "gc_root.h"
#include "gc_type.h"
#include <stdint.h>
#include <vector>
@@ -112,7 +113,7 @@
DISALLOW_COPY_AND_ASSIGN(Iteration);
};
-class GarbageCollector {
+class GarbageCollector : public RootVisitor {
public:
class SCOPED_LOCKABLE ScopedPause {
public:
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index d1ce0bc..8902df8 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -309,19 +309,57 @@
reinterpret_cast<MarkCompact*>(arg)->DelayReferenceReferent(klass, ref);
}
-void MarkCompact::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
- reinterpret_cast<MarkCompact*>(arg)->MarkObject(*root);
-}
-
-void MarkCompact::UpdateRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
- mirror::Object* obj = *root;
- mirror::Object* new_obj = reinterpret_cast<MarkCompact*>(arg)->GetMarkedForwardAddress(obj);
- if (obj != new_obj) {
- *root = new_obj;
- DCHECK(new_obj != nullptr);
+void MarkCompact::VisitRoots(
+ mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) {
+ for (size_t i = 0; i < count; ++i) {
+ MarkObject(*roots[i]);
}
}
+void MarkCompact::VisitRoots(
+ mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info ATTRIBUTE_UNUSED) {
+ for (size_t i = 0; i < count; ++i) {
+ MarkObject(roots[i]->AsMirrorPtr());
+ }
+}
+
+class UpdateRootVisitor : public RootVisitor {
+ public:
+ explicit UpdateRootVisitor(MarkCompact* collector) : collector_(collector) {
+ }
+
+ void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+ for (size_t i = 0; i < count; ++i) {
+ mirror::Object* obj = *roots[i];
+ mirror::Object* new_obj = collector_->GetMarkedForwardAddress(obj);
+ if (obj != new_obj) {
+ *roots[i] = new_obj;
+ DCHECK(new_obj != nullptr);
+ }
+ }
+ }
+
+ void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+ for (size_t i = 0; i < count; ++i) {
+ mirror::Object* obj = roots[i]->AsMirrorPtr();
+ mirror::Object* new_obj = collector_->GetMarkedForwardAddress(obj);
+ if (obj != new_obj) {
+ roots[i]->Assign(new_obj);
+ DCHECK(new_obj != nullptr);
+ }
+ }
+ }
+
+ private:
+ MarkCompact* const collector_;
+};
+
class UpdateObjectReferencesVisitor {
public:
explicit UpdateObjectReferencesVisitor(MarkCompact* collector) : collector_(collector) {
@@ -339,7 +377,8 @@
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
Runtime* runtime = Runtime::Current();
// Update roots.
- runtime->VisitRoots(UpdateRootCallback, this);
+ UpdateRootVisitor update_root_visitor(this);
+ runtime->VisitRoots(&update_root_visitor);
// Update object references in mod union tables and spaces.
for (const auto& space : heap_->GetContinuousSpaces()) {
// If the space is immune then we need to mark the references to other spaces.
@@ -396,7 +435,7 @@
// Marks all objects in the root set.
void MarkCompact::MarkRoots() {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
- Runtime::Current()->VisitRoots(MarkRootCallback, this);
+ Runtime::Current()->VisitRoots(this);
}
mirror::Object* MarkCompact::MarkedForwardingAddressCallback(mirror::Object* obj, void* arg) {
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index 06304bf..4337644 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -114,8 +114,12 @@
void SweepSystemWeaks()
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
- static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
+ virtual void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info)
+ OVERRIDE EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+
+ virtual void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info)
+ OVERRIDE EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
@@ -245,6 +249,8 @@
friend class MoveObjectVisitor;
friend class UpdateObjectReferencesVisitor;
friend class UpdateReferenceVisitor;
+ friend class UpdateRootVisitor;
+
DISALLOW_COPY_AND_ASSIGN(MarkCompact);
};
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index ee4e752..79d1034 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -462,42 +462,66 @@
}
}
-void MarkSweep::MarkRootParallelCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
- reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNullParallel(*root);
+class VerifyRootMarkedVisitor : public SingleRootVisitor {
+ public:
+ explicit VerifyRootMarkedVisitor(MarkSweep* collector) : collector_(collector) { }
+
+ void VisitRoot(mirror::Object* root, const RootInfo& info) OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+ CHECK(collector_->IsMarked(root)) << info.ToString();
+ }
+
+ private:
+ MarkSweep* const collector_;
+};
+
+void MarkSweep::VisitRoots(mirror::Object*** roots, size_t count,
+ const RootInfo& info ATTRIBUTE_UNUSED) {
+ for (size_t i = 0; i < count; ++i) {
+ MarkObjectNonNull(*roots[i]);
+ }
}
-void MarkSweep::VerifyRootMarked(Object** root, void* arg, const RootInfo& /*root_info*/) {
- CHECK(reinterpret_cast<MarkSweep*>(arg)->IsMarked(*root));
+void MarkSweep::VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info ATTRIBUTE_UNUSED) {
+ for (size_t i = 0; i < count; ++i) {
+ MarkObjectNonNull(roots[i]->AsMirrorPtr());
+ }
}
-void MarkSweep::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
- reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(*root);
-}
+class VerifyRootVisitor : public SingleRootVisitor {
+ public:
+ explicit VerifyRootVisitor(MarkSweep* collector) : collector_(collector) { }
-void MarkSweep::VerifyRootCallback(Object** root, void* arg, const RootInfo& root_info) {
- reinterpret_cast<MarkSweep*>(arg)->VerifyRoot(*root, root_info);
-}
+ void VisitRoot(mirror::Object* root, const RootInfo& info) OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+ collector_->VerifyRoot(root, info);
+ }
+
+ private:
+ MarkSweep* const collector_;
+};
void MarkSweep::VerifyRoot(const Object* root, const RootInfo& root_info) {
// See if the root is on any space bitmap.
if (heap_->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == nullptr) {
space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace();
if (large_object_space != nullptr && !large_object_space->Contains(root)) {
- LOG(ERROR) << "Found invalid root: " << root << " ";
- root_info.Describe(LOG(ERROR));
+ LOG(ERROR) << "Found invalid root: " << root << " " << root_info;
}
}
}
void MarkSweep::VerifyRoots() {
- Runtime::Current()->GetThreadList()->VisitRoots(VerifyRootCallback, this);
+ VerifyRootVisitor visitor(this);
+ Runtime::Current()->GetThreadList()->VisitRoots(&visitor);
}
void MarkSweep::MarkRoots(Thread* self) {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
if (Locks::mutator_lock_->IsExclusiveHeld(self)) {
// If we exclusively hold the mutator lock, all threads must be suspended.
- Runtime::Current()->VisitRoots(MarkRootCallback, this);
+ Runtime::Current()->VisitRoots(this);
RevokeAllThreadLocalAllocationStacks(self);
} else {
MarkRootsCheckpoint(self, kRevokeRosAllocThreadLocalBuffersAtCheckpoint);
@@ -510,13 +534,13 @@
void MarkSweep::MarkNonThreadRoots() {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
- Runtime::Current()->VisitNonThreadRoots(MarkRootCallback, this);
+ Runtime::Current()->VisitNonThreadRoots(this);
}
void MarkSweep::MarkConcurrentRoots(VisitRootFlags flags) {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
// Visit all runtime roots and clear dirty flags.
- Runtime::Current()->VisitConcurrentRoots(MarkRootCallback, this, flags);
+ Runtime::Current()->VisitConcurrentRoots(this, flags);
}
class ScanObjectVisitor {
@@ -932,13 +956,12 @@
void MarkSweep::ReMarkRoots() {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
- Runtime::Current()->VisitRoots(
- MarkRootCallback, this, static_cast<VisitRootFlags>(kVisitRootFlagNewRoots |
- kVisitRootFlagStopLoggingNewRoots |
- kVisitRootFlagClearRootLog));
+ Runtime::Current()->VisitRoots(this, static_cast<VisitRootFlags>(
+ kVisitRootFlagNewRoots | kVisitRootFlagStopLoggingNewRoots | kVisitRootFlagClearRootLog));
if (kVerifyRootsMarked) {
TimingLogger::ScopedTiming t2("(Paused)VerifyRoots", GetTimings());
- Runtime::Current()->VisitRoots(VerifyRootMarked, this);
+ VerifyRootMarkedVisitor visitor(this);
+ Runtime::Current()->VisitRoots(&visitor);
}
}
@@ -968,7 +991,7 @@
Runtime::Current()->SweepSystemWeaks(VerifySystemWeakIsLiveCallback, this);
}
-class CheckpointMarkThreadRoots : public Closure {
+class CheckpointMarkThreadRoots : public Closure, public RootVisitor {
public:
explicit CheckpointMarkThreadRoots(MarkSweep* mark_sweep,
bool revoke_ros_alloc_thread_local_buffers_at_checkpoint)
@@ -977,13 +1000,30 @@
revoke_ros_alloc_thread_local_buffers_at_checkpoint) {
}
+ void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+ for (size_t i = 0; i < count; ++i) {
+ mark_sweep_->MarkObjectNonNullParallel(*roots[i]);
+ }
+ }
+
+ void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+ for (size_t i = 0; i < count; ++i) {
+ mark_sweep_->MarkObjectNonNullParallel(roots[i]->AsMirrorPtr());
+ }
+ }
+
virtual void Run(Thread* thread) OVERRIDE NO_THREAD_SAFETY_ANALYSIS {
ATRACE_BEGIN("Marking thread roots");
// Note: self is not necessarily equal to thread since thread may be suspended.
- Thread* self = Thread::Current();
+ Thread* const self = Thread::Current();
CHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc)
<< thread->GetState() << " thread " << thread << " self " << self;
- thread->VisitRoots(MarkSweep::MarkRootParallelCallback, mark_sweep_);
+ thread->VisitRoots(this);
ATRACE_END();
if (revoke_ros_alloc_thread_local_buffers_at_checkpoint_) {
ATRACE_BEGIN("RevokeRosAllocThreadLocalBuffers");
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 3f99e21..31cea17 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -185,11 +185,12 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
+ virtual void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info) OVERRIDE
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- static void VerifyRootMarked(mirror::Object** root, void* arg, const RootInfo& root_info)
+ virtual void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info) OVERRIDE
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -197,9 +198,6 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void MarkRootParallelCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
// Marks an object.
void MarkObject(mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
@@ -250,9 +248,8 @@
// whether or not we care about pauses.
size_t GetThreadCount(bool paused) const;
- static void VerifyRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info);
-
- void VerifyRoot(const mirror::Object* root, const RootInfo& root_info) NO_THREAD_SAFETY_ANALYSIS;
+ void VerifyRoot(const mirror::Object* root, const RootInfo& root_info)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
// Push a single reference on a mark stack.
void PushOnMarkStack(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -326,18 +323,21 @@
friend class CardScanTask;
friend class CheckBitmapVisitor;
friend class CheckReferenceVisitor;
+ friend class CheckpointMarkThreadRoots;
friend class art::gc::Heap;
+ friend class FifoMarkStackChunk;
friend class MarkObjectVisitor;
+ template<bool kUseFinger> friend class MarkStackTask;
+ friend class MarkSweepMarkObjectSlowPath;
friend class ModUnionCheckReferences;
friend class ModUnionClearCardVisitor;
friend class ModUnionReferenceVisitor;
- friend class ModUnionVisitor;
+ friend class ModUnionScanImageRootVisitor;
friend class ModUnionTableBitmap;
friend class ModUnionTableReferenceCache;
- friend class ModUnionScanImageRootVisitor;
- template<bool kUseFinger> friend class MarkStackTask;
- friend class FifoMarkStackChunk;
- friend class MarkSweepMarkObjectSlowPath;
+ friend class ModUnionVisitor;
+ friend class VerifyRootMarkedVisitor;
+ friend class VerifyRootVisitor;
DISALLOW_COPY_AND_ASSIGN(MarkSweep);
};
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index b3d59f2..dbf01d8 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -603,18 +603,29 @@
reinterpret_cast<SemiSpace*>(arg)->DelayReferenceReferent(klass, ref);
}
-void SemiSpace::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
- auto ref = StackReference<mirror::Object>::FromMirrorPtr(*root);
- reinterpret_cast<SemiSpace*>(arg)->MarkObject(&ref);
- if (*root != ref.AsMirrorPtr()) {
- *root = ref.AsMirrorPtr();
+void SemiSpace::VisitRoots(mirror::Object*** roots, size_t count,
+ const RootInfo& info ATTRIBUTE_UNUSED) {
+ for (size_t i = 0; i < count; ++i) {
+ auto* root = roots[i];
+ auto ref = StackReference<mirror::Object>::FromMirrorPtr(*root);
+ MarkObject(&ref);
+ if (*root != ref.AsMirrorPtr()) {
+ *root = ref.AsMirrorPtr();
+ }
+ }
+}
+
+void SemiSpace::VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info ATTRIBUTE_UNUSED) {
+ for (size_t i = 0; i < count; ++i) {
+ MarkObject(roots[i]);
}
}
// Marks all objects in the root set.
void SemiSpace::MarkRoots() {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
- Runtime::Current()->VisitRoots(MarkRootCallback, this);
+ Runtime::Current()->VisitRoots(this);
}
bool SemiSpace::HeapReferenceMarkedCallback(mirror::HeapReference<mirror::Object>* object,
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index 192fb14..61fbead 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -98,7 +98,7 @@
// Find the default mark bitmap.
void FindDefaultMarkBitmap();
- // Returns the new address of the object.
+ // Updates obj_ptr if the object has moved.
template<bool kPoisonReferences>
void MarkObject(mirror::ObjectReference<kPoisonReferences, mirror::Object>* obj_ptr)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
@@ -133,8 +133,12 @@
void SweepSystemWeaks()
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
- static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
+ virtual void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info) OVERRIDE
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+
+ virtual void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info) OVERRIDE
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index be7344a..b9153c1 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -504,7 +504,6 @@
// Retry a second time with no specified request begin.
request_begin = nullptr;
}
- return nullptr;
}
bool Heap::MayUseCollector(CollectorType type) const {
@@ -2395,13 +2394,21 @@
gc_complete_cond_->Broadcast(self);
}
-static void RootMatchesObjectVisitor(mirror::Object** root, void* arg,
- const RootInfo& /*root_info*/) {
- mirror::Object* obj = reinterpret_cast<mirror::Object*>(arg);
- if (*root == obj) {
- LOG(INFO) << "Object " << obj << " is a root";
+class RootMatchesObjectVisitor : public SingleRootVisitor {
+ public:
+ explicit RootMatchesObjectVisitor(const mirror::Object* obj) : obj_(obj) { }
+
+ void VisitRoot(mirror::Object* root, const RootInfo& info)
+ OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (root == obj_) {
+ LOG(INFO) << "Object " << obj_ << " is a root " << info.ToString();
+ }
}
-}
+
+ private:
+ const mirror::Object* const obj_;
+};
+
class ScanVisitor {
public:
@@ -2411,7 +2418,7 @@
};
// Verify a reference from an object.
-class VerifyReferenceVisitor {
+class VerifyReferenceVisitor : public SingleRootVisitor {
public:
explicit VerifyReferenceVisitor(Heap* heap, Atomic<size_t>* fail_count, bool verify_referent)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_)
@@ -2438,11 +2445,12 @@
return heap_->IsLiveObjectLocked(obj, true, false, true);
}
- static void VerifyRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
+ void VisitRoot(mirror::Object* root, const RootInfo& root_info) OVERRIDE
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- VerifyReferenceVisitor* visitor = reinterpret_cast<VerifyReferenceVisitor*>(arg);
- if (!visitor->VerifyReference(nullptr, *root, MemberOffset(0))) {
- LOG(ERROR) << "Root " << *root << " is dead with type " << PrettyTypeOf(*root)
+ if (root == nullptr) {
+ LOG(ERROR) << "Root is null with info " << root_info.GetType();
+ } else if (!VerifyReference(nullptr, root, MemberOffset(0))) {
+ LOG(ERROR) << "Root " << root << " is dead with type " << PrettyTypeOf(root)
<< " thread_id= " << root_info.GetThreadId() << " root_type= " << root_info.GetType();
}
}
@@ -2534,12 +2542,11 @@
}
// Search to see if any of the roots reference our object.
- void* arg = const_cast<void*>(reinterpret_cast<const void*>(obj));
- Runtime::Current()->VisitRoots(&RootMatchesObjectVisitor, arg);
-
+ RootMatchesObjectVisitor visitor1(obj);
+ Runtime::Current()->VisitRoots(&visitor1);
// Search to see if any of the roots reference our reference.
- arg = const_cast<void*>(reinterpret_cast<const void*>(ref));
- Runtime::Current()->VisitRoots(&RootMatchesObjectVisitor, arg);
+ RootMatchesObjectVisitor visitor2(ref);
+ Runtime::Current()->VisitRoots(&visitor2);
}
return false;
}
@@ -2571,6 +2578,13 @@
visitor->operator()(obj);
}
+ void VerifyRoots() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(Locks::heap_bitmap_lock_) {
+ ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+ VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_);
+ Runtime::Current()->VisitRoots(&visitor);
+ }
+
size_t GetFailureCount() const {
return fail_count_->LoadSequentiallyConsistent();
}
@@ -2637,7 +2651,7 @@
// pointing to dead objects if they are not reachable.
VisitObjectsPaused(VerifyObjectVisitor::VisitCallback, &visitor);
// Verify the roots:
- Runtime::Current()->VisitRoots(VerifyReferenceVisitor::VerifyRootCallback, &visitor);
+ visitor.VerifyRoots();
if (visitor.GetFailureCount() > 0) {
// Dump mod-union tables.
for (const auto& table_pair : mod_union_tables_) {
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index b09de6f..9195b06 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -259,7 +259,6 @@
}
GetMemMap()->SetSize(new_capacity);
limit_ = Begin() + new_capacity;
- CHECK(temp_bitmap_.get() == nullptr);
}
} // namespace space
diff --git a/runtime/gc/task_processor.cc b/runtime/gc/task_processor.cc
index 1a3c6f5..2ca4b3f 100644
--- a/runtime/gc/task_processor.cc
+++ b/runtime/gc/task_processor.cc
@@ -67,7 +67,6 @@
}
}
UNREACHABLE();
- return nullptr;
}
void TaskProcessor::UpdateTargetRunTime(Thread* self, HeapTask* task, uint64_t new_target_time) {
diff --git a/runtime/gc_root-inl.h b/runtime/gc_root-inl.h
index a42ec08..57d5689 100644
--- a/runtime/gc_root-inl.h
+++ b/runtime/gc_root-inl.h
@@ -19,6 +19,8 @@
#include "gc_root.h"
+#include <sstream>
+
#include "read_barrier-inl.h"
namespace art {
@@ -26,7 +28,17 @@
template<class MirrorType>
template<ReadBarrierOption kReadBarrierOption>
inline MirrorType* GcRoot<MirrorType>::Read() const {
- return ReadBarrier::BarrierForRoot<MirrorType, kReadBarrierOption>(&root_);
+ return down_cast<MirrorType*>(
+ ReadBarrier::BarrierForRoot<mirror::Object, kReadBarrierOption>(&root_));
+}
+template<class MirrorType>
+inline GcRoot<MirrorType>::GcRoot(MirrorType* ref)
+ : root_(mirror::CompressedReference<mirror::Object>::FromMirrorPtr(ref)) { }
+
+inline std::string RootInfo::ToString() const {
+ std::ostringstream oss;
+ Describe(oss);
+ return oss.str();
}
} // namespace art
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index c5feda5..0d3c93b 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -19,6 +19,7 @@
#include "base/macros.h"
#include "base/mutex.h" // For Locks::mutator_lock_.
+#include "mirror/object_reference.h"
namespace art {
@@ -26,6 +27,12 @@
class Object;
} // namespace mirror
+template <size_t kBufferSize>
+class BufferedRootVisitor;
+
+// Dependent on pointer size so that we don't have frames that are too big on 64 bit.
+static const size_t kDefaultBufferedRootCount = 1024 / sizeof(void*);
+
enum RootType {
kRootUnknown = 0,
kRootJNIGlobal,
@@ -43,12 +50,14 @@
};
std::ostream& operator<<(std::ostream& os, const RootType& root_type);
+// Only used by hprof. tid and root_type are only used by hprof.
class RootInfo {
public:
// Thread id 0 is for non thread roots.
explicit RootInfo(RootType type, uint32_t thread_id = 0)
: type_(type), thread_id_(thread_id) {
}
+ RootInfo(const RootInfo&) = default;
virtual ~RootInfo() {
}
RootType GetType() const {
@@ -60,15 +69,64 @@
virtual void Describe(std::ostream& os) const {
os << "Type=" << type_ << " thread_id=" << thread_id_;
}
+ std::string ToString() const;
private:
const RootType type_;
const uint32_t thread_id_;
};
-// Returns the new address of the object, returns root if it has not moved. tid and root_type are
-// only used by hprof.
-typedef void (RootCallback)(mirror::Object** root, void* arg, const RootInfo& root_info);
+inline std::ostream& operator<<(std::ostream& os, const RootInfo& root_info) {
+ root_info.Describe(os);
+ return os;
+}
+
+class RootVisitor {
+ public:
+ virtual ~RootVisitor() { }
+
+ // Single root versions, not overridable.
+ ALWAYS_INLINE void VisitRoot(mirror::Object** roots, const RootInfo& info)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ VisitRoots(&roots, 1, info);
+ }
+
+ ALWAYS_INLINE void VisitRootIfNonNull(mirror::Object** roots, const RootInfo& info)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (*roots != nullptr) {
+ VisitRoot(roots, info);
+ }
+ }
+
+ virtual void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+
+ virtual void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+};
+
+// Only visits roots one at a time, doesn't handle updating roots. Used when performance isn't
+// critical.
+class SingleRootVisitor : public RootVisitor {
+ private:
+ void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info) OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ for (size_t i = 0; i < count; ++i) {
+ VisitRoot(*roots[i], info);
+ }
+ }
+
+ void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
+ const RootInfo& info) OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ for (size_t i = 0; i < count; ++i) {
+ VisitRoot(roots[i]->AsMirrorPtr(), info);
+ }
+ }
+
+ virtual void VisitRoot(mirror::Object* root, const RootInfo& info) = 0;
+};
template<class MirrorType>
class GcRoot {
@@ -76,37 +134,92 @@
template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ALWAYS_INLINE MirrorType* Read() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitRoot(RootCallback* callback, void* arg, const RootInfo& info) const {
+ void VisitRoot(RootVisitor* visitor, const RootInfo& info) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(!IsNull());
- callback(reinterpret_cast<mirror::Object**>(&root_), arg, info);
+ mirror::CompressedReference<mirror::Object>* roots[1] = { &root_ };
+ visitor->VisitRoots(roots, 1u, info);
DCHECK(!IsNull());
}
- void VisitRootIfNonNull(RootCallback* callback, void* arg, const RootInfo& info) const {
+ void VisitRootIfNonNull(RootVisitor* visitor, const RootInfo& info) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (!IsNull()) {
- VisitRoot(callback, arg, info);
+ VisitRoot(visitor, info);
}
}
- // This is only used by IrtIterator.
- ALWAYS_INLINE MirrorType** AddressWithoutBarrier() {
+ ALWAYS_INLINE mirror::CompressedReference<mirror::Object>* AddressWithoutBarrier() {
return &root_;
}
- bool IsNull() const {
+ ALWAYS_INLINE bool IsNull() const {
// It's safe to null-check it without a read barrier.
- return root_ == nullptr;
+ return root_.IsNull();
}
- ALWAYS_INLINE explicit GcRoot<MirrorType>() : root_(nullptr) {
+ ALWAYS_INLINE GcRoot(MirrorType* ref = nullptr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+ mutable mirror::CompressedReference<mirror::Object> root_;
+
+ template <size_t kBufferSize> friend class BufferedRootVisitor;
+};
+
+// Simple data structure for buffered root visiting to avoid virtual dispatch overhead. Currently
+// only for CompressedReferences since these are more common than the Object** roots which are only
+// for thread local roots.
+template <size_t kBufferSize>
+class BufferedRootVisitor {
+ public:
+ BufferedRootVisitor(RootVisitor* visitor, const RootInfo& root_info)
+ : visitor_(visitor), root_info_(root_info), buffer_pos_(0) {
}
- ALWAYS_INLINE explicit GcRoot<MirrorType>(MirrorType* ref)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : root_(ref) {
+ ~BufferedRootVisitor() {
+ Flush();
+ }
+
+ template <class MirrorType>
+ ALWAYS_INLINE void VisitRootIfNonNull(GcRoot<MirrorType>& root)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (!root.IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ template <class MirrorType>
+ ALWAYS_INLINE void VisitRootIfNonNull(mirror::CompressedReference<MirrorType>* root)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (!root->IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ template <class MirrorType>
+ void VisitRoot(GcRoot<MirrorType>& root) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ VisitRoot(root.AddressWithoutBarrier());
+ }
+
+ template <class MirrorType>
+ void VisitRoot(mirror::CompressedReference<MirrorType>* root)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (UNLIKELY(buffer_pos_ >= kBufferSize)) {
+ Flush();
+ }
+ roots_[buffer_pos_++] = root;
+ }
+
+ void Flush() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ visitor_->VisitRoots(roots_, buffer_pos_, root_info_);
+ buffer_pos_ = 0;
}
private:
- mutable MirrorType* root_;
+ RootVisitor* const visitor_;
+ RootInfo root_info_;
+ mirror::CompressedReference<mirror::Object>* roots_[kBufferSize];
+ size_t buffer_pos_;
};
} // namespace art
diff --git a/runtime/handle.h b/runtime/handle.h
index 6af3220..3ebb2d5 100644
--- a/runtime/handle.h
+++ b/runtime/handle.h
@@ -70,6 +70,16 @@
return reinterpret_cast<jobject>(reference_);
}
+ StackReference<mirror::Object>* GetReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ ALWAYS_INLINE {
+ return reference_;
+ }
+
+ ALWAYS_INLINE const StackReference<mirror::Object>* GetReference() const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return reference_;
+ }
+
protected:
template<typename S>
explicit Handle(StackReference<S>* reference)
@@ -80,14 +90,6 @@
: reference_(handle.reference_) {
}
- StackReference<mirror::Object>* GetReference() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
- return reference_;
- }
- ALWAYS_INLINE const StackReference<mirror::Object>* GetReference() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return reference_;
- }
-
StackReference<mirror::Object>* reference_;
private:
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index a836578..271312e 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -133,6 +133,8 @@
: MutableHandle<T>(handle), obj_(obj) {
}
+ HandleWrapper(const HandleWrapper&) = default;
+
~HandleWrapper() {
*obj_ = MutableHandle<T>::Get();
}
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 656569c..cdb3e2a 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -222,7 +222,7 @@
HandleU4List(values, count);
length_ += count * sizeof(uint32_t);
}
- virtual void UpdateU4(size_t offset ATTRIBUTE_UNUSED, uint32_t new_value ATTRIBUTE_UNUSED) {
+ virtual void UpdateU4(size_t offset, uint32_t new_value ATTRIBUTE_UNUSED) {
DCHECK_LE(offset, length_ - 4);
}
void AddU8List(const uint64_t* values, size_t count) {
@@ -403,9 +403,9 @@
JDWP::JdwpNetStateBase* net_state_;
};
-#define __ output->
+#define __ output_->
-class Hprof {
+class Hprof : public SingleRootVisitor {
public:
Hprof(const char* output_filename, int fd, bool direct_to_ddms)
: filename_(output_filename),
@@ -426,9 +426,11 @@
size_t max_length;
{
EndianOutput count_output;
- ProcessHeap(&count_output, false);
+ output_ = &count_output;
+ ProcessHeap(false);
overall_size = count_output.SumLength();
max_length = count_output.MaxLength();
+ output_ = nullptr;
}
bool okay;
@@ -451,86 +453,70 @@
}
private:
- struct Env {
- Hprof* hprof;
- EndianOutput* output;
- };
-
- static void RootVisitor(mirror::Object** obj, void* arg, const RootInfo& root_info)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(arg != nullptr);
- DCHECK(obj != nullptr);
- DCHECK(*obj != nullptr);
- Env* env = reinterpret_cast<Env*>(arg);
- env->hprof->VisitRoot(*obj, root_info, env->output);
- }
-
static void VisitObjectCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(obj != nullptr);
DCHECK(arg != nullptr);
- Env* env = reinterpret_cast<Env*>(arg);
- env->hprof->DumpHeapObject(obj, env->output);
+ reinterpret_cast<Hprof*>(arg)->DumpHeapObject(obj);
}
- void DumpHeapObject(mirror::Object* obj, EndianOutput* output)
+ void DumpHeapObject(mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void DumpHeapClass(mirror::Class* klass, EndianOutput* output)
+ void DumpHeapClass(mirror::Class* klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void DumpHeapArray(mirror::Array* obj, mirror::Class* klass, EndianOutput* output)
+ void DumpHeapArray(mirror::Array* obj, mirror::Class* klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass, EndianOutput* output)
+ void DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void ProcessHeap(EndianOutput* output, bool header_first)
+ void ProcessHeap(bool header_first)
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Reset current heap and object count.
current_heap_ = HPROF_HEAP_DEFAULT;
objects_in_segment_ = 0;
if (header_first) {
- ProcessHeader(output);
- ProcessBody(output);
+ ProcessHeader();
+ ProcessBody();
} else {
- ProcessBody(output);
- ProcessHeader(output);
+ ProcessBody();
+ ProcessHeader();
}
}
- void ProcessBody(EndianOutput* output) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
- Runtime* runtime = Runtime::Current();
+ void ProcessBody() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Runtime* const runtime = Runtime::Current();
// Walk the roots and the heap.
- output->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
+ output_->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
- Env env = { this, output };
- runtime->VisitRoots(RootVisitor, &env);
- runtime->VisitImageRoots(RootVisitor, &env);
- runtime->GetHeap()->VisitObjectsPaused(VisitObjectCallback, &env);
+ runtime->VisitRoots(this);
+ runtime->VisitImageRoots(this);
+ runtime->GetHeap()->VisitObjectsPaused(VisitObjectCallback, this);
- output->StartNewRecord(HPROF_TAG_HEAP_DUMP_END, kHprofTime);
- output->EndRecord();
+ output_->StartNewRecord(HPROF_TAG_HEAP_DUMP_END, kHprofTime);
+ output_->EndRecord();
}
- void ProcessHeader(EndianOutput* output) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ void ProcessHeader() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Write the header.
- WriteFixedHeader(output);
+ WriteFixedHeader();
// Write the string and class tables, and any stack traces, to the header.
// (jhat requires that these appear before any of the data in the body that refers to them.)
- WriteStringTable(output);
- WriteClassTable(output);
- WriteStackTraces(output);
- output->EndRecord();
+ WriteStringTable();
+ WriteClassTable();
+ WriteStackTraces();
+ output_->EndRecord();
}
- void WriteClassTable(EndianOutput* output) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ void WriteClassTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t nextSerialNumber = 1;
for (mirror::Class* c : classes_) {
CHECK(c != nullptr);
- output->StartNewRecord(HPROF_TAG_LOAD_CLASS, kHprofTime);
+ output_->StartNewRecord(HPROF_TAG_LOAD_CLASS, kHprofTime);
// LOAD CLASS format:
// U4: class serial number (always > 0)
// ID: class object ID. We use the address of the class object structure as its ID.
@@ -543,12 +529,12 @@
}
}
- void WriteStringTable(EndianOutput* output) {
+ void WriteStringTable() {
for (const std::pair<std::string, HprofStringId>& p : strings_) {
const std::string& string = p.first;
const size_t id = p.second;
- output->StartNewRecord(HPROF_TAG_STRING, kHprofTime);
+ output_->StartNewRecord(HPROF_TAG_STRING, kHprofTime);
// STRING format:
// ID: ID for this string
@@ -559,24 +545,24 @@
}
}
- void StartNewHeapDumpSegment(EndianOutput* output) {
+ void StartNewHeapDumpSegment() {
// This flushes the old segment and starts a new one.
- output->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
+ output_->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
objects_in_segment_ = 0;
// Starting a new HEAP_DUMP resets the heap to default.
current_heap_ = HPROF_HEAP_DEFAULT;
}
- void CheckHeapSegmentConstraints(EndianOutput* output) {
- if (objects_in_segment_ >= kMaxObjectsPerSegment || output->Length() >= kMaxBytesPerSegment) {
- StartNewHeapDumpSegment(output);
+ void CheckHeapSegmentConstraints() {
+ if (objects_in_segment_ >= kMaxObjectsPerSegment || output_->Length() >= kMaxBytesPerSegment) {
+ StartNewHeapDumpSegment();
}
}
- void VisitRoot(const mirror::Object* obj, const RootInfo& root_info, EndianOutput* output)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VisitRoot(mirror::Object* obj, const RootInfo& root_info)
+ OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeapTag heap_tag,
- uint32_t thread_serial, EndianOutput* output);
+ uint32_t thread_serial);
HprofClassObjectId LookupClassId(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (c != nullptr) {
@@ -611,7 +597,7 @@
return LookupStringId(PrettyDescriptor(c));
}
- void WriteFixedHeader(EndianOutput* output) {
+ void WriteFixedHeader() {
// Write the file header.
// U1: NUL-terminated magic string.
const char magic[] = "JAVA PROFILE 1.0.3";
@@ -635,9 +621,9 @@
__ AddU4(static_cast<uint32_t>(nowMs & 0xFFFFFFFF));
}
- void WriteStackTraces(EndianOutput* output) {
+ void WriteStackTraces() {
// Write a dummy stack trace record so the analysis tools don't freak out.
- output->StartNewRecord(HPROF_TAG_STACK_TRACE, kHprofTime);
+ output_->StartNewRecord(HPROF_TAG_STACK_TRACE, kHprofTime);
__ AddU4(kHprofNullStackTrace);
__ AddU4(kHprofNullThread);
__ AddU4(0); // no frames
@@ -679,13 +665,15 @@
bool okay;
{
FileEndianOutput file_output(file.get(), max_length);
- ProcessHeap(&file_output, true);
+ output_ = &file_output;
+ ProcessHeap(true);
okay = !file_output.Errors();
if (okay) {
// Check for expected size.
CHECK_EQ(file_output.SumLength(), overall_size);
}
+ output_ = nullptr;
}
if (okay) {
@@ -721,13 +709,15 @@
// Prepare the output and send the chunk header.
NetStateEndianOutput net_output(net_state, max_length);
+ output_ = &net_output;
net_output.AddU1List(chunk_header, kChunkHeaderSize);
// Write the dump.
- ProcessHeap(&net_output, true);
+ ProcessHeap(true);
// Check for expected size.
CHECK_EQ(net_output.SumLength(), overall_size + kChunkHeaderSize);
+ output_ = nullptr;
return true;
}
@@ -741,6 +731,8 @@
uint64_t start_ns_;
+ EndianOutput* output_;
+
HprofHeapId current_heap_; // Which heap we're currently dumping.
size_t objects_in_segment_;
@@ -811,12 +803,12 @@
// only true when marking the root set or unreachable
// objects. Used to add rootset references to obj.
void Hprof::MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeapTag heap_tag,
- uint32_t thread_serial, EndianOutput* output) {
+ uint32_t thread_serial) {
if (heap_tag == 0) {
return;
}
- CheckHeapSegmentConstraints(output);
+ CheckHeapSegmentConstraints();
switch (heap_tag) {
// ID: object ID
@@ -892,7 +884,7 @@
return kHprofNullStackTrace;
}
-void Hprof::DumpHeapObject(mirror::Object* obj, EndianOutput* output) {
+void Hprof::DumpHeapObject(mirror::Object* obj) {
// Ignore classes that are retired.
if (obj->IsClass() && obj->AsClass()->IsRetired()) {
return;
@@ -908,7 +900,7 @@
heap_type = HPROF_HEAP_IMAGE;
}
}
- CheckHeapSegmentConstraints(output);
+ CheckHeapSegmentConstraints();
if (heap_type != current_heap_) {
HprofStringId nameId;
@@ -945,18 +937,22 @@
// allocated which hasn't been initialized yet.
} else {
if (obj->IsClass()) {
- DumpHeapClass(obj->AsClass(), output);
+ DumpHeapClass(obj->AsClass());
} else if (c->IsArrayClass()) {
- DumpHeapArray(obj->AsArray(), c, output);
+ DumpHeapArray(obj->AsArray(), c);
} else {
- DumpHeapInstanceObject(obj, c, output);
+ DumpHeapInstanceObject(obj, c);
}
}
++objects_in_segment_;
}
-void Hprof::DumpHeapClass(mirror::Class* klass, EndianOutput* output) {
+void Hprof::DumpHeapClass(mirror::Class* klass) {
+ if (!klass->IsLoaded() && !klass->IsErroneous()) {
+ // Class is allocated but not yet loaded: we cannot access its fields or super class.
+ return;
+ }
size_t sFieldCount = klass->NumStaticFields();
if (sFieldCount != 0) {
int byteLength = sFieldCount * sizeof(JValue); // TODO bogus; fields are packed
@@ -1049,7 +1045,7 @@
}
}
-void Hprof::DumpHeapArray(mirror::Array* obj, mirror::Class* klass, EndianOutput* output) {
+void Hprof::DumpHeapArray(mirror::Array* obj, mirror::Class* klass) {
uint32_t length = obj->GetLength();
if (obj->IsObjectArray()) {
@@ -1089,8 +1085,7 @@
}
}
-void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass,
- EndianOutput* output) {
+void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass) {
// obj is an instance object.
__ AddU1(HPROF_INSTANCE_DUMP);
__ AddObjectId(obj);
@@ -1099,7 +1094,7 @@
// Reserve some space for the length of the instance data, which we won't
// know until we're done writing it.
- size_t size_patch_offset = output->Length();
+ size_t size_patch_offset = output_->Length();
__ AddU4(0x77777777);
// Write the instance data; fields for this class, followed by super class fields,
@@ -1139,10 +1134,10 @@
}
// Patch the instance field length.
- __ UpdateU4(size_patch_offset, output->Length() - (size_patch_offset + 4));
+ __ UpdateU4(size_patch_offset, output_->Length() - (size_patch_offset + 4));
}
-void Hprof::VisitRoot(const mirror::Object* obj, const RootInfo& info, EndianOutput* output) {
+void Hprof::VisitRoot(mirror::Object* obj, const RootInfo& info) {
static const HprofHeapTag xlate[] = {
HPROF_ROOT_UNKNOWN,
HPROF_ROOT_JNI_GLOBAL,
@@ -1164,7 +1159,7 @@
if (obj == nullptr) {
return;
}
- MarkRootObject(obj, 0, xlate[info.GetType()], info.GetThreadId(), output);
+ MarkRootObject(obj, 0, xlate[info.GetType()], info.GetThreadId());
}
// If "direct_to_ddms" is true, the other arguments are ignored, and data is
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 1a3f107..cd59365 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -242,16 +242,10 @@
madvise(release_start, release_end - release_start, MADV_DONTNEED);
}
-void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg,
- const RootInfo& root_info) {
+void IndirectReferenceTable::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
+ BufferedRootVisitor<kDefaultBufferedRootCount> root_visitor(visitor, root_info);
for (auto ref : *this) {
- if (*ref == nullptr) {
- // Need to skip null entries to make it possible to do the
- // non-null check after the call back.
- continue;
- }
- callback(ref, arg, root_info);
- DCHECK(*ref != nullptr);
+ root_visitor.VisitRootIfNonNull(*ref);
}
}
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index 576a604..25b0281 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -218,7 +218,7 @@
uint32_t serial_;
GcRoot<mirror::Object> references_[kIRTPrevCount];
};
-static_assert(sizeof(IrtEntry) == (1 + kIRTPrevCount) * sizeof(uintptr_t),
+static_assert(sizeof(IrtEntry) == (1 + kIRTPrevCount) * sizeof(uint32_t),
"Unexpected sizeof(IrtEntry)");
class IrtIterator {
@@ -233,9 +233,9 @@
return *this;
}
- mirror::Object** operator*() {
+ GcRoot<mirror::Object>* operator*() {
// This does not have a read barrier as this is used to visit roots.
- return table_[i_].GetReference()->AddressWithoutBarrier();
+ return table_[i_].GetReference();
}
bool equals(const IrtIterator& rhs) const {
@@ -320,7 +320,7 @@
return IrtIterator(table_, Capacity(), Capacity());
}
- void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
+ void VisitRoots(RootVisitor* visitor, const RootInfo& root_info)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
uint32_t GetSegmentState() const {
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 9adb4ac..680b563 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -1077,13 +1077,14 @@
}
}
-void Instrumentation::VisitRoots(RootCallback* callback, void* arg) {
+void Instrumentation::VisitRoots(RootVisitor* visitor) {
WriterMutexLock mu(Thread::Current(), deoptimized_methods_lock_);
if (IsDeoptimizedMethodsEmpty()) {
return;
}
+ BufferedRootVisitor<kDefaultBufferedRootCount> roots(visitor, RootInfo(kRootVMInternal));
for (auto pair : deoptimized_methods_) {
- pair.second.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
+ roots.VisitRoot(pair.second);
}
}
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 8972f3a..77314c60 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -345,7 +345,7 @@
void InstallStubsForMethod(mirror::ArtMethod* method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitRoots(RootCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ void VisitRoots(RootVisitor* visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(deoptimized_methods_lock_);
private:
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 19bfc4e..1f1f9e8 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -53,14 +53,14 @@
os << "Intern table: " << StrongSize() << " strong; " << WeakSize() << " weak\n";
}
-void InternTable::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
+void InternTable::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) {
MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
if ((flags & kVisitRootFlagAllRoots) != 0) {
- strong_interns_.VisitRoots(callback, arg);
+ strong_interns_.VisitRoots(visitor);
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_strong_intern_roots_) {
mirror::String* old_ref = root.Read<kWithoutReadBarrier>();
- root.VisitRoot(callback, arg, RootInfo(kRootInternedString));
+ root.VisitRoot(visitor, RootInfo(kRootInternedString));
mirror::String* new_ref = root.Read<kWithoutReadBarrier>();
if (new_ref != old_ref) {
// The GC moved a root in the log. Need to search the strong interns and update the
@@ -335,12 +335,14 @@
post_zygote_table_.Insert(GcRoot<mirror::String>(s));
}
-void InternTable::Table::VisitRoots(RootCallback* callback, void* arg) {
+void InternTable::Table::VisitRoots(RootVisitor* visitor) {
+ BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
+ visitor, RootInfo(kRootInternedString));
for (auto& intern : pre_zygote_table_) {
- intern.VisitRoot(callback, arg, RootInfo(kRootInternedString));
+ buffered_visitor.VisitRoot(intern);
}
for (auto& intern : post_zygote_table_) {
- intern.VisitRoot(callback, arg, RootInfo(kRootInternedString));
+ buffered_visitor.VisitRoot(intern);
}
}
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index 2e31b7e..200a764 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -80,7 +80,7 @@
// Total number of strongly live interned strings.
size_t WeakSize() const LOCKS_EXCLUDED(Locks::intern_table_lock_);
- void VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags)
+ void VisitRoots(RootVisitor* visitor, VisitRootFlags flags)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void DumpForSigQuit(std::ostream& os) const;
@@ -125,7 +125,7 @@
void Remove(mirror::String* s)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
- void VisitRoots(RootCallback* callback, void* arg)
+ void VisitRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
void SweepWeaks(IsMarkedCallback* callback, void* arg)
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index a310452..375d644 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -467,16 +467,20 @@
}
}
-void AbortTransaction(Thread* self, const char* fmt, ...) {
- CHECK(Runtime::Current()->IsActiveTransaction());
- // Constructs abort message.
+void AbortTransactionF(Thread* self, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
+ AbortTransactionV(self, fmt, args);
+ va_end(args);
+}
+
+void AbortTransactionV(Thread* self, const char* fmt, va_list args) {
+ CHECK(Runtime::Current()->IsActiveTransaction());
+ // Constructs abort message.
std::string abort_msg;
StringAppendV(&abort_msg, fmt, args);
// Throws an exception so we can abort the transaction and rollback every change.
- Runtime::Current()->AbortTransactionAndThrowInternalError(self, abort_msg);
- va_end(args);
+ Runtime::Current()->AbortTransactionAndThrowAbortError(self, abort_msg);
}
template<bool is_range, bool do_assignability_check>
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 7d413c5..2f8bf55 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -81,7 +81,11 @@
ref->MonitorExit(self);
}
-void AbortTransaction(Thread* self, const char* fmt, ...)
+void AbortTransactionF(Thread* self, const char* fmt, ...)
+ __attribute__((__format__(__printf__, 2, 3)))
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+void AbortTransactionV(Thread* self, const char* fmt, va_list args)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void RecordArrayElementsInTransaction(mirror::Array* array, int32_t count)
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index 9c48df6..cead26c 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -536,8 +536,8 @@
// Don't allow finalizable objects to be allocated during a transaction since these can't be
// finalized without a started runtime.
if (transaction_active && obj->GetClass()->IsFinalizable()) {
- AbortTransaction(self, "Allocating finalizable object in transaction: %s",
- PrettyTypeOf(obj).c_str());
+ AbortTransactionF(self, "Allocating finalizable object in transaction: %s",
+ PrettyTypeOf(obj).c_str());
HANDLE_PENDING_EXCEPTION();
}
shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj);
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 609faf5..fe7ad77 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -438,8 +438,8 @@
// Don't allow finalizable objects to be allocated during a transaction since these can't
// be finalized without a started runtime.
if (transaction_active && obj->GetClass()->IsFinalizable()) {
- AbortTransaction(self, "Allocating finalizable object in transaction: %s",
- PrettyTypeOf(obj).c_str());
+ AbortTransactionF(self, "Allocating finalizable object in transaction: %s",
+ PrettyTypeOf(obj).c_str());
HANDLE_PENDING_EXCEPTION();
break;
}
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 281f332..9af8102 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -37,20 +37,28 @@
#include "mirror/string-inl.h"
#include "nth_caller_visitor.h"
#include "thread.h"
+#include "transaction.h"
#include "well_known_classes.h"
namespace art {
namespace interpreter {
static void AbortTransactionOrFail(Thread* self, const char* fmt, ...)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ __attribute__((__format__(__printf__, 2, 3)))
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+static void AbortTransactionOrFail(Thread* self, const char* fmt, ...) {
va_list args;
- va_start(args, fmt);
if (Runtime::Current()->IsActiveTransaction()) {
- AbortTransaction(self, fmt, args);
+ va_start(args, fmt);
+ AbortTransactionV(self, fmt, args);
va_end(args);
} else {
- LOG(FATAL) << "Trying to abort, but not in transaction mode: " << StringPrintf(fmt, args);
+ va_start(args, fmt);
+ std::string msg;
+ StringAppendV(&msg, fmt, args);
+ va_end(args);
+ LOG(FATAL) << "Trying to abort, but not in transaction mode: " << msg;
UNREACHABLE();
}
}
@@ -87,13 +95,14 @@
// Common helper for class-loading cutouts in an unstarted runtime. We call Runtime methods that
// rely on Java code to wrap errors in the correct exception class (i.e., NoClassDefFoundError into
// ClassNotFoundException), so need to do the same. The only exception is if the exception is
-// actually InternalError. This must not be wrapped, as it signals an initialization abort.
+// actually the transaction abort exception. This must not be wrapped, as it signals an
+// initialization abort.
static void CheckExceptionGenerateClassNotFound(Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (self->IsExceptionPending()) {
- // If it is not an InternalError, wrap it.
+ // If it is not the transaction abort exception, wrap it.
std::string type(PrettyTypeOf(self->GetException()));
- if (type != "java.lang.InternalError") {
+ if (type != Transaction::kAbortExceptionDescriptor) {
self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;",
"ClassNotFoundException");
}
@@ -157,8 +166,8 @@
// If we're in a transaction, class must not be finalizable (it or a superclass has a finalizer).
if (Runtime::Current()->IsActiveTransaction()) {
if (h_klass.Get()->IsFinalizable()) {
- AbortTransaction(self, "Class for newInstance is finalizable: '%s'",
- PrettyClass(h_klass.Get()).c_str());
+ AbortTransactionF(self, "Class for newInstance is finalizable: '%s'",
+ PrettyClass(h_klass.Get()).c_str());
return;
}
}
@@ -502,7 +511,7 @@
}
if (!have_dex) {
self->ClearException();
- Runtime::Current()->AbortTransactionAndThrowInternalError(self, "Could not create Dex object");
+ Runtime::Current()->AbortTransactionAndThrowAbortError(self, "Could not create Dex object");
}
}
@@ -570,7 +579,7 @@
int64_t address_long = shadow_frame->GetVRegLong(arg_offset);
mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset + 2);
if (obj == nullptr) {
- Runtime::Current()->AbortTransactionAndThrowInternalError(self, "Null pointer in peekArray");
+ Runtime::Current()->AbortTransactionAndThrowAbortError(self, "Null pointer in peekArray");
return;
}
mirror::Array* array = obj->AsArray();
@@ -580,7 +589,7 @@
if (offset < 0 || offset + count > array->GetLength()) {
std::string error_msg(StringPrintf("Array out of bounds in peekArray: %d/%d vs %d",
offset, count, array->GetLength()));
- Runtime::Current()->AbortTransactionAndThrowInternalError(self, error_msg.c_str());
+ Runtime::Current()->AbortTransactionAndThrowAbortError(self, error_msg.c_str());
return;
}
@@ -709,7 +718,7 @@
result->SetI(args[0]);
}
-static void UnstartedJNIObjectInternalClone(Thread* self ATTRIBUTE_UNUSED,
+static void UnstartedJNIObjectInternalClone(Thread* self,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver,
uint32_t* args ATTRIBUTE_UNUSED,
@@ -718,7 +727,7 @@
result->SetL(receiver->Clone(self));
}
-static void UnstartedJNIObjectNotifyAll(Thread* self ATTRIBUTE_UNUSED,
+static void UnstartedJNIObjectNotifyAll(Thread* self,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver,
uint32_t* args ATTRIBUTE_UNUSED,
@@ -1018,8 +1027,8 @@
if (iter != jni_handlers_.end()) {
(*iter->second)(self, method, receiver, args, result);
} else if (Runtime::Current()->IsActiveTransaction()) {
- AbortTransaction(self, "Attempt to invoke native method in non-started runtime: %s",
- name.c_str());
+ AbortTransactionF(self, "Attempt to invoke native method in non-started runtime: %s",
+ name.c_str());
} else {
LOG(FATAL) << "Calling native method " << PrettyMethod(method) << " in an unstarted "
"non-transactional runtime";
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 09bfbf3..b795d72 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -748,19 +748,18 @@
void JavaVMExt::SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg) {
MutexLock mu(Thread::Current(), weak_globals_lock_);
- for (mirror::Object** entry : weak_globals_) {
- // Since this is called by the GC, we don't need a read barrier.
- mirror::Object* obj = *entry;
- if (obj == nullptr) {
- // Need to skip null here to distinguish between null entries
- // and cleared weak ref entries.
- continue;
+ Runtime* const runtime = Runtime::Current();
+ for (auto* entry : weak_globals_) {
+ // Need to skip null here to distinguish between null entries and cleared weak ref entries.
+ if (!entry->IsNull()) {
+ // Since this is called by the GC, we don't need a read barrier.
+ mirror::Object* obj = entry->Read<kWithoutReadBarrier>();
+ mirror::Object* new_obj = callback(obj, arg);
+ if (new_obj == nullptr) {
+ new_obj = runtime->GetClearedJniWeakGlobal();
+ }
+ *entry = GcRoot<mirror::Object>(new_obj);
}
- mirror::Object* new_obj = callback(obj, arg);
- if (new_obj == nullptr) {
- new_obj = Runtime::Current()->GetClearedJniWeakGlobal();
- }
- *entry = new_obj;
}
}
@@ -769,10 +768,10 @@
globals_.Trim();
}
-void JavaVMExt::VisitRoots(RootCallback* callback, void* arg) {
+void JavaVMExt::VisitRoots(RootVisitor* visitor) {
Thread* self = Thread::Current();
ReaderMutexLock mu(self, globals_lock_);
- globals_.VisitRoots(callback, arg, RootInfo(kRootJNIGlobal));
+ globals_.VisitRoots(visitor, RootInfo(kRootJNIGlobal));
// The weak_globals table is visited by the GC itself (because it mutates the table).
}
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
index 037fbe5..deec6a9 100644
--- a/runtime/java_vm_ext.h
+++ b/runtime/java_vm_ext.h
@@ -103,7 +103,7 @@
bool SetCheckJniEnabled(bool enabled);
- void VisitRoots(RootCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VisitRoots(RootVisitor* visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void DisallowNewWeakGlobals() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void AllowNewWeakGlobals() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index e16221c..31c9a0b 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -403,6 +403,14 @@
// Used for VirtualMachine.Exit command handling.
bool should_exit_;
int exit_status_;
+
+ // Used to synchronize runtime shutdown with JDWP command handler thread.
+ // When the runtime shuts down, it needs to stop JDWP command handler thread by closing the
+ // JDWP connection. However, if the JDWP thread is processing a command, it needs to wait
+ // for the command to finish so we can send its reply before closing the connection.
+ Mutex shutdown_lock_ ACQUIRED_AFTER(event_list_lock_);
+ ConditionVariable shutdown_cond_ GUARDED_BY(shutdown_lock_);
+ bool processing_request_ GUARDED_BY(shutdown_lock_);
};
std::string DescribeField(const FieldId& field_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index add1394..d0ca214 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -271,7 +271,7 @@
static JdwpError VM_Dispose(JdwpState*, Request*, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- Dbg::Disposed();
+ Dbg::Dispose();
return ERR_NONE;
}
@@ -315,11 +315,12 @@
static JdwpError VM_CreateString(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
std::string str(request->ReadUtf8String());
- ObjectId stringId = Dbg::CreateString(str);
- if (stringId == 0) {
- return ERR_OUT_OF_MEMORY;
+ ObjectId string_id;
+ JdwpError status = Dbg::CreateString(str, &string_id);
+ if (status != ERR_NONE) {
+ return status;
}
- expandBufAddObjectId(pReply, stringId);
+ expandBufAddObjectId(pReply, string_id);
return ERR_NONE;
}
@@ -711,9 +712,6 @@
if (status != ERR_NONE) {
return status;
}
- if (object_id == 0) {
- return ERR_OUT_OF_MEMORY;
- }
return RequestInvoke(state, request, pReply, thread_id, object_id, class_id, method_id, true);
}
@@ -730,9 +728,6 @@
if (status != ERR_NONE) {
return status;
}
- if (object_id == 0) {
- return ERR_OUT_OF_MEMORY;
- }
expandBufAdd1(pReply, JT_ARRAY);
expandBufAddObjectId(pReply, object_id);
return ERR_NONE;
@@ -1657,6 +1652,7 @@
if (result == ERR_NONE) {
request->CheckConsumed();
}
+ self->AssertNoPendingException();
break;
}
}
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index e2b88a5..5b30f0c 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -126,6 +126,7 @@
*/
ssize_t JdwpNetStateBase::WritePacket(ExpandBuf* pReply, size_t length) {
MutexLock mu(Thread::Current(), socket_lock_);
+ DCHECK(IsConnected()) << "Connection with debugger is closed";
DCHECK_LE(length, expandBufGetLength(pReply));
return TEMP_FAILURE_RETRY(write(clientSock, expandBufGetBuffer(pReply), length));
}
@@ -140,6 +141,7 @@
ssize_t JdwpNetStateBase::WriteBufferedPacketLocked(const std::vector<iovec>& iov) {
socket_lock_.AssertHeld(Thread::Current());
+ DCHECK(IsConnected()) << "Connection with debugger is closed";
return TEMP_FAILURE_RETRY(writev(clientSock, &iov[0], iov.size()));
}
@@ -225,7 +227,10 @@
jdwp_token_owner_thread_id_(0),
ddm_is_active_(false),
should_exit_(false),
- exit_status_(0) {
+ exit_status_(0),
+ shutdown_lock_("JDWP shutdown lock", kJdwpShutdownLock),
+ shutdown_cond_("JDWP shutdown condition variable", shutdown_lock_),
+ processing_request_(false) {
}
/*
@@ -338,10 +343,20 @@
JdwpState::~JdwpState() {
if (netState != nullptr) {
/*
- * Close down the network to inspire the thread to halt.
+ * Close down the network to inspire the thread to halt. If a request is being processed,
+ * we need to wait for it to finish first.
*/
- VLOG(jdwp) << "JDWP shutting down net...";
- netState->Shutdown();
+ {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, shutdown_lock_);
+ while (processing_request_) {
+ VLOG(jdwp) << "JDWP command in progress: wait for it to finish ...";
+ shutdown_cond_.Wait(self);
+ }
+
+ VLOG(jdwp) << "JDWP shutting down net...";
+ netState->Shutdown();
+ }
if (debug_thread_started_) {
run = false;
@@ -369,7 +384,13 @@
// Returns "false" if we encounter a connection-fatal error.
bool JdwpState::HandlePacket() {
- JdwpNetStateBase* netStateBase = reinterpret_cast<JdwpNetStateBase*>(netState);
+ Thread* const self = Thread::Current();
+ {
+ MutexLock mu(self, shutdown_lock_);
+ processing_request_ = true;
+ }
+ JdwpNetStateBase* netStateBase = netState;
+ CHECK(netStateBase != nullptr) << "Connection has been closed";
JDWP::Request request(netStateBase->input_buffer_, netStateBase->input_count_);
ExpandBuf* pReply = expandBufAlloc();
@@ -388,6 +409,11 @@
}
expandBufFree(pReply);
netStateBase->ConsumeBytes(request.GetLength());
+ {
+ MutexLock mu(self, shutdown_lock_);
+ processing_request_ = false;
+ shutdown_cond_.Broadcast(self);
+ }
return true;
}
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 5e38470..9ec64d4 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -41,7 +41,7 @@
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
-#include "mirror/field.h"
+#include "mirror/field-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/string-inl.h"
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 7f04992..6452f31 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -196,8 +196,8 @@
}
template<class T>
-inline void PrimitiveArray<T>::VisitRoots(RootCallback* callback, void* arg) {
- array_class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
+inline void PrimitiveArray<T>::VisitRoots(RootVisitor* visitor) {
+ array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
template<typename T>
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 83e3688..115fcf2 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -166,8 +166,7 @@
array_class_ = GcRoot<Class>(nullptr);
}
- static void VisitRoots(RootCallback* callback, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static void VisitRoots(RootVisitor* visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
static GcRoot<Class> array_class_;
diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc
index 4c36753..83602d4 100644
--- a/runtime/mirror/art_field.cc
+++ b/runtime/mirror/art_field.cc
@@ -55,8 +55,8 @@
SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, offset_), num_bytes.Uint32Value());
}
-void ArtField::VisitRoots(RootCallback* callback, void* arg) {
- java_lang_reflect_ArtField_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
+void ArtField::VisitRoots(RootVisitor* visitor) {
+ java_lang_reflect_ArtField_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
// TODO: we could speed up the search if fields are ordered by offsets.
diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h
index d640165..9d95cb9 100644
--- a/runtime/mirror/art_field.h
+++ b/runtime/mirror/art_field.h
@@ -138,7 +138,7 @@
static void SetClass(Class* java_lang_reflect_ArtField);
static void ResetClass();
- static void VisitRoots(RootCallback* callback, void* arg)
+ static void VisitRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsVolatile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index c1f7594..edbbb4a 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -61,8 +61,8 @@
}
-void ArtMethod::VisitRoots(RootCallback* callback, void* arg) {
- java_lang_reflect_ArtMethod_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
+void ArtMethod::VisitRoots(RootVisitor* visitor) {
+ java_lang_reflect_ArtMethod_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
mirror::String* ArtMethod::GetNameAsString(Thread* self) {
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index 82e5d00..22481ce 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -488,7 +488,7 @@
static void ResetClass();
- static void VisitRoots(RootCallback* callback, void* arg)
+ static void VisitRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const DexFile* GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 29851a9..8fb8147 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -51,8 +51,8 @@
java_lang_Class_ = GcRoot<Class>(nullptr);
}
-void Class::VisitRoots(RootCallback* callback, void* arg) {
- java_lang_Class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
+void Class::VisitRoots(RootVisitor* visitor) {
+ java_lang_Class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 2dff383..b82a58f 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -971,7 +971,7 @@
// Can't call this SetClass or else gets called instead of Object::SetClass in places.
static void SetClassClass(Class* java_lang_Class) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void ResetClass();
- static void VisitRoots(RootCallback* callback, void* arg)
+ static void VisitRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// When class is verified, set the kAccPreverified flag on each method.
diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc
index 1724682..82cc26e 100644
--- a/runtime/mirror/field.cc
+++ b/runtime/mirror/field.cc
@@ -48,9 +48,9 @@
array_class_ = GcRoot<Class>(nullptr);
}
-void Field::VisitRoots(RootCallback* callback, void* arg) {
- static_class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
- array_class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
+void Field::VisitRoots(RootVisitor* visitor) {
+ static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+ array_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
ArtField* Field::GetArtField() {
diff --git a/runtime/mirror/field.h b/runtime/mirror/field.h
index f54340a..cea06f5 100644
--- a/runtime/mirror/field.h
+++ b/runtime/mirror/field.h
@@ -89,7 +89,7 @@
static void ResetArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void VisitRoots(RootCallback* callback, void* arg)
+ static void VisitRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Slow, try to use only for PrettyField and such.
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index b730670..cfc8549 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -90,6 +90,9 @@
void SetClass(Class* new_klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Object* GetReadBarrierPointer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+#ifndef USE_BAKER_OR_BROOKS_READ_BARRIER
+ NO_RETURN
+#endif
void SetReadBarrierPointer(Object* rb_ptr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool AtomicSetReadBarrierPointer(Object* expected_rb_ptr, Object* rb_ptr)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h
index b63d13d..5edda8b 100644
--- a/runtime/mirror/object_reference.h
+++ b/runtime/mirror/object_reference.h
@@ -43,6 +43,11 @@
void Clear() {
reference_ = 0;
+ DCHECK(IsNull());
+ }
+
+ bool IsNull() const {
+ return reference_ == 0;
}
uint32_t AsVRegValue() const {
@@ -86,6 +91,23 @@
: ObjectReference<kPoisonHeapReferences, MirrorType>(mirror_ptr) {}
};
+// Standard compressed reference used in the runtime. Used for StackRefernce and GC roots.
+template<class MirrorType>
+class MANAGED CompressedReference : public mirror::ObjectReference<false, MirrorType> {
+ public:
+ CompressedReference<MirrorType>() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : mirror::ObjectReference<false, MirrorType>(nullptr) {}
+
+ static CompressedReference<MirrorType> FromMirrorPtr(MirrorType* p)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return CompressedReference<MirrorType>(p);
+ }
+
+ private:
+ CompressedReference<MirrorType>(MirrorType* p) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : mirror::ObjectReference<false, MirrorType>(p) {}
+};
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/reference.cc b/runtime/mirror/reference.cc
index 35130e8..70bcf92 100644
--- a/runtime/mirror/reference.cc
+++ b/runtime/mirror/reference.cc
@@ -16,6 +16,9 @@
#include "reference.h"
+#include "mirror/art_method.h"
+#include "gc_root-inl.h"
+
namespace art {
namespace mirror {
@@ -32,8 +35,8 @@
java_lang_ref_Reference_ = GcRoot<Class>(nullptr);
}
-void Reference::VisitRoots(RootCallback* callback, void* arg) {
- java_lang_ref_Reference_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
+void Reference::VisitRoots(RootVisitor* visitor) {
+ java_lang_ref_Reference_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
} // namespace mirror
diff --git a/runtime/mirror/reference.h b/runtime/mirror/reference.h
index 69ef69c..c11d79d 100644
--- a/runtime/mirror/reference.h
+++ b/runtime/mirror/reference.h
@@ -100,7 +100,7 @@
}
static void SetClass(Class* klass);
static void ResetClass();
- static void VisitRoots(RootCallback* callback, void* arg);
+ static void VisitRoots(RootVisitor* visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
// Note: This avoids a read barrier, it should only be used by the GC.
diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc
index c2a67e8..ec2b495 100644
--- a/runtime/mirror/stack_trace_element.cc
+++ b/runtime/mirror/stack_trace_element.cc
@@ -67,8 +67,8 @@
line_number);
}
-void StackTraceElement::VisitRoots(RootCallback* callback, void* arg) {
- java_lang_StackTraceElement_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
+void StackTraceElement::VisitRoots(RootVisitor* visitor) {
+ java_lang_StackTraceElement_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h
index 70acd1c..dc7131e 100644
--- a/runtime/mirror/stack_trace_element.h
+++ b/runtime/mirror/stack_trace_element.h
@@ -54,7 +54,7 @@
static void SetClass(Class* java_lang_StackTraceElement);
static void ResetClass();
- static void VisitRoots(RootCallback* callback, void* arg)
+ static void VisitRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static Class* GetStackTraceElement() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(!java_lang_StackTraceElement_.IsNull());
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index e7c88c5..bd6a63c 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -253,8 +253,8 @@
return countDiff;
}
-void String::VisitRoots(RootCallback* callback, void* arg) {
- java_lang_String_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
+void String::VisitRoots(RootVisitor* visitor) {
+ java_lang_String_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
} // namespace mirror
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 6c22b9b..0670d0b 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -127,7 +127,7 @@
static void SetClass(Class* java_lang_String);
static void ResetClass();
- static void VisitRoots(RootCallback* callback, void* arg)
+ static void VisitRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// TODO: Make this private. It's only used on ObjectTest at the moment.
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index fdfeb47..b564649 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -144,8 +144,8 @@
java_lang_Throwable_ = GcRoot<Class>(nullptr);
}
-void Throwable::VisitRoots(RootCallback* callback, void* arg) {
- java_lang_Throwable_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
+void Throwable::VisitRoots(RootVisitor* visitor) {
+ java_lang_Throwable_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
} // namespace mirror
diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h
index c22475b..9cc0b6f 100644
--- a/runtime/mirror/throwable.h
+++ b/runtime/mirror/throwable.h
@@ -55,7 +55,7 @@
static void SetClass(Class* java_lang_Throwable);
static void ResetClass();
- static void VisitRoots(RootCallback* callback, void* arg)
+ static void VisitRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index c182a4d..87ae64d 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -297,22 +297,15 @@
return result;
}
-// Java: dalvik.system.DexFile.UP_TO_DATE
-static const jbyte kUpToDate = 0;
-// Java: dalvik.system.DexFile.DEXOPT_NEEDED
-static const jbyte kPatchoatNeeded = 1;
-// Java: dalvik.system.DexFile.PATCHOAT_NEEDED
-static const jbyte kDexoptNeeded = 2;
-
-static jbyte IsDexOptNeededInternal(JNIEnv* env, const char* filename,
+static jint GetDexOptNeeded(JNIEnv* env, const char* filename,
const char* pkgname, const char* instruction_set, const jboolean defer) {
if ((filename == nullptr) || !OS::FileExists(filename)) {
- LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename << "' does not exist";
+ LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist";
ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
const char* message = (filename == nullptr) ? "<empty file name>" : filename;
env->ThrowNew(fnfe.get(), message);
- return kUpToDate;
+ return OatFileAssistant::kNoDexOptNeeded;
}
const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
@@ -330,7 +323,7 @@
// Always treat elements of the bootclasspath as up-to-date.
if (oat_file_assistant.IsInBootClassPath()) {
- return kUpToDate;
+ return OatFileAssistant::kNoDexOptNeeded;
}
// TODO: Checking the profile should probably be done in the GetStatus()
@@ -343,7 +336,7 @@
if (!defer) {
oat_file_assistant.CopyProfileFile();
}
- return kDexoptNeeded;
+ return OatFileAssistant::kDex2OatNeeded;
} else if (oat_file_assistant.ProfileExists()
&& !oat_file_assistant.OldProfileExists()) {
if (!defer) {
@@ -353,16 +346,10 @@
}
}
- OatFileAssistant::Status status = oat_file_assistant.GetStatus();
- switch (status) {
- case OatFileAssistant::kUpToDate: return kUpToDate;
- case OatFileAssistant::kNeedsRelocation: return kPatchoatNeeded;
- case OatFileAssistant::kOutOfDate: return kDexoptNeeded;
- }
- UNREACHABLE();
+ return oat_file_assistant.GetDexOptNeeded();
}
-static jbyte DexFile_isDexOptNeededInternal(JNIEnv* env, jclass, jstring javaFilename,
+static jint DexFile_getDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename,
jstring javaPkgname, jstring javaInstructionSet, jboolean defer) {
ScopedUtfChars filename(env, javaFilename);
if (env->ExceptionCheck()) {
@@ -376,25 +363,25 @@
return 0;
}
- return IsDexOptNeededInternal(env, filename.c_str(), pkgname.c_str(),
- instruction_set.c_str(), defer);
+ return GetDexOptNeeded(env, filename.c_str(), pkgname.c_str(),
+ instruction_set.c_str(), defer);
}
// public API, NULL pkgname
static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
const char* instruction_set = GetInstructionSetString(kRuntimeISA);
ScopedUtfChars filename(env, javaFilename);
- return kUpToDate != IsDexOptNeededInternal(env, filename.c_str(), nullptr /* pkgname */,
- instruction_set, false /* defer */);
+ jint status = GetDexOptNeeded(env, filename.c_str(), nullptr /* pkgname */,
+ instruction_set, false /* defer */);
+ return (status != OatFileAssistant::kNoDexOptNeeded) ? JNI_TRUE : JNI_FALSE;
}
-
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)V"),
NATIVE_METHOD(DexFile, defineClassNative, "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Object;)Ljava/lang/Class;"),
NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
- NATIVE_METHOD(DexFile, isDexOptNeededInternal, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)B"),
+ NATIVE_METHOD(DexFile, getDexOptNeeded, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I"),
NATIVE_METHOD(DexFile, openDexFileNative, "(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/Object;"),
};
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 6e3f1bc..760038a 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -248,13 +248,20 @@
typedef std::map<std::string, mirror::String*> StringTable;
-static void PreloadDexCachesStringsCallback(mirror::Object** root, void* arg,
- const RootInfo& /*root_info*/)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- StringTable& table = *reinterpret_cast<StringTable*>(arg);
- mirror::String* string = const_cast<mirror::Object*>(*root)->AsString();
- table[string->ToModifiedUtf8()] = string;
-}
+class PreloadDexCachesStringsVisitor : public SingleRootVisitor {
+ public:
+ explicit PreloadDexCachesStringsVisitor(StringTable* table) : table_(table) {
+ }
+
+ void VisitRoot(mirror::Object* root, const RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::String* string = root->AsString();
+ table_->operator[](string->ToModifiedUtf8()) = string;
+ }
+
+ private:
+ StringTable* const table_;
+};
// Based on ClassLinker::ResolveString.
static void PreloadDexCachesResolveString(Handle<mirror::DexCache> dex_cache, uint32_t string_idx,
@@ -469,8 +476,8 @@
// We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings
StringTable strings;
if (kPreloadDexCachesStrings) {
- runtime->GetInternTable()->VisitRoots(PreloadDexCachesStringsCallback, &strings,
- kVisitRootFlagAllRoots);
+ PreloadDexCachesStringsVisitor visitor(&strings);
+ runtime->GetInternTable()->VisitRoots(&visitor, kVisitRootFlagAllRoots);
}
const std::vector<const DexFile*>& boot_class_path = linker->GetBootClassPath();
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 0ca9d24..c893f0a 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -24,7 +24,7 @@
#include "mirror/art_field-inl.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
-#include "mirror/field.h"
+#include "mirror/field-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/string-inl.h"
diff --git a/runtime/oat.h b/runtime/oat.h
index 120de6d..de95fef 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -156,6 +156,8 @@
~OatMethodOffsets();
+ OatMethodOffsets& operator=(const OatMethodOffsets&) = default;
+
uint32_t code_offset_;
};
@@ -169,6 +171,8 @@
~OatQuickMethodHeader();
+ OatQuickMethodHeader& operator=(const OatQuickMethodHeader&) = default;
+
// The offset in bytes from the start of the mapping table to the end of the header.
uint32_t mapping_table_offset_;
// The offset in bytes from the start of the vmap table to the end of the header.
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 69cb22d..81703b1 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -453,7 +453,7 @@
std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_,
- dex_file_location_checksum_, GetOatFile(), error_msg);
+ dex_file_location_checksum_, this, error_msg);
}
uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const {
@@ -495,12 +495,12 @@
CHECK_LE(methods_pointer, oat_file_->End()) << oat_file_->GetLocation();
}
- return OatClass(oat_file_,
- status,
- type,
- bitmap_size,
- reinterpret_cast<const uint32_t*>(bitmap_pointer),
- reinterpret_cast<const OatMethodOffsets*>(methods_pointer));
+ return OatFile::OatClass(oat_file_,
+ status,
+ type,
+ bitmap_size,
+ reinterpret_cast<const uint32_t*>(bitmap_pointer),
+ reinterpret_cast<const OatMethodOffsets*>(methods_pointer));
}
OatFile::OatClass::OatClass(const OatFile* oat_file,
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 51952f3..73a8c8e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -37,9 +37,12 @@
class MemMap;
class OatMethodOffsets;
class OatHeader;
+class OatDexFile;
-class OatFile {
+class OatFile FINAL {
public:
+ typedef art::OatDexFile OatDexFile;
+
// Opens an oat file contained within the given elf file. This is always opened as
// non-executable at the moment.
static OatFile* OpenWithElfFile(ElfFile* elf_file, const std::string& location,
@@ -90,9 +93,7 @@
const OatHeader& GetOatHeader() const;
- class OatDexFile;
-
- class OatMethod {
+ class OatMethod FINAL {
public:
void LinkMethod(mirror::ArtMethod* method) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -133,8 +134,11 @@
OatMethod(const uint8_t* base, const uint32_t code_offset)
: begin_(base), code_offset_(code_offset) {
}
+ OatMethod(const OatMethod&) = default;
~OatMethod() {}
+ OatMethod& operator=(const OatMethod&) = default;
+
// A representation of an invalid OatMethod, used when an OatMethod or OatClass can't be found.
// See ClassLinker::FindOatMethodFor.
static const OatMethod Invalid() {
@@ -156,7 +160,7 @@
friend class OatClass;
};
- class OatClass {
+ class OatClass FINAL {
public:
mirror::Class::Status GetStatus() const {
return status_;
@@ -207,63 +211,8 @@
const OatMethodOffsets* const methods_pointer_;
- friend class OatDexFile;
+ friend class art::OatDexFile;
};
-
- class OatDexFile {
- public:
- // Opens the DexFile referred to by this OatDexFile from within the containing OatFile.
- std::unique_ptr<const DexFile> OpenDexFile(std::string* error_msg) const;
-
- const OatFile* GetOatFile() const {
- return oat_file_;
- }
-
- // Returns the size of the DexFile refered to by this OatDexFile.
- size_t FileSize() const;
-
- // Returns original path of DexFile that was the source of this OatDexFile.
- const std::string& GetDexFileLocation() const {
- return dex_file_location_;
- }
-
- // Returns the canonical location of DexFile that was the source of this OatDexFile.
- const std::string& GetCanonicalDexFileLocation() const {
- return canonical_dex_file_location_;
- }
-
- // Returns checksum of original DexFile that was the source of this OatDexFile;
- uint32_t GetDexFileLocationChecksum() const {
- return dex_file_location_checksum_;
- }
-
- // Returns the OatClass for the class specified by the given DexFile class_def_index.
- OatClass GetOatClass(uint16_t class_def_index) const;
-
- // Returns the offset to the OatClass information. Most callers should use GetOatClass.
- uint32_t GetOatClassOffset(uint16_t class_def_index) const;
-
- ~OatDexFile();
-
- private:
- OatDexFile(const OatFile* oat_file,
- const std::string& dex_file_location,
- const std::string& canonical_dex_file_location,
- uint32_t dex_file_checksum,
- const uint8_t* dex_file_pointer,
- const uint32_t* oat_class_offsets_pointer);
-
- const OatFile* const oat_file_;
- const std::string dex_file_location_;
- const std::string canonical_dex_file_location_;
- const uint32_t dex_file_location_checksum_;
- const uint8_t* const dex_file_pointer_;
- const uint32_t* const oat_class_offsets_pointer_;
-
- friend class OatFile;
- DISALLOW_COPY_AND_ASSIGN(OatDexFile);
- };
-
const OatDexFile* GetOatDexFile(const char* dex_location,
const uint32_t* const dex_location_checksum,
bool exception_if_not_found = true) const
@@ -382,11 +331,69 @@
mutable std::list<std::string> string_cache_ GUARDED_BY(secondary_lookup_lock_);
friend class OatClass;
- friend class OatDexFile;
+ friend class art::OatDexFile;
friend class OatDumper; // For GetBase and GetLimit
DISALLOW_COPY_AND_ASSIGN(OatFile);
};
+// OatDexFile should be an inner class of OatFile. Unfortunately, C++ doesn't
+// support forward declarations of inner classes, and we want to
+// forward-declare OatDexFile so that we can store an opaque pointer to an
+// OatDexFile in DexFile.
+class OatDexFile FINAL {
+ public:
+ // Opens the DexFile referred to by this OatDexFile from within the containing OatFile.
+ std::unique_ptr<const DexFile> OpenDexFile(std::string* error_msg) const;
+
+ const OatFile* GetOatFile() const {
+ return oat_file_;
+ }
+
+ // Returns the size of the DexFile refered to by this OatDexFile.
+ size_t FileSize() const;
+
+ // Returns original path of DexFile that was the source of this OatDexFile.
+ const std::string& GetDexFileLocation() const {
+ return dex_file_location_;
+ }
+
+ // Returns the canonical location of DexFile that was the source of this OatDexFile.
+ const std::string& GetCanonicalDexFileLocation() const {
+ return canonical_dex_file_location_;
+ }
+
+ // Returns checksum of original DexFile that was the source of this OatDexFile;
+ uint32_t GetDexFileLocationChecksum() const {
+ return dex_file_location_checksum_;
+ }
+
+ // Returns the OatClass for the class specified by the given DexFile class_def_index.
+ OatFile::OatClass GetOatClass(uint16_t class_def_index) const;
+
+ // Returns the offset to the OatClass information. Most callers should use GetOatClass.
+ uint32_t GetOatClassOffset(uint16_t class_def_index) const;
+
+ ~OatDexFile();
+
+ private:
+ OatDexFile(const OatFile* oat_file,
+ const std::string& dex_file_location,
+ const std::string& canonical_dex_file_location,
+ uint32_t dex_file_checksum,
+ const uint8_t* dex_file_pointer,
+ const uint32_t* oat_class_offsets_pointer);
+
+ const OatFile* const oat_file_;
+ const std::string dex_file_location_;
+ const std::string canonical_dex_file_location_;
+ const uint32_t dex_file_location_checksum_;
+ const uint8_t* const dex_file_pointer_;
+ const uint32_t* const oat_class_offsets_pointer_;
+
+ friend class OatFile;
+ DISALLOW_COPY_AND_ASSIGN(OatDexFile);
+};
+
} // namespace art
#endif // ART_RUNTIME_OAT_FILE_H_
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index d92f59b..e5c27b2 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -142,31 +142,31 @@
return true;
}
-OatFileAssistant::Status OatFileAssistant::GetStatus() {
+OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() {
// TODO: If the profiling code is ever restored, it's worth considering
// whether we should check to see if the profile is out of date here.
- if (OdexFileIsOutOfDate()) {
- // The DEX file is not pre-compiled.
- // TODO: What if the oat file is not out of date? Could we relocate it
- // from itself?
- return OatFileIsUpToDate() ? kUpToDate : kOutOfDate;
- } else {
- // The DEX file is pre-compiled. If the oat file isn't up to date, we can
- // patch the pre-compiled version rather than recompiling.
- if (OatFileIsUpToDate() || OdexFileIsUpToDate()) {
- return kUpToDate;
- } else {
- return kNeedsRelocation;
- }
+ if (OatFileIsUpToDate() || OdexFileIsUpToDate()) {
+ return kNoDexOptNeeded;
}
+
+ if (OdexFileNeedsRelocation()) {
+ return kPatchOatNeeded;
+ }
+
+ if (OatFileNeedsRelocation()) {
+ return kSelfPatchOatNeeded;
+ }
+
+ return kDex2OatNeeded;
}
bool OatFileAssistant::MakeUpToDate(std::string* error_msg) {
- switch (GetStatus()) {
- case kUpToDate: return true;
- case kNeedsRelocation: return RelocateOatFile(error_msg);
- case kOutOfDate: return GenerateOatFile(error_msg);
+ switch (GetDexOptNeeded()) {
+ case kNoDexOptNeeded: return true;
+ case kDex2OatNeeded: return GenerateOatFile(error_msg);
+ case kPatchOatNeeded: return RelocateOatFile(OdexFileName(), error_msg);
+ case kSelfPatchOatNeeded: return RelocateOatFile(OatFileName(), error_msg);
}
UNREACHABLE();
}
@@ -269,14 +269,14 @@
return GetOdexFile() != nullptr;
}
-OatFileAssistant::Status OatFileAssistant::OdexFileStatus() {
+OatFileAssistant::OatStatus OatFileAssistant::OdexFileStatus() {
if (OdexFileIsOutOfDate()) {
- return kOutOfDate;
+ return kOatOutOfDate;
}
if (OdexFileIsUpToDate()) {
- return kUpToDate;
+ return kOatUpToDate;
}
- return kNeedsRelocation;
+ return kOatNeedsRelocation;
}
bool OatFileAssistant::OdexFileIsOutOfDate() {
@@ -293,7 +293,7 @@
}
bool OatFileAssistant::OdexFileNeedsRelocation() {
- return OdexFileStatus() == kNeedsRelocation;
+ return OdexFileStatus() == kOatNeedsRelocation;
}
bool OatFileAssistant::OdexFileIsUpToDate() {
@@ -338,14 +338,14 @@
return GetOatFile() != nullptr;
}
-OatFileAssistant::Status OatFileAssistant::OatFileStatus() {
+OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() {
if (OatFileIsOutOfDate()) {
- return kOutOfDate;
+ return kOatOutOfDate;
}
if (OatFileIsUpToDate()) {
- return kUpToDate;
+ return kOatUpToDate;
}
- return kNeedsRelocation;
+ return kOatNeedsRelocation;
}
bool OatFileAssistant::OatFileIsOutOfDate() {
@@ -362,7 +362,7 @@
}
bool OatFileAssistant::OatFileNeedsRelocation() {
- return OatFileStatus() == kNeedsRelocation;
+ return OatFileStatus() == kOatNeedsRelocation;
}
bool OatFileAssistant::OatFileIsUpToDate() {
@@ -378,17 +378,17 @@
return cached_oat_file_is_up_to_date_;
}
-OatFileAssistant::Status OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
+OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
// TODO: This could cause GivenOatFileIsOutOfDate to be called twice, which
// is more work than we need to do. If performance becomes a concern, and
// this method is actually called, this should be fixed.
if (GivenOatFileIsOutOfDate(file)) {
- return kOutOfDate;
+ return kOatOutOfDate;
}
if (GivenOatFileIsUpToDate(file)) {
- return kUpToDate;
+ return kOatUpToDate;
}
- return kNeedsRelocation;
+ return kOatNeedsRelocation;
}
bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) {
@@ -451,7 +451,7 @@
}
bool OatFileAssistant::GivenOatFileNeedsRelocation(const OatFile& file) {
- return GivenOatFileStatus(file) == kNeedsRelocation;
+ return GivenOatFileStatus(file) == kOatNeedsRelocation;
}
bool OatFileAssistant::GivenOatFileIsUpToDate(const OatFile& file) {
@@ -592,16 +592,17 @@
}
}
-bool OatFileAssistant::RelocateOatFile(std::string* error_msg) {
+bool OatFileAssistant::RelocateOatFile(const std::string* input_file,
+ std::string* error_msg) {
CHECK(error_msg != nullptr);
- if (OdexFileName() == nullptr) {
+ if (input_file == nullptr) {
*error_msg = "Patching of oat file for dex location "
+ std::string(dex_location_)
- + " not attempted because the odex file name could not be determined.";
+ + " not attempted because the input file name could not be determined.";
return false;
}
- const std::string& odex_file_name = *OdexFileName();
+ const std::string& input_file_name = *input_file;
if (OatFileName() == nullptr) {
*error_msg = "Patching of oat file for dex location "
@@ -628,7 +629,7 @@
std::vector<std::string> argv;
argv.push_back(runtime->GetPatchoatExecutable());
argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(isa_)));
- argv.push_back("--input-oat-file=" + odex_file_name);
+ argv.push_back("--input-oat-file=" + input_file_name);
argv.push_back("--output-oat-file=" + oat_file_name);
argv.push_back("--patched-image-location=" + image_info->location);
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index f2abcf9..9e7c2ef 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -43,20 +43,43 @@
// be restored and tested, or removed.
class OatFileAssistant {
public:
- enum Status {
- // kOutOfDate - An oat file is said to be out of date if the file does not
- // exist, or is out of date with respect to the dex file or boot image.
- kOutOfDate,
+ enum DexOptNeeded {
+ // kNoDexOptNeeded - The code for this dex location is up to date and can
+ // be used as is.
+ // Matches Java: dalvik.system.DexFile.NO_DEXOPT_NEEDED = 0
+ kNoDexOptNeeded = 0,
- // kNeedsRelocation - An oat file is said to need relocation if the code
- // is up to date, but not yet properly relocated for address space layout
- // randomization (ASLR). In this case, the oat file is neither "out of
- // date" nor "up to date".
- kNeedsRelocation,
+ // kDex2OatNeeded - In order to make the code for this dex location up to
+ // date, dex2oat must be run on the dex file.
+ // Matches Java: dalvik.system.DexFile.DEX2OAT_NEEDED = 1
+ kDex2OatNeeded = 1,
- // kUpToDate - An oat file is said to be up to date if it is not out of
+ // kPatchOatNeeded - In order to make the code for this dex location up to
+ // date, patchoat must be run on the odex file.
+ // Matches Java: dalvik.system.DexFile.PATCHOAT_NEEDED = 2
+ kPatchOatNeeded = 2,
+
+ // kSelfPatchOatNeeded - In order to make the code for this dex location
+ // up to date, patchoat must be run on the oat file.
+ // Matches Java: dalvik.system.DexFile.SELF_PATCHOAT_NEEDED = 3
+ kSelfPatchOatNeeded = 3,
+ };
+
+ enum OatStatus {
+ // kOatOutOfDate - An oat file is said to be out of date if the file does
+ // not exist, or is out of date with respect to the dex file or boot
+ // image.
+ kOatOutOfDate,
+
+ // kOatNeedsRelocation - An oat file is said to need relocation if the
+ // code is up to date, but not yet properly relocated for address space
+ // layout randomization (ASLR). In this case, the oat file is neither
+ // "out of date" nor "up to date".
+ kOatNeedsRelocation,
+
+ // kOatUpToDate - An oat file is said to be up to date if it is not out of
// date and has been properly relocated for the purposes of ASLR.
- kUpToDate,
+ kOatUpToDate,
};
// Constructs an OatFileAssistant object to assist the oat file
@@ -67,7 +90,6 @@
// Typically the dex_location is the absolute path to the original,
// un-optimized dex file.
//
- //
// Note: Currently the dex_location must have an extension.
// TODO: Relax this restriction?
//
@@ -121,8 +143,9 @@
// file.
bool Lock(std::string* error_msg);
- // Returns the overall compilation status for the given dex location.
- Status GetStatus();
+ // Return what action needs to be taken to produce up-to-date code for this
+ // dex location.
+ DexOptNeeded GetDexOptNeeded();
// Attempts to generate or relocate the oat file as needed to make it up to
// date.
@@ -164,7 +187,7 @@
// determined.
const std::string* OdexFileName();
bool OdexFileExists();
- Status OdexFileStatus();
+ OatStatus OdexFileStatus();
bool OdexFileIsOutOfDate();
bool OdexFileNeedsRelocation();
bool OdexFileIsUpToDate();
@@ -176,20 +199,18 @@
// the dex location.
//
// Notes:
- // * To get the overall status of the compiled code for this dex_location,
- // use the GetStatus() method, not the OatFileStatus() method.
// * OatFileName may return null if the oat file name could not be
// determined.
const std::string* OatFileName();
bool OatFileExists();
- Status OatFileStatus();
+ OatStatus OatFileStatus();
bool OatFileIsOutOfDate();
bool OatFileNeedsRelocation();
bool OatFileIsUpToDate();
// These methods return the status for a given opened oat file with respect
// to the dex location.
- Status GivenOatFileStatus(const OatFile& file);
+ OatStatus GivenOatFileStatus(const OatFile& file);
bool GivenOatFileIsOutOfDate(const OatFile& file);
bool GivenOatFileNeedsRelocation(const OatFile& file);
bool GivenOatFileIsUpToDate(const OatFile& file);
@@ -216,7 +237,7 @@
// Copy the current profile to the old profile location.
void CopyProfileFile();
- // Generates the oat file by relocation from the odex file.
+ // Generates the oat file by relocation from the named input file.
// This does not check the current status before attempting to relocate the
// oat file.
// Returns true on success.
@@ -224,7 +245,7 @@
//
// If there is a failure, the value of error_msg will be set to a string
// describing why there was failure. error_msg must not be nullptr.
- bool RelocateOatFile(std::string* error_msg);
+ bool RelocateOatFile(const std::string* input_file, std::string* error_msg);
// Generate the oat file from the dex file.
// This does not check the current status before attempting to generate the
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index a8b0876..d2362a2 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -29,7 +29,9 @@
#include "common_runtime_test.h"
#include "compiler_callbacks.h"
#include "mem_map.h"
+#include "mirror/art_field-inl.h"
#include "os.h"
+#include "scoped_thread_state_change.h"
#include "thread-inl.h"
#include "utils.h"
@@ -267,42 +269,42 @@
}
// Case: We have a DEX file, but no OAT file for it.
-// Expect: The oat file status is kOutOfDate.
+// Expect: The status is kDex2OatNeeded.
TEST_F(OatFileAssistantTest, DexNoOat) {
std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
Copy(GetDexSrc1(), dex_location);
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_FALSE(oat_file_assistant.OdexFileExists());
EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OdexFileStatus());
EXPECT_FALSE(oat_file_assistant.OatFileExists());
EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
- EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.OatFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OatFileStatus());
}
// Case: We have no DEX file and no OAT file.
-// Expect: Status is out of date. Loading should fail, but not crash.
+// Expect: Status is kDex2OatNeeded. Loading should fail, but not crash.
TEST_F(OatFileAssistantTest, NoDexNoOat) {
std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
- EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
EXPECT_EQ(nullptr, oat_file.get());
}
// Case: We have a DEX file and up-to-date OAT file for it.
-// Expect: The oat file status is kUpToDate.
+// Expect: The status is kNoDexOptNeeded.
TEST_F(OatFileAssistantTest, OatUpToDate) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
Copy(GetDexSrc1(), dex_location);
@@ -310,7 +312,7 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_FALSE(oat_file_assistant.OdexFileExists());
EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
@@ -319,18 +321,20 @@
EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
- EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.OatFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
}
// Case: We have a MultiDEX file and up-to-date OAT file for it.
-// Expect: The oat file status is kUpToDate.
+// Expect: The status is kNoDexOptNeeded and we load all dex files.
TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) {
std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar";
Copy(GetMultiDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str());
- // Verify we can load both dex files.
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+
+ // Verify we can load both dex files.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
EXPECT_TRUE(oat_file->IsExecutable());
@@ -341,7 +345,7 @@
// Case: We have a MultiDEX file and up-to-date OAT file for it with relative
// encoded dex locations.
-// Expect: The oat file status is kUpToDate.
+// Expect: The oat file status is kNoDexOptNeeded.
TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) {
std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar";
std::string oat_location = GetOdexDir() + "/RelativeEncodedDexLocation.oat";
@@ -370,8 +374,8 @@
EXPECT_EQ(2u, dex_files.size());
}
-// Case: We have a DEX file and out of date OAT file.
-// Expect: The oat file status is kOutOfDate.
+// Case: We have a DEX file and out-of-date OAT file.
+// Expect: The status is kDex2OatNeeded.
TEST_F(OatFileAssistantTest, OatOutOfDate) {
std::string dex_location = GetScratchDir() + "/OatOutOfDate.jar";
@@ -382,7 +386,7 @@
Copy(GetDexSrc2(), dex_location);
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_FALSE(oat_file_assistant.OdexFileExists());
@@ -394,7 +398,7 @@
}
// Case: We have a DEX file and an ODEX file, but no OAT file.
-// Expect: The oat file status is kNeedsRelocation.
+// Expect: The status is kPatchOatNeeded.
TEST_F(OatFileAssistantTest, DexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
@@ -406,21 +410,20 @@
// Verify the status.
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_TRUE(oat_file_assistant.OdexFileExists());
EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
- EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.OdexFileNeedsRelocation());
EXPECT_FALSE(oat_file_assistant.OatFileExists());
EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
}
// Case: We have a stripped DEX file and an ODEX file, but no OAT file.
-// Expect: The oat file status is kNeedsRelocation.
+// Expect: The status is kPatchOatNeeded
TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/StrippedDexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/StrippedDexOdexNoOat.odex";
@@ -435,7 +438,7 @@
// Verify the status.
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
- EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -449,7 +452,7 @@
std::string error_msg;
ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
- EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -468,8 +471,8 @@
EXPECT_EQ(1u, dex_files.size());
}
-// Case: We have a stripped DEX file, an ODEX file, and an out of date OAT file.
-// Expect: The oat file status is kNeedsRelocation.
+// Case: We have a stripped DEX file, an ODEX file, and an out-of-date OAT file.
+// Expect: The status is kPatchOatNeeded.
TEST_F(OatFileAssistantTest, StrippedDexOdexOat) {
std::string dex_location = GetScratchDir() + "/StrippedDexOdexOat.jar";
std::string odex_location = GetOdexDir() + "/StrippedDexOdexOat.odex";
@@ -488,7 +491,7 @@
// Verify the status.
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
- EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -503,7 +506,7 @@
std::string error_msg;
ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
- EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -524,9 +527,59 @@
EXPECT_EQ(1u, dex_files.size());
}
+// Case: We have a DEX file, no ODEX file and an OAT file that needs
+// relocation.
+// Expect: The status is kSelfPatchOatNeeded.
+TEST_F(OatFileAssistantTest, SelfRelocation) {
+ std::string dex_location = GetScratchDir() + "/SelfRelocation.jar";
+ std::string oat_location = GetOdexDir() + "/SelfRelocation.oat";
+
+ // Create the dex and odex files
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, oat_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ oat_location.c_str(), kRuntimeISA, true);
+
+ EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+ EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OatFileNeedsRelocation());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+
+ // Make the oat file up to date.
+ std::string error_msg;
+ ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+ EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.OatFileExists());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_TRUE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(1u, dex_files.size());
+}
+
// Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and
// OAT files both have patch delta of 0.
-// Expect: It shouldn't crash.
+// Expect: It shouldn't crash, and status is kPatchOatNeeded.
TEST_F(OatFileAssistantTest, OdexOatOverlap) {
std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
@@ -544,7 +597,7 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(),
oat_location.c_str(), kRuntimeISA, true);
- EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -564,7 +617,7 @@
}
// Case: We have a DEX file and a PIC ODEX file, but no OAT file.
-// Expect: The oat file status is kUpToDate, because PIC needs no relocation.
+// Expect: The status is kNoDexOptNeeded, because PIC needs no relocation.
TEST_F(OatFileAssistantTest, DexPicOdexNoOat) {
std::string dex_location = GetScratchDir() + "/DexPicOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/DexPicOdexNoOat.odex";
@@ -576,7 +629,7 @@
// Verify the status.
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_TRUE(oat_file_assistant.OdexFileExists());
@@ -661,7 +714,7 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.OdexFileExists());
EXPECT_FALSE(oat_file_assistant.OatFileExists());
EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
@@ -720,7 +773,7 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.OdexFileExists());
EXPECT_FALSE(oat_file_assistant.OatFileExists());
EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
@@ -737,7 +790,7 @@
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.OdexFileExists());
EXPECT_FALSE(oat_file_assistant.OatFileExists());
EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
@@ -751,14 +804,14 @@
}
// Case: Non-standard extension for dex file.
-// Expect: The oat file status is kOutOfDate.
+// Expect: The status is kDex2OatNeeded.
TEST_F(OatFileAssistantTest, LongDexExtension) {
std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
Copy(GetDexSrc1(), dex_location);
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_FALSE(oat_file_assistant.OdexFileExists());
@@ -787,7 +840,8 @@
std::vector<std::string> error_msgs;
dex_files = linker->OpenDexFilesFromOat(dex_location_.c_str(), oat_location_.c_str(), &error_msgs);
CHECK(!dex_files.empty()) << Join(error_msgs, '\n');
- loaded_oat_file_ = dex_files[0]->GetOatFile();
+ CHECK(dex_files[0]->GetOatDexFile() != nullptr) << dex_files[0]->GetLocation();
+ loaded_oat_file_ = dex_files[0]->GetOatDexFile()->GetOatFile();
}
const OatFile* GetLoadedOatFile() const {
@@ -894,6 +948,41 @@
"/foo/bar/baz_noext", kArm, &odex_file, &error_msg));
}
+// Verify the dexopt status values from dalvik.system.DexFile
+// match the OatFileAssistant::DexOptStatus values.
+TEST_F(OatFileAssistantTest, DexOptStatusValues) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ Handle<mirror::Class> dexfile(
+ hs.NewHandle(linker->FindSystemClass(soa.Self(), "Ldalvik/system/DexFile;")));
+ ASSERT_FALSE(dexfile.Get() == nullptr);
+ linker->EnsureInitialized(soa.Self(), dexfile, true, true);
+
+ mirror::ArtField* no_dexopt_needed = mirror::Class::FindStaticField(
+ soa.Self(), dexfile, "NO_DEXOPT_NEEDED", "I");
+ ASSERT_FALSE(no_dexopt_needed == nullptr);
+ EXPECT_EQ(no_dexopt_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, no_dexopt_needed->GetInt(dexfile.Get()));
+
+ mirror::ArtField* dex2oat_needed = mirror::Class::FindStaticField(
+ soa.Self(), dexfile, "DEX2OAT_NEEDED", "I");
+ ASSERT_FALSE(dex2oat_needed == nullptr);
+ EXPECT_EQ(dex2oat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, dex2oat_needed->GetInt(dexfile.Get()));
+
+ mirror::ArtField* patchoat_needed = mirror::Class::FindStaticField(
+ soa.Self(), dexfile, "PATCHOAT_NEEDED", "I");
+ ASSERT_FALSE(patchoat_needed == nullptr);
+ EXPECT_EQ(patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, patchoat_needed->GetInt(dexfile.Get()));
+
+ mirror::ArtField* self_patchoat_needed = mirror::Class::FindStaticField(
+ soa.Self(), dexfile, "SELF_PATCHOAT_NEEDED", "I");
+ ASSERT_FALSE(self_patchoat_needed == nullptr);
+ EXPECT_EQ(self_patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, self_patchoat_needed->GetInt(dexfile.Get()));
+}
// TODO: More Tests:
// * Test class linker falls back to unquickened dex for DexNoOat
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 89779bc..c23f744 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -413,7 +413,6 @@
}
UNREACHABLE();
- return false;
}
using M = RuntimeArgumentMap;
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index 8cccec8..7ee4118 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -38,7 +38,7 @@
QuickExceptionHandler(Thread* self, bool is_deoptimization)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ~QuickExceptionHandler() {
+ NO_RETURN ~QuickExceptionHandler() {
LOG(FATAL) << "UNREACHABLE"; // Expected to take long jump.
UNREACHABLE();
}
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index c74fded..5631ff4 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -111,6 +111,48 @@
}
}
+// TODO: Reduce copy paste
+template <typename MirrorType, ReadBarrierOption kReadBarrierOption, bool kMaybeDuringStartup>
+inline MirrorType* ReadBarrier::BarrierForRoot(mirror::CompressedReference<MirrorType>* root) {
+ MirrorType* ref = root->AsMirrorPtr();
+ const bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
+ if (with_read_barrier && kUseBakerReadBarrier) {
+ if (kMaybeDuringStartup && IsDuringStartup()) {
+ // During startup, the heap may not be initialized yet. Just
+ // return the given ref.
+ return ref;
+ }
+ // TODO: separate the read barrier code from the collector code more.
+ if (Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->IsMarking()) {
+ ref = reinterpret_cast<MirrorType*>(Mark(ref));
+ }
+ AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
+ return ref;
+ } else if (with_read_barrier && kUseBrooksReadBarrier) {
+ // To be implemented.
+ return ref;
+ } else if (with_read_barrier && kUseTableLookupReadBarrier) {
+ if (kMaybeDuringStartup && IsDuringStartup()) {
+ // During startup, the heap may not be initialized yet. Just
+ // return the given ref.
+ return ref;
+ }
+ if (Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
+ auto old_ref = mirror::CompressedReference<MirrorType>::FromMirrorPtr(ref);
+ ref = reinterpret_cast<MirrorType*>(Mark(ref));
+ auto new_ref = mirror::CompressedReference<MirrorType>::FromMirrorPtr(ref);
+ // Update the field atomically. This may fail if mutator updates before us, but it's ok.
+ auto* atomic_root =
+ reinterpret_cast<Atomic<mirror::CompressedReference<MirrorType>>*>(root);
+ atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, new_ref);
+ }
+ AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
+ return ref;
+ } else {
+ return ref;
+ }
+}
+
inline bool ReadBarrier::IsDuringStartup() {
gc::Heap* heap = Runtime::Current()->GetHeap();
if (heap == nullptr) {
diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h
index 474b46f..471b37c 100644
--- a/runtime/read_barrier.h
+++ b/runtime/read_barrier.h
@@ -20,6 +20,7 @@
#include "base/mutex.h"
#include "base/macros.h"
#include "jni.h"
+#include "mirror/object_reference.h"
#include "offsets.h"
#include "read_barrier_c.h"
@@ -58,6 +59,13 @@
ALWAYS_INLINE static MirrorType* BarrierForRoot(MirrorType** root)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // It's up to the implementation whether the given root gets updated
+ // whereas the return value must be an updated reference.
+ template <typename MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
+ bool kMaybeDuringStartup = false>
+ ALWAYS_INLINE static MirrorType* BarrierForRoot(mirror::CompressedReference<MirrorType>* root)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
static bool IsDuringStartup();
// Without the holder object.
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index 357d454..beba64f 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -237,9 +237,10 @@
DumpSummaryLine(os, prev, GetElementCount(prev), identical, equiv);
}
-void ReferenceTable::VisitRoots(RootCallback* visitor, void* arg, const RootInfo& root_info) {
+void ReferenceTable::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
+ BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(visitor, root_info);
for (GcRoot<mirror::Object>& root : entries_) {
- root.VisitRoot(visitor, arg, root_info);
+ buffered_visitor.VisitRoot(root);
}
}
diff --git a/runtime/reference_table.h b/runtime/reference_table.h
index 22cf1cd..94f16b6 100644
--- a/runtime/reference_table.h
+++ b/runtime/reference_table.h
@@ -49,7 +49,8 @@
void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitRoots(RootCallback* visitor, void* arg, const RootInfo& root_info);
+ void VisitRoots(RootVisitor* visitor, const RootInfo& root_info)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
typedef std::vector<GcRoot<mirror::Object>,
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 7998f16..1cd0a96 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1291,67 +1291,67 @@
return ncdfe;
}
-void Runtime::VisitConstantRoots(RootCallback* callback, void* arg) {
+void Runtime::VisitConstantRoots(RootVisitor* visitor) {
// Visit the classes held as static in mirror classes, these can be visited concurrently and only
// need to be visited once per GC since they never change.
- mirror::ArtField::VisitRoots(callback, arg);
- mirror::ArtMethod::VisitRoots(callback, arg);
- mirror::Class::VisitRoots(callback, arg);
- mirror::Reference::VisitRoots(callback, arg);
- mirror::StackTraceElement::VisitRoots(callback, arg);
- mirror::String::VisitRoots(callback, arg);
- mirror::Throwable::VisitRoots(callback, arg);
- mirror::Field::VisitRoots(callback, arg);
+ mirror::ArtField::VisitRoots(visitor);
+ mirror::ArtMethod::VisitRoots(visitor);
+ mirror::Class::VisitRoots(visitor);
+ mirror::Reference::VisitRoots(visitor);
+ mirror::StackTraceElement::VisitRoots(visitor);
+ mirror::String::VisitRoots(visitor);
+ mirror::Throwable::VisitRoots(visitor);
+ mirror::Field::VisitRoots(visitor);
// Visit all the primitive array types classes.
- mirror::PrimitiveArray<uint8_t>::VisitRoots(callback, arg); // BooleanArray
- mirror::PrimitiveArray<int8_t>::VisitRoots(callback, arg); // ByteArray
- mirror::PrimitiveArray<uint16_t>::VisitRoots(callback, arg); // CharArray
- mirror::PrimitiveArray<double>::VisitRoots(callback, arg); // DoubleArray
- mirror::PrimitiveArray<float>::VisitRoots(callback, arg); // FloatArray
- mirror::PrimitiveArray<int32_t>::VisitRoots(callback, arg); // IntArray
- mirror::PrimitiveArray<int64_t>::VisitRoots(callback, arg); // LongArray
- mirror::PrimitiveArray<int16_t>::VisitRoots(callback, arg); // ShortArray
+ mirror::PrimitiveArray<uint8_t>::VisitRoots(visitor); // BooleanArray
+ mirror::PrimitiveArray<int8_t>::VisitRoots(visitor); // ByteArray
+ mirror::PrimitiveArray<uint16_t>::VisitRoots(visitor); // CharArray
+ mirror::PrimitiveArray<double>::VisitRoots(visitor); // DoubleArray
+ mirror::PrimitiveArray<float>::VisitRoots(visitor); // FloatArray
+ mirror::PrimitiveArray<int32_t>::VisitRoots(visitor); // IntArray
+ mirror::PrimitiveArray<int64_t>::VisitRoots(visitor); // LongArray
+ mirror::PrimitiveArray<int16_t>::VisitRoots(visitor); // ShortArray
}
-void Runtime::VisitConcurrentRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
- intern_table_->VisitRoots(callback, arg, flags);
- class_linker_->VisitRoots(callback, arg, flags);
+void Runtime::VisitConcurrentRoots(RootVisitor* visitor, VisitRootFlags flags) {
+ intern_table_->VisitRoots(visitor, flags);
+ class_linker_->VisitRoots(visitor, flags);
if ((flags & kVisitRootFlagNewRoots) == 0) {
// Guaranteed to have no new roots in the constant roots.
- VisitConstantRoots(callback, arg);
+ VisitConstantRoots(visitor);
}
}
-void Runtime::VisitTransactionRoots(RootCallback* callback, void* arg) {
+void Runtime::VisitTransactionRoots(RootVisitor* visitor) {
if (preinitialization_transaction_ != nullptr) {
- preinitialization_transaction_->VisitRoots(callback, arg);
+ preinitialization_transaction_->VisitRoots(visitor);
}
}
-void Runtime::VisitNonThreadRoots(RootCallback* callback, void* arg) {
- java_vm_->VisitRoots(callback, arg);
- sentinel_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
- pre_allocated_OutOfMemoryError_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
- resolution_method_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
- pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
- imt_conflict_method_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
- imt_unimplemented_method_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
- default_imt_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+void Runtime::VisitNonThreadRoots(RootVisitor* visitor) {
+ java_vm_->VisitRoots(visitor);
+ sentinel_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
+ pre_allocated_OutOfMemoryError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
+ resolution_method_.VisitRoot(visitor, RootInfo(kRootVMInternal));
+ pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
+ imt_conflict_method_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
+ imt_unimplemented_method_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
+ default_imt_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
- callee_save_methods_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+ callee_save_methods_[i].VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
}
- verifier::MethodVerifier::VisitStaticRoots(callback, arg);
- VisitTransactionRoots(callback, arg);
- instrumentation_.VisitRoots(callback, arg);
+ verifier::MethodVerifier::VisitStaticRoots(visitor);
+ VisitTransactionRoots(visitor);
+ instrumentation_.VisitRoots(visitor);
}
-void Runtime::VisitNonConcurrentRoots(RootCallback* callback, void* arg) {
- thread_list_->VisitRoots(callback, arg);
- VisitNonThreadRoots(callback, arg);
+void Runtime::VisitNonConcurrentRoots(RootVisitor* visitor) {
+ thread_list_->VisitRoots(visitor);
+ VisitNonThreadRoots(visitor);
}
-void Runtime::VisitThreadRoots(RootCallback* callback, void* arg) {
- thread_list_->VisitRoots(callback, arg);
+void Runtime::VisitThreadRoots(RootVisitor* visitor) {
+ thread_list_->VisitRoots(visitor);
}
size_t Runtime::FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback,
@@ -1359,12 +1359,12 @@
return thread_list_->FlipThreadRoots(thread_flip_visitor, flip_callback, collector);
}
-void Runtime::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
- VisitNonConcurrentRoots(callback, arg);
- VisitConcurrentRoots(callback, arg, flags);
+void Runtime::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) {
+ VisitNonConcurrentRoots(visitor);
+ VisitConcurrentRoots(visitor, flags);
}
-void Runtime::VisitImageRoots(RootCallback* callback, void* arg) {
+void Runtime::VisitImageRoots(RootVisitor* visitor) {
for (auto* space : GetHeap()->GetContinuousSpaces()) {
if (space->IsImageSpace()) {
auto* image_space = space->AsImageSpace();
@@ -1373,7 +1373,7 @@
auto* obj = image_header.GetImageRoot(static_cast<ImageHeader::ImageRoot>(i));
if (obj != nullptr) {
auto* after_obj = obj;
- callback(&after_obj, arg, RootInfo(kRootStickyClass));
+ visitor->VisitRoot(&after_obj, RootInfo(kRootStickyClass));
CHECK_EQ(after_obj, obj);
}
}
@@ -1542,21 +1542,20 @@
}
}
-void Runtime::AbortTransactionAndThrowInternalError(Thread* self,
- const std::string& abort_message) {
+void Runtime::AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message) {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
// Throwing an exception may cause its class initialization. If we mark the transaction
// aborted before that, we may warn with a false alarm. Throwing the exception before
// marking the transaction aborted avoids that.
- preinitialization_transaction_->ThrowInternalError(self, false);
+ preinitialization_transaction_->ThrowAbortError(self, false);
preinitialization_transaction_->Abort(abort_message);
}
-void Runtime::ThrowInternalErrorForAbortedTransaction(Thread* self) {
+void Runtime::ThrowTransactionAbortError(Thread* self) {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
- preinitialization_transaction_->ThrowInternalError(self, true);
+ preinitialization_transaction_->ThrowAbortError(self, true);
}
void Runtime::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset,
@@ -1672,6 +1671,10 @@
void Runtime::CreateJit() {
CHECK(!IsAotCompiler());
+ if (GetInstrumentation()->IsForcedInterpretOnly()) {
+ // Don't create JIT if forced interpret only.
+ return;
+ }
std::string error_msg;
jit_.reset(jit::Jit::Create(jit_options_.get(), &error_msg));
if (jit_.get() != nullptr) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 68458ae..baa4d18 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -296,27 +296,27 @@
// Visit all the roots. If only_dirty is true then non-dirty roots won't be visited. If
// clean_dirty is true then dirty roots will be marked as non-dirty after visiting.
- void VisitRoots(RootCallback* visitor, void* arg, VisitRootFlags flags = kVisitRootFlagAllRoots)
+ void VisitRoots(RootVisitor* visitor, VisitRootFlags flags = kVisitRootFlagAllRoots)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Visit image roots, only used for hprof since the GC uses the image space mod union table
// instead.
- void VisitImageRoots(RootCallback* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VisitImageRoots(RootVisitor* visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Visit all of the roots we can do safely do concurrently.
- void VisitConcurrentRoots(RootCallback* visitor, void* arg,
+ void VisitConcurrentRoots(RootVisitor* visitor,
VisitRootFlags flags = kVisitRootFlagAllRoots)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Visit all of the non thread roots, we can do this with mutators unpaused.
- void VisitNonThreadRoots(RootCallback* visitor, void* arg)
+ void VisitNonThreadRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitTransactionRoots(RootCallback* visitor, void* arg)
+ void VisitTransactionRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Visit all of the thread roots.
- void VisitThreadRoots(RootCallback* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VisitThreadRoots(RootVisitor* visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Flip thread roots from from-space refs to to-space refs.
size_t FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback,
@@ -324,7 +324,7 @@
LOCKS_EXCLUDED(Locks::mutator_lock_);
// Visit all other roots which must be done with mutators suspended.
- void VisitNonConcurrentRoots(RootCallback* visitor, void* arg)
+ void VisitNonConcurrentRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Sweep system weaks, the system weak is deleted if the visitor return nullptr. Otherwise, the
@@ -334,7 +334,7 @@
// Constant roots are the roots which never change after the runtime is initialized, they only
// need to be visited once per GC cycle.
- void VisitConstantRoots(RootCallback* callback, void* arg)
+ void VisitConstantRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Returns a special method that calls into a trampoline for runtime method resolution
@@ -469,9 +469,9 @@
void ExitTransactionMode();
bool IsTransactionAborted() const;
- void AbortTransactionAndThrowInternalError(Thread* self, const std::string& abort_message)
+ void AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void ThrowInternalErrorForAbortedTransaction(Thread* self)
+ void ThrowTransactionAbortError(Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, uint8_t value,
diff --git a/runtime/safe_map.h b/runtime/safe_map.h
index f9d81dc..402c7e9 100644
--- a/runtime/safe_map.h
+++ b/runtime/safe_map.h
@@ -44,6 +44,7 @@
typedef typename ::std::map<K, V, Comparator, Allocator>::value_type value_type;
SafeMap() = default;
+ SafeMap(const SafeMap&) = default;
explicit SafeMap(const key_compare& cmp, const allocator_type& allocator = allocator_type())
: map_(cmp, allocator) {
}
diff --git a/runtime/stack.h b/runtime/stack.h
index aab54ba..fbb0aa4 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -59,19 +59,7 @@
// A reference from the shadow stack to a MirrorType object within the Java heap.
template<class MirrorType>
-class MANAGED StackReference : public mirror::ObjectReference<false, MirrorType> {
- public:
- StackReference<MirrorType>() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : mirror::ObjectReference<false, MirrorType>(nullptr) {}
-
- static StackReference<MirrorType> FromMirrorPtr(MirrorType* p)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return StackReference<MirrorType>(p);
- }
-
- private:
- StackReference<MirrorType>(MirrorType* p) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : mirror::ObjectReference<false, MirrorType>(p) {}
+class MANAGED StackReference : public mirror::CompressedReference<MirrorType> {
};
// ShadowFrame has 2 possible layouts:
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 9fee779..d1b0464 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1068,7 +1068,12 @@
// If we're currently in native code, dump that stack before dumping the managed stack.
if (dump_for_abort || ShouldShowNativeStack(this)) {
DumpKernelStack(os, GetTid(), " kernel: ", false);
+ // b/20040863. Temporary workaround for x86 libunwind issue.
+#if defined(__i386__) && defined(HAVE_ANDROID_OS)
+ os << "Cannot dump native stack. b/20040863.\n";
+#else
DumpNativeStack(os, GetTid(), " native: ", GetCurrentMethod(nullptr, !dump_for_abort));
+#endif
}
DumpJavaStack(os);
} else {
@@ -1191,26 +1196,37 @@
}
}
-static void MonitorExitVisitor(mirror::Object** object, void* arg, const RootInfo& /*root_info*/)
- NO_THREAD_SAFETY_ANALYSIS {
- Thread* self = reinterpret_cast<Thread*>(arg);
- mirror::Object* entered_monitor = *object;
- if (self->HoldsLock(entered_monitor)) {
- LOG(WARNING) << "Calling MonitorExit on object "
- << object << " (" << PrettyTypeOf(entered_monitor) << ")"
- << " left locked by native thread "
- << *Thread::Current() << " which is detaching";
- entered_monitor->MonitorExit(self);
+class MonitorExitVisitor : public SingleRootVisitor {
+ public:
+ explicit MonitorExitVisitor(Thread* self) : self_(self) { }
+
+ // NO_THREAD_SAFETY_ANALYSIS due to MonitorExit.
+ void VisitRoot(mirror::Object* entered_monitor, const RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE NO_THREAD_SAFETY_ANALYSIS {
+ if (self_->HoldsLock(entered_monitor)) {
+ LOG(WARNING) << "Calling MonitorExit on object "
+ << entered_monitor << " (" << PrettyTypeOf(entered_monitor) << ")"
+ << " left locked by native thread "
+ << *Thread::Current() << " which is detaching";
+ entered_monitor->MonitorExit(self_);
+ }
}
-}
+
+ private:
+ Thread* const self_;
+};
void Thread::Destroy() {
Thread* self = this;
DCHECK_EQ(self, Thread::Current());
if (tlsPtr_.jni_env != nullptr) {
- // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
- tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, RootInfo(kRootVMInternal));
+ {
+ ScopedObjectAccess soa(self);
+ MonitorExitVisitor visitor(self);
+ // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
+ tlsPtr_.jni_env->monitors.VisitRoots(&visitor, RootInfo(kRootVMInternal));
+ }
// Release locally held global references which releasing may require the mutator lock.
if (tlsPtr_.jpeer != nullptr) {
// If pthread_create fails we don't have a jni env here.
@@ -1368,18 +1384,12 @@
return tlsPtr_.managed_stack.ShadowFramesContain(hs_entry);
}
-void Thread::HandleScopeVisitRoots(RootCallback* visitor, void* arg, uint32_t thread_id) {
+void Thread::HandleScopeVisitRoots(RootVisitor* visitor, uint32_t thread_id) {
+ BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
+ visitor, RootInfo(kRootNativeStack, thread_id));
for (HandleScope* cur = tlsPtr_.top_handle_scope; cur; cur = cur->GetLink()) {
- size_t num_refs = cur->NumberOfReferences();
- for (size_t j = 0; j < num_refs; ++j) {
- mirror::Object* object = cur->GetReference(j);
- if (object != nullptr) {
- mirror::Object* old_obj = object;
- visitor(&object, arg, RootInfo(kRootNativeStack, thread_id));
- if (old_obj != object) {
- cur->SetReference(j, object);
- }
- }
+ for (size_t j = 0, count = cur->NumberOfReferences(); j < count; ++j) {
+ buffered_visitor.VisitRootIfNonNull(cur->GetHandle(j).GetReference());
}
}
}
@@ -2079,7 +2089,7 @@
template <typename RootVisitor>
class ReferenceMapVisitor : public StackVisitor {
public:
- ReferenceMapVisitor(Thread* thread, Context* context, const RootVisitor& visitor)
+ ReferenceMapVisitor(Thread* thread, Context* context, RootVisitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: StackVisitor(thread, context), visitor_(visitor) {}
@@ -2243,55 +2253,50 @@
}
// Visitor for when we visit a root.
- const RootVisitor& visitor_;
+ RootVisitor& visitor_;
};
class RootCallbackVisitor {
public:
- RootCallbackVisitor(RootCallback* callback, void* arg, uint32_t tid)
- : callback_(callback), arg_(arg), tid_(tid) {}
+ RootCallbackVisitor(RootVisitor* visitor, uint32_t tid) : visitor_(visitor), tid_(tid) {}
- void operator()(mirror::Object** obj, size_t vreg, const StackVisitor* stack_visitor) const {
- callback_(obj, arg_, JavaFrameRootInfo(tid_, stack_visitor, vreg));
+ void operator()(mirror::Object** obj, size_t vreg, const StackVisitor* stack_visitor) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ visitor_->VisitRoot(obj, JavaFrameRootInfo(tid_, stack_visitor, vreg));
}
private:
- RootCallback* const callback_;
- void* const arg_;
+ RootVisitor* const visitor_;
const uint32_t tid_;
};
-void Thread::VisitRoots(RootCallback* visitor, void* arg) {
- uint32_t thread_id = GetThreadId();
- if (tlsPtr_.opeer != nullptr) {
- visitor(&tlsPtr_.opeer, arg, RootInfo(kRootThreadObject, thread_id));
- }
+void Thread::VisitRoots(RootVisitor* visitor) {
+ const uint32_t thread_id = GetThreadId();
+ visitor->VisitRootIfNonNull(&tlsPtr_.opeer, RootInfo(kRootThreadObject, thread_id));
if (tlsPtr_.exception != nullptr && tlsPtr_.exception != GetDeoptimizationException()) {
- visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg,
- RootInfo(kRootNativeStack, thread_id));
+ visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception),
+ RootInfo(kRootNativeStack, thread_id));
}
- if (tlsPtr_.monitor_enter_object != nullptr) {
- visitor(&tlsPtr_.monitor_enter_object, arg, RootInfo(kRootNativeStack, thread_id));
- }
- tlsPtr_.jni_env->locals.VisitRoots(visitor, arg, RootInfo(kRootJNILocal, thread_id));
- tlsPtr_.jni_env->monitors.VisitRoots(visitor, arg, RootInfo(kRootJNIMonitor, thread_id));
- HandleScopeVisitRoots(visitor, arg, thread_id);
+ visitor->VisitRootIfNonNull(&tlsPtr_.monitor_enter_object, RootInfo(kRootNativeStack, thread_id));
+ tlsPtr_.jni_env->locals.VisitRoots(visitor, RootInfo(kRootJNILocal, thread_id));
+ tlsPtr_.jni_env->monitors.VisitRoots(visitor, RootInfo(kRootJNIMonitor, thread_id));
+ HandleScopeVisitRoots(visitor, thread_id);
if (tlsPtr_.debug_invoke_req != nullptr) {
- tlsPtr_.debug_invoke_req->VisitRoots(visitor, arg, RootInfo(kRootDebugger, thread_id));
+ tlsPtr_.debug_invoke_req->VisitRoots(visitor, RootInfo(kRootDebugger, thread_id));
}
if (tlsPtr_.single_step_control != nullptr) {
- tlsPtr_.single_step_control->VisitRoots(visitor, arg, RootInfo(kRootDebugger, thread_id));
+ tlsPtr_.single_step_control->VisitRoots(visitor, RootInfo(kRootDebugger, thread_id));
}
if (tlsPtr_.deoptimization_shadow_frame != nullptr) {
- RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
- ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitorToCallback);
+ RootCallbackVisitor visitor_to_callback(visitor, thread_id);
+ ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitor_to_callback);
for (ShadowFrame* shadow_frame = tlsPtr_.deoptimization_shadow_frame; shadow_frame != nullptr;
shadow_frame = shadow_frame->GetLink()) {
mapper.VisitShadowFrame(shadow_frame);
}
}
if (tlsPtr_.shadow_frame_under_construction != nullptr) {
- RootCallbackVisitor visitor_to_callback(visitor, arg, thread_id);
+ RootCallbackVisitor visitor_to_callback(visitor, thread_id);
ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitor_to_callback);
for (ShadowFrame* shadow_frame = tlsPtr_.shadow_frame_under_construction;
shadow_frame != nullptr;
@@ -2300,33 +2305,34 @@
}
}
if (tlsPtr_.method_verifier != nullptr) {
- tlsPtr_.method_verifier->VisitRoots(visitor, arg, RootInfo(kRootNativeStack, thread_id));
+ tlsPtr_.method_verifier->VisitRoots(visitor, RootInfo(kRootNativeStack, thread_id));
}
// Visit roots on this thread's stack
Context* context = GetLongJumpContext();
- RootCallbackVisitor visitor_to_callback(visitor, arg, thread_id);
+ RootCallbackVisitor visitor_to_callback(visitor, thread_id);
ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context, visitor_to_callback);
mapper.WalkStack();
ReleaseLongJumpContext(context);
for (instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) {
- if (frame.this_object_ != nullptr) {
- visitor(&frame.this_object_, arg, RootInfo(kRootVMInternal, thread_id));
- }
- DCHECK(frame.method_ != nullptr);
- visitor(reinterpret_cast<mirror::Object**>(&frame.method_), arg,
- RootInfo(kRootVMInternal, thread_id));
+ visitor->VisitRootIfNonNull(&frame.this_object_, RootInfo(kRootVMInternal, thread_id));
+ visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&frame.method_),
+ RootInfo(kRootVMInternal, thread_id));
}
}
-static void VerifyRoot(mirror::Object** root, void* /*arg*/, const RootInfo& /*root_info*/)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- VerifyObject(*root);
-}
+class VerifyRootVisitor : public SingleRootVisitor {
+ public:
+ void VisitRoot(mirror::Object* root, const RootInfo& info ATTRIBUTE_UNUSED)
+ OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ VerifyObject(root);
+ }
+};
void Thread::VerifyStackImpl() {
+ VerifyRootVisitor visitor;
std::unique_ptr<Context> context(Context::Create());
- RootCallbackVisitor visitorToCallback(VerifyRoot, Runtime::Current()->GetHeap(), GetThreadId());
- ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context.get(), visitorToCallback);
+ RootCallbackVisitor visitor_to_callback(&visitor, GetThreadId());
+ ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context.get(), visitor_to_callback);
mapper.WalkStack();
}
diff --git a/runtime/thread.h b/runtime/thread.h
index 9d4d89d..f89e46b 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -485,7 +485,7 @@
jobjectArray output_array = nullptr, int* stack_depth = nullptr)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitRoots(RootCallback* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VisitRoots(RootVisitor* visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ALWAYS_INLINE void VerifyStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -686,7 +686,7 @@
// Is the given obj in this thread's stack indirect reference table?
bool HandleScopeContains(jobject obj) const;
- void HandleScopeVisitRoots(RootCallback* visitor, void* arg, uint32_t thread_id)
+ void HandleScopeVisitRoots(RootVisitor* visitor, uint32_t thread_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
HandleScope* GetTopHandleScope() {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 1ab0093..560bcc1 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -1156,10 +1156,10 @@
}
}
-void ThreadList::VisitRoots(RootCallback* callback, void* arg) const {
+void ThreadList::VisitRoots(RootVisitor* visitor) const {
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
for (const auto& thread : list_) {
- thread->VisitRoots(callback, arg);
+ thread->VisitRoots(visitor);
}
}
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index c18e285..fa747b8 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -136,7 +136,7 @@
LOCKS_EXCLUDED(Locks::mutator_lock_, Locks::thread_list_lock_);
void Unregister(Thread* self) LOCKS_EXCLUDED(Locks::mutator_lock_, Locks::thread_list_lock_);
- void VisitRoots(RootCallback* callback, void* arg) const
+ void VisitRoots(RootVisitor* visitor) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Return a copy of the thread list.
diff --git a/runtime/trace.cc b/runtime/trace.cc
index ea0a642..5375dc0 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -243,8 +243,7 @@
the_trace->CompareAndUpdateStackTrace(thread, stack_trace);
}
-static void ClearThreadStackTraceAndClockBase(Thread* thread ATTRIBUTE_UNUSED,
- void* arg ATTRIBUTE_UNUSED) {
+static void ClearThreadStackTraceAndClockBase(Thread* thread, void* arg ATTRIBUTE_UNUSED) {
thread->SetTraceClockBase(0);
std::vector<mirror::ArtMethod*>* stack_trace = thread->GetStackTraceSample();
thread->SetStackTraceSample(NULL);
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 186cfea..cc0f15f 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -60,8 +60,8 @@
void Transaction::Abort(const std::string& abort_message) {
MutexLock mu(Thread::Current(), log_lock_);
- // We may abort more than once if the java.lang.InternalError thrown at the
- // time of the abort has been caught during execution of a class initializer.
+ // We may abort more than once if the exception thrown at the time of the
+ // previous abort has been caught during execution of a class initializer.
// We just keep the message of the first abort because it will cause the
// transaction to be rolled back anyway.
if (!aborted_) {
@@ -70,16 +70,13 @@
}
}
-void Transaction::ThrowInternalError(Thread* self, bool rethrow) {
+void Transaction::ThrowAbortError(Thread* self, bool rethrow) {
if (kIsDebugBuild && rethrow) {
- CHECK(IsAborted()) << "Rethrow InternalError while transaction is not aborted";
+ CHECK(IsAborted()) << "Rethrow " << Transaction::kAbortExceptionDescriptor
+ << " while transaction is not aborted";
}
std::string abort_msg(GetAbortMessage());
- // Temporary workaround for b/20019689.
- if (self->IsExceptionPending()) {
- self->ClearException();
- }
- self->ThrowNewException("Ljava/lang/InternalError;", abort_msg.c_str());
+ self->ThrowNewWrappedException(Transaction::kAbortExceptionSignature, abort_msg.c_str());
}
bool Transaction::IsAborted() {
@@ -224,24 +221,24 @@
intern_string_logs_.clear();
}
-void Transaction::VisitRoots(RootCallback* callback, void* arg) {
+void Transaction::VisitRoots(RootVisitor* visitor) {
MutexLock mu(Thread::Current(), log_lock_);
- VisitObjectLogs(callback, arg);
- VisitArrayLogs(callback, arg);
- VisitStringLogs(callback, arg);
+ VisitObjectLogs(visitor);
+ VisitArrayLogs(visitor);
+ VisitStringLogs(visitor);
}
-void Transaction::VisitObjectLogs(RootCallback* callback, void* arg) {
+void Transaction::VisitObjectLogs(RootVisitor* visitor) {
// List of moving roots.
typedef std::pair<mirror::Object*, mirror::Object*> ObjectPair;
std::list<ObjectPair> moving_roots;
// Visit roots.
for (auto it : object_logs_) {
- it.second.VisitRoots(callback, arg);
+ it.second.VisitRoots(visitor);
mirror::Object* old_root = it.first;
mirror::Object* new_root = old_root;
- callback(&new_root, arg, RootInfo(kRootUnknown));
+ visitor->VisitRoot(&new_root, RootInfo(kRootUnknown));
if (new_root != old_root) {
moving_roots.push_back(std::make_pair(old_root, new_root));
}
@@ -259,7 +256,7 @@
}
}
-void Transaction::VisitArrayLogs(RootCallback* callback, void* arg) {
+void Transaction::VisitArrayLogs(RootVisitor* visitor) {
// List of moving roots.
typedef std::pair<mirror::Array*, mirror::Array*> ArrayPair;
std::list<ArrayPair> moving_roots;
@@ -268,7 +265,7 @@
mirror::Array* old_root = it.first;
CHECK(!old_root->IsObjectArray());
mirror::Array* new_root = old_root;
- callback(reinterpret_cast<mirror::Object**>(&new_root), arg, RootInfo(kRootUnknown));
+ visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&new_root), RootInfo(kRootUnknown));
if (new_root != old_root) {
moving_roots.push_back(std::make_pair(old_root, new_root));
}
@@ -286,9 +283,9 @@
}
}
-void Transaction::VisitStringLogs(RootCallback* callback, void* arg) {
+void Transaction::VisitStringLogs(RootVisitor* visitor) {
for (InternStringLog& log : intern_string_logs_) {
- log.VisitRoots(callback, arg);
+ log.VisitRoots(visitor);
}
}
@@ -424,16 +421,12 @@
}
}
-void Transaction::ObjectLog::VisitRoots(RootCallback* callback, void* arg) {
+void Transaction::ObjectLog::VisitRoots(RootVisitor* visitor) {
for (auto it : field_values_) {
FieldValue& field_value = it.second;
if (field_value.kind == ObjectLog::kReference) {
- mirror::Object* obj =
- reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value));
- if (obj != nullptr) {
- callback(&obj, arg, RootInfo(kRootUnknown));
- field_value.value = reinterpret_cast<uintptr_t>(obj);
- }
+ visitor->VisitRootIfNonNull(reinterpret_cast<mirror::Object**>(&field_value.value),
+ RootInfo(kRootUnknown));
}
}
}
@@ -475,8 +468,8 @@
}
}
-void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) {
- callback(reinterpret_cast<mirror::Object**>(&str_), arg, RootInfo(kRootInternedString));
+void Transaction::InternStringLog::VisitRoots(RootVisitor* visitor) {
+ visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&str_), RootInfo(kRootInternedString));
}
void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
diff --git a/runtime/transaction.h b/runtime/transaction.h
index e1b93c9..4d85662 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -39,13 +39,16 @@
class Transaction FINAL {
public:
+ static constexpr const char* kAbortExceptionDescriptor = "dalvik.system.TransactionAbortError";
+ static constexpr const char* kAbortExceptionSignature = "Ldalvik/system/TransactionAbortError;";
+
Transaction();
~Transaction();
void Abort(const std::string& abort_message)
LOCKS_EXCLUDED(log_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void ThrowInternalError(Thread* self, bool rethrow)
+ void ThrowAbortError(Thread* self, bool rethrow)
LOCKS_EXCLUDED(log_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsAborted() LOCKS_EXCLUDED(log_lock_);
@@ -97,7 +100,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(log_lock_);
- void VisitRoots(RootCallback* callback, void* arg)
+ void VisitRoots(RootVisitor* visitor)
LOCKS_EXCLUDED(log_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -113,7 +116,7 @@
void LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile);
void Undo(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitRoots(RootCallback* callback, void* arg);
+ void VisitRoots(RootVisitor* visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
size_t Size() const {
return field_values_.size();
@@ -181,7 +184,7 @@
void Undo(InternTable* intern_table)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_);
- void VisitRoots(RootCallback* callback, void* arg);
+ void VisitRoots(RootVisitor* visitor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
mirror::String* str_;
@@ -204,13 +207,13 @@
EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitObjectLogs(RootCallback* callback, void* arg)
+ void VisitObjectLogs(RootVisitor* visitor)
EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitArrayLogs(RootCallback* callback, void* arg)
+ void VisitArrayLogs(RootVisitor* visitor)
EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitStringLogs(RootCallback* callback, void* arg)
+ void VisitStringLogs(RootVisitor* visitor)
EXCLUSIVE_LOCKS_REQUIRED(log_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index 5db51c8..24ecf6b 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -35,8 +35,9 @@
hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
ASSERT_TRUE(class_loader.Get() != nullptr);
- // Load and initialize java.lang.ExceptionInInitializerError and java.lang.InternalError
- // classes so they can be thrown during class initialization if the transaction aborts.
+ // Load and initialize java.lang.ExceptionInInitializerError and the exception class used
+ // to abort transaction so they can be thrown during class initialization if the transaction
+ // aborts.
MutableHandle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(),
"Ljava/lang/ExceptionInInitializerError;")));
@@ -44,7 +45,8 @@
class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
ASSERT_TRUE(h_klass->IsInitialized());
- h_klass.Assign(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/InternalError;"));
+ h_klass.Assign(class_linker_->FindSystemClass(soa.Self(),
+ Transaction::kAbortExceptionSignature));
ASSERT_TRUE(h_klass.Get() != nullptr);
class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
ASSERT_TRUE(h_klass->IsInitialized());
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 1d04192..d0f8468 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -1075,7 +1075,6 @@
break;
default:
return false;
- break;
}
return true;
}
@@ -4351,12 +4350,12 @@
verifier::RegTypeCache::ShutDown();
}
-void MethodVerifier::VisitStaticRoots(RootCallback* callback, void* arg) {
- RegTypeCache::VisitStaticRoots(callback, arg);
+void MethodVerifier::VisitStaticRoots(RootVisitor* visitor) {
+ RegTypeCache::VisitStaticRoots(visitor);
}
-void MethodVerifier::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
- reg_types_.VisitRoots(callback, arg, root_info);
+void MethodVerifier::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
+ reg_types_.VisitRoots(visitor, root_info);
}
} // namespace verifier
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 6b813ef..c813634 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -225,9 +225,9 @@
// Describe VRegs at the given dex pc.
std::vector<int32_t> DescribeVRegs(uint32_t dex_pc);
- static void VisitStaticRoots(RootCallback* callback, void* arg)
+ static void VisitStaticRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VisitRoots(RootCallback* callback, void* arg, const RootInfo& roots)
+ void VisitRoots(RootVisitor* visitor, const RootInfo& roots)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Accessors used by the compiler via CompilerCallback
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 97d0cbe..c8aa4fd 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -778,8 +778,8 @@
}
}
-void RegType::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) const {
- klass_.VisitRootIfNonNull(callback, arg, root_info);
+void RegType::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) const {
+ klass_.VisitRootIfNonNull(visitor, root_info);
}
void UninitializedThisReferenceType::CheckInvariants() const {
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index d260650..e4d2c3e 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -262,7 +262,7 @@
virtual ~RegType() {}
- void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) const
+ void VisitRoots(RootVisitor* visitor, const RootInfo& root_info) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
protected:
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 6e57857..b371d7e 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -557,33 +557,33 @@
}
}
-void RegTypeCache::VisitStaticRoots(RootCallback* callback, void* arg) {
+void RegTypeCache::VisitStaticRoots(RootVisitor* visitor) {
// Visit the primitive types, this is required since if there are no active verifiers they wont
// be in the entries array, and therefore not visited as roots.
if (primitive_initialized_) {
RootInfo ri(kRootUnknown);
- UndefinedType::GetInstance()->VisitRoots(callback, arg, ri);
- ConflictType::GetInstance()->VisitRoots(callback, arg, ri);
- BooleanType::GetInstance()->VisitRoots(callback, arg, ri);
- ByteType::GetInstance()->VisitRoots(callback, arg, ri);
- ShortType::GetInstance()->VisitRoots(callback, arg, ri);
- CharType::GetInstance()->VisitRoots(callback, arg, ri);
- IntegerType::GetInstance()->VisitRoots(callback, arg, ri);
- LongLoType::GetInstance()->VisitRoots(callback, arg, ri);
- LongHiType::GetInstance()->VisitRoots(callback, arg, ri);
- FloatType::GetInstance()->VisitRoots(callback, arg, ri);
- DoubleLoType::GetInstance()->VisitRoots(callback, arg, ri);
- DoubleHiType::GetInstance()->VisitRoots(callback, arg, ri);
+ UndefinedType::GetInstance()->VisitRoots(visitor, ri);
+ ConflictType::GetInstance()->VisitRoots(visitor, ri);
+ BooleanType::GetInstance()->VisitRoots(visitor, ri);
+ ByteType::GetInstance()->VisitRoots(visitor, ri);
+ ShortType::GetInstance()->VisitRoots(visitor, ri);
+ CharType::GetInstance()->VisitRoots(visitor, ri);
+ IntegerType::GetInstance()->VisitRoots(visitor, ri);
+ LongLoType::GetInstance()->VisitRoots(visitor, ri);
+ LongHiType::GetInstance()->VisitRoots(visitor, ri);
+ FloatType::GetInstance()->VisitRoots(visitor, ri);
+ DoubleLoType::GetInstance()->VisitRoots(visitor, ri);
+ DoubleHiType::GetInstance()->VisitRoots(visitor, ri);
for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
- small_precise_constants_[value - kMinSmallConstant]->VisitRoots(callback, arg, ri);
+ small_precise_constants_[value - kMinSmallConstant]->VisitRoots(visitor, ri);
}
}
}
-void RegTypeCache::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
+void RegTypeCache::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
// Exclude the static roots that are visited by VisitStaticRoots().
for (size_t i = primitive_count_; i < entries_.size(); ++i) {
- entries_[i]->VisitRoots(callback, arg, root_info);
+ entries_[i]->VisitRoots(visitor, root_info);
}
}
diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h
index 01032a0..4b3105c 100644
--- a/runtime/verifier/reg_type_cache.h
+++ b/runtime/verifier/reg_type_cache.h
@@ -137,9 +137,9 @@
void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const RegType& RegTypeFromPrimitiveType(Primitive::Type) const;
- void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
+ void VisitRoots(RootVisitor* visitor, const RootInfo& root_info)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void VisitStaticRoots(RootCallback* callback, void* arg)
+ static void VisitStaticRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index b4bd68b..e61fcd8 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -169,7 +169,8 @@
// action but don't pass it on to the kernel.
// Note that we check that the signal number is in range here. An out of range signal
// number should behave exactly as the libc sigaction.
- if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
+ if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed() &&
+ (new_action == nullptr || new_action->sa_handler != SIG_DFL)) {
struct sigaction saved_action = user_sigactions[signal].GetAction();
if (new_action != NULL) {
user_sigactions[signal].SetAction(*new_action, false);
@@ -210,7 +211,7 @@
// action but don't pass it on to the kernel.
// Note that we check that the signal number is in range here. An out of range signal
// number should behave exactly as the libc sigaction.
- if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
+ if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed() && handler != SIG_DFL) {
oldhandler = reinterpret_cast<sighandler_t>(user_sigactions[signal].GetAction().sa_handler);
user_sigactions[signal].SetAction(sa, true);
return oldhandler;
diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc
index 76779ab..70a4f71 100644
--- a/sigchainlib/sigchain_dummy.cc
+++ b/sigchainlib/sigchain_dummy.cc
@@ -28,6 +28,11 @@
#define ATTRIBUTE_UNUSED __attribute__((__unused__))
+// We cannot annotate the declarations, as they are not no-return in the non-dummy version.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#pragma GCC diagnostic ignored "-Wmissing-noreturn"
+
static void log(const char* format, ...) {
char buf[256];
va_list ap;
@@ -73,4 +78,6 @@
abort();
}
+#pragma GCC diagnostic pop
+
} // namespace art
diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java
index 3d0f074..708f61f 100644
--- a/test/004-UnsafeTest/src/Main.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -104,6 +104,16 @@
if (!unsafe.compareAndSwapInt(t, intOffset, 0, 1)) {
System.out.println("Unexpectedly not succeeding compareAndSwap...");
}
+
+ if (unsafe.compareAndSwapLong(t, longOffset, 0, 1)) {
+ System.out.println("Unexpectedly succeeding compareAndSwapLong...");
+ }
+ if (!unsafe.compareAndSwapLong(t, longOffset, longValue, 0)) {
+ System.out.println("Unexpectedly not succeeding compareAndSwapLong...");
+ }
+ if (!unsafe.compareAndSwapLong(t, longOffset, 0, 1)) {
+ System.out.println("Unexpectedly not succeeding compareAndSwapLong...");
+ }
}
private static class TestClass {
diff --git a/test/107-int-math2/src/Main.java b/test/107-int-math2/src/Main.java
index f0fe934..6a6227c 100644
--- a/test/107-int-math2/src/Main.java
+++ b/test/107-int-math2/src/Main.java
@@ -379,7 +379,7 @@
*/
static int lit16Test(int x) {
- int[] results = new int[8];
+ int[] results = new int[10];
/* try to generate op-int/lit16" instructions */
results[0] = x + 1000;
@@ -390,6 +390,9 @@
results[5] = x & 1000;
results[6] = x | -1000;
results[7] = x ^ -1000;
+ /* use an 16-bit constant that has its MSB (bit-15) set */
+ results[8] = x / 32769;
+ results[9] = x / -32769;
if (results[0] != 78777) { return 1; }
if (results[1] != -76777) { return 2; }
@@ -399,6 +402,8 @@
if (results[5] != 960) { return 6; }
if (results[6] != -39) { return 7; }
if (results[7] != -76855) { return 8; }
+ if (results[8] != 2) { return 9; }
+ if (results[9] != -2) { return 10; }
return 0;
}
diff --git a/test/116-nodex2oat/nodex2oat.cc b/test/116-nodex2oat/nodex2oat.cc
index 564d58d..131af31 100644
--- a/test/116-nodex2oat/nodex2oat.cc
+++ b/test/116-nodex2oat/nodex2oat.cc
@@ -28,8 +28,7 @@
ScopedObjectAccess soa(Thread::Current());
mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
const DexFile& dex_file = klass->GetDexFile();
- const OatFile::OatDexFile* oat_dex_file =
- Runtime::Current()->GetClassLinker()->FindOpenedOatDexFileForDexFile(dex_file);
+ const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
return oat_dex_file != nullptr;
}
};
diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc
index da276f2..7eac412 100644
--- a/test/117-nopatchoat/nopatchoat.cc
+++ b/test/117-nopatchoat/nopatchoat.cc
@@ -28,11 +28,7 @@
ScopedObjectAccess soa(Thread::Current());
mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
const DexFile& dex_file = klass->GetDexFile();
-
- const OatFile::OatDexFile* oat_dex_file =
- Runtime::Current()->GetClassLinker()->FindOpenedOatDexFileForDexFile(dex_file);
-
- return oat_dex_file;
+ return dex_file.GetOatDexFile();
}
static bool hasExecutableOat(jclass cls) {
diff --git a/test/118-noimage-dex2oat/noimage-dex2oat.cc b/test/118-noimage-dex2oat/noimage-dex2oat.cc
index c49a13e..aacf00f 100644
--- a/test/118-noimage-dex2oat/noimage-dex2oat.cc
+++ b/test/118-noimage-dex2oat/noimage-dex2oat.cc
@@ -28,8 +28,7 @@
ScopedObjectAccess soa(Thread::Current());
mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
const DexFile& dex_file = klass->GetDexFile();
- const OatFile::OatDexFile* oat_dex_file =
- Runtime::Current()->GetClassLinker()->FindOpenedOatDexFileForDexFile(dex_file);
+ const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
return oat_dex_file != nullptr;
}
};
diff --git a/test/471-deopt-environment/expected.txt b/test/471-deopt-environment/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/471-deopt-environment/expected.txt
diff --git a/test/471-deopt-environment/info.txt b/test/471-deopt-environment/info.txt
new file mode 100644
index 0000000..bcb9575
--- /dev/null
+++ b/test/471-deopt-environment/info.txt
@@ -0,0 +1,3 @@
+Regression test for the bounds check elimination pass, which
+uses to generate a HDeoptimization instruction with an
+HEnvironment that does not have the uses lists updated.
diff --git a/test/471-deopt-environment/src/Main.java b/test/471-deopt-environment/src/Main.java
new file mode 100644
index 0000000..5c5080b
--- /dev/null
+++ b/test/471-deopt-environment/src/Main.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+public class Main {
+
+ private static int willInline(int a, int b) {
+ return a & b;
+ }
+
+ static int[] a = new int[4];
+ static int field = 42;
+
+ public static void main(String[] args) throws Exception {
+ // The order of optimizations that would lead to the problem was:
+ // 1) Inlining of `willInline`.
+ // 2) Bounds check elimination inserting a deopt at a[0] and removing the HBoundsCheck.
+ // 3) Instruction simplifier simpilifying the inlined willInline to just `field`.
+ //
+ // At this point, if the environment of the HDeoptimization instruction was
+ // just a pointer to the one in a[0], the uses lists would have not been updated
+ // and the HBoundsCheck being dead code after the HDeoptimization, the simplifcation
+ // at step 3) would not updated that environment.
+ int inEnv = willInline(field, field);
+ int doAdds = a[0] + a[1] + a[2] + a[3];
+
+ if (inEnv != 42) {
+ throw new Error("Expected 42");
+ }
+
+ if (doAdds != 0) {
+ throw new Error("Expected 0");
+ }
+ }
+}
diff --git a/tools/dexfuzz/Android.mk b/tools/dexfuzz/Android.mk
index 1e4b4f5..1580bc3 100644
--- a/tools/dexfuzz/Android.mk
+++ b/tools/dexfuzz/Android.mk
@@ -31,7 +31,10 @@
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE := dexfuzz
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/dexfuzz $(ACP) $(HOST_CORE_IMG_OUTS)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/dexfuzz $(ACP)
@echo "Copy: $(PRIVATE_MODULE) ($@)"
$(copy-file-to-new-target)
$(hide) chmod 755 $@
+
+# --- dexfuzz script with core image dependencies ----------------
+fuzzer: $(LOCAL_BUILT_MODULE) $(HOST_CORE_IMG_OUTS)
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Device.java b/tools/dexfuzz/src/dexfuzz/executors/Device.java
index 736aaad..4a53957 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Device.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Device.java
@@ -17,6 +17,7 @@
package dexfuzz.executors;
import java.io.IOException;
+import java.io.File;
import java.util.Map;
import dexfuzz.ExecutionResult;
@@ -67,6 +68,10 @@
return envVars.get(key);
}
+ private String getHostCoreImagePath() {
+ return androidHostOut + "/framework/core.art";
+ }
+
private void setup() {
programPushed = false;
@@ -74,6 +79,13 @@
androidProductOut = checkForEnvVar(envVars, "ANDROID_PRODUCT_OUT");
androidHostOut = checkForEnvVar(envVars, "ANDROID_HOST_OUT");
+ if (Options.executeOnHost) {
+ File coreImage = new File(getHostCoreImagePath());
+ if (!coreImage.exists()) {
+ Log.errorAndQuit("Host core image not found at " + coreImage.getPath()
+ + ". Did you forget to build it?");
+ }
+ }
if (!isHost) {
// Create temporary consumers for the initial test.
StreamConsumer outputConsumer = new StreamConsumer();
@@ -144,7 +156,7 @@
* Get any extra flags required to execute ART on the host.
*/
public String getHostExecutionFlags() {
- return String.format("-Xnorelocate -Ximage:%s/framework/core.art", androidHostOut);
+ return String.format("-Xnorelocate -Ximage:%s", getHostCoreImagePath());
}
public String getAndroidHostOut() {