summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/Android.common_build.mk16
-rw-r--r--compiler/Android.mk5
-rw-r--r--compiler/dex/compiler_enums.h31
-rw-r--r--compiler/dex/mir_graph.cc23
-rw-r--r--compiler/dex/mir_graph.h41
-rw-r--r--compiler/dex/mir_optimization.cc154
-rw-r--r--compiler/dex/quick/arm/call_arm.cc12
-rw-r--r--compiler/dex/quick/arm/codegen_arm.h4
-rw-r--r--compiler/dex/quick/arm/int_arm.cc8
-rw-r--r--compiler/dex/quick/arm64/arm64_lir.h3
-rw-r--r--compiler/dex/quick/arm64/assemble_arm64.cc44
-rw-r--r--compiler/dex/quick/arm64/call_arm64.cc12
-rw-r--r--compiler/dex/quick/arm64/codegen_arm64.h5
-rw-r--r--compiler/dex/quick/arm64/int_arm64.cc13
-rw-r--r--compiler/dex/quick/codegen_util.cc9
-rw-r--r--compiler/dex/quick/gen_common.cc19
-rwxr-xr-xcompiler/dex/quick/gen_invoke.cc5
-rw-r--r--compiler/dex/quick/mips/call_mips.cc8
-rw-r--r--compiler/dex/quick/mips/codegen_mips.h4
-rw-r--r--compiler/dex/quick/mir_to_lir.cc12
-rw-r--r--compiler/dex/quick/mir_to_lir.h14
-rw-r--r--compiler/dex/quick/quick_compiler.cc3
-rw-r--r--compiler/dex/quick/x86/call_x86.cc9
-rw-r--r--compiler/dex/quick/x86/codegen_x86.h5
-rwxr-xr-xcompiler/dex/quick/x86/target_x86.cc14
-rw-r--r--compiler/dex/quick/x86/utility_x86.cc1
-rw-r--r--compiler/dex/ssa_transformation.cc32
-rw-r--r--compiler/driver/compiler_driver-inl.h3
-rw-r--r--compiler/image_test.cc5
-rw-r--r--compiler/image_writer.cc520
-rw-r--r--compiler/image_writer.h99
-rw-r--r--compiler/jni/quick/jni_compiler.cc4
-rw-r--r--compiler/llvm/ir_builder.h2
-rw-r--r--compiler/oat_writer.cc5
-rw-r--r--compiler/optimizing/builder.cc85
-rw-r--r--compiler/optimizing/builder.h13
-rw-r--r--compiler/optimizing/code_generator.cc4
-rw-r--r--compiler/optimizing/code_generator.h9
-rw-r--r--compiler/optimizing/code_generator_arm.cc296
-rw-r--r--compiler/optimizing/code_generator_arm.h3
-rw-r--r--compiler/optimizing/code_generator_arm64.cc817
-rw-r--r--compiler/optimizing/code_generator_arm64.h40
-rw-r--r--compiler/optimizing/code_generator_x86.cc395
-rw-r--r--compiler/optimizing/code_generator_x86.h4
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc366
-rw-r--r--compiler/optimizing/code_generator_x86_64.h4
-rw-r--r--compiler/optimizing/constant_folding.h6
-rw-r--r--compiler/optimizing/constant_folding_test.cc5
-rw-r--r--compiler/optimizing/dead_code_elimination.h6
-rw-r--r--compiler/optimizing/dead_code_elimination_test.cc3
-rw-r--r--compiler/optimizing/gvn.h10
-rw-r--r--compiler/optimizing/instruction_simplifier.cc29
-rw-r--r--compiler/optimizing/instruction_simplifier.h12
-rw-r--r--compiler/optimizing/locations.h8
-rw-r--r--compiler/optimizing/nodes.h64
-rw-r--r--compiler/optimizing/optimization.cc6
-rw-r--r--compiler/optimizing/optimization.h13
-rw-r--r--compiler/optimizing/optimizing_compiler.cc120
-rw-r--r--compiler/optimizing/register_allocator.cc34
-rw-r--r--compiler/optimizing/ssa_builder.cc7
-rw-r--r--compiler/optimizing/ssa_phi_elimination.h17
-rw-r--r--compiler/optimizing/ssa_type_propagation.cc10
-rw-r--r--compiler/utils/arm/assembler_arm.cc17
-rw-r--r--compiler/utils/arm/assembler_arm32.cc2
-rw-r--r--compiler/utils/arm/assembler_arm32_test.cc706
-rw-r--r--compiler/utils/arm/assembler_arm_test.h545
-rw-r--r--compiler/utils/arm/assembler_thumb2.cc2
-rw-r--r--compiler/utils/arm64/assembler_arm64.cc2
-rw-r--r--compiler/utils/assembler_test.h79
-rw-r--r--compiler/utils/x86/assembler_x86.cc2
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.cc2
-rw-r--r--dex2oat/dex2oat.cc198
-rw-r--r--disassembler/disassembler.h10
-rw-r--r--disassembler/disassembler_arm64.cc80
-rw-r--r--disassembler/disassembler_arm64.h30
-rw-r--r--oatdump/oatdump.cc42
-rw-r--r--patchoat/patchoat.cc61
-rw-r--r--patchoat/patchoat.h19
-rw-r--r--runtime/Android.mk5
-rw-r--r--runtime/arch/arm/instruction_set_features_arm.cc10
-rw-r--r--runtime/arch/arm/portable_entrypoints_arm.S2
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S2
-rw-r--r--runtime/arch/arm64/instruction_set_features_arm64.cc35
-rw-r--r--runtime/arch/arm64/instruction_set_features_arm64.h4
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S2
-rw-r--r--runtime/arch/instruction_set.h4
-rw-r--r--runtime/arch/instruction_set_features_test.cc57
-rw-r--r--runtime/arch/mips/instruction_set_features_mips.cc4
-rw-r--r--runtime/arch/mips/portable_entrypoints_mips.S2
-rw-r--r--runtime/arch/mips/quick_entrypoints_mips.S2
-rw-r--r--runtime/arch/x86/instruction_set_features_x86.cc4
-rw-r--r--runtime/arch/x86/portable_entrypoints_x86.S2
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S4
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S6
-rw-r--r--runtime/asm_support.h20
-rw-r--r--runtime/base/scoped_flock.cc8
-rw-r--r--runtime/base/unix_file/fd_file.cc105
-rw-r--r--runtime/base/unix_file/fd_file.h59
-rw-r--r--runtime/base/unix_file/fd_file_test.cc5
-rw-r--r--runtime/base/unix_file/random_access_file_test.h9
-rw-r--r--runtime/base/unix_file/random_access_file_utils_test.cc4
-rw-r--r--runtime/check_reference_map_visitor.h2
-rw-r--r--runtime/class_linker.cc78
-rw-r--r--runtime/class_linker.h7
-rw-r--r--runtime/class_linker_test.cc5
-rw-r--r--runtime/common_runtime_test.cc9
-rw-r--r--runtime/debugger.cc145
-rw-r--r--runtime/debugger.h3
-rw-r--r--runtime/dex_file_test.cc3
-rw-r--r--runtime/dex_file_verifier_test.cc6
-rw-r--r--runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc4
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc2
-rw-r--r--runtime/fault_handler.cc3
-rw-r--r--runtime/gc/allocator/rosalloc.cc2
-rw-r--r--runtime/gc/allocator/rosalloc.h3
-rw-r--r--runtime/gc/heap.cc17
-rw-r--r--runtime/gc/space/image_space.cc48
-rw-r--r--runtime/hprof/hprof.cc9
-rw-r--r--runtime/image.cc2
-rw-r--r--runtime/instrumentation.cc4
-rw-r--r--runtime/instrumentation.h2
-rw-r--r--runtime/intern_table.cc1
-rw-r--r--runtime/interpreter/interpreter.cc37
-rw-r--r--runtime/jdwp/jdwp_event.cc4
-rw-r--r--runtime/jni_internal.cc8
-rw-r--r--runtime/jni_internal_test.cc125
-rw-r--r--runtime/mirror/art_method-inl.h43
-rw-r--r--runtime/mirror/art_method.cc22
-rw-r--r--runtime/mirror/art_method.h243
-rw-r--r--runtime/mirror/class.h7
-rw-r--r--runtime/mirror/object-inl.h4
-rw-r--r--runtime/mirror/object.h45
-rw-r--r--runtime/mirror/object_test.cc8
-rw-r--r--runtime/mirror/string.h17
-rw-r--r--runtime/native_bridge_art_interface.cc4
-rw-r--r--runtime/oat.cc2
-rw-r--r--runtime/parsed_options.cc6
-rw-r--r--runtime/runtime.cc10
-rw-r--r--runtime/signal_catcher.cc12
-rw-r--r--runtime/stack.cc18
-rw-r--r--runtime/thread.cc8
-rw-r--r--runtime/trace.cc9
-rw-r--r--runtime/utils.cc11
-rw-r--r--runtime/utils.h43
-rw-r--r--runtime/utils_test.cc32
-rw-r--r--runtime/zip_archive_test.cc2
-rw-r--r--test/083-compiler-regressions/expected.txt1
-rw-r--r--test/083-compiler-regressions/src/Main.java26
-rw-r--r--test/417-optimizing-arith-div/src/Main.java6
-rw-r--r--test/422-type-conversion/src/Main.java202
-rw-r--r--test/425-invoke-super/expected.txt1
-rw-r--r--test/425-invoke-super/src/Main.java2
-rw-r--r--test/428-optimizing-arith-rem/expected.txt0
-rw-r--r--test/428-optimizing-arith-rem/info.txt1
-rw-r--r--test/428-optimizing-arith-rem/src/Main.java160
-rw-r--r--test/429-ssa-builder/expected.txt0
-rw-r--r--test/429-ssa-builder/info.txt3
-rw-r--r--test/429-ssa-builder/src/Main.java49
-rw-r--r--test/430-live-register-slow-path/expected.txt0
-rw-r--r--test/430-live-register-slow-path/info.txt2
-rw-r--r--test/430-live-register-slow-path/src/Main.java39
-rw-r--r--test/800-smali/expected.txt4
-rw-r--r--test/800-smali/smali/BadCaseInOpRegRegReg.smali13
-rw-r--r--test/800-smali/smali/b_18380491AbstractBase.smali12
-rw-r--r--test/800-smali/smali/b_18380491ConcreteClass.smali19
-rw-r--r--test/800-smali/smali/sameFieldNames.smali64
-rw-r--r--test/800-smali/src/Main.java10
-rw-r--r--test/Android.run-test.mk132
-rwxr-xr-xtest/run-test18
169 files changed, 5928 insertions, 1593 deletions
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 7cb23f8f19..7b38e5ed4c 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -244,7 +244,7 @@ art_target_non_debug_cflags := $(art_non_debug_cflags)
ifeq ($(HOST_OS),linux)
# Larger frame-size for host clang builds today
ifndef SANITIZE_HOST
- art_host_non_debug_cflags += -Wframe-larger-than=2600
+ art_host_non_debug_cflags += -Wframe-larger-than=2700
endif
art_target_non_debug_cflags += -Wframe-larger-than=1728
endif
@@ -252,7 +252,7 @@ endif
ifndef LIBART_IMG_HOST_BASE_ADDRESS
$(error LIBART_IMG_HOST_BASE_ADDRESS unset)
endif
-ART_HOST_CFLAGS += $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS)
+ART_HOST_CFLAGS += $(art_cflags) -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS)
ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default
ifndef LIBART_IMG_TARGET_BASE_ADDRESS
@@ -283,18 +283,6 @@ endif
ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA)
ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA)
-ifeq ($(TARGET_CPU_SMP),true)
- ART_TARGET_CFLAGS += -DANDROID_SMP=1
-else
- ifeq ($(TARGET_CPU_SMP),false)
- ART_TARGET_CFLAGS += -DANDROID_SMP=0
- else
- $(warning TARGET_CPU_SMP should be (true|false), found $(TARGET_CPU_SMP))
- # Make sure we emit barriers for the worst case.
- ART_TARGET_CFLAGS += -DANDROID_SMP=1
- endif
-endif
-
# To use oprofile_android --callgraph, uncomment this and recompile with "mmm art -B -j16"
# ART_TARGET_CFLAGS += -fno-omit-frame-pointer -marm -mapcs
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 610f453816..eb9ad475b4 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -178,6 +178,7 @@ LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \
dex/pass_me.h \
driver/compiler_driver.h \
driver/compiler_options.h \
+ image_writer.h \
optimizing/locations.h \
utils/arm/constants_arm.h
@@ -206,7 +207,9 @@ define build-libart-compiler
ifeq ($$(art_ndebug_or_debug),ndebug)
LOCAL_MODULE := libart-compiler
LOCAL_SHARED_LIBRARIES += libart
- LOCAL_FDO_SUPPORT := true
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_FDO_SUPPORT := true
+ endif
else # debug
LOCAL_MODULE := libartd-compiler
LOCAL_SHARED_LIBRARIES += libartd
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index 5d877fdf80..b56fd6f5c7 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -610,21 +610,22 @@ std::ostream& operator<<(std::ostream& os, const SelectInstructionKind& kind);
// LIR fixup kinds for Arm
enum FixupKind {
kFixupNone,
- kFixupLabel, // For labels we just adjust the offset.
- kFixupLoad, // Mostly for immediates.
- kFixupVLoad, // FP load which *may* be pc-relative.
- kFixupCBxZ, // Cbz, Cbnz.
- kFixupTBxZ, // Tbz, Tbnz.
- kFixupPushPop, // Not really pc relative, but changes size based on args.
- kFixupCondBranch, // Conditional branch
- kFixupT1Branch, // Thumb1 Unconditional branch
- kFixupT2Branch, // Thumb2 Unconditional branch
- kFixupBlx1, // Blx1 (start of Blx1/Blx2 pair).
- kFixupBl1, // Bl1 (start of Bl1/Bl2 pair).
- kFixupAdr, // Adr.
- kFixupMovImmLST, // kThumb2MovImm16LST.
- kFixupMovImmHST, // kThumb2MovImm16HST.
- kFixupAlign4, // Align to 4-byte boundary.
+ kFixupLabel, // For labels we just adjust the offset.
+ kFixupLoad, // Mostly for immediates.
+ kFixupVLoad, // FP load which *may* be pc-relative.
+ kFixupCBxZ, // Cbz, Cbnz.
+ kFixupTBxZ, // Tbz, Tbnz.
+ kFixupPushPop, // Not really pc relative, but changes size based on args.
+ kFixupCondBranch, // Conditional branch
+ kFixupT1Branch, // Thumb1 Unconditional branch
+ kFixupT2Branch, // Thumb2 Unconditional branch
+ kFixupBlx1, // Blx1 (start of Blx1/Blx2 pair).
+ kFixupBl1, // Bl1 (start of Bl1/Bl2 pair).
+ kFixupAdr, // Adr.
+ kFixupMovImmLST, // kThumb2MovImm16LST.
+ kFixupMovImmHST, // kThumb2MovImm16HST.
+ kFixupAlign4, // Align to 4-byte boundary.
+ kFixupA53Erratum835769, // Cortex A53 Erratum 835769.
};
std::ostream& operator<<(std::ostream& os, const FixupKind& kind);
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index b87ab66347..29972ddba5 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -97,11 +97,6 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena)
max_nested_loops_(0u),
i_dom_list_(NULL),
temp_scoped_alloc_(),
- temp_insn_data_(nullptr),
- temp_bit_vector_size_(0u),
- temp_bit_vector_(nullptr),
- temp_bit_matrix_(nullptr),
- temp_gvn_(),
block_list_(arena->Adapter(kArenaAllocBBList)),
try_block_addr_(NULL),
entry_block_(NULL),
@@ -133,6 +128,7 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena)
sfield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
method_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
gen_suspend_test_list_(arena->Adapter()) {
+ memset(&temp_, 0, sizeof(temp_));
use_counts_.reserve(256);
raw_use_counts_.reserve(256);
block_list_.reserve(100);
@@ -1348,9 +1344,10 @@ void MIRGraph::DisassembleExtendedInstr(const MIR* mir, std::string* decoded_mir
decoded_mir->append(", ");
decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false));
}
- decoded_mir->append(StringPrintf(" = vect%d", mir->dalvikInsn.vB));
+ decoded_mir->append(StringPrintf(" = vect%d (extr_idx:%d)", mir->dalvikInsn.vB, mir->dalvikInsn.arg[0]));
} else {
- decoded_mir->append(StringPrintf(" v%d = vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ decoded_mir->append(StringPrintf(" v%d = vect%d (extr_idx:%d)", mir->dalvikInsn.vA,
+ mir->dalvikInsn.vB, mir->dalvikInsn.arg[0]));
}
FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
break;
@@ -1681,9 +1678,9 @@ void MIRGraph::InitializeMethodUses() {
void MIRGraph::SSATransformationStart() {
DCHECK(temp_scoped_alloc_.get() == nullptr);
temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
- temp_bit_vector_size_ = GetNumOfCodeAndTempVRs();
- temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
- temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapRegisterV);
+ temp_.ssa.num_vregs = GetNumOfCodeAndTempVRs();
+ temp_.ssa.work_live_vregs = new (temp_scoped_alloc_.get()) ArenaBitVector(
+ temp_scoped_alloc_.get(), temp_.ssa.num_vregs, false, kBitMapRegisterV);
}
void MIRGraph::SSATransformationEnd() {
@@ -1692,9 +1689,9 @@ void MIRGraph::SSATransformationEnd() {
VerifyDataflow();
}
- temp_bit_vector_size_ = 0u;
- temp_bit_vector_ = nullptr;
- temp_bit_matrix_ = nullptr; // Def block matrix.
+ temp_.ssa.num_vregs = 0u;
+ temp_.ssa.work_live_vregs = nullptr;
+ temp_.ssa.def_block_matrix = nullptr;
DCHECK(temp_scoped_alloc_.get() != nullptr);
temp_scoped_alloc_.reset();
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index a1d24e2c37..d77ad6f76a 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -1270,15 +1270,38 @@ class MIRGraph {
size_t max_nested_loops_;
int* i_dom_list_;
std::unique_ptr<ScopedArenaAllocator> temp_scoped_alloc_;
- uint16_t* temp_insn_data_;
- uint32_t temp_bit_vector_size_;
- ArenaBitVector* temp_bit_vector_;
- // temp_bit_matrix_ used as one of
- // - def_block_matrix: original num registers x num_blocks_,
- // - ending_null_check_matrix: num_blocks_ x original num registers,
- // - ending_clinit_check_matrix: num_blocks_ x unique class count.
- ArenaBitVector** temp_bit_matrix_;
- std::unique_ptr<GlobalValueNumbering> temp_gvn_;
+ // Union of temporaries used by different passes.
+ union {
+ // Class init check elimination.
+ struct {
+ size_t num_class_bits; // 2 bits per class: class initialized and class in dex cache.
+ ArenaBitVector* work_classes_to_check;
+ ArenaBitVector** ending_classes_to_check_matrix; // num_blocks_ x num_class_bits.
+ uint16_t* indexes;
+ } cice;
+ // Null check elimination.
+ struct {
+ size_t num_vregs;
+ ArenaBitVector* work_vregs_to_check;
+ ArenaBitVector** ending_vregs_to_check_matrix; // num_blocks_ x num_vregs.
+ } nce;
+ // Special method inlining.
+ struct {
+ size_t num_indexes;
+ ArenaBitVector* processed_indexes;
+ uint16_t* lowering_infos;
+ } smi;
+ // SSA transformation.
+ struct {
+ size_t num_vregs;
+ ArenaBitVector* work_live_vregs;
+ ArenaBitVector** def_block_matrix; // num_vregs x num_blocks_.
+ } ssa;
+ // Global value numbering.
+ struct {
+ GlobalValueNumbering* gvn;
+ } gvn;
+ } temp_;
static const int kInvalidEntry = -1;
ArenaVector<BasicBlock*> block_list_;
ArenaBitVector* try_block_addr_;
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index a0ad2133be..d025d08168 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -891,12 +891,12 @@ bool MIRGraph::EliminateNullChecksGate() {
DCHECK(temp_scoped_alloc_.get() == nullptr);
temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
- temp_bit_vector_size_ = GetNumOfCodeVRs();
- temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
- temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck);
- temp_bit_matrix_ = static_cast<ArenaBitVector**>(
+ temp_.nce.num_vregs = GetNumOfCodeVRs();
+ temp_.nce.work_vregs_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector(
+ temp_scoped_alloc_.get(), temp_.nce.num_vregs, false, kBitMapNullCheck);
+ temp_.nce.ending_vregs_to_check_matrix = static_cast<ArenaBitVector**>(
temp_scoped_alloc_->Alloc(sizeof(ArenaBitVector*) * GetNumBlocks(), kArenaAllocMisc));
- std::fill_n(temp_bit_matrix_, GetNumBlocks(), nullptr);
+ std::fill_n(temp_.nce.ending_vregs_to_check_matrix, GetNumBlocks(), nullptr);
// reset MIR_MARK
AllNodesIterator iter(this);
@@ -919,7 +919,7 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) {
return false;
}
- ArenaBitVector* vregs_to_check = temp_bit_vector_;
+ ArenaBitVector* vregs_to_check = temp_.nce.work_vregs_to_check;
/*
* Set initial state. Catch blocks don't need any special treatment.
*/
@@ -940,7 +940,7 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) {
// Starting state is union of all incoming arcs.
bool copied_first = false;
for (BasicBlockId pred_id : bb->predecessors) {
- if (temp_bit_matrix_[pred_id] == nullptr) {
+ if (temp_.nce.ending_vregs_to_check_matrix[pred_id] == nullptr) {
continue;
}
BasicBlock* pred_bb = GetBasicBlock(pred_id);
@@ -962,9 +962,9 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) {
}
if (!copied_first) {
copied_first = true;
- vregs_to_check->Copy(temp_bit_matrix_[pred_id]);
+ vregs_to_check->Copy(temp_.nce.ending_vregs_to_check_matrix[pred_id]);
} else {
- vregs_to_check->Union(temp_bit_matrix_[pred_id]);
+ vregs_to_check->Union(temp_.nce.ending_vregs_to_check_matrix[pred_id]);
}
if (null_check_insn != nullptr) {
vregs_to_check->ClearBit(null_check_insn->dalvikInsn.vA);
@@ -1057,27 +1057,27 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) {
// Did anything change?
bool nce_changed = false;
- ArenaBitVector* old_ending_ssa_regs_to_check = temp_bit_matrix_[bb->id];
+ ArenaBitVector* old_ending_ssa_regs_to_check = temp_.nce.ending_vregs_to_check_matrix[bb->id];
if (old_ending_ssa_regs_to_check == nullptr) {
DCHECK(temp_scoped_alloc_.get() != nullptr);
nce_changed = vregs_to_check->GetHighestBitSet() != -1;
- temp_bit_matrix_[bb->id] = vregs_to_check;
+ temp_.nce.ending_vregs_to_check_matrix[bb->id] = vregs_to_check;
// Create a new vregs_to_check for next BB.
- temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
- temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck);
+ temp_.nce.work_vregs_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector(
+ temp_scoped_alloc_.get(), temp_.nce.num_vregs, false, kBitMapNullCheck);
} else if (!vregs_to_check->SameBitsSet(old_ending_ssa_regs_to_check)) {
nce_changed = true;
- temp_bit_matrix_[bb->id] = vregs_to_check;
- temp_bit_vector_ = old_ending_ssa_regs_to_check; // Reuse for vregs_to_check for next BB.
+ temp_.nce.ending_vregs_to_check_matrix[bb->id] = vregs_to_check;
+ temp_.nce.work_vregs_to_check = old_ending_ssa_regs_to_check; // Reuse for next BB.
}
return nce_changed;
}
void MIRGraph::EliminateNullChecksEnd() {
// Clean up temporaries.
- temp_bit_vector_size_ = 0u;
- temp_bit_vector_ = nullptr;
- temp_bit_matrix_ = nullptr;
+ temp_.nce.num_vregs = 0u;
+ temp_.nce.work_vregs_to_check = nullptr;
+ temp_.nce.ending_vregs_to_check_matrix = nullptr;
DCHECK(temp_scoped_alloc_.get() != nullptr);
temp_scoped_alloc_.reset();
@@ -1124,9 +1124,9 @@ bool MIRGraph::EliminateClassInitChecksGate() {
// Each insn we use here has at least 2 code units, offset/2 will be a unique index.
const size_t end = (GetNumDalvikInsns() + 1u) / 2u;
- temp_insn_data_ = static_cast<uint16_t*>(
- temp_scoped_alloc_->Alloc(end * sizeof(*temp_insn_data_), kArenaAllocGrowableArray));
- std::fill_n(temp_insn_data_, end, 0xffffu);
+ temp_.cice.indexes = static_cast<uint16_t*>(
+ temp_scoped_alloc_->Alloc(end * sizeof(*temp_.cice.indexes), kArenaAllocGrowableArray));
+ std::fill_n(temp_.cice.indexes, end, 0xffffu);
uint32_t unique_class_count = 0u;
{
@@ -1173,8 +1173,8 @@ bool MIRGraph::EliminateClassInitChecksGate() {
static_cast<uint16_t>(class_to_index_map.size())
};
uint16_t index = class_to_index_map.insert(entry).first->index;
- // Using offset/2 for index into temp_insn_data_.
- temp_insn_data_[mir->offset / 2u] = index;
+ // Using offset/2 for index into temp_.cice.indexes.
+ temp_.cice.indexes[mir->offset / 2u] = index;
}
} else if (mir->dalvikInsn.opcode == Instruction::INVOKE_STATIC ||
mir->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE) {
@@ -1187,8 +1187,8 @@ bool MIRGraph::EliminateClassInitChecksGate() {
static_cast<uint16_t>(class_to_index_map.size())
};
uint16_t index = class_to_index_map.insert(entry).first->index;
- // Using offset/2 for index into temp_insn_data_.
- temp_insn_data_[mir->offset / 2u] = index;
+ // Using offset/2 for index into temp_.cice.indexes.
+ temp_.cice.indexes[mir->offset / 2u] = index;
}
}
}
@@ -1199,19 +1199,19 @@ bool MIRGraph::EliminateClassInitChecksGate() {
if (unique_class_count == 0u) {
// All SGET/SPUTs refer to initialized classes. Nothing to do.
- temp_insn_data_ = nullptr;
+ temp_.cice.indexes = nullptr;
temp_scoped_alloc_.reset();
return false;
}
// 2 bits for each class: is class initialized, is class in dex cache.
- temp_bit_vector_size_ = 2u * unique_class_count;
- temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
- temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapClInitCheck);
- temp_bit_matrix_ = static_cast<ArenaBitVector**>(
+ temp_.cice.num_class_bits = 2u * unique_class_count;
+ temp_.cice.work_classes_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector(
+ temp_scoped_alloc_.get(), temp_.cice.num_class_bits, false, kBitMapClInitCheck);
+ temp_.cice.ending_classes_to_check_matrix = static_cast<ArenaBitVector**>(
temp_scoped_alloc_->Alloc(sizeof(ArenaBitVector*) * GetNumBlocks(), kArenaAllocMisc));
- std::fill_n(temp_bit_matrix_, GetNumBlocks(), nullptr);
- DCHECK_GT(temp_bit_vector_size_, 0u);
+ std::fill_n(temp_.cice.ending_classes_to_check_matrix, GetNumBlocks(), nullptr);
+ DCHECK_GT(temp_.cice.num_class_bits, 0u);
return true;
}
@@ -1229,22 +1229,22 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) {
/*
* Set initial state. Catch blocks don't need any special treatment.
*/
- ArenaBitVector* classes_to_check = temp_bit_vector_;
+ ArenaBitVector* classes_to_check = temp_.cice.work_classes_to_check;
DCHECK(classes_to_check != nullptr);
if (bb->block_type == kEntryBlock) {
- classes_to_check->SetInitialBits(temp_bit_vector_size_);
+ classes_to_check->SetInitialBits(temp_.cice.num_class_bits);
} else {
// Starting state is union of all incoming arcs.
bool copied_first = false;
for (BasicBlockId pred_id : bb->predecessors) {
- if (temp_bit_matrix_[pred_id] == nullptr) {
+ if (temp_.cice.ending_classes_to_check_matrix[pred_id] == nullptr) {
continue;
}
if (!copied_first) {
copied_first = true;
- classes_to_check->Copy(temp_bit_matrix_[pred_id]);
+ classes_to_check->Copy(temp_.cice.ending_classes_to_check_matrix[pred_id]);
} else {
- classes_to_check->Union(temp_bit_matrix_[pred_id]);
+ classes_to_check->Union(temp_.cice.ending_classes_to_check_matrix[pred_id]);
}
}
DCHECK(copied_first); // At least one predecessor must have been processed before this bb.
@@ -1253,7 +1253,7 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) {
// Walk through the instruction in the block, updating as necessary
for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
- uint16_t index = temp_insn_data_[mir->offset / 2u];
+ uint16_t index = temp_.cice.indexes[mir->offset / 2u];
if (index != 0xffffu) {
bool check_initialization = false;
bool check_dex_cache = false;
@@ -1299,29 +1299,29 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) {
// Did anything change?
bool changed = false;
- ArenaBitVector* old_ending_classes_to_check = temp_bit_matrix_[bb->id];
+ ArenaBitVector* old_ending_classes_to_check = temp_.cice.ending_classes_to_check_matrix[bb->id];
if (old_ending_classes_to_check == nullptr) {
DCHECK(temp_scoped_alloc_.get() != nullptr);
changed = classes_to_check->GetHighestBitSet() != -1;
- temp_bit_matrix_[bb->id] = classes_to_check;
+ temp_.cice.ending_classes_to_check_matrix[bb->id] = classes_to_check;
// Create a new classes_to_check for next BB.
- temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
- temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapClInitCheck);
+ temp_.cice.work_classes_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector(
+ temp_scoped_alloc_.get(), temp_.cice.num_class_bits, false, kBitMapClInitCheck);
} else if (!classes_to_check->Equal(old_ending_classes_to_check)) {
changed = true;
- temp_bit_matrix_[bb->id] = classes_to_check;
- temp_bit_vector_ = old_ending_classes_to_check; // Reuse for classes_to_check for next BB.
+ temp_.cice.ending_classes_to_check_matrix[bb->id] = classes_to_check;
+ temp_.cice.work_classes_to_check = old_ending_classes_to_check; // Reuse for next BB.
}
return changed;
}
void MIRGraph::EliminateClassInitChecksEnd() {
// Clean up temporaries.
- temp_bit_vector_size_ = 0u;
- temp_bit_vector_ = nullptr;
- temp_bit_matrix_ = nullptr;
- DCHECK(temp_insn_data_ != nullptr);
- temp_insn_data_ = nullptr;
+ temp_.cice.num_class_bits = 0u;
+ temp_.cice.work_classes_to_check = nullptr;
+ temp_.cice.ending_classes_to_check_matrix = nullptr;
+ DCHECK(temp_.cice.indexes != nullptr);
+ temp_.cice.indexes = nullptr;
DCHECK(temp_scoped_alloc_.get() != nullptr);
temp_scoped_alloc_.reset();
}
@@ -1333,39 +1333,39 @@ bool MIRGraph::ApplyGlobalValueNumberingGate() {
DCHECK(temp_scoped_alloc_ == nullptr);
temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
- DCHECK(temp_gvn_ == nullptr);
- temp_gvn_.reset(
- new (temp_scoped_alloc_.get()) GlobalValueNumbering(cu_, temp_scoped_alloc_.get(),
- GlobalValueNumbering::kModeGvn));
+ DCHECK(temp_.gvn.gvn == nullptr);
+ temp_.gvn.gvn = new (temp_scoped_alloc_.get()) GlobalValueNumbering(
+ cu_, temp_scoped_alloc_.get(), GlobalValueNumbering::kModeGvn);
return true;
}
bool MIRGraph::ApplyGlobalValueNumbering(BasicBlock* bb) {
- DCHECK(temp_gvn_ != nullptr);
- LocalValueNumbering* lvn = temp_gvn_->PrepareBasicBlock(bb);
+ DCHECK(temp_.gvn.gvn != nullptr);
+ LocalValueNumbering* lvn = temp_.gvn.gvn->PrepareBasicBlock(bb);
if (lvn != nullptr) {
for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
lvn->GetValueNumber(mir);
}
}
- bool change = (lvn != nullptr) && temp_gvn_->FinishBasicBlock(bb);
+ bool change = (lvn != nullptr) && temp_.gvn.gvn->FinishBasicBlock(bb);
return change;
}
void MIRGraph::ApplyGlobalValueNumberingEnd() {
// Perform modifications.
- if (temp_gvn_->Good()) {
+ DCHECK(temp_.gvn.gvn != nullptr);
+ if (temp_.gvn.gvn->Good()) {
if (max_nested_loops_ != 0u) {
- temp_gvn_->StartPostProcessing();
+ temp_.gvn.gvn->StartPostProcessing();
TopologicalSortIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
ScopedArenaAllocator allocator(&cu_->arena_stack); // Reclaim memory after each LVN.
- LocalValueNumbering* lvn = temp_gvn_->PrepareBasicBlock(bb, &allocator);
+ LocalValueNumbering* lvn = temp_.gvn.gvn->PrepareBasicBlock(bb, &allocator);
if (lvn != nullptr) {
for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
lvn->GetValueNumber(mir);
}
- bool change = temp_gvn_->FinishBasicBlock(bb);
+ bool change = temp_.gvn.gvn->FinishBasicBlock(bb);
DCHECK(!change) << PrettyMethod(cu_->method_idx, *cu_->dex_file);
}
}
@@ -1376,16 +1376,16 @@ void MIRGraph::ApplyGlobalValueNumberingEnd() {
LOG(WARNING) << "GVN failed for " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
}
- DCHECK(temp_gvn_ != nullptr);
- temp_gvn_.reset();
+ delete temp_.gvn.gvn;
+ temp_.gvn.gvn = nullptr;
DCHECK(temp_scoped_alloc_ != nullptr);
temp_scoped_alloc_.reset();
}
void MIRGraph::ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke, MIR* iget_or_iput) {
uint32_t method_index = invoke->meta.method_lowering_info;
- if (temp_bit_vector_->IsBitSet(method_index)) {
- iget_or_iput->meta.ifield_lowering_info = temp_insn_data_[method_index];
+ if (temp_.smi.processed_indexes->IsBitSet(method_index)) {
+ iget_or_iput->meta.ifield_lowering_info = temp_.smi.lowering_infos[method_index];
DCHECK_EQ(field_idx, GetIFieldLoweringInfo(iget_or_iput).FieldIndex());
return;
}
@@ -1402,8 +1402,8 @@ void MIRGraph::ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke,
uint32_t field_info_index = ifield_lowering_infos_.size();
ifield_lowering_infos_.push_back(inlined_field_info);
- temp_bit_vector_->SetBit(method_index);
- temp_insn_data_[method_index] = field_info_index;
+ temp_.smi.processed_indexes->SetBit(method_index);
+ temp_.smi.lowering_infos[method_index] = field_info_index;
iget_or_iput->meta.ifield_lowering_info = field_info_index;
}
@@ -1425,12 +1425,12 @@ void MIRGraph::InlineSpecialMethodsStart() {
DCHECK(temp_scoped_alloc_.get() == nullptr);
temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
- temp_bit_vector_size_ = method_lowering_infos_.size();
- temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
- temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapMisc);
- temp_bit_vector_->ClearAllBits();
- temp_insn_data_ = static_cast<uint16_t*>(temp_scoped_alloc_->Alloc(
- temp_bit_vector_size_ * sizeof(*temp_insn_data_), kArenaAllocGrowableArray));
+ temp_.smi.num_indexes = method_lowering_infos_.size();
+ temp_.smi.processed_indexes = new (temp_scoped_alloc_.get()) ArenaBitVector(
+ temp_scoped_alloc_.get(), temp_.smi.num_indexes, false, kBitMapMisc);
+ temp_.smi.processed_indexes->ClearAllBits();
+ temp_.smi.lowering_infos = static_cast<uint16_t*>(temp_scoped_alloc_->Alloc(
+ temp_.smi.num_indexes * sizeof(*temp_.smi.lowering_infos), kArenaAllocGrowableArray));
}
void MIRGraph::InlineSpecialMethods(BasicBlock* bb) {
@@ -1477,10 +1477,12 @@ void MIRGraph::InlineSpecialMethods(BasicBlock* bb) {
}
void MIRGraph::InlineSpecialMethodsEnd() {
- DCHECK(temp_insn_data_ != nullptr);
- temp_insn_data_ = nullptr;
- DCHECK(temp_bit_vector_ != nullptr);
- temp_bit_vector_ = nullptr;
+ // Clean up temporaries.
+ DCHECK(temp_.smi.lowering_infos != nullptr);
+ temp_.smi.lowering_infos = nullptr;
+ temp_.smi.num_indexes = 0u;
+ DCHECK(temp_.smi.processed_indexes != nullptr);
+ temp_.smi.processed_indexes = nullptr;
DCHECK(temp_scoped_alloc_.get() != nullptr);
temp_scoped_alloc_.reset();
}
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index b4eebb320e..f15d707a5c 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -288,18 +288,12 @@ void ArmMir2Lir::GenMoveException(RegLocation rl_dest) {
StoreValue(rl_dest, rl_result);
}
-/*
- * Mark garbage collection card. Skip if the value we're storing is null.
- */
-void ArmMir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) {
+void ArmMir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) {
RegStorage reg_card_base = AllocTemp();
RegStorage reg_card_no = AllocTemp();
- LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL);
LoadWordDisp(rs_rARM_SELF, Thread::CardTableOffset<4>().Int32Value(), reg_card_base);
OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte);
- LIR* target = NewLIR0(kPseudoTargetLabel);
- branch_over->target = target;
FreeTemp(reg_card_base);
FreeTemp(reg_card_no);
}
@@ -536,8 +530,8 @@ static int ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSE
if (direct_code == 0) {
// kInvokeTgt := arg0_ref->entrypoint
cg->LoadWordDisp(arg0_ref,
- mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(),
- cg->TargetPtrReg(kInvokeTgt));
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArmPointerSize).Int32Value(), cg->TargetPtrReg(kInvokeTgt));
}
break;
default:
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index d2351997b7..e8d0c32ffd 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -106,7 +106,9 @@ class ArmMir2Lir FINAL : public Mir2Lir {
OpSize size, VolatileKind is_volatile) OVERRIDE;
LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale,
OpSize size) OVERRIDE;
- void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg);
+
+ /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage)
+ void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE;
// Required for target - register utilities.
RegStorage TargetReg(SpecialTargetRegister reg) OVERRIDE;
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 57544b5187..cab039bfd4 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -16,6 +16,7 @@
/* This file contains codegen for the Thumb2 ISA. */
+#include "arch/instruction_set_features.h"
#include "arm_lir.h"
#include "codegen_arm.h"
#include "dex/quick/mir_to_lir-inl.h"
@@ -1119,7 +1120,9 @@ LIR* ArmMir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* targe
}
bool ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
-#if ANDROID_SMP != 0
+ if (!cu_->GetInstructionSetFeatures()->IsSmp()) {
+ return false;
+ }
// Start off with using the last LIR as the barrier. If it is not enough, then we will generate one.
LIR* barrier = last_lir_insn_;
@@ -1149,9 +1152,6 @@ bool ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
DCHECK(!barrier->flags.use_def_invalid);
barrier->u.m.def_mask = &kEncodeAll;
return ret;
-#else
- return false;
-#endif
}
void ArmMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) {
diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h
index 973279e8b7..f8a7310c20 100644
--- a/compiler/dex/quick/arm64/arm64_lir.h
+++ b/compiler/dex/quick/arm64/arm64_lir.h
@@ -320,6 +320,7 @@ enum A64Opcode {
kA64Mul3rrr, // mul [00011011000] rm[20-16] [011111] rn[9-5] rd[4-0].
kA64Msub4rrrr, // msub[s0011011000] rm[20-16] [1] ra[14-10] rn[9-5] rd[4-0].
kA64Neg3rro, // neg alias of "sub arg0, rzr, arg1, arg2".
+ kA64Nop0, // nop alias of "hint #0" [11010101000000110010000000011111].
kA64Orr3Rrl, // orr [s01100100] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0].
kA64Orr4rrro, // orr [s0101010] shift[23-22] [0] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0].
kA64Ret, // ret [11010110010111110000001111000000].
@@ -332,7 +333,7 @@ enum A64Opcode {
kA64Scvtf2fw, // scvtf [000111100s100010000000] rn[9-5] rd[4-0].
kA64Scvtf2fx, // scvtf [100111100s100010000000] rn[9-5] rd[4-0].
kA64Sdiv3rrr, // sdiv[s0011010110] rm[20-16] [000011] rn[9-5] rd[4-0].
- kA64Smaddl4xwwx, // smaddl [10011011001] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0].
+ kA64Smull3xww, // smull [10011011001] rm[20-16] [011111] rn[9-5] rd[4-0].
kA64Smulh3xxx, // smulh [10011011010] rm[20-16] [011111] rn[9-5] rd[4-0].
kA64Stp4ffXD, // stp [0s10110100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0].
kA64Stp4rrXD, // stp [s010100100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0].
diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc
index 9cdabf18f0..cab11cc4a5 100644
--- a/compiler/dex/quick/arm64/assemble_arm64.cc
+++ b/compiler/dex/quick/arm64/assemble_arm64.cc
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-#include "arm64_lir.h"
#include "codegen_arm64.h"
+
+#include "arch/arm64/instruction_set_features_arm64.h"
+#include "arm64_lir.h"
#include "dex/quick/mir_to_lir-inl.h"
namespace art {
@@ -468,13 +470,17 @@ const A64EncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = {
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
"mul", "!0r, !1r, !2r", kFixupNone),
ENCODING_MAP(WIDE(kA64Msub4rrrr), SF_VARIANTS(0x1b008000),
- kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 14, 10,
- kFmtRegR, 20, 16, IS_QUAD_OP | REG_DEF0_USE123,
- "msub", "!0r, !1r, !3r, !2r", kFixupNone),
+ kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16,
+ kFmtRegR, 14, 10, IS_QUAD_OP | REG_DEF0_USE123 | NEEDS_FIXUP,
+ "msub", "!0r, !1r, !2r, !3r", kFixupA53Erratum835769),
ENCODING_MAP(WIDE(kA64Neg3rro), SF_VARIANTS(0x4b0003e0),
kFmtRegR, 4, 0, kFmtRegR, 20, 16, kFmtShift, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
"neg", "!0r, !1r!2o", kFixupNone),
+ ENCODING_MAP(kA64Nop0, NO_VARIANTS(0xd503201f),
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND,
+ "nop", "", kFixupNone),
ENCODING_MAP(WIDE(kA64Orr3Rrl), SF_VARIANTS(0x32000000),
kFmtRegROrSp, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 22, 10,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
@@ -523,10 +529,10 @@ const A64EncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = {
kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
"sdiv", "!0r, !1r, !2r", kFixupNone),
- ENCODING_MAP(WIDE(kA64Smaddl4xwwx), NO_VARIANTS(0x9b200000),
+ ENCODING_MAP(kA64Smull3xww, NO_VARIANTS(0x9b207c00),
kFmtRegX, 4, 0, kFmtRegW, 9, 5, kFmtRegW, 20, 16,
- kFmtRegX, 14, 10, IS_QUAD_OP | REG_DEF0_USE123,
- "smaddl", "!0x, !1w, !2w, !3x", kFixupNone),
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "smull", "!0x, !1w, !2w", kFixupNone),
ENCODING_MAP(kA64Smulh3xxx, NO_VARIANTS(0x9b407c00),
kFmtRegX, 4, 0, kFmtRegX, 9, 5, kFmtRegX, 20, 16,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
@@ -988,6 +994,30 @@ void Arm64Mir2Lir::AssembleLIR() {
lir->operands[1] = delta;
break;
}
+ case kFixupA53Erratum835769:
+ // Avoid emitting code that could trigger Cortex A53's erratum 835769.
+ // This fixup should be carried out for all multiply-accumulate instructions: madd, msub,
+ // smaddl, smsubl, umaddl and umsubl.
+ if (cu_->GetInstructionSetFeatures()->AsArm64InstructionSetFeatures()
+ ->NeedFixCortexA53_835769()) {
+ // Check that this is a 64-bit multiply-accumulate.
+ if (IS_WIDE(lir->opcode)) {
+ uint64_t prev_insn_flags = EncodingMap[UNWIDE(lir->prev->opcode)].flags;
+ // Check that the instruction preceding the multiply-accumulate is a load or store.
+ if ((prev_insn_flags & IS_LOAD) != 0 || (prev_insn_flags & IS_STORE) != 0) {
+ // insert a NOP between the load/store and the multiply-accumulate.
+ LIR* new_lir = RawLIR(lir->dalvik_offset, kA64Nop0, 0, 0, 0, 0, 0, NULL);
+ new_lir->offset = lir->offset;
+ new_lir->flags.fixup = kFixupNone;
+ new_lir->flags.size = EncodingMap[kA64Nop0].size;
+ InsertLIRBefore(lir, new_lir);
+ lir->offset += new_lir->flags.size;
+ offset_adjustment += new_lir->flags.size;
+ res = kRetryAll;
+ }
+ }
+ }
+ break;
default:
LOG(FATAL) << "Unexpected case " << lir->flags.fixup;
}
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 106996ea44..089e4b6709 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -251,20 +251,14 @@ void Arm64Mir2Lir::GenMoveException(RegLocation rl_dest) {
StoreValue(rl_dest, rl_result);
}
-/*
- * Mark garbage collection card. Skip if the value we're storing is null.
- */
-void Arm64Mir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) {
+void Arm64Mir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) {
RegStorage reg_card_base = AllocTempWide();
RegStorage reg_card_no = AllocTempWide(); // Needs to be wide as addr is ref=64b
- LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL);
LoadWordDisp(rs_xSELF, Thread::CardTableOffset<8>().Int32Value(), reg_card_base);
OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
// TODO(Arm64): generate "strb wB, [xB, wC, uxtw]" rather than "strb wB, [xB, xC]"?
StoreBaseIndexed(reg_card_base, reg_card_no, As32BitReg(reg_card_base),
0, kUnsignedByte);
- LIR* target = NewLIR0(kPseudoTargetLabel);
- branch_over->target = target;
FreeTemp(reg_card_base);
FreeTemp(reg_card_no);
}
@@ -473,8 +467,8 @@ static int Arm64NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
if (direct_code == 0) {
// kInvokeTgt := arg0_ref->entrypoint
cg->LoadWordDisp(arg0_ref,
- mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(),
- cg->TargetPtrReg(kInvokeTgt));
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArm64PointerSize).Int32Value(), cg->TargetPtrReg(kInvokeTgt));
}
break;
default:
diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index 5182a89474..5e10f80fa5 100644
--- a/compiler/dex/quick/arm64/codegen_arm64.h
+++ b/compiler/dex/quick/arm64/codegen_arm64.h
@@ -94,7 +94,10 @@ class Arm64Mir2Lir FINAL : public Mir2Lir {
LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale,
OpSize size) OVERRIDE;
LIR* StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale) OVERRIDE;
- void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) OVERRIDE;
+
+ /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage)
+ void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE;
+
LIR* OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg,
int offset, int check_value, LIR* target, LIR** compare) OVERRIDE;
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index 8a5a58c949..0e00698388 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -16,6 +16,7 @@
/* This file contains codegen for the Thumb2 ISA. */
+#include "arch/instruction_set_features.h"
#include "arm64_lir.h"
#include "codegen_arm64.h"
#include "dex/quick/mir_to_lir-inl.h"
@@ -427,8 +428,7 @@ bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_d
rl_src = LoadValue(rl_src, kCoreReg);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
RegStorage r_long_mul = AllocTemp();
- NewLIR4(kA64Smaddl4xwwx, As64BitReg(r_long_mul).GetReg(),
- r_magic.GetReg(), rl_src.reg.GetReg(), rxzr);
+ NewLIR3(kA64Smull3xww, As64BitReg(r_long_mul).GetReg(), r_magic.GetReg(), rl_src.reg.GetReg());
switch (pattern) {
case Divide3:
OpRegRegImm(kOpLsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul), 32);
@@ -648,7 +648,7 @@ RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegS
}
OpRegRegReg(kOpDiv, temp, r_src1, r_src2);
NewLIR4(kA64Msub4rrrr | wide, rl_result.reg.GetReg(), temp.GetReg(),
- r_src1.GetReg(), r_src2.GetReg());
+ r_src2.GetReg(), r_src1.GetReg());
FreeTemp(temp);
}
return rl_result;
@@ -979,7 +979,9 @@ LIR* Arm64Mir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* tar
}
bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
-#if ANDROID_SMP != 0
+ if (!cu_->GetInstructionSetFeatures()->IsSmp()) {
+ return false;
+ }
// Start off with using the last LIR as the barrier. If it is not enough, then we will generate one.
LIR* barrier = last_lir_insn_;
@@ -1015,9 +1017,6 @@ bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
DCHECK(!barrier->flags.use_def_invalid);
barrier->u.m.def_mask = &kEncodeAll;
return ret;
-#else
- return false;
-#endif
}
void Arm64Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) {
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 9403516641..80cb535307 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -314,6 +314,15 @@ void Mir2Lir::UpdateLIROffsets() {
}
}
+void Mir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) {
+ DCHECK(val_reg.Valid());
+ DCHECK_EQ(val_reg.Is64Bit(), cu_->target64);
+ LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, nullptr);
+ UnconditionallyMarkGCCard(tgt_addr_reg);
+ LIR* target = NewLIR0(kPseudoTargetLabel);
+ branch_over->target = target;
+}
+
/* Dump instructions and constant pool contents */
void Mir2Lir::CodegenDump() {
LOG(INFO) << "Dumping LIR insns for "
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 98ddc36f63..c00f90b6e2 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -416,8 +416,8 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) {
// share array alignment with ints (see comment at head of function)
size_t component_size = sizeof(int32_t);
- // Having a range of 0 is legal
- if (info->is_range && (elems > 0)) {
+ if (elems > 5) {
+ DCHECK(info->is_range); // Non-range insn can't encode more than 5 elems.
/*
* Bit of ugliness here. We're going generate a mem copy loop
* on the register range, but it is possible that some regs
@@ -487,7 +487,11 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) {
OpRegRegImm(kOpAdd, ref_reg, r_dst,
-mirror::Array::DataOffset(component_size).Int32Value());
}
- } else if (!info->is_range) {
+ FreeTemp(r_idx);
+ FreeTemp(r_dst);
+ FreeTemp(r_src);
+ } else {
+ DCHECK_LE(elems, 5); // Usually but not necessarily non-range.
// TUNING: interleave
for (int i = 0; i < elems; i++) {
RegLocation rl_arg;
@@ -507,6 +511,15 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) {
}
}
}
+ if (elems != 0 && info->args[0].ref) {
+ // If there is at least one potentially non-null value, unconditionally mark the GC card.
+ for (int i = 0; i < elems; i++) {
+ if (!mir_graph_->IsConstantNullRef(info->args[i])) {
+ UnconditionallyMarkGCCard(ref_reg);
+ break;
+ }
+ }
+ }
if (info->result.location != kLocInvalid) {
StoreValue(info->result, GetReturn(kRefReg));
}
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 4cb12f1dc9..a7900ae7df 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -476,9 +476,10 @@ static void CommonCallCodeLoadClassIntoArg0(const CallInfo* info, Mir2Lir* cg) {
static bool CommonCallCodeLoadCodePointerIntoInvokeTgt(const RegStorage* alt_from,
const CompilationUnit* cu, Mir2Lir* cg) {
if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
+ int32_t offset = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ InstructionSetPointerSize(cu->instruction_set)).Int32Value();
// Get the compiled code address [use *alt_from or kArg0, set kInvokeTgt]
- cg->LoadWordDisp(alt_from == nullptr ? cg->TargetReg(kArg0, kRef) : *alt_from,
- mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(),
+ cg->LoadWordDisp(alt_from == nullptr ? cg->TargetReg(kArg0, kRef) : *alt_from, offset,
cg->TargetPtrReg(kInvokeTgt));
return true;
}
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index ed73ef0a00..3bb81bf28e 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -222,19 +222,13 @@ void MipsMir2Lir::GenMoveException(RegLocation rl_dest) {
StoreValue(rl_dest, rl_result);
}
-/*
- * Mark garbage collection card. Skip if the value we're storing is null.
- */
-void MipsMir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) {
+void MipsMir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) {
RegStorage reg_card_base = AllocTemp();
RegStorage reg_card_no = AllocTemp();
- LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL);
// NOTE: native pointer.
LoadWordDisp(rs_rMIPS_SELF, Thread::CardTableOffset<4>().Int32Value(), reg_card_base);
OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte);
- LIR* target = NewLIR0(kPseudoTargetLabel);
- branch_over->target = target;
FreeTemp(reg_card_base);
FreeTemp(reg_card_no);
}
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 7e9d80df65..e08846c325 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -49,7 +49,9 @@ class MipsMir2Lir FINAL : public Mir2Lir {
OpSize size) OVERRIDE;
LIR* GenAtomic64Load(RegStorage r_base, int displacement, RegStorage r_dest);
LIR* GenAtomic64Store(RegStorage r_base, int displacement, RegStorage r_src);
- void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg);
+
+ /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage)
+ void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE;
// Required for target - register utilities.
RegStorage Solo64ToPair64(RegStorage reg);
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index 92ef70db7e..70ef991dee 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -201,6 +201,16 @@ void Mir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) {
RegStorage reg_arg_low = GetArgMappingToPhysicalReg(in_position);
RegStorage reg_arg_high = GetArgMappingToPhysicalReg(in_position + 1);
+ if (cu_->instruction_set == kX86) {
+ // Can't handle double split between reg & memory. Flush reg half to memory.
+ if (rl_dest.reg.IsDouble() && (reg_arg_low.Valid() != reg_arg_high.Valid())) {
+ DCHECK(reg_arg_low.Valid());
+ DCHECK(!reg_arg_high.Valid());
+ Store32Disp(TargetPtrReg(kSp), offset, reg_arg_low);
+ reg_arg_low = RegStorage::InvalidReg();
+ }
+ }
+
if (reg_arg_low.Valid() && reg_arg_high.Valid()) {
OpRegCopyWide(rl_dest.reg, RegStorage::MakeRegPair(reg_arg_low, reg_arg_high));
} else if (reg_arg_low.Valid() && !reg_arg_high.Valid()) {
@@ -1238,7 +1248,7 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) {
if (opcode == kMirOpCheck) {
// Combine check and work halves of throwing instruction.
MIR* work_half = mir->meta.throw_insn;
- mir->dalvikInsn.opcode = work_half->dalvikInsn.opcode;
+ mir->dalvikInsn = work_half->dalvikInsn;
mir->optimization_flags = work_half->optimization_flags;
mir->meta = work_half->meta; // Whatever the work_half had, we need to copy it.
opcode = work_half->dalvikInsn.opcode;
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 13ebc1e5d6..886b238ee3 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -1071,6 +1071,13 @@ class Mir2Lir : public Backend {
// Update LIR for verbose listings.
void UpdateLIROffsets();
+ /**
+ * @brief Mark a garbage collection card. Skip if the stored value is null.
+ * @param val_reg the register holding the stored value to check against null.
+ * @param tgt_addr_reg the address of the object or array where the value was stored.
+ */
+ void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg);
+
/*
* @brief Load the address of the dex method into the register.
* @param target_method The MethodReference of the method to be invoked.
@@ -1139,7 +1146,12 @@ class Mir2Lir : public Backend {
OpSize size, VolatileKind is_volatile) = 0;
virtual LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src,
int scale, OpSize size) = 0;
- virtual void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) = 0;
+
+ /**
+ * @brief Unconditionally mark a garbage collection card.
+ * @param tgt_addr_reg the address of the object or array where the value was stored.
+ */
+ virtual void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) = 0;
// Required for target - register utilities.
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index a54c55ffce..8d4cb3c5e9 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -613,7 +613,8 @@ CompiledMethod* QuickCompiler::JniCompile(uint32_t access_flags,
}
uintptr_t QuickCompiler::GetEntryPointOf(mirror::ArtMethod* method) const {
- return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode());
+ return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCodePtrSize(
+ InstructionSetPointerSize(GetCompilerDriver()->GetInstructionSet())));
}
bool QuickCompiler::WriteElf(art::File* file,
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 61dcc28afc..a808459715 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -136,23 +136,16 @@ void X86Mir2Lir::GenMoveException(RegLocation rl_dest) {
StoreValue(rl_dest, rl_result);
}
-/*
- * Mark garbage collection card. Skip if the value we're storing is null.
- */
-void X86Mir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) {
+void X86Mir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) {
DCHECK_EQ(tgt_addr_reg.Is64Bit(), cu_->target64);
- DCHECK_EQ(val_reg.Is64Bit(), cu_->target64);
RegStorage reg_card_base = AllocTempRef();
RegStorage reg_card_no = AllocTempRef();
- LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL);
int ct_offset = cu_->target64 ?
Thread::CardTableOffset<8>().Int32Value() :
Thread::CardTableOffset<4>().Int32Value();
NewLIR2(cu_->target64 ? kX86Mov64RT : kX86Mov32RT, reg_card_base.GetReg(), ct_offset);
OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte);
- LIR* target = NewLIR0(kPseudoTargetLabel);
- branch_over->target = target;
FreeTemp(reg_card_base);
FreeTemp(reg_card_no);
}
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index d57dffb01d..26641f8d59 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -94,7 +94,10 @@ class X86Mir2Lir : public Mir2Lir {
OpSize size, VolatileKind is_volatile) OVERRIDE;
LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale,
OpSize size) OVERRIDE;
- void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) OVERRIDE;
+
+ /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage)
+ void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE;
+
void GenImplicitNullCheck(RegStorage reg, int opt_flags) OVERRIDE;
// Required for target - register utilities.
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index db2f272436..998aeff368 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -18,6 +18,7 @@
#include <inttypes.h>
#include <string>
+#include "arch/instruction_set_features.h"
#include "backend_x86.h"
#include "codegen_x86.h"
#include "dex/compiler_internals.h"
@@ -594,7 +595,9 @@ bool X86Mir2Lir::ProvidesFullMemoryBarrier(X86OpCode opcode) {
}
bool X86Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
-#if ANDROID_SMP != 0
+ if (!cu_->GetInstructionSetFeatures()->IsSmp()) {
+ return false;
+ }
// Start off with using the last LIR as the barrier. If it is not enough, then we will update it.
LIR* mem_barrier = last_lir_insn_;
@@ -630,9 +633,6 @@ bool X86Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
mem_barrier->u.m.def_mask = &kEncodeAll;
}
return ret;
-#else
- return false;
-#endif
}
void X86Mir2Lir::CompilerInitializeRegAlloc() {
@@ -1006,7 +1006,8 @@ LIR* X86Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
call_insn = CallWithLinkerFixup(method_info.GetTargetMethod(), method_info.GetSharpType());
} else {
call_insn = OpMem(kOpBlx, TargetReg(kArg0, kRef),
- mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ cu_->target64 ? 8 : 4).Int32Value());
}
} else {
call_insn = GenInvokeNoInlineCall(this, method_info.GetSharpType());
@@ -2262,7 +2263,8 @@ void X86Mir2Lir::GenReduceVector(MIR* mir) {
StoreFinalValue(rl_dest, rl_result);
} else {
int displacement = SRegOffset(rl_result.s_reg_low);
- LIR *l = NewLIR3(extr_opcode, rs_rX86_SP_32.GetReg(), displacement, vector_src.GetReg());
+ LIR *l = NewLIR4(extr_opcode, rs_rX86_SP_32.GetReg(), displacement, vector_src.GetReg(),
+ extract_index);
AnnotateDalvikRegAccess(l, displacement >> 2, true /* is_load */, is_wide /* is_64bit */);
AnnotateDalvikRegAccess(l, displacement >> 2, false /* is_load */, is_wide /* is_64bit */);
}
diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc
index c1c79caa19..ad3222cd84 100644
--- a/compiler/dex/quick/x86/utility_x86.cc
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -488,6 +488,7 @@ LIR* X86Mir2Lir::OpRegRegReg(OpKind op, RegStorage r_dest, RegStorage r_src1,
case kOpAdc:
case kOpAnd:
case kOpXor:
+ case kOpMul:
break;
default:
LOG(FATAL) << "Bad case in OpRegRegReg " << op;
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index d3d76badd0..ed3388285f 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -126,7 +126,7 @@ bool MIRGraph::FillDefBlockMatrix(BasicBlock* bb) {
for (uint32_t idx : bb->data_flow_info->def_v->Indexes()) {
/* Block bb defines register idx */
- temp_bit_matrix_[idx]->SetBit(bb->id);
+ temp_.ssa.def_block_matrix[idx]->SetBit(bb->id);
}
return true;
}
@@ -135,16 +135,16 @@ void MIRGraph::ComputeDefBlockMatrix() {
int num_registers = GetNumOfCodeAndTempVRs();
/* Allocate num_registers bit vector pointers */
DCHECK(temp_scoped_alloc_ != nullptr);
- DCHECK(temp_bit_matrix_ == nullptr);
- temp_bit_matrix_ = static_cast<ArenaBitVector**>(
+ DCHECK(temp_.ssa.def_block_matrix == nullptr);
+ temp_.ssa.def_block_matrix = static_cast<ArenaBitVector**>(
temp_scoped_alloc_->Alloc(sizeof(ArenaBitVector*) * num_registers, kArenaAllocDFInfo));
int i;
/* Initialize num_register vectors with num_blocks bits each */
for (i = 0; i < num_registers; i++) {
- temp_bit_matrix_[i] = new (temp_scoped_alloc_.get()) ArenaBitVector(arena_, GetNumBlocks(),
- false, kBitMapBMatrix);
- temp_bit_matrix_[i]->ClearAllBits();
+ temp_.ssa.def_block_matrix[i] = new (temp_scoped_alloc_.get()) ArenaBitVector(
+ arena_, GetNumBlocks(), false, kBitMapBMatrix);
+ temp_.ssa.def_block_matrix[i]->ClearAllBits();
}
AllNodesIterator iter(this);
@@ -163,7 +163,7 @@ void MIRGraph::ComputeDefBlockMatrix() {
int num_regs = GetNumOfCodeVRs();
int in_reg = GetFirstInVR();
for (; in_reg < num_regs; in_reg++) {
- temp_bit_matrix_[in_reg]->SetBit(GetEntryBlock()->id);
+ temp_.ssa.def_block_matrix[in_reg]->SetBit(GetEntryBlock()->id);
}
}
@@ -435,32 +435,32 @@ void MIRGraph::ComputeSuccLineIn(ArenaBitVector* dest, const ArenaBitVector* src
* insert a phi node if the variable is live-in to the block.
*/
bool MIRGraph::ComputeBlockLiveIns(BasicBlock* bb) {
- DCHECK_EQ(temp_bit_vector_size_, cu_->mir_graph.get()->GetNumOfCodeAndTempVRs());
- ArenaBitVector* temp_dalvik_register_v = temp_bit_vector_;
+ DCHECK_EQ(temp_.ssa.num_vregs, cu_->mir_graph.get()->GetNumOfCodeAndTempVRs());
+ ArenaBitVector* temp_live_vregs = temp_.ssa.work_live_vregs;
if (bb->data_flow_info == NULL) {
return false;
}
- temp_dalvik_register_v->Copy(bb->data_flow_info->live_in_v);
+ temp_live_vregs->Copy(bb->data_flow_info->live_in_v);
BasicBlock* bb_taken = GetBasicBlock(bb->taken);
BasicBlock* bb_fall_through = GetBasicBlock(bb->fall_through);
if (bb_taken && bb_taken->data_flow_info)
- ComputeSuccLineIn(temp_dalvik_register_v, bb_taken->data_flow_info->live_in_v,
+ ComputeSuccLineIn(temp_live_vregs, bb_taken->data_flow_info->live_in_v,
bb->data_flow_info->def_v);
if (bb_fall_through && bb_fall_through->data_flow_info)
- ComputeSuccLineIn(temp_dalvik_register_v, bb_fall_through->data_flow_info->live_in_v,
+ ComputeSuccLineIn(temp_live_vregs, bb_fall_through->data_flow_info->live_in_v,
bb->data_flow_info->def_v);
if (bb->successor_block_list_type != kNotUsed) {
for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
if (succ_bb->data_flow_info) {
- ComputeSuccLineIn(temp_dalvik_register_v, succ_bb->data_flow_info->live_in_v,
+ ComputeSuccLineIn(temp_live_vregs, succ_bb->data_flow_info->live_in_v,
bb->data_flow_info->def_v);
}
}
}
- if (!temp_dalvik_register_v->Equal(bb->data_flow_info->live_in_v)) {
- bb->data_flow_info->live_in_v->Copy(temp_dalvik_register_v);
+ if (!temp_live_vregs->Equal(bb->data_flow_info->live_in_v)) {
+ bb->data_flow_info->live_in_v->Copy(temp_live_vregs);
return true;
}
return false;
@@ -482,7 +482,7 @@ void MIRGraph::InsertPhiNodes() {
/* Iterate through each Dalvik register */
for (dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) {
- input_blocks->Copy(temp_bit_matrix_[dalvik_reg]);
+ input_blocks->Copy(temp_.ssa.def_block_matrix[dalvik_reg]);
phi_blocks->ClearAllBits();
do {
// TUNING: When we repeat this, we could skip indexes from the previous pass.
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 1805d59961..ebf7874dcf 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -240,7 +240,8 @@ inline int CompilerDriver::IsFastInvoke(
bool can_sharpen_super_based_on_type = (*invoke_type == kSuper) &&
(referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) &&
resolved_method->GetMethodIndex() < methods_class->GetVTableLength() &&
- (methods_class->GetVTableEntry(resolved_method->GetMethodIndex()) == resolved_method);
+ (methods_class->GetVTableEntry(resolved_method->GetMethodIndex()) == resolved_method) &&
+ !resolved_method->IsAbstract();
if (can_sharpen_virtual_based_on_type || can_sharpen_super_based_on_type) {
// Sharpen a virtual call into a direct call. The method_idx is into referrer's
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 7e2be3ee0c..dac1ef4c06 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -105,13 +105,16 @@ TEST_F(ImageTest, WriteRead) {
ASSERT_TRUE(success_image);
bool success_fixup = ElfWriter::Fixup(dup_oat.get(), writer->GetOatDataBegin());
ASSERT_TRUE(success_fixup);
+
+ ASSERT_EQ(dup_oat->FlushCloseOrErase(), 0) << "Could not flush and close oat file "
+ << oat_file.GetFilename();
}
{
std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str()));
ASSERT_TRUE(file.get() != NULL);
ImageHeader image_header;
- file->ReadFully(&image_header, sizeof(image_header));
+ ASSERT_EQ(file->ReadFully(&image_header, sizeof(image_header)), true);
ASSERT_TRUE(image_header.IsValid());
ASSERT_GE(image_header.GetImageBitmapOffset(), sizeof(image_header));
ASSERT_NE(0U, image_header.GetImageBitmapSize());
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index b7283a4e19..4f5026dee3 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -54,7 +54,8 @@
#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "handle_scope-inl.h"
-#include "utils.h"
+
+#include <numeric>
using ::art::mirror::ArtField;
using ::art::mirror::ArtMethod;
@@ -67,12 +68,15 @@ using ::art::mirror::String;
namespace art {
+// Separate objects into multiple bins to optimize dirty memory use.
+static constexpr bool kBinObjects = true;
+
bool ImageWriter::PrepareImageAddressSpace() {
+ target_ptr_size_ = InstructionSetPointerSize(compiler_driver_.GetInstructionSet());
{
Thread::Current()->TransitionFromSuspendedToRunnable();
PruneNonImageClasses(); // Remove junk
ComputeLazyFieldsForImageClasses(); // Add useful information
- ComputeEagerResolvedStrings();
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
}
gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -149,6 +153,11 @@ bool ImageWriter::Write(const std::string& image_filename,
SetOatChecksumFromElfFile(oat_file.get());
+ if (oat_file->FlushCloseOrErase() != 0) {
+ LOG(ERROR) << "Failed to flush and close oat file " << oat_filename << " for " << oat_location;
+ return false;
+ }
+
std::unique_ptr<File> image_file(OS::CreateEmptyFile(image_filename.c_str()));
ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
if (image_file.get() == NULL) {
@@ -157,6 +166,7 @@ bool ImageWriter::Write(const std::string& image_filename,
}
if (fchmod(image_file->Fd(), 0644) != 0) {
PLOG(ERROR) << "Failed to make image file world readable: " << image_filename;
+ image_file->Erase();
return EXIT_FAILURE;
}
@@ -164,6 +174,7 @@ bool ImageWriter::Write(const std::string& image_filename,
CHECK_EQ(image_end_, image_header->GetImageSize());
if (!image_file->WriteFully(image_->Begin(), image_end_)) {
PLOG(ERROR) << "Failed to write image file " << image_filename;
+ image_file->Erase();
return false;
}
@@ -173,64 +184,237 @@ bool ImageWriter::Write(const std::string& image_filename,
image_header->GetImageBitmapSize(),
image_header->GetImageBitmapOffset())) {
PLOG(ERROR) << "Failed to write image file " << image_filename;
+ image_file->Erase();
return false;
}
+ if (image_file->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close image file " << image_filename;
+ return false;
+ }
return true;
}
-void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) {
+void ImageWriter::SetImageOffset(mirror::Object* object,
+ ImageWriter::BinSlot bin_slot,
+ size_t offset) {
DCHECK(object != nullptr);
DCHECK_NE(offset, 0U);
- DCHECK(!IsImageOffsetAssigned(object));
mirror::Object* obj = reinterpret_cast<mirror::Object*>(image_->Begin() + offset);
DCHECK_ALIGNED(obj, kObjectAlignment);
- image_bitmap_->Set(obj);
+
+ image_bitmap_->Set(obj); // Mark the obj as mutated, since we will end up changing it.
+ {
+ // Remember the object-inside-of-the-image's hash code so we can restore it after the copy.
+ auto hash_it = saved_hashes_map_.find(bin_slot);
+ if (hash_it != saved_hashes_map_.end()) {
+ std::pair<BinSlot, uint32_t> slot_hash = *hash_it;
+ saved_hashes_.push_back(std::make_pair(obj, slot_hash.second));
+ saved_hashes_map_.erase(hash_it);
+ }
+ }
+ // The object is already deflated from when we set the bin slot. Just overwrite the lock word.
+ object->SetLockWord(LockWord::FromForwardingAddress(offset), false);
+ DCHECK(IsImageOffsetAssigned(object));
+}
+
+void ImageWriter::AssignImageOffset(mirror::Object* object, ImageWriter::BinSlot bin_slot) {
+ DCHECK(object != nullptr);
+ DCHECK_NE(image_objects_offset_begin_, 0u);
+
+ size_t previous_bin_sizes = GetBinSizeSum(bin_slot.GetBin()); // sum sizes in [0..bin#)
+ size_t new_offset = image_objects_offset_begin_ + previous_bin_sizes + bin_slot.GetIndex();
+ DCHECK_ALIGNED(new_offset, kObjectAlignment);
+
+ SetImageOffset(object, bin_slot, new_offset);
+ DCHECK_LT(new_offset, image_end_);
+}
+
+bool ImageWriter::IsImageOffsetAssigned(mirror::Object* object) const {
+ // Will also return true if the bin slot was assigned since we are reusing the lock word.
+ DCHECK(object != nullptr);
+ return object->GetLockWord(false).GetState() == LockWord::kForwardingAddress;
+}
+
+size_t ImageWriter::GetImageOffset(mirror::Object* object) const {
+ DCHECK(object != nullptr);
+ DCHECK(IsImageOffsetAssigned(object));
+ LockWord lock_word = object->GetLockWord(false);
+ size_t offset = lock_word.ForwardingAddress();
+ DCHECK_LT(offset, image_end_);
+ return offset;
+}
+
+void ImageWriter::SetImageBinSlot(mirror::Object* object, BinSlot bin_slot) {
+ DCHECK(object != nullptr);
+ DCHECK(!IsImageOffsetAssigned(object));
+ DCHECK(!IsImageBinSlotAssigned(object));
+
// Before we stomp over the lock word, save the hash code for later.
Monitor::Deflate(Thread::Current(), object);;
LockWord lw(object->GetLockWord(false));
switch (lw.GetState()) {
case LockWord::kFatLocked: {
- LOG(FATAL) << "Fat locked object " << obj << " found during object copy";
+ LOG(FATAL) << "Fat locked object " << object << " found during object copy";
break;
}
case LockWord::kThinLocked: {
- LOG(FATAL) << "Thin locked object " << obj << " found during object copy";
+ LOG(FATAL) << "Thin locked object " << object << " found during object copy";
break;
}
case LockWord::kUnlocked:
// No hash, don't need to save it.
break;
case LockWord::kHashCode:
- saved_hashes_.push_back(std::make_pair(obj, lw.GetHashCode()));
+ saved_hashes_map_[bin_slot] = lw.GetHashCode();
break;
default:
LOG(FATAL) << "Unreachable.";
UNREACHABLE();
}
- object->SetLockWord(LockWord::FromForwardingAddress(offset), false);
- DCHECK(IsImageOffsetAssigned(object));
+ object->SetLockWord(LockWord::FromForwardingAddress(static_cast<uint32_t>(bin_slot)),
+ false);
+ DCHECK(IsImageBinSlotAssigned(object));
}
-void ImageWriter::AssignImageOffset(mirror::Object* object) {
+void ImageWriter::AssignImageBinSlot(mirror::Object* object) {
DCHECK(object != nullptr);
- SetImageOffset(object, image_end_);
- image_end_ += RoundUp(object->SizeOf(), 8); // 64-bit alignment
+ size_t object_size;
+ if (object->IsArtMethod()) {
+ // Methods are sized based on the target pointer size.
+ object_size = mirror::ArtMethod::InstanceSize(target_ptr_size_);
+ } else {
+ object_size = object->SizeOf();
+ }
+
+ // The magic happens here. We segregate objects into different bins based
+ // on how likely they are to get dirty at runtime.
+ //
+ // Likely-to-dirty objects get packed together into the same bin so that
+ // at runtime their page dirtiness ratio (how many dirty objects a page has) is
+ // maximized.
+ //
+ // This means more pages will stay either clean or shared dirty (with zygote) and
+ // the app will use less of its own (private) memory.
+ Bin bin = kBinRegular;
+
+ if (kBinObjects) {
+ //
+ // Changing the bin of an object is purely a memory-use tuning.
+ // It has no change on runtime correctness.
+ //
+ // Memory analysis has determined that the following types of objects get dirtied
+ // the most:
+ //
+ // * Class'es which are verified [their clinit runs only at runtime]
+ // - classes in general [because their static fields get overwritten]
+ // - initialized classes with all-final statics are unlikely to be ever dirty,
+ // so bin them separately
+ // * Art Methods that are:
+ // - native [their native entry point is not looked up until runtime]
+ // - have declaring classes that aren't initialized
+ // [their interpreter/quick entry points are trampolines until the class
+ // becomes initialized]
+ //
+ // We also assume the following objects get dirtied either never or extremely rarely:
+ // * Strings (they are immutable)
+ // * Art methods that aren't native and have initialized declared classes
+ //
+ // We assume that "regular" bin objects are highly unlikely to become dirtied,
+ // so packing them together will not result in a noticeably tighter dirty-to-clean ratio.
+ //
+ if (object->IsClass()) {
+ bin = kBinClassVerified;
+ mirror::Class* klass = object->AsClass();
+
+ if (klass->GetStatus() == Class::kStatusInitialized) {
+ bin = kBinClassInitialized;
+
+ // If the class's static fields are all final, put it into a separate bin
+ // since it's very likely it will stay clean.
+ uint32_t num_static_fields = klass->NumStaticFields();
+ if (num_static_fields == 0) {
+ bin = kBinClassInitializedFinalStatics;
+ } else {
+ // Maybe all the statics are final?
+ bool all_final = true;
+ for (uint32_t i = 0; i < num_static_fields; ++i) {
+ ArtField* field = klass->GetStaticField(i);
+ if (!field->IsFinal()) {
+ all_final = false;
+ break;
+ }
+ }
+
+ if (all_final) {
+ bin = kBinClassInitializedFinalStatics;
+ }
+ }
+ }
+ } else if (object->IsArtMethod<kVerifyNone>()) {
+ mirror::ArtMethod* art_method = down_cast<ArtMethod*>(object);
+ if (art_method->IsNative()) {
+ bin = kBinArtMethodNative;
+ } else {
+ mirror::Class* declaring_class = art_method->GetDeclaringClass();
+ if (declaring_class->GetStatus() != Class::kStatusInitialized) {
+ bin = kBinArtMethodNotInitialized;
+ } else {
+ // This is highly unlikely to dirty since there's no entry points to mutate.
+ bin = kBinArtMethodsManagedInitialized;
+ }
+ }
+ } else if (object->GetClass<kVerifyNone>()->IsStringClass()) {
+ bin = kBinString; // Strings are almost always immutable (except for object header).
+ } // else bin = kBinRegular
+ }
+
+ size_t current_offset = bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned).
+ // Move the current bin size up to accomodate the object we just assigned a bin slot.
+ size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment
+ bin_slot_sizes_[bin] += offset_delta;
+
+ BinSlot new_bin_slot(bin, current_offset);
+ SetImageBinSlot(object, new_bin_slot);
+
+ ++bin_slot_count_[bin];
+
+ DCHECK_LT(GetBinSizeSum(), image_->Size());
+
+ // Grow the image closer to the end by the object we just assigned.
+ image_end_ += offset_delta;
DCHECK_LT(image_end_, image_->Size());
}
-bool ImageWriter::IsImageOffsetAssigned(mirror::Object* object) const {
+bool ImageWriter::IsImageBinSlotAssigned(mirror::Object* object) const {
DCHECK(object != nullptr);
- return object->GetLockWord(false).GetState() == LockWord::kForwardingAddress;
+
+ // We always stash the bin slot into a lockword, in the 'forwarding address' state.
+ // If it's in some other state, then we haven't yet assigned an image bin slot.
+ if (object->GetLockWord(false).GetState() != LockWord::kForwardingAddress) {
+ return false;
+ } else if (kIsDebugBuild) {
+ LockWord lock_word = object->GetLockWord(false);
+ size_t offset = lock_word.ForwardingAddress();
+ BinSlot bin_slot(offset);
+ DCHECK_LT(bin_slot.GetIndex(), bin_slot_sizes_[bin_slot.GetBin()])
+ << "bin slot offset should not exceed the size of that bin";
+ }
+ return true;
}
-size_t ImageWriter::GetImageOffset(mirror::Object* object) const {
+ImageWriter::BinSlot ImageWriter::GetImageBinSlot(mirror::Object* object) const {
DCHECK(object != nullptr);
- DCHECK(IsImageOffsetAssigned(object));
+ DCHECK(IsImageBinSlotAssigned(object));
+
LockWord lock_word = object->GetLockWord(false);
- size_t offset = lock_word.ForwardingAddress();
- DCHECK_LT(offset, image_end_);
- return offset;
+ size_t offset = lock_word.ForwardingAddress(); // TODO: ForwardingAddress should be uint32_t
+ DCHECK_LE(offset, std::numeric_limits<uint32_t>::max());
+
+ BinSlot bin_slot(static_cast<uint32_t>(offset));
+ DCHECK_LT(bin_slot.GetIndex(), bin_slot_sizes_[bin_slot.GetBin()]);
+
+ return bin_slot;
}
bool ImageWriter::AllocMemory() {
@@ -265,6 +449,149 @@ bool ImageWriter::ComputeLazyFieldsForClassesVisitor(Class* c, void* /*arg*/) {
return true;
}
+// Count the number of strings in the heap and put the result in arg as a size_t pointer.
+static void CountStringsCallback(Object* obj, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (obj->GetClass()->IsStringClass()) {
+ ++*reinterpret_cast<size_t*>(arg);
+ }
+}
+
+// Collect all the java.lang.String in the heap and put them in the output strings_ array.
+class StringCollector {
+ public:
+ StringCollector(Handle<mirror::ObjectArray<mirror::String>> strings, size_t index)
+ : strings_(strings), index_(index) {
+ }
+ static void Callback(Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ auto* collector = reinterpret_cast<StringCollector*>(arg);
+ if (obj->GetClass()->IsStringClass()) {
+ collector->strings_->SetWithoutChecks<false>(collector->index_++, obj->AsString());
+ }
+ }
+ size_t GetIndex() const {
+ return index_;
+ }
+
+ private:
+ Handle<mirror::ObjectArray<mirror::String>> strings_;
+ size_t index_;
+};
+
+// Compare strings based on length, used for sorting strings by length / reverse length.
+class StringLengthComparator {
+ public:
+ explicit StringLengthComparator(Handle<mirror::ObjectArray<mirror::String>> strings)
+ : strings_(strings) {
+ }
+ bool operator()(size_t a, size_t b) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return strings_->GetWithoutChecks(a)->GetLength() < strings_->GetWithoutChecks(b)->GetLength();
+ }
+
+ private:
+ Handle<mirror::ObjectArray<mirror::String>> strings_;
+};
+
+// Normal string < comparison through the chars_ array.
+class SubstringComparator {
+ public:
+ explicit SubstringComparator(const std::vector<uint16_t>* const chars) : chars_(chars) {
+ }
+ bool operator()(const std::pair<size_t, size_t>& a, const std::pair<size_t, size_t>& b) {
+ return std::lexicographical_compare(chars_->begin() + a.first,
+ chars_->begin() + a.first + a.second,
+ chars_->begin() + b.first,
+ chars_->begin() + b.first + b.second);
+ }
+
+ private:
+ const std::vector<uint16_t>* const chars_;
+};
+
+void ImageWriter::ProcessStrings() {
+ size_t total_strings = 0;
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ {
+ ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+ heap->VisitObjects(CountStringsCallback, &total_strings); // Count the strings.
+ }
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ auto strings = hs.NewHandle(cl->AllocStringArray(self, total_strings));
+ StringCollector string_collector(strings, 0U);
+ {
+ ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+ // Read strings into the array.
+ heap->VisitObjects(StringCollector::Callback, &string_collector);
+ }
+ // Some strings could have gotten freed if AllocStringArray caused a GC.
+ CHECK_LE(string_collector.GetIndex(), total_strings);
+ total_strings = string_collector.GetIndex();
+ size_t total_length = 0;
+ std::vector<size_t> reverse_sorted_strings;
+ for (size_t i = 0; i < total_strings; ++i) {
+ mirror::String* s = strings->GetWithoutChecks(i);
+ // Look up the string in the array.
+ total_length += s->GetLength();
+ reverse_sorted_strings.push_back(i);
+ }
+ // Sort by reverse length.
+ StringLengthComparator comparator(strings);
+ std::sort(reverse_sorted_strings.rbegin(), reverse_sorted_strings.rend(), comparator);
+ // Deduplicate prefixes and add strings to the char array.
+ std::vector<uint16_t> combined_chars(total_length, 0U);
+ size_t num_chars = 0;
+ // Characters of strings which are non equal prefix of another string (not the same string).
+ // We don't count the savings from equal strings since these would get interned later anyways.
+ size_t prefix_saved_chars = 0;
+ std::set<std::pair<size_t, size_t>, SubstringComparator> existing_strings((
+ SubstringComparator(&combined_chars)));
+ for (size_t i = 0; i < total_strings; ++i) {
+ mirror::String* s = strings->GetWithoutChecks(reverse_sorted_strings[i]);
+ // Add the string to the end of the char array.
+ size_t length = s->GetLength();
+ for (size_t j = 0; j < length; ++j) {
+ combined_chars[num_chars++] = s->CharAt(j);
+ }
+ // Try to see if the string exists as a prefix of an existing string.
+ size_t new_offset = 0;
+ std::pair<size_t, size_t> new_string(num_chars - length, length);
+ auto it = existing_strings.lower_bound(new_string);
+ bool is_prefix = false;
+ if (it != existing_strings.end()) {
+ CHECK_LE(length, it->second);
+ is_prefix = std::equal(combined_chars.begin() + it->first,
+ combined_chars.begin() + it->first + it->second,
+ combined_chars.begin() + new_string.first);
+ }
+ if (is_prefix) {
+ // Shares a prefix, set the offset to where the new offset will be.
+ new_offset = it->first;
+ // Remove the added chars.
+ num_chars -= length;
+ if (it->second != length) {
+ prefix_saved_chars += length;
+ }
+ } else {
+ new_offset = new_string.first;
+ existing_strings.insert(new_string);
+ }
+ s->SetOffset(new_offset);
+ }
+ // Allocate and update the char arrays.
+ auto* array = mirror::CharArray::Alloc(self, num_chars);
+ for (size_t i = 0; i < num_chars; ++i) {
+ array->SetWithoutChecks<false>(i, combined_chars[i]);
+ }
+ for (size_t i = 0; i < total_strings; ++i) {
+ strings->GetWithoutChecks(i)->SetArray(array);
+ }
+ LOG(INFO) << "Total # image strings=" << total_strings << " combined length="
+ << total_length << " prefix saved chars=" << prefix_saved_chars;
+ ComputeEagerResolvedStrings();
+}
+
void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg ATTRIBUTE_UNUSED) {
if (!obj->GetClass()->IsStringClass()) {
return;
@@ -293,7 +620,7 @@ void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg ATT
}
}
-void ImageWriter::ComputeEagerResolvedStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void ImageWriter::ComputeEagerResolvedStrings() {
ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
Runtime::Current()->GetHeap()->VisitObjects(ComputeEagerResolvedStringsCallback, this);
}
@@ -364,8 +691,7 @@ bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) {
return true;
}
-void ImageWriter::CheckNonImageClassesRemoved()
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void ImageWriter::CheckNonImageClassesRemoved() {
if (compiler_driver_.GetImageClasses() != nullptr) {
gc::Heap* heap = Runtime::Current()->GetHeap();
ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
@@ -394,29 +720,29 @@ void ImageWriter::DumpImageClasses() {
}
}
-void ImageWriter::CalculateObjectOffsets(Object* obj) {
+void ImageWriter::CalculateObjectBinSlots(Object* obj) {
DCHECK(obj != NULL);
// if it is a string, we want to intern it if its not interned.
if (obj->GetClass()->IsStringClass()) {
// we must be an interned string that was forward referenced and already assigned
- if (IsImageOffsetAssigned(obj)) {
+ if (IsImageBinSlotAssigned(obj)) {
DCHECK_EQ(obj, obj->AsString()->Intern());
return;
}
mirror::String* const interned = obj->AsString()->Intern();
if (obj != interned) {
- if (!IsImageOffsetAssigned(interned)) {
+ if (!IsImageBinSlotAssigned(interned)) {
// interned obj is after us, allocate its location early
- AssignImageOffset(interned);
+ AssignImageBinSlot(interned);
}
// point those looking for this object to the interned version.
- SetImageOffset(obj, GetImageOffset(interned));
+ SetImageBinSlot(obj, GetImageBinSlot(interned));
return;
}
// else (obj == interned), nothing to do but fall through to the normal case
}
- AssignImageOffset(obj);
+ AssignImageBinSlot(obj);
}
ObjectArray<Object>* ImageWriter::CreateImageRoots() const {
@@ -497,13 +823,15 @@ void ImageWriter::WalkInstanceFields(mirror::Object* obj, mirror::Class* klass)
// For an unvisited object, visit it then all its children found via fields.
void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) {
- if (!IsImageOffsetAssigned(obj)) {
+ // Use our own visitor routine (instead of GC visitor) to get better locality between
+ // an object and its fields
+ if (!IsImageBinSlotAssigned(obj)) {
// Walk instance fields of all objects
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::Object> h_obj(hs.NewHandle(obj));
Handle<mirror::Class> klass(hs.NewHandle(obj->GetClass()));
// visit the object itself.
- CalculateObjectOffsets(h_obj.Get());
+ CalculateObjectBinSlots(h_obj.Get());
WalkInstanceFields(h_obj.Get(), klass.Get());
// Walk static fields of a Class.
if (h_obj->IsClass()) {
@@ -537,6 +865,24 @@ void ImageWriter::WalkFieldsCallback(mirror::Object* obj, void* arg) {
writer->WalkFieldsInOrder(obj);
}
+void ImageWriter::UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg) {
+ ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg);
+ DCHECK(writer != nullptr);
+ writer->UnbinObjectsIntoOffset(obj);
+}
+
+void ImageWriter::UnbinObjectsIntoOffset(mirror::Object* obj) {
+ CHECK(obj != nullptr);
+
+ // We know the bin slot, and the total bin sizes for all objects by now,
+ // so calculate the object's final image offset.
+
+ DCHECK(IsImageBinSlotAssigned(obj));
+ BinSlot bin_slot = GetImageBinSlot(obj);
+ // Change the lockword from a bin slot into an offset
+ AssignImageOffset(obj, bin_slot);
+}
+
void ImageWriter::CalculateNewObjectOffsets() {
Thread* self = Thread::Current();
StackHandleScope<1> hs(self);
@@ -547,16 +893,22 @@ void ImageWriter::CalculateNewObjectOffsets() {
// Leave space for the header, but do not write it yet, we need to
// know where image_roots is going to end up
- image_end_ += RoundUp(sizeof(ImageHeader), 8); // 64-bit-alignment
+ image_end_ += RoundUp(sizeof(ImageHeader), kObjectAlignment); // 64-bit-alignment
{
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
// TODO: Image spaces only?
DCHECK_LT(image_end_, image_->Size());
- // Clear any pre-existing monitors which may have been in the monitor words.
+ image_objects_offset_begin_ = image_end_;
+ // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
heap->VisitObjects(WalkFieldsCallback, this);
+ // Transform each object's bin slot into an offset which will be used to do the final copy.
+ heap->VisitObjects(UnbinObjectsIntoOffsetCallback, this);
+ DCHECK(saved_hashes_map_.empty()); // All binslot hashes should've been put into vector by now.
}
+ DCHECK_GT(image_end_, GetBinSizeSum());
+
image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots.Get()));
// Note that image_end_ is left at end of used space
@@ -566,6 +918,7 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) {
CHECK_NE(0U, oat_loaded_size);
const uint8_t* oat_file_begin = GetOatFileBegin();
const uint8_t* oat_file_end = oat_file_begin + oat_loaded_size;
+
oat_data_begin_ = oat_file_begin + oat_data_offset;
const uint8_t* oat_data_end = oat_data_begin_ + oat_file_->Size();
@@ -587,9 +940,7 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) {
compile_pic_);
}
-
-void ImageWriter::CopyAndFixupObjects()
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void ImageWriter::CopyAndFixupObjects() {
ScopedAssertNoThreadSuspension ants(Thread::Current(), "ImageWriter");
gc::Heap* heap = Runtime::Current()->GetHeap();
// TODO: heap validation can't handle this fix up pass
@@ -612,7 +963,14 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) {
size_t offset = image_writer->GetImageOffset(obj);
uint8_t* dst = image_writer->image_->Begin() + offset;
const uint8_t* src = reinterpret_cast<const uint8_t*>(obj);
- size_t n = obj->SizeOf();
+ size_t n;
+ if (obj->IsArtMethod()) {
+ // Size without pointer fields since we don't want to overrun the buffer if target art method
+ // is 32 bits but source is 64 bits.
+ n = mirror::ArtMethod::SizeWithoutPointerFields();
+ } else {
+ n = obj->SizeOf();
+ }
DCHECK_LT(offset + n, image_writer->image_->Size());
memcpy(dst, src, n);
Object* copy = reinterpret_cast<Object*>(dst);
@@ -622,6 +980,7 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) {
image_writer->FixupObject(obj, copy);
}
+// Rewrite all the references in the copied object to point to their image address equivalent
class FixupVisitor {
public:
FixupVisitor(ImageWriter* image_writer, Object* copy) : image_writer_(image_writer), copy_(copy) {
@@ -657,8 +1016,9 @@ class FixupClassVisitor FINAL : public FixupVisitor {
void operator()(Object* obj, MemberOffset offset, bool /*is_static*/) const
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
DCHECK(obj->IsClass());
- FixupVisitor::operator()(obj, offset, false);
+ FixupVisitor::operator()(obj, offset, /*is_static*/false);
+ // TODO: Remove dead code
if (offset.Uint32Value() < mirror::Class::EmbeddedVTableOffset().Uint32Value()) {
return;
}
@@ -692,6 +1052,10 @@ void ImageWriter::FixupObject(Object* orig, Object* copy) {
}
if (orig->IsArtMethod<kVerifyNone>()) {
FixupMethod(orig->AsArtMethod<kVerifyNone>(), down_cast<ArtMethod*>(copy));
+ } else if (orig->IsClass() && orig->AsClass()->IsArtMethodClass()) {
+ // Set the right size for the target.
+ size_t size = mirror::ArtMethod::InstanceSize(target_ptr_size_);
+ down_cast<mirror::Class*>(copy)->SetObjectSizeWithoutChecks(size);
}
}
@@ -750,29 +1114,48 @@ const uint8_t* ImageWriter::GetQuickEntryPoint(mirror::ArtMethod* method) {
void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) {
// OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
// oat_begin_
+ // For 64 bit targets we need to repack the current runtime pointer sized fields to the right
+ // locations.
+ // Copy all of the fields from the runtime methods to the target methods first since we did a
+ // bytewise copy earlier.
+ copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
+ orig->GetEntryPointFromPortableCompiledCode(), target_ptr_size_);
+ copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(orig->GetEntryPointFromInterpreter(),
+ target_ptr_size_);
+ copy->SetEntryPointFromJniPtrSize<kVerifyNone>(orig->GetEntryPointFromJni(), target_ptr_size_);
+ copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
+ orig->GetEntryPointFromQuickCompiledCode(), target_ptr_size_);
+ copy->SetNativeGcMapPtrSize<kVerifyNone>(orig->GetNativeGcMap(), target_ptr_size_);
// The resolution method has a special trampoline to call.
Runtime* runtime = Runtime::Current();
if (UNLIKELY(orig == runtime->GetResolutionMethod())) {
- copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_resolution_trampoline_offset_));
- copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_));
+ copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
+ GetOatAddress(portable_resolution_trampoline_offset_), target_ptr_size_);
+ copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
+ GetOatAddress(quick_resolution_trampoline_offset_), target_ptr_size_);
} else if (UNLIKELY(orig == runtime->GetImtConflictMethod() ||
orig == runtime->GetImtUnimplementedMethod())) {
- copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_imt_conflict_trampoline_offset_));
- copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_imt_conflict_trampoline_offset_));
+ copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
+ GetOatAddress(portable_imt_conflict_trampoline_offset_), target_ptr_size_);
+ copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
+ GetOatAddress(quick_imt_conflict_trampoline_offset_), target_ptr_size_);
} else {
// We assume all methods have code. If they don't currently then we set them to the use the
// resolution trampoline. Abstract methods never have code and so we need to make sure their
// use results in an AbstractMethodError. We use the interpreter to achieve this.
if (UNLIKELY(orig->IsAbstract())) {
- copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_to_interpreter_bridge_offset_));
- copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_to_interpreter_bridge_offset_));
- copy->SetEntryPointFromInterpreter<kVerifyNone>(reinterpret_cast<EntryPointFromInterpreter*>
- (const_cast<uint8_t*>(GetOatAddress(interpreter_to_interpreter_bridge_offset_))));
+ copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
+ GetOatAddress(portable_to_interpreter_bridge_offset_), target_ptr_size_);
+ copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(
+ GetOatAddress(quick_to_interpreter_bridge_offset_), target_ptr_size_);
+ copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(
+ reinterpret_cast<EntryPointFromInterpreter*>(const_cast<uint8_t*>(
+ GetOatAddress(interpreter_to_interpreter_bridge_offset_))), target_ptr_size_);
} else {
bool quick_is_interpreted;
const uint8_t* quick_code = GetQuickCode(orig, &quick_is_interpreted);
- copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(quick_code);
+ copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(quick_code, target_ptr_size_);
// Portable entrypoint:
const uint8_t* portable_code = GetOatAddress(orig->GetPortableOatCodeOffset());
@@ -795,18 +1178,19 @@ void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) {
// initialization.
portable_code = GetOatAddress(portable_resolution_trampoline_offset_);
}
- copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(portable_code);
-
+ copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(
+ portable_code, target_ptr_size_);
// JNI entrypoint:
if (orig->IsNative()) {
// The native method's pointer is set to a stub to lookup via dlsym.
// Note this is not the code_ pointer, that is handled above.
- copy->SetNativeMethod<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_));
+ copy->SetEntryPointFromJniPtrSize<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_),
+ target_ptr_size_);
} else {
// Normal (non-abstract non-native) methods have various tables to relocate.
uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset();
const uint8_t* native_gc_map = GetOatAddress(native_gc_map_offset);
- copy->SetNativeGcMap<kVerifyNone>(reinterpret_cast<const uint8_t*>(native_gc_map));
+ copy->SetNativeGcMapPtrSize<kVerifyNone>(native_gc_map, target_ptr_size_);
}
// Interpreter entrypoint:
@@ -814,9 +1198,11 @@ void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) {
uint32_t interpreter_code = (quick_is_interpreted && portable_is_interpreted)
? interpreter_to_interpreter_bridge_offset_
: interpreter_to_compiled_code_bridge_offset_;
- copy->SetEntryPointFromInterpreter<kVerifyNone>(
+ EntryPointFromInterpreter* interpreter_entrypoint =
reinterpret_cast<EntryPointFromInterpreter*>(
- const_cast<uint8_t*>(GetOatAddress(interpreter_code))));
+ const_cast<uint8_t*>(GetOatAddress(interpreter_code)));
+ copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(
+ interpreter_entrypoint, target_ptr_size_);
}
}
}
@@ -846,4 +1232,32 @@ void ImageWriter::SetOatChecksumFromElfFile(File* elf_file) {
image_header->SetOatChecksum(oat_header->GetChecksum());
}
+size_t ImageWriter::GetBinSizeSum(ImageWriter::Bin up_to) const {
+ DCHECK_LE(up_to, kBinSize);
+ return std::accumulate(&bin_slot_sizes_[0], &bin_slot_sizes_[up_to], /*init*/0);
+}
+
+ImageWriter::BinSlot::BinSlot(uint32_t lockword) : lockword_(lockword) {
+ // These values may need to get updated if more bins are added to the enum Bin
+ static_assert(kBinBits == 3, "wrong number of bin bits");
+ static_assert(kBinShift == 29, "wrong number of shift");
+ static_assert(sizeof(BinSlot) == sizeof(LockWord), "BinSlot/LockWord must have equal sizes");
+
+ DCHECK_LT(GetBin(), kBinSize);
+ DCHECK_ALIGNED(GetIndex(), kObjectAlignment);
+}
+
+ImageWriter::BinSlot::BinSlot(Bin bin, uint32_t index)
+ : BinSlot(index | (static_cast<uint32_t>(bin) << kBinShift)) {
+ DCHECK_EQ(index, GetIndex());
+}
+
+ImageWriter::Bin ImageWriter::BinSlot::GetBin() const {
+ return static_cast<Bin>((lockword_ & kBinMask) >> kBinShift);
+}
+
+uint32_t ImageWriter::BinSlot::GetIndex() const {
+ return lockword_ & ~kBinMask;
+}
+
} // namespace art
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index b0cf2b20ad..8c84b686fa 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -23,15 +23,18 @@
#include <memory>
#include <set>
#include <string>
+#include <ostream>
#include "base/macros.h"
#include "driver/compiler_driver.h"
+#include "gc/space/space.h"
#include "mem_map.h"
#include "oat_file.h"
#include "mirror/dex_cache.h"
#include "os.h"
#include "safe_map.h"
#include "gc/space/space.h"
+#include "utils.h"
namespace art {
@@ -41,13 +44,15 @@ class ImageWriter FINAL {
ImageWriter(const CompilerDriver& compiler_driver, uintptr_t image_begin,
bool compile_pic)
: compiler_driver_(compiler_driver), image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
- image_end_(0), image_roots_address_(0), oat_file_(nullptr),
+ image_end_(0), image_objects_offset_begin_(0), image_roots_address_(0), oat_file_(nullptr),
oat_data_begin_(nullptr), interpreter_to_interpreter_bridge_offset_(0),
interpreter_to_compiled_code_bridge_offset_(0), jni_dlsym_lookup_offset_(0),
portable_imt_conflict_trampoline_offset_(0), portable_resolution_trampoline_offset_(0),
portable_to_interpreter_bridge_offset_(0), quick_generic_jni_trampoline_offset_(0),
quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0),
- quick_to_interpreter_bridge_offset_(0), compile_pic_(compile_pic) {
+ quick_to_interpreter_bridge_offset_(0), compile_pic_(compile_pic),
+ target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())),
+ bin_slot_sizes_(), bin_slot_count_() {
CHECK_NE(image_begin, 0U);
}
@@ -86,14 +91,71 @@ class ImageWriter FINAL {
// Mark the objects defined in this space in the given live bitmap.
void RecordImageAllocations() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Classify different kinds of bins that objects end up getting packed into during image writing.
+ enum Bin {
+ // Likely-clean:
+ kBinString, // [String] Almost always immutable (except for obj header).
+ kBinArtMethodsManagedInitialized, // [ArtMethod] Not-native, and initialized. Unlikely to dirty
+ // Unknown mix of clean/dirty:
+ kBinRegular,
+ // Likely-dirty:
+ // All classes get their own bins since their fields often dirty
+ kBinClassInitializedFinalStatics, // Class initializers have been run, no non-final statics
+ kBinClassInitialized, // Class initializers have been run
+ kBinClassVerified, // Class verified, but initializers haven't been run
+ kBinArtMethodNative, // Art method that is actually native
+ kBinArtMethodNotInitialized, // Art method with a declaring class that wasn't initialized
+ // Don't care about other art methods since they don't dirty
+ // Add more bins here if we add more segregation code.
+ kBinSize,
+ };
+
+ friend std::ostream& operator<<(std::ostream& stream, const Bin& bin);
+
+ static constexpr size_t kBinBits = MinimumBitsToStore(kBinSize - 1);
+ // uint32 = typeof(lockword_)
+ static constexpr size_t kBinShift = BitSizeOf<uint32_t>() - kBinBits;
+ // 111000.....0
+ static constexpr size_t kBinMask = ((static_cast<size_t>(1) << kBinBits) - 1) << kBinShift;
+
+ // We use the lock word to store the bin # and bin index of the object in the image.
+ //
+ // The struct size must be exactly sizeof(LockWord), currently 32-bits, since this will end up
+ // stored in the lock word bit-for-bit when object forwarding addresses are being calculated.
+ struct BinSlot {
+ explicit BinSlot(uint32_t lockword);
+ BinSlot(Bin bin, uint32_t index);
+
+ // The bin an object belongs to, i.e. regular, class/verified, class/initialized, etc.
+ Bin GetBin() const;
+ // The offset in bytes from the beginning of the bin. Aligned to object size.
+ uint32_t GetIndex() const;
+ // Pack into a single uint32_t, for storing into a lock word.
+ explicit operator uint32_t() const { return lockword_; }
+ // Comparison operator for map support
+ bool operator<(const BinSlot& other) const { return lockword_ < other.lockword_; }
+
+ private:
+ // Must be the same size as LockWord, any larger and we would truncate the data.
+ const uint32_t lockword_;
+ };
+
// We use the lock word to store the offset of the object in the image.
- void AssignImageOffset(mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetImageOffset(mirror::Object* object, size_t offset)
+ void AssignImageOffset(mirror::Object* object, BinSlot bin_slot)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetImageOffset(mirror::Object* object, BinSlot bin_slot, size_t offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsImageOffsetAssigned(mirror::Object* object) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
size_t GetImageOffset(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void AssignImageBinSlot(mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetImageBinSlot(mirror::Object* object, BinSlot bin_slot)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsImageBinSlotAssigned(mirror::Object* object) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ BinSlot GetImageBinSlot(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
static void* GetImageAddressCallback(void* writer, mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return reinterpret_cast<ImageWriter*>(writer)->GetImageAddress(obj);
@@ -136,13 +198,16 @@ class ImageWriter FINAL {
static void ComputeEagerResolvedStringsCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Combine string char arrays.
+ void ProcessStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Remove unwanted classes from various roots.
void PruneNonImageClasses() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static bool NonImageClassesVisitor(mirror::Class* c, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Verify unwanted classes removed.
- void CheckNonImageClassesRemoved();
+ void CheckNonImageClassesRemoved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void CheckNonImageClassesRemovedCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -153,7 +218,9 @@ class ImageWriter FINAL {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::ObjectArray<mirror::Object>* CreateImageRoots() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CalculateObjectOffsets(mirror::Object* obj)
+ void CalculateObjectBinSlots(mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void UnbinObjectsIntoOffset(mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void WalkInstanceFields(mirror::Object* obj, mirror::Class* klass)
@@ -162,9 +229,11 @@ class ImageWriter FINAL {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void WalkFieldsCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static void UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Creates the contiguous image in memory and adjusts pointers.
- void CopyAndFixupObjects();
+ void CopyAndFixupObjects() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void FixupMethod(mirror::ArtMethod* orig, mirror::ArtMethod* copy)
@@ -182,6 +251,9 @@ class ImageWriter FINAL {
// Patches references in OatFile to expect runtime addresses.
void SetOatChecksumFromElfFile(File* elf_file);
+ // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins.
+ size_t GetBinSizeSum(Bin up_to = kBinSize) const;
+
const CompilerDriver& compiler_driver_;
// Beginning target image address for the output image.
@@ -190,6 +262,9 @@ class ImageWriter FINAL {
// Offset to the free space in image_.
size_t image_end_;
+ // Offset from image_begin_ to where the first object is in image_.
+ size_t image_objects_offset_begin_;
+
// The image roots address in the image.
uint32_t image_roots_address_;
@@ -202,6 +277,9 @@ class ImageWriter FINAL {
// Saved hashes (objects are inside of the image so that they don't move).
std::vector<std::pair<mirror::Object*, uint32_t>> saved_hashes_;
+ // Saved hashes (objects are bin slots to inside of the image, not yet allocated an address).
+ std::map<BinSlot, uint32_t> saved_hashes_map_;
+
// Beginning target oat address for the pointers from the output image to its oat file.
const uint8_t* oat_data_begin_;
@@ -221,6 +299,13 @@ class ImageWriter FINAL {
uint32_t quick_to_interpreter_bridge_offset_;
const bool compile_pic_;
+ // Size of pointers on the target architecture.
+ size_t target_ptr_size_;
+
+ // Bin slot tracking for dirty object packing
+ size_t bin_slot_sizes_[kBinSize]; // Number of bytes in a bin
+ size_t bin_slot_count_[kBinSize]; // Number of objects in a bin
+
friend class FixupVisitor;
friend class FixupClassVisitor;
DISALLOW_COPY_AND_ASSIGN(ImageWriter);
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 3c3aa02502..c3fe75b3f1 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -307,7 +307,9 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver,
}
// 9. Plant call to native code associated with method.
- __ Call(main_jni_conv->MethodStackOffset(), mirror::ArtMethod::NativeMethodOffset(),
+ MemberOffset jni_entrypoint_offset = mirror::ArtMethod::EntryPointFromJniOffset(
+ InstructionSetPointerSize(instruction_set));
+ __ Call(main_jni_conv->MethodStackOffset(), jni_entrypoint_offset,
mr_conv->InterproceduralScratchRegister());
// 10. Fix differences in result widths.
diff --git a/compiler/llvm/ir_builder.h b/compiler/llvm/ir_builder.h
index 03498efcbb..990ba02d3d 100644
--- a/compiler/llvm/ir_builder.h
+++ b/compiler/llvm/ir_builder.h
@@ -101,10 +101,8 @@ class IRBuilder : public LLVMIRBuilder {
// Extend memory barrier
//--------------------------------------------------------------------------
void CreateMemoryBarrier(MemBarrierKind barrier_kind) {
-#if ANDROID_SMP
// TODO: select atomic ordering according to given barrier kind.
CreateFence(::llvm::SequentiallyConsistent);
-#endif
}
//--------------------------------------------------------------------------
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 659c3328fc..c6beb36178 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -309,7 +309,7 @@ class OatWriter::Thumb2RelativeCallPatcher FINAL : public ArmBaseRelativeCallPat
arm::Thumb2Assembler assembler;
assembler.LoadFromOffset(
arm::kLoadWord, arm::PC, arm::R0,
- mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
assembler.bkpt(0);
std::vector<uint8_t> thunk_code(assembler.CodeSize());
MemoryRegion code(thunk_code.data(), thunk_code.size());
@@ -363,7 +363,8 @@ class OatWriter::Arm64RelativeCallPatcher FINAL : public ArmBaseRelativeCallPatc
// The thunk just uses the entry point in the ArtMethod. This works even for calls
// to the generic JNI and interpreter trampolines.
arm64::Arm64Assembler assembler;
- Offset offset(mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
+ Offset offset(mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArm64PointerSize).Int32Value());
assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0));
std::vector<uint8_t> thunk_code(assembler.CodeSize());
MemoryRegion code(thunk_code.data(), thunk_code.size());
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index b51b6e7d25..be8631ad42 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -568,12 +568,13 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
return true;
}
-void HGraphBuilder::BuildCheckedDiv(uint16_t out_vreg,
- uint16_t first_vreg,
- int64_t second_vreg_or_constant,
- uint32_t dex_pc,
- Primitive::Type type,
- bool second_is_constant) {
+void HGraphBuilder::BuildCheckedDivRem(uint16_t out_vreg,
+ uint16_t first_vreg,
+ int64_t second_vreg_or_constant,
+ uint32_t dex_pc,
+ Primitive::Type type,
+ bool second_is_constant,
+ bool isDiv) {
DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
HInstruction* first = LoadLocal(first_vreg, type);
@@ -597,7 +598,11 @@ void HGraphBuilder::BuildCheckedDiv(uint16_t out_vreg,
temps.Add(current_block_->GetLastInstruction());
}
- current_block_->AddInstruction(new (arena_) HDiv(type, first, second, dex_pc));
+ if (isDiv) {
+ current_block_->AddInstruction(new (arena_) HDiv(type, first, second, dex_pc));
+ } else {
+ current_block_->AddInstruction(new (arena_) HRem(type, first, second, dex_pc));
+ }
UpdateLocal(out_vreg, current_block_->GetLastInstruction());
}
@@ -997,6 +1002,16 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
break;
}
+ case Instruction::INT_TO_FLOAT: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimFloat);
+ break;
+ }
+
+ case Instruction::INT_TO_DOUBLE: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimDouble);
+ break;
+ }
+
case Instruction::LONG_TO_INT: {
Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimInt);
break;
@@ -1007,6 +1022,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
break;
}
+ case Instruction::INT_TO_SHORT: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimShort);
+ break;
+ }
+
case Instruction::INT_TO_CHAR: {
Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimChar);
break;
@@ -1078,14 +1098,14 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
}
case Instruction::DIV_INT: {
- BuildCheckedDiv(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimInt, false);
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimInt, false, true);
break;
}
case Instruction::DIV_LONG: {
- BuildCheckedDiv(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimLong, false);
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimLong, false, true);
break;
}
@@ -1099,6 +1119,18 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
break;
}
+ case Instruction::REM_INT: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimInt, false, false);
+ break;
+ }
+
+ case Instruction::REM_LONG: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimLong, false, false);
+ break;
+ }
+
case Instruction::AND_INT: {
Binop_23x<HAnd>(instruction, Primitive::kPrimInt);
break;
@@ -1185,14 +1217,26 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
}
case Instruction::DIV_INT_2ADDR: {
- BuildCheckedDiv(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
- dex_pc, Primitive::kPrimInt, false);
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+ dex_pc, Primitive::kPrimInt, false, true);
break;
}
case Instruction::DIV_LONG_2ADDR: {
- BuildCheckedDiv(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
- dex_pc, Primitive::kPrimLong, false);
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+ dex_pc, Primitive::kPrimLong, false, true);
+ break;
+ }
+
+ case Instruction::REM_INT_2ADDR: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+ dex_pc, Primitive::kPrimInt, false, false);
+ break;
+ }
+
+ case Instruction::REM_LONG_2ADDR: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+ dex_pc, Primitive::kPrimLong, false, false);
break;
}
@@ -1298,8 +1342,15 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
case Instruction::DIV_INT_LIT16:
case Instruction::DIV_INT_LIT8: {
- BuildCheckedDiv(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimInt, true);
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimInt, true, true);
+ break;
+ }
+
+ case Instruction::REM_INT_LIT16:
+ case Instruction::REM_INT_LIT8: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimInt, true, false);
break;
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 799e628a78..897bcece7b 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -123,12 +123,13 @@ class HGraphBuilder : public ValueObject {
Primitive::Type input_type,
Primitive::Type result_type);
- void BuildCheckedDiv(uint16_t out_reg,
- uint16_t first_reg,
- int64_t second_reg_or_constant,
- uint32_t dex_pc,
- Primitive::Type type,
- bool second_is_lit);
+ void BuildCheckedDivRem(uint16_t out_reg,
+ uint16_t first_reg,
+ int64_t second_reg_or_constant,
+ uint32_t dex_pc,
+ Primitive::Type type,
+ bool second_is_lit,
+ bool is_div);
void BuildReturn(const Instruction& instruction, Primitive::Type type);
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 4d71cb780a..0b593275c7 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -589,12 +589,14 @@ void CodeGenerator::SaveLiveRegisters(LocationSummary* locations) {
if (locations->RegisterContainsObject(i)) {
locations->SetStackBit(stack_offset / kVRegSize);
}
+ DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
stack_offset += SaveCoreRegister(stack_offset, i);
}
}
for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
if (register_set->ContainsFloatingPointRegister(i)) {
+ DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
stack_offset += SaveFloatingPointRegister(stack_offset, i);
}
}
@@ -605,12 +607,14 @@ void CodeGenerator::RestoreLiveRegisters(LocationSummary* locations) {
size_t stack_offset = first_register_slot_in_slow_path_;
for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) {
if (register_set->ContainsCoreRegister(i)) {
+ DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
stack_offset += RestoreCoreRegister(stack_offset, i);
}
}
for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
if (register_set->ContainsFloatingPointRegister(i)) {
+ DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize());
stack_offset += RestoreFloatingPointRegister(stack_offset, i);
}
}
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 63bf96ca5a..f906eb8c05 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -168,6 +168,15 @@ class CodeGenerator : public ArenaObject<kArenaAllocMisc> {
void EmitParallelMoves(Location from1, Location to1, Location from2, Location to2);
+ static bool StoreNeedsWriteBarrier(Primitive::Type type, HInstruction* value) {
+ if (kIsDebugBuild) {
+ if (type == Primitive::kPrimNot && value->IsIntConstant()) {
+ CHECK_EQ(value->AsIntConstant()->GetValue(), 0);
+ }
+ }
+ return type == Primitive::kPrimNot && !value->IsIntConstant();
+ }
+
protected:
CodeGenerator(HGraph* graph,
size_t number_of_core_registers,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 09e1b97570..1701ef5f0a 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -80,7 +80,7 @@ class NullCheckSlowPathARM : public SlowPathCodeARM {
public:
explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {}
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
__ Bind(GetEntryLabel());
arm_codegen->InvokeRuntime(
@@ -96,7 +96,7 @@ class DivZeroCheckSlowPathARM : public SlowPathCodeARM {
public:
explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : instruction_(instruction) {}
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
__ Bind(GetEntryLabel());
arm_codegen->InvokeRuntime(
@@ -112,7 +112,7 @@ class StackOverflowCheckSlowPathARM : public SlowPathCodeARM {
public:
StackOverflowCheckSlowPathARM() {}
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
__ LoadFromOffset(kLoadWord, PC, TR,
QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowStackOverflow).Int32Value());
@@ -124,10 +124,10 @@ class StackOverflowCheckSlowPathARM : public SlowPathCodeARM {
class SuspendCheckSlowPathARM : public SlowPathCodeARM {
public:
- explicit SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor)
+ SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor)
: instruction_(instruction), successor_(successor) {}
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
__ Bind(GetEntryLabel());
codegen->SaveLiveRegisters(instruction_->GetLocations());
@@ -166,7 +166,7 @@ class BoundsCheckSlowPathARM : public SlowPathCodeARM {
index_location_(index_location),
length_location_(length_location) {}
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
__ Bind(GetEntryLabel());
// We're moving two locations to locations that could overlap, so we need a parallel
@@ -199,7 +199,7 @@ class LoadClassSlowPathARM : public SlowPathCodeARM {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = at_->GetLocations();
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
@@ -245,7 +245,7 @@ class LoadStringSlowPathARM : public SlowPathCodeARM {
public:
explicit LoadStringSlowPathARM(HLoadString* instruction) : instruction_(instruction) {}
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
@@ -281,7 +281,7 @@ class TypeCheckSlowPathARM : public SlowPathCodeARM {
object_class_(object_class),
dex_pc_(dex_pc) {}
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
DCHECK(instruction_->IsCheckCast()
|| !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
@@ -1188,7 +1188,8 @@ void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache()));
// LR = temp[offset_of_quick_compiled_code]
__ LoadFromOffset(kLoadWord, LR, temp,
- mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArmWordSize).Int32Value());
// LR()
__ blx(LR);
@@ -1229,7 +1230,8 @@ void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
__ LoadFromOffset(kLoadWord, temp, receiver.As<Register>(), class_offset);
}
// temp = temp->GetMethodAt(method_offset);
- uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value();
+ uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArmWordSize).Int32Value();
__ LoadFromOffset(kLoadWord, temp, temp, method_offset);
// LR = temp->GetEntryPoint();
__ LoadFromOffset(kLoadWord, LR, temp, entry_point);
@@ -1265,7 +1267,8 @@ void InstructionCodeGeneratorARM::VisitInvokeInterface(HInvokeInterface* invoke)
__ LoadFromOffset(kLoadWord, temp, receiver.As<Register>(), class_offset);
}
// temp = temp->GetImtEntryAt(method_offset);
- uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value();
+ uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArmWordSize).Int32Value();
__ LoadFromOffset(kLoadWord, temp, temp, method_offset);
// LR = temp->GetEntryPoint();
__ LoadFromOffset(kLoadWord, LR, temp, entry_point);
@@ -1367,6 +1370,22 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
}
break;
+ case Primitive::kPrimShort:
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ // Processing a Dex `int-to-short' instruction.
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ }
+ break;
+
case Primitive::kPrimInt:
switch (input_type) {
case Primitive::kPrimLong:
@@ -1428,9 +1447,49 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) {
break;
case Primitive::kPrimFloat:
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ // Processing a Dex `int-to-float' instruction.
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Type conversion from " << input_type
+ << " to " << result_type << " not yet implemented";
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ };
+ break;
+
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ // Processing a Dex `int-to-double' instruction.
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimFloat:
+ LOG(FATAL) << "Type conversion from " << input_type
+ << " to " << result_type << " not yet implemented";
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ };
break;
default:
@@ -1461,6 +1520,21 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio
}
break;
+ case Primitive::kPrimShort:
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ // Processing a Dex `int-to-short' instruction.
+ __ sbfx(out.As<Register>(), in.As<Register>(), 0, 16);
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ }
+ break;
+
case Primitive::kPrimInt:
switch (input_type) {
case Primitive::kPrimLong:
@@ -1535,9 +1609,52 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio
break;
case Primitive::kPrimFloat:
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar: {
+ // Processing a Dex `int-to-float' instruction.
+ __ vmovsr(out.As<SRegister>(), in.As<Register>());
+ __ vcvtsi(out.As<SRegister>(), out.As<SRegister>());
+ break;
+ }
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Type conversion from " << input_type
+ << " to " << result_type << " not yet implemented";
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ };
+ break;
+
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar: {
+ // Processing a Dex `int-to-double' instruction.
+ __ vmovsr(out.AsFpuRegisterPairLow<SRegister>(), in.As<Register>());
+ __ vcvtdi(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()),
+ out.AsFpuRegisterPairLow<SRegister>());
+ break;
+ }
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimFloat:
+ LOG(FATAL) << "Type conversion from " << input_type
+ << " to " << result_type << " not yet implemented";
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ };
break;
default:
@@ -1842,6 +1959,86 @@ void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) {
}
}
+void LocationsBuilderARM::VisitRem(HRem* rem) {
+ LocationSummary::CallKind call_kind = rem->GetResultType() == Primitive::kPrimLong
+ ? LocationSummary::kCall
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
+
+ switch (rem->GetResultType()) {
+ case Primitive::kPrimInt: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ locations->AddTemp(Location::RequiresRegister());
+ break;
+ }
+ case Primitive::kPrimLong: {
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::RegisterPairLocation(
+ calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(1, Location::RegisterPairLocation(
+ calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
+ // The runtime helper puts the output in R2,R3.
+ locations->SetOut(Location::RegisterPairLocation(R2, R3));
+ break;
+ }
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+ break;
+ }
+
+ default:
+ LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+ }
+}
+
+void InstructionCodeGeneratorARM::VisitRem(HRem* rem) {
+ LocationSummary* locations = rem->GetLocations();
+ Location out = locations->Out();
+ Location first = locations->InAt(0);
+ Location second = locations->InAt(1);
+
+ switch (rem->GetResultType()) {
+ case Primitive::kPrimInt: {
+ Register reg1 = first.As<Register>();
+ Register reg2 = second.As<Register>();
+ Register temp = locations->GetTemp(0).As<Register>();
+
+ // temp = reg1 / reg2 (integer division)
+ // temp = temp * reg2
+ // dest = reg1 - temp
+ __ sdiv(temp, reg1, reg2);
+ __ mul(temp, temp, reg2);
+ __ sub(out.As<Register>(), reg1, ShifterOperand(temp));
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ InvokeRuntimeCallingConvention calling_convention;
+ DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>());
+ DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>());
+ DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>());
+ DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>());
+ DCHECK_EQ(R2, out.AsRegisterPairLow<Register>());
+ DCHECK_EQ(R3, out.AsRegisterPairHigh<Register>());
+
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLmod), rem, rem->GetDexPc());
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+ break;
+ }
+
+ default:
+ LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+ }
+}
+
void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -2034,11 +2231,12 @@ void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) {
void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- bool is_object_type = instruction->GetFieldType() == Primitive::kPrimNot;
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(instruction->GetFieldType(), instruction->GetValue());
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// Temporary registers for the write barrier.
- if (is_object_type) {
+ if (needs_write_barrier) {
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
}
@@ -2069,7 +2267,7 @@ void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instr
case Primitive::kPrimNot: {
Register value = locations->InAt(1).As<Register>();
__ StoreToOffset(kStoreWord, value, obj, offset);
- if (field_type == Primitive::kPrimNot) {
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) {
Register temp = locations->GetTemp(0).As<Register>();
Register card = locations->GetTemp(1).As<Register>();
codegen_->MarkGCCard(temp, card, obj, value);
@@ -2302,10 +2500,14 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
Primitive::Type value_type = instruction->GetComponentType();
- bool is_object = value_type == Primitive::kPrimNot;
+
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
+ bool needs_runtime_call = instruction->NeedsTypeCheck();
+
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
- instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall);
- if (is_object) {
+ instruction, needs_runtime_call ? LocationSummary::kCall : LocationSummary::kNoCall);
+ if (needs_runtime_call) {
InvokeRuntimeCallingConvention calling_convention;
locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
@@ -2314,6 +2516,12 @@ void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
locations->SetInAt(2, Location::RequiresRegister());
+
+ if (needs_write_barrier) {
+ // Temporary registers for the write barrier.
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
}
@@ -2322,6 +2530,9 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
Register obj = locations->InAt(0).As<Register>();
Location index = locations->InAt(1);
Primitive::Type value_type = instruction->GetComponentType();
+ bool needs_runtime_call = locations->WillCall();
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
switch (value_type) {
case Primitive::kPrimBoolean:
@@ -2352,24 +2563,32 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
break;
}
- case Primitive::kPrimInt: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
- Register value = locations->InAt(2).As<Register>();
- if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- __ StoreToOffset(kStoreWord, value, obj, offset);
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ if (!needs_runtime_call) {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ Register value = locations->InAt(2).As<Register>();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ StoreToOffset(kStoreWord, value, obj, offset);
+ } else {
+ DCHECK(index.IsRegister()) << index;
+ __ add(IP, obj, ShifterOperand(index.As<Register>(), LSL, TIMES_4));
+ __ StoreToOffset(kStoreWord, value, IP, data_offset);
+ }
+ if (needs_write_barrier) {
+ DCHECK_EQ(value_type, Primitive::kPrimNot);
+ Register temp = locations->GetTemp(0).As<Register>();
+ Register card = locations->GetTemp(1).As<Register>();
+ codegen_->MarkGCCard(temp, card, obj, value);
+ }
} else {
- __ add(IP, obj, ShifterOperand(index.As<Register>(), LSL, TIMES_4));
- __ StoreToOffset(kStoreWord, value, IP, data_offset);
+ DCHECK_EQ(value_type, Primitive::kPrimNot);
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc());
}
break;
}
- case Primitive::kPrimNot: {
- codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc());
- break;
- }
-
case Primitive::kPrimLong: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
Location value = locations->InAt(2);
@@ -2718,11 +2937,12 @@ void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instructi
void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- bool is_object_type = instruction->GetFieldType() == Primitive::kPrimNot;
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(instruction->GetFieldType(), instruction->GetValue());
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// Temporary registers for the write barrier.
- if (is_object_type) {
+ if (needs_write_barrier) {
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
}
@@ -2753,7 +2973,7 @@ void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instructi
case Primitive::kPrimNot: {
Register value = locations->InAt(1).As<Register>();
__ StoreToOffset(kStoreWord, value, cls, offset);
- if (field_type == Primitive::kPrimNot) {
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) {
Register temp = locations->GetTemp(0).As<Register>();
Register card = locations->GetTemp(1).As<Register>();
codegen_->MarkGCCard(temp, card, cls, value);
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index acc3fd6a25..c00fac1a37 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -28,7 +28,8 @@ namespace arm {
class CodeGeneratorARM;
class SlowPathCodeARM;
-static constexpr size_t kArmWordSize = 4;
+// Use a local definition to prevent copying mistakes.
+static constexpr size_t kArmWordSize = kArmPointerSize;
static constexpr Register kParameterCoreRegisters[] = { R1, R2, R3 };
static constexpr RegisterPair kParameterCorePairRegisters[] = { R1_R2, R2_R3 };
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 887a4efa19..82dced5e4f 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -48,18 +48,28 @@ bool IsFPType(Primitive::Type type) {
return type == Primitive::kPrimFloat || type == Primitive::kPrimDouble;
}
+bool IsIntegralType(Primitive::Type type) {
+ switch (type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimLong:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool Is64BitType(Primitive::Type type) {
return type == Primitive::kPrimLong || type == Primitive::kPrimDouble;
}
// Convenience helpers to ease conversion to and from VIXL operands.
+static_assert((SP == 31) && (WSP == 31) && (XZR == 32) && (WZR == 32),
+ "Unexpected values for register codes.");
int VIXLRegCodeFromART(int code) {
- // TODO: static check?
- DCHECK_EQ(SP, 31);
- DCHECK_EQ(WSP, 31);
- DCHECK_EQ(XZR, 32);
- DCHECK_EQ(WZR, 32);
if (code == SP) {
return vixl::kSPRegInternalCode;
}
@@ -70,11 +80,6 @@ int VIXLRegCodeFromART(int code) {
}
int ARTRegCodeFromVIXL(int code) {
- // TODO: static check?
- DCHECK_EQ(SP, 31);
- DCHECK_EQ(WSP, 31);
- DCHECK_EQ(XZR, 32);
- DCHECK_EQ(WZR, 32);
if (code == vixl::kSPRegInternalCode) {
return SP;
}
@@ -128,6 +133,17 @@ FPRegister InputFPRegisterAt(HInstruction* instr, int input_index) {
instr->InputAt(input_index)->GetType());
}
+CPURegister OutputCPURegister(HInstruction* instr) {
+ return IsFPType(instr->GetType()) ? static_cast<CPURegister>(OutputFPRegister(instr))
+ : static_cast<CPURegister>(OutputRegister(instr));
+}
+
+CPURegister InputCPURegisterAt(HInstruction* instr, int index) {
+ return IsFPType(instr->InputAt(index)->GetType())
+ ? static_cast<CPURegister>(InputFPRegisterAt(instr, index))
+ : static_cast<CPURegister>(InputRegisterAt(instr, index));
+}
+
int64_t Int64ConstantFrom(Location location) {
HConstant* instr = location.GetConstant();
return instr->IsIntConstant() ? instr->AsIntConstant()->GetValue()
@@ -151,14 +167,18 @@ MemOperand StackOperandFrom(Location location) {
return MemOperand(sp, location.GetStackIndex());
}
-MemOperand HeapOperand(const Register& base, Offset offset) {
+MemOperand HeapOperand(const Register& base, size_t offset) {
// A heap reference must be 32bit, so fit in a W register.
DCHECK(base.IsW());
- return MemOperand(base.X(), offset.SizeValue());
+ return MemOperand(base.X(), offset);
}
-MemOperand HeapOperandFrom(Location location, Primitive::Type type, Offset offset) {
- return HeapOperand(RegisterFrom(location, type), offset);
+MemOperand HeapOperand(const Register& base, Offset offset) {
+ return HeapOperand(base, offset.SizeValue());
+}
+
+MemOperand HeapOperandFrom(Location location, Offset offset) {
+ return HeapOperand(RegisterFrom(location, Primitive::kPrimNot), offset);
}
Location LocationFrom(const Register& reg) {
@@ -227,7 +247,8 @@ Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type retur
return ARM64ReturnLocation(return_type);
}
-#define __ reinterpret_cast<Arm64Assembler*>(codegen->GetAssembler())->vixl_masm_->
+#define __ down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler()->
+#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, x).Int32Value()
class SlowPathCodeARM64 : public SlowPathCode {
public:
@@ -245,45 +266,125 @@ class SlowPathCodeARM64 : public SlowPathCode {
class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 {
public:
- explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction,
- Location index_location,
- Location length_location)
- : instruction_(instruction),
- index_location_(index_location),
- length_location_(length_location) {}
-
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
- CodeGeneratorARM64* arm64_codegen = reinterpret_cast<CodeGeneratorARM64*>(codegen);
+ BoundsCheckSlowPathARM64() {}
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
+ __ Brk(__LINE__); // TODO: Unimplemented BoundsCheckSlowPathARM64.
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64);
+};
+
+class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 {
+ public:
+ explicit DivZeroCheckSlowPathARM64(HDivZeroCheck* instruction) : instruction_(instruction) {}
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
+ __ Bind(GetEntryLabel());
+ arm64_codegen->InvokeRuntime(
+ QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc());
+ }
+
+ private:
+ HDivZeroCheck* const instruction_;
+ DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM64);
+};
+
+class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
+ public:
+ LoadClassSlowPathARM64(HLoadClass* cls,
+ HInstruction* at,
+ uint32_t dex_pc,
+ bool do_clinit)
+ : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+ DCHECK(at->IsLoadClass() || at->IsClinitCheck());
+ }
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = at_->GetLocations();
+ CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
+
+ __ Bind(GetEntryLabel());
+ codegen->SaveLiveRegisters(locations);
+
InvokeRuntimeCallingConvention calling_convention;
- arm64_codegen->MoveHelper(LocationFrom(calling_convention.GetRegisterAt(0)),
- index_location_, Primitive::kPrimInt);
- arm64_codegen->MoveHelper(LocationFrom(calling_convention.GetRegisterAt(1)),
- length_location_, Primitive::kPrimInt);
- size_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pThrowArrayBounds).SizeValue();
- __ Ldr(lr, MemOperand(tr, offset));
- __ Blr(lr);
- codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+ __ Mov(calling_convention.GetRegisterAt(0).W(), cls_->GetTypeIndex());
+ arm64_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1).W());
+ int32_t entry_point_offset = do_clinit_ ? QUICK_ENTRY_POINT(pInitializeStaticStorage)
+ : QUICK_ENTRY_POINT(pInitializeType);
+ arm64_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_);
+
+ // Move the class to the desired location.
+ Location out = locations->Out();
+ if (out.IsValid()) {
+ DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
+ Primitive::Type type = at_->GetType();
+ arm64_codegen->MoveHelper(out, calling_convention.GetReturnLocation(type), type);
+ }
+
+ codegen->RestoreLiveRegisters(locations);
+ __ B(GetExitLabel());
}
private:
- HBoundsCheck* const instruction_;
- const Location index_location_;
- const Location length_location_;
+ // The class this slow path will load.
+ HLoadClass* const cls_;
- DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64);
+ // The instruction where this slow path is happening.
+ // (Might be the load class or an initialization check).
+ HInstruction* const at_;
+
+ // The dex PC of `at_`.
+ const uint32_t dex_pc_;
+
+ // Whether to initialize the class.
+ const bool do_clinit_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM64);
+};
+
+class LoadStringSlowPathARM64 : public SlowPathCodeARM64 {
+ public:
+ explicit LoadStringSlowPathARM64(HLoadString* instruction) : instruction_(instruction) {}
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+ CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
+
+ __ Bind(GetEntryLabel());
+ codegen->SaveLiveRegisters(locations);
+
+ InvokeRuntimeCallingConvention calling_convention;
+ arm64_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(0).W());
+ __ Mov(calling_convention.GetRegisterAt(1).W(), instruction_->GetStringIndex());
+ arm64_codegen->InvokeRuntime(
+ QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc());
+ Primitive::Type type = instruction_->GetType();
+ arm64_codegen->MoveHelper(locations->Out(), calling_convention.GetReturnLocation(type), type);
+
+ codegen->RestoreLiveRegisters(locations);
+ __ B(GetExitLabel());
+ }
+
+ private:
+ HLoadString* const instruction_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM64);
};
class NullCheckSlowPathARM64 : public SlowPathCodeARM64 {
public:
explicit NullCheckSlowPathARM64(HNullCheck* instr) : instruction_(instr) {}
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
__ Bind(GetEntryLabel());
- int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pThrowNullPointer).Int32Value();
- __ Ldr(lr, MemOperand(tr, offset));
- __ Blr(lr);
- codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+ arm64_codegen->InvokeRuntime(
+ QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc());
}
private:
@@ -298,13 +399,18 @@ class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 {
HBasicBlock* successor)
: instruction_(instruction), successor_(successor) {}
- virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
- size_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pTestSuspend).SizeValue();
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
__ Bind(GetEntryLabel());
- __ Ldr(lr, MemOperand(tr, offset));
- __ Blr(lr);
- codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
- __ B(GetReturnLabel());
+ codegen->SaveLiveRegisters(instruction_->GetLocations());
+ arm64_codegen->InvokeRuntime(
+ QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc());
+ codegen->RestoreLiveRegisters(instruction_->GetLocations());
+ if (successor_ == nullptr) {
+ __ B(GetReturnLabel());
+ } else {
+ __ B(arm64_codegen->GetLabelOf(successor_));
+ }
}
vixl::Label* GetReturnLabel() {
@@ -324,6 +430,20 @@ class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 {
DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM64);
};
+class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 {
+ public:
+ TypeCheckSlowPathARM64() {}
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ __ Bind(GetEntryLabel());
+ __ Brk(__LINE__); // TODO: Unimplemented TypeCheckSlowPathARM64.
+ __ b(GetExitLabel());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM64);
+};
+
#undef __
Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) {
@@ -356,11 +476,12 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph)
location_builder_(graph, this),
instruction_visitor_(graph, this) {}
-#define __ reinterpret_cast<Arm64Assembler*>(GetAssembler())->vixl_masm_->
+#undef __
+#define __ GetVIXLAssembler()->
void CodeGeneratorARM64::GenerateFrameEntry() {
// TODO: Add proper support for the stack overflow check.
- UseScratchRegisterScope temps(assembler_.vixl_masm_);
+ UseScratchRegisterScope temps(GetVIXLAssembler());
Register temp = temps.AcquireX();
__ Add(temp, sp, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64)));
__ Ldr(temp, MemOperand(temp, 0));
@@ -378,7 +499,7 @@ void CodeGeneratorARM64::GenerateFrameEntry() {
// ... : other preserved registers.
// sp[frame_size - regs_size]: first preserved register.
// ... : reserved frame space.
- // sp[0] : context pointer.
+ // sp[0] : current method.
}
void CodeGeneratorARM64::GenerateFrameExit() {
@@ -413,7 +534,7 @@ void CodeGeneratorARM64::Move(HInstruction* instruction,
__ Mov(dst, value);
} else {
DCHECK(location.IsStackSlot() || location.IsDoubleStackSlot());
- UseScratchRegisterScope temps(assembler_.vixl_masm_);
+ UseScratchRegisterScope temps(GetVIXLAssembler());
Register temp = instruction->IsIntConstant() ? temps.AcquireW() : temps.AcquireX();
__ Mov(temp, value);
__ Str(temp, StackOperandFrom(location));
@@ -465,7 +586,7 @@ Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const {
}
void CodeGeneratorARM64::MarkGCCard(Register object, Register value) {
- UseScratchRegisterScope temps(assembler_.vixl_masm_);
+ UseScratchRegisterScope temps(GetVIXLAssembler());
Register card = temps.AcquireX();
Register temp = temps.AcquireX();
vixl::Label done;
@@ -522,6 +643,19 @@ void CodeGeneratorARM64::DumpFloatingPointRegister(std::ostream& stream, int reg
stream << Arm64ManagedRegister::FromDRegister(DRegister(reg));
}
+void CodeGeneratorARM64::MoveConstant(CPURegister destination, HConstant* constant) {
+ if (constant->IsIntConstant() || constant->IsLongConstant()) {
+ __ Mov(Register(destination),
+ constant->IsIntConstant() ? constant->AsIntConstant()->GetValue()
+ : constant->AsLongConstant()->GetValue());
+ } else if (constant->IsFloatConstant()) {
+ __ Fmov(FPRegister(destination), constant->AsFloatConstant()->GetValue());
+ } else {
+ DCHECK(constant->IsDoubleConstant());
+ __ Fmov(FPRegister(destination), constant->AsDoubleConstant()->GetValue());
+ }
+}
+
void CodeGeneratorARM64::MoveHelper(Location destination,
Location source,
Primitive::Type type) {
@@ -544,13 +678,7 @@ void CodeGeneratorARM64::MoveHelper(Location destination,
} else if (source.IsFpuRegister()) {
__ Fmov(dst, FPRegisterFrom(source, type));
} else {
- HConstant* cst = source.GetConstant();
- if (cst->IsFloatConstant()) {
- __ Fmov(dst, cst->AsFloatConstant()->GetValue());
- } else {
- DCHECK(cst->IsDoubleConstant());
- __ Fmov(dst, cst->AsDoubleConstant()->GetValue());
- }
+ MoveConstant(dst, source.GetConstant());
}
} else {
DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
@@ -558,8 +686,21 @@ void CodeGeneratorARM64::MoveHelper(Location destination,
__ Str(RegisterFrom(source, type), StackOperandFrom(destination));
} else if (source.IsFpuRegister()) {
__ Str(FPRegisterFrom(source, type), StackOperandFrom(destination));
+ } else if (source.IsConstant()) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ HConstant* cst = source.GetConstant();
+ CPURegister temp;
+ if (cst->IsIntConstant() || cst->IsLongConstant()) {
+ temp = cst->IsIntConstant() ? temps.AcquireW() : temps.AcquireX();
+ } else {
+ DCHECK(cst->IsFloatConstant() || cst->IsDoubleConstant());
+ temp = cst->IsFloatConstant() ? temps.AcquireS() : temps.AcquireD();
+ }
+ MoveConstant(temp, cst);
+ __ Str(temp, StackOperandFrom(destination));
} else {
- UseScratchRegisterScope temps(assembler_.vixl_masm_);
+ DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot());
+ UseScratchRegisterScope temps(GetVIXLAssembler());
Register temp = destination.IsDoubleStackSlot() ? temps.AcquireX() : temps.AcquireW();
__ Ldr(temp, StackOperandFrom(source));
__ Str(temp, StackOperandFrom(destination));
@@ -568,61 +709,89 @@ void CodeGeneratorARM64::MoveHelper(Location destination,
}
void CodeGeneratorARM64::Load(Primitive::Type type,
- vixl::Register dst,
+ vixl::CPURegister dst,
const vixl::MemOperand& src) {
switch (type) {
case Primitive::kPrimBoolean:
- __ Ldrb(dst, src);
+ __ Ldrb(Register(dst), src);
break;
case Primitive::kPrimByte:
- __ Ldrsb(dst, src);
+ __ Ldrsb(Register(dst), src);
break;
case Primitive::kPrimShort:
- __ Ldrsh(dst, src);
+ __ Ldrsh(Register(dst), src);
break;
case Primitive::kPrimChar:
- __ Ldrh(dst, src);
+ __ Ldrh(Register(dst), src);
break;
case Primitive::kPrimInt:
case Primitive::kPrimNot:
case Primitive::kPrimLong:
- DCHECK(dst.Is64Bits() == (type == Primitive::kPrimLong));
- __ Ldr(dst, src);
- break;
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
+ DCHECK(dst.Is64Bits() == Is64BitType(type));
+ __ Ldr(dst, src);
+ break;
case Primitive::kPrimVoid:
LOG(FATAL) << "Unreachable type " << type;
}
}
void CodeGeneratorARM64::Store(Primitive::Type type,
- vixl::Register rt,
+ vixl::CPURegister rt,
const vixl::MemOperand& dst) {
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
- __ Strb(rt, dst);
+ __ Strb(Register(rt), dst);
break;
case Primitive::kPrimChar:
case Primitive::kPrimShort:
- __ Strh(rt, dst);
+ __ Strh(Register(rt), dst);
break;
case Primitive::kPrimInt:
case Primitive::kPrimNot:
case Primitive::kPrimLong:
- DCHECK(rt.Is64Bits() == (type == Primitive::kPrimLong));
- __ Str(rt, dst);
- break;
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
+ DCHECK(rt.Is64Bits() == Is64BitType(type));
+ __ Str(rt, dst);
+ break;
case Primitive::kPrimVoid:
LOG(FATAL) << "Unreachable type " << type;
}
}
-#undef __
-#define __ GetAssembler()->vixl_masm_->
+void CodeGeneratorARM64::LoadCurrentMethod(vixl::Register current_method) {
+ DCHECK(current_method.IsW());
+ __ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset));
+}
+
+void CodeGeneratorARM64::InvokeRuntime(int32_t entry_point_offset,
+ HInstruction* instruction,
+ uint32_t dex_pc) {
+ __ Ldr(lr, MemOperand(tr, entry_point_offset));
+ __ Blr(lr);
+ RecordPcInfo(instruction, dex_pc);
+ DCHECK(instruction->IsSuspendCheck()
+ || instruction->IsBoundsCheck()
+ || instruction->IsNullCheck()
+ || instruction->IsDivZeroCheck()
+ || !IsLeafMethod());
+}
+
+void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path,
+ vixl::Register class_reg) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ Register temp = temps.AcquireW();
+ __ Ldr(temp, HeapOperand(class_reg, mirror::Class::StatusOffset()));
+ __ Cmp(temp, mirror::Class::kStatusInitialized);
+ __ B(lt, slow_path->GetEntryLabel());
+ // Even if the initialized flag is set, we may be in a situation where caches are not synced
+ // properly. Therefore, we do a memory fence.
+ __ Dmb(InnerShareable, BarrierAll);
+ __ Bind(slow_path->GetExitLabel());
+}
InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph,
CodeGeneratorARM64* codegen)
@@ -631,27 +800,14 @@ InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph,
codegen_(codegen) {}
#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \
- M(And) \
- M(CheckCast) \
- M(ClinitCheck) \
- M(DivZeroCheck) \
- M(InstanceOf) \
- M(InvokeInterface) \
- M(LoadClass) \
- M(LoadException) \
- M(LoadString) \
- M(MonitorOperation) \
- M(Or) \
M(ParallelMove) \
- M(StaticFieldGet) \
- M(StaticFieldSet) \
- M(Throw) \
- M(TypeConversion) \
- M(Xor) \
+ M(Rem)
#define UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name) name##UnimplementedInstructionBreakCode
enum UnimplementedInstructionBreakCode {
+ // Using a base helps identify when we hit such breakpoints.
+ UnimplementedInstructionBreakCodeBaseCode = 0x900,
#define ENUM_UNIMPLEMENTED_INSTRUCTION(name) UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name),
FOR_EACH_UNIMPLEMENTED_INSTRUCTION(ENUM_UNIMPLEMENTED_INSTRUCTION)
#undef ENUM_UNIMPLEMENTED_INSTRUCTION
@@ -670,9 +826,9 @@ enum UnimplementedInstructionBreakCode {
#undef DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS
#undef UNIMPLEMENTED_INSTRUCTION_BREAK_CODE
+#undef FOR_EACH_UNIMPLEMENTED_INSTRUCTION
-void LocationsBuilderARM64::HandleAddSub(HBinaryOperation* instr) {
- DCHECK(instr->IsAdd() || instr->IsSub());
+void LocationsBuilderARM64::HandleBinaryOp(HBinaryOperation* instr) {
DCHECK_EQ(instr->InputCount(), 2U);
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr);
Primitive::Type type = instr->GetResultType();
@@ -688,7 +844,7 @@ void LocationsBuilderARM64::HandleAddSub(HBinaryOperation* instr) {
case Primitive::kPrimDouble:
locations->SetInAt(0, Location::RequiresFpuRegister());
locations->SetInAt(1, Location::RequiresFpuRegister());
- locations->SetOut(Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
break;
default:
@@ -696,9 +852,7 @@ void LocationsBuilderARM64::HandleAddSub(HBinaryOperation* instr) {
}
}
-void InstructionCodeGeneratorARM64::HandleAddSub(HBinaryOperation* instr) {
- DCHECK(instr->IsAdd() || instr->IsSub());
-
+void InstructionCodeGeneratorARM64::HandleBinaryOp(HBinaryOperation* instr) {
Primitive::Type type = instr->GetType();
switch (type) {
@@ -709,8 +863,15 @@ void InstructionCodeGeneratorARM64::HandleAddSub(HBinaryOperation* instr) {
Operand rhs = InputOperandAt(instr, 1);
if (instr->IsAdd()) {
__ Add(dst, lhs, rhs);
- } else {
+ } else if (instr->IsAnd()) {
+ __ And(dst, lhs, rhs);
+ } else if (instr->IsOr()) {
+ __ Orr(dst, lhs, rhs);
+ } else if (instr->IsSub()) {
__ Sub(dst, lhs, rhs);
+ } else {
+ DCHECK(instr->IsXor());
+ __ Eor(dst, lhs, rhs);
}
break;
}
@@ -721,22 +882,32 @@ void InstructionCodeGeneratorARM64::HandleAddSub(HBinaryOperation* instr) {
FPRegister rhs = InputFPRegisterAt(instr, 1);
if (instr->IsAdd()) {
__ Fadd(dst, lhs, rhs);
- } else {
+ } else if (instr->IsSub()) {
__ Fsub(dst, lhs, rhs);
+ } else {
+ LOG(FATAL) << "Unexpected floating-point binary operation";
}
break;
}
default:
- LOG(FATAL) << "Unexpected add/sub type " << type;
+ LOG(FATAL) << "Unexpected binary operation type " << type;
}
}
void LocationsBuilderARM64::VisitAdd(HAdd* instruction) {
- HandleAddSub(instruction);
+ HandleBinaryOp(instruction);
}
void InstructionCodeGeneratorARM64::VisitAdd(HAdd* instruction) {
- HandleAddSub(instruction);
+ HandleBinaryOp(instruction);
+}
+
+void LocationsBuilderARM64::VisitAnd(HAnd* instruction) {
+ HandleBinaryOp(instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitAnd(HAnd* instruction) {
+ HandleBinaryOp(instruction);
}
void LocationsBuilderARM64::VisitArrayGet(HArrayGet* instruction) {
@@ -751,11 +922,10 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
Primitive::Type type = instruction->GetType();
Register obj = InputRegisterAt(instruction, 0);
- Register out = OutputRegister(instruction);
Location index = locations->InAt(1);
size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(type)).Uint32Value();
MemOperand source(obj);
- UseScratchRegisterScope temps(GetAssembler()->vixl_masm_);
+ UseScratchRegisterScope temps(GetVIXLAssembler());
if (index.IsConstant()) {
offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(type);
@@ -767,7 +937,7 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) {
source = MemOperand(temp, offset);
}
- codegen_->Load(type, out, source);
+ codegen_->Load(type, OutputCPURegister(instruction), source);
}
void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) {
@@ -801,18 +971,16 @@ void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) {
void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) {
Primitive::Type value_type = instruction->GetComponentType();
if (value_type == Primitive::kPrimNot) {
- __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAputObject).Int32Value()));
- __ Blr(lr);
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
- DCHECK(!codegen_->IsLeafMethod());
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc());
+
} else {
LocationSummary* locations = instruction->GetLocations();
Register obj = InputRegisterAt(instruction, 0);
- Register value = InputRegisterAt(instruction, 2);
+ CPURegister value = InputCPURegisterAt(instruction, 2);
Location index = locations->InAt(1);
size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
MemOperand destination(obj);
- UseScratchRegisterScope temps(GetAssembler()->vixl_masm_);
+ UseScratchRegisterScope temps(GetVIXLAssembler());
if (index.IsConstant()) {
offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(value_type);
@@ -828,6 +996,66 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) {
}
}
+void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ if (instruction->HasUses()) {
+ locations->SetOut(Location::SameAsFirstInput());
+ }
+}
+
+void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
+ BoundsCheckSlowPathARM64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64();
+ codegen_->AddSlowPath(slow_path);
+
+ __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1));
+ __ B(slow_path->GetEntryLabel(), hs);
+}
+
+void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+ instruction, LocationSummary::kCallOnSlowPath);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ Register obj = InputRegisterAt(instruction, 0);;
+ Register cls = InputRegisterAt(instruction, 1);;
+ Register temp = temps.AcquireW();
+
+ SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM64();
+ codegen_->AddSlowPath(slow_path);
+
+ // TODO: avoid this check if we know obj is not null.
+ __ Cbz(obj, slow_path->GetExitLabel());
+ // Compare the class of `obj` with `cls`.
+ __ Ldr(temp, HeapOperand(obj, mirror::Object::ClassOffset()));
+ __ Cmp(temp, cls);
+ __ B(ne, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+}
+
+void LocationsBuilderARM64::VisitClinitCheck(HClinitCheck* check) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
+ locations->SetInAt(0, Location::RequiresRegister());
+ if (check->HasUses()) {
+ locations->SetOut(Location::SameAsFirstInput());
+ }
+}
+
+void InstructionCodeGeneratorARM64::VisitClinitCheck(HClinitCheck* check) {
+ // We assume the class is not null.
+ SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64(
+ check->GetLoadClass(), check, check->GetDexPc(), true);
+ codegen_->AddSlowPath(slow_path);
+ GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
+}
+
void LocationsBuilderARM64::VisitCompare(HCompare* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -846,7 +1074,7 @@ void InstructionCodeGeneratorARM64::VisitCompare(HCompare* instruction) {
Register result = OutputRegister(instruction);
Register left = InputRegisterAt(instruction, 0);
Operand right = InputOperandAt(instruction, 1);
- __ Subs(result, left, right);
+ __ Subs(result.X(), left, right);
__ B(eq, &done);
__ Mov(result, 1);
__ Cneg(result, result, le);
@@ -893,6 +1121,7 @@ void InstructionCodeGeneratorARM64::VisitCondition(HCondition* instruction) {
void LocationsBuilderARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); } \
void InstructionCodeGeneratorARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); }
FOR_EACH_CONDITION_INSTRUCTION(DEFINE_CONDITION_VISITORS)
+#undef DEFINE_CONDITION_VISITORS
#undef FOR_EACH_CONDITION_INSTRUCTION
void LocationsBuilderARM64::VisitDiv(HDiv* div) {
@@ -936,6 +1165,33 @@ void InstructionCodeGeneratorARM64::VisitDiv(HDiv* div) {
}
}
+void LocationsBuilderARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
+ if (instruction->HasUses()) {
+ locations->SetOut(Location::SameAsFirstInput());
+ }
+}
+
+void InstructionCodeGeneratorARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
+ SlowPathCodeARM64* slow_path =
+ new (GetGraph()->GetArena()) DivZeroCheckSlowPathARM64(instruction);
+ codegen_->AddSlowPath(slow_path);
+ Location value = instruction->GetLocations()->InAt(0);
+
+ if (value.IsConstant()) {
+ int64_t divisor = Int64ConstantFrom(value);
+ if (divisor == 0) {
+ __ B(slow_path->GetEntryLabel());
+ } else {
+ LOG(FATAL) << "Divisions by non-null constants should have been optimized away.";
+ }
+ } else {
+ __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
+ }
+}
+
void LocationsBuilderARM64::VisitDoubleConstant(HDoubleConstant* constant) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
@@ -955,7 +1211,7 @@ void InstructionCodeGeneratorARM64::VisitExit(HExit* exit) {
UNUSED(exit);
if (kIsDebugBuild) {
down_cast<Arm64Assembler*>(GetAssembler())->Comment("Unreachable");
- __ Brk(0); // TODO: Introduce special markers for such code locations.
+ __ Brk(__LINE__); // TODO: Introduce special markers for such code locations.
}
}
@@ -1038,7 +1294,7 @@ void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction
void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
MemOperand field = MemOperand(InputRegisterAt(instruction, 0),
instruction->GetFieldOffset().Uint32Value());
- codegen_->Load(instruction->GetType(), OutputRegister(instruction), field);
+ codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field);
}
void LocationsBuilderARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
@@ -1049,12 +1305,54 @@ void LocationsBuilderARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction
void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
Primitive::Type field_type = instruction->GetFieldType();
- Register value = InputRegisterAt(instruction, 1);
+ CPURegister value = InputCPURegisterAt(instruction, 1);
Register obj = InputRegisterAt(instruction, 0);
codegen_->Store(field_type, value, MemOperand(obj, instruction->GetFieldOffset().Uint32Value()));
if (field_type == Primitive::kPrimNot) {
- codegen_->MarkGCCard(obj, value);
+ codegen_->MarkGCCard(obj, Register(value));
+ }
+}
+
+void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) {
+ LocationSummary::CallKind call_kind =
+ instruction->IsClassFinal() ? LocationSummary::kNoCall : LocationSummary::kCallOnSlowPath;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), true); // The output does overlap inputs.
+}
+
+void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Register obj = InputRegisterAt(instruction, 0);;
+ Register cls = InputRegisterAt(instruction, 1);;
+ Register out = OutputRegister(instruction);
+
+ vixl::Label done;
+
+ // Return 0 if `obj` is null.
+ // TODO: Avoid this check if we know `obj` is not null.
+ __ Mov(out, 0);
+ __ Cbz(obj, &done);
+
+ // Compare the class of `obj` with `cls`.
+ __ Ldr(out, MemOperand(obj, mirror::Object::ClassOffset().Int32Value()));
+ __ Cmp(out, cls);
+ if (instruction->IsClassFinal()) {
+ // Classes must be equal for the instanceof to succeed.
+ __ Cset(out, eq);
+ } else {
+ // If the classes are not equal, we go into a slow path.
+ DCHECK(locations->OnlyCallsOnSlowPath());
+ SlowPathCodeARM64* slow_path =
+ new (GetGraph()->GetArena()) TypeCheckSlowPathARM64();
+ codegen_->AddSlowPath(slow_path);
+ __ B(ne, slow_path->GetEntryLabel());
+ __ Mov(out, 1);
+ __ Bind(slow_path->GetExitLabel());
}
+
+ __ Bind(&done);
}
void LocationsBuilderARM64::VisitIntConstant(HIntConstant* constant) {
@@ -1067,14 +1365,6 @@ void InstructionCodeGeneratorARM64::VisitIntConstant(HIntConstant* constant) {
UNUSED(constant);
}
-void LocationsBuilderARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
- HandleInvoke(invoke);
-}
-
-void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
- HandleInvoke(invoke);
-}
-
void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
@@ -1092,6 +1382,50 @@ void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) {
}
}
+void LocationsBuilderARM64::VisitInvokeInterface(HInvokeInterface* invoke) {
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorARM64::VisitInvokeInterface(HInvokeInterface* invoke) {
+ // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
+ Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0));
+ uint32_t method_offset = mirror::Class::EmbeddedImTableOffset().Uint32Value() +
+ (invoke->GetImtIndex() % mirror::Class::kImtSize) * sizeof(mirror::Class::ImTableEntry);
+ Location receiver = invoke->GetLocations()->InAt(0);
+ Offset class_offset = mirror::Object::ClassOffset();
+ Offset entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize);
+
+ // The register ip1 is required to be used for the hidden argument in
+ // art_quick_imt_conflict_trampoline, so prevent VIXL from using it.
+ UseScratchRegisterScope scratch_scope(GetVIXLAssembler());
+ scratch_scope.Exclude(ip1);
+ __ Mov(ip1, invoke->GetDexMethodIndex());
+
+ // temp = object->GetClass();
+ if (receiver.IsStackSlot()) {
+ __ Ldr(temp, StackOperandFrom(receiver));
+ __ Ldr(temp, HeapOperand(temp, class_offset));
+ } else {
+ __ Ldr(temp, HeapOperandFrom(receiver, class_offset));
+ }
+ // temp = temp->GetImtEntryAt(method_offset);
+ __ Ldr(temp, HeapOperand(temp, method_offset));
+ // lr = temp->GetEntryPoint();
+ __ Ldr(lr, HeapOperand(temp, entry_point));
+ // lr();
+ __ Blr(lr);
+ DCHECK(!codegen_->IsLeafMethod());
+ codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ HandleInvoke(invoke);
+}
+
+void LocationsBuilderARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
+ HandleInvoke(invoke);
+}
+
void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0));
// Make sure that ArtMethod* is passed in W0 as per the calling convention
@@ -1107,7 +1441,7 @@ void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
// Currently we implement the app -> app logic, which looks up in the resolve cache.
// temp = method;
- __ Ldr(temp, MemOperand(sp, kCurrentMethodStackOffset));
+ codegen_->LoadCurrentMethod(temp);
// temp = temp->dex_cache_resolved_methods_;
__ Ldr(temp, MemOperand(temp.X(),
mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
@@ -1115,7 +1449,8 @@ void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
__ Ldr(temp, MemOperand(temp.X(), index_in_cache));
// lr = temp->entry_point_from_quick_compiled_code_;
__ Ldr(lr, MemOperand(temp.X(),
- mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArm64WordSize).SizeValue()));
// lr();
__ Blr(lr);
@@ -1130,7 +1465,7 @@ void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
size_t method_offset = mirror::Class::EmbeddedVTableOffset().SizeValue() +
invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry);
Offset class_offset = mirror::Object::ClassOffset();
- Offset entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset();
+ Offset entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize);
// temp = object->GetClass();
if (receiver.IsStackSlot()) {
@@ -1138,8 +1473,7 @@ void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
__ Ldr(temp.W(), MemOperand(temp, class_offset.SizeValue()));
} else {
DCHECK(receiver.IsRegister());
- __ Ldr(temp.W(), HeapOperandFrom(receiver, Primitive::kPrimNot,
- class_offset));
+ __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset));
}
// temp = temp->GetMethodAt(method_offset);
__ Ldr(temp.W(), MemOperand(temp, method_offset));
@@ -1151,6 +1485,50 @@ void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
+void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) {
+ LocationSummary::CallKind call_kind = cls->CanCallRuntime() ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) {
+ Register out = OutputRegister(cls);
+ if (cls->IsReferrersClass()) {
+ DCHECK(!cls->CanCallRuntime());
+ DCHECK(!cls->MustGenerateClinitCheck());
+ codegen_->LoadCurrentMethod(out);
+ __ Ldr(out, HeapOperand(out, mirror::ArtMethod::DeclaringClassOffset()));
+ } else {
+ DCHECK(cls->CanCallRuntime());
+ codegen_->LoadCurrentMethod(out);
+ __ Ldr(out, HeapOperand(out, mirror::ArtMethod::DexCacheResolvedTypesOffset()));
+ __ Ldr(out, MemOperand(out.X(), CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+
+ SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64(
+ cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+ codegen_->AddSlowPath(slow_path);
+ __ Cbz(out, slow_path->GetEntryLabel());
+ if (cls->MustGenerateClinitCheck()) {
+ GenerateClassInitializationCheck(slow_path, out);
+ } else {
+ __ Bind(slow_path->GetExitLabel());
+ }
+ }
+}
+
+void LocationsBuilderARM64::VisitLoadException(HLoadException* load) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM64::VisitLoadException(HLoadException* instruction) {
+ MemOperand exception = MemOperand(tr, Thread::ExceptionOffset<kArm64WordSize>().Int32Value());
+ __ Ldr(OutputRegister(instruction), exception);
+ __ Str(wzr, exception);
+}
+
void LocationsBuilderARM64::VisitLoadLocal(HLoadLocal* load) {
load->SetLocations(nullptr);
}
@@ -1160,6 +1538,24 @@ void InstructionCodeGeneratorARM64::VisitLoadLocal(HLoadLocal* load) {
UNUSED(load);
}
+void LocationsBuilderARM64::VisitLoadString(HLoadString* load) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) {
+ SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load);
+ codegen_->AddSlowPath(slow_path);
+
+ Register out = OutputRegister(load);
+ codegen_->LoadCurrentMethod(out);
+ __ Ldr(out, HeapOperand(out, mirror::ArtMethod::DexCacheStringsOffset()));
+ __ Ldr(out, MemOperand(out.X(), CodeGenerator::GetCacheOffset(load->GetStringIndex())));
+ __ Cbz(out, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+}
+
void LocationsBuilderARM64::VisitLocal(HLocal* local) {
local->SetLocations(nullptr);
}
@@ -1178,6 +1574,20 @@ void InstructionCodeGeneratorARM64::VisitLongConstant(HLongConstant* constant) {
UNUSED(constant);
}
+void LocationsBuilderARM64::VisitMonitorOperation(HMonitorOperation* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
+}
+
+void InstructionCodeGeneratorARM64::VisitMonitorOperation(HMonitorOperation* instruction) {
+ codegen_->InvokeRuntime(instruction->IsEnter()
+ ? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject),
+ instruction,
+ instruction->GetDexPc());
+}
+
void LocationsBuilderARM64::VisitMul(HMul* mul) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall);
@@ -1193,7 +1603,7 @@ void LocationsBuilderARM64::VisitMul(HMul* mul) {
case Primitive::kPrimDouble:
locations->SetInAt(0, Location::RequiresFpuRegister());
locations->SetInAt(1, Location::RequiresFpuRegister());
- locations->SetOut(Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
break;
default:
@@ -1223,15 +1633,15 @@ void LocationsBuilderARM64::VisitNeg(HNeg* neg) {
new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
switch (neg->GetResultType()) {
case Primitive::kPrimInt:
- case Primitive::kPrimLong: {
+ case Primitive::kPrimLong:
locations->SetInAt(0, Location::RegisterOrConstant(neg->InputAt(0)));
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
- }
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
- LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType();
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
break;
default:
@@ -1248,7 +1658,7 @@ void InstructionCodeGeneratorARM64::VisitNeg(HNeg* neg) {
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
- LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType();
+ __ Fneg(OutputFPRegister(neg), InputFPRegisterAt(neg, 0));
break;
default:
@@ -1273,14 +1683,10 @@ void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) {
DCHECK(type_index.Is(w0));
Register current_method = RegisterFrom(locations->GetTemp(1), Primitive::kPrimNot);
DCHECK(current_method.Is(w1));
- __ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset));
+ codegen_->LoadCurrentMethod(current_method);
__ Mov(type_index, instruction->GetTypeIndex());
- int32_t quick_entrypoint_offset =
- QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocArrayWithAccessCheck).Int32Value();
- __ Ldr(lr, MemOperand(tr, quick_entrypoint_offset));
- __ Blr(lr);
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
- DCHECK(!codegen_->IsLeafMethod());
+ codegen_->InvokeRuntime(
+ QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc());
}
void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) {
@@ -1298,14 +1704,10 @@ void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction)
DCHECK(type_index.Is(w0));
Register current_method = RegisterFrom(locations->GetTemp(1), Primitive::kPrimNot);
DCHECK(current_method.Is(w1));
- __ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset));
+ codegen_->LoadCurrentMethod(current_method);
__ Mov(type_index, instruction->GetTypeIndex());
- int32_t quick_entrypoint_offset =
- QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocObjectWithAccessCheck).Int32Value();
- __ Ldr(lr, MemOperand(tr, quick_entrypoint_offset));
- __ Blr(lr);
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
- DCHECK(!codegen_->IsLeafMethod());
+ codegen_->InvokeRuntime(
+ QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc());
}
void LocationsBuilderARM64::VisitNot(HNot* instruction) {
@@ -1354,6 +1756,14 @@ void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
}
}
+void LocationsBuilderARM64::VisitOr(HOr* instruction) {
+ HandleBinaryOp(instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitOr(HOr* instruction) {
+ HandleBinaryOp(instruction);
+}
+
void LocationsBuilderARM64::VisitParameterValue(HParameterValue* instruction) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
@@ -1434,31 +1844,43 @@ void InstructionCodeGeneratorARM64::VisitStoreLocal(HStoreLocal* store) {
}
void LocationsBuilderARM64::VisitSub(HSub* instruction) {
- HandleAddSub(instruction);
+ HandleBinaryOp(instruction);
}
void InstructionCodeGeneratorARM64::VisitSub(HSub* instruction) {
- HandleAddSub(instruction);
+ HandleBinaryOp(instruction);
}
-void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
+void LocationsBuilderARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ Register cls = InputRegisterAt(instruction, 0);
+ uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+ codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), MemOperand(cls, offset));
+}
+
+void LocationsBuilderARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
- if (instruction->HasUses()) {
- locations->SetOut(Location::SameAsFirstInput());
- }
}
-void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- BoundsCheckSlowPathARM64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64(
- instruction, locations->InAt(0), locations->InAt(1));
- codegen_->AddSlowPath(slow_path);
+void InstructionCodeGeneratorARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ CPURegister value = InputCPURegisterAt(instruction, 1);
+ Register cls = InputRegisterAt(instruction, 0);
+ uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+ Primitive::Type field_type = instruction->GetFieldType();
- __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1));
- __ B(slow_path->GetEntryLabel(), hs);
+ codegen_->Store(field_type, value, MemOperand(cls, offset));
+ if (field_type == Primitive::kPrimNot) {
+ codegen_->MarkGCCard(cls, Register(value));
+ }
}
void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
@@ -1485,5 +1907,74 @@ void InstructionCodeGeneratorARM64::VisitTemporary(HTemporary* temp) {
UNUSED(temp);
}
+void LocationsBuilderARM64::VisitThrow(HThrow* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
+}
+
+void InstructionCodeGeneratorARM64::VisitThrow(HThrow* instruction) {
+ codegen_->InvokeRuntime(
+ QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc());
+}
+
+void LocationsBuilderARM64::VisitTypeConversion(HTypeConversion* conversion) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall);
+ Primitive::Type input_type = conversion->GetInputType();
+ Primitive::Type result_type = conversion->GetResultType();
+ if ((input_type == Primitive::kPrimNot) || (input_type == Primitive::kPrimVoid) ||
+ (result_type == Primitive::kPrimNot) || (result_type == Primitive::kPrimVoid)) {
+ LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type;
+ }
+
+ if (IsFPType(input_type)) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
+
+ if (IsFPType(result_type)) {
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+ } else {
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ }
+}
+
+void InstructionCodeGeneratorARM64::VisitTypeConversion(HTypeConversion* conversion) {
+ Primitive::Type result_type = conversion->GetResultType();
+ Primitive::Type input_type = conversion->GetInputType();
+
+ DCHECK_NE(input_type, result_type);
+
+ if (IsIntegralType(result_type) && IsIntegralType(input_type)) {
+ int result_size = Primitive::ComponentSize(result_type);
+ int input_size = Primitive::ComponentSize(input_type);
+ int min_size = kBitsPerByte * std::min(result_size, input_size);
+ if ((result_type == Primitive::kPrimChar) ||
+ ((input_type == Primitive::kPrimChar) && (result_size > input_size))) {
+ __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, min_size);
+ } else {
+ __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, min_size);
+ }
+ return;
+ }
+
+ LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type
+ << " to " << result_type;
+}
+
+void LocationsBuilderARM64::VisitXor(HXor* instruction) {
+ HandleBinaryOp(instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitXor(HXor* instruction) {
+ HandleBinaryOp(instruction);
+}
+
+#undef __
+#undef QUICK_ENTRY_POINT
+
} // namespace arm64
} // namespace art
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 54e87f4d9c..a40f27fafa 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -29,8 +29,11 @@ namespace art {
namespace arm64 {
class CodeGeneratorARM64;
+class SlowPathCodeARM64;
+
+// Use a local definition to prevent copying mistakes.
+static constexpr size_t kArm64WordSize = kArm64PointerSize;
-static constexpr size_t kArm64WordSize = 8;
static const vixl::Register kParameterCoreRegisters[] = {
vixl::x1, vixl::x2, vixl::x3, vixl::x4, vixl::x5, vixl::x6, vixl::x7
};
@@ -103,9 +106,11 @@ class InstructionCodeGeneratorARM64 : public HGraphVisitor {
void LoadCurrentMethod(XRegister reg);
Arm64Assembler* GetAssembler() const { return assembler_; }
+ vixl::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->vixl_masm_; }
private:
- void HandleAddSub(HBinaryOperation* instr);
+ void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, vixl::Register class_reg);
+ void HandleBinaryOp(HBinaryOperation* instr);
Arm64Assembler* const assembler_;
CodeGeneratorARM64* const codegen_;
@@ -124,7 +129,7 @@ class LocationsBuilderARM64 : public HGraphVisitor {
#undef DECLARE_VISIT_INSTRUCTION
private:
- void HandleAddSub(HBinaryOperation* instr);
+ void HandleBinaryOp(HBinaryOperation* instr);
void HandleInvoke(HInvoke* instr);
CodeGeneratorARM64* const codegen_;
@@ -162,9 +167,10 @@ class CodeGeneratorARM64 : public CodeGenerator {
return kArm64WordSize;
}
- uintptr_t GetAddressOf(HBasicBlock* block ATTRIBUTE_UNUSED) const OVERRIDE {
- UNIMPLEMENTED(INFO) << "TODO: GetAddressOf";
- return 0u;
+ uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
+ vixl::Label* block_entry_label = GetLabelOf(block);
+ DCHECK(block_entry_label->IsBound());
+ return block_entry_label->location();
}
size_t FrameEntrySpillSize() const OVERRIDE;
@@ -172,6 +178,7 @@ class CodeGeneratorARM64 : public CodeGenerator {
HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; }
HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; }
Arm64Assembler* GetAssembler() OVERRIDE { return &assembler_; }
+ vixl::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->vixl_masm_; }
// Emit a write barrier.
void MarkGCCard(vixl::Register object, vixl::Register value);
@@ -185,18 +192,18 @@ class CodeGeneratorARM64 : public CodeGenerator {
Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
- size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE {
+ size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
UNUSED(stack_index);
UNUSED(reg_id);
- UNIMPLEMENTED(INFO) << "TODO: SaveCoreRegister";
- return 0;
+ LOG(INFO) << "CodeGeneratorARM64::SaveCoreRegister()";
+ return kArm64WordSize;
}
- size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE {
+ size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
UNUSED(stack_index);
UNUSED(reg_id);
- UNIMPLEMENTED(INFO) << "TODO: RestoreCoreRegister";
- return 0;
+ LOG(INFO) << "CodeGeneratorARM64::RestoreCoreRegister()";
+ return kArm64WordSize;
}
// The number of registers that can be allocated. The register allocator may
@@ -226,9 +233,14 @@ class CodeGeneratorARM64 : public CodeGenerator {
}
// Code generation helpers.
+ void MoveConstant(vixl::CPURegister destination, HConstant* constant);
void MoveHelper(Location destination, Location source, Primitive::Type type);
- void Load(Primitive::Type type, vixl::Register dst, const vixl::MemOperand& src);
- void Store(Primitive::Type type, vixl::Register rt, const vixl::MemOperand& dst);
+ void Load(Primitive::Type type, vixl::CPURegister dst, const vixl::MemOperand& src);
+ void Store(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst);
+ void LoadCurrentMethod(vixl::Register current_method);
+
+ // Generate code to invoke a runtime entry point.
+ void InvokeRuntime(int32_t offset, HInstruction* instruction, uint32_t dex_pc);
ParallelMoveResolver* GetMoveResolver() OVERRIDE {
UNIMPLEMENTED(INFO) << "TODO: MoveResolver";
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 8a8fec2609..3c53cea0bf 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -42,6 +42,9 @@ static constexpr size_t kRuntimeParameterCoreRegistersLength =
static constexpr XmmRegister kRuntimeParameterFpuRegisters[] = { };
static constexpr size_t kRuntimeParameterFpuRegistersLength = 0;
+// Marker for places that can be updated once we don't follow the quick ABI.
+static constexpr bool kFollowsQuickABI = true;
+
class InvokeRuntimeCallingConvention : public CallingConvention<Register, XmmRegister> {
public:
InvokeRuntimeCallingConvention()
@@ -100,19 +103,24 @@ class DivZeroCheckSlowPathX86 : public SlowPathCodeX86 {
DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86);
};
-class DivMinusOneSlowPathX86 : public SlowPathCodeX86 {
+class DivRemMinusOneSlowPathX86 : public SlowPathCodeX86 {
public:
- explicit DivMinusOneSlowPathX86(Register reg) : reg_(reg) {}
+ explicit DivRemMinusOneSlowPathX86(Register reg, bool is_div) : reg_(reg), is_div_(is_div) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
- __ negl(reg_);
+ if (is_div_) {
+ __ negl(reg_);
+ } else {
+ __ movl(reg_, Immediate(0));
+ }
__ jmp(GetExitLabel());
}
private:
Register reg_;
- DISALLOW_COPY_AND_ASSIGN(DivMinusOneSlowPathX86);
+ bool is_div_;
+ DISALLOW_COPY_AND_ASSIGN(DivRemMinusOneSlowPathX86);
};
class StackOverflowCheckSlowPathX86 : public SlowPathCodeX86 {
@@ -427,6 +435,7 @@ void CodeGeneratorX86::SetupBlockedRegisters() const {
blocked_core_registers_[ESP] = true;
// TODO: We currently don't use Quick's callee saved registers.
+ DCHECK(kFollowsQuickABI);
blocked_core_registers_[EBP] = true;
blocked_core_registers_[ESI] = true;
blocked_core_registers_[EDI] = true;
@@ -1111,7 +1120,8 @@ void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
// temp = temp[index_in_cache]
__ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache())));
// (temp + offset_of_quick_compiled_code)()
- __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
+ __ call(Address(
+ temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
@@ -1175,7 +1185,8 @@ void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) {
// temp = temp->GetMethodAt(method_offset);
__ movl(temp, Address(temp, method_offset));
// call temp->GetEntryPoint();
- __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
+ __ call(Address(
+ temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
@@ -1210,7 +1221,8 @@ void InstructionCodeGeneratorX86::VisitInvokeInterface(HInvokeInterface* invoke)
// temp = temp->GetImtEntryAt(method_offset);
__ movl(temp, Address(temp, method_offset));
// call temp->GetEntryPoint();
- __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
+ __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kX86WordSize).Int32Value()));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
@@ -1306,6 +1318,22 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
}
break;
+ case Primitive::kPrimShort:
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ // Processing a Dex `int-to-short' instruction.
+ locations->SetInAt(0, Location::Any());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ }
+ break;
+
case Primitive::kPrimInt:
switch (input_type) {
case Primitive::kPrimLong:
@@ -1367,9 +1395,49 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
break;
case Primitive::kPrimFloat:
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ // Processing a Dex `int-to-float' instruction.
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Type conversion from " << input_type
+ << " to " << result_type << " not yet implemented";
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ };
+ break;
+
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ // Processing a Dex `int-to-double' instruction.
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimFloat:
+ LOG(FATAL) << "Type conversion from " << input_type
+ << " to " << result_type << " not yet implemented";
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ }
break;
default:
@@ -1408,6 +1476,29 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio
}
break;
+ case Primitive::kPrimShort:
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ // Processing a Dex `int-to-short' instruction.
+ if (in.IsRegister()) {
+ __ movsxw(out.As<Register>(), in.As<Register>());
+ } else if (in.IsStackSlot()) {
+ __ movsxw(out.As<Register>(), Address(ESP, in.GetStackIndex()));
+ } else {
+ DCHECK(in.GetConstant()->IsIntConstant());
+ int32_t value = in.GetConstant()->AsIntConstant()->GetValue();
+ __ movl(out.As<Register>(), Immediate(static_cast<int16_t>(value)));
+ }
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ }
+ break;
+
case Primitive::kPrimInt:
switch (input_type) {
case Primitive::kPrimLong:
@@ -1486,9 +1577,47 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio
break;
case Primitive::kPrimFloat:
+ switch (input_type) {
+ // Processing a Dex `int-to-float' instruction.
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ __ cvtsi2ss(out.As<XmmRegister>(), in.As<Register>());
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Type conversion from " << input_type
+ << " to " << result_type << " not yet implemented";
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ };
+ break;
+
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ switch (input_type) {
+ // Processing a Dex `int-to-double' instruction.
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ __ cvtsi2sd(out.As<XmmRegister>(), in.As<Register>());
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimFloat:
+ LOG(FATAL) << "Type conversion from " << input_type
+ << " to " << result_type << " not yet implemented";
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ };
break;
default:
@@ -1753,6 +1882,68 @@ void InstructionCodeGeneratorX86::VisitMul(HMul* mul) {
}
}
+void InstructionCodeGeneratorX86::GenerateDivRemIntegral(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location out = locations->Out();
+ Location first = locations->InAt(0);
+ Location second = locations->InAt(1);
+ bool is_div = instruction->IsDiv();
+
+ switch (instruction->GetResultType()) {
+ case Primitive::kPrimInt: {
+ Register second_reg = second.As<Register>();
+ DCHECK_EQ(EAX, first.As<Register>());
+ DCHECK_EQ(is_div ? EAX : EDX, out.As<Register>());
+
+ SlowPathCodeX86* slow_path =
+ new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86(out.As<Register>(), 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.
+
+ __ 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());
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ InvokeRuntimeCallingConvention calling_convention;
+ DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>());
+ DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>());
+ DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>());
+ DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>());
+ DCHECK_EQ(EAX, out.AsRegisterPairLow<Register>());
+ DCHECK_EQ(EDX, out.AsRegisterPairHigh<Register>());
+
+ if (is_div) {
+ __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pLdiv)));
+ } else {
+ __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pLmod)));
+ }
+ uint32_t dex_pc = is_div
+ ? instruction->AsDiv()->GetDexPc()
+ : instruction->AsRem()->GetDexPc();
+ codegen_->RecordPcInfo(instruction, dex_pc);
+
+ break;
+ }
+
+ default:
+ LOG(FATAL) << "Unexpected type for GenerateDivRemIntegral " << instruction->GetResultType();
+ }
+}
+
void LocationsBuilderX86::VisitDiv(HDiv* div) {
LocationSummary::CallKind call_kind = div->GetResultType() == Primitive::kPrimLong
? LocationSummary::kCall
@@ -1798,45 +1989,9 @@ void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) {
Location second = locations->InAt(1);
switch (div->GetResultType()) {
- case Primitive::kPrimInt: {
- DCHECK(first.Equals(out));
- Register first_reg = first.As<Register>();
- Register second_reg = second.As<Register>();
- DCHECK_EQ(EAX, first_reg);
- DCHECK_EQ(EDX, locations->GetTemp(0).As<Register>());
-
- SlowPathCodeX86* slow_path =
- new (GetGraph()->GetArena()) DivMinusOneSlowPathX86(first_reg);
- 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.
-
- __ 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());
- break;
- }
-
+ case Primitive::kPrimInt:
case Primitive::kPrimLong: {
- InvokeRuntimeCallingConvention calling_convention;
- DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>());
- DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>());
- DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>());
- DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>());
- DCHECK_EQ(EAX, out.AsRegisterPairLow<Register>());
- DCHECK_EQ(EDX, out.AsRegisterPairHigh<Register>());
-
- __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pLdiv)));
- codegen_->RecordPcInfo(div, div->GetDexPc());
-
+ GenerateDivRemIntegral(div);
break;
}
@@ -1857,6 +2012,58 @@ void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) {
}
}
+void LocationsBuilderX86::VisitRem(HRem* rem) {
+ LocationSummary::CallKind call_kind = rem->GetResultType() == Primitive::kPrimLong
+ ? LocationSummary::kCall
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind);
+
+ switch (rem->GetResultType()) {
+ case Primitive::kPrimInt: {
+ locations->SetInAt(0, Location::RegisterLocation(EAX));
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RegisterLocation(EDX));
+ break;
+ }
+ case Primitive::kPrimLong: {
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::RegisterPairLocation(
+ calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(1, Location::RegisterPairLocation(
+ calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
+ // Runtime helper puts the result in EAX, EDX.
+ locations->SetOut(Location::RegisterPairLocation(EAX, EDX));
+ break;
+ }
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+ break;
+ }
+
+ default:
+ LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+ }
+}
+
+void InstructionCodeGeneratorX86::VisitRem(HRem* rem) {
+ Primitive::Type type = rem->GetResultType();
+ switch (type) {
+ case Primitive::kPrimInt:
+ case Primitive::kPrimLong: {
+ GenerateDivRemIntegral(rem);
+ break;
+ }
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ LOG(FATAL) << "Unimplemented rem type " << type;
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected rem type " << type;
+ }
+}
+
void LocationsBuilderX86::VisitDivZeroCheck(HDivZeroCheck* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -2081,7 +2288,9 @@ void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction)
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
Primitive::Type field_type = instruction->GetFieldType();
- bool is_object_type = field_type == Primitive::kPrimNot;
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
+
bool is_byte_type = (field_type == Primitive::kPrimBoolean)
|| (field_type == Primitive::kPrimByte);
// The register allocator does not support multiple
@@ -2093,7 +2302,7 @@ void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction)
locations->SetInAt(1, Location::RequiresRegister());
}
// Temporary registers for the write barrier.
- if (is_object_type) {
+ if (needs_write_barrier) {
locations->AddTemp(Location::RequiresRegister());
// Ensure the card is in a byte register.
locations->AddTemp(Location::RegisterLocation(ECX));
@@ -2126,7 +2335,7 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instr
Register value = locations->InAt(1).As<Register>();
__ movl(Address(obj, offset), value);
- if (field_type == Primitive::kPrimNot) {
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
Register temp = locations->GetTemp(0).As<Register>();
Register card = locations->GetTemp(1).As<Register>();
codegen_->MarkGCCard(temp, card, obj, value);
@@ -2372,11 +2581,20 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) {
Primitive::Type value_type = instruction->GetComponentType();
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
+
+ DCHECK(kFollowsQuickABI);
+ bool not_enough_registers = needs_write_barrier
+ && !instruction->GetValue()->IsConstant()
+ && !instruction->GetIndex()->IsConstant();
+ bool needs_runtime_call = instruction->NeedsTypeCheck() || not_enough_registers;
+
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
instruction,
- value_type == Primitive::kPrimNot ? LocationSummary::kCall : LocationSummary::kNoCall);
+ needs_runtime_call ? LocationSummary::kCall : LocationSummary::kNoCall);
- if (value_type == Primitive::kPrimNot) {
+ if (needs_runtime_call) {
InvokeRuntimeCallingConvention calling_convention;
locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
@@ -2395,6 +2613,12 @@ void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) {
} else {
locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)));
}
+ // Temporary registers for the write barrier.
+ if (needs_write_barrier) {
+ locations->AddTemp(Location::RequiresRegister());
+ // Ensure the card is in a byte register.
+ locations->AddTemp(Location::RegisterLocation(ECX));
+ }
}
}
@@ -2404,6 +2628,9 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
Location index = locations->InAt(1);
Location value = locations->InAt(2);
Primitive::Type value_type = instruction->GetComponentType();
+ bool needs_runtime_call = locations->WillCall();
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
switch (value_type) {
case Primitive::kPrimBoolean:
@@ -2452,34 +2679,45 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
break;
}
- case Primitive::kPrimInt: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
- if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- if (value.IsRegister()) {
- __ movl(Address(obj, offset), value.As<Register>());
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ if (!needs_runtime_call) {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ if (value.IsRegister()) {
+ __ movl(Address(obj, offset), value.As<Register>());
+ } else {
+ DCHECK(value.IsConstant()) << value;
+ __ movl(Address(obj, offset),
+ Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+ }
} else {
- __ movl(Address(obj, offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+ DCHECK(index.IsRegister()) << index;
+ if (value.IsRegister()) {
+ __ movl(Address(obj, index.As<Register>(), TIMES_4, data_offset),
+ value.As<Register>());
+ } else {
+ DCHECK(value.IsConstant()) << value;
+ __ movl(Address(obj, index.As<Register>(), TIMES_4, data_offset),
+ Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+ }
}
- } else {
- if (value.IsRegister()) {
- __ movl(Address(obj, index.As<Register>(), TIMES_4, data_offset),
- value.As<Register>());
- } else {
- __ movl(Address(obj, index.As<Register>(), TIMES_4, data_offset),
- Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+
+ if (needs_write_barrier) {
+ Register temp = locations->GetTemp(0).As<Register>();
+ Register card = locations->GetTemp(1).As<Register>();
+ codegen_->MarkGCCard(temp, card, obj, value.As<Register>());
}
+ } else {
+ DCHECK_EQ(value_type, Primitive::kPrimNot);
+ DCHECK(!codegen_->IsLeafMethod());
+ __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAputObject)));
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
}
break;
}
- case Primitive::kPrimNot: {
- DCHECK(!codegen_->IsLeafMethod());
- __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAputObject)));
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
- break;
- }
-
case Primitive::kPrimLong: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
if (index.IsConstant()) {
@@ -2844,7 +3082,8 @@ void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
Primitive::Type field_type = instruction->GetFieldType();
- bool is_object_type = field_type == Primitive::kPrimNot;
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
bool is_byte_type = (field_type == Primitive::kPrimBoolean)
|| (field_type == Primitive::kPrimByte);
// The register allocator does not support multiple
@@ -2856,7 +3095,7 @@ void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
locations->SetInAt(1, Location::RequiresRegister());
}
// Temporary registers for the write barrier.
- if (is_object_type) {
+ if (needs_write_barrier) {
locations->AddTemp(Location::RequiresRegister());
// Ensure the card is in a byte register.
locations->AddTemp(Location::RegisterLocation(ECX));
@@ -2889,7 +3128,7 @@ void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instructi
Register value = locations->InAt(1).As<Register>();
__ movl(Address(cls, offset), value);
- if (field_type == Primitive::kPrimNot) {
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
Register temp = locations->GetTemp(0).As<Register>();
Register card = locations->GetTemp(1).As<Register>();
codegen_->MarkGCCard(temp, card, cls, value);
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 841b28b158..0aff6cc493 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -25,7 +25,8 @@
namespace art {
namespace x86 {
-static constexpr size_t kX86WordSize = 4;
+// Use a local definition to prevent copying mistakes.
+static constexpr size_t kX86WordSize = kX86PointerSize;
class CodeGeneratorX86;
class SlowPathCodeX86;
@@ -130,6 +131,7 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor {
void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
void GenerateClassInitializationCheck(SlowPathCodeX86* slow_path, Register class_reg);
void HandleBitwiseOperation(HBinaryOperation* instruction);
+ void GenerateDivRemIntegral(HBinaryOperation* instruction);
X86Assembler* const assembler_;
CodeGeneratorX86* const codegen_;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 5aa1c4a6c8..97f5e5c7ac 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -106,26 +106,36 @@ class DivZeroCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86_64);
};
-class DivMinusOneSlowPathX86_64 : public SlowPathCodeX86_64 {
+class DivRemMinusOneSlowPathX86_64 : public SlowPathCodeX86_64 {
public:
- explicit DivMinusOneSlowPathX86_64(Register reg, Primitive::Type type)
- : reg_(reg), type_(type) {}
+ explicit DivRemMinusOneSlowPathX86_64(Register reg, Primitive::Type type, bool is_div)
+ : cpu_reg_(CpuRegister(reg)), type_(type), is_div_(is_div) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
if (type_ == Primitive::kPrimInt) {
- __ negl(CpuRegister(reg_));
+ if (is_div_) {
+ __ negl(cpu_reg_);
+ } else {
+ __ movl(cpu_reg_, Immediate(0));
+ }
+
} else {
DCHECK_EQ(Primitive::kPrimLong, type_);
- __ negq(CpuRegister(reg_));
+ if (is_div_) {
+ __ negq(cpu_reg_);
+ } else {
+ __ movq(cpu_reg_, Immediate(0));
+ }
}
__ jmp(GetExitLabel());
}
private:
- const Register reg_;
+ const CpuRegister cpu_reg_;
const Primitive::Type type_;
- DISALLOW_COPY_AND_ASSIGN(DivMinusOneSlowPathX86_64);
+ const bool is_div_;
+ DISALLOW_COPY_AND_ASSIGN(DivRemMinusOneSlowPathX86_64);
};
class StackOverflowCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
@@ -1102,7 +1112,8 @@ void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
// temp = temp[index_in_cache]
__ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache())));
// (temp + offset_of_quick_compiled_code)()
- __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
+ __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kX86_64WordSize).SizeValue()));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
@@ -1161,7 +1172,8 @@ void InstructionCodeGeneratorX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke)
// temp = temp->GetMethodAt(method_offset);
__ movl(temp, Address(temp, method_offset));
// call temp->GetEntryPoint();
- __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
+ __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kX86_64WordSize).SizeValue()));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
@@ -1196,7 +1208,8 @@ void InstructionCodeGeneratorX86_64::VisitInvokeInterface(HInvokeInterface* invo
// temp = temp->GetImtEntryAt(method_offset);
__ movl(temp, Address(temp, method_offset));
// call temp->GetEntryPoint();
- __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
+ __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kX86_64WordSize).SizeValue()));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
@@ -1304,6 +1317,22 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
}
break;
+ case Primitive::kPrimShort:
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ // Processing a Dex `int-to-short' instruction.
+ locations->SetInAt(0, Location::Any());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ }
+ break;
+
case Primitive::kPrimInt:
switch (input_type) {
case Primitive::kPrimLong:
@@ -1367,9 +1396,49 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) {
break;
case Primitive::kPrimFloat:
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ // Processing a Dex `int-to-float' instruction.
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Type conversion from " << input_type
+ << " to " << result_type << " not yet implemented";
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ };
+ break;
+
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ // Processing a Dex `int-to-double' instruction.
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimFloat:
+ LOG(FATAL) << "Type conversion from " << input_type
+ << " to " << result_type << " not yet implemented";
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ }
break;
default:
@@ -1409,6 +1478,30 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
}
break;
+ case Primitive::kPrimShort:
+ switch (input_type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ // Processing a Dex `int-to-short' instruction.
+ if (in.IsRegister()) {
+ __ movsxw(out.As<CpuRegister>(), in.As<CpuRegister>());
+ } else if (in.IsStackSlot()) {
+ __ movsxw(out.As<CpuRegister>(),
+ Address(CpuRegister(RSP), in.GetStackIndex()));
+ } else {
+ DCHECK(in.GetConstant()->IsIntConstant());
+ __ movl(out.As<CpuRegister>(),
+ Immediate(static_cast<int16_t>(in.GetConstant()->AsIntConstant()->GetValue())));
+ }
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ }
+ break;
+
case Primitive::kPrimInt:
switch (input_type) {
case Primitive::kPrimLong:
@@ -1488,9 +1581,47 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver
break;
case Primitive::kPrimFloat:
+ switch (input_type) {
+ // Processing a Dex `int-to-float' instruction.
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ __ cvtsi2ss(out.As<XmmRegister>(), in.As<CpuRegister>());
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Type conversion from " << input_type
+ << " to " << result_type << " not yet implemented";
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ };
+ break;
+
case Primitive::kPrimDouble:
- LOG(FATAL) << "Type conversion from " << input_type
- << " to " << result_type << " not yet implemented";
+ switch (input_type) {
+ // Processing a Dex `int-to-double' instruction.
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimChar:
+ __ cvtsi2sd(out.As<XmmRegister>(), in.As<CpuRegister>());
+ break;
+
+ case Primitive::kPrimLong:
+ case Primitive::kPrimFloat:
+ LOG(FATAL) << "Type conversion from " << input_type
+ << " to " << result_type << " not yet implemented";
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected type conversion from " << input_type
+ << " to " << result_type;
+ };
break;
default:
@@ -1701,6 +1832,47 @@ void InstructionCodeGeneratorX86_64::VisitMul(HMul* mul) {
}
}
+void InstructionCodeGeneratorX86_64::GenerateDivRemIntegral(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+ Primitive::Type type = instruction->GetResultType();
+ DCHECK(type == Primitive::kPrimInt || Primitive::kPrimLong);
+
+ bool is_div = instruction->IsDiv();
+ LocationSummary* locations = instruction->GetLocations();
+
+ CpuRegister out_reg = locations->Out().As<CpuRegister>();
+ CpuRegister second_reg = locations->InAt(1).As<CpuRegister>();
+
+ DCHECK_EQ(RAX, locations->InAt(0).As<CpuRegister>().AsRegister());
+ DCHECK_EQ(is_div ? RAX : RDX, out_reg.AsRegister());
+
+ SlowPathCodeX86_64* slow_path =
+ new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86_64(
+ out_reg.AsRegister(), type, is_div);
+ codegen_->AddSlowPath(slow_path);
+
+ // 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.
+
+ __ cmpl(second_reg, Immediate(-1));
+ __ j(kEqual, slow_path->GetEntryLabel());
+
+ if (type == Primitive::kPrimInt) {
+ // edx:eax <- sign-extended of eax
+ __ cdq();
+ // eax = quotient, edx = remainder
+ __ idivl(second_reg);
+ } else {
+ // rdx:rax <- sign-extended of rax
+ __ cqo();
+ // rax = quotient, rdx = remainder
+ __ idivq(second_reg);
+ }
+
+ __ Bind(slow_path->GetExitLabel());
+}
+
void LocationsBuilderX86_64::VisitDiv(HDiv* div) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall);
@@ -1738,35 +1910,7 @@ void InstructionCodeGeneratorX86_64::VisitDiv(HDiv* div) {
switch (type) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
- CpuRegister first_reg = first.As<CpuRegister>();
- CpuRegister second_reg = second.As<CpuRegister>();
- DCHECK_EQ(RAX, first_reg.AsRegister());
- DCHECK_EQ(RDX, locations->GetTemp(0).As<CpuRegister>().AsRegister());
-
- SlowPathCodeX86_64* slow_path =
- new (GetGraph()->GetArena()) DivMinusOneSlowPathX86_64(first_reg.AsRegister(), type);
- codegen_->AddSlowPath(slow_path);
-
- // 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.
-
- __ cmpl(second_reg, Immediate(-1));
- __ j(kEqual, slow_path->GetEntryLabel());
-
- if (type == Primitive::kPrimInt) {
- // edx:eax <- sign-extended of eax
- __ cdq();
- // eax = quotient, edx = remainder
- __ idivl(second_reg);
- } else {
- // rdx:rax <- sign-extended of rax
- __ cqo();
- // rax = quotient, rdx = remainder
- __ idivq(second_reg);
- }
-
- __ Bind(slow_path->GetExitLabel());
+ GenerateDivRemIntegral(div);
break;
}
@@ -1785,6 +1929,50 @@ void InstructionCodeGeneratorX86_64::VisitDiv(HDiv* div) {
}
}
+void LocationsBuilderX86_64::VisitRem(HRem* rem) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(rem, LocationSummary::kNoCall);
+ switch (rem->GetResultType()) {
+ case Primitive::kPrimInt:
+ case Primitive::kPrimLong: {
+ locations->SetInAt(0, Location::RegisterLocation(RAX));
+ locations->SetInAt(1, Location::RequiresRegister());
+ // Intel uses rdx:rax as the dividend and puts the remainder in rdx
+ locations->SetOut(Location::RegisterLocation(RDX));
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+ break;
+ }
+
+ default:
+ LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+ }
+}
+
+void InstructionCodeGeneratorX86_64::VisitRem(HRem* rem) {
+ Primitive::Type type = rem->GetResultType();
+ switch (type) {
+ case Primitive::kPrimInt:
+ case Primitive::kPrimLong: {
+ GenerateDivRemIntegral(rem);
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType();
+ break;
+ }
+
+ default:
+ LOG(FATAL) << "Unexpected rem type " << rem->GetResultType();
+ }
+}
+
void LocationsBuilderX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -1946,10 +2134,11 @@ void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instructio
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
Primitive::Type field_type = instruction->GetFieldType();
- bool is_object_type = field_type == Primitive::kPrimNot;
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue());
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
- if (is_object_type) {
+ if (needs_write_barrier) {
// Temporary registers for the write barrier.
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
@@ -1981,7 +2170,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* in
case Primitive::kPrimNot: {
CpuRegister value = locations->InAt(1).As<CpuRegister>();
__ movl(Address(obj, offset), value);
- if (field_type == Primitive::kPrimNot) {
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) {
CpuRegister temp = locations->GetTemp(0).As<CpuRegister>();
CpuRegister card = locations->GetTemp(1).As<CpuRegister>();
codegen_->MarkGCCard(temp, card, obj, value);
@@ -2231,10 +2420,14 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) {
Primitive::Type value_type = instruction->GetComponentType();
- bool is_object = value_type == Primitive::kPrimNot;
+
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
+ bool needs_runtime_call = instruction->NeedsTypeCheck();
+
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
- instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall);
- if (is_object) {
+ instruction, needs_runtime_call ? LocationSummary::kCall : LocationSummary::kNoCall);
+ if (needs_runtime_call) {
InvokeRuntimeCallingConvention calling_convention;
locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
@@ -2251,6 +2444,12 @@ void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) {
} else {
locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)));
}
+
+ if (needs_write_barrier) {
+ // Temporary registers for the write barrier.
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
}
@@ -2260,6 +2459,9 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
Location index = locations->InAt(1);
Location value = locations->InAt(2);
Primitive::Type value_type = instruction->GetComponentType();
+ bool needs_runtime_call = locations->WillCall();
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
switch (value_type) {
case Primitive::kPrimBoolean:
@@ -2292,13 +2494,16 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
if (value.IsRegister()) {
__ movw(Address(obj, offset), value.As<CpuRegister>());
} else {
+ DCHECK(value.IsConstant()) << value;
__ movw(Address(obj, offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
} else {
+ DCHECK(index.IsRegister()) << index;
if (value.IsRegister()) {
__ movw(Address(obj, index.As<CpuRegister>(), TIMES_2, data_offset),
value.As<CpuRegister>());
} else {
+ DCHECK(value.IsConstant()) << value;
__ movw(Address(obj, index.As<CpuRegister>(), TIMES_2, data_offset),
Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
}
@@ -2306,35 +2511,47 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
break;
}
- case Primitive::kPrimInt: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
- if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- if (value.IsRegister()) {
- __ movl(Address(obj, offset), value.As<CpuRegister>());
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ if (!needs_runtime_call) {
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ if (index.IsConstant()) {
+ size_t offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ if (value.IsRegister()) {
+ __ movl(Address(obj, offset), value.As<CpuRegister>());
+ } else {
+ DCHECK(value.IsConstant()) << value;
+ __ movl(Address(obj, offset),
+ Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+ }
} else {
- __ movl(Address(obj, offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+ DCHECK(index.IsRegister()) << index;
+ if (value.IsRegister()) {
+ __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset),
+ value.As<CpuRegister>());
+ } else {
+ DCHECK(value.IsConstant()) << value;
+ __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset),
+ Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+ }
}
- } else {
- if (value.IsRegister()) {
- __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset),
- value.As<CpuRegister>());
- } else {
- DCHECK(value.IsConstant()) << value;
- __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset),
- Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
+
+ if (needs_write_barrier) {
+ DCHECK_EQ(value_type, Primitive::kPrimNot);
+ CpuRegister temp = locations->GetTemp(0).As<CpuRegister>();
+ CpuRegister card = locations->GetTemp(1).As<CpuRegister>();
+ codegen_->MarkGCCard(temp, card, obj, value.As<CpuRegister>());
}
+ } else {
+ DCHECK_EQ(value_type, Primitive::kPrimNot);
+ __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAputObject), true));
+ DCHECK(!codegen_->IsLeafMethod());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
}
break;
}
- case Primitive::kPrimNot: {
- __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAputObject), true));
- DCHECK(!codegen_->IsLeafMethod());
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
- break;
- }
-
case Primitive::kPrimLong: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
if (index.IsConstant()) {
@@ -2813,10 +3030,11 @@ void LocationsBuilderX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
Primitive::Type field_type = instruction->GetFieldType();
- bool is_object_type = field_type == Primitive::kPrimNot;
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue());
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
- if (is_object_type) {
+ if (needs_write_barrier) {
// Temporary registers for the write barrier.
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
@@ -2848,7 +3066,7 @@ void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instru
case Primitive::kPrimNot: {
CpuRegister value = locations->InAt(1).As<CpuRegister>();
__ movl(Address(cls, offset), value);
- if (field_type == Primitive::kPrimNot) {
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) {
CpuRegister temp = locations->GetTemp(0).As<CpuRegister>();
CpuRegister card = locations->GetTemp(1).As<CpuRegister>();
codegen_->MarkGCCard(temp, card, cls, value);
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 4c6e4750d7..29c679d8f1 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -25,7 +25,8 @@
namespace art {
namespace x86_64 {
-static constexpr size_t kX86_64WordSize = 8;
+// Use a local definition to prevent copying mistakes.
+static constexpr size_t kX86_64WordSize = kX86_64PointerSize;
static constexpr Register kParameterCoreRegisters[] = { RSI, RDX, RCX, R8, R9 };
static constexpr FloatRegister kParameterFloatRegisters[] =
@@ -134,6 +135,7 @@ class InstructionCodeGeneratorX86_64 : public HGraphVisitor {
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void GenerateClassInitializationCheck(SlowPathCodeX86_64* slow_path, CpuRegister class_reg);
void HandleBitwiseOperation(HBinaryOperation* operation);
+ void GenerateDivRemIntegral(HBinaryOperation* instruction);
X86_64Assembler* const assembler_;
CodeGeneratorX86_64* const codegen_;
diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h
index d2acfa6973..ac00824e33 100644
--- a/compiler/optimizing/constant_folding.h
+++ b/compiler/optimizing/constant_folding.h
@@ -32,10 +32,10 @@ namespace art {
*/
class HConstantFolding : public HOptimization {
public:
- HConstantFolding(HGraph* graph, const HGraphVisualizer& visualizer)
- : HOptimization(graph, true, kConstantFoldingPassName, visualizer) {}
+ explicit HConstantFolding(HGraph* graph)
+ : HOptimization(graph, true, kConstantFoldingPassName) {}
- virtual void Run() OVERRIDE;
+ void Run() OVERRIDE;
static constexpr const char* kConstantFoldingPassName = "constant_folding";
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index 856c5165a3..a56b9d9a12 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -47,8 +47,7 @@ static void TestCode(const uint16_t* data,
ASSERT_EQ(expected_before, actual_before);
x86::CodeGeneratorX86 codegen(graph);
- HGraphVisualizer visualizer(nullptr, graph, codegen, "");
- HConstantFolding(graph, visualizer).Run();
+ HConstantFolding(graph).Run();
SSAChecker ssa_checker(&allocator, graph);
ssa_checker.Run();
ASSERT_TRUE(ssa_checker.IsValid());
@@ -60,7 +59,7 @@ static void TestCode(const uint16_t* data,
check_after_cf(graph);
- HDeadCodeElimination(graph, visualizer).Run();
+ HDeadCodeElimination(graph).Run();
ssa_checker.Run();
ASSERT_TRUE(ssa_checker.IsValid());
diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h
index a4446ae04d..3db2c3ff3f 100644
--- a/compiler/optimizing/dead_code_elimination.h
+++ b/compiler/optimizing/dead_code_elimination.h
@@ -28,10 +28,10 @@ namespace art {
*/
class HDeadCodeElimination : public HOptimization {
public:
- HDeadCodeElimination(HGraph* graph, const HGraphVisualizer& visualizer)
- : HOptimization(graph, true, kDeadCodeEliminationPassName, visualizer) {}
+ explicit HDeadCodeElimination(HGraph* graph)
+ : HOptimization(graph, true, kDeadCodeEliminationPassName) {}
- virtual void Run() OVERRIDE;
+ void Run() OVERRIDE;
static constexpr const char* kDeadCodeEliminationPassName =
"dead_code_elimination";
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index 0c6807482a..5d4b9cb024 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -41,8 +41,7 @@ static void TestCode(const uint16_t* data,
ASSERT_EQ(actual_before, expected_before);
x86::CodeGeneratorX86 codegen(graph);
- HGraphVisualizer visualizer(nullptr, graph, codegen, "");
- HDeadCodeElimination(graph, visualizer).Run();
+ HDeadCodeElimination(graph).Run();
SSAChecker ssa_checker(&allocator, graph);
ssa_checker.Run();
ASSERT_TRUE(ssa_checker.IsValid());
diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h
index 8d2c77475c..a841d5f65a 100644
--- a/compiler/optimizing/gvn.h
+++ b/compiler/optimizing/gvn.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_GVN_H_
#include "nodes.h"
+#include "optimization.h"
namespace art {
@@ -165,11 +166,11 @@ class ValueSet : public ArenaObject<kArenaAllocMisc> {
/**
* Optimization phase that removes redundant instruction.
*/
-class GlobalValueNumberer : public ValueObject {
+class GlobalValueNumberer : public HOptimization {
public:
GlobalValueNumberer(ArenaAllocator* allocator, HGraph* graph)
- : allocator_(allocator),
- graph_(graph),
+ : HOptimization(graph, true, "GVN"),
+ allocator_(allocator),
block_effects_(allocator, graph->GetBlocks().Size()),
loop_effects_(allocator, graph->GetBlocks().Size()),
sets_(allocator, graph->GetBlocks().Size()),
@@ -186,7 +187,7 @@ class GlobalValueNumberer : public ValueObject {
}
}
- void Run();
+ void Run() OVERRIDE;
private:
// Per-block GVN. Will also update the ValueSet of the dominated and
@@ -202,7 +203,6 @@ class GlobalValueNumberer : public ValueObject {
SideEffects GetBlockEffects(HBasicBlock* block) const;
ArenaAllocator* const allocator_;
- HGraph* const graph_;
// Side effects of individual blocks, that is the union of the side effects
// of the instructions in the block.
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 29eabe7e29..3d65e9a0a4 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -18,11 +18,22 @@
namespace art {
+class InstructionSimplifierVisitor : public HGraphVisitor {
+ public:
+ explicit InstructionSimplifierVisitor(HGraph* graph) : HGraphVisitor(graph) {}
+
+ private:
+ void VisitSuspendCheck(HSuspendCheck* check) OVERRIDE;
+ void VisitEqual(HEqual* equal) OVERRIDE;
+ void VisitArraySet(HArraySet* equal) OVERRIDE;
+};
+
void InstructionSimplifier::Run() {
- VisitInsertionOrder();
+ InstructionSimplifierVisitor visitor(graph_);
+ visitor.VisitInsertionOrder();
}
-void InstructionSimplifier::VisitSuspendCheck(HSuspendCheck* check) {
+void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) {
HBasicBlock* block = check->GetBlock();
// Currently always keep the suspend check at entry.
if (block->IsEntryBlock()) return;
@@ -38,7 +49,7 @@ void InstructionSimplifier::VisitSuspendCheck(HSuspendCheck* check) {
block->RemoveInstruction(check);
}
-void InstructionSimplifier::VisitEqual(HEqual* equal) {
+void InstructionSimplifierVisitor::VisitEqual(HEqual* equal) {
HInstruction* input1 = equal->InputAt(0);
HInstruction* input2 = equal->InputAt(1);
if (input1->GetType() == Primitive::kPrimBoolean && input2->IsIntConstant()) {
@@ -55,4 +66,16 @@ void InstructionSimplifier::VisitEqual(HEqual* equal) {
}
}
+void InstructionSimplifierVisitor::VisitArraySet(HArraySet* instruction) {
+ HInstruction* value = instruction->GetValue();
+ if (value->GetType() != Primitive::kPrimNot) return;
+
+ if (value->IsArrayGet()) {
+ if (value->AsArrayGet()->GetArray() == instruction->GetArray()) {
+ // If the code is just swapping elements in the array, no need for a type check.
+ instruction->ClearNeedsTypeCheck();
+ }
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
index d74b624518..7068c7fc10 100644
--- a/compiler/optimizing/instruction_simplifier.h
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -18,21 +18,19 @@
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_
#include "nodes.h"
+#include "optimization.h"
namespace art {
/**
* Implements optimizations specific to each instruction.
*/
-class InstructionSimplifier : public HGraphVisitor {
+class InstructionSimplifier : public HOptimization {
public:
- explicit InstructionSimplifier(HGraph* graph) : HGraphVisitor(graph) {}
+ explicit InstructionSimplifier(HGraph* graph)
+ : HOptimization(graph, true, "instruction_simplifier") {}
- void Run();
-
- private:
- virtual void VisitSuspendCheck(HSuspendCheck* check) OVERRIDE;
- virtual void VisitEqual(HEqual* equal) OVERRIDE;
+ void Run() OVERRIDE;
};
} // namespace art
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index d1555d4e11..e1c8e8ed6e 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -391,6 +391,10 @@ class RegisterSet : public ValueObject {
return (register_set & (1 << reg)) != 0;
}
+ size_t GetNumberOfRegisters() const {
+ return __builtin_popcount(core_registers_) + __builtin_popcount(floating_point_registers_);
+ }
+
private:
uint32_t core_registers_;
uint32_t floating_point_registers_;
@@ -503,6 +507,10 @@ class LocationSummary : public ArenaObject<kArenaAllocMisc> {
return &live_registers_;
}
+ size_t GetNumberOfLiveRegisters() const {
+ return live_registers_.GetNumberOfRegisters();
+ }
+
bool InputOverlapsWithOutputOrTemp(uint32_t input_index, bool is_environment) const {
if (is_environment) return true;
if ((input_index == 0)
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 5af3cdd2d6..7d52d7d221 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -521,6 +521,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> {
M(ParallelMove, Instruction) \
M(ParameterValue, Instruction) \
M(Phi, Instruction) \
+ M(Rem, BinaryOperation) \
M(Return, Instruction) \
M(ReturnVoid, Instruction) \
M(StaticFieldGet, Instruction) \
@@ -1756,10 +1757,15 @@ class HDiv : public HBinaryOperation {
virtual int32_t Evaluate(int32_t x, int32_t y) const {
// Our graph structure ensures we never have 0 for `y` during constant folding.
DCHECK_NE(y, 0);
- // Special case -1 to avoid getting a SIGFPE on x86.
+ // Special case -1 to avoid getting a SIGFPE on x86(_64).
+ return (y == -1) ? -x : x / y;
+ }
+
+ virtual int64_t Evaluate(int64_t x, int64_t y) const {
+ DCHECK_NE(y, 0);
+ // Special case -1 to avoid getting a SIGFPE on x86(_64).
return (y == -1) ? -x : x / y;
}
- virtual int64_t Evaluate(int64_t x, int64_t y) const { return x / y; }
uint32_t GetDexPc() const { return dex_pc_; }
@@ -1771,6 +1777,33 @@ class HDiv : public HBinaryOperation {
DISALLOW_COPY_AND_ASSIGN(HDiv);
};
+class HRem : public HBinaryOperation {
+ public:
+ HRem(Primitive::Type result_type, HInstruction* left, HInstruction* right, uint32_t dex_pc)
+ : HBinaryOperation(result_type, left, right), dex_pc_(dex_pc) {}
+
+ virtual int32_t Evaluate(int32_t x, int32_t y) const {
+ DCHECK_NE(y, 0);
+ // Special case -1 to avoid getting a SIGFPE on x86(_64).
+ return (y == -1) ? 0 : x % y;
+ }
+
+ virtual int64_t Evaluate(int64_t x, int64_t y) const {
+ DCHECK_NE(y, 0);
+ // Special case -1 to avoid getting a SIGFPE on x86(_64).
+ return (y == -1) ? 0 : x % y;
+ }
+
+ uint32_t GetDexPc() const { return dex_pc_; }
+
+ DECLARE_INSTRUCTION(Rem);
+
+ private:
+ const uint32_t dex_pc_;
+
+ DISALLOW_COPY_AND_ASSIGN(HRem);
+};
+
class HDivZeroCheck : public HExpression<1> {
public:
HDivZeroCheck(HInstruction* value, uint32_t dex_pc)
@@ -2034,6 +2067,8 @@ class HInstanceFieldSet : public HTemplateInstruction<2> {
MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
+ HInstruction* GetValue() const { return InputAt(1); }
+
DECLARE_INSTRUCTION(InstanceFieldSet);
private:
@@ -2050,13 +2085,16 @@ class HArrayGet : public HExpression<2> {
SetRawInputAt(1, index);
}
- virtual bool CanBeMoved() const { return true; }
- virtual bool InstructionDataEquals(HInstruction* other) const {
+ bool CanBeMoved() const OVERRIDE { return true; }
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
UNUSED(other);
return true;
}
void SetType(Primitive::Type type) { type_ = type; }
+ HInstruction* GetArray() const { return InputAt(0); }
+ HInstruction* GetIndex() const { return InputAt(1); }
+
DECLARE_INSTRUCTION(ArrayGet);
private:
@@ -2072,20 +2110,29 @@ class HArraySet : public HTemplateInstruction<3> {
uint32_t dex_pc)
: HTemplateInstruction(SideEffects::ChangesSomething()),
dex_pc_(dex_pc),
- expected_component_type_(expected_component_type) {
+ expected_component_type_(expected_component_type),
+ needs_type_check_(value->GetType() == Primitive::kPrimNot) {
SetRawInputAt(0, array);
SetRawInputAt(1, index);
SetRawInputAt(2, value);
}
- virtual bool NeedsEnvironment() const {
+ bool NeedsEnvironment() const {
// We currently always call a runtime method to catch array store
// exceptions.
- return InputAt(2)->GetType() == Primitive::kPrimNot;
+ return needs_type_check_;
}
+ void ClearNeedsTypeCheck() {
+ needs_type_check_ = false;
+ }
+
+ bool NeedsTypeCheck() const { return needs_type_check_; }
+
uint32_t GetDexPc() const { return dex_pc_; }
+ HInstruction* GetArray() const { return InputAt(0); }
+ HInstruction* GetIndex() const { return InputAt(1); }
HInstruction* GetValue() const { return InputAt(2); }
Primitive::Type GetComponentType() const {
@@ -2104,6 +2151,7 @@ class HArraySet : public HTemplateInstruction<3> {
private:
const uint32_t dex_pc_;
const Primitive::Type expected_component_type_;
+ bool needs_type_check_;
DISALLOW_COPY_AND_ASSIGN(HArraySet);
};
@@ -2372,6 +2420,8 @@ class HStaticFieldSet : public HTemplateInstruction<2> {
MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
+ HInstruction* GetValue() const { return InputAt(1); }
+
DECLARE_INSTRUCTION(StaticFieldSet);
private:
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
index ea98186d11..d1178d5798 100644
--- a/compiler/optimizing/optimization.cc
+++ b/compiler/optimizing/optimization.cc
@@ -21,12 +21,6 @@
namespace art {
-void HOptimization::Execute() {
- Run();
- visualizer_.DumpGraph(pass_name_);
- Check();
-}
-
void HOptimization::Check() {
if (kIsDebugBuild) {
if (is_in_ssa_form_) {
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index 59683e2075..d281248f4a 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -29,25 +29,19 @@ class HOptimization : public ValueObject {
public:
HOptimization(HGraph* graph,
bool is_in_ssa_form,
- const char* pass_name,
- const HGraphVisualizer& visualizer)
+ const char* pass_name)
: graph_(graph),
is_in_ssa_form_(is_in_ssa_form),
- pass_name_(pass_name),
- visualizer_(visualizer) {}
+ pass_name_(pass_name) {}
virtual ~HOptimization() {}
- // Execute the optimization pass.
- void Execute();
-
// Return the name of the pass.
const char* GetPassName() const { return pass_name_; }
// Peform the analysis itself.
virtual void Run() = 0;
- private:
// Verify the graph; abort if it is not valid.
void Check();
@@ -59,9 +53,6 @@ class HOptimization : public ValueObject {
const bool is_in_ssa_form_;
// Optimization pass name.
const char* pass_name_;
- // A graph visualiser invoked after the execution of the optimization
- // pass if enabled.
- const HGraphVisualizer& visualizer_;
DISALLOW_COPY_AND_ASSIGN(HOptimization);
};
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 6e3653a359..42ac77d1d8 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -26,9 +26,12 @@
#include "dead_code_elimination.h"
#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
+#include "elf_writer_quick.h"
#include "graph_visualizer.h"
#include "gvn.h"
#include "instruction_simplifier.h"
+#include "jni/quick/jni_compiler.h"
+#include "mirror/art_method-inl.h"
#include "nodes.h"
#include "prepare_for_register_allocation.h"
#include "register_allocator.h"
@@ -88,15 +91,6 @@ class OptimizingCompiler FINAL : public Compiler {
jobject class_loader,
const DexFile& dex_file) const OVERRIDE;
- CompiledMethod* TryCompile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file) const;
-
- // For the following methods we will use the fallback. This is a delegation pattern.
CompiledMethod* JniCompile(uint32_t access_flags,
uint32_t method_idx,
const DexFile& dex_file) const OVERRIDE;
@@ -110,13 +104,16 @@ class OptimizingCompiler FINAL : public Compiler {
const std::string& android_root,
bool is_host) const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const OVERRIDE;
+ Backend* GetCodeGenerator(CompilationUnit* cu ATTRIBUTE_UNUSED,
+ void* compilation_unit ATTRIBUTE_UNUSED) const OVERRIDE {
+ return nullptr;
+ }
- void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;
+ void InitCompilationUnit(CompilationUnit& cu ATTRIBUTE_UNUSED) const OVERRIDE {}
- void Init() const OVERRIDE;
+ void Init() const OVERRIDE {}
- void UnInit() const OVERRIDE;
+ void UnInit() const OVERRIDE {}
private:
// Whether we should run any optimization or register allocation. If false, will
@@ -128,10 +125,6 @@ class OptimizingCompiler FINAL : public Compiler {
std::unique_ptr<std::ostream> visualizer_output_;
- // Delegate to another compiler in case the optimizing compiler cannot compile a method.
- // Currently the fallback is the quick compiler.
- std::unique_ptr<Compiler> delegate_;
-
DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler);
};
@@ -143,21 +136,12 @@ OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver)
driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime),
total_compiled_methods_(0),
unoptimized_compiled_methods_(0),
- optimized_compiled_methods_(0),
- delegate_(Create(driver, Compiler::Kind::kQuick)) {
+ optimized_compiled_methods_(0) {
if (kIsVisualizerEnabled) {
visualizer_output_.reset(new std::ofstream("art.cfg"));
}
}
-void OptimizingCompiler::Init() const {
- delegate_->Init();
-}
-
-void OptimizingCompiler::UnInit() const {
- delegate_->UnInit();
-}
-
OptimizingCompiler::~OptimizingCompiler() {
if (total_compiled_methods_ == 0) {
LOG(INFO) << "Did not compile any method.";
@@ -170,33 +154,28 @@ OptimizingCompiler::~OptimizingCompiler() {
}
}
-bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx, const DexFile& dex_file,
- CompilationUnit* cu) const {
- return delegate_->CanCompileMethod(method_idx, dex_file, cu);
+bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx ATTRIBUTE_UNUSED,
+ const DexFile& dex_file ATTRIBUTE_UNUSED,
+ CompilationUnit* cu ATTRIBUTE_UNUSED) const {
+ return true;
}
CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags,
uint32_t method_idx,
const DexFile& dex_file) const {
- return delegate_->JniCompile(access_flags, method_idx, dex_file);
+ return ArtQuickJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
}
uintptr_t OptimizingCompiler::GetEntryPointOf(mirror::ArtMethod* method) const {
- return delegate_->GetEntryPointOf(method);
+ return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCodePtrSize(
+ InstructionSetPointerSize(GetCompilerDriver()->GetInstructionSet())));
}
bool OptimizingCompiler::WriteElf(art::File* file, OatWriter* oat_writer,
const std::vector<const art::DexFile*>& dex_files,
const std::string& android_root, bool is_host) const {
- return delegate_->WriteElf(file, oat_writer, dex_files, android_root, is_host);
-}
-
-Backend* OptimizingCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
- return delegate_->GetCodeGenerator(cu, compilation_unit);
-}
-
-void OptimizingCompiler::InitCompilationUnit(CompilationUnit& cu) const {
- delegate_->InitCompilationUnit(cu);
+ return art::ElfWriterQuick32::Create(file, oat_writer, dex_files, android_root, is_host,
+ *GetCompilerDriver());
}
static bool IsInstructionSetSupported(InstructionSet instruction_set) {
@@ -211,13 +190,32 @@ static bool CanOptimize(const DexFile::CodeItem& code_item) {
return code_item.tries_size_ == 0;
}
-CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file) const {
+static void RunOptimizations(HGraph* graph, const HGraphVisualizer& visualizer) {
+ HDeadCodeElimination opt1(graph);
+ HConstantFolding opt2(graph);
+ SsaRedundantPhiElimination opt3(graph);
+ SsaDeadPhiElimination opt4(graph);
+ InstructionSimplifier opt5(graph);
+ GlobalValueNumberer opt6(graph->GetArena(), graph);
+ InstructionSimplifier opt7(graph);
+
+ HOptimization* optimizations[] = { &opt1, &opt2, &opt3, &opt4, &opt5, &opt6, &opt7 };
+
+ for (size_t i = 0; i < arraysize(optimizations); ++i) {
+ HOptimization* optimization = optimizations[i];
+ optimization->Run();
+ optimization->Check();
+ visualizer.DumpGraph(optimization->GetPassName());
+ }
+}
+
+CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const DexFile& dex_file) const {
UNUSED(invoke_type);
total_compiled_methods_++;
InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet();
@@ -278,16 +276,9 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite
visualizer.DumpGraph("ssa");
graph->FindNaturalLoops();
- HDeadCodeElimination(graph, visualizer).Execute();
- HConstantFolding(graph, visualizer).Execute();
+ RunOptimizations(graph, visualizer);
- SsaRedundantPhiElimination(graph).Run();
- SsaDeadPhiElimination(graph).Run();
- InstructionSimplifier(graph).Run();
- GlobalValueNumberer(graph->GetArena(), graph).Run();
- visualizer.DumpGraph(kGVNPassName);
PrepareForRegisterAllocation(graph).Run();
-
SsaLivenessAnalysis liveness(*graph, codegen);
liveness.Analyze();
visualizer.DumpGraph(kLivenessPassName);
@@ -360,23 +351,6 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite
}
}
-CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file) const {
- CompiledMethod* method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
- method_idx, class_loader, dex_file);
- if (method != nullptr) {
- return method;
- }
-
- return delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx,
- class_loader, dex_file);
-}
-
Compiler* CreateOptimizingCompiler(CompilerDriver* driver) {
return new OptimizingCompiler(driver);
}
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 4d6e66413d..2948496e15 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -215,9 +215,16 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) {
// By adding the following interval in the algorithm, we can compute this
// maximum before updating locations.
LiveInterval* interval = LiveInterval::MakeSlowPathInterval(allocator_, instruction);
- interval->AddRange(position, position + 1);
- unhandled_core_intervals_.Add(interval);
- unhandled_fp_intervals_.Add(interval);
+ // The start of the interval must be after the position of the safepoint, so that
+ // we can just check the number of active registers at that position. Note that this
+ // will include the current interval in the computation of
+ // `maximum_number_of_live_registers`, so we need a better strategy if this becomes
+ // a problem.
+ // TODO: We could put the logic in AddSorted, to ensure the safepoint range is
+ // after all other intervals starting at that same position.
+ interval->AddRange(position + 1, position + 2);
+ AddSorted(&unhandled_core_intervals_, interval);
+ AddSorted(&unhandled_fp_intervals_, interval);
}
}
@@ -250,6 +257,7 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) {
: unhandled_fp_intervals_;
DCHECK(unhandled.IsEmpty() || current->StartsBeforeOrAt(unhandled.Peek()));
+
// Some instructions define their output in fixed register/stack slot. We need
// to ensure we know these locations before doing register allocation. For a
// given register, we create an interval that covers these locations. The register
@@ -475,6 +483,17 @@ void RegisterAllocator::LinearScan() {
LiveInterval* current = unhandled_->Pop();
DCHECK(!current->IsFixed() && !current->HasSpillSlot());
DCHECK(unhandled_->IsEmpty() || unhandled_->Peek()->GetStart() >= current->GetStart());
+
+ if (current->IsSlowPathSafepoint()) {
+ // Synthesized interval to record the maximum number of live registers
+ // at safepoints. No need to allocate a register for it.
+ // We know that current actives are all live at the safepoint (modulo
+ // the one created by the safepoint).
+ maximum_number_of_live_registers_ =
+ std::max(maximum_number_of_live_registers_, active_.Size());
+ continue;
+ }
+
size_t position = current->GetStart();
// Remember the inactive_ size here since the ones moved to inactive_ from
@@ -515,14 +534,6 @@ void RegisterAllocator::LinearScan() {
}
}
- if (current->IsSlowPathSafepoint()) {
- // Synthesized interval to record the maximum number of live registers
- // at safepoints. No need to allocate a register for it.
- maximum_number_of_live_registers_ =
- std::max(maximum_number_of_live_registers_, active_.Size());
- continue;
- }
-
// (4) Try to find an available register.
bool success = TryAllocateFreeReg(current);
@@ -1062,6 +1073,7 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) {
switch (source.GetKind()) {
case Location::kRegister: {
locations->AddLiveRegister(source);
+ DCHECK_LE(locations->GetNumberOfLiveRegisters(), maximum_number_of_live_registers_);
if (current->GetType() == Primitive::kPrimNot) {
locations->SetRegisterBit(source.reg());
}
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index fec40f93c7..b2cc11996e 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -183,8 +183,7 @@ static HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant) {
static HPhi* GetFloatOrDoubleEquivalentOfPhi(HPhi* phi, Primitive::Type type) {
// We place the floating point phi next to this phi.
HInstruction* next = phi->GetNext();
- if (next == nullptr
- || (next->GetType() != Primitive::kPrimDouble && next->GetType() != Primitive::kPrimFloat)) {
+ if (next == nullptr || (next->AsPhi()->GetRegNumber() != phi->GetRegNumber())) {
ArenaAllocator* allocator = phi->GetBlock()->GetGraph()->GetArena();
HPhi* new_phi = new (allocator) HPhi(allocator, phi->GetRegNumber(), phi->InputCount(), type);
for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
@@ -195,9 +194,7 @@ static HPhi* GetFloatOrDoubleEquivalentOfPhi(HPhi* phi, Primitive::Type type) {
phi->GetBlock()->InsertPhiAfter(new_phi, phi);
return new_phi;
} else {
- // If there is already a phi with the expected type, we know it is the floating
- // point equivalent of this phi.
- DCHECK_EQ(next->AsPhi()->GetRegNumber(), phi->GetRegNumber());
+ DCHECK_EQ(next->GetType(), type);
return next->AsPhi();
}
}
diff --git a/compiler/optimizing/ssa_phi_elimination.h b/compiler/optimizing/ssa_phi_elimination.h
index 5274f09f3f..b7899712d6 100644
--- a/compiler/optimizing/ssa_phi_elimination.h
+++ b/compiler/optimizing/ssa_phi_elimination.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_SSA_PHI_ELIMINATION_H_
#include "nodes.h"
+#include "optimization.h"
namespace art {
@@ -25,15 +26,15 @@ namespace art {
* Optimization phase that removes dead phis from the graph. Dead phis are unused
* phis, or phis only used by other phis.
*/
-class SsaDeadPhiElimination : public ValueObject {
+class SsaDeadPhiElimination : public HOptimization {
public:
explicit SsaDeadPhiElimination(HGraph* graph)
- : graph_(graph), worklist_(graph->GetArena(), kDefaultWorklistSize) {}
+ : HOptimization(graph, true, "dead_phi_elimination"),
+ worklist_(graph->GetArena(), kDefaultWorklistSize) {}
- void Run();
+ void Run() OVERRIDE;
private:
- HGraph* const graph_;
GrowableArray<HPhi*> worklist_;
static constexpr size_t kDefaultWorklistSize = 8;
@@ -47,15 +48,15 @@ class SsaDeadPhiElimination : public ValueObject {
* registers might be updated with the same value, or not updated at all. We can just
* replace the phi with the value when entering the loop.
*/
-class SsaRedundantPhiElimination : public ValueObject {
+class SsaRedundantPhiElimination : public HOptimization {
public:
explicit SsaRedundantPhiElimination(HGraph* graph)
- : graph_(graph), worklist_(graph->GetArena(), kDefaultWorklistSize) {}
+ : HOptimization(graph, true, "redundant_phi_elimination"),
+ worklist_(graph->GetArena(), kDefaultWorklistSize) {}
- void Run();
+ void Run() OVERRIDE;
private:
- HGraph* const graph_;
GrowableArray<HPhi*> worklist_;
static constexpr size_t kDefaultWorklistSize = 8;
diff --git a/compiler/optimizing/ssa_type_propagation.cc b/compiler/optimizing/ssa_type_propagation.cc
index 3828142ed2..cb5ce20c46 100644
--- a/compiler/optimizing/ssa_type_propagation.cc
+++ b/compiler/optimizing/ssa_type_propagation.cc
@@ -90,10 +90,12 @@ void SsaTypePropagation::VisitBasicBlock(HBasicBlock* block) {
}
} else {
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
- HPhi* phi = it.Current()->AsPhi();
- if (UpdateType(phi)) {
- AddDependentInstructionsToWorklist(phi);
- }
+ // Eagerly compute the type of the phi, for quicker convergence. Note
+ // that we don't need to add users to the worklist because we are
+ // doing a reverse post-order visit, therefore either the phi users are
+ // non-loop phi and will be visited later in the visit, or are loop-phis,
+ // and they are already in the work list.
+ UpdateType(it.Current()->AsPhi());
}
}
}
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index 591d461244..9c84bc1e37 100644
--- a/compiler/utils/arm/assembler_arm.cc
+++ b/compiler/utils/arm/assembler_arm.cc
@@ -92,16 +92,29 @@ uint32_t ShifterOperand::encodingArm() const {
break;
case kRegister:
if (is_shift_) {
+ uint32_t shift_type;
+ switch (shift_) {
+ case arm::Shift::ROR:
+ shift_type = static_cast<uint32_t>(shift_);
+ CHECK_NE(immed_, 0U);
+ break;
+ case arm::Shift::RRX:
+ shift_type = static_cast<uint32_t>(arm::Shift::ROR); // Same encoding as ROR.
+ CHECK_EQ(immed_, 0U);
+ break;
+ default:
+ shift_type = static_cast<uint32_t>(shift_);
+ }
// Shifted immediate or register.
if (rs_ == kNoRegister) {
// Immediate shift.
return immed_ << kShiftImmShift |
- static_cast<uint32_t>(shift_) << kShiftShift |
+ shift_type << kShiftShift |
static_cast<uint32_t>(rm_);
} else {
// Register shift.
return static_cast<uint32_t>(rs_) << kShiftRegisterShift |
- static_cast<uint32_t>(shift_) << kShiftShift | (1 << 4) |
+ shift_type << kShiftShift | (1 << 4) |
static_cast<uint32_t>(rm_);
}
} else {
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 39ebf6803c..a1594b02ac 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -1513,10 +1513,8 @@ void Arm32Assembler::MemoryBarrier(ManagedRegister mscratch) {
void Arm32Assembler::dmb(DmbOptions flavor) {
-#if ANDROID_SMP != 0
int32_t encoding = 0xf57ff05f; // dmb
Emit(encoding | flavor);
-#endif
}
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index 277a9eb0fa..837fe1ec18 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -16,49 +16,208 @@
#include "assembler_arm32.h"
+#include <functional>
+#include <type_traits>
+
+#include "base/macros.h"
#include "base/stl_util.h"
-#include "utils/assembler_test.h"
+#include "utils/arm/assembler_arm_test.h"
namespace art {
-class AssemblerArm32Test : public AssemblerTest<arm::Arm32Assembler,
- arm::Register, arm::SRegister,
- uint32_t> {
+using std::placeholders::_1;
+using std::placeholders::_2;
+using std::placeholders::_3;
+using std::placeholders::_4;
+using std::placeholders::_5;
+
+// To speed up tests, don't use all register combinations.
+static constexpr bool kUseSparseRegisterList = true;
+
+// To speed up tests, don't use all condition codes.
+static constexpr bool kUseSparseConditionList = true;
+
+// To speed up tests, don't use all shift immediates.
+static constexpr bool kUseSparseShiftImmediates = true;
+
+class AssemblerArm32Test : public AssemblerArmTest<arm::Arm32Assembler,
+ arm::Register, arm::SRegister,
+ uint32_t, arm::ShifterOperand, arm::Condition> {
protected:
std::string GetArchitectureString() OVERRIDE {
return "arm";
}
+ std::string GetAssemblerParameters() OVERRIDE {
+ return " -march=armv7-a -mcpu=cortex-a15"; // Arm-v7a, cortex-a15 (means we have sdiv).
+ }
+
+ const char* GetAssemblyHeader() OVERRIDE {
+ return kArm32AssemblyHeader;
+ }
+
std::string GetDisassembleParameters() OVERRIDE {
return " -D -bbinary -marm --no-show-raw-insn";
}
void SetUpHelpers() OVERRIDE {
if (registers_.size() == 0) {
- registers_.insert(end(registers_),
- { // NOLINT(whitespace/braces)
- new arm::Register(arm::R0),
- new arm::Register(arm::R1),
- new arm::Register(arm::R2),
- new arm::Register(arm::R3),
- new arm::Register(arm::R4),
- new arm::Register(arm::R5),
- new arm::Register(arm::R6),
- new arm::Register(arm::R7),
- new arm::Register(arm::R8),
- new arm::Register(arm::R9),
- new arm::Register(arm::R10),
- new arm::Register(arm::R11),
- new arm::Register(arm::R12),
- new arm::Register(arm::R13),
- new arm::Register(arm::R14),
- new arm::Register(arm::R15)
- });
+ if (kUseSparseRegisterList) {
+ registers_.insert(end(registers_),
+ { // NOLINT(whitespace/braces)
+ new arm::Register(arm::R0),
+ new arm::Register(arm::R1),
+ new arm::Register(arm::R4),
+ new arm::Register(arm::R8),
+ new arm::Register(arm::R11),
+ new arm::Register(arm::R12),
+ new arm::Register(arm::R13),
+ new arm::Register(arm::R14),
+ new arm::Register(arm::R15)
+ });
+ } else {
+ registers_.insert(end(registers_),
+ { // NOLINT(whitespace/braces)
+ new arm::Register(arm::R0),
+ new arm::Register(arm::R1),
+ new arm::Register(arm::R2),
+ new arm::Register(arm::R3),
+ new arm::Register(arm::R4),
+ new arm::Register(arm::R5),
+ new arm::Register(arm::R6),
+ new arm::Register(arm::R7),
+ new arm::Register(arm::R8),
+ new arm::Register(arm::R9),
+ new arm::Register(arm::R10),
+ new arm::Register(arm::R11),
+ new arm::Register(arm::R12),
+ new arm::Register(arm::R13),
+ new arm::Register(arm::R14),
+ new arm::Register(arm::R15)
+ });
+ }
+ }
+
+ if (!kUseSparseConditionList) {
+ conditions_.push_back(arm::Condition::EQ);
+ conditions_.push_back(arm::Condition::NE);
+ conditions_.push_back(arm::Condition::CS);
+ conditions_.push_back(arm::Condition::CC);
+ conditions_.push_back(arm::Condition::MI);
+ conditions_.push_back(arm::Condition::PL);
+ conditions_.push_back(arm::Condition::VS);
+ conditions_.push_back(arm::Condition::VC);
+ conditions_.push_back(arm::Condition::HI);
+ conditions_.push_back(arm::Condition::LS);
+ conditions_.push_back(arm::Condition::GE);
+ conditions_.push_back(arm::Condition::LT);
+ conditions_.push_back(arm::Condition::GT);
+ conditions_.push_back(arm::Condition::LE);
+ conditions_.push_back(arm::Condition::AL);
+ } else {
+ conditions_.push_back(arm::Condition::EQ);
+ conditions_.push_back(arm::Condition::NE);
+ conditions_.push_back(arm::Condition::CC);
+ conditions_.push_back(arm::Condition::VC);
+ conditions_.push_back(arm::Condition::HI);
+ conditions_.push_back(arm::Condition::LT);
+ conditions_.push_back(arm::Condition::AL);
+ }
+
+ shifter_operands_.push_back(arm::ShifterOperand(0));
+ shifter_operands_.push_back(arm::ShifterOperand(1));
+ shifter_operands_.push_back(arm::ShifterOperand(2));
+ shifter_operands_.push_back(arm::ShifterOperand(3));
+ shifter_operands_.push_back(arm::ShifterOperand(4));
+ shifter_operands_.push_back(arm::ShifterOperand(5));
+ shifter_operands_.push_back(arm::ShifterOperand(127));
+ shifter_operands_.push_back(arm::ShifterOperand(128));
+ shifter_operands_.push_back(arm::ShifterOperand(254));
+ shifter_operands_.push_back(arm::ShifterOperand(255));
+
+ if (!kUseSparseRegisterList) {
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R0));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R1));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R2));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R3));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R4));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R5));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R6));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R7));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R8));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R9));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R10));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R11));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R12));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R13));
+ } else {
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R0));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R1));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R4));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R8));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R11));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R12));
+ shifter_operands_.push_back(arm::ShifterOperand(arm::R13));
+ }
+
+ std::vector<arm::Shift> shifts {
+ arm::Shift::LSL, arm::Shift::LSR, arm::Shift::ASR, arm::Shift::ROR, arm::Shift::RRX
+ };
+
+ // ShifterOperands of form "reg shift-type imm."
+ for (arm::Shift shift : shifts) {
+ for (arm::Register* reg : registers_) { // Note: this will pick up the sparse set.
+ if (*reg == arm::R15) { // Skip PC.
+ continue;
+ }
+ if (shift != arm::Shift::RRX) {
+ if (!kUseSparseShiftImmediates) {
+ for (uint32_t imm = 1; imm < 32; ++imm) {
+ shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, imm));
+ }
+ } else {
+ shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 1));
+ shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 2));
+ shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 3));
+ shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 7));
+ shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 15));
+ shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 16));
+ shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 30));
+ shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 31));
+ }
+ } else {
+ // RRX doesn't have an immediate.
+ shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 0));
+ }
+ }
+ }
+ }
+
+ std::vector<arm::ShifterOperand> CreateRegisterShifts(std::vector<arm::Register*>& base_regs,
+ int32_t shift_min, int32_t shift_max) {
+ std::vector<arm::ShifterOperand> res;
+ static constexpr arm::Shift kShifts[] = { arm::Shift::LSL, arm::Shift::LSR, arm::Shift::ASR,
+ arm::Shift::ROR };
+
+ for (arm::Shift shift : kShifts) {
+ for (arm::Register* reg : base_regs) {
+ // Take the min, the max, and three values in between.
+ res.push_back(arm::ShifterOperand(*reg, shift, shift_min));
+ if (shift_min != shift_max) {
+ res.push_back(arm::ShifterOperand(*reg, shift, shift_max));
+ int32_t middle = (shift_min + shift_max) / 2;
+ res.push_back(arm::ShifterOperand(*reg, shift, middle));
+ res.push_back(arm::ShifterOperand(*reg, shift, middle - 1));
+ res.push_back(arm::ShifterOperand(*reg, shift, middle + 1));
+ }
+ }
}
+
+ return res;
}
void TearDown() OVERRIDE {
- AssemblerTest::TearDown();
+ AssemblerArmTest::TearDown();
STLDeleteElements(&registers_);
}
@@ -70,8 +229,281 @@ class AssemblerArm32Test : public AssemblerTest<arm::Arm32Assembler,
return imm_value;
}
+ std::vector<arm::Condition>& GetConditions() OVERRIDE {
+ return conditions_;
+ }
+
+ std::string GetConditionString(arm::Condition c) OVERRIDE {
+ std::ostringstream oss;
+ oss << c;
+ return oss.str();
+ }
+
+ arm::Register GetPCRegister() OVERRIDE {
+ return arm::R15;
+ }
+
+ std::vector<arm::ShifterOperand>& GetShiftOperands() OVERRIDE {
+ return shifter_operands_;
+ }
+
+ std::string GetShiftString(arm::ShifterOperand sop) OVERRIDE {
+ std::ostringstream oss;
+ if (sop.IsShift()) {
+ // Not a rotate...
+ if (sop.GetShift() == arm::Shift::RRX) {
+ oss << sop.GetRegister() << ", " << sop.GetShift();
+ } else {
+ oss << sop.GetRegister() << ", " << sop.GetShift() << " #" << sop.GetImmediate();
+ }
+ } else if (sop.IsRegister()) {
+ oss << sop.GetRegister();
+ } else {
+ CHECK(sop.IsImmediate());
+ oss << "#" << sop.GetImmediate();
+ }
+ return oss.str();
+ }
+
+ static const char* GetRegTokenFromDepth(int depth) {
+ switch (depth) {
+ case 0:
+ return Base::REG1_TOKEN;
+ case 1:
+ return Base::REG2_TOKEN;
+ case 2:
+ return REG3_TOKEN;
+ case 3:
+ return REG4_TOKEN;
+ default:
+ LOG(FATAL) << "Depth problem.";
+ UNREACHABLE();
+ }
+ }
+
+ void ExecuteAndPrint(std::function<void()> f, std::string fmt, std::ostringstream& oss) {
+ if (first_) {
+ first_ = false;
+ } else {
+ oss << "\n";
+ }
+ oss << fmt;
+
+ f();
+ }
+
+ void TemplateHelper(std::function<void(arm::Register)> f, int depth ATTRIBUTE_UNUSED,
+ bool without_pc,
+ std::string fmt, std::ostringstream& oss) {
+ std::vector<arm::Register*> registers = without_pc ? GetRegistersWithoutPC() : GetRegisters();
+ for (auto reg : registers) {
+ std::string after_reg = fmt;
+
+ std::string reg_string = GetRegName<RegisterView::kUsePrimaryName>(*reg);
+ size_t reg_index;
+ const char* reg_token = GetRegTokenFromDepth(depth);
+
+ while ((reg_index = after_reg.find(reg_token)) != std::string::npos) {
+ after_reg.replace(reg_index, strlen(reg_token), reg_string);
+ }
+
+ ExecuteAndPrint([&] () { f(*reg); }, after_reg, oss);
+ }
+ }
+
+ void TemplateHelper(std::function<void(const arm::ShifterOperand&)> f, int depth ATTRIBUTE_UNUSED,
+ bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::ostringstream& oss) {
+ for (const arm::ShifterOperand& shift : GetShiftOperands()) {
+ std::string after_shift = fmt;
+
+ std::string shift_string = GetShiftString(shift);
+ size_t shift_index;
+ while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) {
+ after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string);
+ }
+
+ ExecuteAndPrint([&] () { f(shift); }, after_shift, oss);
+ }
+ }
+
+ void TemplateHelper(std::function<void(arm::Condition)> f, int depth ATTRIBUTE_UNUSED,
+ bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::ostringstream& oss) {
+ for (arm::Condition c : GetConditions()) {
+ std::string after_cond = fmt;
+
+ size_t cond_index = after_cond.find(COND_TOKEN);
+ if (cond_index != std::string::npos) {
+ after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+ }
+
+ ExecuteAndPrint([&] () { f(c); }, after_cond, oss);
+ }
+ }
+
+ template <typename... Args>
+ void TemplateHelper(std::function<void(arm::Register, Args...)> f, int depth, bool without_pc,
+ std::string fmt, std::ostringstream& oss) {
+ std::vector<arm::Register*> registers = without_pc ? GetRegistersWithoutPC() : GetRegisters();
+ for (auto reg : registers) {
+ std::string after_reg = fmt;
+
+ std::string reg_string = GetRegName<RegisterView::kUsePrimaryName>(*reg);
+ size_t reg_index;
+ const char* reg_token = GetRegTokenFromDepth(depth);
+
+ while ((reg_index = after_reg.find(reg_token)) != std::string::npos) {
+ after_reg.replace(reg_index, strlen(reg_token), reg_string);
+ }
+
+ auto lambda = [&] (Args... args) { f(*reg, args...); }; // NOLINT [readability/braces] [4]
+ TemplateHelper(std::function<void(Args...)>(lambda), depth + 1, without_pc,
+ after_reg, oss);
+ }
+ }
+
+ template <typename... Args>
+ void TemplateHelper(std::function<void(const arm::ShifterOperand&, Args...)> f, int depth,
+ bool without_pc, std::string fmt, std::ostringstream& oss) {
+ for (const arm::ShifterOperand& shift : GetShiftOperands()) {
+ std::string after_shift = fmt;
+
+ std::string shift_string = GetShiftString(shift);
+ size_t shift_index;
+ while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) {
+ after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string);
+ }
+
+ auto lambda = [&] (Args... args) { f(shift, args...); }; // NOLINT [readability/braces] [4]
+ TemplateHelper(std::function<void(Args...)>(lambda), depth, without_pc,
+ after_shift, oss);
+ }
+ }
+
+ template <typename... Args>
+ void TemplateHelper(std::function<void(arm::Condition, Args...)> f, int depth, bool without_pc,
+ std::string fmt, std::ostringstream& oss) {
+ for (arm::Condition c : GetConditions()) {
+ std::string after_cond = fmt;
+
+ size_t cond_index = after_cond.find(COND_TOKEN);
+ if (cond_index != std::string::npos) {
+ after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+ }
+
+ auto lambda = [&] (Args... args) { f(c, args...); }; // NOLINT [readability/braces] [4]
+ TemplateHelper(std::function<void(Args...)>(lambda), depth, without_pc,
+ after_cond, oss);
+ }
+ }
+
+ template <typename T1, typename T2>
+ std::function<void(T1, T2)> GetBoundFunction2(void (arm::Arm32Assembler::*f)(T1, T2)) {
+ return std::bind(f, GetAssembler(), _1, _2);
+ }
+
+ template <typename T1, typename T2, typename T3>
+ std::function<void(T1, T2, T3)> GetBoundFunction3(void (arm::Arm32Assembler::*f)(T1, T2, T3)) {
+ return std::bind(f, GetAssembler(), _1, _2, _3);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4>
+ std::function<void(T1, T2, T3, T4)> GetBoundFunction4(
+ void (arm::Arm32Assembler::*f)(T1, T2, T3, T4)) {
+ return std::bind(f, GetAssembler(), _1, _2, _3, _4);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ std::function<void(T1, T2, T3, T4, T5)> GetBoundFunction5(
+ void (arm::Arm32Assembler::*f)(T1, T2, T3, T4, T5)) {
+ return std::bind(f, GetAssembler(), _1, _2, _3, _4, _5);
+ }
+
+ template <typename... Args>
+ void GenericTemplateHelper(std::function<void(Args...)> f, bool without_pc,
+ std::string fmt, std::string test_name) {
+ first_ = false;
+ WarnOnCombinations(CountHelper<Args...>(without_pc));
+
+ std::ostringstream oss;
+
+ TemplateHelper(f, 0, without_pc, fmt, oss);
+
+ oss << "\n"; // Trailing newline.
+
+ DriverStr(oss.str(), test_name);
+ }
+
+ template <typename... Args>
+ void T2Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt,
+ std::string test_name) {
+ GenericTemplateHelper(GetBoundFunction2(f), without_pc, fmt, test_name);
+ }
+
+ template <typename... Args>
+ void T3Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt,
+ std::string test_name) {
+ GenericTemplateHelper(GetBoundFunction3(f), without_pc, fmt, test_name);
+ }
+
+ template <typename... Args>
+ void T4Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt,
+ std::string test_name) {
+ GenericTemplateHelper(GetBoundFunction4(f), without_pc, fmt, test_name);
+ }
+
+ template <typename... Args>
+ void T5Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt,
+ std::string test_name) {
+ GenericTemplateHelper(GetBoundFunction5(f), without_pc, fmt, test_name);
+ }
+
private:
+ template <typename T>
+ size_t CountHelper(bool without_pc) {
+ size_t tmp;
+ if (std::is_same<T, arm::Register>::value) {
+ tmp = GetRegisters().size();
+ if (without_pc) {
+ tmp--;; // Approximation...
+ }
+ return tmp;
+ } else if (std::is_same<T, const arm::ShifterOperand&>::value) {
+ return GetShiftOperands().size();
+ } else if (std::is_same<T, arm::Condition>::value) {
+ return GetConditions().size();
+ } else {
+ LOG(WARNING) << "Unknown type while counting.";
+ return 1;
+ }
+ }
+
+ template <typename T1, typename T2, typename... Args>
+ size_t CountHelper(bool without_pc) {
+ size_t tmp;
+ if (std::is_same<T1, arm::Register>::value) {
+ tmp = GetRegisters().size();
+ if (without_pc) {
+ tmp--;; // Approximation...
+ }
+ } else if (std::is_same<T1, const arm::ShifterOperand&>::value) {
+ tmp = GetShiftOperands().size();
+ } else if (std::is_same<T1, arm::Condition>::value) {
+ tmp = GetConditions().size();
+ } else {
+ LOG(WARNING) << "Unknown type while counting.";
+ tmp = 1;
+ }
+ size_t rec = CountHelper<T2, Args...>(without_pc);
+ return rec * tmp;
+ }
+
+ bool first_;
+
+ static constexpr const char* kArm32AssemblyHeader = ".arm\n";
+
std::vector<arm::Register*> registers_;
+ std::vector<arm::Condition> conditions_;
+ std::vector<arm::ShifterOperand> shifter_operands_;
};
@@ -79,77 +511,181 @@ TEST_F(AssemblerArm32Test, Toolchain) {
EXPECT_TRUE(CheckTools());
}
-
TEST_F(AssemblerArm32Test, Sbfx) {
- GetAssembler()->sbfx(arm::R0, arm::R1, 0, 1);
- GetAssembler()->sbfx(arm::R0, arm::R1, 0, 8);
- GetAssembler()->sbfx(arm::R0, arm::R1, 0, 16);
- GetAssembler()->sbfx(arm::R0, arm::R1, 0, 32);
+ std::vector<std::pair<uint32_t, uint32_t>> immediates;
+ immediates.push_back({0, 1});
+ immediates.push_back({0, 8});
+ immediates.push_back({0, 15});
+ immediates.push_back({0, 16});
+ immediates.push_back({0, 31});
+ immediates.push_back({0, 32});
+
+ immediates.push_back({1, 1});
+ immediates.push_back({1, 15});
+ immediates.push_back({1, 31});
+
+ immediates.push_back({8, 1});
+ immediates.push_back({8, 15});
+ immediates.push_back({8, 16});
+ immediates.push_back({8, 24});
- GetAssembler()->sbfx(arm::R0, arm::R1, 8, 1);
- GetAssembler()->sbfx(arm::R0, arm::R1, 8, 8);
- GetAssembler()->sbfx(arm::R0, arm::R1, 8, 16);
- GetAssembler()->sbfx(arm::R0, arm::R1, 8, 24);
+ immediates.push_back({31, 1});
- GetAssembler()->sbfx(arm::R0, arm::R1, 16, 1);
- GetAssembler()->sbfx(arm::R0, arm::R1, 16, 8);
- GetAssembler()->sbfx(arm::R0, arm::R1, 16, 16);
+ DriverStr(RepeatRRiiC(&arm::Arm32Assembler::sbfx, immediates,
+ "sbfx{cond} {reg1}, {reg2}, #{imm1}, #{imm2}"), "sbfx");
+}
- GetAssembler()->sbfx(arm::R0, arm::R1, 31, 1);
+TEST_F(AssemblerArm32Test, Ubfx) {
+ std::vector<std::pair<uint32_t, uint32_t>> immediates;
+ immediates.push_back({0, 1});
+ immediates.push_back({0, 8});
+ immediates.push_back({0, 15});
+ immediates.push_back({0, 16});
+ immediates.push_back({0, 31});
+ immediates.push_back({0, 32});
- const char* expected =
- "sbfx r0, r1, #0, #1\n"
- "sbfx r0, r1, #0, #8\n"
- "sbfx r0, r1, #0, #16\n"
- "sbfx r0, r1, #0, #32\n"
+ immediates.push_back({1, 1});
+ immediates.push_back({1, 15});
+ immediates.push_back({1, 31});
- "sbfx r0, r1, #8, #1\n"
- "sbfx r0, r1, #8, #8\n"
- "sbfx r0, r1, #8, #16\n"
- "sbfx r0, r1, #8, #24\n"
+ immediates.push_back({8, 1});
+ immediates.push_back({8, 15});
+ immediates.push_back({8, 16});
+ immediates.push_back({8, 24});
- "sbfx r0, r1, #16, #1\n"
- "sbfx r0, r1, #16, #8\n"
- "sbfx r0, r1, #16, #16\n"
+ immediates.push_back({31, 1});
- "sbfx r0, r1, #31, #1\n";
- DriverStr(expected, "sbfx");
+ DriverStr(RepeatRRiiC(&arm::Arm32Assembler::ubfx, immediates,
+ "ubfx{cond} {reg1}, {reg2}, #{imm1}, #{imm2}"), "ubfx");
}
-TEST_F(AssemblerArm32Test, Ubfx) {
- GetAssembler()->ubfx(arm::R0, arm::R1, 0, 1);
- GetAssembler()->ubfx(arm::R0, arm::R1, 0, 8);
- GetAssembler()->ubfx(arm::R0, arm::R1, 0, 16);
- GetAssembler()->ubfx(arm::R0, arm::R1, 0, 32);
-
- GetAssembler()->ubfx(arm::R0, arm::R1, 8, 1);
- GetAssembler()->ubfx(arm::R0, arm::R1, 8, 8);
- GetAssembler()->ubfx(arm::R0, arm::R1, 8, 16);
- GetAssembler()->ubfx(arm::R0, arm::R1, 8, 24);
-
- GetAssembler()->ubfx(arm::R0, arm::R1, 16, 1);
- GetAssembler()->ubfx(arm::R0, arm::R1, 16, 8);
- GetAssembler()->ubfx(arm::R0, arm::R1, 16, 16);
-
- GetAssembler()->ubfx(arm::R0, arm::R1, 31, 1);
-
- const char* expected =
- "ubfx r0, r1, #0, #1\n"
- "ubfx r0, r1, #0, #8\n"
- "ubfx r0, r1, #0, #16\n"
- "ubfx r0, r1, #0, #32\n"
-
- "ubfx r0, r1, #8, #1\n"
- "ubfx r0, r1, #8, #8\n"
- "ubfx r0, r1, #8, #16\n"
- "ubfx r0, r1, #8, #24\n"
-
- "ubfx r0, r1, #16, #1\n"
- "ubfx r0, r1, #16, #8\n"
- "ubfx r0, r1, #16, #16\n"
-
- "ubfx r0, r1, #31, #1\n";
- DriverStr(expected, "ubfx");
+TEST_F(AssemblerArm32Test, Mul) {
+ T4Helper(&arm::Arm32Assembler::mul, true, "mul{cond} {reg1}, {reg2}, {reg3}", "mul");
+}
+
+TEST_F(AssemblerArm32Test, Mla) {
+ T5Helper(&arm::Arm32Assembler::mla, true, "mla{cond} {reg1}, {reg2}, {reg3}, {reg4}", "mul");
+}
+
+/* TODO: Needs support to filter out register combinations, as rdhi must not be equal to rdlo.
+TEST_F(AssemblerArm32Test, Umull) {
+ T5Helper(&arm::Arm32Assembler::umull, true, "umull{cond} {reg1}, {reg2}, {reg3}, {reg4}",
+ "umull");
+}
+*/
+
+TEST_F(AssemblerArm32Test, Sdiv) {
+ T4Helper(&arm::Arm32Assembler::sdiv, true, "sdiv{cond} {reg1}, {reg2}, {reg3}", "sdiv");
+}
+
+TEST_F(AssemblerArm32Test, Udiv) {
+ T4Helper(&arm::Arm32Assembler::udiv, true, "udiv{cond} {reg1}, {reg2}, {reg3}", "udiv");
+}
+
+TEST_F(AssemblerArm32Test, And) {
+ T4Helper(&arm::Arm32Assembler::and_, true, "and{cond} {reg1}, {reg2}, {shift}", "and");
+}
+
+TEST_F(AssemblerArm32Test, Eor) {
+ T4Helper(&arm::Arm32Assembler::eor, true, "eor{cond} {reg1}, {reg2}, {shift}", "eor");
+}
+
+TEST_F(AssemblerArm32Test, Orr) {
+ T4Helper(&arm::Arm32Assembler::orr, true, "orr{cond} {reg1}, {reg2}, {shift}", "orr");
+}
+
+TEST_F(AssemblerArm32Test, Orrs) {
+ T4Helper(&arm::Arm32Assembler::orrs, true, "orr{cond}s {reg1}, {reg2}, {shift}", "orrs");
+}
+
+TEST_F(AssemblerArm32Test, Bic) {
+ T4Helper(&arm::Arm32Assembler::bic, true, "bic{cond} {reg1}, {reg2}, {shift}", "bic");
+}
+
+TEST_F(AssemblerArm32Test, Mov) {
+ T3Helper(&arm::Arm32Assembler::mov, true, "mov{cond} {reg1}, {shift}", "mov");
+}
+
+TEST_F(AssemblerArm32Test, Movs) {
+ T3Helper(&arm::Arm32Assembler::movs, true, "mov{cond}s {reg1}, {shift}", "movs");
+}
+
+TEST_F(AssemblerArm32Test, Mvn) {
+ T3Helper(&arm::Arm32Assembler::mvn, true, "mvn{cond} {reg1}, {shift}", "mvn");
+}
+
+TEST_F(AssemblerArm32Test, Mvns) {
+ T3Helper(&arm::Arm32Assembler::mvns, true, "mvn{cond}s {reg1}, {shift}", "mvns");
+}
+
+TEST_F(AssemblerArm32Test, Add) {
+ T4Helper(&arm::Arm32Assembler::add, false, "add{cond} {reg1}, {reg2}, {shift}", "add");
+}
+
+TEST_F(AssemblerArm32Test, Adds) {
+ T4Helper(&arm::Arm32Assembler::adds, false, "add{cond}s {reg1}, {reg2}, {shift}", "adds");
+}
+
+TEST_F(AssemblerArm32Test, Adc) {
+ T4Helper(&arm::Arm32Assembler::adc, false, "adc{cond} {reg1}, {reg2}, {shift}", "adc");
+}
+
+TEST_F(AssemblerArm32Test, Sub) {
+ T4Helper(&arm::Arm32Assembler::sub, false, "sub{cond} {reg1}, {reg2}, {shift}", "sub");
+}
+
+TEST_F(AssemblerArm32Test, Subs) {
+ T4Helper(&arm::Arm32Assembler::subs, false, "sub{cond}s {reg1}, {reg2}, {shift}", "subs");
+}
+
+TEST_F(AssemblerArm32Test, Sbc) {
+ T4Helper(&arm::Arm32Assembler::sbc, false, "sbc{cond} {reg1}, {reg2}, {shift}", "sbc");
+}
+
+TEST_F(AssemblerArm32Test, Rsb) {
+ T4Helper(&arm::Arm32Assembler::rsb, true, "rsb{cond} {reg1}, {reg2}, {shift}", "rsb");
+}
+
+TEST_F(AssemblerArm32Test, Rsbs) {
+ T4Helper(&arm::Arm32Assembler::rsbs, true, "rsb{cond}s {reg1}, {reg2}, {shift}", "rsbs");
+}
+
+TEST_F(AssemblerArm32Test, Rsc) {
+ T4Helper(&arm::Arm32Assembler::rsc, true, "rsc{cond} {reg1}, {reg2}, {shift}", "rsc");
+}
+
+/* TODO: Needs support to filter out register combinations, as reg1 must not be equal to reg3.
+TEST_F(AssemblerArm32Test, Strex) {
+ RRRCWithoutPCHelper(&arm::Arm32Assembler::strex, "strex{cond} {reg1}, {reg2}, [{reg3}]", "strex");
+}
+*/
+
+TEST_F(AssemblerArm32Test, Clz) {
+ T3Helper(&arm::Arm32Assembler::clz, true, "clz{cond} {reg1}, {reg2}", "clz");
+}
+
+TEST_F(AssemblerArm32Test, Tst) {
+ T3Helper(&arm::Arm32Assembler::tst, true, "tst{cond} {reg1}, {shift}", "tst");
+}
+
+TEST_F(AssemblerArm32Test, Teq) {
+ T3Helper(&arm::Arm32Assembler::teq, true, "teq{cond} {reg1}, {shift}", "teq");
+}
+
+TEST_F(AssemblerArm32Test, Cmp) {
+ T3Helper(&arm::Arm32Assembler::cmp, true, "cmp{cond} {reg1}, {shift}", "cmp");
+}
+
+TEST_F(AssemblerArm32Test, Cmn) {
+ T3Helper(&arm::Arm32Assembler::cmn, true, "cmn{cond} {reg1}, {shift}", "cmn");
+}
+
+TEST_F(AssemblerArm32Test, Blx) {
+ T2Helper(&arm::Arm32Assembler::blx, true, "blx{cond} {reg1}", "blx");
+}
+
+TEST_F(AssemblerArm32Test, Bx) {
+ T2Helper(&arm::Arm32Assembler::bx, true, "bx{cond} {reg1}", "bx");
}
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm_test.h b/compiler/utils/arm/assembler_arm_test.h
new file mode 100644
index 0000000000..838abb696d
--- /dev/null
+++ b/compiler/utils/arm/assembler_arm_test.h
@@ -0,0 +1,545 @@
+/*
+ * 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_ARM_ASSEMBLER_ARM_TEST_H_
+#define ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_TEST_H_
+
+#include "utils/assembler_test.h"
+
+namespace art {
+
+template<typename Ass, typename Reg, typename FPReg, typename Imm, typename SOp, typename Cond>
+class AssemblerArmTest : public AssemblerTest<Ass, Reg, FPReg, Imm> {
+ public:
+ typedef AssemblerTest<Ass, Reg, FPReg, Imm> Base;
+
+ using Base::GetRegisters;
+ using Base::GetRegName;
+ using Base::CreateImmediate;
+ using Base::WarnOnCombinations;
+
+ static constexpr int64_t kFullImmRangeThreshold = 32;
+
+ virtual void FillImmediates(std::vector<Imm>& immediates, int64_t imm_min, int64_t imm_max) {
+ // Small range: do completely.
+ if (imm_max - imm_min <= kFullImmRangeThreshold) {
+ for (int64_t i = imm_min; i <= imm_max; ++i) {
+ immediates.push_back(CreateImmediate(i));
+ }
+ } else {
+ immediates.push_back(CreateImmediate(imm_min));
+ immediates.push_back(CreateImmediate(imm_max));
+ if (imm_min < imm_max - 1) {
+ immediates.push_back(CreateImmediate(imm_min + 1));
+ }
+ if (imm_min < imm_max - 2) {
+ immediates.push_back(CreateImmediate(imm_min + 2));
+ }
+ if (imm_min < imm_max - 3) {
+ immediates.push_back(CreateImmediate(imm_max - 1));
+ }
+ if (imm_min < imm_max - 4) {
+ immediates.push_back(CreateImmediate((imm_min + imm_max) / 2));
+ }
+ }
+ }
+
+ std::string RepeatRRIIC(void (Ass::*f)(Reg, Reg, Imm, Imm, Cond),
+ int64_t imm1_min, int64_t imm1_max,
+ int64_t imm2_min, int64_t imm2_max,
+ std::string fmt) {
+ return RepeatTemplatedRRIIC(f, GetRegisters(), GetRegisters(),
+ &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+ &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+ imm1_min, imm1_max, imm2_min, imm2_max,
+ fmt);
+ }
+
+ template <typename Reg1, typename Reg2>
+ std::string RepeatTemplatedRRIIC(void (Ass::*f)(Reg1, Reg2, Imm, Imm, Cond),
+ const std::vector<Reg1*> reg1_registers,
+ const std::vector<Reg2*> reg2_registers,
+ std::string (AssemblerArmTest::*GetName1)(const Reg1&),
+ std::string (AssemblerArmTest::*GetName2)(const Reg2&),
+ int64_t imm1_min, int64_t imm1_max,
+ int64_t imm2_min, int64_t imm2_max,
+ std::string fmt) {
+ std::vector<Imm> immediates1;
+ FillImmediates(immediates1, imm1_min, imm1_max);
+ std::vector<Imm> immediates2;
+ FillImmediates(immediates2, imm2_min, imm2_max);
+
+ std::vector<Cond>& cond = GetConditions();
+
+ WarnOnCombinations(cond.size() * immediates1.size() * immediates2.size() *
+ reg1_registers.size() * reg2_registers.size());
+
+ std::ostringstream oss;
+ bool first = true;
+ for (Cond& c : cond) {
+ std::string after_cond = fmt;
+
+ size_t cond_index = after_cond.find(COND_TOKEN);
+ if (cond_index != std::string::npos) {
+ after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+ }
+
+ for (Imm i : immediates1) {
+ std::string base = after_cond;
+
+ size_t imm1_index = base.find(IMM1_TOKEN);
+ if (imm1_index != std::string::npos) {
+ std::ostringstream sreg;
+ sreg << i;
+ std::string imm_string = sreg.str();
+ base.replace(imm1_index, ConstexprStrLen(IMM1_TOKEN), imm_string);
+ }
+
+ for (Imm j : immediates2) {
+ std::string base2 = base;
+
+ size_t imm2_index = base2.find(IMM2_TOKEN);
+ if (imm2_index != std::string::npos) {
+ std::ostringstream sreg;
+ sreg << j;
+ std::string imm_string = sreg.str();
+ base2.replace(imm2_index, ConstexprStrLen(IMM2_TOKEN), imm_string);
+ }
+
+ for (auto reg1 : reg1_registers) {
+ std::string base3 = base2;
+
+ std::string reg1_string = (this->*GetName1)(*reg1);
+ size_t reg1_index;
+ while ((reg1_index = base3.find(Base::REG1_TOKEN)) != std::string::npos) {
+ base3.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
+ }
+
+ for (auto reg2 : reg2_registers) {
+ std::string base4 = base3;
+
+ std::string reg2_string = (this->*GetName2)(*reg2);
+ size_t reg2_index;
+ while ((reg2_index = base4.find(Base::REG2_TOKEN)) != std::string::npos) {
+ base4.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ oss << "\n";
+ }
+ oss << base4;
+
+ (Base::GetAssembler()->*f)(*reg1, *reg2, i, j, c);
+ }
+ }
+ }
+ }
+ }
+ // Add a newline at the end.
+ oss << "\n";
+
+ return oss.str();
+ }
+
+ std::string RepeatRRiiC(void (Ass::*f)(Reg, Reg, Imm, Imm, Cond),
+ std::vector<std::pair<Imm, Imm>>& immediates,
+ std::string fmt) {
+ return RepeatTemplatedRRiiC<Reg, Reg>(f, GetRegisters(), GetRegisters(),
+ &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+ &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+ immediates, fmt);
+ }
+
+ template <typename Reg1, typename Reg2>
+ std::string RepeatTemplatedRRiiC(void (Ass::*f)(Reg1, Reg2, Imm, Imm, Cond),
+ const std::vector<Reg1*> reg1_registers,
+ const std::vector<Reg2*> reg2_registers,
+ std::string (AssemblerArmTest::*GetName1)(const Reg1&),
+ std::string (AssemblerArmTest::*GetName2)(const Reg2&),
+ std::vector<std::pair<Imm, Imm>>& immediates,
+ std::string fmt) {
+ std::vector<Cond>& cond = GetConditions();
+
+ WarnOnCombinations(cond.size() * immediates.size() * reg1_registers.size() *
+ reg2_registers.size());
+
+ std::ostringstream oss;
+ bool first = true;
+ for (Cond& c : cond) {
+ std::string after_cond = fmt;
+
+ size_t cond_index = after_cond.find(COND_TOKEN);
+ if (cond_index != std::string::npos) {
+ after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+ }
+
+ for (std::pair<Imm, Imm>& pair : immediates) {
+ Imm i = pair.first;
+ Imm j = pair.second;
+ std::string after_imm1 = after_cond;
+
+ size_t imm1_index = after_imm1.find(IMM1_TOKEN);
+ if (imm1_index != std::string::npos) {
+ std::ostringstream sreg;
+ sreg << i;
+ std::string imm_string = sreg.str();
+ after_imm1.replace(imm1_index, ConstexprStrLen(IMM1_TOKEN), imm_string);
+ }
+
+ std::string after_imm2 = after_imm1;
+
+ size_t imm2_index = after_imm2.find(IMM2_TOKEN);
+ if (imm2_index != std::string::npos) {
+ std::ostringstream sreg;
+ sreg << j;
+ std::string imm_string = sreg.str();
+ after_imm2.replace(imm2_index, ConstexprStrLen(IMM2_TOKEN), imm_string);
+ }
+
+ for (auto reg1 : reg1_registers) {
+ std::string after_reg1 = after_imm2;
+
+ std::string reg1_string = (this->*GetName1)(*reg1);
+ size_t reg1_index;
+ while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) {
+ after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
+ }
+
+ for (auto reg2 : reg2_registers) {
+ std::string after_reg2 = after_reg1;
+
+ std::string reg2_string = (this->*GetName2)(*reg2);
+ size_t reg2_index;
+ while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) {
+ after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ oss << "\n";
+ }
+ oss << after_reg2;
+
+ (Base::GetAssembler()->*f)(*reg1, *reg2, i, j, c);
+ }
+ }
+ }
+ }
+ // Add a newline at the end.
+ oss << "\n";
+
+ return oss.str();
+ }
+
+ std::string RepeatRRC(void (Ass::*f)(Reg, Reg, Cond), std::string fmt) {
+ return RepeatTemplatedRRC(f, GetRegisters(), GetRegisters(), GetConditions(),
+ &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+ &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+ fmt);
+ }
+
+ template <typename Reg1, typename Reg2>
+ std::string RepeatTemplatedRRC(void (Ass::*f)(Reg1, Reg2, Cond),
+ const std::vector<Reg1*>& reg1_registers,
+ const std::vector<Reg2*>& reg2_registers,
+ const std::vector<Cond>& cond,
+ std::string (AssemblerArmTest::*GetName1)(const Reg1&),
+ std::string (AssemblerArmTest::*GetName2)(const Reg2&),
+ std::string fmt) {
+ WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size());
+
+ std::ostringstream oss;
+ bool first = true;
+ for (const Cond& c : cond) {
+ std::string after_cond = fmt;
+
+ size_t cond_index = after_cond.find(COND_TOKEN);
+ if (cond_index != std::string::npos) {
+ after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+ }
+
+ for (auto reg1 : reg1_registers) {
+ std::string after_reg1 = after_cond;
+
+ std::string reg1_string = (this->*GetName1)(*reg1);
+ size_t reg1_index;
+ while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) {
+ after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
+ }
+
+ for (auto reg2 : reg2_registers) {
+ std::string after_reg2 = after_reg1;
+
+ std::string reg2_string = (this->*GetName2)(*reg2);
+ size_t reg2_index;
+ while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) {
+ after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ oss << "\n";
+ }
+ oss << after_reg2;
+
+ (Base::GetAssembler()->*f)(*reg1, *reg2, c);
+ }
+ }
+ }
+ // Add a newline at the end.
+ oss << "\n";
+
+ return oss.str();
+ }
+
+ std::string RepeatRRRC(void (Ass::*f)(Reg, Reg, Reg, Cond), std::string fmt) {
+ return RepeatTemplatedRRRC(f, GetRegisters(), GetRegisters(), GetRegisters(), GetConditions(),
+ &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+ &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+ &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+ fmt);
+ }
+
+ template <typename Reg1, typename Reg2, typename Reg3>
+ std::string RepeatTemplatedRRRC(void (Ass::*f)(Reg1, Reg2, Reg3, Cond),
+ const std::vector<Reg1*>& reg1_registers,
+ const std::vector<Reg2*>& reg2_registers,
+ const std::vector<Reg3*>& reg3_registers,
+ const std::vector<Cond>& cond,
+ std::string (AssemblerArmTest::*GetName1)(const Reg1&),
+ std::string (AssemblerArmTest::*GetName2)(const Reg2&),
+ std::string (AssemblerArmTest::*GetName3)(const Reg3&),
+ std::string fmt) {
+ WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size() *
+ reg3_registers.size());
+
+ std::ostringstream oss;
+ bool first = true;
+ for (const Cond& c : cond) {
+ std::string after_cond = fmt;
+
+ size_t cond_index = after_cond.find(COND_TOKEN);
+ if (cond_index != std::string::npos) {
+ after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+ }
+
+ for (auto reg1 : reg1_registers) {
+ std::string after_reg1 = after_cond;
+
+ std::string reg1_string = (this->*GetName1)(*reg1);
+ size_t reg1_index;
+ while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) {
+ after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
+ }
+
+ for (auto reg2 : reg2_registers) {
+ std::string after_reg2 = after_reg1;
+
+ std::string reg2_string = (this->*GetName2)(*reg2);
+ size_t reg2_index;
+ while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) {
+ after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
+ }
+
+ for (auto reg3 : reg3_registers) {
+ std::string after_reg3 = after_reg2;
+
+ std::string reg3_string = (this->*GetName3)(*reg3);
+ size_t reg3_index;
+ while ((reg3_index = after_reg3.find(REG3_TOKEN)) != std::string::npos) {
+ after_reg3.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string);
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ oss << "\n";
+ }
+ oss << after_reg3;
+
+ (Base::GetAssembler()->*f)(*reg1, *reg2, *reg3, c);
+ }
+ }
+ }
+ }
+ // Add a newline at the end.
+ oss << "\n";
+
+ return oss.str();
+ }
+
+ template <typename RegT>
+ std::string RepeatTemplatedRSC(void (Ass::*f)(RegT, SOp, Cond),
+ const std::vector<RegT*>& registers,
+ const std::vector<SOp>& shifts,
+ const std::vector<Cond>& cond,
+ std::string (AssemblerArmTest::*GetName)(const RegT&),
+ std::string fmt) {
+ WarnOnCombinations(cond.size() * registers.size() * shifts.size());
+
+ std::ostringstream oss;
+ bool first = true;
+ for (const Cond& c : cond) {
+ std::string after_cond = fmt;
+
+ size_t cond_index = after_cond.find(COND_TOKEN);
+ if (cond_index != std::string::npos) {
+ after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+ }
+
+ for (const SOp& shift : shifts) {
+ std::string after_shift = after_cond;
+
+ std::string shift_string = GetShiftString(shift);
+ size_t shift_index;
+ while ((shift_index = after_shift.find(Base::SHIFT_TOKEN)) != std::string::npos) {
+ after_shift.replace(shift_index, ConstexprStrLen(Base::SHIFT_TOKEN), shift_string);
+ }
+
+ for (auto reg : registers) {
+ std::string after_reg = after_shift;
+
+ std::string reg_string = (this->*GetName)(*reg);
+ size_t reg_index;
+ while ((reg_index = after_reg.find(Base::REG_TOKEN)) != std::string::npos) {
+ after_reg.replace(reg_index, ConstexprStrLen(Base::REG_TOKEN), reg_string);
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ oss << "\n";
+ }
+ oss << after_reg;
+
+ (Base::GetAssembler()->*f)(*reg, shift, c);
+ }
+ }
+ }
+ // Add a newline at the end.
+ oss << "\n";
+
+ return oss.str();
+ }
+
+ template <typename Reg1, typename Reg2>
+ std::string RepeatTemplatedRRSC(void (Ass::*f)(Reg1, Reg2, const SOp&, Cond),
+ const std::vector<Reg1*>& reg1_registers,
+ const std::vector<Reg2*>& reg2_registers,
+ const std::vector<SOp>& shifts,
+ const std::vector<Cond>& cond,
+ std::string (AssemblerArmTest::*GetName1)(const Reg1&),
+ std::string (AssemblerArmTest::*GetName2)(const Reg2&),
+ std::string fmt) {
+ WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size() * shifts.size());
+
+ std::ostringstream oss;
+ bool first = true;
+ for (const Cond& c : cond) {
+ std::string after_cond = fmt;
+
+ size_t cond_index = after_cond.find(COND_TOKEN);
+ if (cond_index != std::string::npos) {
+ after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+ }
+
+ for (const SOp& shift : shifts) {
+ std::string after_shift = after_cond;
+
+ std::string shift_string = GetShiftString(shift);
+ size_t shift_index;
+ while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) {
+ after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string);
+ }
+
+ for (auto reg1 : reg1_registers) {
+ std::string after_reg1 = after_shift;
+
+ std::string reg1_string = (this->*GetName1)(*reg1);
+ size_t reg1_index;
+ while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) {
+ after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
+ }
+
+ for (auto reg2 : reg2_registers) {
+ std::string after_reg2 = after_reg1;
+
+ std::string reg2_string = (this->*GetName2)(*reg2);
+ size_t reg2_index;
+ while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) {
+ after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ oss << "\n";
+ }
+ oss << after_reg2;
+
+ (Base::GetAssembler()->*f)(*reg1, *reg2, shift, c);
+ }
+ }
+ }
+ }
+ // Add a newline at the end.
+ oss << "\n";
+
+ return oss.str();
+ }
+
+ protected:
+ explicit AssemblerArmTest() {}
+
+ virtual std::vector<Cond>& GetConditions() = 0;
+ virtual std::string GetConditionString(Cond c) = 0;
+
+ virtual std::vector<SOp>& GetShiftOperands() = 0;
+ virtual std::string GetShiftString(SOp sop) = 0;
+
+ virtual Reg GetPCRegister() = 0;
+ virtual std::vector<Reg*> GetRegistersWithoutPC() {
+ std::vector<Reg*> without_pc = GetRegisters();
+ Reg pc_reg = GetPCRegister();
+
+ for (auto it = without_pc.begin(); it != without_pc.end(); ++it) {
+ if (**it == pc_reg) {
+ without_pc.erase(it);
+ break;
+ }
+ }
+
+ return without_pc;
+ }
+
+ static constexpr const char* IMM1_TOKEN = "{imm1}";
+ static constexpr const char* IMM2_TOKEN = "{imm2}";
+ static constexpr const char* REG3_TOKEN = "{reg3}";
+ static constexpr const char* REG4_TOKEN = "{reg4}";
+ static constexpr const char* COND_TOKEN = "{cond}";
+ static constexpr const char* SHIFT_TOKEN = "{shift}";
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AssemblerArmTest);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_TEST_H_
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 3ab9b2ba03..a34920999e 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -2599,10 +2599,8 @@ void Thumb2Assembler::MemoryBarrier(ManagedRegister mscratch) {
void Thumb2Assembler::dmb(DmbOptions flavor) {
-#if ANDROID_SMP != 0
int32_t encoding = 0xf3bf8f50; // dmb in T1 encoding.
Emit32(encoding | flavor);
-#endif
}
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index 02011b87a0..390f2ea449 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -476,9 +476,7 @@ void Arm64Assembler::Copy(FrameOffset /*dst*/, Offset /*dest_offset*/,
void Arm64Assembler::MemoryBarrier(ManagedRegister m_scratch ATTRIBUTE_UNUSED) {
// TODO: Should we check that m_scratch is IP? - see arm.
-#if ANDROID_SMP != 0
___ Dmb(vixl::InnerShareable, vixl::BarrierAll);
-#endif
}
void Arm64Assembler::SignExtend(ManagedRegister mreg, size_t size) {
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 9d3fa01a9d..1fadb916fc 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -38,14 +38,14 @@ constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) {
// temp directory.
static std::string tmpnam_;
+enum class RegisterView { // private
+ kUsePrimaryName,
+ kUseSecondaryName
+};
+
template<typename Ass, typename Reg, typename FPReg, typename Imm>
class AssemblerTest : public testing::Test {
public:
- enum class RegisterView { // private
- kUsePrimaryName,
- kUseSecondaryName
- };
-
Ass* GetAssembler() {
return assembler_.get();
}
@@ -159,6 +159,9 @@ class AssemblerTest : public testing::Test {
bool as_uint = false) {
std::string str;
std::vector<int64_t> imms = CreateImmediateValues(imm_bytes, as_uint);
+
+ WarnOnCombinations(imms.size());
+
for (int64_t imm : imms) {
Imm new_imm = CreateImmediate(imm);
(assembler_.get()->*f)(new_imm);
@@ -184,12 +187,12 @@ class AssemblerTest : public testing::Test {
// This is intended to be run as a test.
bool CheckTools() {
- if (!FileExists(GetAssemblerCommand())) {
+ if (!FileExists(FindTool(GetAssemblerCmdName()))) {
return false;
}
LOG(INFO) << "Chosen assembler command: " << GetAssemblerCommand();
- if (!FileExists(GetObjdumpCommand())) {
+ if (!FileExists(FindTool(GetObjdumpCmdName()))) {
return false;
}
LOG(INFO) << "Chosen objdump command: " << GetObjdumpCommand();
@@ -197,7 +200,7 @@ class AssemblerTest : public testing::Test {
// Disassembly is optional.
std::string disassembler = GetDisassembleCommand();
if (disassembler.length() != 0) {
- if (!FileExists(disassembler)) {
+ if (!FileExists(FindTool(GetDisassembleCmdName()))) {
return false;
}
LOG(INFO) << "Chosen disassemble command: " << GetDisassembleCommand();
@@ -271,7 +274,7 @@ class AssemblerTest : public testing::Test {
resolved_assembler_cmd_ = line + GetAssemblerParameters();
- return line;
+ return resolved_assembler_cmd_;
}
// Get the name of the objdump, e.g., "objdump" by default.
@@ -298,7 +301,7 @@ class AssemblerTest : public testing::Test {
resolved_objdump_cmd_ = line + GetObjdumpParameters();
- return line;
+ return resolved_objdump_cmd_;
}
// Get the name of the objdump, e.g., "objdump" by default.
@@ -324,7 +327,7 @@ class AssemblerTest : public testing::Test {
resolved_disassemble_cmd_ = line + GetDisassembleParameters();
- return line;
+ return resolved_disassemble_cmd_;
}
// Create a couple of immediate values up to the number of bytes given.
@@ -406,6 +409,8 @@ class AssemblerTest : public testing::Test {
std::string (AssemblerTest::*GetName1)(const Reg1&),
std::string (AssemblerTest::*GetName2)(const Reg2&),
std::string fmt) {
+ WarnOnCombinations(reg1_registers.size() * reg2_registers.size());
+
std::string str;
for (auto reg1 : reg1_registers) {
for (auto reg2 : reg2_registers) {
@@ -435,7 +440,6 @@ class AssemblerTest : public testing::Test {
return str;
}
- private:
template <RegisterView kRegView>
std::string GetRegName(const Reg& reg) {
std::ostringstream sreg;
@@ -457,12 +461,32 @@ class AssemblerTest : public testing::Test {
return sreg.str();
}
+ // If the assembly file needs a header, return it in a sub-class.
+ virtual const char* GetAssemblyHeader() {
+ return nullptr;
+ }
+
+ void WarnOnCombinations(size_t count) {
+ if (count > kWarnManyCombinationsThreshold) {
+ GTEST_LOG_(WARNING) << "Many combinations (" << count << "), test generation might be slow.";
+ }
+ }
+
+ static constexpr const char* REG_TOKEN = "{reg}";
+ static constexpr const char* REG1_TOKEN = "{reg1}";
+ static constexpr const char* REG2_TOKEN = "{reg2}";
+ static constexpr const char* IMM_TOKEN = "{imm}";
+
+ private:
template <RegisterView kRegView>
std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes,
std::string fmt) {
const std::vector<Reg*> registers = GetRegisters();
std::string str;
std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
+
+ WarnOnCombinations(registers.size() * imms.size());
+
for (auto reg : registers) {
for (int64_t imm : imms) {
Imm new_imm = CreateImmediate(imm);
@@ -547,7 +571,7 @@ class AssemblerTest : public testing::Test {
// Compile the assembly file from_file to a binary file to_file. Returns true on success.
bool Assemble(const char* from_file, const char* to_file, std::string* error_msg) {
- bool have_assembler = FileExists(GetAssemblerCommand());
+ bool have_assembler = FileExists(FindTool(GetAssemblerCmdName()));
EXPECT_TRUE(have_assembler) << "Cannot find assembler:" << GetAssemblerCommand();
if (!have_assembler) {
return false;
@@ -569,13 +593,20 @@ class AssemblerTest : public testing::Test {
args.push_back("-c");
args.push_back(cmd);
- return Exec(args, error_msg);
+ bool success = Exec(args, error_msg);
+ if (!success) {
+ LOG(INFO) << "Assembler command line:";
+ for (std::string arg : args) {
+ LOG(INFO) << arg;
+ }
+ }
+ return success;
}
// Runs objdump -h on the binary file and extracts the first line with .text.
// Returns "" on failure.
std::string Objdump(std::string file) {
- bool have_objdump = FileExists(GetObjdumpCommand());
+ bool have_objdump = FileExists(FindTool(GetObjdumpCmdName()));
EXPECT_TRUE(have_objdump) << "Cannot find objdump: " << GetObjdumpCommand();
if (!have_objdump) {
return "";
@@ -652,10 +683,10 @@ class AssemblerTest : public testing::Test {
// If you want to take a look at the differences between the ART assembler and GCC, comment
// out the removal code.
- std::remove(data_name.c_str());
- std::remove(as_name.c_str());
- std::remove((data_name + ".dis").c_str());
- std::remove((as_name + ".dis").c_str());
+// std::remove(data_name.c_str());
+// std::remove(as_name.c_str());
+// std::remove((data_name + ".dis").c_str());
+// std::remove((as_name + ".dis").c_str());
return result;
}
@@ -714,6 +745,10 @@ class AssemblerTest : public testing::Test {
// TODO: Lots of error checking.
std::ofstream s_out(res->base_name + ".S");
+ const char* header = GetAssemblyHeader();
+ if (header != nullptr) {
+ s_out << header;
+ }
s_out << assembly_code;
s_out.close();
@@ -862,13 +897,9 @@ class AssemblerTest : public testing::Test {
return tmpnam_;
}
+ static constexpr size_t kWarnManyCombinationsThreshold = 500;
static constexpr size_t OBJDUMP_SECTION_LINE_MIN_TOKENS = 6;
- static constexpr const char* REG_TOKEN = "{reg}";
- static constexpr const char* REG1_TOKEN = "{reg1}";
- static constexpr const char* REG2_TOKEN = "{reg2}";
- static constexpr const char* IMM_TOKEN = "{imm}";
-
std::unique_ptr<Ass> assembler_;
std::string resolved_assembler_cmd_;
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 8ebb40e338..afa4a3b958 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -1830,9 +1830,7 @@ void X86Assembler::Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, O
}
void X86Assembler::MemoryBarrier(ManagedRegister) {
-#if ANDROID_SMP != 0
mfence();
-#endif
}
void X86Assembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 2bb2ed8c9c..8c428f455e 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -2371,9 +2371,7 @@ void X86_64Assembler::Copy(FrameOffset dest, Offset dest_offset, FrameOffset src
}
void X86_64Assembler::MemoryBarrier(ManagedRegister) {
-#if ANDROID_SMP != 0
mfence();
-#endif
}
void X86_64Assembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 2d2a82e985..7d4b726292 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -942,9 +942,11 @@ class Dex2Oat FINAL {
oat_location_ = oat_filename_;
}
} else {
- oat_file_.reset(new File(oat_fd_, oat_location_));
+ oat_file_.reset(new File(oat_fd_, oat_location_, true));
oat_file_->DisableAutoClose();
- oat_file_->SetLength(0);
+ if (oat_file_->SetLength(0) != 0) {
+ PLOG(WARNING) << "Truncating oat file " << oat_location_ << " failed.";
+ }
}
if (oat_file_.get() == nullptr) {
PLOG(ERROR) << "Failed to create oat file: " << oat_location_;
@@ -952,6 +954,7 @@ class Dex2Oat FINAL {
}
if (create_file && fchmod(oat_file_->Fd(), 0644) != 0) {
PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location_;
+ oat_file_->Erase();
return false;
}
return true;
@@ -1075,7 +1078,10 @@ class Dex2Oat FINAL {
<< ". Try: adb shell chmod 777 /data/local/tmp";
continue;
}
- tmp_file->WriteFully(dex_file->Begin(), dex_file->Size());
+ // This is just dumping files for debugging. Ignore errors, and leave remnants.
+ UNUSED(tmp_file->WriteFully(dex_file->Begin(), dex_file->Size()));
+ UNUSED(tmp_file->Flush());
+ UNUSED(tmp_file->Close());
LOG(INFO) << "Wrote input to " << tmp_file_name;
}
}
@@ -1214,6 +1220,8 @@ class Dex2Oat FINAL {
// Write out the generated code part. Calls the OatWriter and ElfBuilder. Also prepares the
// ImageWriter, if necessary.
+ // Note: Flushing (and closing) the file is the caller's responsibility, except for the failure
+ // case (when the file will be explicitly erased).
bool CreateOatFile() {
CHECK(key_value_store_.get() != nullptr);
@@ -1266,15 +1274,7 @@ class Dex2Oat FINAL {
if (!driver_->WriteElf(android_root_, is_host_, dex_files_, oat_writer.get(),
oat_file_.get())) {
LOG(ERROR) << "Failed to write ELF file " << oat_file_->GetPath();
- return false;
- }
- }
-
- // Flush result to disk.
- {
- TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_);
- if (oat_file_->Flush() != 0) {
- LOG(ERROR) << "Failed to flush ELF file " << oat_file_->GetPath();
+ oat_file_->Erase();
return false;
}
}
@@ -1295,14 +1295,19 @@ class Dex2Oat FINAL {
return true;
}
- // Strip the oat file, if requested. This first creates a copy from unstripped to stripped, and
- // then runs the ElfStripper. Currently only relevant for the portable compiler.
- bool Strip() {
+ // Create a copy from unstripped to stripped.
+ bool CopyUnstrippedToStripped() {
// If we don't want to strip in place, copy from unstripped location to stripped location.
// We need to strip after image creation because FixupElf needs to use .strtab.
if (oat_unstripped_ != oat_stripped_) {
+ // If the oat file is still open, flush it.
+ if (oat_file_.get() != nullptr && oat_file_->IsOpened()) {
+ if (!FlushCloseOatFile()) {
+ return false;
+ }
+ }
+
TimingLogger::ScopedTiming t("dex2oat OatFile copy", timings_);
- oat_file_.reset();
std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped_.c_str()));
std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped_.c_str()));
size_t buffer_size = 8192;
@@ -1315,14 +1320,27 @@ class Dex2Oat FINAL {
bool write_ok = out->WriteFully(buffer.get(), bytes_read);
CHECK(write_ok);
}
- oat_file_.reset(out.release());
+ if (kUsePortableCompiler) {
+ oat_file_.reset(out.release());
+ } else {
+ if (out->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close copied oat file: " << oat_stripped_;
+ return false;
+ }
+ }
VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped_;
}
+ return true;
+ }
+ // Run the ElfStripper. Currently only relevant for the portable compiler.
+ bool Strip() {
if (kUsePortableCompiler) {
// Portable includes debug symbols unconditionally. If we are not supposed to create them,
// strip them now. Quick generates debug symbols only when the flag(s) are set.
if (!compiler_options_->GetIncludeDebugSymbols()) {
+ CHECK(oat_file_.get() != nullptr && oat_file_->IsOpened());
+
TimingLogger::ScopedTiming t("dex2oat ElfStripper", timings_);
// Strip unneeded sections for target
off_t seek_actual = lseek(oat_file_->Fd(), 0, SEEK_SET);
@@ -1330,6 +1348,11 @@ class Dex2Oat FINAL {
std::string error_msg;
if (!ElfFile::Strip(oat_file_.get(), &error_msg)) {
LOG(ERROR) << "Failed to strip elf file: " << error_msg;
+ oat_file_->Erase();
+ return false;
+ }
+
+ if (!FlushCloseOatFile()) {
return false;
}
@@ -1343,6 +1366,31 @@ class Dex2Oat FINAL {
return true;
}
+ bool FlushOatFile() {
+ if (oat_file_.get() != nullptr) {
+ TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_);
+ if (oat_file_->Flush() != 0) {
+ PLOG(ERROR) << "Failed to flush oat file: " << oat_location_ << " / "
+ << oat_filename_;
+ oat_file_->Erase();
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool FlushCloseOatFile() {
+ if (oat_file_.get() != nullptr) {
+ std::unique_ptr<File> tmp(oat_file_.release());
+ if (tmp->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location_ << " / "
+ << oat_filename_;
+ return false;
+ }
+ }
+ return true;
+ }
+
void DumpTiming() {
if (dump_timing_ || (dump_slow_timing_ && timings_->GetTotalNs() > MsToNs(1000))) {
LOG(INFO) << Dumpable<TimingLogger>(*timings_);
@@ -1356,6 +1404,10 @@ class Dex2Oat FINAL {
return compiler_options_.get();
}
+ bool IsImage() const {
+ return image_;
+ }
+
bool IsHost() const {
return is_host_;
}
@@ -1451,18 +1503,24 @@ class Dex2Oat FINAL {
// Destroy ImageWriter before doing FixupElf.
image_writer_.reset();
- std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_unstripped_.c_str()));
- if (oat_file.get() == nullptr) {
- PLOG(ERROR) << "Failed to open ELF file: " << oat_unstripped_;
- return false;
- }
-
// Do not fix up the ELF file if we are --compile-pic
if (!compiler_options_->GetCompilePic()) {
+ std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_unstripped_.c_str()));
+ if (oat_file.get() == nullptr) {
+ PLOG(ERROR) << "Failed to open ELF file: " << oat_unstripped_;
+ return false;
+ }
+
if (!ElfWriter::Fixup(oat_file.get(), oat_data_begin)) {
+ oat_file->Erase();
LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
return false;
}
+
+ if (oat_file->FlushCloseOrErase()) {
+ PLOG(ERROR) << "Failed to flush and close fixed ELF file " << oat_file->GetPath();
+ return false;
+ }
}
return true;
@@ -1609,49 +1667,121 @@ static void b13564922() {
#endif
}
-static int dex2oat(int argc, char** argv) {
- b13564922();
+static int CompileImage(Dex2Oat& dex2oat) {
+ dex2oat.Compile();
- TimingLogger timings("compiler", false, false);
+ // Create the boot.oat.
+ if (!dex2oat.CreateOatFile()) {
+ return EXIT_FAILURE;
+ }
- Dex2Oat dex2oat(&timings);
+ // Flush and close the boot.oat. We always expect the output file by name, and it will be
+ // re-opened from the unstripped name.
+ if (!dex2oat.FlushCloseOatFile()) {
+ return EXIT_FAILURE;
+ }
- // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
- dex2oat.ParseArgs(argc, argv);
+ // Creates the boot.art and patches the boot.oat.
+ if (!dex2oat.HandleImage()) {
+ return EXIT_FAILURE;
+ }
- // Check early that the result of compilation can be written
- if (!dex2oat.OpenFile()) {
+ // When given --host, finish early without stripping.
+ if (dex2oat.IsHost()) {
+ dex2oat.DumpTiming();
+ return EXIT_SUCCESS;
+ }
+
+ // Copy unstripped to stripped location, if necessary.
+ if (!dex2oat.CopyUnstrippedToStripped()) {
return EXIT_FAILURE;
}
- LOG(INFO) << CommandLine();
+ // Strip, if necessary.
+ if (!dex2oat.Strip()) {
+ return EXIT_FAILURE;
+ }
- if (!dex2oat.Setup()) {
+ // FlushClose again, as stripping might have re-opened the oat file.
+ if (!dex2oat.FlushCloseOatFile()) {
return EXIT_FAILURE;
}
+ dex2oat.DumpTiming();
+ return EXIT_SUCCESS;
+}
+
+static int CompileApp(Dex2Oat& dex2oat) {
dex2oat.Compile();
+ // Create the app oat.
if (!dex2oat.CreateOatFile()) {
return EXIT_FAILURE;
}
- if (!dex2oat.HandleImage()) {
+ // Do not close the oat file here. We might haven gotten the output file by file descriptor,
+ // which we would lose.
+ if (!dex2oat.FlushOatFile()) {
return EXIT_FAILURE;
}
+ // When given --host, finish early without stripping.
if (dex2oat.IsHost()) {
+ if (!dex2oat.FlushCloseOatFile()) {
+ return EXIT_FAILURE;
+ }
+
dex2oat.DumpTiming();
return EXIT_SUCCESS;
}
+ // Copy unstripped to stripped location, if necessary. This will implicitly flush & close the
+ // unstripped version. If this is given, we expect to be able to open writable files by name.
+ if (!dex2oat.CopyUnstrippedToStripped()) {
+ return EXIT_FAILURE;
+ }
+
+ // Strip, if necessary.
if (!dex2oat.Strip()) {
return EXIT_FAILURE;
}
+ // Flush and close the file.
+ if (!dex2oat.FlushCloseOatFile()) {
+ return EXIT_FAILURE;
+ }
+
dex2oat.DumpTiming();
return EXIT_SUCCESS;
}
+
+static int dex2oat(int argc, char** argv) {
+ b13564922();
+
+ TimingLogger timings("compiler", false, false);
+
+ Dex2Oat dex2oat(&timings);
+
+ // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
+ dex2oat.ParseArgs(argc, argv);
+
+ // Check early that the result of compilation can be written
+ if (!dex2oat.OpenFile()) {
+ return EXIT_FAILURE;
+ }
+
+ LOG(INFO) << CommandLine();
+
+ if (!dex2oat.Setup()) {
+ return EXIT_FAILURE;
+ }
+
+ if (dex2oat.IsImage()) {
+ return CompileImage(dex2oat);
+ } else {
+ return CompileApp(dex2oat);
+ }
+}
} // namespace art
int main(int argc, char** argv) {
diff --git a/disassembler/disassembler.h b/disassembler/disassembler.h
index 9cd631c984..966ee3ac89 100644
--- a/disassembler/disassembler.h
+++ b/disassembler/disassembler.h
@@ -34,8 +34,14 @@ class DisassemblerOptions {
// Base addess for calculating relative code offsets when absolute_addresses_ is false.
const uint8_t* const base_address_;
- DisassemblerOptions(bool absolute_addresses, const uint8_t* base_address)
- : absolute_addresses_(absolute_addresses), base_address_(base_address) {}
+ // If set, the disassembler is allowed to look at load targets in literal
+ // pools.
+ const bool can_read_literals_;
+
+ DisassemblerOptions(bool absolute_addresses, const uint8_t* base_address,
+ bool can_read_literals)
+ : absolute_addresses_(absolute_addresses), base_address_(base_address),
+ can_read_literals_(can_read_literals) {}
private:
DISALLOW_COPY_AND_ASSIGN(DisassemblerOptions);
diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc
index 229ac9755f..fe50421ed4 100644
--- a/disassembler/disassembler_arm64.cc
+++ b/disassembler/disassembler_arm64.cc
@@ -27,10 +27,88 @@
namespace art {
namespace arm64 {
+void CustomDisassembler::AppendRegisterNameToOutput(
+ const vixl::Instruction* instr,
+ const vixl::CPURegister& reg) {
+ USE(instr);
+ if (reg.IsRegister()) {
+ // This enumeration should mirror the declarations in
+ // runtime/arch/arm64/registers_arm64.h. We do not include that file to
+ // avoid a dependency on libart.
+ enum {
+ TR = 18,
+ ETR = 21,
+ IP0 = 16,
+ IP1 = 17,
+ FP = 29,
+ LR = 30
+ };
+ switch (reg.code()) {
+ case IP0: AppendToOutput(reg.Is64Bits() ? "ip0" : "wip0"); return;
+ case IP1: AppendToOutput(reg.Is64Bits() ? "ip1" : "wip1"); return;
+ case TR: AppendToOutput(reg.Is64Bits() ? "tr" : "w18"); return;
+ case ETR: AppendToOutput(reg.Is64Bits() ? "etr" : "w21"); return;
+ case FP: AppendToOutput(reg.Is64Bits() ? "fp" : "w29"); return;
+ case LR: AppendToOutput(reg.Is64Bits() ? "lr" : "w30"); return;
+ default:
+ // Fall through.
+ break;
+ }
+ }
+ // Print other register names as usual.
+ Disassembler::AppendRegisterNameToOutput(instr, reg);
+}
+
+void CustomDisassembler::VisitLoadLiteral(const vixl::Instruction* instr) {
+ Disassembler::VisitLoadLiteral(instr);
+
+ if (!read_literals_) {
+ return;
+ }
+
+ char* buffer = buffer_;
+ char* buffer_end = buffer_ + buffer_size_;
+
+ // Find the end position in the buffer.
+ while ((*buffer != 0) && (buffer < buffer_end)) {
+ ++buffer;
+ }
+
+ void* data_address = instr->LiteralAddress();
+ ptrdiff_t buf_size_remaining = buffer_end - buffer;
+ vixl::Instr op = instr->Mask(vixl::LoadLiteralMask);
+
+ switch (op) {
+ case vixl::LDR_w_lit:
+ case vixl::LDR_x_lit:
+ case vixl::LDRSW_x_lit: {
+ int64_t data = op == vixl::LDR_x_lit ? *reinterpret_cast<int64_t*>(data_address)
+ : *reinterpret_cast<int32_t*>(data_address);
+ snprintf(buffer, buf_size_remaining, " (0x%" PRIx64 " / %" PRId64 ")", data, data);
+ break;
+ }
+ case vixl::LDR_s_lit:
+ case vixl::LDR_d_lit: {
+ double data = (op == vixl::LDR_s_lit) ? *reinterpret_cast<float*>(data_address)
+ : *reinterpret_cast<double*>(data_address);
+ snprintf(buffer, buf_size_remaining, " (%g)", data);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
size_t DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin) {
const vixl::Instruction* instr = reinterpret_cast<const vixl::Instruction*>(begin);
decoder.Decode(instr);
- os << FormatInstructionPointer(begin)
+ // TODO: Use FormatInstructionPointer() once VIXL provides the appropriate
+ // features.
+ // VIXL does not yet allow remapping addresses disassembled. Using
+ // FormatInstructionPointer() would show incoherences between the instruction
+ // location addresses and the target addresses disassembled by VIXL (eg. for
+ // branch instructions).
+ os << StringPrintf("%p", instr)
<< StringPrintf(": %08x\t%s\n", instr->InstructionBits(), disasm.GetOutput());
return vixl::kInstructionSize;
}
diff --git a/disassembler/disassembler_arm64.h b/disassembler/disassembler_arm64.h
index e56fe4f08a..a370b8ded9 100644
--- a/disassembler/disassembler_arm64.h
+++ b/disassembler/disassembler_arm64.h
@@ -28,9 +28,35 @@
namespace art {
namespace arm64 {
+class CustomDisassembler FINAL : public vixl::Disassembler {
+ public:
+ explicit CustomDisassembler(bool read_literals) :
+ vixl::Disassembler(), read_literals_(read_literals) {}
+
+ // Use register aliases in the disassembly.
+ virtual void AppendRegisterNameToOutput(const vixl::Instruction* instr,
+ const vixl::CPURegister& reg) OVERRIDE;
+
+ // Improve the disassembly of literal load instructions.
+ virtual void VisitLoadLiteral(const vixl::Instruction* instr) OVERRIDE;
+
+ private:
+ // Indicate if the disassembler should read data loaded from literal pools.
+ // This should only be enabled if reading the target of literal loads is safe.
+ // Here are possible outputs when the option is on or off:
+ // read_literals_ | disassembly
+ // true | 0x72681558: 1c000acb ldr s11, pc+344 (addr 0x726816b0)
+ // false | 0x72681558: 1c000acb ldr s11, pc+344 (addr 0x726816b0) (3.40282e+38)
+ const bool read_literals_;
+};
+
class DisassemblerArm64 FINAL : public Disassembler {
public:
- explicit DisassemblerArm64(DisassemblerOptions* options) : Disassembler(options) {
+ // TODO: Update this code once VIXL provides the ability to map code addresses
+ // to disassemble as a different address (the way FormatInstructionPointer()
+ // does).
+ explicit DisassemblerArm64(DisassemblerOptions* options) :
+ Disassembler(options), disasm(options->can_read_literals_) {
decoder.AppendVisitor(&disasm);
}
@@ -39,7 +65,7 @@ class DisassemblerArm64 FINAL : public Disassembler {
private:
vixl::Decoder decoder;
- vixl::Disassembler disasm;
+ CustomDisassembler disasm;
DISALLOW_COPY_AND_ASSIGN(DisassemblerArm64);
};
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index d6309f7be3..08352de7e6 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -182,8 +182,8 @@ class OatSymbolizer FINAL : public CodeOutput {
bool result = builder_->Write();
- elf_output_->Flush();
- elf_output_->Close();
+ // Ignore I/O errors.
+ UNUSED(elf_output_->FlushClose());
return result;
}
@@ -386,9 +386,11 @@ class OatDumper {
: oat_file_(oat_file),
oat_dex_files_(oat_file.GetOatDexFiles()),
options_(options),
- disassembler_(Disassembler::Create(oat_file_.GetOatHeader().GetInstructionSet(),
+ instruction_set_(oat_file_.GetOatHeader().GetInstructionSet()),
+ disassembler_(Disassembler::Create(instruction_set_,
new DisassemblerOptions(options_->absolute_addresses_,
- oat_file.Begin()))) {
+ oat_file.Begin(),
+ true /* can_read_litals_ */))) {
CHECK(options_->class_loader_ != nullptr);
AddAllOffsets();
}
@@ -398,6 +400,10 @@ class OatDumper {
delete disassembler_;
}
+ InstructionSet GetInstructionSet() {
+ return instruction_set_;
+ }
+
bool Dump(std::ostream& os) {
bool success = true;
const OatHeader& oat_header = oat_file_.GetOatHeader();
@@ -514,7 +520,7 @@ class OatDumper {
return end_offset - begin_offset;
}
- InstructionSet GetInstructionSet() {
+ InstructionSet GetOatInstructionSet() {
return oat_file_.GetOatHeader().GetInstructionSet();
}
@@ -1259,6 +1265,7 @@ class OatDumper {
const OatFile& oat_file_;
const std::vector<const OatFile::OatDexFile*> oat_dex_files_;
const OatDumperOptions* options_;
+ InstructionSet instruction_set_;
std::set<uintptr_t> offsets_;
Disassembler* disassembler_;
};
@@ -1520,7 +1527,8 @@ class ImageDumper {
const void* GetQuickOatCodeBegin(mirror::ArtMethod* m)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- const void* quick_code = m->GetEntryPointFromQuickCompiledCode();
+ const void* quick_code = m->GetEntryPointFromQuickCompiledCodePtrSize(
+ InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet()));
if (Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(quick_code)) {
quick_code = oat_dumper_->GetQuickOatCode(m);
}
@@ -1621,11 +1629,14 @@ class ImageDumper {
}
}
} else if (obj->IsArtMethod()) {
+ const size_t image_pointer_size = InstructionSetPointerSize(
+ state->oat_dumper_->GetOatInstructionSet());
mirror::ArtMethod* method = obj->AsArtMethod();
if (method->IsNative()) {
// TODO: portable dumping.
- DCHECK(method->GetNativeGcMap() == nullptr) << PrettyMethod(method);
- DCHECK(method->GetMappingTable() == nullptr) << PrettyMethod(method);
+ DCHECK(method->GetNativeGcMapPtrSize(image_pointer_size) == nullptr)
+ << PrettyMethod(method);
+ DCHECK(method->GetMappingTable(image_pointer_size) == nullptr) << PrettyMethod(method);
bool first_occurrence;
const void* quick_oat_code = state->GetQuickOatCodeBegin(method);
uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method);
@@ -1633,33 +1644,36 @@ class ImageDumper {
if (first_occurrence) {
state->stats_.native_to_managed_code_bytes += quick_oat_code_size;
}
- if (quick_oat_code != method->GetEntryPointFromQuickCompiledCode()) {
+ if (quick_oat_code != method->GetEntryPointFromQuickCompiledCodePtrSize(
+ image_pointer_size)) {
indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code);
}
} else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
method->IsResolutionMethod() || method->IsImtConflictMethod() ||
method->IsImtUnimplementedMethod() || method->IsClassInitializer()) {
- DCHECK(method->GetNativeGcMap() == nullptr) << PrettyMethod(method);
- DCHECK(method->GetMappingTable() == nullptr) << PrettyMethod(method);
+ DCHECK(method->GetNativeGcMapPtrSize(image_pointer_size) == nullptr)
+ << PrettyMethod(method);
+ DCHECK(method->GetMappingTable(image_pointer_size) == nullptr) << PrettyMethod(method);
} else {
const DexFile::CodeItem* code_item = method->GetCodeItem();
size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
state->stats_.dex_instruction_bytes += dex_instruction_bytes;
bool first_occurrence;
- size_t gc_map_bytes = state->ComputeOatSize(method->GetNativeGcMap(), &first_occurrence);
+ size_t gc_map_bytes = state->ComputeOatSize(
+ method->GetNativeGcMapPtrSize(image_pointer_size), &first_occurrence);
if (first_occurrence) {
state->stats_.gc_map_bytes += gc_map_bytes;
}
size_t pc_mapping_table_bytes =
- state->ComputeOatSize(method->GetMappingTable(), &first_occurrence);
+ state->ComputeOatSize(method->GetMappingTable(image_pointer_size), &first_occurrence);
if (first_occurrence) {
state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
}
size_t vmap_table_bytes =
- state->ComputeOatSize(method->GetVmapTable(), &first_occurrence);
+ state->ComputeOatSize(method->GetVmapTable(image_pointer_size), &first_occurrence);
if (first_occurrence) {
state->stats_.vmap_table_bytes += vmap_table_bytes;
}
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 6b6d11e96c..b15c7127ee 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -175,7 +175,7 @@ bool PatchOat::Patch(const std::string& image_location, off_t delta,
}
gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetImageSpace();
- PatchOat p(image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
+ PatchOat p(isa, image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
delta, timings);
t.NewTiming("Patching files");
if (!p.PatchImage()) {
@@ -297,7 +297,7 @@ bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t d
CHECK(is_oat_pic == NOT_PIC);
}
- PatchOat p(elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
+ PatchOat p(isa, elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(),
delta, timings);
t.NewTiming("Patching files");
if (!skip_patching_oat && !p.PatchElf()) {
@@ -532,39 +532,44 @@ void PatchOat::VisitObject(mirror::Object* object) {
PatchOat::PatchVisitor visitor(this, copy);
object->VisitReferences<true, kVerifyNone>(visitor, visitor);
if (object->IsArtMethod<kVerifyNone>()) {
- FixupMethod(static_cast<mirror::ArtMethod*>(object),
- static_cast<mirror::ArtMethod*>(copy));
+ FixupMethod(down_cast<mirror::ArtMethod*>(object), down_cast<mirror::ArtMethod*>(copy));
}
}
void PatchOat::FixupMethod(mirror::ArtMethod* object, mirror::ArtMethod* copy) {
+ const size_t pointer_size = InstructionSetPointerSize(isa_);
// Just update the entry points if it looks like we should.
// TODO: sanity check all the pointers' values
uintptr_t portable = reinterpret_cast<uintptr_t>(
- object->GetEntryPointFromPortableCompiledCode<kVerifyNone>());
+ object->GetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(pointer_size));
if (portable != 0) {
- copy->SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(portable + delta_));
+ copy->SetEntryPointFromPortableCompiledCodePtrSize(reinterpret_cast<void*>(portable + delta_),
+ pointer_size);
}
uintptr_t quick= reinterpret_cast<uintptr_t>(
- object->GetEntryPointFromQuickCompiledCode<kVerifyNone>());
+ object->GetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(pointer_size));
if (quick != 0) {
- copy->SetEntryPointFromQuickCompiledCode(reinterpret_cast<void*>(quick + delta_));
+ copy->SetEntryPointFromQuickCompiledCodePtrSize(reinterpret_cast<void*>(quick + delta_),
+ pointer_size);
}
uintptr_t interpreter = reinterpret_cast<uintptr_t>(
- object->GetEntryPointFromInterpreter<kVerifyNone>());
+ object->GetEntryPointFromInterpreterPtrSize<kVerifyNone>(pointer_size));
if (interpreter != 0) {
- copy->SetEntryPointFromInterpreter(
- reinterpret_cast<mirror::EntryPointFromInterpreter*>(interpreter + delta_));
+ copy->SetEntryPointFromInterpreterPtrSize(
+ reinterpret_cast<mirror::EntryPointFromInterpreter*>(interpreter + delta_), pointer_size);
}
- uintptr_t native_method = reinterpret_cast<uintptr_t>(object->GetNativeMethod());
+ uintptr_t native_method = reinterpret_cast<uintptr_t>(
+ object->GetEntryPointFromJniPtrSize(pointer_size));
if (native_method != 0) {
- copy->SetNativeMethod(reinterpret_cast<void*>(native_method + delta_));
+ copy->SetEntryPointFromJniPtrSize(reinterpret_cast<void*>(native_method + delta_),
+ pointer_size);
}
- uintptr_t native_gc_map = reinterpret_cast<uintptr_t>(object->GetNativeGcMap());
+ uintptr_t native_gc_map = reinterpret_cast<uintptr_t>(
+ object->GetNativeGcMapPtrSize(pointer_size));
if (native_gc_map != 0) {
- copy->SetNativeGcMap(reinterpret_cast<uint8_t*>(native_gc_map + delta_));
+ copy->SetNativeGcMapPtrSize(reinterpret_cast<uint8_t*>(native_gc_map + delta_), pointer_size);
}
}
@@ -899,6 +904,20 @@ static File* CreateOrOpen(const char* name, bool* created) {
}
}
+// Either try to close the file (close=true), or erase it.
+static bool FinishFile(File* file, bool close) {
+ if (close) {
+ if (file->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close file.";
+ return false;
+ }
+ return true;
+ } else {
+ file->Erase();
+ return false;
+ }
+}
+
static int patchoat(int argc, char **argv) {
InitLogging(argv);
MemMap::Init();
@@ -1170,7 +1189,7 @@ static int patchoat(int argc, char **argv) {
if (output_image_filename.empty()) {
output_image_filename = "output-image-file";
}
- output_image.reset(new File(output_image_fd, output_image_filename));
+ output_image.reset(new File(output_image_fd, output_image_filename, true));
} else {
CHECK(!output_image_filename.empty());
output_image.reset(CreateOrOpen(output_image_filename.c_str(), &new_image_out));
@@ -1184,7 +1203,7 @@ static int patchoat(int argc, char **argv) {
if (input_oat_filename.empty()) {
input_oat_filename = "input-oat-file";
}
- input_oat.reset(new File(input_oat_fd, input_oat_filename));
+ input_oat.reset(new File(input_oat_fd, input_oat_filename, false));
if (input_oat == nullptr) {
// Unlikely, but ensure exhaustive logging in non-0 exit code case
LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd;
@@ -1203,7 +1222,7 @@ static int patchoat(int argc, char **argv) {
if (output_oat_filename.empty()) {
output_oat_filename = "output-oat-file";
}
- output_oat.reset(new File(output_oat_fd, output_oat_filename));
+ output_oat.reset(new File(output_oat_fd, output_oat_filename, true));
if (output_oat == nullptr) {
// Unlikely, but ensure exhaustive logging in non-0 exit code case
LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd;
@@ -1276,14 +1295,20 @@ static int patchoat(int argc, char **argv) {
output_oat.get(), output_image.get(), isa, &timings,
output_oat_fd >= 0, // was it opened from FD?
new_oat_out);
+ // The order here doesn't matter. If the first one is successfully saved and the second one
+ // erased, ImageSpace will still detect a problem and not use the files.
+ ret = ret && FinishFile(output_image.get(), ret);
+ ret = ret && FinishFile(output_oat.get(), ret);
} else if (have_oat_files) {
TimingLogger::ScopedTiming pt("patch oat", &timings);
ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
output_oat_fd >= 0, // was it opened from FD?
new_oat_out);
+ ret = ret && FinishFile(output_oat.get(), ret);
} else if (have_image_files) {
TimingLogger::ScopedTiming pt("patch image", &timings);
ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings);
+ ret = ret && FinishFile(output_image.get(), ret);
} else {
CHECK(false);
ret = true;
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 5a3545bb7b..578df3aef7 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -61,15 +61,16 @@ class PatchOat {
// Takes ownership only of the ElfFile. All other pointers are only borrowed.
PatchOat(ElfFile* oat_file, off_t delta, TimingLogger* timings)
: oat_file_(oat_file), image_(nullptr), bitmap_(nullptr), heap_(nullptr), delta_(delta),
- timings_(timings) {}
- PatchOat(MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap,
+ isa_(kNone), timings_(timings) {}
+ PatchOat(InstructionSet isa, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap,
MemMap* heap, off_t delta, TimingLogger* timings)
: image_(image), bitmap_(bitmap), heap_(heap),
- delta_(delta), timings_(timings) {}
- PatchOat(ElfFile* oat_file, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap,
- MemMap* heap, off_t delta, TimingLogger* timings)
+ delta_(delta), isa_(isa), timings_(timings) {}
+ PatchOat(InstructionSet isa, ElfFile* oat_file, MemMap* image,
+ gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta,
+ TimingLogger* timings)
: oat_file_(oat_file), image_(image), bitmap_(bitmap), heap_(heap),
- delta_(delta), timings_(timings) {}
+ delta_(delta), isa_(isa), timings_(timings) {}
~PatchOat() {}
// Was the .art image at image_path made with --compile-pic ?
@@ -156,8 +157,10 @@ class PatchOat {
const MemMap* const heap_;
// The amount we are changing the offset by.
const off_t delta_;
- // Timing splits.
- TimingLogger* const timings_;
+ // Active instruction set, used to know the entrypoint size.
+ const InstructionSet isa_;
+
+ TimingLogger* timings_;
DISALLOW_IMPLICIT_CONSTRUCTORS(PatchOat);
};
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 25fe45f5cd..087c0ea990 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -302,6 +302,7 @@ LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \
base/allocator.h \
base/mutex.h \
debugger.h \
+ base/unix_file/fd_file.h \
dex_file.h \
dex_instruction.h \
gc/allocator/rosalloc.h \
@@ -389,7 +390,9 @@ define build-libart
LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION)
ifeq ($$(art_ndebug_or_debug),ndebug)
LOCAL_MODULE := libart
- LOCAL_FDO_SUPPORT := true
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_FDO_SUPPORT := true
+ endif
else # debug
LOCAL_MODULE := libartd
endif
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index f49c0371e0..f8590d3bd2 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -108,12 +108,7 @@ const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromBitmap(uint32_t
}
const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCppDefines() {
-#if defined(HAVE_ANDROID_OS) && (ANDROID_SMP == 0)
- const bool smp = false;
-#else
const bool smp = true;
-#endif
-
#if defined(__ARM_ARCH_EXT_IDIV__)
const bool has_div = true;
#else
@@ -204,11 +199,8 @@ static void bad_divide_inst_handle(int signo ATTRIBUTE_UNUSED, siginfo_t* si ATT
}
const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromAssembly() {
-#if defined(HAVE_ANDROID_OS) && (ANDROID_SMP == 0)
- const bool smp = false;
-#else
const bool smp = true;
-#endif
+
// See if have a sdiv instruction. Register a signal handler and try to execute an sdiv
// instruction. If we get a SIGILL then it's not supported.
struct sigaction sa, osa;
diff --git a/runtime/arch/arm/portable_entrypoints_arm.S b/runtime/arch/arm/portable_entrypoints_arm.S
index d37e7604eb..89ac1f7a03 100644
--- a/runtime/arch/arm/portable_entrypoints_arm.S
+++ b/runtime/arch/arm/portable_entrypoints_arm.S
@@ -53,7 +53,7 @@ ENTRY art_portable_invoke_stub
mov ip, #0 @ set ip to 0
str ip, [sp] @ store NULL for method* at bottom of frame
add sp, #16 @ first 4 args are not passed on stack for portable
- ldr ip, [r0, #MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET] @ get pointer to the code
+ ldr ip, [r0, #MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32] @ get pointer to the code
blx ip @ call the method
mov sp, r11 @ restore the stack pointer
ldr ip, [sp, #24] @ load the result pointer
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 632b41435f..1782db50b9 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -422,7 +422,7 @@ ENTRY art_quick_invoke_stub_internal
mov r4, #SUSPEND_CHECK_INTERVAL @ reset r4 to suspend check interval
#endif
- ldr ip, [r0, #MIRROR_ART_METHOD_QUICK_CODE_OFFSET] @ get pointer to the code
+ ldr ip, [r0, #MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32] @ get pointer to the code
blx ip @ call the method
mov sp, r11 @ restore the stack pointer
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index 696dd94e58..a1270dcde9 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -26,15 +26,29 @@ namespace art {
const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromVariant(
const std::string& variant ATTRIBUTE_UNUSED, std::string* error_msg ATTRIBUTE_UNUSED) {
- if (variant != "default" && variant != "generic") {
- std::ostringstream os;
- os << "Unexpected CPU variant for Arm64: " << variant;
- *error_msg = os.str();
- return nullptr;
- }
const bool smp = true; // Conservative default.
- const bool is_a53 = true; // Pessimistically assume all ARM64s are A53s.
- return new Arm64InstructionSetFeatures(smp, is_a53);
+
+ // Look for variants that need a fix for a53 erratum 835769.
+ static const char* arm64_variants_with_a53_835769_bug[] = {
+ "default", "generic" // Pessimistically assume all generic ARM64s are A53s.
+ };
+ bool needs_a53_835769_fix = FindVariantInArray(arm64_variants_with_a53_835769_bug,
+ arraysize(arm64_variants_with_a53_835769_bug),
+ variant);
+
+ if (!needs_a53_835769_fix) {
+ // Check to see if this is an expected variant.
+ static const char* arm64_known_variants[] = {
+ "denver64"
+ };
+ if (!FindVariantInArray(arm64_known_variants, arraysize(arm64_known_variants), variant)) {
+ std::ostringstream os;
+ os << "Unexpected CPU variant for Arm64: " << variant;
+ *error_msg = os.str();
+ return nullptr;
+ }
+ }
+ return new Arm64InstructionSetFeatures(smp, needs_a53_835769_fix);
}
const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromBitmap(uint32_t bitmap) {
@@ -44,12 +58,7 @@ const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromBitmap(uint3
}
const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromCppDefines() {
-#if defined(HAVE_ANDROID_OS) && (ANDROID_SMP == 0)
- const bool smp = false;
-#else
const bool smp = true;
-#endif
-
const bool is_a53 = true; // Pessimistically assume all ARM64s are A53s.
return new Arm64InstructionSetFeatures(smp, is_a53);
}
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.h b/runtime/arch/arm64/instruction_set_features_arm64.h
index ee41536449..b0c66b3272 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.h
+++ b/runtime/arch/arm64/instruction_set_features_arm64.h
@@ -70,8 +70,8 @@ class Arm64InstructionSetFeatures FINAL : public InstructionSetFeatures {
std::string* error_msg) const OVERRIDE;
private:
- explicit Arm64InstructionSetFeatures(bool smp, bool is_a53)
- : InstructionSetFeatures(smp), fix_cortex_a53_835769_(is_a53) {
+ explicit Arm64InstructionSetFeatures(bool smp, bool needs_a53_835769_fix)
+ : InstructionSetFeatures(smp), fix_cortex_a53_835769_(needs_a53_835769_fix) {
}
// Bitmap positions for encoding features as a bitmap.
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 147d4348be..44159354ab 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -564,7 +564,7 @@ SAVE_SIZE_AND_METHOD=SAVE_SIZE+STACK_REFERENCE_SIZE
.macro INVOKE_STUB_CALL_AND_RETURN
// load method-> METHOD_QUICK_CODE_OFFSET
- ldr x9, [x0 , #MIRROR_ART_METHOD_QUICK_CODE_OFFSET]
+ ldr x9, [x0 , #MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64]
// Branch to method.
blr x9
diff --git a/runtime/arch/instruction_set.h b/runtime/arch/instruction_set.h
index 46622eb37d..e413880094 100644
--- a/runtime/arch/instruction_set.h
+++ b/runtime/arch/instruction_set.h
@@ -127,6 +127,10 @@ static inline bool Is64BitInstructionSet(InstructionSet isa) {
}
}
+static inline size_t InstructionSetPointerSize(InstructionSet isa) {
+ return Is64BitInstructionSet(isa) ? 8U : 4U;
+}
+
static inline size_t GetBytesPerGprSpillLocation(InstructionSet isa) {
switch (isa) {
case kArm:
diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc
index 83571cf2b1..e6f4e7ab01 100644
--- a/runtime/arch/instruction_set_features_test.cc
+++ b/runtime/arch/instruction_set_features_test.cc
@@ -27,12 +27,17 @@
namespace art {
#ifdef HAVE_ANDROID_OS
+#if defined(__aarch64__)
+TEST(InstructionSetFeaturesTest, DISABLED_FeaturesFromSystemPropertyVariant) {
+ LOG(WARNING) << "Test disabled due to no CPP define for A53 erratum 835769";
+#else
TEST(InstructionSetFeaturesTest, FeaturesFromSystemPropertyVariant) {
+#endif
// Take the default set of instruction features from the build.
std::unique_ptr<const InstructionSetFeatures> instruction_set_features(
InstructionSetFeatures::FromCppDefines());
- // Read the features property.
+ // Read the variant property.
std::string key = StringPrintf("dalvik.vm.isa.%s.variant", GetInstructionSetString(kRuntimeISA));
char dex2oat_isa_variant[PROPERTY_VALUE_MAX];
if (property_get(key.c_str(), dex2oat_isa_variant, nullptr) > 0) {
@@ -49,29 +54,41 @@ TEST(InstructionSetFeaturesTest, FeaturesFromSystemPropertyVariant) {
}
}
+#if defined(__aarch64__)
+TEST(InstructionSetFeaturesTest, DISABLED_FeaturesFromSystemPropertyString) {
+ LOG(WARNING) << "Test disabled due to no CPP define for A53 erratum 835769";
+#else
TEST(InstructionSetFeaturesTest, FeaturesFromSystemPropertyString) {
+#endif
// Take the default set of instruction features from the build.
std::unique_ptr<const InstructionSetFeatures> instruction_set_features(
InstructionSetFeatures::FromCppDefines());
- // Read the features property.
- std::string key = StringPrintf("dalvik.vm.isa.%s.features", GetInstructionSetString(kRuntimeISA));
- char dex2oat_isa_features[PROPERTY_VALUE_MAX];
- if (property_get(key.c_str(), dex2oat_isa_features, nullptr) > 0) {
- // Use features from property to build InstructionSetFeatures and check against build's
- // features.
- std::string error_msg;
- std::unique_ptr<const InstructionSetFeatures> base_features(
- InstructionSetFeatures::FromVariant(kRuntimeISA, "default", &error_msg));
- ASSERT_TRUE(base_features.get() != nullptr) << error_msg;
-
- std::unique_ptr<const InstructionSetFeatures> property_features(
- base_features->AddFeaturesFromString(dex2oat_isa_features, &error_msg));
- ASSERT_TRUE(property_features.get() != nullptr) << error_msg;
-
- EXPECT_TRUE(property_features->Equals(instruction_set_features.get()))
+ // Read the variant property.
+ std::string variant_key = StringPrintf("dalvik.vm.isa.%s.variant",
+ GetInstructionSetString(kRuntimeISA));
+ char dex2oat_isa_variant[PROPERTY_VALUE_MAX];
+ if (property_get(variant_key.c_str(), dex2oat_isa_variant, nullptr) > 0) {
+ // Read the features property.
+ std::string features_key = StringPrintf("dalvik.vm.isa.%s.features",
+ GetInstructionSetString(kRuntimeISA));
+ char dex2oat_isa_features[PROPERTY_VALUE_MAX];
+ if (property_get(features_key.c_str(), dex2oat_isa_features, nullptr) > 0) {
+ // Use features from property to build InstructionSetFeatures and check against build's
+ // features.
+ std::string error_msg;
+ std::unique_ptr<const InstructionSetFeatures> base_features(
+ InstructionSetFeatures::FromVariant(kRuntimeISA, dex2oat_isa_variant, &error_msg));
+ ASSERT_TRUE(base_features.get() != nullptr) << error_msg;
+
+ std::unique_ptr<const InstructionSetFeatures> property_features(
+ base_features->AddFeaturesFromString(dex2oat_isa_features, &error_msg));
+ ASSERT_TRUE(property_features.get() != nullptr) << error_msg;
+
+ EXPECT_TRUE(property_features->Equals(instruction_set_features.get()))
<< "System property features: " << *property_features.get()
<< "\nFeatures from build: " << *instruction_set_features.get();
+ }
}
}
@@ -127,13 +144,7 @@ TEST(InstructionSetFeaturesTest, FeaturesFromHwcap) {
<< "\nFeatures from build: " << *instruction_set_features.get();
}
-
-#if defined(__arm__)
-TEST(InstructionSetFeaturesTest, DISABLED_FeaturesFromAssembly) {
- LOG(WARNING) << "Test disabled due to buggy ARM kernels";
-#else
TEST(InstructionSetFeaturesTest, FeaturesFromAssembly) {
-#endif
// Take the default set of instruction features from the build.
std::unique_ptr<const InstructionSetFeatures> instruction_set_features(
InstructionSetFeatures::FromCppDefines());
diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc
index efec993340..11be2a8bc2 100644
--- a/runtime/arch/mips/instruction_set_features_mips.cc
+++ b/runtime/arch/mips/instruction_set_features_mips.cc
@@ -44,11 +44,7 @@ const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromBitmap(uint32_
}
const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromCppDefines() {
-#if defined(HAVE_ANDROID_OS) && (ANDROID_SMP == 0)
- const bool smp = false;
-#else
const bool smp = true;
-#endif
// TODO: here we assume the FPU is always 32-bit.
const bool fpu_32bit = true;
diff --git a/runtime/arch/mips/portable_entrypoints_mips.S b/runtime/arch/mips/portable_entrypoints_mips.S
index d7e7a8e96b..8d418e8dd1 100644
--- a/runtime/arch/mips/portable_entrypoints_mips.S
+++ b/runtime/arch/mips/portable_entrypoints_mips.S
@@ -98,7 +98,7 @@ ENTRY art_portable_invoke_stub
lw $a1, 4($sp) # copy arg value for a1
lw $a2, 8($sp) # copy arg value for a2
lw $a3, 12($sp) # copy arg value for a3
- lw $t9, MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET($a0) # get pointer to the code
+ lw $t9, MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32($a0) # get pointer to the code
jalr $t9 # call the method
sw $zero, 0($sp) # store NULL for method* at bottom of frame
move $sp, $fp # restore the stack
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index e878ef7186..482485747a 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -512,7 +512,7 @@ ENTRY art_quick_invoke_stub
lw $a1, 4($sp) # copy arg value for a1
lw $a2, 8($sp) # copy arg value for a2
lw $a3, 12($sp) # copy arg value for a3
- lw $t9, MIRROR_ART_METHOD_QUICK_CODE_OFFSET($a0) # get pointer to the code
+ lw $t9, MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32($a0) # get pointer to the code
jalr $t9 # call the method
sw $zero, 0($sp) # store NULL for method* at bottom of frame
move $sp, $fp # restore the stack
diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc
index 32cf909fd1..a12773dc9c 100644
--- a/runtime/arch/x86/instruction_set_features_x86.cc
+++ b/runtime/arch/x86/instruction_set_features_x86.cc
@@ -70,11 +70,7 @@ const X86InstructionSetFeatures* X86InstructionSetFeatures::FromBitmap(uint32_t
}
const X86InstructionSetFeatures* X86InstructionSetFeatures::FromCppDefines(bool x86_64) {
-#if defined(HAVE_ANDROID_OS) && (ANDROID_SMP == 0)
- const bool smp = false;
-#else
const bool smp = true;
-#endif
#ifndef __SSSE3__
const bool has_SSSE3 = false;
diff --git a/runtime/arch/x86/portable_entrypoints_x86.S b/runtime/arch/x86/portable_entrypoints_x86.S
index a7c4124a47..1f0900e86d 100644
--- a/runtime/arch/x86/portable_entrypoints_x86.S
+++ b/runtime/arch/x86/portable_entrypoints_x86.S
@@ -46,7 +46,7 @@ DEFINE_FUNCTION art_portable_invoke_stub
addl LITERAL(12), %esp // pop arguments to memcpy
mov 12(%ebp), %eax // move method pointer into eax
mov %eax, (%esp) // push method pointer onto stack
- call *MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET(%eax) // call the method
+ call *MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32(%eax) // call the method
mov %ebp, %esp // restore stack pointer
POP ebx // pop ebx
POP ebp // pop ebp
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 0109a7c553..1ce01c46f1 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -316,7 +316,7 @@ DEFINE_FUNCTION art_quick_invoke_stub
mov 4(%esp), %ecx // copy arg1 into ecx
mov 8(%esp), %edx // copy arg2 into edx
mov 12(%esp), %ebx // copy arg3 into ebx
- call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET(%eax) // call the method
+ call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // call the method
mov %ebp, %esp // restore stack pointer
CFI_DEF_CFA_REGISTER(esp)
POP ebx // pop ebx
@@ -1117,7 +1117,7 @@ DEFINE_FUNCTION art_quick_instrumentation_entry
PUSH eax // Save eax which will be clobbered by the callee-save method.
subl LITERAL(12), %esp // Align stack.
CFI_ADJUST_CFA_OFFSET(12)
- pushl 40(%esp) // Pass LR.
+ pushl FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-4+16(%esp) // Pass LR.
CFI_ADJUST_CFA_OFFSET(4)
pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current().
CFI_ADJUST_CFA_OFFSET(4)
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index bed7238b09..a80e7d2989 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -518,7 +518,7 @@ DEFINE_FUNCTION art_quick_invoke_stub
LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished
LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished
.Lgpr_setup_finished:
- call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method.
+ call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
movq %rbp, %rsp // Restore stack pointer.
CFI_DEF_CFA_REGISTER(rsp)
POP r9 // Pop r9 - shorty*.
@@ -601,7 +601,7 @@ DEFINE_FUNCTION art_quick_invoke_static_stub
LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished2
LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished2
.Lgpr_setup_finished2:
- call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method.
+ call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method.
movq %rbp, %rsp // Restore stack pointer.
CFI_DEF_CFA_REGISTER(rsp)
POP r9 // Pop r9 - shorty*.
@@ -1446,7 +1446,7 @@ DEFINE_FUNCTION art_quick_instrumentation_entry
movq %rdi, %r12 // Preserve method pointer in a callee-save.
movq %gs:THREAD_SELF_OFFSET, %rdx // Pass thread.
- movq FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-8(%rsp), %r8 // Pass return PC.
+ movq FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-8(%rsp), %rcx // Pass return PC.
call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, LR)
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 26df045a25..4b4c8855d4 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -148,13 +148,21 @@ ADD_TEST_EQ(MIRROR_STRING_OFFSET_OFFSET, art::mirror::String::OffsetOffset().Int
ADD_TEST_EQ(MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET,
art::mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())
-#define MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET (32 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET,
- art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset().Int32Value())
+#define MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32 (48 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32,
+ art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset(4).Int32Value())
-#define MIRROR_ART_METHOD_QUICK_CODE_OFFSET (40 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET,
- art::mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value())
+#define MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32 (40 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32,
+ art::mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value())
+
+#define MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_64 (64 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_64,
+ art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset(8).Int32Value())
+
+#define MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64 (48 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64,
+ art::mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(8).Int32Value())
#if defined(__cplusplus)
} // End of CheckAsmSupportOffsets.
diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc
index bf091d00d2..0e93eee627 100644
--- a/runtime/base/scoped_flock.cc
+++ b/runtime/base/scoped_flock.cc
@@ -27,6 +27,9 @@ namespace art {
bool ScopedFlock::Init(const char* filename, std::string* error_msg) {
while (true) {
+ if (file_.get() != nullptr) {
+ UNUSED(file_->FlushCloseOrErase()); // Ignore result.
+ }
file_.reset(OS::OpenFileWithFlags(filename, O_CREAT | O_RDWR));
if (file_.get() == NULL) {
*error_msg = StringPrintf("Failed to open file '%s': %s", filename, strerror(errno));
@@ -59,7 +62,7 @@ bool ScopedFlock::Init(const char* filename, std::string* error_msg) {
}
bool ScopedFlock::Init(File* file, std::string* error_msg) {
- file_.reset(new File(dup(file->Fd())));
+ file_.reset(new File(dup(file->Fd()), true));
if (file_->Fd() == -1) {
file_.reset();
*error_msg = StringPrintf("Failed to duplicate open file '%s': %s",
@@ -89,6 +92,9 @@ ScopedFlock::~ScopedFlock() {
if (file_.get() != NULL) {
int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN));
CHECK_EQ(0, flock_result);
+ if (file_->FlushCloseOrErase() != 0) {
+ PLOG(WARNING) << "Could not close scoped file lock file.";
+ }
}
}
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index f29a7ec974..6e5e7a1582 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -14,28 +14,68 @@
* limitations under the License.
*/
-#include "base/logging.h"
#include "base/unix_file/fd_file.h"
+
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include "base/logging.h"
+
namespace unix_file {
-FdFile::FdFile() : fd_(-1), auto_close_(true) {
+FdFile::FdFile() : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true) {
}
-FdFile::FdFile(int fd) : fd_(fd), auto_close_(true) {
+FdFile::FdFile(int fd, bool check_usage)
+ : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
+ fd_(fd), auto_close_(true) {
}
-FdFile::FdFile(int fd, const std::string& path) : fd_(fd), file_path_(path), auto_close_(true) {
+FdFile::FdFile(int fd, const std::string& path, bool check_usage)
+ : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
+ fd_(fd), file_path_(path), auto_close_(true) {
CHECK_NE(0U, path.size());
}
FdFile::~FdFile() {
+ if (kCheckSafeUsage && (guard_state_ < GuardState::kNoCheck)) {
+ if (guard_state_ < GuardState::kFlushed) {
+ LOG(::art::ERROR) << "File " << file_path_ << " wasn't explicitly flushed before destruction.";
+ }
+ if (guard_state_ < GuardState::kClosed) {
+ LOG(::art::ERROR) << "File " << file_path_ << " wasn't explicitly closed before destruction.";
+ }
+ CHECK_GE(guard_state_, GuardState::kClosed);
+ }
if (auto_close_ && fd_ != -1) {
- Close();
+ if (Close() != 0) {
+ PLOG(::art::WARNING) << "Failed to close file " << file_path_;
+ }
+ }
+}
+
+void FdFile::moveTo(GuardState target, GuardState warn_threshold, const char* warning) {
+ if (kCheckSafeUsage) {
+ if (guard_state_ < GuardState::kNoCheck) {
+ if (warn_threshold < GuardState::kNoCheck && guard_state_ >= warn_threshold) {
+ LOG(::art::ERROR) << warning;
+ }
+ guard_state_ = target;
+ }
+ }
+}
+
+void FdFile::moveUp(GuardState target, const char* warning) {
+ if (kCheckSafeUsage) {
+ if (guard_state_ < GuardState::kNoCheck) {
+ if (guard_state_ < target) {
+ guard_state_ = target;
+ } else if (target < guard_state_) {
+ LOG(::art::ERROR) << warning;
+ }
+ }
}
}
@@ -54,11 +94,28 @@ bool FdFile::Open(const std::string& path, int flags, mode_t mode) {
return false;
}
file_path_ = path;
+ static_assert(O_RDONLY == 0, "Readonly flag has unexpected value.");
+ if (kCheckSafeUsage && (flags & (O_RDWR | O_CREAT | O_WRONLY)) != 0) {
+ // Start in the base state (not flushed, not closed).
+ guard_state_ = GuardState::kBase;
+ } else {
+ // We are not concerned with read-only files. In that case, proper flushing and closing is
+ // not important.
+ guard_state_ = GuardState::kNoCheck;
+ }
return true;
}
int FdFile::Close() {
int result = TEMP_FAILURE_RETRY(close(fd_));
+
+ // Test here, so the file is closed and not leaked.
+ if (kCheckSafeUsage) {
+ CHECK_GE(guard_state_, GuardState::kFlushed) << "File " << file_path_
+ << " has not been flushed before closing.";
+ moveUp(GuardState::kClosed, nullptr);
+ }
+
if (result == -1) {
return -errno;
} else {
@@ -74,6 +131,7 @@ int FdFile::Flush() {
#else
int rc = TEMP_FAILURE_RETRY(fsync(fd_));
#endif
+ moveUp(GuardState::kFlushed, "Flushing closed file.");
return (rc == -1) ? -errno : rc;
}
@@ -92,6 +150,7 @@ int FdFile::SetLength(int64_t new_length) {
#else
int rc = TEMP_FAILURE_RETRY(ftruncate(fd_, new_length));
#endif
+ moveTo(GuardState::kBase, GuardState::kClosed, "Truncating closed file.");
return (rc == -1) ? -errno : rc;
}
@@ -107,6 +166,7 @@ int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
#else
int rc = TEMP_FAILURE_RETRY(pwrite(fd_, buf, byte_count, offset));
#endif
+ moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
return (rc == -1) ? -errno : rc;
}
@@ -135,6 +195,7 @@ bool FdFile::ReadFully(void* buffer, size_t byte_count) {
bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
const char* ptr = static_cast<const char*>(buffer);
+ moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
while (byte_count > 0) {
ssize_t bytes_written = TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
if (bytes_written == -1) {
@@ -146,4 +207,38 @@ bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
return true;
}
+void FdFile::Erase() {
+ TEMP_FAILURE_RETRY(SetLength(0));
+ TEMP_FAILURE_RETRY(Flush());
+ TEMP_FAILURE_RETRY(Close());
+}
+
+int FdFile::FlushCloseOrErase() {
+ int flush_result = TEMP_FAILURE_RETRY(Flush());
+ if (flush_result != 0) {
+ LOG(::art::ERROR) << "CloseOrErase failed while flushing a file.";
+ Erase();
+ return flush_result;
+ }
+ int close_result = TEMP_FAILURE_RETRY(Close());
+ if (close_result != 0) {
+ LOG(::art::ERROR) << "CloseOrErase failed while closing a file.";
+ Erase();
+ return close_result;
+ }
+ return 0;
+}
+
+int FdFile::FlushClose() {
+ int flush_result = TEMP_FAILURE_RETRY(Flush());
+ if (flush_result != 0) {
+ LOG(::art::ERROR) << "FlushClose failed while flushing a file.";
+ }
+ int close_result = TEMP_FAILURE_RETRY(Close());
+ if (close_result != 0) {
+ LOG(::art::ERROR) << "FlushClose failed while closing a file.";
+ }
+ return (flush_result != 0) ? flush_result : close_result;
+}
+
} // namespace unix_file
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index 01f4ca2819..8db2ee4a81 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -24,6 +24,9 @@
namespace unix_file {
+// If true, check whether Flush and Close are called before destruction.
+static constexpr bool kCheckSafeUsage = true;
+
// A RandomAccessFile implementation backed by a file descriptor.
//
// Not thread safe.
@@ -32,8 +35,8 @@ class FdFile : public RandomAccessFile {
FdFile();
// Creates an FdFile using the given file descriptor. Takes ownership of the
// file descriptor. (Use DisableAutoClose to retain ownership.)
- explicit FdFile(int fd);
- explicit FdFile(int fd, const std::string& path);
+ explicit FdFile(int fd, bool checkUsage);
+ explicit FdFile(int fd, const std::string& path, bool checkUsage);
// Destroys an FdFile, closing the file descriptor if Close hasn't already
// been called. (If you care about the return value of Close, call it
@@ -47,12 +50,21 @@ class FdFile : public RandomAccessFile {
bool Open(const std::string& file_path, int flags, mode_t mode);
// RandomAccessFile API.
- virtual int Close();
- virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const;
- virtual int SetLength(int64_t new_length);
+ virtual int Close() WARN_UNUSED;
+ virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const WARN_UNUSED;
+ virtual int SetLength(int64_t new_length) WARN_UNUSED;
virtual int64_t GetLength() const;
- virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset);
- virtual int Flush();
+ virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset) WARN_UNUSED;
+ virtual int Flush() WARN_UNUSED;
+
+ // Short for SetLength(0); Flush(); Close();
+ void Erase();
+
+ // Try to Flush(), then try to Close(); If either fails, call Erase().
+ int FlushCloseOrErase() WARN_UNUSED;
+
+ // Try to Flush and Close(). Attempts both, but returns the first error.
+ int FlushClose() WARN_UNUSED;
// Bonus API.
int Fd() const;
@@ -61,8 +73,35 @@ class FdFile : public RandomAccessFile {
return file_path_;
}
void DisableAutoClose();
- bool ReadFully(void* buffer, size_t byte_count);
- bool WriteFully(const void* buffer, size_t byte_count);
+ bool ReadFully(void* buffer, size_t byte_count) WARN_UNUSED;
+ bool WriteFully(const void* buffer, size_t byte_count) WARN_UNUSED;
+
+ // This enum is public so that we can define the << operator over it.
+ enum class GuardState {
+ kBase, // Base, file has not been flushed or closed.
+ kFlushed, // File has been flushed, but not closed.
+ kClosed, // File has been flushed and closed.
+ kNoCheck // Do not check for the current file instance.
+ };
+
+ protected:
+ // If the guard state indicates checking (!=kNoCheck), go to the target state "target". Print the
+ // given warning if the current state is or exceeds warn_threshold.
+ void moveTo(GuardState target, GuardState warn_threshold, const char* warning);
+
+ // If the guard state indicates checking (<kNoCheck), and is below the target state "target", go
+ // to "target." If the current state is higher (excluding kNoCheck) than the trg state, print the
+ // warning.
+ void moveUp(GuardState target, const char* warning);
+
+ // Forcefully sets the state to the given one. This can overwrite kNoCheck.
+ void resetGuard(GuardState new_state) {
+ if (kCheckSafeUsage) {
+ guard_state_ = new_state;
+ }
+ }
+
+ GuardState guard_state_;
private:
int fd_;
@@ -72,6 +111,8 @@ class FdFile : public RandomAccessFile {
DISALLOW_COPY_AND_ASSIGN(FdFile);
};
+std::ostream& operator<<(std::ostream& os, const FdFile::GuardState& kind);
+
} // namespace unix_file
#endif // ART_RUNTIME_BASE_UNIX_FILE_FD_FILE_H_
diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc
index 3481f2ff9f..a7e5b96460 100644
--- a/runtime/base/unix_file/fd_file_test.cc
+++ b/runtime/base/unix_file/fd_file_test.cc
@@ -24,7 +24,7 @@ namespace unix_file {
class FdFileTest : public RandomAccessFileTest {
protected:
virtual RandomAccessFile* MakeTestFile() {
- return new FdFile(fileno(tmpfile()));
+ return new FdFile(fileno(tmpfile()), false);
}
};
@@ -53,6 +53,7 @@ TEST_F(FdFileTest, OpenClose) {
ASSERT_TRUE(file.Open(good_path, O_CREAT | O_WRONLY));
EXPECT_GE(file.Fd(), 0);
EXPECT_TRUE(file.IsOpened());
+ EXPECT_EQ(0, file.Flush());
EXPECT_EQ(0, file.Close());
EXPECT_EQ(-1, file.Fd());
EXPECT_FALSE(file.IsOpened());
@@ -60,7 +61,7 @@ TEST_F(FdFileTest, OpenClose) {
EXPECT_GE(file.Fd(), 0);
EXPECT_TRUE(file.IsOpened());
- file.Close();
+ ASSERT_EQ(file.Close(), 0);
ASSERT_EQ(unlink(good_path.c_str()), 0);
}
diff --git a/runtime/base/unix_file/random_access_file_test.h b/runtime/base/unix_file/random_access_file_test.h
index 0002433628..e7ace4caaa 100644
--- a/runtime/base/unix_file/random_access_file_test.h
+++ b/runtime/base/unix_file/random_access_file_test.h
@@ -76,6 +76,8 @@ class RandomAccessFileTest : public testing::Test {
ASSERT_EQ(content.size(), static_cast<uint64_t>(file->Write(content.data(), content.size(), 0)));
TestReadContent(content, file.get());
+
+ CleanUp(file.get());
}
void TestReadContent(const std::string& content, RandomAccessFile* file) {
@@ -131,6 +133,8 @@ class RandomAccessFileTest : public testing::Test {
ASSERT_EQ(new_length, file->GetLength());
ASSERT_TRUE(ReadString(file.get(), &new_content));
ASSERT_EQ('\0', new_content[new_length - 1]);
+
+ CleanUp(file.get());
}
void TestWrite() {
@@ -163,6 +167,11 @@ class RandomAccessFileTest : public testing::Test {
ASSERT_EQ(file->GetLength(), new_length);
ASSERT_TRUE(ReadString(file.get(), &new_content));
ASSERT_EQ(std::string("hello\0hello", new_length), new_content);
+
+ CleanUp(file.get());
+ }
+
+ virtual void CleanUp(RandomAccessFile* file ATTRIBUTE_UNUSED) {
}
protected:
diff --git a/runtime/base/unix_file/random_access_file_utils_test.cc b/runtime/base/unix_file/random_access_file_utils_test.cc
index 63179220a2..9457d22424 100644
--- a/runtime/base/unix_file/random_access_file_utils_test.cc
+++ b/runtime/base/unix_file/random_access_file_utils_test.cc
@@ -37,14 +37,14 @@ TEST_F(RandomAccessFileUtilsTest, CopyFile) {
}
TEST_F(RandomAccessFileUtilsTest, BadSrc) {
- FdFile src(-1);
+ FdFile src(-1, false);
StringFile dst;
ASSERT_FALSE(CopyFile(src, &dst));
}
TEST_F(RandomAccessFileUtilsTest, BadDst) {
StringFile src;
- FdFile dst(-1);
+ FdFile dst(-1, false);
// We need some source content to trigger a write.
// Copying an empty file is a no-op.
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index 9d2d59c9a4..821d613040 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -53,7 +53,7 @@ class CheckReferenceMapVisitor : public StackVisitor {
void CheckReferences(int* registers, int number_of_references, uint32_t native_pc_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (GetMethod()->IsOptimized()) {
+ if (GetMethod()->IsOptimized(sizeof(void*))) {
CheckOptimizedMethod(registers, number_of_references, native_pc_offset);
} else {
CheckQuickMethod(registers, number_of_references, native_pc_offset);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 84cbcdc466..e1b79c9f87 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -243,7 +243,8 @@ ClassLinker::ClassLinker(InternTable* intern_table)
portable_imt_conflict_trampoline_(nullptr),
quick_imt_conflict_trampoline_(nullptr),
quick_generic_jni_trampoline_(nullptr),
- quick_to_interpreter_bridge_trampoline_(nullptr) {
+ quick_to_interpreter_bridge_trampoline_(nullptr),
+ image_pointer_size_(sizeof(void*)) {
memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
}
@@ -378,10 +379,9 @@ void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class
Handle<mirror::Class> java_lang_reflect_ArtMethod(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::ArtMethod::ClassSize())));
CHECK(java_lang_reflect_ArtMethod.Get() != nullptr);
- java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize());
+ java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*)));
SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.Get());
java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved, self);
-
mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.Get());
// Set up array classes for string, field, method
@@ -407,8 +407,7 @@ void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class
// DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses
// these roots.
CHECK_NE(0U, boot_class_path.size());
- for (size_t i = 0; i != boot_class_path.size(); ++i) {
- const DexFile* dex_file = boot_class_path[i];
+ for (const DexFile* dex_file : boot_class_path) {
CHECK(dex_file != nullptr);
AppendToBootClassPath(self, *dex_file);
}
@@ -1682,6 +1681,20 @@ void ClassLinker::InitFromImage() {
// Set classes on AbstractMethod early so that IsMethod tests can be performed during the live
// bitmap walk.
mirror::ArtMethod::SetClass(GetClassRoot(kJavaLangReflectArtMethod));
+ size_t art_method_object_size = mirror::ArtMethod::GetJavaLangReflectArtMethod()->GetObjectSize();
+ if (!Runtime::Current()->IsCompiler()) {
+ // Compiler supports having an image with a different pointer size than the runtime. This
+ // happens on the host for compile 32 bit tests since we use a 64 bit libart compiler. We may
+ // also use 32 bit dex2oat on a system with 64 bit apps.
+ CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(sizeof(void*)))
+ << sizeof(void*);
+ }
+ if (art_method_object_size == mirror::ArtMethod::InstanceSize(4)) {
+ image_pointer_size_ = 4;
+ } else {
+ CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(8));
+ image_pointer_size_ = 8;
+ }
// Set entry point to interpreter if in InterpretOnly mode.
if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) {
@@ -1695,7 +1708,7 @@ void ClassLinker::InitFromImage() {
// reinit array_iftable_ from any array class instance, they should be ==
array_iftable_ = GcRoot<mirror::IfTable>(GetClassRoot(kObjectArrayClass)->GetIfTable());
- DCHECK(array_iftable_.Read() == GetClassRoot(kBooleanArrayClass)->GetIfTable());
+ DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable());
// String class root was set above
mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
mirror::ArtField::SetClass(GetClassRoot(kJavaLangReflectArtField));
@@ -2047,6 +2060,7 @@ mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlready
Thread* self, const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader) {
+ // Can we special case for a well understood PathClassLoader with the BootClassLoader as parent?
if (class_loader->GetClass() !=
soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) ||
class_loader->GetParent()->GetClass() !=
@@ -2058,17 +2072,21 @@ mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlready
if (pair.second != nullptr) {
mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr);
if (klass != nullptr) {
- return EnsureResolved(self, descriptor, klass);
+ // May return null if resolution on another thread fails.
+ klass = EnsureResolved(self, descriptor, klass);
+ } else {
+ // May OOME.
+ klass = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
+ *pair.second);
}
- klass = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
- *pair.second);
- if (klass != nullptr) {
- return klass;
+ if (klass == nullptr) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ self->ClearException();
}
- CHECK(self->IsExceptionPending()) << descriptor;
- self->ClearException();
+ return klass;
} else {
- // RegisterDexFile may allocate dex caches (and cause thread suspension).
+ // Handle as if this is the child PathClassLoader.
+ // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
StackHandleScope<3> hs(self);
// The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
// We need to get the DexPathList and loop through it.
@@ -2125,8 +2143,9 @@ mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlready
}
}
}
+ self->AssertNoPendingException();
+ return nullptr;
}
- return nullptr;
}
mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
@@ -5216,8 +5235,10 @@ struct LinkFieldsComparator {
// Primitive types differ but sizes match. Arbitrarily order by primitive type.
return type1 < type2;
}
- // same basic group? then sort by string.
- return strcmp(field1->GetName(), field2->GetName()) < 0;
+ // Same basic group? Then sort by dex field index. This is guaranteed to be sorted
+ // by name and for equal names by type id index.
+ // NOTE: This works also for proxies. Their static fields are assigned appropriate indexes.
+ return field1->GetDexFieldIndex() < field2->GetDexFieldIndex();
}
};
@@ -5310,14 +5331,18 @@ bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_
} else {
klass->SetNumReferenceInstanceFields(num_reference_fields);
if (!klass->IsVariableSize()) {
- std::string temp;
- DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp);
- size_t previous_size = klass->GetObjectSize();
- if (previous_size != 0) {
- // Make sure that we didn't originally have an incorrect size.
- CHECK_EQ(previous_size, size) << klass->GetDescriptor(&temp);
+ if (klass->DescriptorEquals("Ljava/lang/reflect/ArtMethod;")) {
+ klass->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*)));
+ } else {
+ std::string temp;
+ DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp);
+ size_t previous_size = klass->GetObjectSize();
+ if (previous_size != 0) {
+ // Make sure that we didn't originally have an incorrect size.
+ CHECK_EQ(previous_size, size) << klass->GetDescriptor(&temp);
+ }
+ klass->SetObjectSize(size);
}
- klass->SetObjectSize(size);
}
}
@@ -5342,7 +5367,9 @@ bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_
}
if (i != 0) {
mirror::ArtField* prev_field = fields->Get(i - 1u);
- CHECK_LT(strcmp(prev_field->GetName(), field->GetName()), 0);
+ // NOTE: The field names can be the same. This is not possible in the Java language
+ // but it's valid Java/dex bytecode and for example proguard can generate such bytecode.
+ CHECK_LE(strcmp(prev_field->GetName(), field->GetName()), 0);
}
Primitive::Type type = field->GetTypeAsPrimitiveType();
bool is_primitive = type != Primitive::kPrimNot;
@@ -5368,7 +5395,6 @@ bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_
}
CHECK_EQ(current_ref_offset.Uint32Value(), end_ref_offset.Uint32Value());
}
-
return true;
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 385f135192..b78d0b5bc3 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -117,8 +117,8 @@ class ClassLinker {
Handle<mirror::ClassLoader> class_loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Find a class in the path class loader, loading it if necessary. Hash function is supposed to
- // be ComputeModifiedUtf8Hash(descriptor).
+ // Find a class in the path class loader, loading it if necessary without using JNI. Hash
+ // function is supposed to be ComputeModifiedUtf8Hash(descriptor).
mirror::Class* FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self, const char* descriptor, size_t hash,
Handle<mirror::ClassLoader> class_loader)
@@ -805,6 +805,9 @@ class ClassLinker {
const void* quick_generic_jni_trampoline_;
const void* quick_to_interpreter_bridge_trampoline_;
+ // Image pointer size.
+ size_t image_pointer_size_;
+
friend class ImageWriter; // for GetClassRoots
friend class ImageDumper; // for FindOpenedOatFileFromOatLocation
friend class ElfPatcher; // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index ba5aa3d75a..0c86761979 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -499,11 +499,6 @@ struct ArtMethodOffsets : public CheckOffsets<mirror::ArtMethod> {
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_strings_), "dexCacheStrings"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_code_item_offset_), "dexCodeItemOffset"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_method_index_), "dexMethodIndex"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_interpreter_), "entryPointFromInterpreter"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_jni_), "entryPointFromJni"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_portable_compiled_code_), "entryPointFromPortableCompiledCode"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_quick_compiled_code_), "entryPointFromQuickCompiledCode"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, gc_map_), "gcMap"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, method_index_), "methodIndex"));
};
};
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 6e3ebc24d8..03b33e962a 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -59,7 +59,7 @@ ScratchFile::ScratchFile() {
filename_ += "/TmpFile-XXXXXX";
int fd = mkstemp(&filename_[0]);
CHECK_NE(-1, fd);
- file_.reset(new File(fd, GetFilename()));
+ file_.reset(new File(fd, GetFilename(), true));
}
ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix) {
@@ -67,7 +67,7 @@ ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix) {
filename_ += suffix;
int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666);
CHECK_NE(-1, fd);
- file_.reset(new File(fd, GetFilename()));
+ file_.reset(new File(fd, GetFilename(), true));
}
ScratchFile::ScratchFile(File* file) {
@@ -88,6 +88,11 @@ void ScratchFile::Unlink() {
if (!OS::FileExists(filename_.c_str())) {
return;
}
+ if (file_.get() != nullptr) {
+ if (file_->FlushCloseOrErase() != 0) {
+ PLOG(WARNING) << "Error closing scratch file.";
+ }
+ }
int unlink_result = unlink(filename_.c_str());
CHECK_EQ(0, unlink_result);
}
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index e2f6085ae2..ef5db2d5ea 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -183,16 +183,20 @@ class AllocRecord {
class Breakpoint {
public:
- Breakpoint(mirror::ArtMethod* method, uint32_t dex_pc, bool need_full_deoptimization)
+ Breakpoint(mirror::ArtMethod* method, uint32_t dex_pc,
+ DeoptimizationRequest::Kind deoptimization_kind)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : method_(nullptr), dex_pc_(dex_pc), need_full_deoptimization_(need_full_deoptimization) {
+ : method_(nullptr), dex_pc_(dex_pc), deoptimization_kind_(deoptimization_kind) {
+ CHECK(deoptimization_kind_ == DeoptimizationRequest::kNothing ||
+ deoptimization_kind_ == DeoptimizationRequest::kSelectiveDeoptimization ||
+ deoptimization_kind_ == DeoptimizationRequest::kFullDeoptimization);
ScopedObjectAccessUnchecked soa(Thread::Current());
method_ = soa.EncodeMethod(method);
}
Breakpoint(const Breakpoint& other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: method_(nullptr), dex_pc_(other.dex_pc_),
- need_full_deoptimization_(other.need_full_deoptimization_) {
+ deoptimization_kind_(other.deoptimization_kind_) {
ScopedObjectAccessUnchecked soa(Thread::Current());
method_ = soa.EncodeMethod(other.Method());
}
@@ -206,8 +210,8 @@ class Breakpoint {
return dex_pc_;
}
- bool NeedFullDeoptimization() const {
- return need_full_deoptimization_;
+ DeoptimizationRequest::Kind GetDeoptimizationKind() const {
+ return deoptimization_kind_;
}
private:
@@ -216,7 +220,7 @@ class Breakpoint {
uint32_t dex_pc_;
// Indicates whether breakpoint needs full deoptimization or selective deoptimization.
- bool need_full_deoptimization_;
+ DeoptimizationRequest::Kind deoptimization_kind_;
};
static std::ostream& operator<<(std::ostream& os, const Breakpoint& rhs)
@@ -736,6 +740,12 @@ bool Dbg::IsDisposed() {
return gDisposed;
}
+bool Dbg::RequiresDeoptimization() {
+ // We don't need deoptimization if everything runs with interpreter after
+ // enabling -Xint mode.
+ return !Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly();
+}
+
void Dbg::GoActive() {
// Enable all debugging features, including scans for breakpoints.
// This is a no-op if we're already active.
@@ -768,7 +778,9 @@ void Dbg::GoActive() {
Thread* self = Thread::Current();
ThreadState old_state = self->SetStateUnsafe(kRunnable);
CHECK_NE(old_state, kRunnable);
- runtime->GetInstrumentation()->EnableDeoptimization();
+ if (RequiresDeoptimization()) {
+ runtime->GetInstrumentation()->EnableDeoptimization();
+ }
instrumentation_events_ = 0;
gDebuggerActive = true;
CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
@@ -806,7 +818,9 @@ void Dbg::Disconnected() {
instrumentation_events_);
instrumentation_events_ = 0;
}
- runtime->GetInstrumentation()->DisableDeoptimization();
+ if (RequiresDeoptimization()) {
+ runtime->GetInstrumentation()->DisableDeoptimization();
+ }
gDebuggerActive = false;
}
gRegistry->Clear();
@@ -3035,9 +3049,11 @@ void Dbg::ProcessDeoptimizationRequest(const DeoptimizationRequest& request) {
}
void Dbg::DelayFullUndeoptimization() {
- MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
- ++delayed_full_undeoptimization_count_;
- DCHECK_LE(delayed_full_undeoptimization_count_, full_deoptimization_event_count_);
+ if (RequiresDeoptimization()) {
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
+ ++delayed_full_undeoptimization_count_;
+ DCHECK_LE(delayed_full_undeoptimization_count_, full_deoptimization_event_count_);
+ }
}
void Dbg::ProcessDelayedFullUndeoptimizations() {
@@ -3196,64 +3212,101 @@ static const Breakpoint* FindFirstBreakpointForMethod(mirror::ArtMethod* m)
}
// Sanity checks all existing breakpoints on the same method.
-static void SanityCheckExistingBreakpoints(mirror::ArtMethod* m, bool need_full_deoptimization)
+static void SanityCheckExistingBreakpoints(mirror::ArtMethod* m,
+ DeoptimizationRequest::Kind deoptimization_kind)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::breakpoint_lock_) {
for (const Breakpoint& breakpoint : gBreakpoints) {
- CHECK_EQ(need_full_deoptimization, breakpoint.NeedFullDeoptimization());
+ if (breakpoint.Method() == m) {
+ CHECK_EQ(deoptimization_kind, breakpoint.GetDeoptimizationKind());
+ }
}
- if (need_full_deoptimization) {
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (deoptimization_kind == DeoptimizationRequest::kFullDeoptimization) {
// We should have deoptimized everything but not "selectively" deoptimized this method.
- CHECK(Runtime::Current()->GetInstrumentation()->AreAllMethodsDeoptimized());
- CHECK(!Runtime::Current()->GetInstrumentation()->IsDeoptimized(m));
- } else {
+ CHECK(instrumentation->AreAllMethodsDeoptimized());
+ CHECK(!instrumentation->IsDeoptimized(m));
+ } else if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) {
// We should have "selectively" deoptimized this method.
// Note: while we have not deoptimized everything for this method, we may have done it for
// another event.
- CHECK(Runtime::Current()->GetInstrumentation()->IsDeoptimized(m));
+ CHECK(instrumentation->IsDeoptimized(m));
+ } else {
+ // This method does not require deoptimization.
+ CHECK_EQ(deoptimization_kind, DeoptimizationRequest::kNothing);
+ CHECK(!instrumentation->IsDeoptimized(m));
}
}
-// Installs a breakpoint at the specified location. Also indicates through the deoptimization
-// request if we need to deoptimize.
-void Dbg::WatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequest* req) {
- Thread* const self = Thread::Current();
- mirror::ArtMethod* m = FromMethodId(location->method_id);
- DCHECK(m != nullptr) << "No method for method id " << location->method_id;
-
+static DeoptimizationRequest::Kind GetRequiredDeoptimizationKind(Thread* self,
+ mirror::ArtMethod* m)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (!Dbg::RequiresDeoptimization()) {
+ // We already run in interpreter-only mode so we don't need to deoptimize anything.
+ VLOG(jdwp) << "No need for deoptimization when fully running with interpreter for method "
+ << PrettyMethod(m);
+ return DeoptimizationRequest::kNothing;
+ }
const Breakpoint* existing_breakpoint;
{
ReaderMutexLock mu(self, *Locks::breakpoint_lock_);
existing_breakpoint = FindFirstBreakpointForMethod(m);
}
- bool need_full_deoptimization;
if (existing_breakpoint == nullptr) {
// There is no breakpoint on this method yet: we need to deoptimize. If this method may be
// inlined, we deoptimize everything; otherwise we deoptimize only this method.
// Note: IsMethodPossiblyInlined goes into the method verifier and may cause thread suspension.
// Therefore we must not hold any lock when we call it.
- need_full_deoptimization = IsMethodPossiblyInlined(self, m);
+ bool need_full_deoptimization = IsMethodPossiblyInlined(self, m);
if (need_full_deoptimization) {
- req->SetKind(DeoptimizationRequest::kFullDeoptimization);
- req->SetMethod(nullptr);
+ VLOG(jdwp) << "Need full deoptimization because of possible inlining of method "
+ << PrettyMethod(m);
+ return DeoptimizationRequest::kFullDeoptimization;
} else {
- req->SetKind(DeoptimizationRequest::kSelectiveDeoptimization);
- req->SetMethod(m);
+ // We don't need to deoptimize if the method has not been compiled.
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ const bool is_compiled = class_linker->GetOatMethodQuickCodeFor(m) != nullptr;
+ if (is_compiled) {
+ VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m);
+ return DeoptimizationRequest::kSelectiveDeoptimization;
+ } else {
+ // Method is not compiled: we don't need to deoptimize.
+ VLOG(jdwp) << "No need for deoptimization for non-compiled method " << PrettyMethod(m);
+ return DeoptimizationRequest::kNothing;
+ }
}
} else {
// There is at least one breakpoint for this method: we don't need to deoptimize.
- req->SetKind(DeoptimizationRequest::kNothing);
- req->SetMethod(nullptr);
-
- need_full_deoptimization = existing_breakpoint->NeedFullDeoptimization();
+ // Let's check that all breakpoints are configured the same way for deoptimization.
+ VLOG(jdwp) << "Breakpoint already set: no deoptimization is required";
+ DeoptimizationRequest::Kind deoptimization_kind = existing_breakpoint->GetDeoptimizationKind();
if (kIsDebugBuild) {
ReaderMutexLock mu(self, *Locks::breakpoint_lock_);
- SanityCheckExistingBreakpoints(m, need_full_deoptimization);
+ SanityCheckExistingBreakpoints(m, deoptimization_kind);
}
+ return DeoptimizationRequest::kNothing;
+ }
+}
+
+// Installs a breakpoint at the specified location. Also indicates through the deoptimization
+// request if we need to deoptimize.
+void Dbg::WatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequest* req) {
+ Thread* const self = Thread::Current();
+ mirror::ArtMethod* m = FromMethodId(location->method_id);
+ DCHECK(m != nullptr) << "No method for method id " << location->method_id;
+
+ const DeoptimizationRequest::Kind deoptimization_kind = GetRequiredDeoptimizationKind(self, m);
+ req->SetKind(deoptimization_kind);
+ if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) {
+ req->SetMethod(m);
+ } else {
+ CHECK(deoptimization_kind == DeoptimizationRequest::kNothing ||
+ deoptimization_kind == DeoptimizationRequest::kFullDeoptimization);
+ req->SetMethod(nullptr);
}
{
WriterMutexLock mu(self, *Locks::breakpoint_lock_);
- gBreakpoints.push_back(Breakpoint(m, location->dex_pc, need_full_deoptimization));
+ gBreakpoints.push_back(Breakpoint(m, location->dex_pc, deoptimization_kind));
VLOG(jdwp) << "Set breakpoint #" << (gBreakpoints.size() - 1) << ": "
<< gBreakpoints[gBreakpoints.size() - 1];
}
@@ -3265,12 +3318,13 @@ void Dbg::UnwatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequ
WriterMutexLock mu(Thread::Current(), *Locks::breakpoint_lock_);
mirror::ArtMethod* m = FromMethodId(location->method_id);
DCHECK(m != nullptr) << "No method for method id " << location->method_id;
- bool need_full_deoptimization = false;
+ DeoptimizationRequest::Kind deoptimization_kind = DeoptimizationRequest::kNothing;
for (size_t i = 0, e = gBreakpoints.size(); i < e; ++i) {
if (gBreakpoints[i].DexPc() == location->dex_pc && gBreakpoints[i].Method() == m) {
VLOG(jdwp) << "Removed breakpoint #" << i << ": " << gBreakpoints[i];
- need_full_deoptimization = gBreakpoints[i].NeedFullDeoptimization();
- DCHECK_NE(need_full_deoptimization, Runtime::Current()->GetInstrumentation()->IsDeoptimized(m));
+ deoptimization_kind = gBreakpoints[i].GetDeoptimizationKind();
+ DCHECK_EQ(deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization,
+ Runtime::Current()->GetInstrumentation()->IsDeoptimized(m));
gBreakpoints.erase(gBreakpoints.begin() + i);
break;
}
@@ -3278,21 +3332,26 @@ void Dbg::UnwatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequ
const Breakpoint* const existing_breakpoint = FindFirstBreakpointForMethod(m);
if (existing_breakpoint == nullptr) {
// There is no more breakpoint on this method: we need to undeoptimize.
- if (need_full_deoptimization) {
+ if (deoptimization_kind == DeoptimizationRequest::kFullDeoptimization) {
// This method required full deoptimization: we need to undeoptimize everything.
req->SetKind(DeoptimizationRequest::kFullUndeoptimization);
req->SetMethod(nullptr);
- } else {
+ } else if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) {
// This method required selective deoptimization: we need to undeoptimize only that method.
req->SetKind(DeoptimizationRequest::kSelectiveUndeoptimization);
req->SetMethod(m);
+ } else {
+ // This method had no need for deoptimization: do nothing.
+ CHECK_EQ(deoptimization_kind, DeoptimizationRequest::kNothing);
+ req->SetKind(DeoptimizationRequest::kNothing);
+ req->SetMethod(nullptr);
}
} else {
// There is at least one breakpoint for this method: we don't need to undeoptimize.
req->SetKind(DeoptimizationRequest::kNothing);
req->SetMethod(nullptr);
if (kIsDebugBuild) {
- SanityCheckExistingBreakpoints(m, need_full_deoptimization);
+ SanityCheckExistingBreakpoints(m, deoptimization_kind);
}
}
}
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 488ba7f728..92031634cd 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -523,6 +523,9 @@ class Dbg {
LOCKS_EXCLUDED(Locks::breakpoint_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Indicates whether we need deoptimization for debugging.
+ static bool RequiresDeoptimization();
+
// Records deoptimization request in the queue.
static void RequestDeoptimization(const DeoptimizationRequest& req)
LOCKS_EXCLUDED(Locks::deoptimization_lock_)
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 134e284999..b304779568 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -146,6 +146,9 @@ static const DexFile* OpenDexFileBase64(const char* base64,
if (!file->WriteFully(dex_bytes.get(), length)) {
PLOG(FATAL) << "Failed to write base64 as dex file";
}
+ if (file->FlushCloseOrErase() != 0) {
+ PLOG(FATAL) << "Could not flush and close test file.";
+ }
file.reset();
// read dex file
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index addd94833e..ec1e5f02fe 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -115,6 +115,9 @@ static const DexFile* OpenDexFileBase64(const char* base64, const char* location
if (!file->WriteFully(dex_bytes.get(), length)) {
PLOG(FATAL) << "Failed to write base64 as dex file";
}
+ if (file->FlushCloseOrErase() != 0) {
+ PLOG(FATAL) << "Could not flush and close test file.";
+ }
file.reset();
// read dex file
@@ -177,6 +180,9 @@ static const DexFile* FixChecksumAndOpen(uint8_t* bytes, size_t length, const ch
if (!file->WriteFully(bytes, length)) {
PLOG(FATAL) << "Failed to write base64 as dex file";
}
+ if (file->FlushCloseOrErase() != 0) {
+ PLOG(FATAL) << "Could not flush and close test file.";
+ }
file.reset();
// read dex file
diff --git a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
index 00f5cd5eb1..54dbd8c770 100644
--- a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
@@ -35,9 +35,9 @@ extern "C" const void* artInstrumentationMethodEntryFromCode(mirror::ArtMethod*
if (instrumentation->IsDeoptimized(method)) {
result = GetQuickToInterpreterBridge();
} else {
- result = instrumentation->GetQuickCodeFor(method);
+ result = instrumentation->GetQuickCodeFor(method, sizeof(void*));
+ DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(result));
}
- DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(result));
bool interpreter_entry = (result == GetQuickToInterpreterBridge());
instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? nullptr : this_object,
method, lr, interpreter_entry);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index c4bc969038..0b7d382deb 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1659,7 +1659,7 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self,
*(sp32 - 1) = cookie;
// Retrieve the stored native code.
- const void* nativeCode = called->GetNativeMethod();
+ void* nativeCode = called->GetEntryPointFromJni();
// There are two cases for the content of nativeCode:
// 1) Pointer to the native function.
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index ab3ec62d69..835485c351 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -360,7 +360,8 @@ bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, void* context, bool che
// at the return PC address.
if (true || kIsDebugBuild) {
VLOG(signals) << "looking for dex pc for return pc " << std::hex << return_pc;
- const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(method_obj);
+ const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(method_obj,
+ sizeof(void*));
uint32_t sought_offset = return_pc - reinterpret_cast<uintptr_t>(code);
VLOG(signals) << "pc offset: " << std::hex << sought_offset;
}
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 991b956f80..7c2474f3f9 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -1772,7 +1772,7 @@ void RosAlloc::Initialize() {
if (i < 4) {
numOfPages[i] = 1;
} else if (i < 8) {
- numOfPages[i] = 2;
+ numOfPages[i] = 1;
} else if (i < 16) {
numOfPages[i] = 4;
} else if (i < 32) {
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index 431686a977..3269e102bc 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -414,8 +414,7 @@ class RosAlloc {
// We use thread-local runs for the size Brackets whose indexes
// are less than this index. We use shared (current) runs for the rest.
-
- static const size_t kNumThreadLocalSizeBrackets = 11;
+ static const size_t kNumThreadLocalSizeBrackets = 8;
private:
// The base address of the memory region that's managed by this allocator.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 0e67978d05..0cceaa4467 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -96,6 +96,8 @@ static const size_t kDefaultMarkStackSize = 64 * KB;
static const char* kDlMallocSpaceName[2] = {"main dlmalloc space", "main dlmalloc space 1"};
static const char* kRosAllocSpaceName[2] = {"main rosalloc space", "main rosalloc space 1"};
static const char* kMemMapSpaceName[2] = {"main space", "main space 1"};
+static const char* kNonMovingSpaceName = "non moving space";
+static const char* kZygoteSpaceName = "zygote space";
static constexpr size_t kGSSBumpPointerSpaceCapacity = 32 * MB;
Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free,
@@ -232,9 +234,13 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
+-main alloc space2 / bump space 2 (capacity_)+-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
*/
+ // We don't have hspace compaction enabled with GSS.
+ if (foreground_collector_type_ == kCollectorTypeGSS) {
+ use_homogeneous_space_compaction_for_oom_ = false;
+ }
bool support_homogeneous_space_compaction =
background_collector_type_ == gc::kCollectorTypeHomogeneousSpaceCompact ||
- use_homogeneous_space_compaction_for_oom;
+ use_homogeneous_space_compaction_for_oom_;
// We may use the same space the main space for the non moving space if we don't need to compact
// from the main space.
// This is not the case if we support homogeneous compaction or have a moving background
@@ -254,10 +260,14 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
std::string error_str;
std::unique_ptr<MemMap> non_moving_space_mem_map;
if (separate_non_moving_space) {
+ // If we are the zygote, the non moving space becomes the zygote space when we run
+ // PreZygoteFork the first time. In this case, call the map "zygote space" since we can't
+ // rename the mem map later.
+ const char* space_name = is_zygote ? kZygoteSpaceName: kNonMovingSpaceName;
// Reserve the non moving mem map before the other two since it needs to be at a specific
// address.
non_moving_space_mem_map.reset(
- MemMap::MapAnonymous("non moving space", requested_alloc_space_begin,
+ MemMap::MapAnonymous(space_name, requested_alloc_space_begin,
non_moving_space_capacity, PROT_READ | PROT_WRITE, true, &error_str));
CHECK(non_moving_space_mem_map != nullptr) << error_str;
// Try to reserve virtual memory at a lower address if we have a separate non moving space.
@@ -1972,7 +1982,8 @@ void Heap::PreZygoteFork() {
// from this point on.
RemoveRememberedSet(old_alloc_space);
}
- zygote_space_ = old_alloc_space->CreateZygoteSpace("alloc space", low_memory_mode_,
+ // Remaining space becomes the new non moving space.
+ zygote_space_ = old_alloc_space->CreateZygoteSpace(kNonMovingSpaceName, low_memory_mode_,
&non_moving_space_);
CHECK(!non_moving_space_->CanMoveObjects());
if (same_space) {
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index b23212842a..071997f6b5 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -74,17 +74,17 @@ static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta)
// out-of-date. We also don't really care if this fails since it is just a convenience.
// Adapted from prune_dex_cache(const char* subdir) in frameworks/native/cmds/installd/commands.c
// Note this should only be used during first boot.
-static void RealPruneDexCache(const std::string& cache_dir_path);
+static void RealPruneDalvikCache(const std::string& cache_dir_path);
-static void PruneDexCache(InstructionSet isa) {
+static void PruneDalvikCache(InstructionSet isa) {
CHECK_NE(isa, kNone);
// Prune the base /data/dalvik-cache.
- RealPruneDexCache(GetDalvikCacheOrDie(".", false));
+ RealPruneDalvikCache(GetDalvikCacheOrDie(".", false));
// Prune /data/dalvik-cache/<isa>.
- RealPruneDexCache(GetDalvikCacheOrDie(GetInstructionSetString(isa), false));
+ RealPruneDalvikCache(GetDalvikCacheOrDie(GetInstructionSetString(isa), false));
}
-static void RealPruneDexCache(const std::string& cache_dir_path) {
+static void RealPruneDalvikCache(const std::string& cache_dir_path) {
if (!OS::DirectoryExists(cache_dir_path.c_str())) {
return;
}
@@ -118,6 +118,28 @@ static void RealPruneDexCache(const std::string& cache_dir_path) {
CHECK_EQ(0, TEMP_FAILURE_RETRY(closedir(cache_dir))) << "Unable to close directory.";
}
+// We write out an empty file to the zygote's ISA specific cache dir at the start of
+// every zygote boot and delete it when the boot completes. If we find a file already
+// present, it usually means the boot didn't complete. We wipe the entire dalvik
+// cache if that's the case.
+static void MarkZygoteStart(const InstructionSet isa) {
+ const std::string isa_subdir = GetDalvikCacheOrDie(GetInstructionSetString(isa), false);
+ const std::string boot_marker = isa_subdir + "/.booting";
+
+ if (OS::FileExists(boot_marker.c_str())) {
+ LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache";
+ RealPruneDalvikCache(isa_subdir);
+ }
+
+ VLOG(startup) << "Creating boot start marker: " << boot_marker;
+ std::unique_ptr<File> f(OS::CreateEmptyFile(boot_marker.c_str()));
+ if (f.get() != nullptr) {
+ if (f->FlushCloseOrErase() != 0) {
+ PLOG(WARNING) << "Failed to write boot marker.";
+ }
+ }
+}
+
static bool GenerateImage(const std::string& image_filename, InstructionSet image_isa,
std::string* error_msg) {
const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString());
@@ -130,7 +152,7 @@ static bool GenerateImage(const std::string& image_filename, InstructionSet imag
// We should clean up so we are more likely to have room for the image.
if (Runtime::Current()->IsZygote()) {
LOG(INFO) << "Pruning dalvik-cache since we are generating an image and will need to recompile";
- PruneDexCache(image_isa);
+ PruneDalvikCache(image_isa);
}
std::vector<std::string> arg_vector;
@@ -232,7 +254,7 @@ static bool RelocateImage(const char* image_location, const char* dest_filename,
// We should clean up so we are more likely to have room for the image.
if (Runtime::Current()->IsZygote()) {
LOG(INFO) << "Pruning dalvik-cache since we are relocating an image and will need to recompile";
- PruneDexCache(isa);
+ PruneDalvikCache(isa);
}
std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
@@ -427,6 +449,10 @@ ImageSpace* ImageSpace::Create(const char* image_location,
&has_system, &cache_filename, &dalvik_cache_exists,
&has_cache, &is_global_cache);
+ if (Runtime::Current()->IsZygote()) {
+ MarkZygoteStart(image_isa);
+ }
+
ImageSpace* space;
bool relocate = Runtime::Current()->ShouldRelocate();
bool can_compile = Runtime::Current()->IsImageDex2OatEnabled();
@@ -475,7 +501,7 @@ ImageSpace* ImageSpace::Create(const char* image_location,
// Since ImageCreationAllowed was true above, we are the zygote
// and therefore the only process expected to generate these for
// the device.
- PruneDexCache(image_isa);
+ PruneDalvikCache(image_isa);
return nullptr;
}
}
@@ -530,7 +556,7 @@ ImageSpace* ImageSpace::Create(const char* image_location,
"but image failed to load: %s",
image_location, cache_filename.c_str(), system_filename.c_str(),
error_msg->c_str());
- PruneDexCache(image_isa);
+ PruneDalvikCache(image_isa);
return nullptr;
} else if (is_system) {
// If the /system file exists, it should be up-to-date, don't try to generate it.
@@ -558,13 +584,13 @@ ImageSpace* ImageSpace::Create(const char* image_location,
// Since ImageCreationAllowed was true above, we are the zygote
// and therefore the only process expected to generate these for
// the device.
- PruneDexCache(image_isa);
+ PruneDalvikCache(image_isa);
return nullptr;
} else {
// Check whether there is enough space left over after we have generated the image.
if (!CheckSpace(cache_filename, error_msg)) {
// No. Delete the generated image and try to run out of the dex files.
- PruneDexCache(image_isa);
+ PruneDalvikCache(image_isa);
return nullptr;
}
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 14d7432508..3069581522 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -475,9 +475,14 @@ class Hprof {
}
}
- std::unique_ptr<File> file(new File(out_fd, filename_));
+ std::unique_ptr<File> file(new File(out_fd, filename_, true));
okay = file->WriteFully(header_data_ptr_, header_data_size_) &&
- file->WriteFully(body_data_ptr_, body_data_size_);
+ file->WriteFully(body_data_ptr_, body_data_size_);
+ if (okay) {
+ okay = file->FlushCloseOrErase() == 0;
+ } else {
+ file->Erase();
+ }
if (!okay) {
std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
filename_.c_str(), strerror(errno)));
diff --git a/runtime/image.cc b/runtime/image.cc
index aee84bc3c0..b83eeb16d0 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '1', '2', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '1', '3', '\0' };
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 003e1601ce..639b0f0766 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -869,10 +869,10 @@ void Instrumentation::DisableMethodTracing() {
ConfigureStubs(false, false);
}
-const void* Instrumentation::GetQuickCodeFor(mirror::ArtMethod* method) const {
+const void* Instrumentation::GetQuickCodeFor(mirror::ArtMethod* method, size_t pointer_size) const {
Runtime* runtime = Runtime::Current();
if (LIKELY(!instrumentation_stubs_installed_)) {
- const void* code = method->GetEntryPointFromQuickCompiledCode();
+ const void* code = method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
DCHECK(code != nullptr);
ClassLinker* class_linker = runtime->GetClassLinker();
if (LIKELY(!class_linker->IsQuickResolutionStub(code) &&
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 369039d39f..effa9f7b2e 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -200,7 +200,7 @@ class Instrumentation {
// Get the quick code for the given method. More efficient than asking the class linker as it
// will short-cut to GetCode if instrumentation and static method resolution stubs aren't
// installed.
- const void* GetQuickCodeFor(mirror::ArtMethod* method) const
+ const void* GetQuickCodeFor(mirror::ArtMethod* method, size_t pointer_size) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void ForceInterpretOnly() {
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 56a6d2cc50..7ecb58e7e9 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -148,6 +148,7 @@ void InternTable::RemoveWeakFromTransaction(mirror::String* s) {
}
void InternTable::AddImageStringsToTable(gc::space::ImageSpace* image_space) {
+ CHECK(image_space != nullptr);
MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
if (!image_added_to_intern_table_) {
mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 18de133147..b17f3039c6 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -138,7 +138,7 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s
if (method->IsStatic()) {
if (shorty == "L") {
typedef jobject (fntype)(JNIEnv*, jclass);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
jobject jresult;
@@ -149,35 +149,35 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s
result->SetL(soa.Decode<Object*>(jresult));
} else if (shorty == "V") {
typedef void (fntype)(JNIEnv*, jclass);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedThreadStateChange tsc(self, kNative);
fn(soa.Env(), klass.get());
} else if (shorty == "Z") {
typedef jboolean (fntype)(JNIEnv*, jclass);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedThreadStateChange tsc(self, kNative);
result->SetZ(fn(soa.Env(), klass.get()));
} else if (shorty == "BI") {
typedef jbyte (fntype)(JNIEnv*, jclass, jint);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedThreadStateChange tsc(self, kNative);
result->SetB(fn(soa.Env(), klass.get(), args[0]));
} else if (shorty == "II") {
typedef jint (fntype)(JNIEnv*, jclass, jint);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedThreadStateChange tsc(self, kNative);
result->SetI(fn(soa.Env(), klass.get(), args[0]));
} else if (shorty == "LL") {
typedef jobject (fntype)(JNIEnv*, jclass, jobject);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedLocalRef<jobject> arg0(soa.Env(),
@@ -190,14 +190,15 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s
result->SetL(soa.Decode<Object*>(jresult));
} else if (shorty == "IIZ") {
typedef jint (fntype)(JNIEnv*, jclass, jint, jboolean);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedThreadStateChange tsc(self, kNative);
result->SetI(fn(soa.Env(), klass.get(), args[0], args[1]));
} else if (shorty == "ILI") {
typedef jint (fntype)(JNIEnv*, jclass, jobject, jint);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(
+ method->GetEntryPointFromJni()));
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedLocalRef<jobject> arg0(soa.Env(),
@@ -206,21 +207,21 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s
result->SetI(fn(soa.Env(), klass.get(), arg0.get(), args[1]));
} else if (shorty == "SIZ") {
typedef jshort (fntype)(JNIEnv*, jclass, jint, jboolean);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetEntryPointFromJni()));
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedThreadStateChange tsc(self, kNative);
result->SetS(fn(soa.Env(), klass.get(), args[0], args[1]));
} else if (shorty == "VIZ") {
typedef void (fntype)(JNIEnv*, jclass, jint, jboolean);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedThreadStateChange tsc(self, kNative);
fn(soa.Env(), klass.get(), args[0], args[1]);
} else if (shorty == "ZLL") {
typedef jboolean (fntype)(JNIEnv*, jclass, jobject, jobject);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedLocalRef<jobject> arg0(soa.Env(),
@@ -231,7 +232,7 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s
result->SetZ(fn(soa.Env(), klass.get(), arg0.get(), arg1.get()));
} else if (shorty == "ZILL") {
typedef jboolean (fntype)(JNIEnv*, jclass, jint, jobject, jobject);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedLocalRef<jobject> arg1(soa.Env(),
@@ -242,7 +243,7 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s
result->SetZ(fn(soa.Env(), klass.get(), args[0], arg1.get(), arg2.get()));
} else if (shorty == "VILII") {
typedef void (fntype)(JNIEnv*, jclass, jint, jobject, jint, jint);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedLocalRef<jobject> arg1(soa.Env(),
@@ -251,7 +252,7 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s
fn(soa.Env(), klass.get(), args[0], arg1.get(), args[2], args[3]);
} else if (shorty == "VLILII") {
typedef void (fntype)(JNIEnv*, jclass, jobject, jint, jobject, jint, jint);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedLocalRef<jobject> arg0(soa.Env(),
@@ -267,7 +268,7 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s
} else {
if (shorty == "L") {
typedef jobject (fntype)(JNIEnv*, jobject);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jobject> rcvr(soa.Env(),
soa.AddLocalReference<jobject>(receiver));
jobject jresult;
@@ -278,14 +279,14 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s
result->SetL(soa.Decode<Object*>(jresult));
} else if (shorty == "V") {
typedef void (fntype)(JNIEnv*, jobject);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jobject> rcvr(soa.Env(),
soa.AddLocalReference<jobject>(receiver));
ScopedThreadStateChange tsc(self, kNative);
fn(soa.Env(), rcvr.get());
} else if (shorty == "LL") {
typedef jobject (fntype)(JNIEnv*, jobject, jobject);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jobject> rcvr(soa.Env(),
soa.AddLocalReference<jobject>(receiver));
ScopedLocalRef<jobject> arg0(soa.Env(),
@@ -299,7 +300,7 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s
ScopedThreadStateChange tsc(self, kNative);
} else if (shorty == "III") {
typedef jint (fntype)(JNIEnv*, jobject, jint, jint);
- fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod()));
+ fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
ScopedLocalRef<jobject> rcvr(soa.Env(),
soa.AddLocalReference<jobject>(receiver));
ScopedThreadStateChange tsc(self, kNative);
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index 44f713ca96..1e0a2d2c22 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -125,6 +125,10 @@ struct ModBasket {
};
static bool NeedsFullDeoptimization(JdwpEventKind eventKind) {
+ if (!Dbg::RequiresDeoptimization()) {
+ // We don't need deoptimization for debugging.
+ return false;
+ }
switch (eventKind) {
case EK_METHOD_ENTRY:
case EK_METHOD_EXIT:
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 3b0656e4c6..1dcfcabf9d 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -360,7 +360,7 @@ class JNI {
ScopedObjectAccess soa(env);
mirror::ArtMethod* m = soa.DecodeMethod(mid);
CHECK(!kMovingMethods);
- jobject art_method = soa.AddLocalReference<jobject>(m);
+ ScopedLocalRef<jobject> art_method(env, soa.AddLocalReference<jobject>(m));
jobject reflect_method;
if (m->IsConstructor()) {
reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Constructor);
@@ -371,7 +371,7 @@ class JNI {
return nullptr;
}
SetObjectField(env, reflect_method,
- WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod, art_method);
+ WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod, art_method.get());
return reflect_method;
}
@@ -379,13 +379,13 @@ class JNI {
CHECK_NON_NULL_ARGUMENT(fid);
ScopedObjectAccess soa(env);
mirror::ArtField* f = soa.DecodeField(fid);
- jobject art_field = soa.AddLocalReference<jobject>(f);
+ ScopedLocalRef<jobject> art_field(env, soa.AddLocalReference<jobject>(f));
jobject reflect_field = env->AllocObject(WellKnownClasses::java_lang_reflect_Field);
if (env->ExceptionCheck()) {
return nullptr;
}
SetObjectField(env, reflect_field,
- WellKnownClasses::java_lang_reflect_Field_artField, art_field);
+ WellKnownClasses::java_lang_reflect_Field_artField, art_field.get());
return reflect_field;
}
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index ccad137164..62b6b3407f 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -390,59 +390,72 @@ class JniInternalTest : public CommonCompilerTest {
void ReleasePrimitiveArrayElementsOfWrongType(bool check_jni) {
bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
CheckJniAbortCatcher jni_abort_catcher;
-
- jbooleanArray array = env_->NewBooleanArray(10);
- ASSERT_TRUE(array != nullptr);
- jboolean is_copy;
- jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy);
- ASSERT_TRUE(elements != nullptr);
- env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array),
- reinterpret_cast<jbyte*>(elements), 0);
- jni_abort_catcher.Check(
- check_jni ? "incompatible array type boolean[] expected byte[]"
- : "attempt to release byte primitive array elements with an object of type boolean[]");
- env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array),
- reinterpret_cast<jshort*>(elements), 0);
- jni_abort_catcher.Check(
- check_jni ? "incompatible array type boolean[] expected short[]"
- : "attempt to release short primitive array elements with an object of type boolean[]");
- env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array),
- reinterpret_cast<jchar*>(elements), 0);
- jni_abort_catcher.Check(
- check_jni ? "incompatible array type boolean[] expected char[]"
- : "attempt to release char primitive array elements with an object of type boolean[]");
- env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array),
- reinterpret_cast<jint*>(elements), 0);
- jni_abort_catcher.Check(
- check_jni ? "incompatible array type boolean[] expected int[]"
- : "attempt to release int primitive array elements with an object of type boolean[]");
- env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array),
- reinterpret_cast<jlong*>(elements), 0);
- jni_abort_catcher.Check(
- check_jni ? "incompatible array type boolean[] expected long[]"
- : "attempt to release long primitive array elements with an object of type boolean[]");
- env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array),
- reinterpret_cast<jfloat*>(elements), 0);
- jni_abort_catcher.Check(
- check_jni ? "incompatible array type boolean[] expected float[]"
- : "attempt to release float primitive array elements with an object of type boolean[]");
- env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array),
- reinterpret_cast<jdouble*>(elements), 0);
- jni_abort_catcher.Check(
- check_jni ? "incompatible array type boolean[] expected double[]"
- : "attempt to release double primitive array elements with an object of type boolean[]");
- jbyteArray array2 = env_->NewByteArray(10);
- env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0);
- jni_abort_catcher.Check(
- check_jni ? "incompatible array type byte[] expected boolean[]"
- : "attempt to release boolean primitive array elements with an object of type byte[]");
- jobject object = env_->NewStringUTF("Test String");
- env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0);
- jni_abort_catcher.Check(
- check_jni ? "jarray argument has non-array type: java.lang.String"
- : "attempt to release boolean primitive array elements with an object of type "
+ {
+ jbooleanArray array = env_->NewBooleanArray(10);
+ ASSERT_TRUE(array != nullptr);
+ jboolean is_copy;
+ jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy);
+ ASSERT_TRUE(elements != nullptr);
+ env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array),
+ reinterpret_cast<jbyte*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected byte[]"
+ : "attempt to release byte primitive array elements with an object of type boolean[]");
+ env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array),
+ reinterpret_cast<jshort*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected short[]"
+ : "attempt to release short primitive array elements with an object of type boolean[]");
+ env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array),
+ reinterpret_cast<jchar*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected char[]"
+ : "attempt to release char primitive array elements with an object of type boolean[]");
+ env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array),
+ reinterpret_cast<jint*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected int[]"
+ : "attempt to release int primitive array elements with an object of type boolean[]");
+ env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array),
+ reinterpret_cast<jlong*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected long[]"
+ : "attempt to release long primitive array elements with an object of type boolean[]");
+ env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array),
+ reinterpret_cast<jfloat*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected float[]"
+ : "attempt to release float primitive array elements with an object of type boolean[]");
+ env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array),
+ reinterpret_cast<jdouble*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected double[]"
+ : "attempt to release double primitive array elements with an object of type boolean[]");
+
+ // Don't leak the elements array.
+ env_->ReleaseBooleanArrayElements(array, elements, 0);
+ }
+ {
+ jbyteArray array = env_->NewByteArray(10);
+ jboolean is_copy;
+ jbyte* elements = env_->GetByteArrayElements(array, &is_copy);
+
+ env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array),
+ reinterpret_cast<jboolean*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type byte[] expected boolean[]"
+ : "attempt to release boolean primitive array elements with an object of type byte[]");
+ jobject object = env_->NewStringUTF("Test String");
+ env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object),
+ reinterpret_cast<jboolean*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "attempt to release boolean primitive array elements with an object of type "
"java.lang.String");
+ // Don't leak the elements array.
+ env_->ReleaseByteArrayElements(array, elements, 0);
+ }
EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
}
@@ -799,6 +812,11 @@ TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) {
ASSERT_NE(fid, nullptr);
// Turn the fid into a java.lang.reflect.Field...
jobject field = env_->ToReflectedField(c, fid, JNI_FALSE);
+ for (size_t i = 0; i <= kLocalsMax; ++i) {
+ // Regression test for b/18396311, ToReflectedField leaking local refs causing a local
+ // reference table overflows with 512 references to ArtField
+ env_->DeleteLocalRef(env_->ToReflectedField(c, fid, JNI_FALSE));
+ }
ASSERT_NE(c, nullptr);
ASSERT_TRUE(env_->IsInstanceOf(field, jlrField));
// ...and back again.
@@ -825,6 +843,11 @@ TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) {
ASSERT_NE(mid, nullptr);
// Turn the mid into a java.lang.reflect.Constructor...
jobject method = env_->ToReflectedMethod(c, mid, JNI_FALSE);
+ for (size_t i = 0; i <= kLocalsMax; ++i) {
+ // Regression test for b/18396311, ToReflectedMethod leaking local refs causing a local
+ // reference table overflows with 512 references to ArtMethod
+ env_->DeleteLocalRef(env_->ToReflectedMethod(c, mid, JNI_FALSE));
+ }
ASSERT_NE(method, nullptr);
ASSERT_TRUE(env_->IsInstanceOf(method, jlrConstructor));
// ...and back again.
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index 494fa2fca2..b93651156e 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -199,17 +199,17 @@ inline void ArtMethod::SetPortableOatCodeOffset(uint32_t code_offset) {
SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(code_offset));
}
-inline const uint8_t* ArtMethod::GetMappingTable() {
- const void* code_pointer = GetQuickOatCodePointer();
+inline const uint8_t* ArtMethod::GetMappingTable(size_t pointer_size) {
+ const void* code_pointer = GetQuickOatCodePointer(pointer_size);
if (code_pointer == nullptr) {
return nullptr;
}
- return GetMappingTable(code_pointer);
+ return GetMappingTable(code_pointer, pointer_size);
}
-inline const uint8_t* ArtMethod::GetMappingTable(const void* code_pointer) {
+inline const uint8_t* ArtMethod::GetMappingTable(const void* code_pointer, size_t pointer_size) {
DCHECK(code_pointer != nullptr);
- DCHECK(code_pointer == GetQuickOatCodePointer());
+ DCHECK_EQ(code_pointer, GetQuickOatCodePointer(pointer_size));
uint32_t offset =
reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].mapping_table_offset_;
if (UNLIKELY(offset == 0u)) {
@@ -218,18 +218,18 @@ inline const uint8_t* ArtMethod::GetMappingTable(const void* code_pointer) {
return reinterpret_cast<const uint8_t*>(code_pointer) - offset;
}
-inline const uint8_t* ArtMethod::GetVmapTable() {
- const void* code_pointer = GetQuickOatCodePointer();
+inline const uint8_t* ArtMethod::GetVmapTable(size_t pointer_size) {
+ const void* code_pointer = GetQuickOatCodePointer(pointer_size);
if (code_pointer == nullptr) {
return nullptr;
}
- return GetVmapTable(code_pointer);
+ return GetVmapTable(code_pointer, pointer_size);
}
-inline const uint8_t* ArtMethod::GetVmapTable(const void* code_pointer) {
- CHECK(!IsOptimized()) << "Unimplemented vmap table for optimized compiler";
+inline const uint8_t* ArtMethod::GetVmapTable(const void* code_pointer, size_t pointer_size) {
+ CHECK(!IsOptimized(pointer_size)) << "Unimplemented vmap table for optimized compiler";
DCHECK(code_pointer != nullptr);
- DCHECK(code_pointer == GetQuickOatCodePointer());
+ DCHECK_EQ(code_pointer, GetQuickOatCodePointer(pointer_size));
uint32_t offset =
reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].vmap_table_offset_;
if (UNLIKELY(offset == 0u)) {
@@ -243,8 +243,8 @@ inline StackMap ArtMethod::GetStackMap(uint32_t native_pc_offset) {
}
inline CodeInfo ArtMethod::GetOptimizedCodeInfo() {
- DCHECK(IsOptimized());
- const void* code_pointer = GetQuickOatCodePointer();
+ DCHECK(IsOptimized(sizeof(void*)));
+ const void* code_pointer = GetQuickOatCodePointer(sizeof(void*));
DCHECK(code_pointer != nullptr);
uint32_t offset =
reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].vmap_table_offset_;
@@ -303,19 +303,14 @@ inline bool ArtMethod::IsImtUnimplementedMethod() {
}
inline uintptr_t ArtMethod::NativeQuickPcOffset(const uintptr_t pc) {
- const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this);
+ const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(
+ this, sizeof(void*));
return pc - reinterpret_cast<uintptr_t>(code);
}
-template<VerifyObjectFlags kVerifyFlags>
-inline void ArtMethod::SetNativeMethod(const void* native_method) {
- SetFieldPtr<false, true, kVerifyFlags>(
- OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_), native_method);
-}
-
inline QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo(const void* code_pointer) {
DCHECK(code_pointer != nullptr);
- DCHECK_EQ(code_pointer, GetQuickOatCodePointer());
+ DCHECK_EQ(code_pointer, GetQuickOatCodePointer(sizeof(void*)));
return reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].frame_info_;
}
@@ -485,6 +480,12 @@ inline mirror::Class* ArtMethod::GetReturnType(bool resolve) {
return type;
}
+inline void ArtMethod::CheckObjectSizeEqualsMirrorSize() {
+ // Using the default, check the class object size to make sure it matches the size of the
+ // object.
+ DCHECK_EQ(GetClass()->GetObjectSize(), sizeof(*this));
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index a742aaad9a..6d4af83058 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -165,9 +165,9 @@ uint32_t ArtMethod::ToDexPc(const uintptr_t pc, bool abort_on_failure) {
// Portable doesn't use the machine pc, we just use dex pc instead.
return static_cast<uint32_t>(pc);
}
- const void* entry_point = GetQuickOatEntryPoint();
- MappingTable table(
- entry_point != nullptr ? GetMappingTable(EntryPointToCodePointer(entry_point)) : nullptr);
+ const void* entry_point = GetQuickOatEntryPoint(sizeof(void*));
+ MappingTable table(entry_point != nullptr ?
+ GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr);
if (table.TotalSize() == 0) {
// NOTE: Special methods (see Mir2Lir::GenSpecialCase()) have an empty mapping
// but they have no suspend checks and, consequently, we never call ToDexPc() for them.
@@ -198,9 +198,9 @@ uint32_t ArtMethod::ToDexPc(const uintptr_t pc, bool abort_on_failure) {
}
uintptr_t ArtMethod::ToNativeQuickPc(const uint32_t dex_pc, bool abort_on_failure) {
- const void* entry_point = GetQuickOatEntryPoint();
- MappingTable table(
- entry_point != nullptr ? GetMappingTable(EntryPointToCodePointer(entry_point)) : nullptr);
+ const void* entry_point = GetQuickOatEntryPoint(sizeof(void*));
+ MappingTable table(entry_point != nullptr ?
+ GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr);
if (table.TotalSize() == 0) {
DCHECK_EQ(dex_pc, 0U);
return 0; // Special no mapping/pc == 0 case
@@ -320,13 +320,13 @@ bool ArtMethod::IsEntrypointInterpreter() {
}
}
-const void* ArtMethod::GetQuickOatEntryPoint() {
+const void* ArtMethod::GetQuickOatEntryPoint(size_t pointer_size) {
if (IsPortableCompiled() || IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) {
return nullptr;
}
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
- const void* code = runtime->GetInstrumentation()->GetQuickCodeFor(this);
+ const void* code = runtime->GetInstrumentation()->GetQuickCodeFor(this, pointer_size);
// On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method
// indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline)
// for non-native methods.
@@ -340,7 +340,7 @@ const void* ArtMethod::GetQuickOatEntryPoint() {
#ifndef NDEBUG
uintptr_t ArtMethod::NativeQuickPcOffset(const uintptr_t pc, const void* quick_entry_point) {
CHECK_NE(quick_entry_point, GetQuickToInterpreterBridge());
- CHECK_EQ(quick_entry_point, Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this));
+ CHECK_EQ(quick_entry_point, Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this, sizeof(void*)));
return pc - reinterpret_cast<uintptr_t>(quick_entry_point);
}
#endif
@@ -447,7 +447,7 @@ QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() {
return runtime->GetRuntimeMethodFrameInfo(this);
}
- const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this);
+ const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this, sizeof(void*));
ClassLinker* class_linker = runtime->GetClassLinker();
// On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method
// indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline)
@@ -483,7 +483,7 @@ void ArtMethod::RegisterNative(const void* native_method, bool is_fast) {
if (is_fast) {
SetAccessFlags(GetAccessFlags() | kAccFastNative);
}
- SetNativeMethod(native_method);
+ SetEntryPointFromJni(native_method);
}
void ArtMethod::UnregisterNative() {
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index d92d00a8cb..d292552af2 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -48,11 +48,6 @@ class MANAGED ArtMethod FINAL : public Object {
// Size of java.lang.reflect.ArtMethod.class.
static uint32_t ClassSize();
- // Size of an instance of java.lang.reflect.ArtMethod not including its value array.
- static constexpr uint32_t InstanceSize() {
- return sizeof(ArtMethod);
- }
-
static ArtMethod* FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa,
jobject jlr_method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -151,13 +146,13 @@ class MANAGED ArtMethod FINAL : public Object {
SetAccessFlags(GetAccessFlags() | kAccPreverified);
}
- bool IsOptimized() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool IsOptimized(size_t pointer_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Temporary solution for detecting if a method has been optimized: the compiler
// does not create a GC map. Instead, the vmap table contains the stack map
// (as in stack_map.h).
- return (GetEntryPointFromQuickCompiledCode() != nullptr)
- && (GetQuickOatCodePointer() != nullptr)
- && (GetNativeGcMap() == nullptr);
+ return GetEntryPointFromQuickCompiledCodePtrSize(pointer_size) != nullptr
+ && GetQuickOatCodePointer(pointer_size) != nullptr
+ && GetNativeGcMapPtrSize(pointer_size) == nullptr;
}
bool IsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -258,49 +253,92 @@ class MANAGED ArtMethod FINAL : public Object {
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
EntryPointFromInterpreter* GetEntryPointFromInterpreter()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetFieldPtr<EntryPointFromInterpreter*, kVerifyFlags>(
- OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_));
+ CheckObjectSizeEqualsMirrorSize();
+ return GetEntryPointFromInterpreterPtrSize(sizeof(void*));
}
-
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ EntryPointFromInterpreter* GetEntryPointFromInterpreterPtrSize(size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetFieldPtrWithSize<EntryPointFromInterpreter*, kVerifyFlags>(
+ EntryPointFromInterpreterOffset(pointer_size), pointer_size);
+ }
+
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetEntryPointFromInterpreter(EntryPointFromInterpreter* entry_point_from_interpreter)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetFieldPtr<false, true, kVerifyFlags>(
- OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_),
- entry_point_from_interpreter);
+ CheckObjectSizeEqualsMirrorSize();
+ SetEntryPointFromInterpreterPtrSize(entry_point_from_interpreter, sizeof(void*));
+ }
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void SetEntryPointFromInterpreterPtrSize(EntryPointFromInterpreter* entry_point_from_interpreter,
+ size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SetFieldPtrWithSize<false, true, kVerifyFlags>(
+ EntryPointFromInterpreterOffset(pointer_size), entry_point_from_interpreter, pointer_size);
}
- static MemberOffset EntryPointFromPortableCompiledCodeOffset() {
- return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_portable_compiled_code_));
+ ALWAYS_INLINE static MemberOffset EntryPointFromPortableCompiledCodeOffset(size_t pointer_size) {
+ return MemberOffset(PtrSizedFieldsOffset() + OFFSETOF_MEMBER(
+ PtrSizedFields, entry_point_from_portable_compiled_code_) / sizeof(void*) * pointer_size);
}
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- const void* GetEntryPointFromPortableCompiledCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetFieldPtr<const void*, kVerifyFlags>(
- EntryPointFromPortableCompiledCodeOffset());
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ const void* GetEntryPointFromPortableCompiledCode()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ CheckObjectSizeEqualsMirrorSize();
+ return GetEntryPointFromPortableCompiledCodePtrSize(sizeof(void*));
}
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE const void* GetEntryPointFromPortableCompiledCodePtrSize(size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetFieldPtrWithSize<const void*, kVerifyFlags>(
+ EntryPointFromPortableCompiledCodeOffset(pointer_size), pointer_size);
+ }
+
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetEntryPointFromPortableCompiledCode(const void* entry_point_from_portable_compiled_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetFieldPtr<false, true, kVerifyFlags>(
- EntryPointFromPortableCompiledCodeOffset(), entry_point_from_portable_compiled_code);
+ CheckObjectSizeEqualsMirrorSize();
+ return SetEntryPointFromPortableCompiledCodePtrSize(entry_point_from_portable_compiled_code,
+ sizeof(void*));
}
- static MemberOffset EntryPointFromQuickCompiledCodeOffset() {
- return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_quick_compiled_code_));
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void SetEntryPointFromPortableCompiledCodePtrSize(
+ const void* entry_point_from_portable_compiled_code, size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SetFieldPtrWithSize<false, true, kVerifyFlags>(
+ EntryPointFromPortableCompiledCodeOffset(pointer_size),
+ entry_point_from_portable_compiled_code, pointer_size);
}
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
const void* GetEntryPointFromQuickCompiledCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetFieldPtr<const void*, kVerifyFlags>(EntryPointFromQuickCompiledCodeOffset());
+ CheckObjectSizeEqualsMirrorSize();
+ return GetEntryPointFromQuickCompiledCodePtrSize(sizeof(void*));
+ }
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE const void* GetEntryPointFromQuickCompiledCodePtrSize(size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetFieldPtrWithSize<const void*, kVerifyFlags>(
+ EntryPointFromQuickCompiledCodeOffset(pointer_size), pointer_size);
}
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetEntryPointFromQuickCompiledCode(const void* entry_point_from_quick_compiled_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetFieldPtr<false, true, kVerifyFlags>(
- EntryPointFromQuickCompiledCodeOffset(), entry_point_from_quick_compiled_code);
+ CheckObjectSizeEqualsMirrorSize();
+ SetEntryPointFromQuickCompiledCodePtrSize(entry_point_from_quick_compiled_code,
+ sizeof(void*));
+ }
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE void SetEntryPointFromQuickCompiledCodePtrSize(
+ const void* entry_point_from_quick_compiled_code, size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SetFieldPtrWithSize<false, true, kVerifyFlags>(
+ EntryPointFromQuickCompiledCodeOffset(pointer_size), entry_point_from_quick_compiled_code,
+ pointer_size);
}
uint32_t GetCodeSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -343,33 +381,48 @@ class MANAGED ArtMethod FINAL : public Object {
return reinterpret_cast<const void*>(code);
}
- // Actual entry point pointer to compiled oat code or nullptr if method has no compiled code.
- const void* GetQuickOatEntryPoint() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+ // Actual entry point pointer to compiled oat code or nullptr.
+ const void* GetQuickOatEntryPoint(size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Actual pointer to compiled oat code or nullptr.
- const void* GetQuickOatCodePointer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return EntryPointToCodePointer(GetQuickOatEntryPoint());
+ const void* GetQuickOatCodePointer(size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return EntryPointToCodePointer(GetQuickOatEntryPoint(pointer_size));
}
// Callers should wrap the uint8_t* in a MappingTable instance for convenient access.
- const uint8_t* GetMappingTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const uint8_t* GetMappingTable(const void* code_pointer)
+ const uint8_t* GetMappingTable(size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const uint8_t* GetMappingTable(const void* code_pointer, size_t pointer_size)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Callers should wrap the uint8_t* in a VmapTable instance for convenient access.
- const uint8_t* GetVmapTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const uint8_t* GetVmapTable(const void* code_pointer)
+ const uint8_t* GetVmapTable(size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const uint8_t* GetVmapTable(const void* code_pointer, size_t pointer_size)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
StackMap GetStackMap(uint32_t native_pc_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
CodeInfo GetOptimizedCodeInfo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const uint8_t* GetNativeGcMap() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_));
+ CheckObjectSizeEqualsMirrorSize();
+ return GetNativeGcMapPtrSize(sizeof(void*));
}
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE const uint8_t* GetNativeGcMapPtrSize(size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetFieldPtrWithSize<uint8_t*>(GcMapOffset(pointer_size), pointer_size);
+ }
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetNativeGcMap(const uint8_t* data) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- SetFieldPtr<false, true, kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data);
+ CheckObjectSizeEqualsMirrorSize();
+ SetNativeGcMapPtrSize(data, sizeof(void*));
+ }
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE void SetNativeGcMapPtrSize(const uint8_t* data, size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SetFieldPtrWithSize<false, true, kVerifyFlags>(GcMapOffset(pointer_size), data,
+ pointer_size);
}
// When building the oat need a convenient place to stuff the offset of the native GC map.
@@ -409,16 +462,46 @@ class MANAGED ArtMethod FINAL : public Object {
void UnregisterNative() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static MemberOffset NativeMethodOffset() {
- return OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_);
+ static MemberOffset EntryPointFromInterpreterOffset(size_t pointer_size) {
+ return MemberOffset(PtrSizedFieldsOffset() + OFFSETOF_MEMBER(
+ PtrSizedFields, entry_point_from_interpreter_) / sizeof(void*) * pointer_size);
}
- const void* GetNativeMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetFieldPtr<const void*>(NativeMethodOffset());
+ static MemberOffset EntryPointFromJniOffset(size_t pointer_size) {
+ return MemberOffset(PtrSizedFieldsOffset() + OFFSETOF_MEMBER(
+ PtrSizedFields, entry_point_from_jni_) / sizeof(void*) * pointer_size);
}
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void SetNativeMethod(const void*) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static MemberOffset EntryPointFromQuickCompiledCodeOffset(size_t pointer_size) {
+ return MemberOffset(PtrSizedFieldsOffset() + OFFSETOF_MEMBER(
+ PtrSizedFields, entry_point_from_quick_compiled_code_) / sizeof(void*) * pointer_size);
+ }
+
+ static MemberOffset GcMapOffset(size_t pointer_size) {
+ return MemberOffset(PtrSizedFieldsOffset() + OFFSETOF_MEMBER(
+ PtrSizedFields, gc_map_) / sizeof(void*) * pointer_size);
+ }
+
+ void* GetEntryPointFromJni() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ CheckObjectSizeEqualsMirrorSize();
+ return GetEntryPointFromJniPtrSize(sizeof(void*));
+ }
+ ALWAYS_INLINE void* GetEntryPointFromJniPtrSize(size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetFieldPtrWithSize<void*>(EntryPointFromJniOffset(pointer_size), pointer_size);
+ }
+
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void SetEntryPointFromJni(const void* entrypoint) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ CheckObjectSizeEqualsMirrorSize();
+ SetEntryPointFromJniPtrSize<kVerifyFlags>(entrypoint, sizeof(void*));
+ }
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE void SetEntryPointFromJniPtrSize(const void* entrypoint, size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SetFieldPtrWithSize<false, true, kVerifyFlags>(
+ EntryPointFromJniOffset(pointer_size), entrypoint, pointer_size);
+ }
static MemberOffset GetMethodIndexOffset() {
return OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_);
@@ -521,7 +604,16 @@ class MANAGED ArtMethod FINAL : public Object {
ALWAYS_INLINE ArtMethod* GetInterfaceMethodIfProxy() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- private:
+ static size_t SizeWithoutPointerFields() {
+ return sizeof(ArtMethod) - sizeof(PtrSizedFields);
+ }
+
+ // Size of an instance of java.lang.reflect.ArtMethod not including its value array.
+ static size_t InstanceSize(size_t pointer_size) {
+ return SizeWithoutPointerFields() + (sizeof(PtrSizedFields) / sizeof(void*)) * pointer_size;
+ }
+
+ protected:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
// The class we are a part of.
HeapReference<Class> declaring_class_;
@@ -535,26 +627,6 @@ class MANAGED ArtMethod FINAL : public Object {
// Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
HeapReference<ObjectArray<String>> dex_cache_strings_;
- // Method dispatch from the interpreter invokes this pointer which may cause a bridge into
- // compiled code.
- uint64_t entry_point_from_interpreter_;
-
- // Pointer to JNI function registered to this method, or a function to resolve the JNI function.
- uint64_t entry_point_from_jni_;
-
- // Method dispatch from portable compiled code invokes this pointer which may cause bridging into
- // quick compiled code or the interpreter.
- uint64_t entry_point_from_portable_compiled_code_;
-
- // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
- // portable compiled code or the interpreter.
- uint64_t entry_point_from_quick_compiled_code_;
-
- // Pointer to a data structure created by the compiler and used by the garbage collector to
- // determine which registers hold live references to objects within the heap. Keyed by native PC
- // offsets for the quick compiler and dex PCs for the portable.
- uint64_t gc_map_;
-
// Access flags; low 16 bits are defined by spec.
uint32_t access_flags_;
@@ -573,15 +645,46 @@ class MANAGED ArtMethod FINAL : public Object {
// ifTable.
uint32_t method_index_;
+ // Add alignment word here if necessary.
+
+ // Must be the last fields in the method.
+ struct PACKED(4) PtrSizedFields {
+ // Method dispatch from the interpreter invokes this pointer which may cause a bridge into
+ // compiled code.
+ void* entry_point_from_interpreter_;
+
+ // Pointer to JNI function registered to this method, or a function to resolve the JNI function.
+ void* entry_point_from_jni_;
+
+ // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
+ // portable compiled code or the interpreter.
+ void* entry_point_from_quick_compiled_code_;
+
+ // Pointer to a data structure created by the compiler and used by the garbage collector to
+ // determine which registers hold live references to objects within the heap. Keyed by native PC
+ // offsets for the quick compiler and dex PCs for the portable.
+ void* gc_map_;
+
+ // Method dispatch from portable compiled code invokes this pointer which may cause bridging
+ // into quick compiled code or the interpreter. Last to simplify entrypoint logic.
+ void* entry_point_from_portable_compiled_code_;
+ } ptr_sized_fields_;
+
static GcRoot<Class> java_lang_reflect_ArtMethod_;
private:
+ ALWAYS_INLINE void CheckObjectSizeEqualsMirrorSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
ALWAYS_INLINE ObjectArray<ArtMethod>* GetDexCacheResolvedMethods()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ALWAYS_INLINE ObjectArray<Class>* GetDexCacheResolvedTypes()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static size_t PtrSizedFieldsOffset() {
+ return OFFSETOF_MEMBER(ArtMethod, ptr_sized_fields_);
+ }
+
friend struct art::ArtMethodOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(ArtMethod);
};
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index f45ea8571f..82425b559d 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -528,6 +528,13 @@ class MANAGED Class FINAL : public Object {
return SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size);
}
+ void SetObjectSizeWithoutChecks(uint32_t new_object_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Not called within a transaction.
+ return SetField32<false, false, kVerifyNone>(
+ OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size);
+ }
+
// Returns true if this class is in the same packages as that class.
bool IsInSamePackage(Class* that) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 4199eef619..121947ddee 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -404,8 +404,7 @@ inline size_t Object::SizeOf() {
}
DCHECK_GE(result, sizeof(Object))
<< " class=" << PrettyTypeOf(GetClass<kNewFlags, kReadBarrierOption>());
- DCHECK(!(IsArtField<kNewFlags, kReadBarrierOption>()) || result == sizeof(ArtField));
- DCHECK(!(IsArtMethod<kNewFlags, kReadBarrierOption>()) || result == sizeof(ArtMethod));
+ DCHECK(!(IsArtField<kNewFlags, kReadBarrierOption>()) || result == sizeof(ArtField));
return result;
}
@@ -962,7 +961,6 @@ inline void Object::VisitReferences(const Visitor& visitor,
}
}
}
-
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 0ce5231b40..221feca6ad 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -392,15 +392,26 @@ class MANAGED LOCKABLE Object {
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename T>
void SetFieldPtr(MemberOffset field_offset, T new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-#ifndef __LP64__
- SetField32<kTransactionActive, kCheckTransaction, kVerifyFlags>(
- field_offset, reinterpret_cast<int32_t>(new_value));
-#else
- SetField64<kTransactionActive, kCheckTransaction, kVerifyFlags>(
- field_offset, reinterpret_cast<int64_t>(new_value));
-#endif
+ SetFieldPtrWithSize<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+ field_offset, new_value, sizeof(void*));
}
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename T>
+ ALWAYS_INLINE void SetFieldPtrWithSize(MemberOffset field_offset, T new_value,
+ size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(pointer_size == 4 || pointer_size == 8) << pointer_size;
+ if (pointer_size == 4) {
+ intptr_t ptr = reinterpret_cast<intptr_t>(new_value);
+ DCHECK_EQ(static_cast<int32_t>(ptr), ptr); // Check that we dont lose any non 0 bits.
+ SetField32<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+ field_offset, static_cast<int32_t>(ptr));
+ } else {
+ SetField64<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+ field_offset, static_cast<int64_t>(reinterpret_cast<intptr_t>(new_value)));
+ }
+ }
// TODO fix thread safety analysis broken by the use of template. This should be
// SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
@@ -413,11 +424,21 @@ class MANAGED LOCKABLE Object {
template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
T GetFieldPtr(MemberOffset field_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-#ifndef __LP64__
- return reinterpret_cast<T>(GetField32<kVerifyFlags, kIsVolatile>(field_offset));
-#else
- return reinterpret_cast<T>(GetField64<kVerifyFlags, kIsVolatile>(field_offset));
-#endif
+ return GetFieldPtrWithSize<T, kVerifyFlags, kIsVolatile>(field_offset, sizeof(void*));
+ }
+
+ template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+ ALWAYS_INLINE T GetFieldPtrWithSize(MemberOffset field_offset, size_t pointer_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(pointer_size == 4 || pointer_size == 8) << pointer_size;
+ if (pointer_size == 4) {
+ return reinterpret_cast<T>(GetField32<kVerifyFlags, kIsVolatile>(field_offset));
+ } else {
+ int64_t v = GetField64<kVerifyFlags, kIsVolatile>(field_offset);
+ // Check that we dont lose any non 0 bits.
+ DCHECK_EQ(reinterpret_cast<int64_t>(reinterpret_cast<T>(v)), v);
+ return reinterpret_cast<T>(v);
+ }
}
// TODO: Fixme when anotatalysis works with visitors.
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index a0aaa9e8c7..4402031ef1 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -78,6 +78,14 @@ class ObjectTest : public CommonRuntimeTest {
TEST_F(ObjectTest, Constants) {
EXPECT_EQ(kObjectReferenceSize, sizeof(HeapReference<Object>));
EXPECT_EQ(kObjectHeaderSize, sizeof(Object));
+ EXPECT_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32,
+ ArtMethod::EntryPointFromPortableCompiledCodeOffset(4).Int32Value());
+ EXPECT_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_64,
+ ArtMethod::EntryPointFromPortableCompiledCodeOffset(8).Int32Value());
+ EXPECT_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32,
+ ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value());
+ EXPECT_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64,
+ ArtMethod::EntryPointFromQuickCompiledCodeOffset(8).Int32Value());
}
TEST_F(ObjectTest, IsInSamePackage) {
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 64408a6604..30b8aa3c97 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -109,6 +109,14 @@ class MANAGED String FINAL : public Object {
int32_t CompareTo(String* other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetOffset(int32_t new_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Offset is only used during testing so use non-transactional mode.
+ DCHECK_LE(0, new_offset);
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset);
+ }
+
+ void SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
static Class* GetJavaLangString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(!java_lang_String_.IsNull());
return java_lang_String_.Read();
@@ -134,21 +142,12 @@ class MANAGED String FINAL : public Object {
SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(String, count_), new_count);
}
- void SetOffset(int32_t new_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // Offset is only used during testing so use non-transactional mode.
- DCHECK_LE(0, new_offset);
- DCHECK_GE(GetLength(), new_offset);
- SetField32<false>(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset);
- }
-
static String* Alloc(Thread* self, int32_t utf16_length)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static String* Alloc(Thread* self, Handle<CharArray> array)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
HeapReference<CharArray> array_;
diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc
index c2c6b12037..ffadfc61a7 100644
--- a/runtime/native_bridge_art_interface.cc
+++ b/runtime/native_bridge_art_interface.cc
@@ -72,7 +72,7 @@ static uint32_t GetNativeMethods(JNIEnv* env, jclass clazz, JNINativeMethod* met
if (count < method_count) {
methods[count].name = m->GetName();
methods[count].signature = m->GetShorty();
- methods[count].fnPtr = const_cast<void*>(m->GetNativeMethod());
+ methods[count].fnPtr = m->GetEntryPointFromJni();
count++;
} else {
LOG(WARNING) << "Output native method array too small. Skipping " << PrettyMethod(m);
@@ -85,7 +85,7 @@ static uint32_t GetNativeMethods(JNIEnv* env, jclass clazz, JNINativeMethod* met
if (count < method_count) {
methods[count].name = m->GetName();
methods[count].signature = m->GetShorty();
- methods[count].fnPtr = const_cast<void*>(m->GetNativeMethod());
+ methods[count].fnPtr = m->GetEntryPointFromJni();
count++;
} else {
LOG(WARNING) << "Output native method array too small. Skipping " << PrettyMethod(m);
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 2f7357fa7f..bfb27ddb3c 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -25,7 +25,7 @@
namespace art {
const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '4', '7', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '4', '8', '\0' };
static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
size_t estimate = 0U;
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 1a97c357fa..3e6c86b7c9 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -105,11 +105,7 @@ ParsedOptions::ParsedOptions()
profile_clock_source_(kDefaultTraceClockSource),
verify_(true),
image_isa_(kRuntimeISA),
- use_homogeneous_space_compaction_for_oom_(false), // If we are using homogeneous space
- // compaction then default background
- // compaction to off since homogeneous
- // space compactions when we transition
- // to not jank perceptible.
+ use_homogeneous_space_compaction_for_oom_(true), // Enable hspace compaction on OOM by default.
min_interval_homogeneous_space_compaction_by_oom_(MsToNs(100 * 1000)) // 100s.
{}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 78c6542827..078e7d24e1 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -438,10 +438,14 @@ bool Runtime::Start() {
started_ = true;
- if (IsZygote()) {
+ // Use !IsCompiler so that we get test coverage, tests are never the zygote.
+ if (!IsCompiler()) {
ScopedObjectAccess soa(self);
- Runtime::Current()->GetInternTable()->AddImageStringsToTable(heap_->GetImageSpace());
- Runtime::Current()->GetClassLinker()->MoveImageClassesToClassTable();
+ gc::space::ImageSpace* image_space = heap_->GetImageSpace();
+ if (image_space != nullptr) {
+ Runtime::Current()->GetInternTable()->AddImageStringsToTable(image_space);
+ Runtime::Current()->GetClassLinker()->MoveImageClassesToClassTable();
+ }
}
if (!IsImageDex2OatEnabled() || !Runtime::Current()->GetHeap()->HasImageSpace()) {
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index d448460dc3..e377542e45 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -110,11 +110,17 @@ void SignalCatcher::Output(const std::string& s) {
PLOG(ERROR) << "Unable to open stack trace file '" << stack_trace_file_ << "'";
return;
}
- std::unique_ptr<File> file(new File(fd, stack_trace_file_));
- if (!file->WriteFully(s.data(), s.size())) {
- PLOG(ERROR) << "Failed to write stack traces to '" << stack_trace_file_ << "'";
+ std::unique_ptr<File> file(new File(fd, stack_trace_file_, true));
+ bool success = file->WriteFully(s.data(), s.size());
+ if (success) {
+ success = file->FlushCloseOrErase() == 0;
} else {
+ file->Erase();
+ }
+ if (success) {
LOG(INFO) << "Wrote stack traces to '" << stack_trace_file_ << "'";
+ } else {
+ PLOG(ERROR) << "Failed to write stack traces to '" << stack_trace_file_ << "'";
}
}
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 44086096f0..43714b95e8 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -125,7 +125,7 @@ mirror::Object* StackVisitor::GetThisObject() const {
} else {
return cur_shadow_frame_->GetVRegReference(0);
}
- } else if (m->IsOptimized()) {
+ } else if (m->IsOptimized(sizeof(void*))) {
// TODO: Implement, currently only used for exceptions when jdwp is enabled.
UNIMPLEMENTED(WARNING)
<< "StackVisitor::GetThisObject is unimplemented with the optimizing compiler";
@@ -153,9 +153,9 @@ bool StackVisitor::GetVReg(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind,
if (cur_quick_frame_ != nullptr) {
DCHECK(context_ != nullptr); // You can't reliably read registers without a context.
DCHECK(m == GetMethod());
- const void* code_pointer = m->GetQuickOatCodePointer();
+ const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
DCHECK(code_pointer != nullptr);
- const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+ const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
uint32_t vmap_offset;
// TODO: IsInContext stops before spotting floating point registers.
@@ -207,9 +207,9 @@ bool StackVisitor::GetVRegPair(mirror::ArtMethod* m, uint16_t vreg, VRegKind kin
if (cur_quick_frame_ != nullptr) {
DCHECK(context_ != nullptr); // You can't reliably read registers without a context.
DCHECK(m == GetMethod());
- const void* code_pointer = m->GetQuickOatCodePointer();
+ const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
DCHECK(code_pointer != nullptr);
- const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+ const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
uint32_t vmap_offset_lo, vmap_offset_hi;
// TODO: IsInContext stops before spotting floating point registers.
@@ -254,9 +254,9 @@ bool StackVisitor::SetVReg(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_val
if (cur_quick_frame_ != nullptr) {
DCHECK(context_ != nullptr); // You can't reliably write registers without a context.
DCHECK(m == GetMethod());
- const void* code_pointer = m->GetQuickOatCodePointer();
+ const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
DCHECK(code_pointer != nullptr);
- const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+ const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
uint32_t vmap_offset;
// TODO: IsInContext stops before spotting floating point registers.
@@ -318,9 +318,9 @@ bool StackVisitor::SetVRegPair(mirror::ArtMethod* m, uint16_t vreg, uint64_t new
if (cur_quick_frame_ != nullptr) {
DCHECK(context_ != nullptr); // You can't reliably write registers without a context.
DCHECK(m == GetMethod());
- const void* code_pointer = m->GetQuickOatCodePointer();
+ const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
DCHECK(code_pointer != nullptr);
- const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+ const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
uint32_t vmap_offset_lo, vmap_offset_hi;
// TODO: IsInContext stops before spotting floating point registers.
diff --git a/runtime/thread.cc b/runtime/thread.cc
index c769faf3e5..cd47b5ea6e 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2136,9 +2136,9 @@ class ReferenceMapVisitor : public StackVisitor {
// Process register map (which native and runtime methods don't have)
if (!m->IsNative() && !m->IsRuntimeMethod() && !m->IsProxyMethod()) {
- if (m->IsOptimized()) {
+ if (m->IsOptimized(sizeof(void*))) {
Runtime* runtime = Runtime::Current();
- const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m);
+ const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m, sizeof(void*));
uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point);
StackMap map = m->GetStackMap(native_pc_offset);
MemoryRegion mask = map.GetStackMask();
@@ -2167,12 +2167,12 @@ class ReferenceMapVisitor : public StackVisitor {
static_cast<size_t>(code_item->registers_size_));
if (num_regs > 0) {
Runtime* runtime = Runtime::Current();
- const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m);
+ const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m, sizeof(void*));
uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point);
const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
DCHECK(reg_bitmap != nullptr);
const void* code_pointer = mirror::ArtMethod::EntryPointToCodePointer(entry_point);
- const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+ const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
// For all dex registers in the bitmap
DCHECK(cur_quick_frame != nullptr);
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 29c01e4d47..2cc50b3732 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -431,6 +431,15 @@ void Trace::Stop() {
instrumentation::Instrumentation::kMethodExited |
instrumentation::Instrumentation::kMethodUnwind);
}
+ if (the_trace->trace_file_.get() != nullptr) {
+ // Do not try to erase, so flush and close explicitly.
+ if (the_trace->trace_file_->Flush() != 0) {
+ PLOG(ERROR) << "Could not flush trace file.";
+ }
+ if (the_trace->trace_file_->Close() != 0) {
+ PLOG(ERROR) << "Could not close trace file.";
+ }
+ }
delete the_trace;
}
runtime->GetThreadList()->ResumeAll();
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 9a4c8759b8..ad46be644f 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1120,13 +1120,20 @@ std::string GetSchedulerGroupName(pid_t tid) {
void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix,
mirror::ArtMethod* current_method) {
- // TODO: enable on __linux__ b/15446488.
-#if 0
+#if __linux__
// b/18119146
if (RUNNING_ON_VALGRIND != 0) {
return;
}
+#if !defined(HAVE_ANDROID_OS)
+ if (GetTid() != tid) {
+ // TODO: dumping of other threads is disabled to avoid crashes during stress testing.
+ // b/15446488.
+ return;
+ }
+#endif
+
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
if (!backtrace->Unwind(0)) {
os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n";
diff --git a/runtime/utils.h b/runtime/utils.h
index d83013abec..668c897276 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -165,6 +165,18 @@ struct TypeIdentity {
typedef T type;
};
+// Like sizeof, but count how many bits a type takes. Pass type explicitly.
+template <typename T>
+static constexpr size_t BitSizeOf() {
+ return sizeof(T) * CHAR_BIT;
+}
+
+// Like sizeof, but count how many bits a type takes. Infers type from parameter.
+template <typename T>
+static constexpr size_t BitSizeOf(T /*x*/) {
+ return sizeof(T) * CHAR_BIT;
+}
+
// For rounding integers.
template<typename T>
static constexpr T RoundDown(T x, typename TypeIdentity<T>::type n) WARN_UNUSED;
@@ -201,10 +213,39 @@ static inline T* AlignUp(T* x, uintptr_t n) {
return reinterpret_cast<T*>(RoundUp(reinterpret_cast<uintptr_t>(x), n));
}
+namespace utils {
+namespace detail { // Private, implementation-specific namespace. Do not poke outside of this file.
+template <typename T>
+static constexpr inline T RoundUpToPowerOfTwoRecursive(T x, size_t bit) {
+ return bit == (BitSizeOf<T>()) ? x: RoundUpToPowerOfTwoRecursive(x | x >> bit, bit << 1);
+}
+} // namespace detail
+} // namespace utils
+
+// Recursive implementation is from "Hacker's Delight" by Henry S. Warren, Jr.,
+// figure 3-3, page 48, where the function is called clp2.
+template <typename T>
+static constexpr inline T RoundUpToPowerOfTwo(T x) {
+ return art::utils::detail::RoundUpToPowerOfTwoRecursive(x - 1, 1) + 1;
+}
+
+// Find the bit position of the most significant bit (0-based), or -1 if there were no bits set.
+template <typename T>
+static constexpr ssize_t MostSignificantBit(T value) {
+ return (value == 0) ? -1 : (MostSignificantBit(value >> 1) + 1);
+}
+
+// How many bits (minimally) does it take to store the constant 'value'? i.e. 1 for 1, 3 for 5, etc.
+template <typename T>
+static constexpr size_t MinimumBitsToStore(T value) {
+ return static_cast<size_t>(MostSignificantBit(value) + 1);
+}
+
template<typename T>
static constexpr int CLZ(T x) {
+ static_assert(sizeof(T) <= sizeof(long long), "T too large, must be smaller than long long"); // NOLINT [runtime/int] [4]
return (sizeof(T) == sizeof(uint32_t))
- ? __builtin_clz(x)
+ ? __builtin_clz(x) // TODO: __builtin_clz[ll] has undefined behavior for x=0
: __builtin_clzll(x);
}
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 92323da554..a98bc909f2 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -402,4 +402,36 @@ TEST_F(UtilsTest, ExecError) {
}
}
+TEST_F(UtilsTest, RoundUpToPowerOfTwo) {
+ // Tests the constexpr variant since all the parameters are constexpr
+ EXPECT_EQ(0, RoundUpToPowerOfTwo(0));
+ EXPECT_EQ(1, RoundUpToPowerOfTwo(1));
+ EXPECT_EQ(2, RoundUpToPowerOfTwo(2));
+ EXPECT_EQ(4, RoundUpToPowerOfTwo(3));
+ EXPECT_EQ(8, RoundUpToPowerOfTwo(7));
+
+ EXPECT_EQ(0b10000L, RoundUpToPowerOfTwo(0b01101L));
+ EXPECT_EQ(1ULL << 63, RoundUpToPowerOfTwo(1ULL << 62 | 1ULL));
+}
+
+TEST_F(UtilsTest, MostSignificantBit) {
+ EXPECT_EQ(-1, MostSignificantBit(0));
+ EXPECT_EQ(0, MostSignificantBit(1));
+ EXPECT_EQ(31, MostSignificantBit(~static_cast<uint32_t>(0)));
+ EXPECT_EQ(2, MostSignificantBit(0b110));
+ EXPECT_EQ(2, MostSignificantBit(0b100));
+}
+
+TEST_F(UtilsTest, MinimumBitsToStore) {
+ EXPECT_EQ(0u, MinimumBitsToStore(0));
+ EXPECT_EQ(1u, MinimumBitsToStore(1));
+ EXPECT_EQ(2u, MinimumBitsToStore(0b10));
+ EXPECT_EQ(2u, MinimumBitsToStore(0b11));
+ EXPECT_EQ(3u, MinimumBitsToStore(0b100));
+ EXPECT_EQ(3u, MinimumBitsToStore(0b110));
+ EXPECT_EQ(3u, MinimumBitsToStore(0b101));
+ EXPECT_EQ(8u, MinimumBitsToStore(0xFF));
+ EXPECT_EQ(32u, MinimumBitsToStore(~static_cast<uint32_t>(0)));
+}
+
} // namespace art
diff --git a/runtime/zip_archive_test.cc b/runtime/zip_archive_test.cc
index 96abee2dc3..70a4ddaabf 100644
--- a/runtime/zip_archive_test.cc
+++ b/runtime/zip_archive_test.cc
@@ -41,7 +41,7 @@ TEST_F(ZipArchiveTest, FindAndExtract) {
ScratchFile tmp;
ASSERT_NE(-1, tmp.GetFd());
- std::unique_ptr<File> file(new File(tmp.GetFd(), tmp.GetFilename()));
+ std::unique_ptr<File> file(new File(tmp.GetFd(), tmp.GetFilename(), false));
ASSERT_TRUE(file.get() != NULL);
bool success = zip_entry->ExtractToFile(*file, &error_msg);
ASSERT_TRUE(success) << error_msg;
diff --git a/test/083-compiler-regressions/expected.txt b/test/083-compiler-regressions/expected.txt
index 51bf8471c5..78c92fc51a 100644
--- a/test/083-compiler-regressions/expected.txt
+++ b/test/083-compiler-regressions/expected.txt
@@ -1,3 +1,4 @@
+b17325447 passes
b17630605 passes
b17411468 passes
b2296099 passes
diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java
index 9ad8ea7b1b..285c3608ca 100644
--- a/test/083-compiler-regressions/src/Main.java
+++ b/test/083-compiler-regressions/src/Main.java
@@ -30,6 +30,7 @@ public class Main {
}
public static void main(String args[]) throws Exception {
+ b17325447();
b17630605();
b17411468();
b2296099Test();
@@ -64,6 +65,31 @@ public class Main {
minDoubleWith3ConstsTest();
}
+ public static double b17325447_i1(int i1, double f) {
+ return f;
+ }
+
+ public static double b17325447_i2(int i1, int i2, double f) {
+ return f;
+ }
+
+ public static double b17325447_i3(int i1, int i2, int i3, double f) {
+ return f;
+ }
+
+ public static void b17325447() {
+ // b/17325447 - x86 handling of special identity method w/ double spanning reg/mem.
+ double d = 0.0;
+ d += b17325447_i1(123, 1.0);
+ d += b17325447_i2(123, 456, 2.0);
+ d += b17325447_i3(123, 456, 789, 3.0);
+ if (d == 6.0) {
+ System.out.println("b17325447 passes");
+ } else {
+ System.out.println("b17325447 fails: " + d);
+ }
+ }
+
public static void b17630605() {
// b/17630605 - failure to properly handle min long immediates.
long a1 = 40455547223404749L;
diff --git a/test/417-optimizing-arith-div/src/Main.java b/test/417-optimizing-arith-div/src/Main.java
index a5dea15559..909ceb43d6 100644
--- a/test/417-optimizing-arith-div/src/Main.java
+++ b/test/417-optimizing-arith-div/src/Main.java
@@ -24,6 +24,12 @@ public class Main {
}
}
+ public static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
public static void expectEquals(float expected, float result) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java
index 88b45280ac..37bc7779bd 100644
--- a/test/422-type-conversion/src/Main.java
+++ b/test/422-type-conversion/src/Main.java
@@ -24,6 +24,12 @@ public class Main {
}
}
+ public static void assertShortEquals(short expected, short result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
public static void assertIntEquals(int expected, int result) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
@@ -39,23 +45,57 @@ public class Main {
public static void assertCharEquals(char expected, char result) {
if (expected != result) {
// Values are cast to int to display numeric values instead of
- // (Unicode) characters.
+ // (UTF-16 encoded) characters.
throw new Error("Expected: " + (int)expected + ", found: " + (int)result);
}
}
+ public static void assertFloatEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void assertDoubleEquals(double expected, double result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+
public static void main(String[] args) {
+ // Generate, compile and check int-to-long Dex instructions.
byteToLong();
shortToLong();
intToLong();
charToLong();
+ // Generate, compile and check int-to-float Dex instructions.
+ byteToFloat();
+ shortToFloat();
+ intToFloat();
+ charToFloat();
+
+ // Generate, compile and check int-to-double Dex instructions.
+ byteToDouble();
+ shortToDouble();
+ intToDouble();
+ charToDouble();
+
+ // Generate, compile and check long-to-int Dex instructions.
longToInt();
+ // Generate, compile and check int-to-byte Dex instructions.
shortToByte();
intToByte();
charToByte();
+ // Generate, compile and check int-to-short Dex instructions.
+ byteToShort();
+ intToShort();
+ charToShort();
+
+ // Generate, compile and check int-to-char Dex instructions.
byteToChar();
shortToChar();
intToChar();
@@ -100,16 +140,106 @@ public class Main {
assertLongEquals(51L, $opt$CharToLong((char)51));
assertLongEquals(32767L, $opt$CharToLong((char)32767)); // 2^15 - 1
assertLongEquals(65535L, $opt$CharToLong((char)65535)); // 2^16 - 1
-
- assertLongEquals(0L, $opt$CharToLong('\u0000'));
- assertLongEquals(65535L, $opt$CharToLong('\uFFFF')); // 2^16 - 1
-
assertLongEquals(65535L, $opt$CharToLong((char)-1));
assertLongEquals(65485L, $opt$CharToLong((char)-51));
assertLongEquals(32769L, $opt$CharToLong((char)-32767)); // -(2^15 - 1)
assertLongEquals(32768L, $opt$CharToLong((char)-32768)); // -(2^15)
}
+ private static void byteToFloat() {
+ assertFloatEquals(1F, $opt$ByteToFloat((byte)1));
+ assertFloatEquals(0F, $opt$ByteToFloat((byte)0));
+ assertFloatEquals(-1F, $opt$ByteToFloat((byte)-1));
+ assertFloatEquals(51F, $opt$ByteToFloat((byte)51));
+ assertFloatEquals(-51F, $opt$ByteToFloat((byte)-51));
+ assertFloatEquals(127F, $opt$ByteToFloat((byte)127)); // 2^7 - 1
+ assertFloatEquals(-127F, $opt$ByteToFloat((byte)-127)); // -(2^7 - 1)
+ assertFloatEquals(-128F, $opt$ByteToFloat((byte)-128)); // -(2^7)
+ }
+
+ private static void shortToFloat() {
+ assertFloatEquals(1F, $opt$ShortToFloat((short)1));
+ assertFloatEquals(0F, $opt$ShortToFloat((short)0));
+ assertFloatEquals(-1F, $opt$ShortToFloat((short)-1));
+ assertFloatEquals(51F, $opt$ShortToFloat((short)51));
+ assertFloatEquals(-51F, $opt$ShortToFloat((short)-51));
+ assertFloatEquals(32767F, $opt$ShortToFloat((short)32767)); // 2^15 - 1
+ assertFloatEquals(-32767F, $opt$ShortToFloat((short)-32767)); // -(2^15 - 1)
+ assertFloatEquals(-32768F, $opt$ShortToFloat((short)-32768)); // -(2^15)
+ }
+
+ private static void intToFloat() {
+ assertFloatEquals(1F, $opt$IntToFloat(1));
+ assertFloatEquals(0F, $opt$IntToFloat(0));
+ assertFloatEquals(-1F, $opt$IntToFloat(-1));
+ assertFloatEquals(51F, $opt$IntToFloat(51));
+ assertFloatEquals(-51F, $opt$IntToFloat(-51));
+ assertFloatEquals(16777215F, $opt$IntToFloat(16777215)); // 2^24 - 1
+ assertFloatEquals(-16777215F, $opt$IntToFloat(-16777215)); // -(2^24 - 1)
+ assertFloatEquals(16777216F, $opt$IntToFloat(16777216)); // 2^24
+ assertFloatEquals(-16777216F, $opt$IntToFloat(-16777216)); // -(2^24)
+ assertFloatEquals(2147483647F, $opt$IntToFloat(2147483647)); // 2^31 - 1
+ assertFloatEquals(-2147483648F, $opt$IntToFloat(-2147483648)); // -(2^31)
+ }
+
+ private static void charToFloat() {
+ assertFloatEquals(1F, $opt$CharToFloat((char)1));
+ assertFloatEquals(0F, $opt$CharToFloat((char)0));
+ assertFloatEquals(51F, $opt$CharToFloat((char)51));
+ assertFloatEquals(32767F, $opt$CharToFloat((char)32767)); // 2^15 - 1
+ assertFloatEquals(65535F, $opt$CharToFloat((char)65535)); // 2^16 - 1
+ assertFloatEquals(65535F, $opt$CharToFloat((char)-1));
+ assertFloatEquals(65485F, $opt$CharToFloat((char)-51));
+ assertFloatEquals(32769F, $opt$CharToFloat((char)-32767)); // -(2^15 - 1)
+ assertFloatEquals(32768F, $opt$CharToFloat((char)-32768)); // -(2^15)
+ }
+
+ private static void byteToDouble() {
+ assertDoubleEquals(1D, $opt$ByteToDouble((byte)1));
+ assertDoubleEquals(0D, $opt$ByteToDouble((byte)0));
+ assertDoubleEquals(-1D, $opt$ByteToDouble((byte)-1));
+ assertDoubleEquals(51D, $opt$ByteToDouble((byte)51));
+ assertDoubleEquals(-51D, $opt$ByteToDouble((byte)-51));
+ assertDoubleEquals(127D, $opt$ByteToDouble((byte)127)); // 2^7 - 1
+ assertDoubleEquals(-127D, $opt$ByteToDouble((byte)-127)); // -(2^7 - 1)
+ assertDoubleEquals(-128D, $opt$ByteToDouble((byte)-128)); // -(2^7)
+ }
+
+ private static void shortToDouble() {
+ assertDoubleEquals(1D, $opt$ShortToDouble((short)1));
+ assertDoubleEquals(0D, $opt$ShortToDouble((short)0));
+ assertDoubleEquals(-1D, $opt$ShortToDouble((short)-1));
+ assertDoubleEquals(51D, $opt$ShortToDouble((short)51));
+ assertDoubleEquals(-51D, $opt$ShortToDouble((short)-51));
+ assertDoubleEquals(32767D, $opt$ShortToDouble((short)32767)); // 2^15 - 1
+ assertDoubleEquals(-32767D, $opt$ShortToDouble((short)-32767)); // -(2^15 - 1)
+ assertDoubleEquals(-32768D, $opt$ShortToDouble((short)-32768)); // -(2^15)
+ }
+
+ private static void intToDouble() {
+ assertDoubleEquals(1D, $opt$IntToDouble(1));
+ assertDoubleEquals(0D, $opt$IntToDouble(0));
+ assertDoubleEquals(-1D, $opt$IntToDouble(-1));
+ assertDoubleEquals(51D, $opt$IntToDouble(51));
+ assertDoubleEquals(-51D, $opt$IntToDouble(-51));
+ assertDoubleEquals(16777216D, $opt$IntToDouble(16777216)); // 2^24
+ assertDoubleEquals(-16777216D, $opt$IntToDouble(-16777216)); // -(2^24)
+ assertDoubleEquals(2147483647D, $opt$IntToDouble(2147483647)); // 2^31 - 1
+ assertDoubleEquals(-2147483648D, $opt$IntToDouble(-2147483648)); // -(2^31)
+ }
+
+ private static void charToDouble() {
+ assertDoubleEquals(1D, $opt$CharToDouble((char)1));
+ assertDoubleEquals(0D, $opt$CharToDouble((char)0));
+ assertDoubleEquals(51D, $opt$CharToDouble((char)51));
+ assertDoubleEquals(32767D, $opt$CharToDouble((char)32767)); // 2^15 - 1
+ assertDoubleEquals(65535D, $opt$CharToDouble((char)65535)); // 2^16 - 1
+ assertDoubleEquals(65535D, $opt$CharToDouble((char)-1));
+ assertDoubleEquals(65485D, $opt$CharToDouble((char)-51));
+ assertDoubleEquals(32769D, $opt$CharToDouble((char)-32767)); // -(2^15 - 1)
+ assertDoubleEquals(32768D, $opt$CharToDouble((char)-32768)); // -(2^15)
+ }
+
private static void longToInt() {
assertIntEquals(1, $opt$LongToInt(1L));
assertIntEquals(0, $opt$LongToInt(0L));
@@ -175,10 +305,6 @@ public class Main {
assertByteEquals((byte)-128, $opt$CharToByte((char)128)); // 2^7
assertByteEquals((byte)-1, $opt$CharToByte((char)32767)); // 2^15 - 1
assertByteEquals((byte)-1, $opt$CharToByte((char)65535)); // 2^16 - 1
-
- assertByteEquals((byte)0, $opt$CharToByte('\u0000'));
- assertByteEquals((byte)-1, $opt$CharToByte('\uFFFF')); // 2^16 - 1
-
assertByteEquals((byte)-1, $opt$CharToByte((char)-1));
assertByteEquals((byte)-51, $opt$CharToByte((char)-51));
assertByteEquals((byte)-127, $opt$CharToByte((char)-127)); // -(2^7 - 1)
@@ -186,6 +312,47 @@ public class Main {
assertByteEquals((byte)127, $opt$CharToByte((char)-129)); // -(2^7 + 1)
}
+ private static void byteToShort() {
+ assertShortEquals((short)1, $opt$ByteToShort((byte)1));
+ assertShortEquals((short)0, $opt$ByteToShort((byte)0));
+ assertShortEquals((short)-1, $opt$ByteToShort((byte)-1));
+ assertShortEquals((short)51, $opt$ByteToShort((byte)51));
+ assertShortEquals((short)-51, $opt$ByteToShort((byte)-51));
+ assertShortEquals((short)127, $opt$ByteToShort((byte)127)); // 2^7 - 1
+ assertShortEquals((short)-127, $opt$ByteToShort((byte)-127)); // -(2^7 - 1)
+ assertShortEquals((short)-128, $opt$ByteToShort((byte)-128)); // -(2^7)
+ }
+
+ private static void intToShort() {
+ assertShortEquals((short)1, $opt$IntToShort(1));
+ assertShortEquals((short)0, $opt$IntToShort(0));
+ assertShortEquals((short)-1, $opt$IntToShort(-1));
+ assertShortEquals((short)51, $opt$IntToShort(51));
+ assertShortEquals((short)-51, $opt$IntToShort(-51));
+ assertShortEquals((short)32767, $opt$IntToShort(32767)); // 2^15 - 1
+ assertShortEquals((short)-32767, $opt$IntToShort(-32767)); // -(2^15 - 1)
+ assertShortEquals((short)-32768, $opt$IntToShort(-32768)); // -(2^15)
+ assertShortEquals((short)-32768, $opt$IntToShort(32768)); // 2^15
+ assertShortEquals((short)32767, $opt$IntToShort(-32769)); // -(2^15 + 1)
+ assertShortEquals((short)-1, $opt$IntToShort(2147483647)); // 2^31 - 1
+ assertShortEquals((short)0, $opt$IntToShort(-2147483648)); // -(2^31)
+ }
+
+ private static void charToShort() {
+ assertShortEquals((short)1, $opt$CharToShort((char)1));
+ assertShortEquals((short)0, $opt$CharToShort((char)0));
+ assertShortEquals((short)51, $opt$CharToShort((char)51));
+ assertShortEquals((short)32767, $opt$CharToShort((char)32767)); // 2^15 - 1
+ assertShortEquals((short)-32768, $opt$CharToShort((char)32768)); // 2^15
+ assertShortEquals((short)-32767, $opt$CharToShort((char)32769)); // 2^15
+ assertShortEquals((short)-1, $opt$CharToShort((char)65535)); // 2^16 - 1
+ assertShortEquals((short)-1, $opt$CharToShort((char)-1));
+ assertShortEquals((short)-51, $opt$CharToShort((char)-51));
+ assertShortEquals((short)-32767, $opt$CharToShort((char)-32767)); // -(2^15 - 1)
+ assertShortEquals((short)-32768, $opt$CharToShort((char)-32768)); // -(2^15)
+ assertShortEquals((short)32767, $opt$CharToShort((char)-32769)); // -(2^15 + 1)
+ }
+
private static void byteToChar() {
assertCharEquals((char)1, $opt$ByteToChar((byte)1));
assertCharEquals((char)0, $opt$ByteToChar((byte)0));
@@ -233,6 +400,18 @@ public class Main {
static long $opt$IntToLong(int a) { return a; }
static long $opt$CharToLong(int a) { return a; }
+ // These methods produce int-to-float Dex instructions.
+ static float $opt$ByteToFloat(byte a) { return a; }
+ static float $opt$ShortToFloat(short a) { return a; }
+ static float $opt$IntToFloat(int a) { return a; }
+ static float $opt$CharToFloat(char a) { return a; }
+
+ // These methods produce int-to-double Dex instructions.
+ static double $opt$ByteToDouble(byte a) { return a; }
+ static double $opt$ShortToDouble(short a) { return a; }
+ static double $opt$IntToDouble(int a) { return a; }
+ static double $opt$CharToDouble(int a) { return a; }
+
// These methods produce long-to-int Dex instructions.
static int $opt$LongToInt(long a){ return (int)a; }
static int $opt$LongLiteralToInt(){ return (int)42L; }
@@ -242,6 +421,11 @@ public class Main {
static byte $opt$IntToByte(int a){ return (byte)a; }
static byte $opt$CharToByte(char a){ return (byte)a; }
+ // These methods produce int-to-short Dex instructions.
+ static short $opt$ByteToShort(byte a){ return (short)a; }
+ static short $opt$IntToShort(int a){ return (short)a; }
+ static short $opt$CharToShort(char a){ return (short)a; }
+
// These methods produce int-to-char Dex instructions.
static char $opt$ByteToChar(byte a){ return (char)a; }
static char $opt$ShortToChar(short a){ return (char)a; }
diff --git a/test/425-invoke-super/expected.txt b/test/425-invoke-super/expected.txt
index e69de29bb2..f7f6ae4911 100644
--- a/test/425-invoke-super/expected.txt
+++ b/test/425-invoke-super/expected.txt
@@ -0,0 +1 @@
+Test started
diff --git a/test/425-invoke-super/src/Main.java b/test/425-invoke-super/src/Main.java
index 1fb62d0871..f3166fd35b 100644
--- a/test/425-invoke-super/src/Main.java
+++ b/test/425-invoke-super/src/Main.java
@@ -39,6 +39,8 @@ public class Main {
}
public static void main(String[] args) throws Exception {
+ // Workaround for b/18051191.
+ System.out.println("Test started");
assertEquals(1, new B().$opt$bar());
assertEquals(1, new C().$opt$bar());
assertEquals(1, new D().$opt$bar());
diff --git a/test/428-optimizing-arith-rem/expected.txt b/test/428-optimizing-arith-rem/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/428-optimizing-arith-rem/expected.txt
diff --git a/test/428-optimizing-arith-rem/info.txt b/test/428-optimizing-arith-rem/info.txt
new file mode 100644
index 0000000000..3e37ffeee8
--- /dev/null
+++ b/test/428-optimizing-arith-rem/info.txt
@@ -0,0 +1 @@
+Tests for modulo (rem) operation.
diff --git a/test/428-optimizing-arith-rem/src/Main.java b/test/428-optimizing-arith-rem/src/Main.java
new file mode 100644
index 0000000000..46bd3c6ac5
--- /dev/null
+++ b/test/428-optimizing-arith-rem/src/Main.java
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+public class Main {
+
+ public static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void expectDivisionByZero(int value) {
+ try {
+ $opt$Rem(value, 0);
+ throw new Error("Expected RuntimeException when modulo by 0");
+ } catch (java.lang.RuntimeException e) {
+ }
+ try {
+ $opt$RemZero(value);
+ throw new Error("Expected RuntimeException when modulo by 0");
+ } catch (java.lang.RuntimeException e) {
+ }
+ }
+
+ public static void expectDivisionByZero(long value) {
+ try {
+ $opt$Rem(value, 0L);
+ throw new Error("Expected RuntimeException when modulo by 0");
+ } catch (java.lang.RuntimeException e) {
+ }
+ try {
+ $opt$RemZero(value);
+ throw new Error("Expected RuntimeException when modulo by 0");
+ } catch (java.lang.RuntimeException e) {
+ }
+ }
+
+ public static void main(String[] args) {
+ rem();
+ }
+
+ public static void rem() {
+ remInt();
+ remLong();
+ }
+
+ private static void remInt() {
+ expectEquals(2, $opt$RemConst(6));
+ expectEquals(2, $opt$Rem(6, 4));
+ expectEquals(2, $opt$Rem(6, -4));
+ expectEquals(0, $opt$Rem(6, 3));
+ expectEquals(0, $opt$Rem(6, -3));
+ expectEquals(0, $opt$Rem(6, 1));
+ expectEquals(0, $opt$Rem(6, -1));
+ expectEquals(-1, $opt$Rem(-7, 3));
+ expectEquals(-1, $opt$Rem(-7, -3));
+ expectEquals(0, $opt$Rem(6, 6));
+ expectEquals(0, $opt$Rem(-6, -6));
+ expectEquals(7, $opt$Rem(7, 9));
+ expectEquals(7, $opt$Rem(7, -9));
+ expectEquals(-7, $opt$Rem(-7, 9));
+ expectEquals(-7, $opt$Rem(-7, -9));
+
+ expectEquals(0, $opt$Rem(Integer.MAX_VALUE, 1));
+ expectEquals(0, $opt$Rem(Integer.MAX_VALUE, -1));
+ expectEquals(0, $opt$Rem(Integer.MIN_VALUE, 1));
+ expectEquals(0, $opt$Rem(Integer.MIN_VALUE, -1)); // no overflow
+ expectEquals(-1, $opt$Rem(Integer.MIN_VALUE, Integer.MAX_VALUE));
+ expectEquals(Integer.MAX_VALUE, $opt$Rem(Integer.MAX_VALUE, Integer.MIN_VALUE));
+
+ expectEquals(0, $opt$Rem(0, 7));
+ expectEquals(0, $opt$Rem(0, Integer.MAX_VALUE));
+ expectEquals(0, $opt$Rem(0, Integer.MIN_VALUE));
+
+ expectDivisionByZero(0);
+ expectDivisionByZero(1);
+ expectDivisionByZero(5);
+ expectDivisionByZero(Integer.MAX_VALUE);
+ expectDivisionByZero(Integer.MIN_VALUE);
+ }
+
+ private static void remLong() {
+ expectEquals(2L, $opt$RemConst(6L));
+ expectEquals(2L, $opt$Rem(6L, 4L));
+ expectEquals(2L, $opt$Rem(6L, -4L));
+ expectEquals(0L, $opt$Rem(6L, 3L));
+ expectEquals(0L, $opt$Rem(6L, -3L));
+ expectEquals(0L, $opt$Rem(6L, 1L));
+ expectEquals(0L, $opt$Rem(6L, -1L));
+ expectEquals(-1L, $opt$Rem(-7L, 3L));
+ expectEquals(-1L, $opt$Rem(-7L, -3L));
+ expectEquals(0L, $opt$Rem(6L, 6L));
+ expectEquals(0L, $opt$Rem(-6L, -6L));
+ expectEquals(7L, $opt$Rem(7L, 9L));
+ expectEquals(7L, $opt$Rem(7L, -9L));
+ expectEquals(-7L, $opt$Rem(-7L, 9L));
+ expectEquals(-7L, $opt$Rem(-7L, -9L));
+
+ expectEquals(0L, $opt$Rem(Integer.MAX_VALUE, 1L));
+ expectEquals(0L, $opt$Rem(Integer.MAX_VALUE, -1L));
+ expectEquals(0L, $opt$Rem(Integer.MIN_VALUE, 1L));
+ expectEquals(0L, $opt$Rem(Integer.MIN_VALUE, -1L)); // no overflow
+ expectEquals(-1L, $opt$Rem(Integer.MIN_VALUE, Integer.MAX_VALUE));
+ expectEquals(Integer.MAX_VALUE, $opt$Rem(Integer.MAX_VALUE, Integer.MIN_VALUE));
+
+ expectEquals(0L, $opt$Rem(0L, 7L));
+ expectEquals(0L, $opt$Rem(0L, Integer.MAX_VALUE));
+ expectEquals(0L, $opt$Rem(0L, Integer.MIN_VALUE));
+
+ expectDivisionByZero(0L);
+ expectDivisionByZero(1L);
+ expectDivisionByZero(5L);
+ expectDivisionByZero(Integer.MAX_VALUE);
+ expectDivisionByZero(Integer.MIN_VALUE);
+ }
+
+ static int $opt$Rem(int a, int b) {
+ return a % b;
+ }
+
+ static int $opt$RemZero(int a) {
+ return a % 0;
+ }
+
+ // Modulo by literals != 0 should not generate checks.
+ static int $opt$RemConst(int a) {
+ return a % 4;
+ }
+
+ static long $opt$RemConst(long a) {
+ return a % 4L;
+ }
+
+ static long $opt$Rem(long a, long b) {
+ return a % b;
+ }
+
+ static long $opt$RemZero(long a) {
+ return a % 0L;
+ }
+}
diff --git a/test/429-ssa-builder/expected.txt b/test/429-ssa-builder/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/429-ssa-builder/expected.txt
diff --git a/test/429-ssa-builder/info.txt b/test/429-ssa-builder/info.txt
new file mode 100644
index 0000000000..509d00f5de
--- /dev/null
+++ b/test/429-ssa-builder/info.txt
@@ -0,0 +1,3 @@
+Regression test for the type propagation phase of the optimizing
+compiler, that used to crash when dealing with phi floating-point
+equivalents.
diff --git a/test/429-ssa-builder/src/Main.java b/test/429-ssa-builder/src/Main.java
new file mode 100644
index 0000000000..32fcef0aa9
--- /dev/null
+++ b/test/429-ssa-builder/src/Main.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ if (new Main().$opt$TestFloatPhi() != 33.0f) {
+ throw new Error("Unexpected result");
+ }
+ }
+
+ public float $opt$TestFloatPhi() {
+ float a = floatField;
+ float b = 42.0f;
+ if (test1) {
+ // The phi for `a` will be found to be of type float.
+ a = otherFloatField;
+ // The phi for `b` will be found to be of type int (constants in DEX).
+ b = 33.0f;
+ }
+ // Use a different condition to avoid having dx being too clever.
+ if (test2) {
+ // Type propagation now realizes that `b` must be of type float. So
+ // it requests a float equivalent for `b`. Because the phi for `a` is
+ // next to the phi for `b` in the phi list, the compiler used to crash,
+ // assuming that a float phi following a phi *must* be for the same DEX
+ // register.
+ a = b;
+ }
+ return a;
+ }
+
+ float floatField = 4.2f;
+ float otherFloatField = 42.2f;
+ boolean test1 = true;
+ boolean test2 = true;
+}
diff --git a/test/430-live-register-slow-path/expected.txt b/test/430-live-register-slow-path/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/430-live-register-slow-path/expected.txt
diff --git a/test/430-live-register-slow-path/info.txt b/test/430-live-register-slow-path/info.txt
new file mode 100644
index 0000000000..6f2af283b9
--- /dev/null
+++ b/test/430-live-register-slow-path/info.txt
@@ -0,0 +1,2 @@
+Regression test for the linear scan register allocator. It used
+to miscompute the number of live registers at a safepoint.
diff --git a/test/430-live-register-slow-path/src/Main.java b/test/430-live-register-slow-path/src/Main.java
new file mode 100644
index 0000000000..b84e6479f0
--- /dev/null
+++ b/test/430-live-register-slow-path/src/Main.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ $opt$TestSlowPath();
+ }
+
+ public static void $opt$TestSlowPath() {
+ Object[] o = bar();
+ assertEquals(0, o.length);
+ // The slowpath of the instanceof requires the live register
+ // holding `o` to be saved before going into runtime. The linear
+ // scan register allocator used to miscompute the number of
+ // live registers at a safepoint, so the place at which the register
+ // was saved was wrong.
+ doCall(o instanceof Interface[], o);
+ }
+
+ public static void assertEquals(int a, int b) {}
+ public static boolean doCall(boolean val, Object o) { return val; }
+
+ static Object[] bar() { return new Object[0]; }
+
+ static interface Interface {}
+}
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index f766b0a6cf..7674a8ab68 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -2,4 +2,8 @@ b/17790197
b/17978759
FloatBadArgReg
negLong
+sameFieldNames
+b/18380491
+invoke-super abstract
+BadCaseInOpRegRegReg
Done!
diff --git a/test/800-smali/smali/BadCaseInOpRegRegReg.smali b/test/800-smali/smali/BadCaseInOpRegRegReg.smali
new file mode 100644
index 0000000000..2683790365
--- /dev/null
+++ b/test/800-smali/smali/BadCaseInOpRegRegReg.smali
@@ -0,0 +1,13 @@
+.class public LBadCaseInOpRegRegReg;
+
+.super Ljava/lang/Object;
+
+.method public static getInt()I
+ .registers 2
+ const/4 v0, 0x0
+ const/4 v1, 0x1
+ add-int/2addr v0, v1
+ add-int/lit8 v1, v0, 0x1
+ mul-int v0, v1, v0
+ return v0
+.end method
diff --git a/test/800-smali/smali/b_18380491AbstractBase.smali b/test/800-smali/smali/b_18380491AbstractBase.smali
new file mode 100644
index 0000000000..7aa1b1a12e
--- /dev/null
+++ b/test/800-smali/smali/b_18380491AbstractBase.smali
@@ -0,0 +1,12 @@
+.class public LB18380491ActractBase;
+
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+ .locals 0
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public abstract foo(I)I
+.end method
diff --git a/test/800-smali/smali/b_18380491ConcreteClass.smali b/test/800-smali/smali/b_18380491ConcreteClass.smali
new file mode 100644
index 0000000000..db5ef3ba6b
--- /dev/null
+++ b/test/800-smali/smali/b_18380491ConcreteClass.smali
@@ -0,0 +1,19 @@
+.class public LB18380491ConcreteClass;
+
+.super LB18380491ActractBase;
+
+.method public constructor <init>()V
+ .locals 0
+ invoke-direct {p0}, LB18380491ActractBase;-><init>()V
+ return-void
+.end method
+
+.method public foo(I)I
+ .locals 1
+ if-eqz p1, :invoke_super_abstract
+ return p1
+ :invoke_super_abstract
+ invoke-super {p0, p1}, LB18380491ActractBase;->foo(I)I
+ move-result v0
+ return v0
+.end method
diff --git a/test/800-smali/smali/sameFieldNames.smali b/test/800-smali/smali/sameFieldNames.smali
new file mode 100644
index 0000000000..107161b538
--- /dev/null
+++ b/test/800-smali/smali/sameFieldNames.smali
@@ -0,0 +1,64 @@
+.class public LsameFieldNames;
+.super Ljava/lang/Object;
+
+# Test multiple fields with the same name and different types.
+# (Invalid in Java language but valid in bytecode.)
+.field static public a:D
+.field static public a:S
+.field static public a:J
+.field static public a:F
+.field static public a:Z
+.field static public a:I
+.field static public a:B
+.field static public a:C
+.field static public a:Ljava/lang/Integer;
+.field static public a:Ljava/lang/Long;
+.field static public a:Ljava/lang/Float;
+.field static public a:Ljava/lang/Double;
+.field static public a:Ljava/lang/Boolean;
+.field static public a:Ljava/lang/Void;
+.field static public a:Ljava/lang/Short;
+.field static public a:Ljava/lang/Char;
+.field static public a:Ljava/lang/Byte;
+
+# Add some more fields to stress test the sorting for offset assignment.
+.field static public b:C
+.field static public c:J
+.field static public d:C
+.field static public e:B
+.field static public f:C
+.field static public g:J
+.field static public h:C
+.field static public i:J
+.field static public j:I
+.field static public k:J
+.field static public l:J
+.field static public m:I
+.field static public n:J
+.field static public o:I
+.field static public p:Ljava/lang/Integer;
+.field static public q:I
+.field static public r:J
+.field static public s:I
+.field static public t:Ljava/lang/Integer;
+.field static public u:I
+.field static public v:J
+.field static public w:I
+.field static public x:Ljava/lang/Integer;
+.field static public y:I
+.field static public z:Ljava/lang/Integer;
+
+.method public static getInt()I
+ .locals 2
+ const/4 v0, 2
+ sput v0, LsameFieldNames;->a:I
+ sget-object v1, LsameFieldNames;->a:Ljava/lang/Integer;
+ const/4 v1, 0
+ if-nez v1, :fail
+ const/4 v0, 7
+ :ret
+ return v0
+ :fail
+ const/4 v0, 0
+ goto :ret
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 014edc0fa4..8d318c354b 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.LinkedList;
@@ -54,6 +55,12 @@ public class Main {
testCases.add(new TestCase("FloatBadArgReg", "FloatBadArgReg", "getInt",
new Object[]{100}, null, 100));
testCases.add(new TestCase("negLong", "negLong", "negLong", null, null, 122142L));
+ testCases.add(new TestCase("sameFieldNames", "sameFieldNames", "getInt", null, null, 7));
+ testCases.add(new TestCase("b/18380491", "B18380491ConcreteClass", "foo",
+ new Object[]{42}, null, 42));
+ testCases.add(new TestCase("invoke-super abstract", "B18380491ConcreteClass", "foo",
+ new Object[]{0}, new AbstractMethodError(), null));
+ testCases.add(new TestCase("BadCaseInOpRegRegReg", "BadCaseInOpRegRegReg", "getInt", null, null, 2));
}
public void runTests() {
@@ -115,6 +122,9 @@ public class Main {
} catch (Throwable exc) {
if (tc.expectedException == null) {
errorReturn = new IllegalStateException("Did not expect exception", exc);
+ } else if (exc instanceof InvocationTargetException && exc.getCause() != null &&
+ exc.getCause().getClass().equals(tc.expectedException.getClass())) {
+ // Expected exception is wrapped in InvocationTargetException.
} else if (!tc.expectedException.getClass().equals(exc.getClass())) {
errorReturn = new IllegalStateException("Expected " +
tc.expectedException.getClass().getName() +
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 3b949d6ad0..29da2f6200 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -298,163 +298,39 @@ TEST_ART_BROKEN_DEFAULT_RUN_TESTS :=
# Known broken tests for the arm64 optimizing compiler backend.
TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS := \
- 001-HelloWorld \
- 002-sleep \
003-omnibus-opcodes \
- 004-InterfaceTest \
- 004-JniTest \
004-NativeAllocations \
004-ReferenceMap \
- 004-SignalTest \
- 004-StackWalk \
- 004-UnsafeTest \
005-annotations \
- 006-args \
- 007-count10 \
- 008-exceptions \
009-instanceof \
010-instance \
- 011-array-copy \
- 013-math2 \
- 014-math3 \
- 016-intern \
- 017-float \
- 018-stack-overflow \
- 019-wrong-array-type \
- 020-string \
- 021-string2 \
- 022-interface \
+ 012-math \
023-many-interfaces \
- 024-illegal-access \
- 025-access-controller \
- 026-access \
- 028-array-write \
- 029-assert \
- 030-bad-finalizer \
- 031-class-attributes \
- 032-concrete-sub \
- 033-class-init-deadlock \
- 034-call-null \
- 035-enum \
- 036-finalizer \
037-inherit \
- 038-inner-null \
- 039-join-main \
- 040-miranda \
- 042-new-instance \
- 043-privates \
044-proxy \
045-reflect-array \
046-reflect \
047-returns \
- 049-show-object \
- 050-sync-test \
- 051-thread \
- 052-verifier-fun \
- 054-uncaught \
- 055-enum-performance \
- 056-const-string-jumbo \
- 058-enum-order \
- 061-out-of-memory \
062-character-encodings \
063-process-manager \
- 064-field-access \
- 065-mismatched-implements \
- 066-mismatched-super \
- 067-preemptive-unpark \
068-classloader \
069-field-type \
- 070-nio-buffer \
071-dexfile \
- 072-precise-gc \
- 074-gc-thrash \
- 075-verification-error \
- 076-boolean-put \
- 077-method-override \
- 078-polymorphic-virtual \
- 079-phantom \
- 080-oom-throw \
- 081-hot-exceptions \
- 082-inline-execute \
083-compiler-regressions \
- 084-class-init \
- 085-old-style-inner-class \
- 086-null-super \
- 087-gc-after-link \
- 088-monitor-verification \
- 090-loop-formation \
- 092-locale \
- 093-serialization \
- 094-pattern \
- 096-array-copy-concurrent-gc \
- 097-duplicate-method \
- 098-ddmc \
- 100-reflect2 \
- 101-fibonacci \
- 102-concurrent-gc \
- 103-string-append \
- 104-growth-limit \
- 105-invoke \
106-exceptions2 \
107-int-math2 \
- 108-check-cast \
- 109-suspend-check \
- 110-field-access \
- 111-unresolvable-exception \
- 112-double-math \
- 113-multidex \
114-ParallelGC \
- 117-nopatchoat \
- 118-noimage-dex2oat \
- 119-noimage-patchoat \
- 120-hashcode \
- 121-modifiers \
- 121-simple-suspend-check \
- 122-npe \
- 123-compiler-regressions-mt \
- 124-missing-classes \
- 125-gc-and-classloading \
- 126-miranda-multidex \
201-built-in-exception-detail-messages \
- 202-thread-oome \
- 300-package-override \
- 301-abstract-protected \
- 303-verification-stress \
- 304-method-tracing \
- 401-optimizing-compiler \
- 402-optimizing-control-flow \
- 403-optimizing-long \
- 404-optimizing-allocator \
- 405-optimizing-long-allocator \
- 406-fields \
407-arrays \
- 409-materialized-condition \
- 410-floats \
- 411-optimizing-arith \
412-new-array \
- 413-regalloc-regression \
- 414-optimizing-arith-sub \
- 414-static-fields \
- 415-optimizing-arith-neg \
- 416-optimizing-arith-not \
- 417-optimizing-arith-div \
- 418-const-string \
- 419-long-parameter \
- 420-const-class \
- 421-exceptions \
- 421-large-frame \
422-instanceof \
422-type-conversion \
- 423-invoke-interface \
424-checkcast \
- 426-monitor \
- 427-bitwise \
427-bounds \
- 700-LoadArgRegs \
+ 428-optimizing-arith-rem \
+ 430-live-register-slow-path \
701-easy-div-rem \
- 702-LargeBranchOffset \
- 703-floating-point-div \
- 800-smali
+ 800-smali \
ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/run-test b/test/run-test
index b43668d0e8..843714b949 100755
--- a/test/run-test
+++ b/test/run-test
@@ -501,6 +501,8 @@ if ! ulimit -S "$file_size_limit"; then
fi
good="no"
+good_build="yes"
+good_run="yes"
if [ "$dev_mode" = "yes" ]; then
"./${build}" 2>&1
build_exit="$?"
@@ -548,7 +550,15 @@ else
if [ "$build_exit" = '0' ]; then
echo "${test_dir}: running..." 1>&2
"./${run}" $run_args "$@" >"$output" 2>&1
+ run_exit="$?"
+ if [ "$run_exit" != "0" ]; then
+ echo "run exit status: $run_exit" 1>&2
+ good_run="no"
+ else
+ good_run="yes"
+ fi
else
+ good_build="no"
cp "$build_output" "$output"
echo "Failed to build in tmpdir=${tmp_dir} from oldwd=${oldwd} and cwd=`pwd`" >> "$output"
echo "Non-canonical tmpdir was ${noncanonical_tmp_dir}" >> "$output"
@@ -561,9 +571,11 @@ else
fi
./$check_cmd "$expected" "$output"
if [ "$?" = "0" ]; then
- # output == expected
- good="yes"
- echo "${test_dir}: succeeded!" 1>&2
+ if [ "$good_build" = "no" -o "$good_run" = "yes" ]; then
+ # output == expected
+ good="yes"
+ echo "${test_dir}: succeeded!" 1>&2
+ fi
fi
fi