Merge "Add conditional branches, and build dominator tree."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c419268..67f09f9 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -17,6 +17,7 @@
LOCAL_PATH := art
TEST_COMMON_SRC_FILES := \
+ compiler/dex/local_value_numbering_test.cc \
compiler/driver/compiler_driver_test.cc \
compiler/elf_writer_test.cc \
compiler/image_test.cc \
@@ -49,7 +50,9 @@
runtime/exception_test.cc \
runtime/gc/accounting/space_bitmap_test.cc \
runtime/gc/heap_test.cc \
- runtime/gc/space/space_test.cc \
+ runtime/gc/space/dlmalloc_space_test.cc \
+ runtime/gc/space/rosalloc_space_test.cc \
+ runtime/gc/space/large_object_space_test.cc \
runtime/gtest_test.cc \
runtime/indenter_test.cc \
runtime/indirect_reference_table_test.cc \
diff --git a/compiler/dex/arena_bit_vector.cc b/compiler/dex/arena_bit_vector.cc
index b567ae8..1b37b71 100644
--- a/compiler/dex/arena_bit_vector.cc
+++ b/compiler/dex/arena_bit_vector.cc
@@ -44,4 +44,14 @@
bool expandable, OatBitMapKind kind)
: BitVector(start_bits, expandable, new (arena) ArenaBitVectorAllocator(arena)), kind_(kind) {}
+BasicBlock* ArenaBitVector::BasicBlockIterator::Next() {
+ int idx = internal_iterator_.Next();
+
+ if (idx == -1) {
+ return nullptr;
+ }
+
+ return mir_graph_->GetBasicBlock(idx);
+}
+
} // namespace art
diff --git a/compiler/dex/arena_bit_vector.h b/compiler/dex/arena_bit_vector.h
index e904406..cdd5c68 100644
--- a/compiler/dex/arena_bit_vector.h
+++ b/compiler/dex/arena_bit_vector.h
@@ -20,14 +20,45 @@
#include "base/bit_vector.h"
#include "compiler_enums.h"
#include "utils/arena_allocator.h"
+#include "compiler_ir.h"
namespace art {
+// Forward declaration
+class MIRGraph;
+
/*
* A BitVector implementation that uses Arena allocation.
*/
class ArenaBitVector : public BitVector {
public:
+ /**
+ * @class BasicBlockIterator
+ * @brief Helper class to get the BasicBlocks when iterating through the ArenaBitVector.
+ */
+ class BasicBlockIterator {
+ public:
+ explicit BasicBlockIterator(ArenaBitVector* bv, MIRGraph* mir_graph)
+ : mir_graph_(mir_graph),
+ internal_iterator_(bv) {}
+
+ explicit BasicBlockIterator(ArenaBitVector* bv, CompilationUnit* c_unit)
+ : mir_graph_(c_unit->mir_graph.get()),
+ internal_iterator_(bv) {}
+
+ BasicBlock* Next();
+
+ static void* operator new(size_t size, ArenaAllocator* arena) {
+ return arena->Alloc(sizeof(ArenaBitVector::BasicBlockIterator),
+ ArenaAllocator::kAllocGrowableArray);
+ };
+ static void operator delete(void* p) {} // Nop.
+
+ private:
+ MIRGraph* const mir_graph_;
+ Iterator internal_iterator_;
+ };
+
ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable,
OatBitMapKind kind = kBitMapMisc);
~ArenaBitVector() {}
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 591d92a..6800f7b 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -211,7 +211,7 @@
class_loader, dex_file);
cu.NewTimingSplit("MIROpt:CheckFilters");
- if (cu.mir_graph->SkipCompilation(Runtime::Current()->GetCompilerFilter())) {
+ if (cu.mir_graph->SkipCompilation()) {
return NULL;
}
diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc
index 9e83210..a3ea034 100644
--- a/compiler/dex/local_value_numbering.cc
+++ b/compiler/dex/local_value_numbering.cc
@@ -16,8 +16,120 @@
#include "local_value_numbering.h"
+#include "mir_graph.h"
+
namespace art {
+uint16_t LocalValueNumbering::GetFieldId(const DexFile* dex_file, uint16_t field_idx) {
+ FieldReference key = { dex_file, field_idx };
+ auto it = field_index_map_.find(key);
+ if (it != field_index_map_.end()) {
+ return it->second;
+ }
+ uint16_t id = field_index_map_.size();
+ field_index_map_.Put(key, id);
+ return id;
+}
+
+void LocalValueNumbering::AdvanceGlobalMemory() {
+ // See AdvanceMemoryVersion() for explanation.
+ global_memory_version_ = next_memory_version_;
+ ++next_memory_version_;
+}
+
+uint16_t LocalValueNumbering::GetMemoryVersion(uint16_t base, uint16_t field, uint16_t type) {
+ // See AdvanceMemoryVersion() for explanation.
+ MemoryVersionKey key = { base, field, type };
+ MemoryVersionMap::iterator it = memory_version_map_.find(key);
+ uint16_t memory_version = (it != memory_version_map_.end()) ? it->second : 0u;
+ if (base != NO_VALUE && non_aliasing_refs_.find(base) == non_aliasing_refs_.end()) {
+ // Check modifications by potentially aliased access.
+ MemoryVersionKey aliased_access_key = { NO_VALUE, field, type };
+ auto aa_it = memory_version_map_.find(aliased_access_key);
+ if (aa_it != memory_version_map_.end() && aa_it->second > memory_version) {
+ memory_version = aa_it->second;
+ }
+ memory_version = std::max(memory_version, global_memory_version_);
+ } else if (base != NO_VALUE) {
+ // Ignore global_memory_version_ for access via unique references.
+ } else {
+ memory_version = std::max(memory_version, global_memory_version_);
+ }
+ return memory_version;
+};
+
+uint16_t LocalValueNumbering::AdvanceMemoryVersion(uint16_t base, uint16_t field, uint16_t type) {
+ // When we read the same value from memory, we want to assign the same value name to it.
+ // However, we need to be careful not to assign the same value name if the memory location
+ // may have been written to between the reads. To avoid that we do "memory versioning".
+ //
+ // For each write to a memory location (instance field, static field, array element) we assign
+ // a new memory version number to the location identified by the value name of the base register,
+ // the field id and type, or "{ base, field, type }". For static fields the "base" is NO_VALUE
+ // since they are not accessed via a reference. For arrays the "field" is NO_VALUE since they
+ // don't have a field id.
+ //
+ // To account for the possibility of aliased access to the same memory location via different
+ // "base", we also store the memory version number with the key "{ NO_VALUE, field, type }"
+ // if "base" is an aliasing reference and check it in GetMemoryVersion() on reads via
+ // aliasing references. A global memory version is set for method calls as a method can
+ // potentially write to any memory location accessed via an aliasing reference.
+
+ uint16_t result = next_memory_version_;
+ ++next_memory_version_;
+ MemoryVersionKey key = { base, field, type };
+ memory_version_map_.Overwrite(key, result);
+ if (base != NO_VALUE && non_aliasing_refs_.find(base) == non_aliasing_refs_.end()) {
+ // Advance memory version for aliased access.
+ MemoryVersionKey aliased_access_key = { NO_VALUE, field, type };
+ memory_version_map_.Overwrite(aliased_access_key, result);
+ }
+ return result;
+};
+
+uint16_t LocalValueNumbering::MarkNonAliasingNonNull(MIR* mir) {
+ uint16_t res = GetOperandValue(mir->ssa_rep->defs[0]);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ DCHECK(null_checked_.find(res) == null_checked_.end());
+ null_checked_.insert(res);
+ non_aliasing_refs_.insert(res);
+ return res;
+}
+
+void LocalValueNumbering::MakeArgsAliasing(MIR* mir) {
+ for (size_t i = 0u, count = mir->ssa_rep->num_uses; i != count; ++i) {
+ uint16_t reg = GetOperandValue(mir->ssa_rep->uses[i]);
+ non_aliasing_refs_.erase(reg);
+ }
+}
+
+void LocalValueNumbering::HandleNullCheck(MIR* mir, uint16_t reg) {
+ if (null_checked_.find(reg) != null_checked_.end()) {
+ if (cu_->verbose) {
+ LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
+ }
+ mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+ } else {
+ null_checked_.insert(reg);
+ }
+}
+
+void LocalValueNumbering::HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index) {
+ if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) {
+ if (cu_->verbose) {
+ LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
+ }
+ mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK;
+ }
+ // Use side effect to note range check completed.
+ (void)LookupValue(ARRAY_REF, array, index, NO_VALUE);
+}
+
+void LocalValueNumbering::HandlePutObject(MIR* mir) {
+ // If we're storing a non-aliasing reference, stop tracking it as non-aliasing now.
+ uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]);
+ non_aliasing_refs_.erase(base);
+}
uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) {
uint16_t res = NO_VALUE;
@@ -36,8 +148,6 @@
case Instruction::CHECK_CAST:
case Instruction::THROW:
case Instruction::FILL_ARRAY_DATA:
- case Instruction::FILLED_NEW_ARRAY:
- case Instruction::FILLED_NEW_ARRAY_RANGE:
case Instruction::PACKED_SWITCH:
case Instruction::SPARSE_SWITCH:
case Instruction::IF_EQ:
@@ -52,16 +162,6 @@
case Instruction::IF_GEZ:
case Instruction::IF_GTZ:
case Instruction::IF_LEZ:
- case Instruction::INVOKE_STATIC_RANGE:
- case Instruction::INVOKE_STATIC:
- case Instruction::INVOKE_DIRECT:
- case Instruction::INVOKE_DIRECT_RANGE:
- case Instruction::INVOKE_VIRTUAL:
- case Instruction::INVOKE_VIRTUAL_RANGE:
- case Instruction::INVOKE_SUPER:
- case Instruction::INVOKE_SUPER_RANGE:
- case Instruction::INVOKE_INTERFACE:
- case Instruction::INVOKE_INTERFACE_RANGE:
case kMirOpFusedCmplFloat:
case kMirOpFusedCmpgFloat:
case kMirOpFusedCmplDouble:
@@ -70,25 +170,55 @@
// Nothing defined - take no action.
break;
- case Instruction::MOVE_EXCEPTION:
+ case Instruction::FILLED_NEW_ARRAY:
+ case Instruction::FILLED_NEW_ARRAY_RANGE:
+ // Nothing defined but the result will be unique and non-null.
+ if (mir->next != nullptr && mir->next->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
+ MarkNonAliasingNonNull(mir->next);
+ // The MOVE_RESULT_OBJECT will be processed next and we'll return the value name then.
+ }
+ MakeArgsAliasing(mir);
+ break;
+
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_SUPER:
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE: {
+ // Nothing defined but handle the null check.
+ uint16_t reg = GetOperandValue(mir->ssa_rep->uses[0]);
+ HandleNullCheck(mir, reg);
+ }
+ // Intentional fall-through.
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_STATIC_RANGE:
+ AdvanceGlobalMemory();
+ MakeArgsAliasing(mir);
+ break;
+
case Instruction::MOVE_RESULT:
case Instruction::MOVE_RESULT_OBJECT:
case Instruction::INSTANCE_OF:
+ // 1 result, treat as unique each time, use result s_reg - will be unique.
+ res = GetOperandValue(mir->ssa_rep->defs[0]);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
+ break;
+ case Instruction::MOVE_EXCEPTION:
case Instruction::NEW_INSTANCE:
case Instruction::CONST_STRING:
case Instruction::CONST_STRING_JUMBO:
case Instruction::CONST_CLASS:
- case Instruction::NEW_ARRAY: {
- // 1 result, treat as unique each time, use result s_reg - will be unique.
- uint16_t res = GetOperandValue(mir->ssa_rep->defs[0]);
- SetOperandValue(mir->ssa_rep->defs[0], res);
- }
+ case Instruction::NEW_ARRAY:
+ // 1 result, treat as unique each time, use result s_reg - will be unique.
+ res = MarkNonAliasingNonNull(mir);
break;
- case Instruction::MOVE_RESULT_WIDE: {
- // 1 wide result, treat as unique each time, use result s_reg - will be unique.
- uint16_t res = GetOperandValueWide(mir->ssa_rep->defs[0]);
- SetOperandValueWide(mir->ssa_rep->defs[0], res);
- }
+ case Instruction::MOVE_RESULT_WIDE:
+ // 1 wide result, treat as unique each time, use result s_reg - will be unique.
+ res = GetOperandValueWide(mir->ssa_rep->defs[0]);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
break;
case kMirOpPhi:
@@ -104,35 +234,31 @@
case Instruction::MOVE_OBJECT_16:
case Instruction::MOVE_FROM16:
case Instruction::MOVE_OBJECT_FROM16:
- case kMirOpCopy: {
- // Just copy value number of source to value number of resulit.
- uint16_t res = GetOperandValue(mir->ssa_rep->uses[0]);
- SetOperandValue(mir->ssa_rep->defs[0], res);
- }
+ case kMirOpCopy:
+ // Just copy value number of source to value number of result.
+ res = GetOperandValue(mir->ssa_rep->uses[0]);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
break;
case Instruction::MOVE_WIDE:
case Instruction::MOVE_WIDE_16:
- case Instruction::MOVE_WIDE_FROM16: {
- // Just copy value number of source to value number of result.
- uint16_t res = GetOperandValueWide(mir->ssa_rep->uses[0]);
- SetOperandValueWide(mir->ssa_rep->defs[0], res);
- }
+ case Instruction::MOVE_WIDE_FROM16:
+ // Just copy value number of source to value number of result.
+ res = GetOperandValueWide(mir->ssa_rep->uses[0]);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
break;
case Instruction::CONST:
case Instruction::CONST_4:
- case Instruction::CONST_16: {
- uint16_t res = LookupValue(Instruction::CONST, Low16Bits(mir->dalvikInsn.vB),
- High16Bits(mir->dalvikInsn.vB >> 16), 0);
- SetOperandValue(mir->ssa_rep->defs[0], res);
- }
+ case Instruction::CONST_16:
+ res = LookupValue(Instruction::CONST, Low16Bits(mir->dalvikInsn.vB),
+ High16Bits(mir->dalvikInsn.vB >> 16), 0);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
break;
- case Instruction::CONST_HIGH16: {
- uint16_t res = LookupValue(Instruction::CONST, 0, mir->dalvikInsn.vB, 0);
- SetOperandValue(mir->ssa_rep->defs[0], res);
- }
+ case Instruction::CONST_HIGH16:
+ res = LookupValue(Instruction::CONST, 0, mir->dalvikInsn.vB, 0);
+ SetOperandValue(mir->ssa_rep->defs[0], res);
break;
case Instruction::CONST_WIDE_16:
@@ -145,8 +271,8 @@
} else {
high_res = LookupValue(Instruction::CONST, 0, 0, 2);
}
- uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3);
- SetOperandValue(mir->ssa_rep->defs[0], res);
+ res = LookupValue(Instruction::CONST, low_res, high_res, 3);
+ SetOperandValueWide(mir->ssa_rep->defs[0], res);
}
break;
@@ -157,7 +283,7 @@
High16Bits(low_word), 1);
uint16_t high_res = LookupValue(Instruction::CONST, Low16Bits(high_word),
High16Bits(high_word), 2);
- uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3);
+ res = LookupValue(Instruction::CONST, low_res, high_res, 3);
SetOperandValueWide(mir->ssa_rep->defs[0], res);
}
break;
@@ -165,7 +291,7 @@
case Instruction::CONST_WIDE_HIGH16: {
uint16_t low_res = LookupValue(Instruction::CONST, 0, 0, 1);
uint16_t high_res = LookupValue(Instruction::CONST, 0, Low16Bits(mir->dalvikInsn.vB), 2);
- uint16_t res = LookupValue(Instruction::CONST, low_res, high_res, 3);
+ res = LookupValue(Instruction::CONST, low_res, high_res, 3);
SetOperandValueWide(mir->ssa_rep->defs[0], res);
}
break;
@@ -181,7 +307,7 @@
case Instruction::FLOAT_TO_INT: {
// res = op + 1 operand
uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
- uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+ res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
SetOperandValue(mir->ssa_rep->defs[0], res);
}
break;
@@ -192,7 +318,7 @@
case Instruction::DOUBLE_TO_INT: {
// res = op + 1 wide operand
uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
- uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+ res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
SetOperandValue(mir->ssa_rep->defs[0], res);
}
break;
@@ -205,7 +331,7 @@
case Instruction::NEG_DOUBLE: {
// wide res = op + 1 wide operand
uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
- uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+ res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
SetOperandValueWide(mir->ssa_rep->defs[0], res);
}
break;
@@ -216,7 +342,7 @@
case Instruction::INT_TO_LONG: {
// wide res = op + 1 operand
uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
- uint16_t res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
+ res = LookupValue(opcode, operand1, NO_VALUE, NO_VALUE);
SetOperandValueWide(mir->ssa_rep->defs[0], res);
}
break;
@@ -227,7 +353,7 @@
// res = op + 2 wide operands
uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]);
- uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+ res = LookupValue(opcode, operand1, operand2, NO_VALUE);
SetOperandValue(mir->ssa_rep->defs[0], res);
}
break;
@@ -259,7 +385,7 @@
// res = op + 2 operands
uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]);
- uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+ res = LookupValue(opcode, operand1, operand2, NO_VALUE);
SetOperandValue(mir->ssa_rep->defs[0], res);
}
break;
@@ -293,7 +419,7 @@
// wide res = op + 2 wide operands
uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]);
- uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+ res = LookupValue(opcode, operand1, operand2, NO_VALUE);
SetOperandValueWide(mir->ssa_rep->defs[0], res);
}
break;
@@ -307,7 +433,7 @@
// wide res = op + 1 wide operand + 1 operand
uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]);
uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]);
- uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+ res = LookupValue(opcode, operand1, operand2, NO_VALUE);
SetOperandValueWide(mir->ssa_rep->defs[0], res);
}
break;
@@ -325,7 +451,7 @@
// res = op + 2 operands
uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]);
- uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+ res = LookupValue(opcode, operand1, operand2, NO_VALUE);
SetOperandValue(mir->ssa_rep->defs[0], res);
}
break;
@@ -352,38 +478,25 @@
// Same as res = op + 2 operands, except use vB as operand 2
uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]);
uint16_t operand2 = LookupValue(Instruction::CONST, mir->dalvikInsn.vB, 0, 0);
- uint16_t res = LookupValue(opcode, operand1, operand2, NO_VALUE);
+ res = LookupValue(opcode, operand1, operand2, NO_VALUE);
SetOperandValue(mir->ssa_rep->defs[0], res);
}
break;
- case Instruction::AGET_WIDE:
- case Instruction::AGET:
case Instruction::AGET_OBJECT:
+ case Instruction::AGET:
+ case Instruction::AGET_WIDE:
case Instruction::AGET_BOOLEAN:
case Instruction::AGET_BYTE:
case Instruction::AGET_CHAR:
case Instruction::AGET_SHORT: {
+ uint16_t type = opcode - Instruction::AGET;
uint16_t array = GetOperandValue(mir->ssa_rep->uses[0]);
- if (null_checked_.find(array) != null_checked_.end()) {
- if (cu_->verbose) {
- LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
- }
- mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
- } else {
- null_checked_.insert(array);
- }
+ HandleNullCheck(mir, array);
uint16_t index = GetOperandValue(mir->ssa_rep->uses[1]);
- if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) {
- if (cu_->verbose) {
- LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
- }
- mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK;
- }
- // Use side effect to note range check completed.
- (void)LookupValue(ARRAY_REF, array, index, NO_VALUE);
+ HandleRangeCheck(mir, array, index);
// Establish value number for loaded register. Note use of memory version.
- uint16_t memory_version = GetMemoryVersion(array, NO_VALUE);
+ uint16_t memory_version = GetMemoryVersion(array, NO_VALUE, type);
uint16_t res = LookupValue(ARRAY_REF, array, index, memory_version);
if (opcode == Instruction::AGET_WIDE) {
SetOperandValueWide(mir->ssa_rep->defs[0], res);
@@ -393,116 +506,113 @@
}
break;
- case Instruction::APUT_WIDE:
- case Instruction::APUT:
case Instruction::APUT_OBJECT:
- case Instruction::APUT_SHORT:
- case Instruction::APUT_CHAR:
+ HandlePutObject(mir);
+ // Intentional fall-through.
+ case Instruction::APUT:
+ case Instruction::APUT_WIDE:
case Instruction::APUT_BYTE:
- case Instruction::APUT_BOOLEAN: {
+ case Instruction::APUT_BOOLEAN:
+ case Instruction::APUT_SHORT:
+ case Instruction::APUT_CHAR: {
+ uint16_t type = opcode - Instruction::APUT;
int array_idx = (opcode == Instruction::APUT_WIDE) ? 2 : 1;
int index_idx = array_idx + 1;
uint16_t array = GetOperandValue(mir->ssa_rep->uses[array_idx]);
- if (null_checked_.find(array) != null_checked_.end()) {
- if (cu_->verbose) {
- LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
- }
- mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
- } else {
- null_checked_.insert(array);
- }
+ HandleNullCheck(mir, array);
uint16_t index = GetOperandValue(mir->ssa_rep->uses[index_idx]);
- if (ValueExists(ARRAY_REF, array, index, NO_VALUE)) {
- if (cu_->verbose) {
- LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset;
- }
- mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK;
- }
- // Use side effect to note range check completed.
- (void)LookupValue(ARRAY_REF, array, index, NO_VALUE);
+ HandleRangeCheck(mir, array, index);
// Rev the memory version
- AdvanceMemoryVersion(array, NO_VALUE);
+ AdvanceMemoryVersion(array, NO_VALUE, type);
}
break;
case Instruction::IGET_OBJECT:
- case Instruction::IGET_WIDE:
case Instruction::IGET:
- case Instruction::IGET_CHAR:
- case Instruction::IGET_SHORT:
+ case Instruction::IGET_WIDE:
case Instruction::IGET_BOOLEAN:
- case Instruction::IGET_BYTE: {
+ case Instruction::IGET_BYTE:
+ case Instruction::IGET_CHAR:
+ case Instruction::IGET_SHORT: {
uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]);
- if (null_checked_.find(base) != null_checked_.end()) {
- if (cu_->verbose) {
- LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
- }
- mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
- } else {
- null_checked_.insert(base);
- }
- uint16_t field_ref = mir->dalvikInsn.vC;
- uint16_t memory_version = GetMemoryVersion(base, field_ref);
+ HandleNullCheck(mir, base);
+ uint16_t memory_version;
+ uint16_t field_id;
+ // TODO: all gets treated as volatile.
+ // Volatile fields always get a new memory version; field id is irrelevant.
+ // Unresolved fields are always marked as volatile and handled the same way here.
+ field_id = 0u;
+ memory_version = next_memory_version_;
+ ++next_memory_version_;
if (opcode == Instruction::IGET_WIDE) {
- uint16_t res = LookupValue(Instruction::IGET_WIDE, base, field_ref, memory_version);
+ res = LookupValue(Instruction::IGET_WIDE, base, field_id, memory_version);
SetOperandValueWide(mir->ssa_rep->defs[0], res);
} else {
- uint16_t res = LookupValue(Instruction::IGET, base, field_ref, memory_version);
+ res = LookupValue(Instruction::IGET, base, field_id, memory_version);
SetOperandValue(mir->ssa_rep->defs[0], res);
}
}
break;
- case Instruction::IPUT_WIDE:
case Instruction::IPUT_OBJECT:
+ HandlePutObject(mir);
+ // Intentional fall-through.
case Instruction::IPUT:
+ case Instruction::IPUT_WIDE:
case Instruction::IPUT_BOOLEAN:
case Instruction::IPUT_BYTE:
case Instruction::IPUT_CHAR:
case Instruction::IPUT_SHORT: {
+ uint16_t type = opcode - Instruction::IPUT;
int base_reg = (opcode == Instruction::IPUT_WIDE) ? 2 : 1;
uint16_t base = GetOperandValue(mir->ssa_rep->uses[base_reg]);
- if (null_checked_.find(base) != null_checked_.end()) {
- if (cu_->verbose) {
- LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset;
- }
- mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
- } else {
- null_checked_.insert(base);
- }
- uint16_t field_ref = mir->dalvikInsn.vC;
- AdvanceMemoryVersion(base, field_ref);
+ HandleNullCheck(mir, base);
+ // TODO: all puts treated as unresolved.
+ // Unresolved fields always alias with everything of the same type.
+ unresolved_ifield_version_[type] = next_memory_version_;
+ ++next_memory_version_;
}
break;
case Instruction::SGET_OBJECT:
case Instruction::SGET:
+ case Instruction::SGET_WIDE:
case Instruction::SGET_BOOLEAN:
case Instruction::SGET_BYTE:
case Instruction::SGET_CHAR:
- case Instruction::SGET_SHORT:
- case Instruction::SGET_WIDE: {
- uint16_t field_ref = mir->dalvikInsn.vB;
- uint16_t memory_version = GetMemoryVersion(NO_VALUE, field_ref);
+ case Instruction::SGET_SHORT: {
+ uint16_t memory_version;
+ uint16_t field_id;
+ // TODO: all gets treated as volatile.
+ // Volatile fields always get a new memory version; field id is irrelevant.
+ // Unresolved fields are always marked as volatile and handled the same way here.
+ field_id = 0u;
+ memory_version = next_memory_version_;
+ ++next_memory_version_;
if (opcode == Instruction::SGET_WIDE) {
- uint16_t res = LookupValue(Instruction::SGET_WIDE, NO_VALUE, field_ref, memory_version);
+ res = LookupValue(Instruction::SGET_WIDE, NO_VALUE, field_id, memory_version);
SetOperandValueWide(mir->ssa_rep->defs[0], res);
} else {
- uint16_t res = LookupValue(Instruction::SGET, NO_VALUE, field_ref, memory_version);
+ res = LookupValue(Instruction::SGET, NO_VALUE, field_id, memory_version);
SetOperandValue(mir->ssa_rep->defs[0], res);
}
}
break;
case Instruction::SPUT_OBJECT:
+ HandlePutObject(mir);
+ // Intentional fall-through.
case Instruction::SPUT:
+ case Instruction::SPUT_WIDE:
case Instruction::SPUT_BOOLEAN:
case Instruction::SPUT_BYTE:
case Instruction::SPUT_CHAR:
- case Instruction::SPUT_SHORT:
- case Instruction::SPUT_WIDE: {
- uint16_t field_ref = mir->dalvikInsn.vB;
- AdvanceMemoryVersion(NO_VALUE, field_ref);
+ case Instruction::SPUT_SHORT: {
+ uint16_t type = opcode - Instruction::SPUT;
+ // TODO: all puts treated as unresolved.
+ // Unresolved fields always alias with everything of the same type.
+ unresolved_sfield_version_[type] = next_memory_version_;
+ ++next_memory_version_;
}
break;
}
diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h
index 33ca8f1..348bedc 100644
--- a/compiler/dex/local_value_numbering.h
+++ b/compiler/dex/local_value_numbering.h
@@ -24,16 +24,78 @@
namespace art {
-// Key is s_reg, value is value name.
-typedef SafeMap<uint16_t, uint16_t> SregValueMap;
-// Key is concatenation of quad, value is value name.
-typedef SafeMap<uint64_t, uint16_t> ValueMap;
-// Key represents a memory address, value is generation.
-typedef SafeMap<uint32_t, uint16_t> MemoryVersionMap;
+class DexFile;
class LocalValueNumbering {
+ private:
+ // Field types correspond to the ordering of GET/PUT instructions; this order is the same
+ // for IGET, IPUT, SGET, SPUT, AGET and APUT:
+ // op 0
+ // op_WIDE 1
+ // op_OBJECT 2
+ // op_BOOLEAN 3
+ // op_BYTE 4
+ // op_CHAR 5
+ // op_SHORT 6
+ static constexpr size_t kFieldTypeCount = 7;
+
+ // FieldReference represents either a unique resolved field or all unresolved fields together.
+ struct FieldReference {
+ const DexFile* dex_file;
+ uint16_t field_idx;
+ };
+
+ struct FieldReferenceComparator {
+ bool operator()(const FieldReference& lhs, const FieldReference& rhs) const {
+ if (lhs.field_idx != rhs.field_idx) {
+ return lhs.field_idx < rhs.field_idx;
+ }
+ return lhs.dex_file < rhs.dex_file;
+ }
+ };
+
+ struct MemoryVersionKey {
+ uint16_t base;
+ uint16_t field_id;
+ uint16_t type;
+ };
+
+ struct MemoryVersionKeyComparator {
+ bool operator()(const MemoryVersionKey& lhs, const MemoryVersionKey& rhs) const {
+ if (lhs.base != rhs.base) {
+ return lhs.base < rhs.base;
+ }
+ if (lhs.field_id != rhs.field_id) {
+ return lhs.field_id < rhs.field_id;
+ }
+ return lhs.type < rhs.type;
+ }
+ };
+
+ // Key is s_reg, value is value name.
+ typedef SafeMap<uint16_t, uint16_t> SregValueMap;
+ // Key is concatenation of opcode, operand1, operand2 and modifier, value is value name.
+ typedef SafeMap<uint64_t, uint16_t> ValueMap;
+ // Key represents a memory address, value is generation.
+ typedef SafeMap<MemoryVersionKey, uint16_t, MemoryVersionKeyComparator> MemoryVersionMap;
+ // Maps field key to field id for resolved fields.
+ typedef SafeMap<FieldReference, uint32_t, FieldReferenceComparator> FieldIndexMap;
+
public:
- explicit LocalValueNumbering(CompilationUnit* cu) : cu_(cu) {}
+ explicit LocalValueNumbering(CompilationUnit* cu)
+ : cu_(cu),
+ sreg_value_map_(),
+ sreg_wide_value_map_(),
+ value_map_(),
+ next_memory_version_(1u),
+ global_memory_version_(0u),
+ memory_version_map_(),
+ field_index_map_(),
+ non_aliasing_refs_(),
+ null_checked_() {
+ std::fill_n(unresolved_sfield_version_, kFieldTypeCount, 0u);
+ std::fill_n(unresolved_ifield_version_, kFieldTypeCount, 0u);
+ }
static uint64_t BuildKey(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) {
return (static_cast<uint64_t>(op) << 48 | static_cast<uint64_t>(operand1) << 32 |
@@ -59,29 +121,6 @@
return (it != value_map_.end());
};
- uint16_t GetMemoryVersion(uint16_t base, uint16_t field) {
- uint32_t key = (base << 16) | field;
- uint16_t res;
- MemoryVersionMap::iterator it = memory_version_map_.find(key);
- if (it == memory_version_map_.end()) {
- res = 0;
- memory_version_map_.Put(key, res);
- } else {
- res = it->second;
- }
- return res;
- };
-
- void AdvanceMemoryVersion(uint16_t base, uint16_t field) {
- uint32_t key = (base << 16) | field;
- MemoryVersionMap::iterator it = memory_version_map_.find(key);
- if (it == memory_version_map_.end()) {
- memory_version_map_.Put(key, 0);
- } else {
- it->second++;
- }
- };
-
void SetOperandValue(uint16_t s_reg, uint16_t value) {
SregValueMap::iterator it = sreg_value_map_.find(s_reg);
if (it != sreg_value_map_.end()) {
@@ -129,11 +168,28 @@
uint16_t GetValueNumber(MIR* mir);
private:
+ uint16_t GetFieldId(const DexFile* dex_file, uint16_t field_idx);
+ void AdvanceGlobalMemory();
+ uint16_t GetMemoryVersion(uint16_t base, uint16_t field, uint16_t type);
+ uint16_t AdvanceMemoryVersion(uint16_t base, uint16_t field, uint16_t type);
+ uint16_t MarkNonAliasingNonNull(MIR* mir);
+ void MakeArgsAliasing(MIR* mir);
+ void HandleNullCheck(MIR* mir, uint16_t reg);
+ void HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index);
+ void HandlePutObject(MIR* mir);
+
CompilationUnit* const cu_;
SregValueMap sreg_value_map_;
SregValueMap sreg_wide_value_map_;
ValueMap value_map_;
+ uint16_t next_memory_version_;
+ uint16_t global_memory_version_;
+ uint16_t unresolved_sfield_version_[kFieldTypeCount];
+ uint16_t unresolved_ifield_version_[kFieldTypeCount];
MemoryVersionMap memory_version_map_;
+ FieldIndexMap field_index_map_;
+ // Value names of references to objects that cannot be reached through a different value name.
+ std::set<uint16_t> non_aliasing_refs_;
std::set<uint16_t> null_checked_;
};
diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc
new file mode 100644
index 0000000..6ab6c51
--- /dev/null
+++ b/compiler/dex/local_value_numbering_test.cc
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include "local_value_numbering.h"
+#include "compiler_internals.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+class LocalValueNumberingTest : public testing::Test {
+ protected:
+ struct IFieldDef {
+ uint16_t field_idx;
+ uintptr_t declaring_dex_file;
+ uint16_t declaring_field_idx;
+ bool is_volatile;
+ };
+
+ struct SFieldDef {
+ uint16_t field_idx;
+ uintptr_t declaring_dex_file;
+ uint16_t declaring_field_idx;
+ bool is_volatile;
+ };
+
+ struct MIRDef {
+ static constexpr size_t kMaxSsaDefs = 2;
+ static constexpr size_t kMaxSsaUses = 3;
+
+ Instruction::Code opcode;
+ int64_t value;
+ uint32_t field_annotation;
+ size_t num_uses;
+ int32_t uses[kMaxSsaUses];
+ size_t num_defs;
+ int32_t defs[kMaxSsaDefs];
+ };
+
+#define DEF_CONST(opcode, reg, value) \
+ { opcode, value, 0u, 0, { }, 1, { reg } }
+#define DEF_CONST_WIDE(opcode, reg, value) \
+ { opcode, value, 0u, 0, { }, 2, { reg, reg + 1 } }
+#define DEF_IGET(opcode, reg, obj, field_annotation) \
+ { opcode, 0u, field_annotation, 1, { obj }, 1, { reg } }
+#define DEF_IGET_WIDE(opcode, reg, obj, field_annotation) \
+ { opcode, 0u, field_annotation, 1, { obj }, 2, { reg, reg + 1 } }
+#define DEF_IPUT(opcode, reg, obj, field_annotation) \
+ { opcode, 0u, field_annotation, 2, { reg, obj }, 0, { } }
+#define DEF_IPUT_WIDE(opcode, reg, obj, field_annotation) \
+ { opcode, 0u, field_annotation, 3, { reg, reg + 1, obj }, 0, { } }
+#define DEF_SGET(opcode, reg, field_annotation) \
+ { opcode, 0u, field_annotation, 0, { }, 1, { reg } }
+#define DEF_SGET_WIDE(opcode, reg, field_annotation) \
+ { opcode, 0u, field_annotation, 0, { }, 2, { reg, reg + 1 } }
+#define DEF_SPUT(opcode, reg, field_annotation) \
+ { opcode, 0u, field_annotation, 1, { reg }, 0, { } }
+#define DEF_SPUT_WIDE(opcode, reg, field_annotation) \
+ { opcode, 0u, field_annotation, 2, { reg, reg + 1 }, 0, { } }
+#define DEF_INVOKE1(opcode, reg) \
+ { opcode, 0u, 0u, 1, { reg }, 0, { } }
+#define DEF_UNIQUE_REF(opcode, reg) \
+ { opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ...
+
+ void DoPrepareIFields(const IFieldDef* defs, size_t count) {
+ }
+
+ template <size_t count>
+ void PrepareIFields(const IFieldDef (&defs)[count]) {
+ DoPrepareIFields(defs, count);
+ }
+
+ void DoPrepareSFields(const SFieldDef* defs, size_t count) {
+ }
+
+ template <size_t count>
+ void PrepareSFields(const SFieldDef (&defs)[count]) {
+ DoPrepareSFields(defs, count);
+ }
+
+ void DoPrepareMIRs(const MIRDef* defs, size_t count) {
+ mir_count_ = count;
+ mirs_ = reinterpret_cast<MIR*>(cu_.arena.Alloc(sizeof(MIR) * count, ArenaAllocator::kAllocMIR));
+ ssa_reps_.resize(count);
+ for (size_t i = 0u; i != count; ++i) {
+ const MIRDef* def = &defs[i];
+ MIR* mir = &mirs_[i];
+ mir->dalvikInsn.opcode = def->opcode;
+ mir->dalvikInsn.vB = static_cast<int32_t>(def->value);
+ mir->dalvikInsn.vB_wide = def->value;
+ mir->ssa_rep = &ssa_reps_[i];
+ mir->ssa_rep->num_uses = def->num_uses;
+ mir->ssa_rep->uses = const_cast<int32_t*>(def->uses); // Not modified by LVN.
+ mir->ssa_rep->fp_use = nullptr; // Not used by LVN.
+ mir->ssa_rep->num_defs = def->num_defs;
+ mir->ssa_rep->defs = const_cast<int32_t*>(def->defs); // Not modified by LVN.
+ mir->ssa_rep->fp_def = nullptr; // Not used by LVN.
+ mir->dalvikInsn.opcode = def->opcode;
+ mir->offset = i; // LVN uses offset only for debug output
+ mir->width = 1u; // Not used by LVN.
+ mir->optimization_flags = 0u;
+
+ if (i != 0u) {
+ mirs_[i - 1u].next = mir;
+ }
+ }
+ mirs_[count - 1u].next = nullptr;
+ }
+
+ template <size_t count>
+ void PrepareMIRs(const MIRDef (&defs)[count]) {
+ DoPrepareMIRs(defs, count);
+ }
+
+ void PerformLVN() {
+ value_names_.resize(mir_count_);
+ for (size_t i = 0; i != mir_count_; ++i) {
+ value_names_[i] = lvn_.GetValueNumber(&mirs_[i]);
+ }
+ }
+
+ LocalValueNumberingTest() : pool_(), cu_(&pool_), mir_count_(0u), mirs_(nullptr), lvn_(&cu_) {
+ cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
+ }
+
+ ArenaPool pool_;
+ CompilationUnit cu_;
+ size_t mir_count_;
+ MIR* mirs_;
+ std::vector<SSARepresentation> ssa_reps_;
+ std::vector<uint16_t> value_names_;
+ LocalValueNumbering lvn_;
+};
+
+#if 0 // TODO: re-enable when LVN is handling memory igets.
+TEST_F(LocalValueNumberingTest, TestIGetIGetInvokeIGet) {
+ static const IFieldDef ifields[] = {
+ { 1u, 1u, 1u, false }
+ };
+ static const MIRDef mirs[] = {
+ DEF_IGET(Instruction::IGET, 0u, 10u, 0u),
+ DEF_IGET(Instruction::IGET, 1u, 10u, 0u),
+ DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 11u),
+ DEF_IGET(Instruction::IGET, 2u, 10u, 0u),
+ };
+
+ PrepareIFields(ifields);
+ PrepareMIRs(mirs);
+ PerformLVN();
+ ASSERT_EQ(value_names_.size(), 4u);
+ EXPECT_EQ(value_names_[0], value_names_[1]);
+ EXPECT_NE(value_names_[0], value_names_[3]);
+ EXPECT_EQ(mirs_[0].optimization_flags, 0u);
+ EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK);
+ EXPECT_EQ(mirs_[2].optimization_flags, 0u);
+ EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK);
+}
+#endif
+
+TEST_F(LocalValueNumberingTest, TestIGetIPutIGetIGetIGet) {
+ static const IFieldDef ifields[] = {
+ { 1u, 1u, 1u, false },
+ { 2u, 1u, 2u, false },
+ };
+ static const MIRDef mirs[] = {
+ DEF_IGET(Instruction::IGET, 0u, 10u, 0u),
+ DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u), // May alias.
+ DEF_IGET(Instruction::IGET, 2u, 10u, 0u),
+ DEF_IGET(Instruction::IGET, 3u, 0u, 1u),
+ DEF_IGET(Instruction::IGET, 4u, 2u, 1u),
+ };
+
+ PrepareIFields(ifields);
+ PrepareMIRs(mirs);
+ PerformLVN();
+ ASSERT_EQ(value_names_.size(), 5u);
+ EXPECT_NE(value_names_[0], value_names_[2]);
+ EXPECT_NE(value_names_[3], value_names_[4]);
+ EXPECT_EQ(mirs_[0].optimization_flags, 0u);
+ EXPECT_EQ(mirs_[1].optimization_flags, 0u);
+ EXPECT_EQ(mirs_[2].optimization_flags, MIR_IGNORE_NULL_CHECK);
+ EXPECT_EQ(mirs_[3].optimization_flags, 0u);
+ EXPECT_EQ(mirs_[4].optimization_flags, 0u);
+}
+
+#if 0 // TODO: re-enable when LVN is handling memory igets.
+TEST_F(LocalValueNumberingTest, TestUniquePreserve1) {
+ static const IFieldDef ifields[] = {
+ { 1u, 1u, 1u, false },
+ };
+ static const MIRDef mirs[] = {
+ DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 10u),
+ DEF_IGET(Instruction::IGET, 0u, 10u, 0u),
+ DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u), // No aliasing since 10u is unique.
+ DEF_IGET(Instruction::IGET, 2u, 10u, 0u),
+ };
+
+ PrepareIFields(ifields);
+ PrepareMIRs(mirs);
+ PerformLVN();
+ ASSERT_EQ(value_names_.size(), 4u);
+ EXPECT_EQ(value_names_[1], value_names_[3]);
+ EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK);
+ EXPECT_EQ(mirs_[2].optimization_flags, 0u);
+ EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK);
+}
+#endif
+
+#if 0 // TODO: re-enable when LVN is handling memory igets.
+TEST_F(LocalValueNumberingTest, TestUniquePreserve2) {
+ static const IFieldDef ifields[] = {
+ { 1u, 1u, 1u, false },
+ };
+ static const MIRDef mirs[] = {
+ DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 11u),
+ DEF_IGET(Instruction::IGET, 0u, 10u, 0u),
+ DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u), // No aliasing since 11u is unique.
+ DEF_IGET(Instruction::IGET, 2u, 10u, 0u),
+ };
+
+ PrepareIFields(ifields);
+ PrepareMIRs(mirs);
+ PerformLVN();
+ ASSERT_EQ(value_names_.size(), 4u);
+ EXPECT_EQ(value_names_[1], value_names_[3]);
+ EXPECT_EQ(mirs_[1].optimization_flags, 0u);
+ EXPECT_EQ(mirs_[2].optimization_flags, MIR_IGNORE_NULL_CHECK);
+ EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK);
+}
+#endif
+
+#if 0 // TODO: re-enable when LVN is handling memory igets.
+TEST_F(LocalValueNumberingTest, TestUniquePreserveAndEscape) {
+ static const IFieldDef ifields[] = {
+ { 1u, 1u, 1u, false },
+ };
+ static const MIRDef mirs[] = {
+ DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 10u),
+ DEF_IGET(Instruction::IGET, 0u, 10u, 0u),
+ DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 11u), // 10u still unique.
+ DEF_IGET(Instruction::IGET, 2u, 10u, 0u),
+ DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 10u), // 10u not unique anymore.
+ DEF_IGET(Instruction::IGET, 3u, 10u, 0u),
+ };
+
+ PrepareIFields(ifields);
+ PrepareMIRs(mirs);
+ PerformLVN();
+ ASSERT_EQ(value_names_.size(), 6u);
+ EXPECT_EQ(value_names_[1], value_names_[3]);
+ EXPECT_NE(value_names_[1], value_names_[5]);
+ EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK);
+ EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK);
+ EXPECT_EQ(mirs_[5].optimization_flags, MIR_IGNORE_NULL_CHECK);
+}
+#endif
+
+TEST_F(LocalValueNumberingTest, TestVolatile) {
+ static const IFieldDef ifields[] = {
+ { 1u, 1u, 1u, false },
+ { 2u, 1u, 2u, true },
+ };
+ static const MIRDef mirs[] = {
+ DEF_IGET(Instruction::IGET, 0u, 10u, 1u), // Volatile.
+ DEF_IGET(Instruction::IGET, 1u, 0u, 0u), // Non-volatile.
+ DEF_IGET(Instruction::IGET, 2u, 10u, 1u), // Volatile.
+ DEF_IGET(Instruction::IGET, 3u, 2u, 1u), // Non-volatile.
+ };
+
+ PrepareIFields(ifields);
+ PrepareMIRs(mirs);
+ PerformLVN();
+ ASSERT_EQ(value_names_.size(), 4u);
+ EXPECT_NE(value_names_[0], value_names_[2]); // Volatile has always different value name.
+ EXPECT_NE(value_names_[1], value_names_[3]); // Used different base because of volatile.
+ EXPECT_EQ(mirs_[0].optimization_flags, 0u);
+ EXPECT_EQ(mirs_[1].optimization_flags, 0u);
+ EXPECT_EQ(mirs_[2].optimization_flags, MIR_IGNORE_NULL_CHECK);
+ EXPECT_EQ(mirs_[3].optimization_flags, 0u);
+}
+
+} // namespace art
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index ab55333..7ce8f69 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -18,6 +18,7 @@
#include "dataflow_iterator-inl.h"
#include "dex/quick/dex_file_method_inliner.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "driver/compiler_options.h"
namespace art {
@@ -958,7 +959,7 @@
}
// Complex, logic-intensive?
- if ((GetNumDalvikInsns() > Runtime::Current()->GetSmallMethodThreshold()) &&
+ if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) &&
stats->branch_ratio > 0.3) {
return false;
}
@@ -984,7 +985,7 @@
}
// If significant in size and high proportion of expensive operations, skip.
- if ((GetNumDalvikInsns() > Runtime::Current()->GetSmallMethodThreshold()) &&
+ if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) &&
(stats->heavyweight_ratio > 0.3)) {
return true;
}
@@ -996,12 +997,14 @@
* Will eventually want this to be a bit more sophisticated and happen at verification time.
* Ultimate goal is to drive with profile data.
*/
-bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) {
- if (compiler_filter == Runtime::kEverything) {
+bool MIRGraph::SkipCompilation() {
+ const CompilerOptions& compiler_options = cu_->compiler_driver->GetCompilerOptions();
+ CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter();
+ if (compiler_filter == CompilerOptions::kEverything) {
return false;
}
- if (compiler_filter == Runtime::kInterpretOnly) {
+ if (compiler_filter == CompilerOptions::kInterpretOnly) {
LOG(WARNING) << "InterpretOnly should ideally be filtered out prior to parsing.";
return true;
}
@@ -1010,17 +1013,17 @@
size_t small_cutoff = 0;
size_t default_cutoff = 0;
switch (compiler_filter) {
- case Runtime::kBalanced:
- small_cutoff = Runtime::Current()->GetSmallMethodThreshold();
- default_cutoff = Runtime::Current()->GetLargeMethodThreshold();
+ case CompilerOptions::kBalanced:
+ small_cutoff = compiler_options.GetSmallMethodThreshold();
+ default_cutoff = compiler_options.GetLargeMethodThreshold();
break;
- case Runtime::kSpace:
- small_cutoff = Runtime::Current()->GetTinyMethodThreshold();
- default_cutoff = Runtime::Current()->GetSmallMethodThreshold();
+ case CompilerOptions::kSpace:
+ small_cutoff = compiler_options.GetTinyMethodThreshold();
+ default_cutoff = compiler_options.GetSmallMethodThreshold();
break;
- case Runtime::kSpeed:
- small_cutoff = Runtime::Current()->GetHugeMethodThreshold();
- default_cutoff = Runtime::Current()->GetHugeMethodThreshold();
+ case CompilerOptions::kSpeed:
+ small_cutoff = compiler_options.GetHugeMethodThreshold();
+ default_cutoff = compiler_options.GetHugeMethodThreshold();
break;
default:
LOG(FATAL) << "Unexpected compiler_filter_: " << compiler_filter;
@@ -1033,17 +1036,17 @@
* Filter 1: Huge methods are likely to be machine generated, but some aren't.
* If huge, assume we won't compile, but allow futher analysis to turn it back on.
*/
- if (GetNumDalvikInsns() > Runtime::Current()->GetHugeMethodThreshold()) {
+ if (compiler_options.IsHugeMethod(GetNumDalvikInsns())) {
skip_compilation = true;
// If we're got a huge number of basic blocks, don't bother with further analysis.
- if (static_cast<size_t>(num_blocks_) > (Runtime::Current()->GetHugeMethodThreshold() / 2)) {
+ if (static_cast<size_t>(num_blocks_) > (compiler_options.GetHugeMethodThreshold() / 2)) {
return true;
}
- } else if (GetNumDalvikInsns() > Runtime::Current()->GetLargeMethodThreshold() &&
+ } else if (compiler_options.IsLargeMethod(GetNumDalvikInsns()) &&
/* If it's large and contains no branches, it's likely to be machine generated initialization */
(GetBranchCount() == 0)) {
return true;
- } else if (compiler_filter == Runtime::kSpeed) {
+ } else if (compiler_filter == CompilerOptions::kSpeed) {
// If not huge, compile.
return false;
}
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index d304db9..2174f67 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -372,7 +372,7 @@
* Examine the graph to determine whether it's worthwile to spend the time compiling
* this method.
*/
- bool SkipCompilation(Runtime::CompilerFilter compiler_filter);
+ bool SkipCompilation();
/*
* Parse dex method and add MIR at current insert point. Returns id (which is
@@ -917,6 +917,8 @@
size_t num_non_special_compiler_temps_;
size_t max_available_non_special_compiler_temps_;
size_t max_available_special_compiler_temps_;
+
+ friend class LocalValueNumberingTest;
};
} // namespace art
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index b91ef28..67d4769 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -406,7 +406,7 @@
// Is this the select pattern?
// TODO: flesh out support for Mips. NOTE: llvm's select op doesn't quite work here.
// TUNING: expand to support IF_xx compare & branches
- if (cu_->compiler_backend->IsPortable() &&
+ if (!cu_->compiler_backend->IsPortable() &&
(cu_->instruction_set == kThumb2 || cu_->instruction_set == kX86) &&
((mir->dalvikInsn.opcode == Instruction::IF_EQZ) ||
(mir->dalvikInsn.opcode == Instruction::IF_NEZ))) {
diff --git a/compiler/dex/pass_driver.cc b/compiler/dex/pass_driver.cc
index 4f8739a..b60f296 100644
--- a/compiler/dex/pass_driver.cc
+++ b/compiler/dex/pass_driver.cc
@@ -116,7 +116,7 @@
}
void PassDriver::DispatchPass(CompilationUnit* c_unit, const Pass* curPass) {
- LOG(DEBUG) << "Dispatching " << curPass->GetName();
+ VLOG(compiler) << "Dispatching " << curPass->GetName();
DataFlowAnalysisMode mode = curPass->GetTraversal();
@@ -145,7 +145,7 @@
case kNoNodes:
break;
default:
- LOG(DEBUG) << "Iterator mode not handled in dispatcher: " << mode;
+ LOG(FATAL) << "Iterator mode not handled in dispatcher: " << mode;
break;
}
}
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index c5dccda..3185449 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -25,6 +25,7 @@
#include "dex/verified_method.h"
#include "verifier/dex_gc_map.h"
#include "verifier/method_verifier.h"
+#include "vmap_table.h"
namespace art {
@@ -1042,30 +1043,32 @@
}
CompiledMethod* Mir2Lir::GetCompiledMethod() {
- // Combine vmap tables - core regs, then fp regs - into vmap_table
- std::vector<uint16_t> raw_vmap_table;
- // Core regs may have been inserted out of order - sort first
- std::sort(core_vmap_table_.begin(), core_vmap_table_.end());
- for (size_t i = 0 ; i < core_vmap_table_.size(); ++i) {
- // Copy, stripping out the phys register sort key
- raw_vmap_table.push_back(~(-1 << VREG_NUM_WIDTH) & core_vmap_table_[i]);
- }
- // If we have a frame, push a marker to take place of lr
+ // Combine vmap tables - core regs, then fp regs - into vmap_table.
+ Leb128EncodingVector vmap_encoder;
if (frame_size_ > 0) {
- raw_vmap_table.push_back(INVALID_VREG);
+ // Prefix the encoded data with its size.
+ size_t size = core_vmap_table_.size() + 1 /* marker */ + fp_vmap_table_.size();
+ vmap_encoder.Reserve(size + 1u); // All values are likely to be one byte in ULEB128 (<128).
+ vmap_encoder.PushBackUnsigned(size);
+ // Core regs may have been inserted out of order - sort first.
+ std::sort(core_vmap_table_.begin(), core_vmap_table_.end());
+ for (size_t i = 0 ; i < core_vmap_table_.size(); ++i) {
+ // Copy, stripping out the phys register sort key.
+ vmap_encoder.PushBackUnsigned(
+ ~(-1 << VREG_NUM_WIDTH) & (core_vmap_table_[i] + VmapTable::kEntryAdjustment));
+ }
+ // Push a marker to take place of lr.
+ vmap_encoder.PushBackUnsigned(VmapTable::kAdjustedFpMarker);
+ // fp regs already sorted.
+ for (uint32_t i = 0; i < fp_vmap_table_.size(); i++) {
+ vmap_encoder.PushBackUnsigned(fp_vmap_table_[i] + VmapTable::kEntryAdjustment);
+ }
} else {
DCHECK_EQ(__builtin_popcount(core_spill_mask_), 0);
DCHECK_EQ(__builtin_popcount(fp_spill_mask_), 0);
- }
- // Combine vmap tables - core regs, then fp regs. fp regs already sorted
- for (uint32_t i = 0; i < fp_vmap_table_.size(); i++) {
- raw_vmap_table.push_back(fp_vmap_table_[i]);
- }
- Leb128EncodingVector vmap_encoder;
- // Prefix the encoded data with its size.
- vmap_encoder.PushBackUnsigned(raw_vmap_table.size());
- for (uint16_t cur : raw_vmap_table) {
- vmap_encoder.PushBackUnsigned(cur);
+ DCHECK_EQ(core_vmap_table_.size(), 0u);
+ DCHECK_EQ(fp_vmap_table_.size(), 0u);
+ vmap_encoder.PushBackUnsigned(0u); // Size is 0.
}
CompiledMethod* result =
new CompiledMethod(*cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_,
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 5fa4596..35d193c 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -1257,17 +1257,13 @@
} else {
LoadValueDirectFixed(rl_start, reg_start);
}
- int r_tgt = (cu_->instruction_set != kX86) ? LoadHelper(QUICK_ENTRYPOINT_OFFSET(pIndexOf)) : 0;
+ int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pIndexOf));
GenNullCheck(rl_obj.s_reg_low, reg_ptr, info->opt_flags);
LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info));
intrinsic_launchpads_.Insert(launch_pad);
OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, launch_pad);
// NOTE: not a safepoint
- if (cu_->instruction_set != kX86) {
- OpReg(kOpBlx, r_tgt);
- } else {
- OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(pIndexOf));
- }
+ OpReg(kOpBlx, r_tgt);
LIR* resume_tgt = NewLIR0(kPseudoTargetLabel);
launch_pad->operands[2] = WrapPointer(resume_tgt);
// Record that we've already inlined & null checked
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index c60c394..6e97c53 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -676,7 +676,7 @@
bool GenInlinedAbsDouble(CallInfo* info);
bool GenInlinedFloatCvt(CallInfo* info);
bool GenInlinedDoubleCvt(CallInfo* info);
- bool GenInlinedIndexOf(CallInfo* info, bool zero_based);
+ virtual bool GenInlinedIndexOf(CallInfo* info, bool zero_based);
bool GenInlinedStringCompareTo(CallInfo* info);
bool GenInlinedCurrentThread(CallInfo* info);
bool GenInlinedUnsafeGet(CallInfo* info, bool is_long, bool is_volatile);
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index 6481589..538ce0d 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -175,6 +175,8 @@
{ kX86Mov32AI, kArrayImm, IS_STORE | IS_QUIN_OP | REG_USE01, { 0, 0, 0xC7, 0, 0, 0, 0, 4 }, "Mov32AI", "[!0r+!1r<<!2d+!3d],!4d" },
{ kX86Mov32TI, kThreadImm, IS_STORE | IS_BINARY_OP, { THREAD_PREFIX, 0, 0xC7, 0, 0, 0, 0, 4 }, "Mov32TI", "fs:[!0d],!1d" },
+ { kX86Lea32RM, kRegMem, IS_TERTIARY_OP | IS_LOAD | REG_DEF0_USE12, { 0, 0, 0x8D, 0, 0, 0, 0, 0 }, "Lea32RM", "!0r,[!1r+!2d]" },
+
{ kX86Lea32RA, kRegArray, IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8D, 0, 0, 0, 0, 0 }, "Lea32RA", "!0r,[!1r+!2r<<!3d+!4d]" },
{ kX86Cmov32RRC, kRegRegCond, IS_TERTIARY_OP | REG_DEF0_USE01 | USES_CCODES, {0, 0, 0x0F, 0x40, 0, 0, 0, 0}, "Cmovcc32RR", "!2c !0r,!1r" },
@@ -354,6 +356,7 @@
{ kX86Jmp8, kJmp, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP, { 0, 0, 0xEB, 0, 0, 0, 0, 0 }, "Jmp8", "!0t" },
{ kX86Jmp32, kJmp, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP, { 0, 0, 0xE9, 0, 0, 0, 0, 0 }, "Jmp32", "!0t" },
{ kX86JmpR, kJmp, IS_UNARY_OP | IS_BRANCH | REG_USE0, { 0, 0, 0xFF, 0, 0, 4, 0, 0 }, "JmpR", "!0r" },
+ { kX86Jecxz8, kJmp, NO_OPERAND | IS_BRANCH | NEEDS_FIXUP | REG_USEC, { 0, 0, 0xE3, 0, 0, 0, 0, 0 }, "Jecxz", "!0t" },
{ kX86CallR, kCall, IS_UNARY_OP | IS_BRANCH | REG_USE0, { 0, 0, 0xE8, 0, 0, 0, 0, 0 }, "CallR", "!0r" },
{ kX86CallM, kCall, IS_BINARY_OP | IS_BRANCH | IS_LOAD | REG_USE0, { 0, 0, 0xFF, 0, 0, 2, 0, 0 }, "CallM", "[!0r+!1d]" },
{ kX86CallA, kCall, IS_QUAD_OP | IS_BRANCH | IS_LOAD | REG_USE01, { 0, 0, 0xFF, 0, 0, 2, 0, 0 }, "CallA", "[!0r+!1r<<!2d+!3d]" },
@@ -364,6 +367,7 @@
{ kX86StartOfMethod, kMacro, IS_UNARY_OP | SETS_CCODES, { 0, 0, 0, 0, 0, 0, 0, 0 }, "StartOfMethod", "!0r" },
{ kX86PcRelLoadRA, kPcRel, IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8B, 0, 0, 0, 0, 0 }, "PcRelLoadRA", "!0r,[!1r+!2r<<!3d+!4p]" },
{ kX86PcRelAdr, kPcRel, IS_LOAD | IS_BINARY_OP | REG_DEF0, { 0, 0, 0xB8, 0, 0, 0, 0, 4 }, "PcRelAdr", "!0r,!1d" },
+ { kX86RepneScasw, kPrefix2Nullary, NO_OPERAND | SETS_CCODES, { 0x66, 0xF2, 0xAF, 0, 0, 0, 0, 0 }, "RepNE ScasW", "" },
};
static size_t ComputeSize(const X86EncodingMap* entry, int base, int displacement, bool has_sib) {
@@ -407,6 +411,8 @@
return lir->operands[0]; // length of nop is sole operand
case kNullary:
return 1; // 1 byte of opcode
+ case kPrefix2Nullary:
+ return 3; // 1 byte of opcode + 2 prefixes
case kRegOpcode: // lir operands - 0: reg
return ComputeSize(entry, 0, 0, false) - 1; // substract 1 for modrm
case kReg: // lir operands - 0: reg
@@ -489,7 +495,7 @@
return 6; // 2 byte opcode + rel32
}
case kJmp:
- if (lir->opcode == kX86Jmp8) {
+ if (lir->opcode == kX86Jmp8 || lir->opcode == kX86Jecxz8) {
return 2; // opcode + rel8
} else if (lir->opcode == kX86Jmp32) {
return 5; // opcode + rel32
@@ -957,6 +963,10 @@
code_buffer_.push_back((rel >> 8) & 0xFF);
code_buffer_.push_back((rel >> 16) & 0xFF);
code_buffer_.push_back((rel >> 24) & 0xFF);
+ } else if (entry->opcode == kX86Jecxz8) {
+ DCHECK(IS_SIMM8(rel));
+ code_buffer_.push_back(0xE3);
+ code_buffer_.push_back(rel & 0xFF);
} else {
DCHECK(entry->opcode == kX86JmpR);
code_buffer_.push_back(entry->skeleton.opcode);
@@ -1148,6 +1158,17 @@
lir->operands[0] = delta;
break;
}
+ case kX86Jecxz8: {
+ LIR *target_lir = lir->target;
+ DCHECK(target_lir != NULL);
+ CodeOffset pc;
+ pc = lir->offset + 2; // opcode + rel8
+ CodeOffset target = target_lir->offset;
+ int delta = target - pc;
+ lir->operands[0] = delta;
+ DCHECK(IS_SIMM8(delta));
+ break;
+ }
case kX86Jmp8: {
LIR *target_lir = lir->target;
DCHECK(target_lir != NULL);
@@ -1226,6 +1247,14 @@
DCHECK_EQ(0, entry->skeleton.ax_opcode);
DCHECK_EQ(0, entry->skeleton.immediate_bytes);
break;
+ case kPrefix2Nullary: // 1 byte of opcode + 2 prefixes.
+ DCHECK_NE(0, entry->skeleton.prefix1);
+ DCHECK_NE(0, entry->skeleton.prefix2);
+ EmitPrefixAndOpcode(entry);
+ DCHECK_EQ(0, entry->skeleton.modrm_opcode);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+ break;
case kRegOpcode: // lir operands - 0: reg
EmitOpRegOpcode(entry, lir->operands[0]);
break;
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 6100a1d..421d51e 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -359,6 +359,15 @@
void GenConstWide(RegLocation rl_dest, int64_t value);
/*
+ * @brief generate inline code for fast case of Strng.indexOf.
+ * @param info Call parameters
+ * @param zero_based 'true' if the index into the string is 0.
+ * @returns 'true' if the call was inlined, 'false' if a regular call needs to be
+ * generated.
+ */
+ bool GenInlinedIndexOf(CallInfo* info, bool zero_based);
+
+ /*
* @brief Return the correct x86 opcode for the Dex operation
* @param op Dex opcode for the operation
* @param loc Register location of the operand
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index fa9a944..eea7191 100644
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -20,6 +20,8 @@
#include "codegen_x86.h"
#include "dex/compiler_internals.h"
#include "dex/quick/mir_to_lir-inl.h"
+#include "mirror/array.h"
+#include "mirror/string.h"
#include "x86_lir.h"
namespace art {
@@ -185,6 +187,14 @@
if (flags & REG_USEB) {
SetupRegMask(&lir->u.m.use_mask, rBX);
}
+
+ // Fixup hard to describe instruction: Uses rAX, rCX, rDI; sets rDI.
+ if (lir->opcode == kX86RepneScasw) {
+ SetupRegMask(&lir->u.m.use_mask, rAX);
+ SetupRegMask(&lir->u.m.use_mask, rCX);
+ SetupRegMask(&lir->u.m.use_mask, rDI);
+ SetupRegMask(&lir->u.m.def_mask, rDI);
+ }
}
/* For dumping instructions */
@@ -936,4 +946,176 @@
Mir2Lir::InstallLiteralPools();
}
+/*
+ * Fast string.index_of(I) & (II). Inline check for simple case of char <= 0xffff,
+ * otherwise bails to standard library code.
+ */
+bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
+ ClobberCallerSave();
+ LockCallTemps(); // Using fixed registers
+
+ // EAX: 16 bit character being searched.
+ // ECX: count: number of words to be searched.
+ // EDI: String being searched.
+ // EDX: temporary during execution.
+ // EBX: temporary during execution.
+
+ RegLocation rl_obj = info->args[0];
+ RegLocation rl_char = info->args[1];
+ RegLocation rl_start = info->args[2];
+
+ uint32_t char_value =
+ rl_char.is_const ? mir_graph_->ConstantValue(rl_char.orig_sreg) : 0;
+
+ if (char_value > 0xFFFF) {
+ // We have to punt to the real String.indexOf.
+ return false;
+ }
+
+ // Okay, we are commited to inlining this.
+ RegLocation rl_return = GetReturn(false);
+ RegLocation rl_dest = InlineTarget(info);
+
+ // Is the string non-NULL?
+ LoadValueDirectFixed(rl_obj, rDX);
+ GenNullCheck(rl_obj.s_reg_low, rDX, info->opt_flags);
+
+ // Record that we have inlined & null checked the object.
+ info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK);
+
+ // Does the character fit in 16 bits?
+ LIR* launch_pad = nullptr;
+ if (rl_char.is_const) {
+ // We need the value in EAX.
+ LoadConstantNoClobber(rAX, char_value);
+ } else {
+ // Character is not a constant; compare at runtime.
+ LoadValueDirectFixed(rl_char, rAX);
+ launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info));
+ intrinsic_launchpads_.Insert(launch_pad);
+ OpCmpImmBranch(kCondGt, rAX, 0xFFFF, launch_pad);
+ }
+
+ // From here down, we know that we are looking for a char that fits in 16 bits.
+ // Location of reference to data array within the String object.
+ int value_offset = mirror::String::ValueOffset().Int32Value();
+ // Location of count within the String object.
+ int count_offset = mirror::String::CountOffset().Int32Value();
+ // Starting offset within data array.
+ int offset_offset = mirror::String::OffsetOffset().Int32Value();
+ // Start of char data with array_.
+ int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value();
+
+ // Character is in EAX.
+ // Object pointer is in EDX.
+
+ // We need to preserve EDI, but have no spare registers, so push it on the stack.
+ // We have to remember that all stack addresses after this are offset by sizeof(EDI).
+ NewLIR1(kX86Push32R, rDI);
+
+ // Compute the number of words to search in to rCX.
+ LoadWordDisp(rDX, count_offset, rCX);
+ LIR *length_compare = nullptr;
+ int start_value = 0;
+ if (zero_based) {
+ // We have to handle an empty string. Use special instruction JECXZ.
+ length_compare = NewLIR0(kX86Jecxz8);
+ } else {
+ // We have to offset by the start index.
+ if (rl_start.is_const) {
+ start_value = mir_graph_->ConstantValue(rl_start.orig_sreg);
+ start_value = std::max(start_value, 0);
+
+ // Is the start > count?
+ length_compare = OpCmpImmBranch(kCondLe, rCX, start_value, nullptr);
+
+ if (start_value != 0) {
+ OpRegImm(kOpSub, rCX, start_value);
+ }
+ } else {
+ // Runtime start index.
+ rl_start = UpdateLoc(rl_start);
+ if (rl_start.location == kLocPhysReg) {
+ length_compare = OpCmpBranch(kCondLe, rCX, rl_start.low_reg, nullptr);
+ OpRegReg(kOpSub, rCX, rl_start.low_reg);
+ } else {
+ // Compare to memory to avoid a register load. Handle pushed EDI.
+ int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t);
+ OpRegMem(kOpCmp, rDX, rX86_SP, displacement);
+ length_compare = NewLIR2(kX86Jcc8, 0, kX86CondLe);
+ OpRegMem(kOpSub, rCX, rX86_SP, displacement);
+ }
+ }
+ }
+ DCHECK(length_compare != nullptr);
+
+ // ECX now contains the count in words to be searched.
+
+ // Load the address of the string into EBX.
+ // The string starts at VALUE(String) + 2 * OFFSET(String) + DATA_OFFSET.
+ LoadWordDisp(rDX, value_offset, rDI);
+ LoadWordDisp(rDX, offset_offset, rBX);
+ OpLea(rBX, rDI, rBX, 1, data_offset);
+
+ // Now compute into EDI where the search will start.
+ if (zero_based || rl_start.is_const) {
+ if (start_value == 0) {
+ OpRegCopy(rDI, rBX);
+ } else {
+ NewLIR3(kX86Lea32RM, rDI, rBX, 2 * start_value);
+ }
+ } else {
+ if (rl_start.location == kLocPhysReg) {
+ if (rl_start.low_reg == rDI) {
+ // We have a slight problem here. We are already using RDI!
+ // Grab the value from the stack.
+ LoadWordDisp(rX86_SP, 0, rDX);
+ OpLea(rDI, rBX, rDX, 1, 0);
+ } else {
+ OpLea(rDI, rBX, rl_start.low_reg, 1, 0);
+ }
+ } else {
+ OpRegCopy(rDI, rBX);
+ // Load the start index from stack, remembering that we pushed EDI.
+ int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t);
+ LoadWordDisp(rX86_SP, displacement, rDX);
+ OpLea(rDI, rBX, rDX, 1, 0);
+ }
+ }
+
+ // EDI now contains the start of the string to be searched.
+ // We are all prepared to do the search for the character.
+ NewLIR0(kX86RepneScasw);
+
+ // Did we find a match?
+ LIR* failed_branch = OpCondBranch(kCondNe, nullptr);
+
+ // yes, we matched. Compute the index of the result.
+ // index = ((curr_ptr - orig_ptr) / 2) - 1.
+ OpRegReg(kOpSub, rDI, rBX);
+ OpRegImm(kOpAsr, rDI, 1);
+ NewLIR3(kX86Lea32RM, rl_return.low_reg, rDI, -1);
+ LIR *all_done = NewLIR1(kX86Jmp8, 0);
+
+ // Failed to match; return -1.
+ LIR *not_found = NewLIR0(kPseudoTargetLabel);
+ length_compare->target = not_found;
+ failed_branch->target = not_found;
+ LoadConstantNoClobber(rl_return.low_reg, -1);
+
+ // And join up at the end.
+ all_done->target = NewLIR0(kPseudoTargetLabel);
+ // Restore EDI from the stack.
+ NewLIR1(kX86Pop32R, rDI);
+
+ // Out of line code returns here.
+ if (launch_pad != nullptr) {
+ LIR *return_point = NewLIR0(kPseudoTargetLabel);
+ launch_pad->operands[2] = WrapPointer(return_point);
+ }
+
+ StoreValue(rl_dest, rl_return);
+ return true;
+}
+
} // namespace art
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index 480d5f5..4064bd6 100644
--- a/compiler/dex/quick/x86/x86_lir.h
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -277,6 +277,7 @@
kX86Mov32MR, kX86Mov32AR, kX86Mov32TR,
kX86Mov32RR, kX86Mov32RM, kX86Mov32RA, kX86Mov32RT,
kX86Mov32RI, kX86Mov32MI, kX86Mov32AI, kX86Mov32TI,
+ kX86Lea32RM,
kX86Lea32RA,
// RRC - Register Register ConditionCode - cond_opcode reg1, reg2
// - lir operands - 0: reg1, 1: reg2, 2: CC
@@ -384,6 +385,7 @@
kX86Jcc8, kX86Jcc32, // jCC rel8/32; lir operands - 0: rel, 1: CC, target assigned
kX86Jmp8, kX86Jmp32, // jmp rel8/32; lir operands - 0: rel, target assigned
kX86JmpR, // jmp reg; lir operands - 0: reg
+ kX86Jecxz8, // jcexz rel8; jump relative if ECX is zero.
kX86CallR, // call reg; lir operands - 0: reg
kX86CallM, // call [base + disp]; lir operands - 0: base, 1: disp
kX86CallA, // call [base + index * scale + disp]
@@ -396,6 +398,7 @@
kX86PcRelLoadRA, // mov reg, [base + index * scale + PC relative displacement]
// lir operands - 0: reg, 1: base, 2: index, 3: scale, 4: table
kX86PcRelAdr, // mov reg, PC relative displacement; lir operands - 0: reg, 1: table
+ kX86RepneScasw, // repne scasw
kX86Last
};
@@ -404,6 +407,7 @@
kData, // Special case for raw data.
kNop, // Special case for variable length nop.
kNullary, // Opcode that takes no arguments.
+ kPrefix2Nullary, // Opcode that takes no arguments, but 2 prefixes.
kRegOpcode, // Shorter form of R instruction kind (opcode+rd)
kReg, kMem, kArray, // R, M and A instruction kinds.
kMemReg, kArrayReg, kThreadReg, // MR, AR and TR instruction kinds.
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index 502df1e..0f79f41 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -248,22 +248,11 @@
}
/* Calculate DF_up */
- ArenaBitVector::Iterator bv_iterator(bb->i_dominated);
- while (true) {
- // TUNING: hot call to BitVectorIteratorNext
- int dominated_idx = bv_iterator.Next();
- if (dominated_idx == -1) {
- break;
- }
- BasicBlock* dominated_bb = GetBasicBlock(dominated_idx);
- ArenaBitVector::Iterator df_iterator(dominated_bb->dom_frontier);
- while (true) {
- // TUNING: hot call to BitVectorIteratorNext
- int df_up_idx = df_iterator.Next();
- if (df_up_idx == -1) {
- break;
- }
- BasicBlock* df_up_block = GetBasicBlock(df_up_idx);
+ ArenaBitVector::BasicBlockIterator it(bb->i_dominated, cu_);
+ for (BasicBlock *dominated_bb = it.Next(); dominated_bb != nullptr; dominated_bb = it.Next()) {
+ ArenaBitVector::BasicBlockIterator inner_it(dominated_bb->dom_frontier, cu_);
+ for (BasicBlock *df_up_block = inner_it.Next(); df_up_block != nullptr;
+ df_up_block = inner_it.Next()) {
CheckForDominanceFrontier(bb, df_up_block);
}
}
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index edccec5..947c22d 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -19,6 +19,8 @@
#include "base/stl_util.h"
#include "base/mutex.h"
#include "base/mutex-inl.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "thread.h"
#include "thread-inl.h"
#include "verified_method.h"
@@ -27,8 +29,9 @@
namespace art {
-VerificationResults::VerificationResults()
- : verified_methods_lock_("compiler verified methods lock"),
+VerificationResults::VerificationResults(const CompilerOptions* compiler_options)
+ : compiler_options_(compiler_options),
+ verified_methods_lock_("compiler verified methods lock"),
verified_methods_(),
rejected_classes_lock_("compiler rejected classes lock"),
rejected_classes_() {
@@ -43,6 +46,7 @@
}
bool VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
+ DCHECK(method_verifier != NULL);
MethodReference ref = method_verifier->GetMethodReference();
bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
// TODO: Check also for virtual/interface invokes when DEX-to-DEX supports devirtualization.
@@ -95,16 +99,18 @@
bool VerificationResults::IsCandidateForCompilation(MethodReference& method_ref,
const uint32_t access_flags) {
#ifdef ART_SEA_IR_MODE
- bool use_sea = Runtime::Current()->IsSeaIRMode();
- use_sea = use_sea && (std::string::npos != PrettyMethod(
- method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
- if (use_sea) return true;
+ bool use_sea = compiler_options_->GetSeaIrMode();
+ use_sea = use_sea && (std::string::npos != PrettyMethod(
+ method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
+ if (use_sea) {
+ return true;
+ }
#endif
// Don't compile class initializers, ever.
if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
return false;
}
- return (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly);
+ return (compiler_options_->GetCompilerFilter() != CompilerOptions::kInterpretOnly);
}
} // namespace art
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 2eb0713..278182f 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -33,11 +33,13 @@
class MethodVerifier;
} // namespace verifier
+class CompilerOptions;
class VerifiedMethod;
+// Used by CompilerCallbacks to track verification information from the Runtime.
class VerificationResults {
public:
- VerificationResults();
+ explicit VerificationResults(const CompilerOptions* compiler_options);
~VerificationResults();
bool ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
@@ -50,15 +52,17 @@
void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
- static bool IsCandidateForCompilation(MethodReference& method_ref,
- const uint32_t access_flags);
+ bool IsCandidateForCompilation(MethodReference& method_ref,
+ const uint32_t access_flags);
private:
+ const CompilerOptions* compiler_options_;
+
// Verified methods.
typedef SafeMap<MethodReference, const VerifiedMethod*,
MethodReferenceComparator> VerifiedMethodMap;
ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- VerifiedMethodMap verified_methods_;
+ VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_);
// Rejected classes.
ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/compiler/driver/compiler_callbacks_impl.h b/compiler/driver/compiler_callbacks_impl.h
new file mode 100644
index 0000000..fd2cd4a
--- /dev/null
+++ b/compiler/driver/compiler_callbacks_impl.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_
+#define ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_
+
+#include "compiler_callbacks.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "verifier/method_verifier-inl.h"
+
+namespace art {
+
+class CompilerCallbacksImpl : public CompilerCallbacks {
+ public:
+ CompilerCallbacksImpl(VerificationResults* verification_results,
+ DexFileToMethodInlinerMap* method_inliner_map)
+ : verification_results_(verification_results),
+ method_inliner_map_(method_inliner_map) {
+ CHECK(verification_results != nullptr);
+ CHECK(method_inliner_map != nullptr);
+ }
+
+ virtual ~CompilerCallbacksImpl() { }
+
+ virtual bool MethodVerified(verifier::MethodVerifier* verifier)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool result = verification_results_->ProcessVerifiedMethod(verifier);
+ if (result) {
+ MethodReference ref = verifier->GetMethodReference();
+ method_inliner_map_->GetMethodInliner(ref.dex_file)
+ ->AnalyseMethodCode(verifier);
+ }
+ return result;
+ }
+ virtual void ClassRejected(ClassReference ref) {
+ verification_results_->AddRejectedClass(ref);
+ }
+
+ private:
+ VerificationResults* const verification_results_;
+ DexFileToMethodInlinerMap* const method_inliner_map_;
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_COMPILER_CALLBACKS_IMPL_H_
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 5adb792..1b284de 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -293,14 +293,16 @@
jobject class_loader,
const art::DexFile& dex_file);
-CompilerDriver::CompilerDriver(VerificationResults* verification_results,
+CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options,
+ VerificationResults* verification_results,
DexFileToMethodInlinerMap* method_inliner_map,
CompilerBackend::Kind compiler_backend_kind,
InstructionSet instruction_set,
InstructionSetFeatures instruction_set_features,
bool image, DescriptorSet* image_classes, size_t thread_count,
bool dump_stats, bool dump_passes, CumulativeLogger* timer)
- : verification_results_(verification_results),
+ : compiler_options_(compiler_options),
+ verification_results_(verification_results),
method_inliner_map_(method_inliner_map),
compiler_backend_(CompilerBackend::Create(compiler_backend_kind)),
instruction_set_(instruction_set),
@@ -325,6 +327,9 @@
dedupe_mapping_table_("dedupe mapping table"),
dedupe_vmap_table_("dedupe vmap table"),
dedupe_gc_map_("dedupe gc map") {
+ DCHECK(compiler_options_ != nullptr);
+ DCHECK(verification_results_ != nullptr);
+ DCHECK(method_inliner_map_ != nullptr);
CHECK_PTHREAD_CALL(pthread_key_create, (&tls_key_, NULL), "compiler tls key");
@@ -447,13 +452,12 @@
}
static DexToDexCompilationLevel GetDexToDexCompilationlevel(
- SirtRef<mirror::ClassLoader>& class_loader, const DexFile& dex_file,
+ Thread* self, SirtRef<mirror::ClassLoader>& class_loader, const DexFile& dex_file,
const DexFile::ClassDef& class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const char* descriptor = dex_file.GetClassDescriptor(class_def);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- mirror::Class* klass = class_linker->FindClass(descriptor, class_loader);
+ mirror::Class* klass = class_linker->FindClass(self, descriptor, class_loader);
if (klass == NULL) {
- Thread* self = Thread::Current();
CHECK(self->IsExceptionPending());
self->ClearException();
return kDontDexToDexCompile;
@@ -515,7 +519,8 @@
const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
soa.Decode<mirror::ClassLoader*>(jclass_loader));
- dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, *dex_file, class_def);
+ dex_to_dex_compilation_level = GetDexToDexCompilationlevel(self, class_loader, *dex_file,
+ class_def);
}
CompileMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx, jclass_loader,
*dex_file, dex_to_dex_compilation_level);
@@ -633,7 +638,7 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
for (auto it = image_classes_->begin(), end = image_classes_->end(); it != end;) {
const std::string& descriptor(*it);
- SirtRef<mirror::Class> klass(self, class_linker->FindSystemClass(descriptor.c_str()));
+ SirtRef<mirror::Class> klass(self, class_linker->FindSystemClass(self, descriptor.c_str()));
if (klass.get() == NULL) {
VLOG(compiler) << "Failed to find class " << descriptor;
image_classes_->erase(it++);
@@ -648,7 +653,7 @@
// Do this here so that exception classes appear to have been specified image classes.
std::set<std::pair<uint16_t, const DexFile*> > unresolved_exception_types;
SirtRef<mirror::Class> java_lang_Throwable(self,
- class_linker->FindSystemClass("Ljava/lang/Throwable;"));
+ class_linker->FindSystemClass(self, "Ljava/lang/Throwable;"));
do {
unresolved_exception_types.clear();
class_linker->VisitClasses(ResolveCatchBlockExceptionsClassVisitor,
@@ -1661,7 +1666,8 @@
jobject jclass_loader = manager->GetClassLoader();
SirtRef<mirror::ClassLoader> class_loader(
soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader));
- SirtRef<mirror::Class> klass(soa.Self(), class_linker->FindClass(descriptor, class_loader));
+ SirtRef<mirror::Class> klass(soa.Self(), class_linker->FindClass(soa.Self(), descriptor,
+ class_loader));
if (klass.get() == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
@@ -1716,7 +1722,8 @@
SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
soa.Decode<mirror::ClassLoader*>(jclass_loader));
SirtRef<mirror::Class> klass(soa.Self(),
- manager->GetClassLinker()->FindClass(descriptor, class_loader));
+ manager->GetClassLinker()->FindClass(soa.Self(), descriptor,
+ class_loader));
if (klass.get() != nullptr && !SkipClass(jclass_loader, dex_file, klass.get())) {
// Only try to initialize classes that were successfully verified.
@@ -1861,7 +1868,8 @@
ScopedObjectAccess soa(Thread::Current());
SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
soa.Decode<mirror::ClassLoader*>(jclass_loader));
- dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, dex_file, class_def);
+ dex_to_dex_compilation_level = GetDexToDexCompilationlevel(soa.Self(), class_loader, dex_file,
+ class_def);
}
ClassDataItemIterator it(dex_file, class_data);
// Skip fields
@@ -1929,7 +1937,7 @@
} else if ((access_flags & kAccAbstract) != 0) {
} else {
MethodReference method_ref(&dex_file, method_idx);
- bool compile = VerificationResults::IsCandidateForCompilation(method_ref, access_flags);
+ bool compile = verification_results_->IsCandidateForCompilation(method_ref, access_flags);
if (compile) {
// NOTE: if compiler declines to compile this method, it will return NULL.
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 092fe52..377eb6f 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -45,6 +45,7 @@
} // namespace verifier
class AOTCompilationStats;
+class CompilerOptions;
class DexCompilationUnit;
class DexFileToMethodInlinerMap;
class InlineIGetIPutData;
@@ -94,7 +95,8 @@
// enabled. "image_classes" lets the compiler know what classes it
// can assume will be in the image, with NULL implying all available
// classes.
- explicit CompilerDriver(VerificationResults* verification_results,
+ explicit CompilerDriver(const CompilerOptions* compiler_options,
+ VerificationResults* verification_results,
DexFileToMethodInlinerMap* method_inliner_map,
CompilerBackend::Kind compiler_backend_kind,
InstructionSet instruction_set,
@@ -129,6 +131,11 @@
return instruction_set_features_;
}
+ const CompilerOptions& GetCompilerOptions() const {
+ DCHECK(compiler_options_ != nullptr);
+ return *compiler_options_;
+ }
+
CompilerBackend* GetCompilerBackend() const {
return compiler_backend_.get();
}
@@ -551,8 +558,9 @@
std::vector<const CallPatchInformation*> methods_to_patch_;
std::vector<const TypePatchInformation*> classes_to_patch_;
- VerificationResults* verification_results_;
- DexFileToMethodInlinerMap* method_inliner_map_;
+ const CompilerOptions* const compiler_options_;
+ VerificationResults* const verification_results_;
+ DexFileToMethodInlinerMap* const method_inliner_map_;
UniquePtr<CompilerBackend> compiler_backend_;
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 0d0c204..ec0a8bd 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -79,9 +79,8 @@
const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
const char* descriptor = dex_file.GetClassDescriptor(class_def);
ScopedObjectAccess soa(Thread::Current());
- Thread* self = Thread::Current();
- SirtRef<mirror::ClassLoader> loader(self, soa.Decode<mirror::ClassLoader*>(class_loader));
- mirror::Class* c = class_linker->FindClass(descriptor, loader);
+ SirtRef<mirror::ClassLoader> loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(class_loader));
+ mirror::Class* c = class_linker->FindClass(soa.Self(), descriptor, loader);
CHECK(c != NULL);
for (size_t i = 0; i < c->NumDirectMethods(); i++) {
MakeExecutable(c->GetDirectMethod(i));
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
new file mode 100644
index 0000000..9f6745b
--- /dev/null
+++ b/compiler/driver/compiler_options.h
@@ -0,0 +1,136 @@
+/*
+ * 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_DRIVER_COMPILER_OPTIONS_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
+
+namespace art {
+
+class CompilerOptions {
+ public:
+ enum CompilerFilter {
+ kInterpretOnly, // Compile nothing.
+ kSpace, // Maximize space savings.
+ kBalanced, // Try to get the best performance return on compilation investment.
+ kSpeed, // Maximize runtime performance.
+ kEverything // Force compilation (Note: excludes compilaton of class initializers).
+ };
+
+ // Guide heuristics to determine whether to compile method if profile data not available.
+ static const CompilerFilter kDefaultCompilerFilter = kSpeed;
+ static const size_t kDefaultHugeMethodThreshold = 10000;
+ static const size_t kDefaultLargeMethodThreshold = 600;
+ static const size_t kDefaultSmallMethodThreshold = 60;
+ static const size_t kDefaultTinyMethodThreshold = 20;
+ static const size_t kDefaultNumDexMethodsThreshold = 900;
+
+ CompilerOptions() :
+ compiler_filter_(kDefaultCompilerFilter),
+ huge_method_threshold_(kDefaultHugeMethodThreshold),
+ large_method_threshold_(kDefaultLargeMethodThreshold),
+ small_method_threshold_(kDefaultSmallMethodThreshold),
+ tiny_method_threshold_(kDefaultTinyMethodThreshold),
+ num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold)
+#ifdef ART_SEA_IR_MODE
+ , sea_ir_mode_(false)
+#endif
+ {}
+
+ CompilerOptions(CompilerFilter compiler_filter,
+ size_t huge_method_threshold,
+ size_t large_method_threshold,
+ size_t small_method_threshold,
+ size_t tiny_method_threshold,
+ size_t num_dex_methods_threshold
+#ifdef ART_SEA_IR_MODE
+ , bool sea_ir_mode
+#endif
+ ) : // NOLINT(whitespace/parens)
+ compiler_filter_(compiler_filter),
+ huge_method_threshold_(huge_method_threshold),
+ large_method_threshold_(large_method_threshold),
+ small_method_threshold_(small_method_threshold),
+ tiny_method_threshold_(tiny_method_threshold),
+ num_dex_methods_threshold_(num_dex_methods_threshold)
+#ifdef ART_SEA_IR_MODE
+ , sea_ir_mode_(sea_ir_mode)
+#endif
+ {}
+
+ CompilerFilter GetCompilerFilter() const {
+ return compiler_filter_;
+ }
+
+ void SetCompilerFilter(CompilerFilter compiler_filter) {
+ compiler_filter_ = compiler_filter;
+ }
+
+ size_t GetHugeMethodThreshold() const {
+ return huge_method_threshold_;
+ }
+
+ size_t GetLargeMethodThreshold() const {
+ return large_method_threshold_;
+ }
+
+ size_t GetSmallMethodThreshold() const {
+ return small_method_threshold_;
+ }
+
+ size_t GetTinyMethodThreshold() const {
+ return tiny_method_threshold_;
+ }
+
+ bool IsHugeMethod(size_t num_dalvik_instructions) const {
+ return num_dalvik_instructions > huge_method_threshold_;
+ }
+
+ bool IsLargeMethod(size_t num_dalvik_instructions) const {
+ return num_dalvik_instructions > large_method_threshold_;
+ }
+
+ bool IsSmallMethod(size_t num_dalvik_instructions) const {
+ return num_dalvik_instructions > small_method_threshold_;
+ }
+
+ bool IsTinyMethod(size_t num_dalvik_instructions) const {
+ return num_dalvik_instructions > tiny_method_threshold_;
+ }
+
+ size_t GetNumDexMethodsThreshold() const {
+ return num_dex_methods_threshold_;
+ }
+
+#ifdef ART_SEA_IR_MODE
+ bool GetSeaIrMode();
+#endif
+
+ private:
+ CompilerFilter compiler_filter_;
+ size_t huge_method_threshold_;
+ size_t large_method_threshold_;
+ size_t small_method_threshold_;
+ size_t tiny_method_threshold_;
+ size_t num_dex_methods_threshold_;
+
+#ifdef ART_SEA_IR_MODE
+ bool sea_ir_mode_;
+#endif
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_COMPILER_OPTIONS_H_
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 3406fe6..49cabdc 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -151,7 +151,7 @@
for (size_t i = 0; i < dex->NumClassDefs(); ++i) {
const DexFile::ClassDef& class_def = dex->GetClassDef(i);
const char* descriptor = dex->GetClassDescriptor(class_def);
- mirror::Class* klass = class_linker_->FindSystemClass(descriptor);
+ mirror::Class* klass = class_linker_->FindSystemClass(soa.Self(), descriptor);
EXPECT_TRUE(klass != nullptr) << descriptor;
if (image_classes.find(descriptor) != image_classes.end()) {
// Image classes should be located inside the image.
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 60beebb..c8447be 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -401,7 +401,8 @@
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
Thread* self = Thread::Current();
- SirtRef<Class> object_array_class(self, class_linker->FindSystemClass("[Ljava/lang/Object;"));
+ SirtRef<Class> object_array_class(self, class_linker->FindSystemClass(self,
+ "[Ljava/lang/Object;"));
// build an Object[] of all the DexCaches used in the source_space_
ObjectArray<Object>* dex_caches = ObjectArray<Object>::Alloc(self, object_array_class.get(),
@@ -581,14 +582,15 @@
void ImageWriter::FixupObject(Object* orig, Object* copy) {
DCHECK(orig != NULL);
DCHECK(copy != NULL);
- copy->SetClass(down_cast<Class*>(GetImageAddress(orig->GetClass())));
+ copy->SetClass<kVerifyNone>(down_cast<Class*>(GetImageAddress(orig->GetClass())));
// TODO: special case init of pointers to malloc data (or removal of these pointers)
- if (orig->IsClass()) {
- FixupClass(orig->AsClass(), down_cast<Class*>(copy));
- } else if (orig->IsObjectArray()) {
- FixupObjectArray(orig->AsObjectArray<Object>(), down_cast<ObjectArray<Object>*>(copy));
- } else if (orig->IsArtMethod()) {
- FixupMethod(orig->AsArtMethod(), down_cast<ArtMethod*>(copy));
+ if (orig->IsClass<kVerifyNone>()) {
+ FixupClass(orig->AsClass<kVerifyNone>(), down_cast<Class*>(copy));
+ } else if (orig->IsObjectArray<kVerifyNone>()) {
+ FixupObjectArray(orig->AsObjectArray<Object, kVerifyNone>(),
+ down_cast<ObjectArray<Object>*>(copy));
+ } else if (orig->IsArtMethod<kVerifyNone>()) {
+ FixupMethod(orig->AsArtMethod<kVerifyNone>(), down_cast<ArtMethod*>(copy));
} else {
FixupInstanceFields(orig, copy);
}
@@ -607,54 +609,54 @@
// The resolution method has a special trampoline to call.
if (UNLIKELY(orig == Runtime::Current()->GetResolutionMethod())) {
- copy->SetEntryPointFromPortableCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_));
- copy->SetEntryPointFromQuickCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_));
+ copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_resolution_trampoline_offset_));
+ copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_));
} else if (UNLIKELY(orig == Runtime::Current()->GetImtConflictMethod())) {
- copy->SetEntryPointFromPortableCompiledCode(GetOatAddress(portable_imt_conflict_trampoline_offset_));
- copy->SetEntryPointFromQuickCompiledCode(GetOatAddress(quick_imt_conflict_trampoline_offset_));
+ copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_imt_conflict_trampoline_offset_));
+ copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_imt_conflict_trampoline_offset_));
} 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(GetOatAddress(portable_to_interpreter_bridge_offset_));
- copy->SetEntryPointFromQuickCompiledCode(GetOatAddress(quick_to_interpreter_bridge_offset_));
- copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*>
+ 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<byte*>(GetOatAddress(interpreter_to_interpreter_bridge_offset_))));
} else {
- copy->SetEntryPointFromInterpreter(reinterpret_cast<EntryPointFromInterpreter*>
+ copy->SetEntryPointFromInterpreter<kVerifyNone>(reinterpret_cast<EntryPointFromInterpreter*>
(const_cast<byte*>(GetOatAddress(interpreter_to_compiled_code_bridge_offset_))));
// Use original code if it exists. Otherwise, set the code pointer to the resolution
// trampoline.
const byte* quick_code = GetOatAddress(orig->GetQuickOatCodeOffset());
if (quick_code != nullptr) {
- copy->SetEntryPointFromQuickCompiledCode(quick_code);
+ copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(quick_code);
} else {
- copy->SetEntryPointFromQuickCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_));
+ copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_));
}
const byte* portable_code = GetOatAddress(orig->GetPortableOatCodeOffset());
if (portable_code != nullptr) {
- copy->SetEntryPointFromPortableCompiledCode(portable_code);
+ copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(portable_code);
} else {
- copy->SetEntryPointFromPortableCompiledCode(GetOatAddress(portable_resolution_trampoline_offset_));
+ copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_resolution_trampoline_offset_));
}
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(GetOatAddress(jni_dlsym_lookup_offset_));
+ copy->SetNativeMethod<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_));
} else {
// Normal (non-abstract non-native) methods have various tables to relocate.
uint32_t mapping_table_off = orig->GetOatMappingTableOffset();
const byte* mapping_table = GetOatAddress(mapping_table_off);
- copy->SetMappingTable(mapping_table);
+ copy->SetMappingTable<kVerifyNone>(mapping_table);
uint32_t vmap_table_offset = orig->GetOatVmapTableOffset();
const byte* vmap_table = GetOatAddress(vmap_table_offset);
- copy->SetVmapTable(vmap_table);
+ copy->SetVmapTable<kVerifyNone>(vmap_table);
uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset();
const byte* native_gc_map = GetOatAddress(native_gc_map_offset);
- copy->SetNativeGcMap(reinterpret_cast<const uint8_t*>(native_gc_map));
+ copy->SetNativeGcMap<kVerifyNone>(reinterpret_cast<const uint8_t*>(native_gc_map));
}
}
}
@@ -663,7 +665,7 @@
void ImageWriter::FixupObjectArray(ObjectArray<Object>* orig, ObjectArray<Object>* copy) {
for (int32_t i = 0; i < orig->GetLength(); ++i) {
Object* element = orig->Get(i);
- copy->SetWithoutChecksAndWriteBarrier<false>(i, GetImageAddress(element));
+ copy->SetWithoutChecksAndWriteBarrier<false, true, kVerifyNone>(i, GetImageAddress(element));
}
}
@@ -690,10 +692,11 @@
while (ref_offsets != 0) {
size_t right_shift = CLZ(ref_offsets);
MemberOffset byte_offset = CLASS_OFFSET_FROM_CLZ(right_shift);
- Object* ref = orig->GetFieldObject<Object>(byte_offset, false);
+ Object* ref = orig->GetFieldObject<Object, kVerifyNone>(byte_offset, false);
// Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the
// image.
- copy->SetFieldObjectWithoutWriteBarrier<false>(byte_offset, GetImageAddress(ref), false);
+ copy->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(
+ byte_offset, GetImageAddress(ref), false);
ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift);
}
} else {
@@ -712,10 +715,11 @@
? klass->GetStaticField(i)
: klass->GetInstanceField(i));
MemberOffset field_offset = field->GetOffset();
- Object* ref = orig->GetFieldObject<Object>(field_offset, false);
+ Object* ref = orig->GetFieldObject<Object, kVerifyNone>(field_offset, false);
// Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the
// image.
- copy->SetFieldObjectWithoutWriteBarrier<false>(field_offset, GetImageAddress(ref), false);
+ copy->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(
+ field_offset, GetImageAddress(ref), false);
}
}
}
@@ -726,7 +730,8 @@
Object* ref = orig->GetFieldObject<Object>(field_offset, false);
// Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the
// image.
- copy->SetFieldObjectWithoutWriteBarrier<false>(field_offset, GetImageAddress(ref), false);
+ copy->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(
+ field_offset, GetImageAddress(ref), false);
}
}
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index c77d319..1bdff37 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -50,7 +50,7 @@
ScopedObjectAccess soa(Thread::Current());
SirtRef<mirror::ClassLoader> loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(class_loader));
// Compile the native method before starting the runtime
- mirror::Class* c = class_linker_->FindClass("LMyClassNatives;", loader);
+ mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader);
mirror::ArtMethod* method;
if (direct) {
method = c->FindDirectMethod(method_name, method_sig);
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 10d2c5c..e91ffcb 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -91,11 +91,14 @@
InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
InstructionSetFeatures insn_features;
- verification_results_.reset(new VerificationResults);
+ compiler_options_.reset(new CompilerOptions);
+ verification_results_.reset(new VerificationResults(compiler_options_.get()));
method_inliner_map_.reset(new DexFileToMethodInlinerMap);
- callbacks_.Reset(verification_results_.get(), method_inliner_map_.get());
+ callbacks_.reset(new CompilerCallbacksImpl(verification_results_.get(),
+ method_inliner_map_.get()));
timer_.reset(new CumulativeLogger("Compilation times"));
- compiler_driver_.reset(new CompilerDriver(verification_results_.get(),
+ compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+ verification_results_.get(),
method_inliner_map_.get(),
compiler_backend, insn_set,
insn_features, false, NULL, 2, true, true,
@@ -150,8 +153,8 @@
num_virtual_methods = it.NumVirtualMethods();
}
const char* descriptor = dex_file->GetClassDescriptor(class_def);
- SirtRef<mirror::ClassLoader> loader(Thread::Current(), nullptr);
- mirror::Class* klass = class_linker->FindClass(descriptor, loader);
+ SirtRef<mirror::ClassLoader> loader(soa.Self(), nullptr);
+ mirror::Class* klass = class_linker->FindClass(soa.Self(), descriptor, loader);
UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(i));
CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class->GetStatus()) << descriptor;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index dab419f..a082e36 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -34,7 +34,9 @@
#include "compiler_callbacks.h"
#include "dex_file-inl.h"
#include "dex/verification_results.h"
+#include "driver/compiler_callbacks_impl.h"
#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "elf_fixup.h"
#include "elf_stripper.h"
#include "gc/space/image_space.h"
@@ -54,15 +56,22 @@
#include "scoped_thread_state_change.h"
#include "sirt_ref.h"
#include "vector_output_stream.h"
-#include "verifier/method_verifier.h"
-#include "verifier/method_verifier-inl.h"
#include "well_known_classes.h"
#include "zip_archive.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
-
namespace art {
+static int original_argc;
+static char** original_argv;
+
+static std::string CommandLine() {
+ std::vector<std::string> command;
+ for (int i = 0; i < original_argc; ++i) {
+ command.push_back(original_argv[i]);
+ }
+ return Join(command, ' ');
+}
+
static void UsageErrorV(const char* fmt, va_list ap) {
std::string error;
StringAppendV(&error, fmt, ap);
@@ -82,6 +91,8 @@
UsageErrorV(fmt, ap);
va_end(ap);
+ UsageError("Command: %s", CommandLine().c_str());
+
UsageError("Usage: dex2oat [options]...");
UsageError("");
UsageError(" --dex-file=<dex-file>: specifies a .dex file to compile.");
@@ -147,6 +158,46 @@
UsageError(" Example: --compiler-backend=Portable");
UsageError(" Default: Quick");
UsageError("");
+ UsageError(" --compiler-filter=(interpret-only|space|balanced|speed|everything): select");
+ UsageError(" compiler filter.");
+ UsageError(" Example: --compiler-filter=everything");
+#if ART_SMALL_MODE
+ UsageError(" Default: interpret-only");
+#else
+ UsageError(" Default: speed");
+#endif
+ UsageError("");
+ UsageError(" --huge-method-max=<method-instruction-count>: the threshold size for a huge");
+ UsageError(" method for compiler filter tuning.");
+ UsageError(" Example: --huge-method-max=%d", CompilerOptions::kDefaultHugeMethodThreshold);
+ UsageError(" Default: %d", CompilerOptions::kDefaultHugeMethodThreshold);
+ UsageError("");
+ UsageError(" --huge-method-max=<method-instruction-count>: threshold size for a huge");
+ UsageError(" method for compiler filter tuning.");
+ UsageError(" Example: --huge-method-max=%d", CompilerOptions::kDefaultHugeMethodThreshold);
+ UsageError(" Default: %d", CompilerOptions::kDefaultHugeMethodThreshold);
+ UsageError("");
+ UsageError(" --large-method-max=<method-instruction-count>: threshold size for a large");
+ UsageError(" method for compiler filter tuning.");
+ UsageError(" Example: --large-method-max=%d", CompilerOptions::kDefaultLargeMethodThreshold);
+ UsageError(" Default: %d", CompilerOptions::kDefaultLargeMethodThreshold);
+ UsageError("");
+ UsageError(" --small-method-max=<method-instruction-count>: threshold size for a small");
+ UsageError(" method for compiler filter tuning.");
+ UsageError(" Example: --small-method-max=%d", CompilerOptions::kDefaultSmallMethodThreshold);
+ UsageError(" Default: %d", CompilerOptions::kDefaultSmallMethodThreshold);
+ UsageError("");
+ UsageError(" --tiny-method-max=<method-instruction-count>: threshold size for a tiny");
+ UsageError(" method for compiler filter tuning.");
+ UsageError(" Example: --tiny-method-max=%d", CompilerOptions::kDefaultTinyMethodThreshold);
+ UsageError(" Default: %d", CompilerOptions::kDefaultTinyMethodThreshold);
+ UsageError("");
+ UsageError(" --num-dex-methods=<method-count>: threshold size for a small dex file for");
+ UsageError(" compiler filter tuning. If the input has fewer than this many methods");
+ UsageError(" and the filter is not interpret-only, overrides the filter to use speed");
+ UsageError(" Example: --num-dex-method=%d", CompilerOptions::kDefaultNumDexMethodsThreshold);
+ UsageError(" Default: %d", CompilerOptions::kDefaultNumDexMethodsThreshold);
+ UsageError("");
UsageError(" --host: used with Portable backend to link against host runtime libraries");
UsageError("");
UsageError(" --dump-timing: display a breakdown of where time was spent");
@@ -163,15 +214,25 @@
class Dex2Oat {
public:
static bool Create(Dex2Oat** p_dex2oat,
- Runtime::Options& options,
+ const Runtime::Options& runtime_options,
+ const CompilerOptions& compiler_options,
CompilerBackend::Kind compiler_backend,
InstructionSet instruction_set,
InstructionSetFeatures instruction_set_features,
+ VerificationResults* verification_results,
+ DexFileToMethodInlinerMap* method_inliner_map,
size_t thread_count)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
- UniquePtr<Dex2Oat> dex2oat(new Dex2Oat(compiler_backend, instruction_set,
- instruction_set_features, thread_count));
- if (!dex2oat->CreateRuntime(options, instruction_set)) {
+ CHECK(verification_results != nullptr);
+ CHECK(method_inliner_map != nullptr);
+ UniquePtr<Dex2Oat> dex2oat(new Dex2Oat(&compiler_options,
+ compiler_backend,
+ instruction_set,
+ instruction_set_features,
+ verification_results,
+ method_inliner_map,
+ thread_count));
+ if (!dex2oat->CreateRuntime(runtime_options, instruction_set)) {
*p_dex2oat = NULL;
return false;
}
@@ -275,8 +336,9 @@
Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files);
}
- UniquePtr<CompilerDriver> driver(new CompilerDriver(verification_results_.get(),
- method_inliner_map_.get(),
+ UniquePtr<CompilerDriver> driver(new CompilerDriver(compiler_options_,
+ verification_results_,
+ method_inliner_map_,
compiler_backend_,
instruction_set_,
instruction_set_features_,
@@ -353,53 +415,30 @@
}
private:
- class Dex2OatCompilerCallbacks : public CompilerCallbacks {
- public:
- Dex2OatCompilerCallbacks(VerificationResults* verification_results,
- DexFileToMethodInlinerMap* method_inliner_map)
- : verification_results_(verification_results),
- method_inliner_map_(method_inliner_map) { }
- virtual ~Dex2OatCompilerCallbacks() { }
-
- virtual bool MethodVerified(verifier::MethodVerifier* verifier)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- bool result = verification_results_->ProcessVerifiedMethod(verifier);
- if (result) {
- MethodReference ref = verifier->GetMethodReference();
- method_inliner_map_->GetMethodInliner(ref.dex_file)
- ->AnalyseMethodCode(verifier);
- }
- return result;
- }
- virtual void ClassRejected(ClassReference ref) {
- verification_results_->AddRejectedClass(ref);
- }
-
- private:
- VerificationResults* verification_results_;
- DexFileToMethodInlinerMap* method_inliner_map_;
- };
-
- explicit Dex2Oat(CompilerBackend::Kind compiler_backend,
+ explicit Dex2Oat(const CompilerOptions* compiler_options,
+ CompilerBackend::Kind compiler_backend,
InstructionSet instruction_set,
InstructionSetFeatures instruction_set_features,
+ VerificationResults* verification_results,
+ DexFileToMethodInlinerMap* method_inliner_map,
size_t thread_count)
- : compiler_backend_(compiler_backend),
+ : compiler_options_(compiler_options),
+ compiler_backend_(compiler_backend),
instruction_set_(instruction_set),
instruction_set_features_(instruction_set_features),
- verification_results_(new VerificationResults),
- method_inliner_map_(new DexFileToMethodInlinerMap),
- callbacks_(verification_results_.get(), method_inliner_map_.get()),
+ verification_results_(verification_results),
+ method_inliner_map_(method_inliner_map),
runtime_(nullptr),
thread_count_(thread_count),
start_ns_(NanoTime()) {
+ CHECK(compiler_options != nullptr);
+ CHECK(verification_results != nullptr);
+ CHECK(method_inliner_map != nullptr);
}
- bool CreateRuntime(Runtime::Options& options, InstructionSet instruction_set)
+ bool CreateRuntime(const Runtime::Options& runtime_options, InstructionSet instruction_set)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
- options.push_back(
- std::make_pair("compilercallbacks", static_cast<CompilerCallbacks*>(&callbacks_)));
- if (!Runtime::Create(options, false)) {
+ if (!Runtime::Create(runtime_options, false)) {
LOG(ERROR) << "Failed to create runtime";
return false;
}
@@ -448,14 +487,14 @@
return false;
}
+ const CompilerOptions* const compiler_options_;
const CompilerBackend::Kind compiler_backend_;
const InstructionSet instruction_set_;
const InstructionSetFeatures instruction_set_features_;
- UniquePtr<VerificationResults> verification_results_;
- UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_;
- Dex2OatCompilerCallbacks callbacks_;
+ VerificationResults* const verification_results_;
+ DexFileToMethodInlinerMap* const method_inliner_map_;
Runtime* runtime_;
size_t thread_count_;
uint64_t start_ns_;
@@ -579,7 +618,7 @@
CHECK_GT(kWatchDogTimeoutSeconds, kWatchDogWarningSeconds);
// TODO: tune the multiplier for GC verification, the following is just to make the timeout
// large.
- int64_t multiplier = gc::kDesiredHeapVerification > gc::kVerifyAllFast ? 100 : 1;
+ int64_t multiplier = kVerifyObjectSupport > kVerifyObjectModeFast ? 100 : 1;
timespec warning_ts;
InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogWarningSeconds * 1000, 0, &warning_ts);
timespec timeout_ts;
@@ -656,6 +695,9 @@
}
static int dex2oat(int argc, char** argv) {
+ original_argc = argc;
+ original_argv = argv;
+
TimingLogger timings("compiler", false, false);
CumulativeLogger compiler_phases_timings("compilation times");
@@ -690,6 +732,12 @@
CompilerBackend::Kind compiler_backend = kUsePortableCompiler
? CompilerBackend::kPortable
: CompilerBackend::kQuick;
+ const char* compiler_filter_string = NULL;
+ int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold;
+ int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold;
+ int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold;
+ int tiny_method_threshold = CompilerOptions::kDefaultTinyMethodThreshold;
+ int num_dex_methods_threshold = CompilerOptions::kDefaultNumDexMethodsThreshold;
// Take the default set of instruction features from the build.
InstructionSetFeatures instruction_set_features =
@@ -713,7 +761,6 @@
bool dump_slow_timing = kIsDebugBuild;
bool watch_dog_enabled = !kIsTargetBuild;
-
for (int i = 0; i < argc; i++) {
const StringPiece option(argv[i]);
bool log_options = false;
@@ -729,6 +776,9 @@
if (!ParseInt(zip_fd_str, &zip_fd)) {
Usage("Failed to parse --zip-fd argument '%s' as an integer", zip_fd_str);
}
+ if (zip_fd < 0) {
+ Usage("--zip-fd passed a negative value %d", zip_fd);
+ }
} else if (option.starts_with("--zip-location=")) {
zip_location = option.substr(strlen("--zip-location=")).data();
} else if (option.starts_with("--oat-file=")) {
@@ -740,6 +790,9 @@
if (!ParseInt(oat_fd_str, &oat_fd)) {
Usage("Failed to parse --oat-fd argument '%s' as an integer", oat_fd_str);
}
+ if (oat_fd < 0) {
+ Usage("--oat-fd passed a negative value %d", oat_fd);
+ }
} else if (option == "--watch-dog") {
watch_dog_enabled = true;
} else if (option == "--no-watch-dog") {
@@ -793,6 +846,48 @@
} else if (backend_str == "Portable") {
compiler_backend = CompilerBackend::kPortable;
}
+ } else if (option.starts_with("--compiler-filter=")) {
+ compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
+ } else if (option.starts_with("--huge-method-max=")) {
+ const char* threshold = option.substr(strlen("--huge-method-max=")).data();
+ if (!ParseInt(threshold, &huge_method_threshold)) {
+ Usage("Failed to parse --huge-method-max '%s' as an integer", threshold);
+ }
+ if (huge_method_threshold < 0) {
+ Usage("--huge-method-max passed a negative value %s", huge_method_threshold);
+ }
+ } else if (option.starts_with("--large-method-max=")) {
+ const char* threshold = option.substr(strlen("--large-method-max=")).data();
+ if (!ParseInt(threshold, &large_method_threshold)) {
+ Usage("Failed to parse --large-method-max '%s' as an integer", threshold);
+ }
+ if (large_method_threshold < 0) {
+ Usage("--large-method-max passed a negative value %s", large_method_threshold);
+ }
+ } else if (option.starts_with("--small-method-max=")) {
+ const char* threshold = option.substr(strlen("--small-method-max=")).data();
+ if (!ParseInt(threshold, &small_method_threshold)) {
+ Usage("Failed to parse --small-method-max '%s' as an integer", threshold);
+ }
+ if (small_method_threshold < 0) {
+ Usage("--small-method-max passed a negative value %s", small_method_threshold);
+ }
+ } else if (option.starts_with("--tiny-method-max=")) {
+ const char* threshold = option.substr(strlen("--tiny-method-max=")).data();
+ if (!ParseInt(threshold, &tiny_method_threshold)) {
+ Usage("Failed to parse --tiny-method-max '%s' as an integer", threshold);
+ }
+ if (tiny_method_threshold < 0) {
+ Usage("--tiny-method-max passed a negative value %s", tiny_method_threshold);
+ }
+ } else if (option.starts_with("--num-dex-methods=")) {
+ const char* threshold = option.substr(strlen("--num-dex-methods=")).data();
+ if (!ParseInt(threshold, &num_dex_methods_threshold)) {
+ Usage("Failed to parse --num-dex-methods '%s' as an integer", threshold);
+ }
+ if (num_dex_methods_threshold < 0) {
+ Usage("--num-dex-methods passed a negative value %s", num_dex_methods_threshold);
+ }
} else if (option == "--host") {
is_host = true;
} else if (option == "--runtime-arg") {
@@ -915,6 +1010,44 @@
oat_unstripped += oat_filename;
}
+ if (compiler_filter_string == NULL) {
+ if (image) {
+ compiler_filter_string = "everything";
+ } else {
+#if ART_SMALL_MODE
+ compiler_filter_string = "interpret-only";
+#else
+ compiler_filter_string = "speed";
+#endif
+ }
+ }
+ CHECK(compiler_filter_string != nullptr);
+ CompilerOptions::CompilerFilter compiler_filter = CompilerOptions::kDefaultCompilerFilter;
+ if (strcmp(compiler_filter_string, "interpret-only") == 0) {
+ compiler_filter = CompilerOptions::kInterpretOnly;
+ } else if (strcmp(compiler_filter_string, "space") == 0) {
+ compiler_filter = CompilerOptions::kSpace;
+ } else if (strcmp(compiler_filter_string, "balanced") == 0) {
+ compiler_filter = CompilerOptions::kBalanced;
+ } else if (strcmp(compiler_filter_string, "speed") == 0) {
+ compiler_filter = CompilerOptions::kSpeed;
+ } else if (strcmp(compiler_filter_string, "everything") == 0) {
+ compiler_filter = CompilerOptions::kEverything;
+ } else {
+ Usage("Unknown --compiler-filter value %s", compiler_filter_string);
+ }
+
+ CompilerOptions compiler_options(compiler_filter,
+ huge_method_threshold,
+ large_method_threshold,
+ small_method_threshold,
+ tiny_method_threshold,
+ num_dex_methods_threshold
+#ifdef ART_SEA_IR_MODE
+ , compiler_options.sea_ir_ = true;
+#endif
+ ); // NOLINT(whitespace/parens)
+
// Done with usage checks, enable watchdog if requested
WatchDog watch_dog(watch_dog_enabled);
@@ -940,22 +1073,9 @@
}
timings.StartSplit("dex2oat Setup");
- LOG(INFO) << "dex2oat: " << oat_location;
+ LOG(INFO) << "dex2oat: " << CommandLine();
- if (image) {
- bool has_compiler_filter = false;
- for (const char* r : runtime_args) {
- if (strncmp(r, "-compiler-filter:", 17) == 0) {
- has_compiler_filter = true;
- break;
- }
- }
- if (!has_compiler_filter) {
- runtime_args.push_back("-compiler-filter:everything");
- }
- }
-
- Runtime::Options options;
+ Runtime::Options runtime_options;
std::vector<const DexFile*> boot_class_path;
if (boot_image_option.empty()) {
size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path);
@@ -963,24 +1083,33 @@
LOG(ERROR) << "Failed to open some dex files: " << failure_count;
return EXIT_FAILURE;
}
- options.push_back(std::make_pair("bootclasspath", &boot_class_path));
+ runtime_options.push_back(std::make_pair("bootclasspath", &boot_class_path));
} else {
- options.push_back(std::make_pair(boot_image_option.c_str(), reinterpret_cast<void*>(NULL)));
+ runtime_options.push_back(std::make_pair(boot_image_option.c_str(),
+ reinterpret_cast<void*>(NULL)));
}
if (host_prefix.get() != NULL) {
- options.push_back(std::make_pair("host-prefix", host_prefix->c_str()));
+ runtime_options.push_back(std::make_pair("host-prefix", host_prefix->c_str()));
}
for (size_t i = 0; i < runtime_args.size(); i++) {
- options.push_back(std::make_pair(runtime_args[i], reinterpret_cast<void*>(NULL)));
+ runtime_options.push_back(std::make_pair(runtime_args[i], reinterpret_cast<void*>(NULL)));
}
-#ifdef ART_SEA_IR_MODE
- options.push_back(std::make_pair("-sea_ir", reinterpret_cast<void*>(NULL)));
-#endif
+ VerificationResults verification_results(&compiler_options);
+ DexFileToMethodInlinerMap method_inliner_map;
+ CompilerCallbacksImpl callbacks(&verification_results, &method_inliner_map);
+ runtime_options.push_back(std::make_pair("compilercallbacks", &callbacks));
Dex2Oat* p_dex2oat;
- if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set,
- instruction_set_features, thread_count)) {
+ if (!Dex2Oat::Create(&p_dex2oat,
+ runtime_options,
+ compiler_options,
+ compiler_backend,
+ instruction_set,
+ instruction_set_features,
+ &verification_results,
+ &method_inliner_map,
+ thread_count)) {
LOG(ERROR) << "Failed to create dex2oat";
return EXIT_FAILURE;
}
@@ -1043,6 +1172,22 @@
}
}
+ const bool kSaveDexInput = false;
+ if (kSaveDexInput) {
+ for (size_t i = 0; i < dex_files.size(); ++i) {
+ const DexFile* dex_file = dex_files[i];
+ std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex", getpid(), i));
+ UniquePtr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str()));
+ if (tmp_file.get() == nullptr) {
+ PLOG(ERROR) << "Failed to open file " << tmp_file_name
+ << ". Try: adb shell chmod 777 /data/local/tmp";
+ continue;
+ }
+ tmp_file->WriteFully(dex_file->Begin(), dex_file->Size());
+ LOG(INFO) << "Wrote input to " << tmp_file_name;
+ }
+ }
+
// Ensure opened dex files are writable for dex-to-dex transformations.
for (const auto& dex_file : dex_files) {
if (!dex_file->EnableWrite()) {
@@ -1055,15 +1200,15 @@
* If we're not in interpret-only mode, go ahead and compile small applications. Don't
* bother to check if we're doing the image.
*/
- if (!image && (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly)) {
+ if (!image && (compiler_options.GetCompilerFilter() != CompilerOptions::kInterpretOnly)) {
size_t num_methods = 0;
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != NULL);
num_methods += dex_file->NumMethodIds();
}
- if (num_methods <= Runtime::Current()->GetNumDexMethodsThreshold()) {
- Runtime::Current()->SetCompilerFilter(Runtime::kSpeed);
+ if (num_methods <= compiler_options.GetNumDexMethodsThreshold()) {
+ compiler_options.SetCompilerFilter(CompilerOptions::kSpeed);
VLOG(compiler) << "Below method threshold, compiling anyways";
}
}
@@ -1207,13 +1352,13 @@
// Everything was successfully written, do an explicit exit here to avoid running Runtime
// destructors that take time (bug 10645725) unless we're a debug build or running on valgrind.
- if (!kIsDebugBuild || (RUNNING_ON_VALGRIND == 0)) {
+ if (!kIsDebugBuild && (RUNNING_ON_VALGRIND == 0)) {
dex2oat->LogCompletionTime();
exit(EXIT_SUCCESS);
}
return EXIT_SUCCESS;
-}
+} // NOLINT(readability/fn_size)
} // namespace art
int main(int argc, char** argv) {
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index b6ddc95..d86ba7b 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -633,6 +633,9 @@
case 0x99:
opcode << "cdq";
break;
+ case 0xAF:
+ opcode << (prefix[2] == 0x66 ? "scasw" : "scasl");
+ break;
case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7:
opcode << "mov";
immediate_bytes = 1;
@@ -693,6 +696,7 @@
has_modrm = true;
reg_is_opcode = true;
break;
+ case 0xE3: opcode << "jecxz"; branch_bytes = 1; break;
case 0xE8: opcode << "call"; branch_bytes = 4; break;
case 0xE9: opcode << "jmp"; branch_bytes = 4; break;
case 0xEB: opcode << "jmp"; branch_bytes = 1; break;
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 53b07f9..330a307 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -49,6 +49,7 @@
#include "runtime.h"
#include "safe_map.h"
#include "scoped_thread_state_change.h"
+#include "thread_list.h"
#include "verifier/dex_gc_map.h"
#include "verifier/method_verifier.h"
#include "vmap_table.h"
@@ -83,6 +84,11 @@
" --output=<file> may be used to send the output to a file.\n"
" Example: --output=/tmp/oatdump.txt\n"
"\n");
+ fprintf(stderr,
+ " --dump:[raw_mapping_table|raw_gc_map]\n"
+ " Example: --dump:raw_gc_map\n"
+ " Default: neither\n"
+ "\n");
exit(EXIT_FAILURE);
}
@@ -100,10 +106,12 @@
class OatDumper {
public:
- explicit OatDumper(const std::string& host_prefix, const OatFile& oat_file)
+ explicit OatDumper(const std::string& host_prefix, const OatFile& oat_file, bool dump_raw_mapping_table, bool dump_raw_gc_map)
: host_prefix_(host_prefix),
oat_file_(oat_file),
oat_dex_files_(oat_file.GetOatDexFiles()),
+ dump_raw_mapping_table_(dump_raw_mapping_table),
+ dump_raw_gc_map_(dump_raw_gc_map),
disassembler_(Disassembler::Create(oat_file_.GetOatHeader().GetInstructionSet())) {
AddAllOffsets();
}
@@ -358,16 +366,14 @@
DumpVmap(indent2_os, oat_method);
indent2_os << StringPrintf("mapping_table: %p (offset=0x%08x)\n",
oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
- const bool kDumpRawMappingTable = false;
- if (kDumpRawMappingTable) {
+ if (dump_raw_mapping_table_) {
Indenter indent3_filter(indent2_os.rdbuf(), kIndentChar, kIndentBy1Count);
std::ostream indent3_os(&indent3_filter);
DumpMappingTable(indent3_os, oat_method);
}
indent2_os << StringPrintf("gc_map: %p (offset=0x%08x)\n",
oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset());
- const bool kDumpRawGcMap = false;
- if (kDumpRawGcMap) {
+ if (dump_raw_gc_map_) {
Indenter indent3_filter(indent2_os.rdbuf(), kIndentChar, kIndentBy1Count);
std::ostream indent3_os(&indent3_filter);
DumpGcMap(indent3_os, oat_method, code_item);
@@ -699,6 +705,8 @@
const std::string host_prefix_;
const OatFile& oat_file_;
std::vector<const OatFile::OatDexFile*> oat_dex_files_;
+ bool dump_raw_mapping_table_;
+ bool dump_raw_gc_map_;
std::set<uintptr_t> offsets_;
UniquePtr<Disassembler> disassembler_;
};
@@ -707,9 +715,12 @@
public:
explicit ImageDumper(std::ostream* os, const std::string& image_filename,
const std::string& host_prefix, gc::space::ImageSpace& image_space,
- const ImageHeader& image_header)
+ const ImageHeader& image_header, bool dump_raw_mapping_table,
+ bool dump_raw_gc_map)
: os_(os), image_filename_(image_filename), host_prefix_(host_prefix),
- image_space_(image_space), image_header_(image_header) {}
+ image_space_(image_space), image_header_(image_header),
+ dump_raw_mapping_table_(dump_raw_mapping_table),
+ dump_raw_gc_map_(dump_raw_gc_map) {}
void Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
std::ostream& os = *os_;
@@ -791,7 +802,8 @@
stats_.oat_file_bytes = oat_file->Size();
- oat_dumper_.reset(new OatDumper(host_prefix_, *oat_file));
+ oat_dumper_.reset(new OatDumper(host_prefix_, *oat_file, dump_raw_mapping_table_,
+ dump_raw_gc_map_));
for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
CHECK(oat_dex_file != NULL);
@@ -806,8 +818,21 @@
const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces();
Thread* self = Thread::Current();
{
- WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
- heap->FlushAllocStack();
+ {
+ WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ heap->FlushAllocStack();
+ }
+ // Since FlushAllocStack() above resets the (active) allocation
+ // stack. Need to revoke the thread-local allocation stacks that
+ // point into it.
+ {
+ self->TransitionFromRunnableToSuspended(kNative);
+ ThreadList* thread_list = Runtime::Current()->GetThreadList();
+ thread_list->SuspendAll();
+ heap->RevokeAllThreadLocalAllocationStacks(self);
+ thread_list->ResumeAll();
+ self->TransitionFromSuspendedToRunnable();
+ }
}
{
std::ostream* saved_os = os_;
@@ -1402,6 +1427,8 @@
const std::string host_prefix_;
gc::space::ImageSpace& image_space_;
const ImageHeader& image_header_;
+ bool dump_raw_mapping_table_;
+ bool dump_raw_gc_map_;
DISALLOW_COPY_AND_ASSIGN(ImageDumper);
};
@@ -1425,6 +1452,8 @@
UniquePtr<std::string> host_prefix;
std::ostream* os = &std::cout;
UniquePtr<std::ofstream> out;
+ bool dump_raw_mapping_table = false;
+ bool dump_raw_gc_map = false;
for (int i = 0; i < argc; i++) {
const StringPiece option(argv[i]);
@@ -1436,6 +1465,15 @@
boot_image_filename = option.substr(strlen("--boot-image=")).data();
} else if (option.starts_with("--host-prefix=")) {
host_prefix.reset(new std::string(option.substr(strlen("--host-prefix=")).data()));
+ } else if (option.starts_with("--dump:")) {
+ if (option == "--dump:raw_mapping_table") {
+ dump_raw_mapping_table = true;
+ } else if (option == "--dump:raw_gc_map") {
+ dump_raw_gc_map = true;
+ } else {
+ fprintf(stderr, "Unknown argument %s\n", option.data());
+ usage();
+ }
} else if (option.starts_with("--output=")) {
const char* filename = option.substr(strlen("--output=")).data();
out.reset(new std::ofstream(filename));
@@ -1477,7 +1515,7 @@
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
}
- OatDumper oat_dumper(*host_prefix.get(), *oat_file);
+ OatDumper oat_dumper(*host_prefix.get(), *oat_file, dump_raw_mapping_table, dump_raw_gc_map);
oat_dumper.Dump(*os);
return EXIT_SUCCESS;
}
@@ -1520,7 +1558,6 @@
// give it away now and then switch to a more managable ScopedObjectAccess.
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
ScopedObjectAccess soa(Thread::Current());
-
gc::Heap* heap = Runtime::Current()->GetHeap();
gc::space::ImageSpace* image_space = heap->GetImageSpace();
CHECK(image_space != NULL);
@@ -1529,7 +1566,8 @@
fprintf(stderr, "Invalid image header %s\n", image_filename);
return EXIT_FAILURE;
}
- ImageDumper image_dumper(os, image_filename, *host_prefix.get(), *image_space, image_header);
+ ImageDumper image_dumper(os, image_filename, *host_prefix.get(), *image_space, image_header,
+ dump_raw_mapping_table, dump_raw_gc_map);
image_dumper.Dump();
return EXIT_SUCCESS;
}
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index cf3f72e..ed8bc13 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -351,31 +351,31 @@
*/
.extern artLockObjectFromCode
ENTRY art_quick_lock_object
- cbz r0, slow_lock
-retry_lock:
+ cbz r0, .Lslow_lock
+.Lretry_lock:
ldr r2, [r9, #THREAD_ID_OFFSET]
ldrex r1, [r0, #LOCK_WORD_OFFSET]
- cbnz r1, not_unlocked @ already thin locked
+ cbnz r1, .Lnot_unlocked @ already thin locked
@ unlocked case - r2 holds thread id with count of 0
strex r3, r2, [r0, #LOCK_WORD_OFFSET]
- cbnz r3, strex_fail @ store failed, retry
+ cbnz r3, .Lstrex_fail @ store failed, retry
dmb ish @ full (LoadLoad) memory barrier
bx lr
-strex_fail:
- b retry_lock @ unlikely forward branch, need to reload and recheck r1/r2
-not_unlocked:
+.Lstrex_fail:
+ b .Lretry_lock @ unlikely forward branch, need to reload and recheck r1/r2
+.Lnot_unlocked:
lsr r3, r1, 30
- cbnz r3, slow_lock @ if either of the top two bits are set, go slow path
+ cbnz r3, .Lslow_lock @ if either of the top two bits are set, go slow path
eor r2, r1, r2 @ lock_word.ThreadId() ^ self->ThreadId()
uxth r2, r2 @ zero top 16 bits
- cbnz r2, slow_lock @ lock word and self thread id's match -> recursive lock
+ cbnz r2, .Lslow_lock @ lock word and self thread id's match -> recursive lock
@ else contention, go to slow path
add r2, r1, #65536 @ increment count in lock word placing in r2 for storing
lsr r1, r2, 30 @ if either of the top two bits are set, we overflowed.
- cbnz r1, slow_lock @ if we overflow the count go slow path
+ cbnz r1, .Lslow_lock @ if we overflow the count go slow path
str r2, [r0, #LOCK_WORD_OFFSET] @ no need for strex as we hold the lock
bx lr
-slow_lock:
+.Lslow_lock:
SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case we block
mov r1, r9 @ pass Thread::Current
mov r2, sp @ pass SP
@@ -391,25 +391,25 @@
*/
.extern artUnlockObjectFromCode
ENTRY art_quick_unlock_object
- cbz r0, slow_unlock
+ cbz r0, .Lslow_unlock
ldr r1, [r0, #LOCK_WORD_OFFSET]
lsr r2, r1, 30
- cbnz r2, slow_unlock @ if either of the top two bits are set, go slow path
+ cbnz r2, .Lslow_unlock @ if either of the top two bits are set, go slow path
ldr r2, [r9, #THREAD_ID_OFFSET]
eor r3, r1, r2 @ lock_word.ThreadId() ^ self->ThreadId()
uxth r3, r3 @ zero top 16 bits
- cbnz r3, slow_unlock @ do lock word and self thread id's match?
+ cbnz r3, .Lslow_unlock @ do lock word and self thread id's match?
cmp r1, #65536
- bpl recursive_thin_unlock
+ bpl .Lrecursive_thin_unlock
@ transition to unlocked, r3 holds 0
str r3, [r0, #LOCK_WORD_OFFSET]
dmb ish @ full (StoreLoad) memory barrier
bx lr
-recursive_thin_unlock:
+.Lrecursive_thin_unlock:
sub r1, r1, #65536
str r1, [r0, #LOCK_WORD_OFFSET]
bx lr
-slow_unlock:
+.Lslow_unlock:
SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case exception allocation triggers GC
mov r1, r9 @ pass Thread::Current
mov r2, sp @ pass SP
@@ -435,11 +435,11 @@
.pad #4
.cfi_adjust_cfa_offset 4
bl artIsAssignableFromCode
- cbz r0, throw_class_cast_exception
+ cbz r0, .Lthrow_class_cast_exception
add sp, #4
.cfi_adjust_cfa_offset -4
pop {r0-r1, pc}
-throw_class_cast_exception:
+.Lthrow_class_cast_exception:
add sp, #4
.cfi_adjust_cfa_offset -4
pop {r0-r1, lr}
@@ -473,29 +473,29 @@
END art_quick_aput_obj_with_bound_check
ENTRY art_quick_aput_obj
- cbz r2, do_aput_null
+ cbz r2, .Ldo_aput_null
ldr r3, [r0, #CLASS_OFFSET]
ldr ip, [r2, #CLASS_OFFSET]
ldr r3, [r3, #CLASS_COMPONENT_TYPE_OFFSET]
cmp r3, ip @ value's type == array's component type - trivial assignability
- bne check_assignability
-do_aput:
+ bne .Lcheck_assignability
+.Ldo_aput:
add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
str r2, [r3, r1, lsl #2]
ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
lsr r0, r0, #7
strb r3, [r3, r0]
blx lr
-do_aput_null:
+.Ldo_aput_null:
add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
str r2, [r3, r1, lsl #2]
blx lr
-check_assignability:
- push {r0-r2, lr} @ save arguments
+.Lcheck_assignability:
+ push {r0-r2, lr} @ save arguments
mov r1, ip
mov r0, r3
bl artIsAssignableFromCode
- cbz r0, throw_array_store_exception
+ cbz r0, .Lthrow_array_store_exception
pop {r0-r2, lr}
add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
str r2, [r3, r1, lsl #2]
@@ -503,7 +503,7 @@
lsr r0, r0, #7
strb r3, [r3, r0]
blx lr
-throw_array_store_exception:
+.Lthrow_array_store_exception:
pop {r0-r2, lr}
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
mov r1, r2
@@ -1181,55 +1181,55 @@
*/
subs r2, #4
- blt indexof_remainder
+ blt .Lindexof_remainder
-indexof_loop4:
+.Lindexof_loop4:
ldrh r3, [r0, #2]!
ldrh r4, [r0, #2]!
ldrh r10, [r0, #2]!
ldrh r11, [r0, #2]!
cmp r3, r1
- beq match_0
+ beq .Lmatch_0
cmp r4, r1
- beq match_1
+ beq .Lmatch_1
cmp r10, r1
- beq match_2
+ beq .Lmatch_2
cmp r11, r1
- beq match_3
+ beq .Lmatch_3
subs r2, #4
- bge indexof_loop4
+ bge .Lindexof_loop4
-indexof_remainder:
- adds r2, #4
- beq indexof_nomatch
+.Lindexof_remainder:
+ adds r2, #4
+ beq .Lindexof_nomatch
-indexof_loop1:
+.Lindexof_loop1:
ldrh r3, [r0, #2]!
cmp r3, r1
- beq match_3
+ beq .Lmatch_3
subs r2, #1
- bne indexof_loop1
+ bne .Lindexof_loop1
-indexof_nomatch:
+.Lindexof_nomatch:
mov r0, #-1
pop {r4, r10-r11, pc}
-match_0:
+.Lmatch_0:
sub r0, #6
sub r0, r12
asr r0, r0, #1
pop {r4, r10-r11, pc}
-match_1:
+.Lmatch_1:
sub r0, #4
sub r0, r12
asr r0, r0, #1
pop {r4, r10-r11, pc}
-match_2:
+.Lmatch_2:
sub r0, #2
sub r0, r12
asr r0, r0, #1
pop {r4, r10-r11, pc}
-match_3:
+.Lmatch_3:
sub r0, r12
asr r0, r0, #1
pop {r4, r10-r11, pc}
@@ -1308,7 +1308,7 @@
*/
subs r10, #2
- blt do_remainder2
+ blt .Ldo_remainder2
/*
* Unroll the first two checks so we can quickly catch early mismatch
@@ -1322,13 +1322,13 @@
subs r0, r3, r4
it eq
subseq r0, r7, r8
- bne done
+ bne .Ldone
cmp r10, #28
- bgt do_memcmp16
+ bgt .Ldo_memcmp16
subs r10, #3
- blt do_remainder
+ blt .Ldo_remainder
-loopback_triple:
+.Lloopback_triple:
ldrh r3, [r2, #2]!
ldrh r4, [r1, #2]!
ldrh r7, [r2, #2]!
@@ -1340,34 +1340,34 @@
subseq r0, r7, r8
it eq
subseq r0, r9, r12
- bne done
+ bne .Ldone
subs r10, #3
- bge loopback_triple
+ bge .Lloopback_triple
-do_remainder:
+.Ldo_remainder:
adds r10, #3
- beq returnDiff
+ beq .Lreturn_diff
-loopback_single:
+.Lloopback_single:
ldrh r3, [r2, #2]!
ldrh r4, [r1, #2]!
subs r0, r3, r4
- bne done
+ bne .Ldone
subs r10, #1
- bne loopback_single
+ bne .Lloopback_single
-returnDiff:
+.Lreturn_diff:
mov r0, r11
pop {r4, r7-r12, pc}
-do_remainder2:
+.Ldo_remainder2:
adds r10, #2
- bne loopback_single
+ bne .Lloopback_single
mov r0, r11
pop {r4, r7-r12, pc}
/* Long string case */
-do_memcmp16:
+.Ldo_memcmp16:
mov r7, r11
add r0, r2, #2
add r1, r1, #2
@@ -1376,6 +1376,6 @@
cmp r0, #0
it eq
moveq r0, r7
-done:
+.Ldone:
pop {r4, r7-r12, pc}
END art_quick_string_compareto
diff --git a/runtime/arch/mips/jni_entrypoints_mips.S b/runtime/arch/mips/jni_entrypoints_mips.S
index ad7c021..f9ca7df 100644
--- a/runtime/arch/mips/jni_entrypoints_mips.S
+++ b/runtime/arch/mips/jni_entrypoints_mips.S
@@ -44,13 +44,13 @@
lw $a2, 8($sp)
lw $a3, 12($sp)
lw $ra, 16($sp)
- beq $v0, $zero, no_native_code_found
+ beq $v0, $zero, .Lno_native_code_found
addiu $sp, $sp, 32 # restore the stack
.cfi_adjust_cfa_offset -32
move $t9, $v0 # put method code result in $t9
jr $t9 # leaf call to method's code
nop
-no_native_code_found:
+.Lno_native_code_found:
jr $ra
nop
END art_jni_dlsym_lookup_stub
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index f9a200a..d23be47 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -336,7 +336,7 @@
.extern artThrowNullPointerExceptionFromCode
ENTRY art_quick_throw_null_pointer_exception
GENERATE_GLOBAL_POINTER
-art_quick_throw_null_pointer_exception_gp_set:
+.Lart_quick_throw_null_pointer_exception_gp_set:
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
move $a0, rSELF # pass Thread::Current
la $t9, artThrowNullPointerExceptionFromCode
@@ -363,7 +363,7 @@
.extern artThrowArrayBoundsFromCode
ENTRY art_quick_throw_array_bounds
GENERATE_GLOBAL_POINTER
-art_quick_throw_array_bounds_gp_set:
+.Lart_quick_throw_array_bounds_gp_set:
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
move $a2, rSELF # pass Thread::Current
la $t9, artThrowArrayBoundsFromCode
@@ -537,7 +537,7 @@
.extern artLockObjectFromCode
ENTRY art_quick_lock_object
GENERATE_GLOBAL_POINTER
- beqz $a0, art_quick_throw_null_pointer_exception_gp_set
+ beqz $a0, .Lart_quick_throw_null_pointer_exception_gp_set
nop
SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case we block
move $a1, rSELF # pass Thread::Current
@@ -552,7 +552,7 @@
.extern artUnlockObjectFromCode
ENTRY art_quick_unlock_object
GENERATE_GLOBAL_POINTER
- beqz $a0, art_quick_throw_null_pointer_exception_gp_set
+ beqz $a0, .Lart_quick_throw_null_pointer_exception_gp_set
nop
SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC
move $a1, rSELF # pass Thread::Current
@@ -576,12 +576,12 @@
sw $a0, 0($sp)
jal artIsAssignableFromCode
nop
- beqz $v0, throw_class_cast_exception
+ beqz $v0, .Lthrow_class_cast_exception
lw $ra, 12($sp)
jr $ra
addiu $sp, $sp, 16
.cfi_adjust_cfa_offset -16
-throw_class_cast_exception:
+.Lthrow_class_cast_exception:
lw $t9, 8($sp)
lw $a1, 4($sp)
lw $a0, 0($sp)
@@ -601,35 +601,35 @@
*/
ENTRY art_quick_aput_obj_with_null_and_bound_check
GENERATE_GLOBAL_POINTER
- bnez $a0, art_quick_aput_obj_with_bound_check_gp_set
+ bnez $a0, .Lart_quick_aput_obj_with_bound_check_gp_set
nop
- b art_quick_throw_null_pointer_exception_gp_set
+ b .Lart_quick_throw_null_pointer_exception_gp_set
nop
END art_quick_aput_obj_with_null_and_bound_check
ENTRY art_quick_aput_obj_with_bound_check
GENERATE_GLOBAL_POINTER
-art_quick_aput_obj_with_bound_check_gp_set:
+.Lart_quick_aput_obj_with_bound_check_gp_set:
lw $t0, ARRAY_LENGTH_OFFSET($a0)
sltu $t1, $a1, $t0
- bnez $t1, art_quick_aput_obj_gp_set
+ bnez $t1, .Lart_quick_aput_obj_gp_set
nop
move $a0, $a1
- b art_quick_throw_array_bounds_gp_set
+ b .Lart_quick_throw_array_bounds_gp_set
move $a1, $t0
END art_quick_aput_obj_with_bound_check
ENTRY art_quick_aput_obj
GENERATE_GLOBAL_POINTER
-art_quick_aput_obj_gp_set:
- beqz $a2, do_aput_null
+.Lart_quick_aput_obj_gp_set:
+ beqz $a2, .Ldo_aput_null
nop
lw $t0, CLASS_OFFSET($a0)
lw $t1, CLASS_OFFSET($a2)
lw $t0, CLASS_COMPONENT_TYPE_OFFSET($t0)
- bne $t1, $t0, check_assignability # value's type == array's component type - trivial assignability
+ bne $t1, $t0, .Lcheck_assignability # value's type == array's component type - trivial assignability
nop
-do_aput:
+.Ldo_aput:
sll $a1, $a1, 2
add $t0, $a0, $a1
sw $a2, OBJECT_ARRAY_DATA_OFFSET($t0)
@@ -639,13 +639,13 @@
sb $t0, ($t1)
jr $ra
nop
-do_aput_null:
+.Ldo_aput_null:
sll $a1, $a1, 2
add $t0, $a0, $a1
sw $a2, OBJECT_ARRAY_DATA_OFFSET($t0)
jr $ra
nop
-check_assignability:
+.Lcheck_assignability:
addiu $sp, $sp, -32
.cfi_adjust_cfa_offset 32
sw $ra, 28($sp)
@@ -665,7 +665,7 @@
lw $a0, 0($sp)
add $sp, 32
.cfi_adjust_cfa_offset -32
- bnez $v0, do_aput
+ bnez $v0, .Ldo_aput
nop
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
move $a1, $a2
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index 589c7d9..888310a 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -84,7 +84,6 @@
// Intrinsic entrypoints.
extern "C" int32_t art_quick_memcmp16(void*, void*, int32_t);
-extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
extern "C" int32_t art_quick_string_compareto(void*, void*);
extern "C" void* art_quick_memcpy(void*, const void*, size_t);
@@ -193,7 +192,7 @@
qpoints->pUshrLong = art_quick_lushr;
// Intrinsics
- qpoints->pIndexOf = art_quick_indexof;
+ // qpoints->pIndexOf = nullptr; // Not needed on x86
qpoints->pMemcmp16 = art_quick_memcmp16;
qpoints->pStringCompareTo = art_quick_string_compareto;
qpoints->pMemcpy = art_quick_memcpy;
diff --git a/runtime/arch/x86/jni_entrypoints_x86.S b/runtime/arch/x86/jni_entrypoints_x86.S
index 2eb5ada..ebd82b5 100644
--- a/runtime/arch/x86/jni_entrypoints_x86.S
+++ b/runtime/arch/x86/jni_entrypoints_x86.S
@@ -30,8 +30,8 @@
addl LITERAL(8), %esp // restore the stack
CFI_ADJUST_CFA_OFFSET(-12)
cmpl LITERAL(0), %eax // check if returned method code is null
- je no_native_code_found // if null, jump to return to handle
+ je .Lno_native_code_found // if null, jump to return to handle
jmp *%eax // otherwise, tail call to intended method
-no_native_code_found:
+.Lno_native_code_found:
ret
END_FUNCTION art_jni_dlsym_lookup_stub
diff --git a/runtime/arch/x86/portable_entrypoints_x86.S b/runtime/arch/x86/portable_entrypoints_x86.S
index 4bd6173..5f270f8 100644
--- a/runtime/arch/x86/portable_entrypoints_x86.S
+++ b/runtime/arch/x86/portable_entrypoints_x86.S
@@ -52,16 +52,16 @@
POP ebp // pop ebp
mov 20(%esp), %ecx // get result pointer
cmpl LITERAL(68), 24(%esp) // test if result type char == 'D'
- je return_double_portable
+ je .Lreturn_double_portable
cmpl LITERAL(70), 24(%esp) // test if result type char == 'F'
- je return_float_portable
+ je .Lreturn_float_portable
mov %eax, (%ecx) // store the result
mov %edx, 4(%ecx) // store the other half of the result
ret
-return_double_portable:
+.Lreturn_double_portable:
fstpl (%ecx) // store the floating point result as double
ret
-return_float_portable:
+.Lreturn_float_portable:
fstps (%ecx) // store the floating point result as float
ret
END_FUNCTION art_portable_invoke_stub
@@ -109,9 +109,9 @@
CFI_RESTORE(%ebp)
CFI_DEF_CFA(%esp, 4)
testl %eax, %eax
- jz resolve_fail
+ jz .Lresolve_fail
jmp * %eax
-resolve_fail: // Resolution failed, return with exception pending.
+.Lresolve_fail: // Resolution failed, return with exception pending.
ret
END_FUNCTION art_portable_resolution_trampoline
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index c76c6b2..b1f2275 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -285,14 +285,14 @@
mov %edx, 4(%ecx) // store the other half of the result
mov 24(%esp), %edx // get the shorty
cmpb LITERAL(68), (%edx) // test if result type char == 'D'
- je return_double_quick
+ je .Lreturn_double_quick
cmpb LITERAL(70), (%edx) // test if result type char == 'F'
- je return_float_quick
+ je .Lreturn_float_quick
ret
-return_double_quick:
+.Lreturn_double_quick:
movsd %xmm0, (%ecx) // store the floating point result
ret
-return_float_quick:
+.Lreturn_float_quick:
movss %xmm0, (%ecx) // store the floating point result
ret
END_FUNCTION art_quick_invoke_stub
@@ -514,32 +514,32 @@
DEFINE_FUNCTION art_quick_lock_object
testl %eax, %eax // null check object/eax
- jz slow_lock
-retry_lock:
+ jz .Lslow_lock
+.Lretry_lock:
movl LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word
test LITERAL(0xC0000000), %ecx // test the 2 high bits.
- jne slow_lock // slow path if either of the two high bits are set.
+ jne .Lslow_lock // slow path if either of the two high bits are set.
movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id
test %ecx, %ecx
- jnz already_thin // lock word contains a thin lock
+ jnz .Lalready_thin // lock word contains a thin lock
// unlocked case - %edx holds thread id with count of 0
movl %eax, %ecx // remember object in case of retry
xor %eax, %eax // eax == 0 for comparison with lock word in cmpxchg
lock cmpxchg %edx, LOCK_WORD_OFFSET(%ecx)
- jnz cmpxchg_fail // cmpxchg failed retry
+ jnz .Lcmpxchg_fail // cmpxchg failed retry
ret
-cmpxchg_fail:
+.Lcmpxchg_fail:
movl %ecx, %eax // restore eax
- jmp retry_lock
-already_thin:
+ jmp .Lretry_lock
+.Lalready_thin:
cmpw %ax, %dx // do we hold the lock already?
- jne slow_lock
+ jne .Lslow_lock
addl LITERAL(65536), %eax // increment recursion count
test LITERAL(0xC0000000), %eax // overflowed if either of top two bits are set
- jne slow_lock // count overflowed so go slow
+ jne .Lslow_lock // count overflowed so go slow
movl %eax, LOCK_WORD_OFFSET(%ecx) // update lockword, cmpxchg not necessary as we hold lock
ret
-slow_lock:
+.Lslow_lock:
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %edx // remember SP
SETUP_GOT_NOSAVE // clobbers EBX
@@ -558,22 +558,22 @@
DEFINE_FUNCTION art_quick_unlock_object
testl %eax, %eax // null check object/eax
- jz slow_unlock
+ jz .Lslow_unlock
movl LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word
movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id
test %ecx, %ecx
- jb slow_unlock // lock word contains a monitor
+ jb .Lslow_unlock // lock word contains a monitor
cmpw %cx, %dx // does the thread id match?
- jne slow_unlock
+ jne .Lslow_unlock
cmpl LITERAL(65536), %ecx
- jae recursive_thin_unlock
+ jae .Lrecursive_thin_unlock
movl LITERAL(0), LOCK_WORD_OFFSET(%eax)
ret
-recursive_thin_unlock:
+.Lrecursive_thin_unlock:
subl LITERAL(65536), %ecx
mov %ecx, LOCK_WORD_OFFSET(%eax)
ret
-slow_unlock:
+.Lslow_unlock:
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %edx // remember SP
SETUP_GOT_NOSAVE // clobbers EBX
@@ -651,21 +651,21 @@
DEFINE_FUNCTION art_quick_aput_obj
test %edx, %edx // store of null
- jz do_aput_null
+ jz .Ldo_aput_null
movl CLASS_OFFSET(%eax), %ebx
movl CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx
cmpl CLASS_OFFSET(%edx), %ebx // value's type == array's component type - trivial assignability
- jne check_assignability
-do_aput:
+ jne .Lcheck_assignability
+.Ldo_aput:
movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
shrl LITERAL(7), %eax
movb %dl, (%edx, %eax)
ret
-do_aput_null:
+.Ldo_aput_null:
movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
ret
-check_assignability:
+.Lcheck_assignability:
PUSH eax // save arguments
PUSH ecx
PUSH edx
@@ -679,7 +679,7 @@
addl LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
testl %eax, %eax
- jz throw_array_store_exception
+ jz .Lthrow_array_store_exception
POP edx
POP ecx
POP eax
@@ -688,7 +688,7 @@
shrl LITERAL(7), %eax
movb %dl, (%edx, %eax)
ret
-throw_array_store_exception:
+.Lthrow_array_store_exception:
POP edx
POP ecx
POP eax
@@ -792,14 +792,14 @@
DEFINE_FUNCTION art_quick_idivmod
cmpl LITERAL(0x80000000), %eax
- je check_arg2 // special case
-args_ok:
+ je .Lcheck_arg2 // special case
+.Largs_ok:
cdq // edx:eax = sign extend eax
idiv %ecx // (edx,eax) = (edx:eax % ecx, edx:eax / ecx)
ret
-check_arg2:
+.Lcheck_arg2:
cmpl LITERAL(-1), %ecx
- jne args_ok
+ jne .Largs_ok
xorl %edx, %edx
ret // eax already holds min int
END_FUNCTION art_quick_idivmod
@@ -1277,56 +1277,6 @@
END_FUNCTION art_quick_deoptimize
/*
- * String's indexOf.
- *
- * On entry:
- * eax: string object (known non-null)
- * ecx: char to match (known <= 0xFFFF)
- * edx: Starting offset in string data
- */
-DEFINE_FUNCTION art_quick_indexof
- PUSH edi // push callee save reg
- mov STRING_COUNT_OFFSET(%eax), %ebx
- mov STRING_VALUE_OFFSET(%eax), %edi
- mov STRING_OFFSET_OFFSET(%eax), %eax
- testl %edx, %edx // check if start < 0
- jl clamp_min
-clamp_done:
- cmpl %ebx, %edx // check if start >= count
- jge not_found
- lea STRING_DATA_OFFSET(%edi, %eax, 2), %edi // build a pointer to the start of string data
- mov %edi, %eax // save a copy in eax to later compute result
- lea (%edi, %edx, 2), %edi // build pointer to start of data to compare
- subl %edx, %ebx // compute iteration count
- /*
- * At this point we have:
- * eax: original start of string data
- * ecx: char to compare
- * ebx: length to compare
- * edi: start of data to test
- */
- mov %eax, %edx
- mov %ecx, %eax // put char to match in %eax
- mov %ebx, %ecx // put length to compare in %ecx
- repne scasw // find %ax, starting at [%edi], up to length %ecx
- jne not_found
- subl %edx, %edi
- sar LITERAL(1), %edi
- decl %edi // index = ((curr_ptr - orig_ptr) / 2) - 1
- mov %edi, %eax
- POP edi // pop callee save reg
- ret
- .balign 16
-not_found:
- mov LITERAL(-1), %eax // return -1 (not found)
- POP edi // pop callee save reg
- ret
-clamp_min:
- xor %edx, %edx // clamp start to 0
- jmp clamp_done
-END_FUNCTION art_quick_indexof
-
- /*
* String's compareTo.
*
* On entry:
@@ -1358,12 +1308,12 @@
* edi: pointer to comp string data
*/
repe cmpsw // find nonmatching chars in [%esi] and [%edi], up to length %ecx
- jne not_equal
+ jne .Lnot_equal
POP edi // pop callee save reg
POP esi // pop callee save reg
ret
.balign 16
-not_equal:
+.Lnot_equal:
movzwl -2(%esi), %eax // get last compared char from this string
movzwl -2(%edi), %ecx // get last compared char from comp string
subl %ecx, %eax // return the difference
diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h
index d425ed8..444fa22 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.h
+++ b/runtime/arch/x86_64/asm_support_x86_64.h
@@ -19,6 +19,13 @@
#include "asm_support.h"
+// Offset of field Runtime::callee_save_methods_[kSaveAll]
+#define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 208
+// Offset of field Runtime::callee_save_methods_[kRefsOnly]
+#define RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET 216
+// Offset of field Runtime::callee_save_methods_[kRefsAndArgs]
+#define RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET 224
+
// Offset of field Thread::self_ verified in InitCpu
#define THREAD_SELF_OFFSET 72
// Offset of field Thread::card_table_ verified in InitCpu
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 44bc7a2..ac238f0 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -46,12 +46,63 @@
* Runtime::CreateCalleeSaveMethod(kRefsAndArgs)
*/
MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME)
- int3
- int3
+ // R10 := Runtime::Current()
+ movq _ZN3art7Runtime9instance_E@GOTPCREL(%rip), %r10
+ movq (%r10), %r10
+ // Save callee and GPR args, mixed together to agree with core spills bitmap.
+ PUSH r15 // Callee save.
+ PUSH r14 // Callee save.
+ PUSH r13 // Callee save.
+ PUSH r12 // Callee save.
+ PUSH r9 // Arg.
+ PUSH r8 // Arg.
+ PUSH rsi // Arg.
+ PUSH rbp // Callee save.
+ PUSH rbx // Callee save.
+ PUSH rdx // Arg.
+ PUSH rcx // Arg.
+ // Create space for FPR args and create 2 slots, 1 of padding and 1 for the ArtMethod*.
+ subq LITERAL(80), %rsp
+ CFI_ADJUST_CFA_OFFSET(80)
+ // R10 := ArtMethod* for ref and args callee save frame method.
+ movq RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
+ // Save FPRs.
+ movq %xmm0, 16(%rsp)
+ movq %xmm1, 24(%rsp)
+ movq %xmm2, 32(%rsp)
+ movq %xmm3, 40(%rsp)
+ movq %xmm4, 48(%rsp)
+ movq %xmm5, 56(%rsp)
+ movq %xmm6, 64(%rsp)
+ movq %xmm7, 72(%rsp)
+ // Store ArtMethod* to bottom of stack.
+ movq %r10, 0(%rsp)
END_MACRO
MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME)
- int3
+ // Restore FPRs.
+ movq 16(%rsp), %xmm0
+ movq 24(%rsp), %xmm1
+ movq 32(%rsp), %xmm2
+ movq 40(%rsp), %xmm3
+ movq 48(%rsp), %xmm4
+ movq 56(%rsp), %xmm5
+ movq 64(%rsp), %xmm6
+ movq 72(%rsp), %xmm7
+ addq LITERAL(80), %rsp
+ CFI_ADJUST_CFA_OFFSET(-80)
+ // Save callee and GPR args, mixed together to agree with core spills bitmap.
+ POP rcx
+ POP rdx
+ POP rbx
+ POP rbp
+ POP rsi
+ POP r8
+ POP r9
+ POP r12
+ POP r13
+ POP r14
+ POP r15
END_MACRO
/*
@@ -147,20 +198,210 @@
INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck
INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
+
+ /*
+ * Helper for quick invocation stub to set up XMM registers. Assumes r10 == shorty,
+ * r11 == arg_array. Clobbers r10, r11 and al. Branches to xmm_setup_finished if it encounters
+ * the end of the shorty.
+ */
+MACRO2(LOOP_OVER_SHORTY_LOADING_XMMS, xmm_reg, finished)
+1: // LOOP
+ movb (%r10), %al // al := *shorty
+ addq LITERAL(1), %r10 // shorty++
+ cmpb LITERAL(0), %al // if (al == '\0') goto xmm_setup_finished
+ je VAR(finished, 1)
+ cmpb LITERAL(68), %al // if (al == 'D') goto FOUND_DOUBLE
+ je 2f
+ cmpb LITERAL(70), %al // if (al == 'F') goto FOUND_FLOAT
+ je 3f
+ addq LITERAL(4), %r11 // arg_array++
+ // Handle extra space in arg array taken by a long.
+ cmpb LITERAL(74), %al // if (al != 'J') goto LOOP
+ jne 1b
+ addq LITERAL(4), %r11 // arg_array++
+ jmp 1b // goto LOOP
+2: // FOUND_DOUBLE
+ movsd (%r11), REG_VAR(xmm_reg, 0)
+ addq LITERAL(8), %r11 // arg_array+=2
+ jmp 4f
+3: // FOUND_FLOAT
+ movss (%r11), REG_VAR(xmm_reg, 0)
+ addq LITERAL(4), %r11 // arg_array++
+4:
+END_MACRO
+
+ /*
+ * Helper for quick invocation stub to set up GPR registers. Assumes r10 == shorty,
+ * r11 == arg_array. Clobbers r10, r11 and al. Branches to gpr_setup_finished if it encounters
+ * the end of the shorty.
+ */
+MACRO3(LOOP_OVER_SHORTY_LOADING_GPRS, gpr_reg64, gpr_reg32, finished)
+1: // LOOP
+ movb (%r10), %al // al := *shorty
+ addq LITERAL(1), %r10 // shorty++
+ cmpb LITERAL(0), %al // if (al == '\0') goto gpr_setup_finished
+ je VAR(finished, 2)
+ cmpb LITERAL(74), %al // if (al == 'J') goto FOUND_LONG
+ je 2f
+ cmpb LITERAL(70), %al // if (al == 'F') goto SKIP_FLOAT
+ je 3f
+ cmpb LITERAL(68), %al // if (al == 'D') goto SKIP_DOUBLE
+ je 4f
+ movl (%r11), REG_VAR(gpr_reg32, 1)
+ addq LITERAL(4), %r11 // arg_array++
+ jmp 5f
+2: // FOUND_LONG
+ movq (%r11), REG_VAR(gpr_reg64, 0)
+ addq LITERAL(8), %r11 // arg_array+=2
+ jmp 5f
+3: // SKIP_FLOAT
+ addq LITERAL(4), %r11 // arg_array++
+ jmp 1b
+4: // SKIP_DOUBLE
+ addq LITERAL(8), %r11 // arg_array+=2
+ jmp 1b
+5:
+END_MACRO
+
/*
* Quick invocation stub.
* On entry:
* [sp] = return address
* rdi = method pointer
- * rsi = argument array or NULL for no argument methods
+ * rsi = argument array that must at least contain the this pointer.
* rdx = size of argument array in bytes
* rcx = (managed) thread pointer
* r8 = JValue* result
* r9 = char* shorty
*/
DEFINE_FUNCTION art_quick_invoke_stub
- int3
- int3
+ // Set up argument XMM registers.
+ leaq 1(%r9), %r10 // R10 := shorty + 1 ; ie skip return arg character.
+ leaq 4(%rsi), %r11 // R11 := arg_array + 4 ; ie skip this pointer.
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm0, .Lxmm_setup_finished
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm1, .Lxmm_setup_finished
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm2, .Lxmm_setup_finished
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm3, .Lxmm_setup_finished
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm4, .Lxmm_setup_finished
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm5, .Lxmm_setup_finished
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm6, .Lxmm_setup_finished
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm7, .Lxmm_setup_finished
+ .balign 16
+.Lxmm_setup_finished:
+ PUSH rbp // Save rbp.
+ PUSH r8 // Save r8/result*.
+ PUSH r9 // Save r9/shorty*.
+ mov %rsp, %rbp // Copy value of stack pointer into base pointer.
+ CFI_DEF_CFA_REGISTER(rbp)
+ movl %edx, %r10d
+ addl LITERAL(64), %edx // Reserve space for return addr, method*, rbp, r8 and r9 in frame.
+ andl LITERAL(0xFFFFFFF0), %edx // Align frame size to 16 bytes.
+ subl LITERAL(32), %edx // Remove space for return address, rbp, r8 and r9.
+ subq %rdx, %rsp // Reserve stack space for argument array.
+ movq LITERAL(0), (%rsp) // Store NULL for method*
+ movl %r10d, %ecx // Place size of args in rcx.
+ movq %rdi, %rax // RAX := method to be called
+ movq %rsi, %r11 // R11 := arg_array
+ leaq 8(%rsp), %rdi // Rdi is pointing just above the method* in the stack arguments.
+ // Copy arg array into stack.
+ rep movsb // while (rcx--) { *rdi++ = *rsi++ }
+ leaq 1(%r9), %r10 // R10 := shorty + 1 ; ie skip return arg character
+ movq %rax, %rdi // RDI := method to be called
+ movl (%r11), %esi // RSI := this pointer
+ addq LITERAL(4), %r11 // arg_array++
+ LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, .Lgpr_setup_finished
+ LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, .Lgpr_setup_finished
+ LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished
+ LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished
+.Lgpr_setup_finished:
+ call *METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method.
+ movq %rbp, %rsp // Restore stack pointer.
+ CFI_DEF_CFA_REGISTER(rsp)
+ POP r9 // Pop r9 - shorty*.
+ POP r8 // Pop r8 - result*.
+ POP rbp // Pop rbp
+ cmpb LITERAL(68), (%r9) // Test if result type char == 'D'.
+ je .Lreturn_double_quick
+ cmpb LITERAL(70), (%r9) // Test if result type char == 'F'.
+ je .Lreturn_float_quick
+ movq %rax, (%r8) // Store the result assuming its a long, int or Object*
+ ret
+.Lreturn_double_quick:
+ movsd %xmm0, (%r8) // Store the double floating point result.
+ ret
+.Lreturn_float_quick:
+ movss %xmm0, (%r8) // Store the floating point result.
+ ret
+END_FUNCTION art_quick_invoke_stub
+
+ /*
+ * Quick invocation stub.
+ * On entry:
+ * [sp] = return address
+ * rdi = method pointer
+ * rsi = argument array or NULL if no arguments.
+ * rdx = size of argument array in bytes
+ * rcx = (managed) thread pointer
+ * r8 = JValue* result
+ * r9 = char* shorty
+ */
+DEFINE_FUNCTION art_quick_invoke_static_stub
+ // Set up argument XMM registers.
+ leaq 1(%r9), %r10 // R10 := shorty + 1 ; ie skip return arg character
+ movq %rsi, %r11 // R11 := arg_array
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm0, .Lxmm_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm1, .Lxmm_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm2, .Lxmm_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm3, .Lxmm_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm4, .Lxmm_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm5, .Lxmm_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm6, .Lxmm_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_XMMS xmm7, .Lxmm_setup_finished2
+ .balign 16
+.Lxmm_setup_finished2:
+ PUSH rbp // Save rbp.
+ PUSH r8 // Save r8/result*.
+ PUSH r9 // Save r9/shorty*.
+ mov %rsp, %rbp // Copy value of stack pointer into base pointer.
+ CFI_DEF_CFA_REGISTER(rbp)
+ movl %edx, %r10d
+ addl LITERAL(64), %edx // Reserve space for return addr, method*, rbp, r8 and r9 in frame.
+ andl LITERAL(0xFFFFFFF0), %edx // Align frame size to 16 bytes.
+ subl LITERAL(32), %edx // Remove space for return address, rbp, r8 and r9.
+ subq %rdx, %rsp // Reserve stack space for argument array.
+ movq LITERAL(0), (%rsp) // Store NULL for method*
+ movl %r10d, %ecx // Place size of args in rcx.
+ movq %rdi, %rax // RAX := method to be called
+ movq %rsi, %r11 // R11 := arg_array
+ leaq 8(%rsp), %rdi // Rdi is pointing just above the method* in the stack arguments.
+ // Copy arg array into stack.
+ rep movsb // while (rcx--) { *rdi++ = *rsi++ }
+ leaq 1(%r9), %r10 // R10 := shorty + 1 ; ie skip return arg character
+ movq %rax, %rdi // RDI := method to be called
+ LOOP_OVER_SHORTY_LOADING_GPRS rsi, esi, .Lgpr_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, .Lgpr_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, .Lgpr_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished2
+ LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished2
+.Lgpr_setup_finished2:
+ call *METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method.
+ movq %rbp, %rsp // Restore stack pointer.
+ CFI_DEF_CFA_REGISTER(rsp)
+ POP r9 // Pop r9 - shorty*.
+ POP r8 // Pop r8 - result*.
+ POP rbp // Pop rbp
+ cmpb LITERAL(68), (%r9) // Test if result type char == 'D'.
+ je .Lreturn_double_quick2
+ cmpb LITERAL(70), (%r9) // Test if result type char == 'F'.
+ je .Lreturn_float_quick2
+ movq %rax, (%r8) // Store the result assuming its a long, int or Object*
+ ret
+.Lreturn_double_quick2:
+ movsd %xmm0, (%r8) // Store the double floating point result.
+ ret
+.Lreturn_float_quick2:
+ movss %xmm0, (%r8) // Store the floating point result.
+ ret
END_FUNCTION art_quick_invoke_stub
MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro)
@@ -210,8 +451,11 @@
END_MACRO
MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION)
- int3
- int3
+ movq %gs:THREAD_EXCEPTION_OFFSET, %rcx // get exception field
+ testq %rcx, %rcx // rcx == 0 ?
+ jnz 1f // if rcx != 0 goto 1
+ ret // return
+1: // deliver exception on current thread
DELIVER_PENDING_EXCEPTION
END_MACRO
@@ -390,7 +634,22 @@
*/
UNIMPLEMENTED art_quick_imt_conflict_trampoline
UNIMPLEMENTED art_quick_resolution_trampoline
-UNIMPLEMENTED art_quick_to_interpreter_bridge
+
+ /*
+ * Called to bridge from the quick to interpreter ABI. On entry the arguments match those
+ * of a quick call:
+ * RDI = method being called / to bridge to.
+ * RSI, RDX, RCX, R8, R9 are arguments to that method.
+ */
+DEFINE_FUNCTION art_quick_to_interpreter_bridge
+ SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // Set up frame and save arguments.
+ movq %gs:THREAD_SELF_OFFSET, %rsi // RSI := Thread::Current()
+ movq %rsp, %rdx // RDX := sp
+ call PLT_SYMBOL(artQuickToInterpreterBridge) // (method, Thread*, SP)
+ RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME // TODO: no need to restore arguments in this case.
+ movq %rax, %xmm0 // Place return value also into floating point return value.
+ RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
+END_FUNCTION art_quick_to_interpreter_bridge
/*
* Routine that intercepts method calls and returns.
diff --git a/runtime/arch/x86_64/registers_x86_64.h b/runtime/arch/x86_64/registers_x86_64.h
index 9808d91..c1a9942 100644
--- a/runtime/arch/x86_64/registers_x86_64.h
+++ b/runtime/arch/x86_64/registers_x86_64.h
@@ -48,6 +48,26 @@
};
std::ostream& operator<<(std::ostream& os, const Register& rhs);
+enum FloatRegister {
+ XMM0 = 0,
+ XMM1 = 1,
+ XMM2 = 2,
+ XMM3 = 3,
+ XMM4 = 4,
+ XMM5 = 5,
+ XMM6 = 6,
+ XMM7 = 7,
+ XMM8 = 8,
+ XMM9 = 9,
+ XMM10 = 10,
+ XMM11 = 11,
+ XMM12 = 12,
+ XMM13 = 13,
+ XMM14 = 14,
+ XMM15 = 15,
+};
+std::ostream& operator<<(std::ostream& os, const FloatRegister& rhs);
+
} // namespace x86_64
} // namespace art
diff --git a/runtime/arch/x86_64/thread_x86_64.cc b/runtime/arch/x86_64/thread_x86_64.cc
index 9e45a72..b74fc5d 100644
--- a/runtime/arch/x86_64/thread_x86_64.cc
+++ b/runtime/arch/x86_64/thread_x86_64.cc
@@ -48,6 +48,12 @@
CHECK_EQ(self_check, this);
// Sanity check other offsets.
+ CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET),
+ Runtime::GetCalleeSaveMethodOffset(Runtime::kSaveAll));
+ CHECK_EQ(static_cast<size_t>(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET),
+ Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsOnly));
+ CHECK_EQ(static_cast<size_t>(RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET),
+ Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsAndArgs));
CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_));
CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_));
CHECK_EQ(THREAD_ID_OFFSET, OFFSETOF_MEMBER(Thread, thin_lock_thread_id_));
diff --git a/runtime/atomic.h b/runtime/atomic.h
index 2a47e46..795f917 100644
--- a/runtime/atomic.h
+++ b/runtime/atomic.h
@@ -96,7 +96,7 @@
// quasiatomic operations that are performed on partially-overlapping
// memory.
class QuasiAtomic {
-#if !defined(__arm__) && !defined(__i386__)
+#if defined(__mips__) && !defined(__LP64__)
static constexpr bool kNeedSwapMutexes = true;
#else
static constexpr bool kNeedSwapMutexes = false;
@@ -141,7 +141,7 @@
}
static void MembarLoadStore() {
- #if defined(__arm__)
+ #if defined(__arm__) || defined(__aarch64__)
__asm__ __volatile__("dmb ish" : : : "memory");
#elif defined(__i386__) || defined(__x86_64__)
__asm__ __volatile__("" : : : "memory");
@@ -153,7 +153,7 @@
}
static void MembarLoadLoad() {
- #if defined(__arm__)
+ #if defined(__arm__) || defined(__aarch64__)
__asm__ __volatile__("dmb ish" : : : "memory");
#elif defined(__i386__) || defined(__x86_64__)
__asm__ __volatile__("" : : : "memory");
@@ -165,7 +165,7 @@
}
static void MembarStoreStore() {
- #if defined(__arm__)
+ #if defined(__arm__) || defined(__aarch64__)
__asm__ __volatile__("dmb ishst" : : : "memory");
#elif defined(__i386__) || defined(__x86_64__)
__asm__ __volatile__("" : : : "memory");
@@ -177,7 +177,7 @@
}
static void MembarStoreLoad() {
- #if defined(__arm__)
+ #if defined(__arm__) || defined(__aarch64__)
__asm__ __volatile__("dmb ish" : : : "memory");
#elif defined(__i386__) || defined(__x86_64__)
__asm__ __volatile__("mfence" : : : "memory");
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 66c24b5..6ef0082 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -23,10 +23,40 @@
#include "mirror/dex_cache.h"
#include "mirror/iftable.h"
#include "mirror/object_array.h"
+#include "object_utils.h"
#include "sirt_ref.h"
namespace art {
+inline bool ClassLinker::IsInBootClassPath(const char* descriptor) {
+ DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
+ return pair.second != nullptr;
+}
+
+inline mirror::Class* ClassLinker::FindSystemClass(Thread* self, const char* descriptor) {
+ SirtRef<mirror::ClassLoader> class_loader(self, nullptr);
+ return FindClass(self, descriptor, class_loader);
+}
+
+inline mirror::Class* ClassLinker::FindArrayClass(Thread* self, mirror::Class* element_class) {
+ for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
+ // Read the cached the array class once to avoid races with other threads setting it.
+ mirror::Class* array_class = find_array_class_cache_[i];
+ if (array_class != nullptr && array_class->GetComponentType() == element_class) {
+ return array_class;
+ }
+ }
+ std::string descriptor("[");
+ descriptor += ClassHelper(element_class).GetDescriptor();
+ SirtRef<mirror::ClassLoader> class_loader(self, element_class->GetClassLoader());
+ mirror::Class* array_class = FindClass(self, descriptor.c_str(), class_loader);
+ // Benign races in storing array class and incrementing index.
+ size_t victim_index = find_array_class_cache_next_victim_;
+ find_array_class_cache_[victim_index] = array_class;
+ find_array_class_cache_next_victim_ = (victim_index + 1) % kFindArrayCacheSize;
+ return array_class;
+}
+
inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx,
mirror::ArtMethod* referrer) {
mirror::String* resolved_string = referrer->GetDexCacheStrings()->Get(string_idx);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index fa50324..69d957f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -19,8 +19,6 @@
#include <fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
#include <deque>
#include <string>
@@ -176,17 +174,19 @@
: dex_lock_("ClassLinker dex lock", kDefaultMutexLevel),
dex_cache_image_class_lookup_required_(false),
failed_dex_cache_class_lookups_(0),
- class_roots_(NULL),
- array_iftable_(NULL),
+ class_roots_(nullptr),
+ array_iftable_(nullptr),
+ find_array_class_cache_next_victim_(0),
init_done_(false),
dex_caches_dirty_(false),
class_table_dirty_(false),
intern_table_(intern_table),
- portable_resolution_trampoline_(NULL),
- quick_resolution_trampoline_(NULL),
- portable_imt_conflict_trampoline_(NULL),
- quick_imt_conflict_trampoline_(NULL) {
+ portable_resolution_trampoline_(nullptr),
+ quick_resolution_trampoline_(nullptr),
+ portable_imt_conflict_trampoline_(nullptr),
+ quick_imt_conflict_trampoline_(nullptr) {
CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
+ memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
}
void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class_path) {
@@ -335,54 +335,54 @@
// Object, String and DexCache need to be rerun through FindSystemClass to finish init
java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self);
- mirror::Class* Object_class = FindSystemClass("Ljava/lang/Object;");
+ mirror::Class* Object_class = FindSystemClass(self, "Ljava/lang/Object;");
CHECK_EQ(java_lang_Object.get(), Object_class);
CHECK_EQ(java_lang_Object->GetObjectSize(), sizeof(mirror::Object));
java_lang_String->SetStatus(mirror::Class::kStatusNotReady, self);
- mirror::Class* String_class = FindSystemClass("Ljava/lang/String;");
+ mirror::Class* String_class = FindSystemClass(self, "Ljava/lang/String;");
CHECK_EQ(java_lang_String.get(), String_class);
CHECK_EQ(java_lang_String->GetObjectSize(), sizeof(mirror::String));
java_lang_DexCache->SetStatus(mirror::Class::kStatusNotReady, self);
- mirror::Class* DexCache_class = FindSystemClass("Ljava/lang/DexCache;");
+ mirror::Class* DexCache_class = FindSystemClass(self, "Ljava/lang/DexCache;");
CHECK_EQ(java_lang_String.get(), String_class);
CHECK_EQ(java_lang_DexCache.get(), DexCache_class);
CHECK_EQ(java_lang_DexCache->GetObjectSize(), sizeof(mirror::DexCache));
// Setup the primitive array type classes - can't be done until Object has a vtable.
- SetClassRoot(kBooleanArrayClass, FindSystemClass("[Z"));
+ SetClassRoot(kBooleanArrayClass, FindSystemClass(self, "[Z"));
mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
- SetClassRoot(kByteArrayClass, FindSystemClass("[B"));
+ SetClassRoot(kByteArrayClass, FindSystemClass(self, "[B"));
mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
- mirror::Class* found_char_array_class = FindSystemClass("[C");
+ mirror::Class* found_char_array_class = FindSystemClass(self, "[C");
CHECK_EQ(char_array_class.get(), found_char_array_class);
- SetClassRoot(kShortArrayClass, FindSystemClass("[S"));
+ SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S"));
mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
- mirror::Class* found_int_array_class = FindSystemClass("[I");
+ mirror::Class* found_int_array_class = FindSystemClass(self, "[I");
CHECK_EQ(int_array_class.get(), found_int_array_class);
- SetClassRoot(kLongArrayClass, FindSystemClass("[J"));
+ SetClassRoot(kLongArrayClass, FindSystemClass(self, "[J"));
mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass));
- SetClassRoot(kFloatArrayClass, FindSystemClass("[F"));
+ SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F"));
mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass));
- SetClassRoot(kDoubleArrayClass, FindSystemClass("[D"));
+ SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D"));
mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass));
- mirror::Class* found_class_array_class = FindSystemClass("[Ljava/lang/Class;");
+ mirror::Class* found_class_array_class = FindSystemClass(self, "[Ljava/lang/Class;");
CHECK_EQ(class_array_class.get(), found_class_array_class);
- mirror::Class* found_object_array_class = FindSystemClass("[Ljava/lang/Object;");
+ mirror::Class* found_object_array_class = FindSystemClass(self, "[Ljava/lang/Object;");
CHECK_EQ(object_array_class.get(), found_object_array_class);
// Setup the single, global copy of "iftable".
- mirror::Class* java_lang_Cloneable = FindSystemClass("Ljava/lang/Cloneable;");
+ mirror::Class* java_lang_Cloneable = FindSystemClass(self, "Ljava/lang/Cloneable;");
CHECK(java_lang_Cloneable != NULL);
- mirror::Class* java_io_Serializable = FindSystemClass("Ljava/io/Serializable;");
+ mirror::Class* java_io_Serializable = FindSystemClass(self, "Ljava/io/Serializable;");
CHECK(java_io_Serializable != NULL);
// We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to
// crawl up and explicitly list all of the supers as well.
@@ -398,73 +398,73 @@
CHECK_EQ(java_io_Serializable, kh.GetDirectInterface(1));
// Run Class, ArtField, and ArtMethod through FindSystemClass. This initializes their
// dex_cache_ fields and register them in class_table_.
- mirror::Class* Class_class = FindSystemClass("Ljava/lang/Class;");
+ mirror::Class* Class_class = FindSystemClass(self, "Ljava/lang/Class;");
CHECK_EQ(java_lang_Class.get(), Class_class);
java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusNotReady, self);
- mirror::Class* Art_method_class = FindSystemClass("Ljava/lang/reflect/ArtMethod;");
+ mirror::Class* Art_method_class = FindSystemClass(self, "Ljava/lang/reflect/ArtMethod;");
CHECK_EQ(java_lang_reflect_ArtMethod.get(), Art_method_class);
java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusNotReady, self);
- mirror::Class* Art_field_class = FindSystemClass("Ljava/lang/reflect/ArtField;");
+ mirror::Class* Art_field_class = FindSystemClass(self, "Ljava/lang/reflect/ArtField;");
CHECK_EQ(java_lang_reflect_ArtField.get(), Art_field_class);
- mirror::Class* String_array_class = FindSystemClass(class_roots_descriptors_[kJavaLangStringArrayClass]);
+ mirror::Class* String_array_class = FindSystemClass(self, class_roots_descriptors_[kJavaLangStringArrayClass]);
CHECK_EQ(object_array_string.get(), String_array_class);
mirror::Class* Art_method_array_class =
- FindSystemClass(class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]);
+ FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]);
CHECK_EQ(object_array_art_method.get(), Art_method_array_class);
mirror::Class* Art_field_array_class =
- FindSystemClass(class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]);
+ FindSystemClass(self, class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]);
CHECK_EQ(object_array_art_field.get(), Art_field_array_class);
// End of special init trickery, subsequent classes may be loaded via FindSystemClass.
// Create java.lang.reflect.Proxy root.
- mirror::Class* java_lang_reflect_Proxy = FindSystemClass("Ljava/lang/reflect/Proxy;");
+ mirror::Class* java_lang_reflect_Proxy = FindSystemClass(self, "Ljava/lang/reflect/Proxy;");
SetClassRoot(kJavaLangReflectProxy, java_lang_reflect_Proxy);
// java.lang.ref classes need to be specially flagged, but otherwise are normal classes
- mirror::Class* java_lang_ref_Reference = FindSystemClass("Ljava/lang/ref/Reference;");
+ mirror::Class* java_lang_ref_Reference = FindSystemClass(self, "Ljava/lang/ref/Reference;");
SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference);
- mirror::Class* java_lang_ref_FinalizerReference = FindSystemClass("Ljava/lang/ref/FinalizerReference;");
+ mirror::Class* java_lang_ref_FinalizerReference = FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;");
java_lang_ref_FinalizerReference->SetAccessFlags(
java_lang_ref_FinalizerReference->GetAccessFlags() |
kAccClassIsReference | kAccClassIsFinalizerReference);
- mirror::Class* java_lang_ref_PhantomReference = FindSystemClass("Ljava/lang/ref/PhantomReference;");
+ mirror::Class* java_lang_ref_PhantomReference = FindSystemClass(self, "Ljava/lang/ref/PhantomReference;");
java_lang_ref_PhantomReference->SetAccessFlags(
java_lang_ref_PhantomReference->GetAccessFlags() |
kAccClassIsReference | kAccClassIsPhantomReference);
- mirror::Class* java_lang_ref_SoftReference = FindSystemClass("Ljava/lang/ref/SoftReference;");
+ mirror::Class* java_lang_ref_SoftReference = FindSystemClass(self, "Ljava/lang/ref/SoftReference;");
java_lang_ref_SoftReference->SetAccessFlags(
java_lang_ref_SoftReference->GetAccessFlags() | kAccClassIsReference);
- mirror::Class* java_lang_ref_WeakReference = FindSystemClass("Ljava/lang/ref/WeakReference;");
+ mirror::Class* java_lang_ref_WeakReference = FindSystemClass(self, "Ljava/lang/ref/WeakReference;");
java_lang_ref_WeakReference->SetAccessFlags(
java_lang_ref_WeakReference->GetAccessFlags() |
kAccClassIsReference | kAccClassIsWeakReference);
// Setup the ClassLoader, verifying the object_size_.
- mirror::Class* java_lang_ClassLoader = FindSystemClass("Ljava/lang/ClassLoader;");
+ mirror::Class* java_lang_ClassLoader = FindSystemClass(self, "Ljava/lang/ClassLoader;");
CHECK_EQ(java_lang_ClassLoader->GetObjectSize(), sizeof(mirror::ClassLoader));
SetClassRoot(kJavaLangClassLoader, java_lang_ClassLoader);
// Set up java.lang.Throwable, java.lang.ClassNotFoundException, and
// java.lang.StackTraceElement as a convenience.
- SetClassRoot(kJavaLangThrowable, FindSystemClass("Ljava/lang/Throwable;"));
+ SetClassRoot(kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;"));
mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
- SetClassRoot(kJavaLangClassNotFoundException, FindSystemClass("Ljava/lang/ClassNotFoundException;"));
- SetClassRoot(kJavaLangStackTraceElement, FindSystemClass("Ljava/lang/StackTraceElement;"));
- SetClassRoot(kJavaLangStackTraceElementArrayClass, FindSystemClass("[Ljava/lang/StackTraceElement;"));
+ SetClassRoot(kJavaLangClassNotFoundException, FindSystemClass(self, "Ljava/lang/ClassNotFoundException;"));
+ SetClassRoot(kJavaLangStackTraceElement, FindSystemClass(self, "Ljava/lang/StackTraceElement;"));
+ SetClassRoot(kJavaLangStackTraceElementArrayClass, FindSystemClass(self, "[Ljava/lang/StackTraceElement;"));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
- FinishInit();
+ FinishInit(self);
VLOG(startup) << "ClassLinker::InitFromCompiler exiting";
}
-void ClassLinker::FinishInit() {
+void ClassLinker::FinishInit(Thread* self) {
VLOG(startup) << "ClassLinker::FinishInit entering";
// Let the heap know some key offsets into java.lang.ref instances
@@ -473,7 +473,7 @@
// fully initialized
mirror::Class* java_lang_ref_Reference = GetClassRoot(kJavaLangRefReference);
mirror::Class* java_lang_ref_FinalizerReference =
- FindSystemClass("Ljava/lang/ref/FinalizerReference;");
+ FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;");
mirror::ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0);
FieldHelper fh(pendingNext);
@@ -543,108 +543,45 @@
const char* oat_cache_filename,
std::string* error_msg) {
Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC.
- std::string dex2oat_string(GetAndroidRoot());
- dex2oat_string += (kIsDebugBuild ? "/bin/dex2oatd" : "/bin/dex2oat");
- const char* dex2oat = dex2oat_string.c_str();
-
- const char* class_path = Runtime::Current()->GetClassPathString().c_str();
+ std::string dex2oat(GetAndroidRoot());
+ dex2oat += (kIsDebugBuild ? "/bin/dex2oatd" : "/bin/dex2oat");
gc::Heap* heap = Runtime::Current()->GetHeap();
- std::string boot_image_option_string("--boot-image=");
- boot_image_option_string += heap->GetImageSpace()->GetImageFilename();
- const char* boot_image_option = boot_image_option_string.c_str();
+ std::string boot_image_option("--boot-image=");
+ boot_image_option += heap->GetImageSpace()->GetImageFilename();
- std::string dex_file_option_string("--dex-file=");
- dex_file_option_string += dex_filename;
- const char* dex_file_option = dex_file_option_string.c_str();
+ std::string dex_file_option("--dex-file=");
+ dex_file_option += dex_filename;
- std::string oat_fd_option_string("--oat-fd=");
- StringAppendF(&oat_fd_option_string, "%d", oat_fd);
- const char* oat_fd_option = oat_fd_option_string.c_str();
+ std::string oat_fd_option("--oat-fd=");
+ StringAppendF(&oat_fd_option, "%d", oat_fd);
- std::string oat_location_option_string("--oat-location=");
- oat_location_option_string += oat_cache_filename;
- const char* oat_location_option = oat_location_option_string.c_str();
+ std::string oat_location_option("--oat-location=");
+ oat_location_option += oat_cache_filename;
- std::string oat_compiler_filter_string("-compiler-filter:");
- Runtime::CompilerFilter filter = Runtime::Current()->GetCompilerFilter();
- switch (filter) {
- case Runtime::kInterpretOnly:
- oat_compiler_filter_string += "interpret-only";
- break;
- case Runtime::kSpace:
- oat_compiler_filter_string += "space";
- break;
- case Runtime::kBalanced:
- oat_compiler_filter_string += "balanced";
- break;
- case Runtime::kSpeed:
- oat_compiler_filter_string += "speed";
- break;
- case Runtime::kEverything:
- oat_compiler_filter_string += "everything";
- break;
- default:
- LOG(FATAL) << "Unexpected case: " << filter;
+ std::vector<std::string> argv;
+ argv.push_back(dex2oat);
+ argv.push_back("--runtime-arg");
+ argv.push_back("-Xms64m");
+ argv.push_back("--runtime-arg");
+ argv.push_back("-Xmx64m");
+ argv.push_back("--runtime-arg");
+ argv.push_back("-classpath");
+ argv.push_back("--runtime-arg");
+ argv.push_back(Runtime::Current()->GetClassPathString());
+ if (!kIsTargetBuild) {
+ argv.push_back("--host");
}
- const char* oat_compiler_filter_option = oat_compiler_filter_string.c_str();
-
- // fork and exec dex2oat
- pid_t pid = fork();
- if (pid == 0) {
- // no allocation allowed between fork and exec
-
- // change process groups, so we don't get reaped by ProcessManager
- setpgid(0, 0);
-
- // gLogVerbosity.class_linker = true;
- VLOG(class_linker) << dex2oat
- << " --runtime-arg -Xms64m"
- << " --runtime-arg -Xmx64m"
- << " --runtime-arg -classpath"
- << " --runtime-arg " << class_path
- << " --runtime-arg " << oat_compiler_filter_option
-#if !defined(ART_TARGET)
- << " --host"
-#endif
- << " " << boot_image_option
- << " " << dex_file_option
- << " " << oat_fd_option
- << " " << oat_location_option;
-
- execl(dex2oat, dex2oat,
- "--runtime-arg", "-Xms64m",
- "--runtime-arg", "-Xmx64m",
- "--runtime-arg", "-classpath",
- "--runtime-arg", class_path,
- "--runtime-arg", oat_compiler_filter_option,
-#if !defined(ART_TARGET)
- "--host",
-#endif
- boot_image_option,
- dex_file_option,
- oat_fd_option,
- oat_location_option,
- NULL);
-
- PLOG(FATAL) << "execl(" << dex2oat << ") failed";
- return false;
- } else {
- // wait for dex2oat to finish
- int status;
- pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
- if (got_pid != pid) {
- *error_msg = StringPrintf("Failed to create oat file. Waitpid failed: wanted %d, got %d",
- pid, got_pid);
- return false;
- }
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- *error_msg = StringPrintf("Failed to create oat file. %s failed with dex-file '%s'",
- dex2oat, dex_filename);
- return false;
- }
+ argv.push_back(boot_image_option);
+ argv.push_back(dex_file_option);
+ argv.push_back(oat_fd_option);
+ argv.push_back(oat_location_option);
+ const std::vector<std::string>& compiler_options = Runtime::Current()->GetCompilerOptions();
+ for (size_t i = 0; compiler_options.size(); ++i) {
+ argv.push_back(compiler_options[i].c_str());
}
- return true;
+
+ return Exec(argv, error_msg);
}
const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
@@ -1111,7 +1048,7 @@
mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
- FinishInit();
+ FinishInit(self);
VLOG(startup) << "ClassLinker::InitFromImage exiting";
}
@@ -1150,6 +1087,13 @@
}
callback(reinterpret_cast<mirror::Object**>(&array_iftable_), arg, 0, kRootVMInternal);
DCHECK(array_iftable_ != nullptr);
+ for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
+ if (find_array_class_cache_[i] != nullptr) {
+ callback(reinterpret_cast<mirror::Object**>(&find_array_class_cache_[i]), arg, 0,
+ kRootVMInternal);
+ DCHECK(find_array_class_cache_[i] != nullptr);
+ }
+ }
}
void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) {
@@ -1307,21 +1251,10 @@
return klass;
}
-bool ClassLinker::IsInBootClassPath(const char* descriptor) {
- DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
- return pair.second != NULL;
-}
-
-mirror::Class* ClassLinker::FindSystemClass(const char* descriptor) {
- SirtRef<mirror::ClassLoader> class_loader(Thread::Current(), nullptr);
- return FindClass(descriptor, class_loader);
-}
-
-mirror::Class* ClassLinker::FindClass(const char* descriptor,
+mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
const SirtRef<mirror::ClassLoader>& class_loader) {
DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
- Thread* self = Thread::Current();
- DCHECK(self != NULL);
+ DCHECK(self != nullptr);
self->AssertNoPendingException();
if (descriptor[1] == '\0') {
// only the descriptors of primitive types should be 1 character long, also avoid class lookup
@@ -1335,7 +1268,7 @@
}
// Class is not yet loaded.
if (descriptor[0] == '[') {
- return CreateArrayClass(descriptor, class_loader);
+ return CreateArrayClass(self, descriptor, class_loader);
} else if (class_loader.get() == nullptr) {
DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
if (pair.second != NULL) {
@@ -1346,7 +1279,7 @@
// First try the boot class path, we check the descriptor first to avoid an unnecessary
// throw of a NoClassDefFoundError.
if (IsInBootClassPath(descriptor)) {
- mirror::Class* system_class = FindSystemClass(descriptor);
+ mirror::Class* system_class = FindSystemClass(self, descriptor);
CHECK(system_class != NULL);
return system_class;
}
@@ -1365,7 +1298,7 @@
}
} else {
- ScopedObjectAccessUnchecked soa(self->GetJniEnv());
+ ScopedObjectAccessUnchecked soa(self);
ScopedLocalRef<jobject> class_loader_object(soa.Env(),
soa.AddLocalReference<jobject>(class_loader.get()));
std::string class_name_string(DescriptorToDot(descriptor));
@@ -1382,7 +1315,7 @@
WellKnownClasses::java_lang_ClassLoader_loadClass,
class_name_object.get()));
}
- if (soa.Self()->IsExceptionPending()) {
+ if (self->IsExceptionPending()) {
// If the ClassLoader threw, pass that exception up.
return NULL;
} else if (result.get() == NULL) {
@@ -2133,12 +2066,11 @@
// array class; that always comes from the base element class.
//
// Returns NULL with an exception raised on failure.
-mirror::Class* ClassLinker::CreateArrayClass(const char* descriptor,
+mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descriptor,
const SirtRef<mirror::ClassLoader>& class_loader) {
// Identify the underlying component type
CHECK_EQ('[', descriptor[0]);
- Thread* self = Thread::Current();
- SirtRef<mirror::Class> component_type(self, FindClass(descriptor + 1, class_loader));
+ SirtRef<mirror::Class> component_type(self, FindClass(self, descriptor + 1, class_loader));
if (component_type.get() == nullptr) {
DCHECK(self->IsExceptionPending());
return nullptr;
@@ -2308,7 +2240,7 @@
CHECK(klass == existing);
}
}
- Runtime::Current()->GetHeap()->VerifyObject(klass);
+ VerifyObject(klass);
class_table_.insert(std::make_pair(hash, klass));
class_table_dirty_ = true;
return NULL;
@@ -3242,7 +3174,7 @@
for (int i = super->GetVTable()->GetLength() - 1; i >= 0; --i) {
mirror::ArtMethod* method = klass->GetVTable()->Get(i);
if (method != super->GetVTable()->Get(i) &&
- !IsSameMethodSignatureInDifferentClassContexts(method, super.get(), klass.get())) {
+ !IsSameMethodSignatureInDifferentClassContexts(self, method, super.get(), klass.get())) {
ThrowLinkageError(klass.get(), "Class %s method %s resolves differently in superclass %s",
PrettyDescriptor(klass.get()).c_str(), PrettyMethod(method).c_str(),
PrettyDescriptor(super.get()).c_str());
@@ -3255,7 +3187,7 @@
if (klass->GetClassLoader() != interface->GetClassLoader()) {
for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) {
mirror::ArtMethod* method = klass->GetIfTable()->GetMethodArray(i)->Get(j);
- if (!IsSameMethodSignatureInDifferentClassContexts(method, interface.get(),
+ if (!IsSameMethodSignatureInDifferentClassContexts(self, method, interface.get(),
method->GetDeclaringClass())) {
ThrowLinkageError(klass.get(), "Class %s method %s resolves differently in interface %s",
PrettyDescriptor(method->GetDeclaringClass()).c_str(),
@@ -3271,13 +3203,13 @@
// Returns true if classes referenced by the signature of the method are the
// same classes in klass1 as they are in klass2.
-bool ClassLinker::IsSameMethodSignatureInDifferentClassContexts(mirror::ArtMethod* method,
+bool ClassLinker::IsSameMethodSignatureInDifferentClassContexts(Thread* self,
+ mirror::ArtMethod* method,
mirror::Class* klass1,
mirror::Class* klass2) {
if (klass1 == klass2) {
return true;
}
- Thread* self = Thread::Current();
CHECK(klass1 != nullptr);
CHECK(klass2 != nullptr);
SirtRef<mirror::ClassLoader> loader1(self, klass1->GetClassLoader());
@@ -3292,7 +3224,7 @@
}
if (descriptor[0] == 'L' || descriptor[0] == '[') {
// Found a non-primitive type.
- if (!IsSameDescriptorInDifferentClassContexts(descriptor, loader1, loader2)) {
+ if (!IsSameDescriptorInDifferentClassContexts(self, descriptor, loader1, loader2)) {
return false;
}
}
@@ -3300,7 +3232,7 @@
// Check the return type
const char* descriptor = dex_file.GetReturnTypeDescriptor(proto_id);
if (descriptor[0] == 'L' || descriptor[0] == '[') {
- if (!IsSameDescriptorInDifferentClassContexts(descriptor, loader1, loader2)) {
+ if (!IsSameDescriptorInDifferentClassContexts(self, descriptor, loader1, loader2)) {
return false;
}
}
@@ -3308,16 +3240,15 @@
}
// Returns true if the descriptor resolves to the same class in the context of loader1 and loader2.
-bool ClassLinker::IsSameDescriptorInDifferentClassContexts(const char* descriptor,
+bool ClassLinker::IsSameDescriptorInDifferentClassContexts(Thread* self, const char* descriptor,
SirtRef<mirror::ClassLoader>& loader1,
SirtRef<mirror::ClassLoader>& loader2) {
CHECK(descriptor != nullptr);
- Thread* self = Thread::Current();
- SirtRef<mirror::Class> found1(self, FindClass(descriptor, loader1));
+ SirtRef<mirror::Class> found1(self, FindClass(self, descriptor, loader1));
if (found1.get() == nullptr) {
self->ClearException();
}
- mirror::Class* found2 = FindClass(descriptor, loader2);
+ mirror::Class* found2 = FindClass(self, descriptor, loader2);
if (found2 == nullptr) {
self->ClearException();
}
@@ -4117,22 +4048,22 @@
DCHECK(dex_cache.get() != NULL);
mirror::Class* resolved = dex_cache->GetResolvedType(type_idx);
if (resolved == NULL) {
+ Thread* self = Thread::Current();
const char* descriptor = dex_file.StringByTypeIdx(type_idx);
- resolved = FindClass(descriptor, class_loader);
+ resolved = FindClass(self, descriptor, class_loader);
if (resolved != NULL) {
// TODO: we used to throw here if resolved's class loader was not the
// boot class loader. This was to permit different classes with the
// same name to be loaded simultaneously by different loaders
dex_cache->SetResolvedType(type_idx, resolved);
} else {
- Thread* self = Thread::Current();
CHECK(self->IsExceptionPending())
<< "Expected pending exception for failed resolution of: " << descriptor;
// Convert a ClassNotFoundException to a NoClassDefFoundError.
SirtRef<mirror::Throwable> cause(self, self->GetException(NULL));
if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) {
DCHECK(resolved == NULL); // No SirtRef needed to preserve resolved.
- Thread::Current()->ClearException();
+ self->ClearException();
ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor);
self->GetException(NULL)->SetCause(cause.get());
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 0745ee2..f346102 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -72,10 +72,17 @@
// Finds a class by its descriptor, loading it if necessary.
// If class_loader is null, searches boot_class_path_.
- mirror::Class* FindClass(const char* descriptor, const SirtRef<mirror::ClassLoader>& class_loader)
+ mirror::Class* FindClass(Thread* self, const char* descriptor,
+ const SirtRef<mirror::ClassLoader>& class_loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::Class* FindSystemClass(const char* descriptor)
+ // Finds a class by its descriptor using the "system" class loader, ie by searching the
+ // boot_class_path_.
+ mirror::Class* FindSystemClass(Thread* self, const char* descriptor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Finds the array class given for the element class.
+ mirror::Class* FindArrayClass(Thread* self, mirror::Class* element_class)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Reutrns true if the class linker is initialized.
@@ -378,7 +385,7 @@
LOCKS_EXCLUDED(dex_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void FinishInit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void FinishInit(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// For early bootstrapping by Init
mirror::Class* AllocClass(Thread* self, mirror::Class* java_lang_Class, size_t class_size)
@@ -399,7 +406,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::Class* CreateArrayClass(const char* descriptor,
+ mirror::Class* CreateArrayClass(Thread* self, const char* descriptor,
const SirtRef<mirror::ClassLoader>& class_loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -449,12 +456,12 @@
bool ValidateSuperClassDescriptors(const SirtRef<mirror::Class>& klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsSameDescriptorInDifferentClassContexts(const char* descriptor,
+ bool IsSameDescriptorInDifferentClassContexts(Thread* self, const char* descriptor,
SirtRef<mirror::ClassLoader>& class_loader1,
SirtRef<mirror::ClassLoader>& class_loader2)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsSameMethodSignatureInDifferentClassContexts(mirror::ArtMethod* method,
+ bool IsSameMethodSignatureInDifferentClassContexts(Thread* self, mirror::ArtMethod* method,
mirror::Class* klass1,
mirror::Class* klass2)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -617,8 +624,15 @@
return descriptor;
}
+ // The interface table used by all arrays.
mirror::IfTable* array_iftable_;
+ // A cache of the last FindArrayClass results. The cache serves to avoid creating array class
+ // descriptors for the sake of performing FindClass.
+ static constexpr size_t kFindArrayCacheSize = 16;
+ mirror::Class* find_array_class_cache_[kFindArrayCacheSize];
+ size_t find_array_class_cache_next_victim_;
+
bool init_done_;
bool dex_caches_dirty_ GUARDED_BY(dex_lock_);
bool class_table_dirty_ GUARDED_BY(Locks::classlinker_classes_lock_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index ebf02fe..d6a67cc 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -41,18 +41,20 @@
protected:
void AssertNonExistentClass(const std::string& descriptor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- EXPECT_TRUE(class_linker_->FindSystemClass(descriptor.c_str()) == NULL);
Thread* self = Thread::Current();
+ EXPECT_TRUE(class_linker_->FindSystemClass(self, descriptor.c_str()) == NULL);
EXPECT_TRUE(self->IsExceptionPending());
mirror::Object* exception = self->GetException(NULL);
self->ClearException();
- mirror::Class* exception_class = class_linker_->FindSystemClass("Ljava/lang/NoClassDefFoundError;");
+ mirror::Class* exception_class =
+ class_linker_->FindSystemClass(self, "Ljava/lang/NoClassDefFoundError;");
EXPECT_TRUE(exception->InstanceOf(exception_class));
}
void AssertPrimitiveClass(const std::string& descriptor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- AssertPrimitiveClass(descriptor, class_linker_->FindSystemClass(descriptor.c_str()));
+ Thread* self = Thread::Current();
+ AssertPrimitiveClass(descriptor, class_linker_->FindSystemClass(self, descriptor.c_str()));
}
void AssertPrimitiveClass(const std::string& descriptor, mirror::Class* primitive)
@@ -98,7 +100,7 @@
Thread* self = Thread::Current();
SirtRef<mirror::ClassLoader> loader(self, class_loader);
SirtRef<mirror::Class> array(self,
- class_linker_->FindClass(array_descriptor.c_str(), loader));
+ class_linker_->FindClass(self, array_descriptor.c_str(), loader));
ClassHelper array_component_ch(array->GetComponentType());
EXPECT_STREQ(component_type.c_str(), array_component_ch.GetDescriptor());
EXPECT_EQ(class_loader, array->GetClassLoader());
@@ -115,7 +117,8 @@
EXPECT_TRUE(array->GetClass()->GetSuperClass() != NULL);
ASSERT_STREQ(array_descriptor.c_str(), kh.GetDescriptor());
EXPECT_TRUE(array->GetSuperClass() != NULL);
- EXPECT_EQ(class_linker_->FindSystemClass("Ljava/lang/Object;"), array->GetSuperClass());
+ Thread* self = Thread::Current();
+ EXPECT_EQ(class_linker_->FindSystemClass(self, "Ljava/lang/Object;"), array->GetSuperClass());
EXPECT_TRUE(array->HasSuperClass());
ASSERT_TRUE(array->GetComponentType() != NULL);
kh.ChangeClass(array->GetComponentType());
@@ -147,6 +150,7 @@
kh.ChangeClass(array.get());
kh.ChangeClass(kh.GetDirectInterface(1));
EXPECT_STREQ(kh.GetDescriptor(), "Ljava/io/Serializable;");
+ EXPECT_EQ(class_linker_->FindArrayClass(self, array->GetComponentType()), array.get());
}
void AssertMethod(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -300,8 +304,8 @@
void AssertDexFileClass(mirror::ClassLoader* class_loader, const std::string& descriptor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ASSERT_TRUE(descriptor != NULL);
- SirtRef<mirror::Class> klass(Thread::Current(),
- class_linker_->FindSystemClass(descriptor.c_str()));
+ Thread* self = Thread::Current();
+ SirtRef<mirror::Class> klass(self, class_linker_->FindSystemClass(self, descriptor.c_str()));
ASSERT_TRUE(klass.get() != nullptr);
EXPECT_STREQ(descriptor.c_str(), ClassHelper(klass.get()).GetDescriptor());
EXPECT_EQ(class_loader, klass->GetClassLoader());
@@ -359,7 +363,9 @@
std::vector<CheckOffset> offsets;
bool Check() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Class* klass = Runtime::Current()->GetClassLinker()->FindSystemClass(class_descriptor.c_str());
+ Thread* self = Thread::Current();
+ mirror::Class* klass =
+ Runtime::Current()->GetClassLinker()->FindSystemClass(self, class_descriptor.c_str());
CHECK(klass != NULL) << class_descriptor;
bool error = false;
@@ -646,12 +652,12 @@
ScopedObjectAccess soa(Thread::Current());
SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Nested")));
- mirror::Class* outer = class_linker_->FindClass("LNested;", class_loader);
+ mirror::Class* outer = class_linker_->FindClass(soa.Self(), "LNested;", class_loader);
ASSERT_TRUE(outer != NULL);
EXPECT_EQ(0U, outer->NumVirtualMethods());
EXPECT_EQ(1U, outer->NumDirectMethods());
- mirror::Class* inner = class_linker_->FindClass("LNested$Inner;", class_loader);
+ mirror::Class* inner = class_linker_->FindClass(soa.Self(), "LNested$Inner;", class_loader);
ASSERT_TRUE(inner != NULL);
EXPECT_EQ(0U, inner->NumVirtualMethods());
EXPECT_EQ(1U, inner->NumDirectMethods());
@@ -673,7 +679,7 @@
TEST_F(ClassLinkerTest, FindClass) {
ScopedObjectAccess soa(Thread::Current());
- mirror::Class* JavaLangObject = class_linker_->FindSystemClass("Ljava/lang/Object;");
+ mirror::Class* JavaLangObject = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
ClassHelper kh(JavaLangObject);
ASSERT_TRUE(JavaLangObject != NULL);
ASSERT_TRUE(JavaLangObject->GetClass() != NULL);
@@ -710,7 +716,7 @@
SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass")));
AssertNonExistentClass("LMyClass;");
- mirror::Class* MyClass = class_linker_->FindClass("LMyClass;", class_loader);
+ mirror::Class* MyClass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader);
kh.ChangeClass(MyClass);
ASSERT_TRUE(MyClass != NULL);
ASSERT_TRUE(MyClass->GetClass() != NULL);
@@ -761,7 +767,7 @@
// start of the object
TEST_F(ClassLinkerTest, ValidateObjectArrayElementsOffset) {
ScopedObjectAccess soa(Thread::Current());
- mirror::Class* array_class = class_linker_->FindSystemClass("[Ljava/lang/String;");
+ mirror::Class* array_class = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
mirror::ObjectArray<mirror::String>* array =
mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), array_class, 0);
uintptr_t data_offset =
@@ -777,27 +783,27 @@
TEST_F(ClassLinkerTest, ValidatePrimitiveArrayElementsOffset) {
ScopedObjectAccess soa(Thread::Current());
SirtRef<mirror::LongArray> long_array(soa.Self(), mirror::LongArray::Alloc(soa.Self(), 0));
- EXPECT_EQ(class_linker_->FindSystemClass("[J"), long_array->GetClass());
+ EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[J"), long_array->GetClass());
uintptr_t data_offset = reinterpret_cast<uintptr_t>(long_array->GetData());
EXPECT_TRUE(IsAligned<8>(data_offset)); // Longs require 8 byte alignment
SirtRef<mirror::DoubleArray> double_array(soa.Self(), mirror::DoubleArray::Alloc(soa.Self(), 0));
- EXPECT_EQ(class_linker_->FindSystemClass("[D"), double_array->GetClass());
+ EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[D"), double_array->GetClass());
data_offset = reinterpret_cast<uintptr_t>(double_array->GetData());
EXPECT_TRUE(IsAligned<8>(data_offset)); // Doubles require 8 byte alignment
SirtRef<mirror::IntArray> int_array(soa.Self(), mirror::IntArray::Alloc(soa.Self(), 0));
- EXPECT_EQ(class_linker_->FindSystemClass("[I"), int_array->GetClass());
+ EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[I"), int_array->GetClass());
data_offset = reinterpret_cast<uintptr_t>(int_array->GetData());
EXPECT_TRUE(IsAligned<4>(data_offset)); // Ints require 4 byte alignment
SirtRef<mirror::CharArray> char_array(soa.Self(), mirror::CharArray::Alloc(soa.Self(), 0));
- EXPECT_EQ(class_linker_->FindSystemClass("[C"), char_array->GetClass());
+ EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[C"), char_array->GetClass());
data_offset = reinterpret_cast<uintptr_t>(char_array->GetData());
EXPECT_TRUE(IsAligned<2>(data_offset)); // Chars require 2 byte alignment
SirtRef<mirror::ShortArray> short_array(soa.Self(), mirror::ShortArray::Alloc(soa.Self(), 0));
- EXPECT_EQ(class_linker_->FindSystemClass("[S"), short_array->GetClass());
+ EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "[S"), short_array->GetClass());
data_offset = reinterpret_cast<uintptr_t>(short_array->GetData());
EXPECT_TRUE(IsAligned<2>(data_offset)); // Shorts require 2 byte alignment
@@ -810,28 +816,28 @@
ScopedObjectAccess soa(Thread::Current());
SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr);
mirror::Class* c;
- c = class_linker_->FindClass("Ljava/lang/Boolean;", class_loader);
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Boolean;", class_loader);
FieldHelper fh(c->GetIFields()->Get(0));
EXPECT_STREQ("value", fh.GetName());
- c = class_linker_->FindClass("Ljava/lang/Byte;", class_loader);
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Byte;", class_loader);
fh.ChangeField(c->GetIFields()->Get(0));
EXPECT_STREQ("value", fh.GetName());
- c = class_linker_->FindClass("Ljava/lang/Character;", class_loader);
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Character;", class_loader);
fh.ChangeField(c->GetIFields()->Get(0));
EXPECT_STREQ("value", fh.GetName());
- c = class_linker_->FindClass("Ljava/lang/Double;", class_loader);
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Double;", class_loader);
fh.ChangeField(c->GetIFields()->Get(0));
EXPECT_STREQ("value", fh.GetName());
- c = class_linker_->FindClass("Ljava/lang/Float;", class_loader);
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Float;", class_loader);
fh.ChangeField(c->GetIFields()->Get(0));
EXPECT_STREQ("value", fh.GetName());
- c = class_linker_->FindClass("Ljava/lang/Integer;", class_loader);
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Integer;", class_loader);
fh.ChangeField(c->GetIFields()->Get(0));
EXPECT_STREQ("value", fh.GetName());
- c = class_linker_->FindClass("Ljava/lang/Long;", class_loader);
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Long;", class_loader);
fh.ChangeField(c->GetIFields()->Get(0));
EXPECT_STREQ("value", fh.GetName());
- c = class_linker_->FindClass("Ljava/lang/Short;", class_loader);
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Short;", class_loader);
fh.ChangeField(c->GetIFields()->Get(0));
EXPECT_STREQ("value", fh.GetName());
}
@@ -840,8 +846,8 @@
ScopedObjectAccess soa(Thread::Current());
SirtRef<mirror::ClassLoader> class_loader_1(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass")));
SirtRef<mirror::ClassLoader> class_loader_2(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass")));
- mirror::Class* MyClass_1 = class_linker_->FindClass("LMyClass;", class_loader_1);
- mirror::Class* MyClass_2 = class_linker_->FindClass("LMyClass;", class_loader_2);
+ mirror::Class* MyClass_1 = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_1);
+ mirror::Class* MyClass_2 = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader_2);
EXPECT_TRUE(MyClass_1 != NULL);
EXPECT_TRUE(MyClass_2 != NULL);
EXPECT_NE(MyClass_1, MyClass_2);
@@ -849,8 +855,10 @@
TEST_F(ClassLinkerTest, StaticFields) {
ScopedObjectAccess soa(Thread::Current());
- SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Statics")));
- SirtRef<mirror::Class> statics(soa.Self(), class_linker_->FindClass("LStatics;", class_loader));
+ SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
+ soa.Decode<mirror::ClassLoader*>(LoadDex("Statics")));
+ SirtRef<mirror::Class> statics(soa.Self(), class_linker_->FindClass(soa.Self(), "LStatics;",
+ class_loader));
class_linker_->EnsureInitialized(statics, true, true);
// Static final primitives that are initialized by a compile-time constant
@@ -933,11 +941,11 @@
TEST_F(ClassLinkerTest, Interfaces) {
ScopedObjectAccess soa(Thread::Current());
SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Interfaces")));
- mirror::Class* I = class_linker_->FindClass("LInterfaces$I;", class_loader);
- mirror::Class* J = class_linker_->FindClass("LInterfaces$J;", class_loader);
- mirror::Class* K = class_linker_->FindClass("LInterfaces$K;", class_loader);
- mirror::Class* A = class_linker_->FindClass("LInterfaces$A;", class_loader);
- mirror::Class* B = class_linker_->FindClass("LInterfaces$B;", class_loader);
+ mirror::Class* I = class_linker_->FindClass(soa.Self(), "LInterfaces$I;", class_loader);
+ mirror::Class* J = class_linker_->FindClass(soa.Self(), "LInterfaces$J;", class_loader);
+ mirror::Class* K = class_linker_->FindClass(soa.Self(), "LInterfaces$K;", class_loader);
+ mirror::Class* A = class_linker_->FindClass(soa.Self(), "LInterfaces$A;", class_loader);
+ mirror::Class* B = class_linker_->FindClass(soa.Self(), "LInterfaces$B;", class_loader);
EXPECT_TRUE(I->IsAssignableFrom(A));
EXPECT_TRUE(J->IsAssignableFrom(A));
EXPECT_TRUE(J->IsAssignableFrom(K));
@@ -996,7 +1004,7 @@
SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader));
const DexFile* dex_file = Runtime::Current()->GetCompileTimeClassPath(jclass_loader)[0];
CHECK(dex_file != NULL);
- mirror::Class* klass = class_linker_->FindClass("LStaticsFromCode;", class_loader);
+ mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader);
mirror::ArtMethod* clinit = klass->FindClassInitializer();
mirror::ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;");
const DexFile::StringId* string_id = dex_file->FindStringId("LStaticsFromCode;");
@@ -1017,32 +1025,32 @@
mirror::Class* c;
// Object has a finalize method, but we know it's empty.
- c = class_linker_->FindSystemClass("Ljava/lang/Object;");
+ c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
EXPECT_FALSE(c->IsFinalizable());
// Enum has a finalize method to prevent its subclasses from implementing one.
- c = class_linker_->FindSystemClass("Ljava/lang/Enum;");
+ c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Enum;");
EXPECT_FALSE(c->IsFinalizable());
// RoundingMode is an enum.
- c = class_linker_->FindSystemClass("Ljava/math/RoundingMode;");
+ c = class_linker_->FindSystemClass(soa.Self(), "Ljava/math/RoundingMode;");
EXPECT_FALSE(c->IsFinalizable());
// RandomAccessFile extends Object and overrides finalize.
- c = class_linker_->FindSystemClass("Ljava/io/RandomAccessFile;");
+ c = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/RandomAccessFile;");
EXPECT_TRUE(c->IsFinalizable());
// FileInputStream is finalizable and extends InputStream which isn't.
- c = class_linker_->FindSystemClass("Ljava/io/InputStream;");
+ c = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/InputStream;");
EXPECT_FALSE(c->IsFinalizable());
- c = class_linker_->FindSystemClass("Ljava/io/FileInputStream;");
+ c = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/FileInputStream;");
EXPECT_TRUE(c->IsFinalizable());
// ScheduledThreadPoolExecutor doesn't have a finalize method but
// extends ThreadPoolExecutor which does.
- c = class_linker_->FindSystemClass("Ljava/util/concurrent/ThreadPoolExecutor;");
+ c = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/concurrent/ThreadPoolExecutor;");
EXPECT_TRUE(c->IsFinalizable());
- c = class_linker_->FindSystemClass("Ljava/util/concurrent/ScheduledThreadPoolExecutor;");
+ c = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/concurrent/ScheduledThreadPoolExecutor;");
EXPECT_TRUE(c->IsFinalizable());
}
diff --git a/runtime/common_test.h b/runtime/common_test.h
index 7f9b6b1..9eaec46 100644
--- a/runtime/common_test.h
+++ b/runtime/common_test.h
@@ -28,7 +28,9 @@
#include "../compiler/compiler_backend.h"
#include "../compiler/dex/quick/dex_file_to_method_inliner_map.h"
#include "../compiler/dex/verification_results.h"
+#include "../compiler/driver/compiler_callbacks_impl.h"
#include "../compiler/driver/compiler_driver.h"
+#include "../compiler/driver/compiler_options.h"
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/stringprintf.h"
@@ -418,8 +420,9 @@
void MakeExecutable(mirror::ClassLoader* class_loader, const char* class_name)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
std::string class_descriptor(DotToDescriptor(class_name));
- SirtRef<mirror::ClassLoader> loader(Thread::Current(), class_loader);
- mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), loader);
+ Thread* self = Thread::Current();
+ SirtRef<mirror::ClassLoader> loader(self, class_loader);
+ mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader);
CHECK(klass != NULL) << "Class not found " << class_name;
for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
MakeExecutable(klass->GetDirectMethod(i));
@@ -458,11 +461,13 @@
? CompilerBackend::kPortable
: CompilerBackend::kQuick;
- verification_results_.reset(new VerificationResults);
+ compiler_options_.reset(new CompilerOptions);
+ verification_results_.reset(new VerificationResults(compiler_options_.get()));
method_inliner_map_.reset(new DexFileToMethodInlinerMap);
- callbacks_.Reset(verification_results_.get(), method_inliner_map_.get());
+ callbacks_.reset(new CompilerCallbacksImpl(verification_results_.get(),
+ method_inliner_map_.get()));
Runtime::Options options;
- options.push_back(std::make_pair("compilercallbacks", static_cast<CompilerCallbacks*>(&callbacks_)));
+ options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
options.push_back(std::make_pair("bootclasspath", &boot_class_path_));
options.push_back(std::make_pair("-Xcheck:jni", reinterpret_cast<void*>(NULL)));
options.push_back(std::make_pair(min_heap_string.c_str(), reinterpret_cast<void*>(NULL)));
@@ -472,8 +477,8 @@
return;
}
runtime_.reset(Runtime::Current());
- // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
- // give it away now and then switch to a more managable ScopedObjectAccess.
+ // Runtime::Create acquired the mutator_lock_ that is normally given away when we
+ // Runtime::Start, give it away now and then switch to a more managable ScopedObjectAccess.
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
{
ScopedObjectAccess soa(Thread::Current());
@@ -512,7 +517,8 @@
}
class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
timer_.reset(new CumulativeLogger("Compilation times"));
- compiler_driver_.reset(new CompilerDriver(verification_results_.get(),
+ compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+ verification_results_.get(),
method_inliner_map_.get(),
compiler_backend, instruction_set,
instruction_set_features,
@@ -563,9 +569,10 @@
compiler_driver_.reset();
timer_.reset();
- callbacks_.Reset(nullptr, nullptr);
+ callbacks_.reset();
method_inliner_map_.reset();
verification_results_.reset();
+ compiler_options_.reset();
STLDeleteElements(&opened_dex_files_);
Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test
@@ -632,8 +639,9 @@
void CompileClass(mirror::ClassLoader* class_loader, const char* class_name)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
std::string class_descriptor(DotToDescriptor(class_name));
- SirtRef<mirror::ClassLoader> loader(Thread::Current(), class_loader);
- mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), loader);
+ Thread* self = Thread::Current();
+ SirtRef<mirror::ClassLoader> loader(self, class_loader);
+ mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), loader);
CHECK(klass != NULL) << "Class not found " << class_name;
for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
CompileMethod(klass->GetDirectMethod(i));
@@ -656,7 +664,8 @@
const char* method_name, const char* signature)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
std::string class_descriptor(DotToDescriptor(class_name));
- mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), class_loader);
+ Thread* self = Thread::Current();
+ mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader);
CHECK(klass != NULL) << "Class not found " << class_name;
mirror::ArtMethod* method = klass->FindDirectMethod(method_name, signature);
CHECK(method != NULL) << "Direct method not found: "
@@ -668,7 +677,8 @@
const char* method_name, const char* signature)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
std::string class_descriptor(DotToDescriptor(class_name));
- mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), class_loader);
+ Thread* self = Thread::Current();
+ mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader);
CHECK(klass != NULL) << "Class not found " << class_name;
mirror::ArtMethod* method = klass->FindVirtualMethod(method_name, signature);
CHECK(method != NULL) << "Virtual method not found: "
@@ -693,36 +703,6 @@
image_reservation_.reset();
}
- class TestCompilerCallbacks : public CompilerCallbacks {
- public:
- TestCompilerCallbacks() : verification_results_(nullptr), method_inliner_map_(nullptr) {}
-
- void Reset(VerificationResults* verification_results,
- DexFileToMethodInlinerMap* method_inliner_map) {
- verification_results_ = verification_results;
- method_inliner_map_ = method_inliner_map;
- }
-
- virtual bool MethodVerified(verifier::MethodVerifier* verifier)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- CHECK(verification_results_);
- bool result = verification_results_->ProcessVerifiedMethod(verifier);
- if (result && method_inliner_map_ != nullptr) {
- MethodReference ref = verifier->GetMethodReference();
- method_inliner_map_->GetMethodInliner(ref.dex_file)
- ->AnalyseMethodCode(verifier);
- }
- return result;
- }
- virtual void ClassRejected(ClassReference ref) {
- verification_results_->AddRejectedClass(ref);
- }
-
- private:
- VerificationResults* verification_results_;
- DexFileToMethodInlinerMap* method_inliner_map_;
- };
-
std::string android_data_;
std::string dalvik_cache_;
const DexFile* java_lang_dex_file_; // owned by runtime_
@@ -730,9 +710,10 @@
UniquePtr<Runtime> runtime_;
// Owned by the runtime
ClassLinker* class_linker_;
+ UniquePtr<CompilerOptions> compiler_options_;
UniquePtr<VerificationResults> verification_results_;
UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_;
- TestCompilerCallbacks callbacks_;
+ UniquePtr<CompilerCallbacksImpl> callbacks_;
UniquePtr<CompilerDriver> compiler_driver_;
UniquePtr<CumulativeLogger> timer_;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 3c238d6..9f09709 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -289,27 +289,39 @@
return static_cast<JDWP::JdwpTag>(descriptor[0]);
}
-static JDWP::JdwpTag TagFromClass(mirror::Class* c)
+static JDWP::JdwpTag TagFromClass(const ScopedObjectAccessUnchecked& soa, mirror::Class* c)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
CHECK(c != NULL);
if (c->IsArrayClass()) {
return JDWP::JT_ARRAY;
}
-
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
if (c->IsStringClass()) {
return JDWP::JT_STRING;
- } else if (c->IsClassClass()) {
- return JDWP::JT_CLASS_OBJECT;
- } else if (class_linker->FindSystemClass("Ljava/lang/Thread;")->IsAssignableFrom(c)) {
- return JDWP::JT_THREAD;
- } else if (class_linker->FindSystemClass("Ljava/lang/ThreadGroup;")->IsAssignableFrom(c)) {
- return JDWP::JT_THREAD_GROUP;
- } else if (class_linker->FindSystemClass("Ljava/lang/ClassLoader;")->IsAssignableFrom(c)) {
- return JDWP::JT_CLASS_LOADER;
- } else {
- return JDWP::JT_OBJECT;
}
+ if (c->IsClassClass()) {
+ return JDWP::JT_CLASS_OBJECT;
+ }
+ {
+ mirror::Class* thread_class = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
+ if (thread_class->IsAssignableFrom(c)) {
+ return JDWP::JT_THREAD;
+ }
+ }
+ {
+ mirror::Class* thread_group_class =
+ soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+ if (thread_group_class->IsAssignableFrom(c)) {
+ return JDWP::JT_THREAD_GROUP;
+ }
+ }
+ {
+ mirror::Class* class_loader_class =
+ soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ClassLoader);
+ if (class_loader_class->IsAssignableFrom(c)) {
+ return JDWP::JT_CLASS_LOADER;
+ }
+ }
+ return JDWP::JT_OBJECT;
}
/*
@@ -320,9 +332,9 @@
*
* Null objects are tagged JT_OBJECT.
*/
-static JDWP::JdwpTag TagFromObject(mirror::Object* o)
+static JDWP::JdwpTag TagFromObject(const ScopedObjectAccessUnchecked& soa, mirror::Object* o)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(o->GetClass());
+ return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(soa, o->GetClass());
}
static bool IsPrimitiveTag(JDWP::JdwpTag tag) {
@@ -587,20 +599,24 @@
runtime->GetThreadList()->SuspendAll();
Thread* self = Thread::Current();
ThreadState old_state = self->SetStateUnsafe(kRunnable);
- {
- // Since we're going to disable deoptimization, we clear the deoptimization requests queue.
- // This prevent us from having any pending deoptimization request when the debugger attaches to
- // us again while no event has been requested yet.
- MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
- gDeoptimizationRequests.clear();
+
+ // Debugger may not be active at this point.
+ if (gDebuggerActive) {
+ {
+ // Since we're going to disable deoptimization, we clear the deoptimization requests queue.
+ // This prevents us from having any pending deoptimization request when the debugger attaches
+ // to us again while no event has been requested yet.
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
+ gDeoptimizationRequests.clear();
+ }
+ runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener,
+ instrumentation::Instrumentation::kMethodEntered |
+ instrumentation::Instrumentation::kMethodExited |
+ instrumentation::Instrumentation::kDexPcMoved |
+ instrumentation::Instrumentation::kExceptionCaught);
+ runtime->GetInstrumentation()->DisableDeoptimization();
+ gDebuggerActive = false;
}
- runtime->GetInstrumentation()->DisableDeoptimization();
- runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener,
- instrumentation::Instrumentation::kMethodEntered |
- instrumentation::Instrumentation::kMethodExited |
- instrumentation::Instrumentation::kDexPcMoved |
- instrumentation::Instrumentation::kExceptionCaught);
- gDebuggerActive = false;
gRegistry->Clear();
gDebuggerConnected = false;
CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
@@ -1011,11 +1027,12 @@
}
JDWP::JdwpError Dbg::GetObjectTag(JDWP::ObjectId object_id, uint8_t& tag) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
if (o == ObjectRegistry::kInvalidObject) {
return JDWP::ERR_INVALID_OBJECT;
}
- tag = TagFromObject(o);
+ tag = TagFromObject(soa, o);
return JDWP::ERR_NONE;
}
@@ -1062,7 +1079,7 @@
JDWP::JdwpError Dbg::OutputArray(JDWP::ObjectId array_id, int offset, int count, JDWP::ExpandBuf* pReply) {
JDWP::JdwpError status;
mirror::Array* a = DecodeArray(array_id, status);
- if (a == NULL) {
+ if (a == nullptr) {
return status;
}
@@ -1093,10 +1110,12 @@
memcpy(dst, &src[offset * width], count * width);
}
} else {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
mirror::ObjectArray<mirror::Object>* oa = a->AsObjectArray<mirror::Object>();
for (int i = 0; i < count; ++i) {
mirror::Object* element = oa->Get(offset + i);
- JDWP::JdwpTag specific_tag = (element != NULL) ? TagFromObject(element) : tag;
+ JDWP::JdwpTag specific_tag = (element != nullptr) ? TagFromObject(soa, element)
+ : tag;
expandBufAdd1(pReply, specific_tag);
expandBufAddObjectId(pReply, gRegistry->Add(element));
}
@@ -1639,8 +1658,9 @@
CHECK_EQ(tag, JDWP::JT_VOID);
}
} else {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
mirror::Object* value = return_value->GetL();
- expandBufAdd1(pReply, TagFromObject(value));
+ expandBufAdd1(pReply, TagFromObject(soa, value));
expandBufAddObjectId(pReply, gRegistry->Add(value));
}
}
@@ -1672,7 +1692,7 @@
if (thread_object == ObjectRegistry::kInvalidObject) {
return JDWP::ERR_INVALID_OBJECT;
}
-
+ const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroup");
// Okay, so it's an object, but is it actually a thread?
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
Thread* thread;
@@ -1685,14 +1705,14 @@
if (error != JDWP::ERR_NONE) {
return error;
}
-
- mirror::Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Thread;");
- CHECK(c != NULL);
+ mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
+ CHECK(c != nullptr);
mirror::ArtField* f = c->FindInstanceField("group", "Ljava/lang/ThreadGroup;");
CHECK(f != NULL);
mirror::Object* group = f->GetObject(thread_object);
CHECK(group != NULL);
JDWP::ObjectId thread_group_id = gRegistry->Add(group);
+ soa.Self()->EndAssertNoThreadSuspension(old_cause);
expandBufAddObjectId(pReply, thread_group_id);
return JDWP::ERR_NONE;
@@ -1701,25 +1721,28 @@
std::string Dbg::GetThreadGroupName(JDWP::ObjectId thread_group_id) {
ScopedObjectAccess soa(Thread::Current());
mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
- CHECK(thread_group != NULL);
-
- mirror::Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
- CHECK(c != NULL);
+ CHECK(thread_group != nullptr);
+ const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupName");
+ mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+ CHECK(c != nullptr);
mirror::ArtField* f = c->FindInstanceField("name", "Ljava/lang/String;");
CHECK(f != NULL);
mirror::String* s = reinterpret_cast<mirror::String*>(f->GetObject(thread_group));
+ soa.Self()->EndAssertNoThreadSuspension(old_cause);
return s->ToModifiedUtf8();
}
JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId thread_group_id) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
- CHECK(thread_group != NULL);
-
- mirror::Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
- CHECK(c != NULL);
+ CHECK(thread_group != nullptr);
+ const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupParent");
+ mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+ CHECK(c != nullptr);
mirror::ArtField* f = c->FindInstanceField("parent", "Ljava/lang/ThreadGroup;");
CHECK(f != NULL);
mirror::Object* parent = f->GetObject(thread_group);
+ soa.Self()->EndAssertNoThreadSuspension(old_cause);
return gRegistry->Add(parent);
}
@@ -2090,13 +2113,13 @@
return JDWP::ERR_NONE;
}
-void Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag,
- uint8_t* buf, size_t width) {
+void Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
+ JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
struct GetLocalVisitor : public StackVisitor {
- GetLocalVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id, int slot,
- JDWP::JdwpTag tag, uint8_t* buf, size_t width)
+ GetLocalVisitor(const ScopedObjectAccessUnchecked& soa, Thread* thread, Context* context,
+ JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, context), frame_id_(frame_id), slot_(slot), tag_(tag),
+ : StackVisitor(thread, context), soa_(soa), frame_id_(frame_id), slot_(slot), tag_(tag),
buf_(buf), width_(width) {}
// TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
@@ -2175,7 +2198,7 @@
if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
LOG(FATAL) << "Register " << reg << " expected to hold object: " << o;
}
- tag_ = TagFromObject(o);
+ tag_ = TagFromObject(soa_, o);
JDWP::SetObjectId(buf_+1, gRegistry->Add(o));
}
break;
@@ -2208,7 +2231,7 @@
JDWP::Set1(buf_, tag_);
return false;
}
-
+ const ScopedObjectAccessUnchecked& soa_;
const JDWP::FrameId frame_id_;
const int slot_;
JDWP::JdwpTag tag_;
@@ -2224,7 +2247,7 @@
return;
}
UniquePtr<Context> context(Context::Create());
- GetLocalVisitor visitor(thread, context.get(), frame_id, slot, tag, buf, width);
+ GetLocalVisitor visitor(soa, thread, context.get(), frame_id, slot, tag, buf, width);
visitor.WalkStack();
}
@@ -3037,7 +3060,7 @@
pReq->result_value.SetJ(0);
} else if (pReq->result_tag == JDWP::JT_OBJECT) {
/* if no exception thrown, examine object result more closely */
- JDWP::JdwpTag new_tag = TagFromObject(pReq->result_value.GetL());
+ JDWP::JdwpTag new_tag = TagFromObject(soa, pReq->result_value.GetL());
if (new_tag != pReq->result_tag) {
VLOG(jdwp) << " JDWP promoted result from " << pReq->result_tag << " to " << new_tag;
pReq->result_tag = new_tag;
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 5e2b9ff..a3415d3 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -721,7 +721,8 @@
}
if (it.HasNext()) {
- LOG(ERROR) << "invalid stream - problem with parameter iterator in " << GetLocation();
+ LOG(ERROR) << "invalid stream - problem with parameter iterator in " << GetLocation()
+ << " for method " << PrettyMethod(method_idx, *this);
return;
}
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 8b94b5a..2c08351 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -18,7 +18,7 @@
#define ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_
#include "base/macros.h"
-#include "class_linker.h"
+#include "class_linker-inl.h"
#include "common_throws.h"
#include "dex_file.h"
#include "indirect_reference_table.h"
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 012dabb..7cbeb29 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -29,16 +29,18 @@
#include "object_utils.h"
#include "runtime.h"
-
-
namespace art {
// Visits the arguments as saved to the stack by a Runtime::kRefAndArgs callee save frame.
class QuickArgumentVisitor {
- public:
-// Offset to first (not the Method*) argument in a Runtime::kRefAndArgs callee save frame.
-// Size of Runtime::kRefAndArgs callee save frame.
-// Size of Method* and register parameters in out stack arguments.
+ // Size of each spilled GPR.
+#ifdef __LP64__
+ static constexpr size_t kBytesPerGprSpillLocation = 8;
+#else
+ static constexpr size_t kBytesPerGprSpillLocation = 4;
+#endif
+ // Number of bytes for each out register in the caller method's frame.
+ static constexpr size_t kBytesStackArgLocation = 4;
#if defined(__arm__)
// The callee save frame is pointed to by SP.
// | argN | |
@@ -53,12 +55,19 @@
// | R3 | arg3
// | R2 | arg2
// | R1 | arg1
- // | R0 |
+ // | R0 | padding
// | Method* | <- sp
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 44
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48
-#define QUICK_STACK_ARG_SKIP 16
+ static constexpr bool kSoftFloatAbi = true; // This is a soft float ABI.
+ static constexpr size_t kNumGprArgs = 3; // 3 arguments passed in GPRs.
+ static constexpr size_t kNumFprArgs = 0; // 0 arguments passed in FPRs.
+ static constexpr size_t kBytesPerFprSpillLocation = 4; // FPR spill size is 4 bytes.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0; // Offset of first FPR arg.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 8; // Offset of first GPR arg.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 44; // Offset of return address.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 48; // Frame size.
+ static size_t GprIndexToGprOffset(uint32_t gpr_index) {
+ return gpr_index * kBytesPerGprSpillLocation;
+ }
#elif defined(__mips__)
// The callee save frame is pointed to by SP.
// | argN | |
@@ -74,10 +83,17 @@
// | A2 | arg2
// | A1 | arg1
// | A0/Method* | <- sp
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 60
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 64
-#define QUICK_STACK_ARG_SKIP 16
+ static constexpr bool kSoftFloatAbi = true; // This is a soft float ABI.
+ static constexpr size_t kNumGprArgs = 3; // 3 arguments passed in GPRs.
+ static constexpr size_t kNumFprArgs = 0; // 0 arguments passed in FPRs.
+ static constexpr size_t kBytesPerFprSpillLocation = 4; // FPR spill size is 4 bytes.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0; // Offset of first FPR arg.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4; // Offset of first GPR arg.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 60; // Offset of return address.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 64; // Frame size.
+ static size_t GprIndexToGprOffset(uint32_t gpr_index) {
+ return gpr_index * kBytesPerGprSpillLocation;
+ }
#elif defined(__i386__)
// The callee save frame is pointed to by SP.
// | argN | |
@@ -93,49 +109,96 @@
// | EDX | arg2
// | ECX | arg1
// | EAX/Method* | <- sp
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 28
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 32
-#define QUICK_STACK_ARG_SKIP 16
+ static constexpr bool kSoftFloatAbi = true; // This is a soft float ABI.
+ static constexpr size_t kNumGprArgs = 3; // 3 arguments passed in GPRs.
+ static constexpr size_t kNumFprArgs = 0; // 0 arguments passed in FPRs.
+ static constexpr size_t kBytesPerFprSpillLocation = 8; // FPR spill size is 8 bytes.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0; // Offset of first FPR arg.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4; // Offset of first GPR arg.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 28; // Offset of return address.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 32; // Frame size.
+ static size_t GprIndexToGprOffset(uint32_t gpr_index) {
+ return gpr_index * kBytesPerGprSpillLocation;
+ }
#elif defined(__x86_64__)
-// TODO: implement and check these.
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 56
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 64
-#define QUICK_STACK_ARG_SKIP 32
+ // The callee save frame is pointed to by SP.
+ // | argN | |
+ // | ... | |
+ // | reg. arg spills | | Caller's frame
+ // | Method* | ---
+ // | Return |
+ // | R15 | callee save
+ // | R14 | callee save
+ // | R13 | callee save
+ // | R12 | callee save
+ // | R9 | arg5
+ // | R8 | arg4
+ // | RSI/R6 | arg1
+ // | RBP/R5 | callee save
+ // | RBX/R3 | callee save
+ // | RDX/R2 | arg2
+ // | RCX/R1 | arg3
+ // | XMM7 | float arg 8
+ // | XMM6 | float arg 7
+ // | XMM5 | float arg 6
+ // | XMM4 | float arg 5
+ // | XMM3 | float arg 4
+ // | XMM2 | float arg 3
+ // | XMM1 | float arg 2
+ // | XMM0 | float arg 1
+ // | Padding |
+ // | RDI/Method* | <- sp
+ static constexpr bool kSoftFloatAbi = false; // This is a hard float ABI.
+ static constexpr size_t kNumGprArgs = 5; // 3 arguments passed in GPRs.
+ static constexpr size_t kNumFprArgs = 8; // 0 arguments passed in FPRs.
+ static constexpr size_t kBytesPerFprSpillLocation = 8; // FPR spill size is 8 bytes.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 16; // Offset of first FPR arg.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80; // Offset of first GPR arg.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 168; // Offset of return address.
+ static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 176; // Frame size.
+ static size_t GprIndexToGprOffset(uint32_t gpr_index) {
+ switch (gpr_index) {
+ case 0: return (4 * kBytesPerGprSpillLocation);
+ case 1: return (1 * kBytesPerGprSpillLocation);
+ case 2: return (0 * kBytesPerGprSpillLocation);
+ case 3: return (5 * kBytesPerGprSpillLocation);
+ case 4: return (6 * kBytesPerGprSpillLocation);
+ default:
+ LOG(FATAL) << "Unexpected GPR index: " << gpr_index;
+ return 0;
+ }
+ }
#else
#error "Unsupported architecture"
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 0
-#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0
-#define QUICK_STACK_ARG_SKIP 0
#endif
- static mirror::ArtMethod* GetCallingMethod(mirror::ArtMethod** sp) {
- byte* previous_sp = reinterpret_cast<byte*>(sp) +
- QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE;
+ public:
+ static mirror::ArtMethod* GetCallingMethod(mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK((*sp)->IsCalleeSaveMethod());
+ byte* previous_sp = reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize;
return *reinterpret_cast<mirror::ArtMethod**>(previous_sp);
}
- static uintptr_t GetCallingPc(mirror::ArtMethod** sp) {
- byte* lr = reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET;
+ // For the given quick ref and args quick frame, return the caller's PC.
+ static uintptr_t GetCallingPc(mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK((*sp)->IsCalleeSaveMethod());
+ byte* lr = reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_LrOffset;
return *reinterpret_cast<uintptr_t*>(lr);
}
QuickArgumentVisitor(mirror::ArtMethod** sp, bool is_static,
const char* shorty, uint32_t shorty_len)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
- is_static_(is_static), shorty_(shorty), shorty_len_(shorty_len),
- args_in_regs_(ComputeArgsInRegs(is_static, shorty, shorty_len)),
- num_params_((is_static ? 0 : 1) + shorty_len - 1), // +1 for this, -1 for return type
- reg_args_(reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET),
- stack_args_(reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE
- + QUICK_STACK_ARG_SKIP),
- cur_args_(reg_args_),
- cur_arg_index_(0),
- param_index_(0),
- is_split_long_or_double_(false) {
- DCHECK_EQ(static_cast<size_t>(QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE),
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
+ is_static_(is_static), shorty_(shorty), shorty_len_(shorty_len),
+ gpr_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset),
+ fpr_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset),
+ stack_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize
+ + StackArgumentStartFromShorty(is_static, shorty, shorty_len)),
+ gpr_index_(0), fpr_index_(0), stack_index_(0), cur_type_(Primitive::kPrimVoid),
+ is_split_long_or_double_(false) {
+ DCHECK_EQ(kQuickCalleeSaveFrame_RefAndArgs_FrameSize,
Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes());
}
@@ -143,30 +206,38 @@
virtual void Visit() = 0;
- Primitive::Type GetParamPrimitiveType() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- size_t index = param_index_;
- if (is_static_) {
- index++; // 0th argument must skip return value at start of the shorty
- } else if (index == 0) {
- return Primitive::kPrimNot;
- }
- CHECK_LT(index, shorty_len_);
- return Primitive::GetType(shorty_[index]);
+ Primitive::Type GetParamPrimitiveType() const {
+ return cur_type_;
}
byte* GetParamAddress() const {
- return cur_args_ + (cur_arg_index_ * kPointerSize);
+ if (!kSoftFloatAbi) {
+ Primitive::Type type = GetParamPrimitiveType();
+ if (UNLIKELY((type == Primitive::kPrimDouble) || (type == Primitive::kPrimFloat))) {
+ if ((kNumFprArgs != 0) && (fpr_index_ + 1 < kNumFprArgs + 1)) {
+ return fpr_args_ + (fpr_index_ * kBytesPerFprSpillLocation);
+ }
+ }
+ }
+ if (gpr_index_ < kNumGprArgs) {
+ return gpr_args_ + GprIndexToGprOffset(gpr_index_);
+ }
+ return stack_args_ + (stack_index_ * kBytesStackArgLocation);
}
bool IsSplitLongOrDouble() const {
- return is_split_long_or_double_;
+ if ((kBytesPerGprSpillLocation == 4) || (kBytesPerFprSpillLocation == 4)) {
+ return is_split_long_or_double_;
+ } else {
+ return false; // An optimization for when GPR and FPRs are 64bit.
+ }
}
- bool IsParamAReference() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool IsParamAReference() const {
return GetParamPrimitiveType() == Primitive::kPrimNot;
}
- bool IsParamALongOrDouble() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool IsParamALongOrDouble() const {
Primitive::Type type = GetParamPrimitiveType();
return type == Primitive::kPrimLong || type == Primitive::kPrimDouble;
}
@@ -179,51 +250,179 @@
}
void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- for (cur_arg_index_ = 0; cur_arg_index_ < args_in_regs_ && param_index_ < num_params_; ) {
- is_split_long_or_double_ = (cur_arg_index_ == 2) && IsParamALongOrDouble();
+ gpr_index_ = 0;
+ fpr_index_ = 0;
+ stack_index_ = 0;
+ if (!is_static_) { // Handle this.
+ cur_type_ = Primitive::kPrimNot;
+ is_split_long_or_double_ = false;
Visit();
- cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1);
- param_index_++;
+ if (kNumGprArgs > 0) {
+ gpr_index_++;
+ } else {
+ stack_index_++;
+ }
}
- cur_args_ = stack_args_;
- cur_arg_index_ = is_split_long_or_double_ ? 1 : 0;
- is_split_long_or_double_ = false;
- while (param_index_ < num_params_) {
- Visit();
- cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1);
- param_index_++;
+ for (uint32_t shorty_index = 1; shorty_index < shorty_len_; ++shorty_index) {
+ cur_type_ = Primitive::GetType(shorty_[shorty_index]);
+ switch (cur_type_) {
+ case Primitive::kPrimNot:
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ is_split_long_or_double_ = false;
+ Visit();
+ if (gpr_index_ < kNumGprArgs) {
+ gpr_index_++;
+ } else {
+ stack_index_++;
+ }
+ break;
+ case Primitive::kPrimFloat:
+ is_split_long_or_double_ = false;
+ Visit();
+ if (kSoftFloatAbi) {
+ if (gpr_index_ < kNumGprArgs) {
+ gpr_index_++;
+ } else {
+ stack_index_++;
+ }
+ } else {
+ if ((kNumFprArgs != 0) && (fpr_index_ + 1 < kNumFprArgs + 1)) {
+ fpr_index_++;
+ } else {
+ stack_index_++;
+ }
+ }
+ break;
+ case Primitive::kPrimDouble:
+ case Primitive::kPrimLong:
+ if (kSoftFloatAbi || (cur_type_ == Primitive::kPrimLong)) {
+ is_split_long_or_double_ = (kBytesPerGprSpillLocation == 4) &&
+ ((gpr_index_ + 1) == kNumGprArgs);
+ Visit();
+ if (gpr_index_ < kNumGprArgs) {
+ gpr_index_++;
+ if (kBytesPerGprSpillLocation == 4) {
+ if (gpr_index_ < kNumGprArgs) {
+ gpr_index_++;
+ } else {
+ stack_index_++;
+ }
+ }
+ } else {
+ if (kBytesStackArgLocation == 4) {
+ stack_index_+= 2;
+ } else {
+ CHECK_EQ(kBytesStackArgLocation, 8U);
+ stack_index_++;
+ }
+ }
+ } else {
+ is_split_long_or_double_ = (kBytesPerFprSpillLocation == 4) &&
+ ((fpr_index_ + 1) == kNumFprArgs);
+ Visit();
+ if ((kNumFprArgs != 0) && (fpr_index_ + 1 < kNumFprArgs + 1)) {
+ fpr_index_++;
+ if (kBytesPerFprSpillLocation == 4) {
+ if ((kNumFprArgs != 0) && (fpr_index_ + 1 < kNumFprArgs + 1)) {
+ fpr_index_++;
+ } else {
+ stack_index_++;
+ }
+ }
+ } else {
+ if (kBytesStackArgLocation == 4) {
+ stack_index_+= 2;
+ } else {
+ CHECK_EQ(kBytesStackArgLocation, 8U);
+ stack_index_++;
+ }
+ }
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected type: " << cur_type_ << " in " << shorty_;
+ }
}
}
private:
- static size_t ComputeArgsInRegs(bool is_static, const char* shorty, uint32_t shorty_len)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- size_t args_in_regs = (is_static ? 0 : 1);
- for (size_t i = 0; i < shorty_len; i++) {
- char s = shorty[i];
- if (s == 'J' || s == 'D') {
- args_in_regs += 2;
- } else {
- args_in_regs++;
+ static size_t StackArgumentStartFromShorty(bool is_static, const char* shorty,
+ uint32_t shorty_len) {
+ if (kSoftFloatAbi) {
+ CHECK_EQ(kNumFprArgs, 0U);
+ return (kNumGprArgs * kBytesPerGprSpillLocation) + kBytesPerGprSpillLocation /* ArtMethod* */;
+ } else {
+ size_t offset = kBytesPerGprSpillLocation; // Skip Method*.
+ size_t gprs_seen = 0;
+ size_t fprs_seen = 0;
+ if (!is_static && (gprs_seen < kNumGprArgs)) {
+ gprs_seen++;
+ offset += kBytesStackArgLocation;
}
- if (args_in_regs > 3) {
- args_in_regs = 3;
- break;
+ for (uint32_t i = 1; i < shorty_len; ++i) {
+ switch (shorty[i]) {
+ case 'Z':
+ case 'B':
+ case 'C':
+ case 'S':
+ case 'I':
+ case 'L':
+ if (gprs_seen < kNumGprArgs) {
+ gprs_seen++;
+ offset += kBytesStackArgLocation;
+ }
+ break;
+ case 'J':
+ if (gprs_seen < kNumGprArgs) {
+ gprs_seen++;
+ offset += 2 * kBytesStackArgLocation;
+ if (kBytesPerGprSpillLocation == 4) {
+ if (gprs_seen < kNumGprArgs) {
+ gprs_seen++;
+ }
+ }
+ }
+ break;
+ case 'F':
+ if ((kNumFprArgs != 0) && (fprs_seen + 1 < kNumFprArgs + 1)) {
+ fprs_seen++;
+ offset += kBytesStackArgLocation;
+ }
+ break;
+ case 'D':
+ if ((kNumFprArgs != 0) && (fprs_seen + 1 < kNumFprArgs + 1)) {
+ fprs_seen++;
+ offset += 2 * kBytesStackArgLocation;
+ if (kBytesPerFprSpillLocation == 4) {
+ if ((kNumFprArgs != 0) && (fprs_seen + 1 < kNumFprArgs + 1)) {
+ fprs_seen++;
+ }
+ }
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected shorty character: " << shorty[i] << " in " << shorty;
+ }
}
+ return offset;
}
- return args_in_regs;
}
const bool is_static_;
const char* const shorty_;
const uint32_t shorty_len_;
- const size_t args_in_regs_;
- const size_t num_params_;
- byte* const reg_args_;
- byte* const stack_args_;
- byte* cur_args_;
- size_t cur_arg_index_;
- size_t param_index_;
+ byte* const gpr_args_; // Address of GPR arguments in callee save frame.
+ byte* const fpr_args_; // Address of FPR arguments in callee save frame.
+ byte* const stack_args_; // Address of stack arguments in caller's frame.
+ uint32_t gpr_index_; // Index into spilled GPRs.
+ uint32_t fpr_index_; // Index into spilled FPRs.
+ uint32_t stack_index_; // Index into arguments on the stack.
+ // The current type of argument during VisitArguments.
+ Primitive::Type cur_type_;
// Does a 64bit parameter straddle the register and stack arguments?
bool is_split_long_or_double_;
};
@@ -231,9 +430,8 @@
// Visits arguments on the stack placing them into the shadow frame.
class BuildQuickShadowFrameVisitor : public QuickArgumentVisitor {
public:
- BuildQuickShadowFrameVisitor(mirror::ArtMethod** sp,
- bool is_static, const char* shorty,
- uint32_t shorty_len, ShadowFrame& sf, size_t first_arg_reg) :
+ BuildQuickShadowFrameVisitor(mirror::ArtMethod** sp, bool is_static, const char* shorty,
+ uint32_t shorty_len, ShadowFrame* sf, size_t first_arg_reg) :
QuickArgumentVisitor(sp, is_static, shorty, shorty_len), sf_(sf), cur_reg_(first_arg_reg) {}
virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -242,14 +440,17 @@
case Primitive::kPrimLong: // Fall-through.
case Primitive::kPrimDouble:
if (IsSplitLongOrDouble()) {
- sf_.SetVRegLong(cur_reg_, ReadSplitLongParam());
+ sf_->SetVRegLong(cur_reg_, ReadSplitLongParam());
} else {
- sf_.SetVRegLong(cur_reg_, *reinterpret_cast<jlong*>(GetParamAddress()));
+ sf_->SetVRegLong(cur_reg_, *reinterpret_cast<jlong*>(GetParamAddress()));
}
++cur_reg_;
break;
- case Primitive::kPrimNot:
- sf_.SetVRegReference(cur_reg_, *reinterpret_cast<mirror::Object**>(GetParamAddress()));
+ case Primitive::kPrimNot: {
+ StackReference<mirror::Object>* stack_ref =
+ reinterpret_cast<StackReference<mirror::Object>*>(GetParamAddress());
+ sf_->SetVRegReference(cur_reg_, stack_ref->AsMirrorPtr());
+ }
break;
case Primitive::kPrimBoolean: // Fall-through.
case Primitive::kPrimByte: // Fall-through.
@@ -257,7 +458,7 @@
case Primitive::kPrimShort: // Fall-through.
case Primitive::kPrimInt: // Fall-through.
case Primitive::kPrimFloat:
- sf_.SetVReg(cur_reg_, *reinterpret_cast<jint*>(GetParamAddress()));
+ sf_->SetVReg(cur_reg_, *reinterpret_cast<jint*>(GetParamAddress()));
break;
case Primitive::kPrimVoid:
LOG(FATAL) << "UNREACHABLE";
@@ -267,8 +468,8 @@
}
private:
- ShadowFrame& sf_;
- size_t cur_reg_;
+ ShadowFrame* const sf_;
+ uint32_t cur_reg_;
DISALLOW_COPY_AND_ASSIGN(BuildQuickShadowFrameVisitor);
};
@@ -293,8 +494,8 @@
method, 0, memory));
size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_;
BuildQuickShadowFrameVisitor shadow_frame_builder(sp, mh.IsStatic(), mh.GetShorty(),
- mh.GetShortyLength(),
- *shadow_frame, first_arg_reg);
+ mh.GetShortyLength(),
+ shadow_frame, first_arg_reg);
shadow_frame_builder.VisitArguments();
// Push a transition back into managed code onto the linked list in thread.
ManagedStack fragment;
@@ -315,6 +516,7 @@
JValue result = interpreter::EnterInterpreterFromStub(self, mh, code_item, *shadow_frame);
// Pop transition.
self->PopManagedStackFragment(fragment);
+ // No need to restore the args since the method has already been run by the interpreter.
return result.GetJ();
}
}
@@ -333,8 +535,10 @@
Primitive::Type type = GetParamPrimitiveType();
switch (type) {
case Primitive::kPrimNot: {
- mirror::Object* obj = *reinterpret_cast<mirror::Object**>(GetParamAddress());
- val.l = soa_->AddLocalReference<jobject>(obj);
+ StackReference<mirror::Object>* stack_ref =
+ reinterpret_cast<StackReference<mirror::Object>*>(GetParamAddress());
+ val.l = soa_->AddLocalReference<jobject>(stack_ref->AsMirrorPtr());
+ references_.push_back(std::make_pair(val.l, stack_ref));
break;
}
case Primitive::kPrimLong: // Fall-through.
@@ -351,7 +555,7 @@
case Primitive::kPrimShort: // Fall-through.
case Primitive::kPrimInt: // Fall-through.
case Primitive::kPrimFloat:
- val.i = *reinterpret_cast<jint*>(GetParamAddress());
+ val.i = *reinterpret_cast<jint*>(GetParamAddress());
break;
case Primitive::kPrimVoid:
LOG(FATAL) << "UNREACHABLE";
@@ -361,10 +565,18 @@
args_->push_back(val);
}
+ void FixupReferences() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Fixup any references which may have changed.
+ for (const auto& pair : references_) {
+ pair.second->Assign(soa_->Decode<mirror::Object*>(pair.first));
+ }
+ }
+
private:
ScopedObjectAccessUnchecked* soa_;
std::vector<jvalue>* args_;
-
+ // References which we must update when exiting in case the GC moved the objects.
+ std::vector<std::pair<jobject, StackReference<mirror::Object>*> > references_;
DISALLOW_COPY_AND_ASSIGN(BuildQuickArgumentVisitor);
};
@@ -417,6 +629,8 @@
self->EndAssertNoThreadSuspension(old_cause);
JValue result = InvokeProxyInvocationHandler(soa, proxy_mh.GetShorty(),
rcvr_jobj, interface_method_jobj, args);
+ // Restore references which might have moved.
+ local_ref_visitor.FixupReferences();
return result.GetJ();
}
@@ -430,23 +644,25 @@
virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (IsParamAReference()) {
- mirror::Object** param_address = reinterpret_cast<mirror::Object**>(GetParamAddress());
+ StackReference<mirror::Object>* stack_ref =
+ reinterpret_cast<StackReference<mirror::Object>*>(GetParamAddress());
jobject reference =
- soa_->AddLocalReference<jobject>(*param_address);
- references_.push_back(std::make_pair(reference, param_address));
+ soa_->AddLocalReference<jobject>(stack_ref->AsMirrorPtr());
+ references_.push_back(std::make_pair(reference, stack_ref));
}
}
void FixupReferences() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Fixup any references which may have changed.
- for (std::pair<jobject, mirror::Object**>& it : references_) {
- *it.second = soa_->Decode<mirror::Object*>(it.first);
+ for (const auto& pair : references_) {
+ pair.second->Assign(soa_->Decode<mirror::Object*>(pair.first));
}
}
private:
ScopedObjectAccessUnchecked* soa_;
- std::vector<std::pair<jobject, mirror::Object**> > references_;
+ // References which we must update when exiting in case the GC moved the objects.
+ std::vector<std::pair<jobject, StackReference<mirror::Object>*> > references_;
DISALLOW_COPY_AND_ASSIGN(RememberForGcArgumentVisitor);
};
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index f7b621f..910a817 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -28,6 +28,7 @@
#include "sirt_ref.h"
#include "thread.h"
#include "UniquePtr.h"
+#include "vmap_table.h"
namespace art {
@@ -39,7 +40,7 @@
ScopedObjectAccess soa(Thread::Current());
SirtRef<mirror::ClassLoader> class_loader(
soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("ExceptionHandle")));
- my_klass_ = class_linker_->FindClass("LExceptionHandle;", class_loader);
+ my_klass_ = class_linker_->FindClass(soa.Self(), "LExceptionHandle;", class_loader);
ASSERT_TRUE(my_klass_ != NULL);
SirtRef<mirror::Class> sirt_klass(soa.Self(), my_klass_);
class_linker_->EnsureInitialized(sirt_klass, true, true);
@@ -66,7 +67,7 @@
fake_mapping_data_.PushBackUnsigned(3 - 0); // offset 3
fake_mapping_data_.PushBackSigned(3 - 0); // maps to dex offset 3
- fake_vmap_table_data_.PushBackUnsigned(0);
+ fake_vmap_table_data_.PushBackUnsigned(0 + VmapTable::kEntryAdjustment);
fake_gc_map_.push_back(0); // 0 bytes to encode references and native pc offsets.
fake_gc_map_.push_back(0);
diff --git a/runtime/gc/collector/mark_sweep-inl.h b/runtime/gc/collector/mark_sweep-inl.h
index d148ae5..4915532 100644
--- a/runtime/gc/collector/mark_sweep-inl.h
+++ b/runtime/gc/collector/mark_sweep-inl.h
@@ -30,19 +30,19 @@
template <typename MarkVisitor>
inline void MarkSweep::ScanObjectVisit(mirror::Object* obj, const MarkVisitor& visitor) {
- DCHECK(obj != NULL);
if (kIsDebugBuild && !IsMarked(obj)) {
heap_->DumpSpaces();
LOG(FATAL) << "Scanning unmarked object " << obj;
}
+ // The GetClass verifies the object, don't need to reverify after.
mirror::Class* klass = obj->GetClass();
- DCHECK(klass != NULL);
+ // IsArrayClass verifies klass.
if (UNLIKELY(klass->IsArrayClass())) {
if (kCountScannedTypes) {
++array_count_;
}
- if (klass->IsObjectArrayClass()) {
- VisitObjectArrayReferences(obj->AsObjectArray<mirror::Object>(), visitor);
+ if (klass->IsObjectArrayClass<kVerifyNone>()) {
+ VisitObjectArrayReferences(obj->AsObjectArray<mirror::Object, kVerifyNone>(), visitor);
}
} else if (UNLIKELY(klass == mirror::Class::GetJavaLangClass())) {
if (kCountScannedTypes) {
@@ -54,7 +54,7 @@
++other_count_;
}
VisitOtherReferences(klass, obj, visitor);
- if (UNLIKELY(klass->IsReferenceClass())) {
+ if (UNLIKELY(klass->IsReferenceClass<kVerifyNone>())) {
DelayReferenceReferent(klass, obj);
}
}
@@ -65,24 +65,19 @@
bool visit_class)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
Locks::mutator_lock_) {
- DCHECK(obj != NULL);
- DCHECK(obj->GetClass() != NULL);
mirror::Class* klass = obj->GetClass();
- DCHECK(klass != NULL);
- if (klass == mirror::Class::GetJavaLangClass()) {
- DCHECK_EQ(klass->GetClass(), mirror::Class::GetJavaLangClass());
+ if (klass->IsArrayClass()) {
+ if (visit_class) {
+ visitor(obj, klass, mirror::Object::ClassOffset(), false);
+ }
+ if (klass->IsObjectArrayClass<kVerifyNone>()) {
+ VisitObjectArrayReferences(obj->AsObjectArray<mirror::Object, kVerifyNone>(), visitor);
+ }
+ } else if (klass == mirror::Class::GetJavaLangClass()) {
+ DCHECK_EQ(klass->GetClass<kVerifyNone>(), mirror::Class::GetJavaLangClass());
VisitClassReferences(klass, obj, visitor);
} else {
- if (klass->IsArrayClass()) {
- if (visit_class) {
- visitor(obj, klass, mirror::Object::ClassOffset(), false);
- }
- if (klass->IsObjectArrayClass()) {
- VisitObjectArrayReferences(obj->AsObjectArray<mirror::Object>(), visitor);
- }
- } else {
- VisitOtherReferences(klass, obj, visitor);
- }
+ VisitOtherReferences(klass, obj, visitor);
}
}
@@ -90,9 +85,7 @@
inline void MarkSweep::VisitInstanceFieldsReferences(mirror::Class* klass, mirror::Object* obj,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
- DCHECK(obj != NULL);
- DCHECK(klass != NULL);
- VisitFieldsReferences(obj, klass->GetReferenceInstanceOffsets(), false, visitor);
+ VisitFieldsReferences(obj, klass->GetReferenceInstanceOffsets<kVerifyNone>(), false, visitor);
}
template <typename Visitor>
@@ -100,14 +93,13 @@
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
VisitInstanceFieldsReferences(klass, obj, visitor);
- VisitStaticFieldsReferences(obj->AsClass(), visitor);
+ VisitStaticFieldsReferences(obj->AsClass<kVerifyNone>(), visitor);
}
template <typename Visitor>
inline void MarkSweep::VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
- DCHECK(klass != NULL);
- VisitFieldsReferences(klass, klass->GetReferenceStaticOffsets(), true, visitor);
+ VisitFieldsReferences(klass, klass->GetReferenceStaticOffsets<kVerifyNone>(), true, visitor);
}
template <typename Visitor>
@@ -118,7 +110,7 @@
while (ref_offsets != 0) {
size_t right_shift = CLZ(ref_offsets);
MemberOffset field_offset = CLASS_OFFSET_FROM_CLZ(right_shift);
- mirror::Object* ref = obj->GetFieldObject<mirror::Object>(field_offset, false);
+ mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyReads>(field_offset, false);
visitor(obj, ref, field_offset, is_static);
ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift);
}
@@ -127,7 +119,7 @@
// walk up the class inheritance hierarchy and find reference
// offsets the hard way. In the static case, just consider this
// class.
- for (mirror::Class* klass = is_static ? obj->AsClass() : obj->GetClass();
+ for (mirror::Class* klass = is_static ? obj->AsClass<kVerifyNone>() : obj->GetClass<kVerifyNone>();
klass != nullptr;
klass = is_static ? nullptr : klass->GetSuperClass()) {
size_t num_reference_fields = (is_static
@@ -137,7 +129,7 @@
mirror::ArtField* field = (is_static ? klass->GetStaticField(i)
: klass->GetInstanceField(i));
MemberOffset field_offset = field->GetOffset();
- mirror::Object* ref = obj->GetFieldObject<mirror::Object>(field_offset, false);
+ mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyReads>(field_offset, false);
visitor(obj, ref, field_offset, is_static);
}
}
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 006c271..8ca3892 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -62,29 +62,30 @@
namespace collector {
// Performance options.
-constexpr bool kUseRecursiveMark = false;
-constexpr bool kUseMarkStackPrefetch = true;
-constexpr size_t kSweepArrayChunkFreeSize = 1024;
+static constexpr bool kUseRecursiveMark = false;
+static constexpr bool kUseMarkStackPrefetch = true;
+static constexpr size_t kSweepArrayChunkFreeSize = 1024;
+static constexpr bool kPreCleanCards = true;
// Parallelism options.
-constexpr bool kParallelCardScan = true;
-constexpr bool kParallelRecursiveMark = true;
+static constexpr bool kParallelCardScan = true;
+static constexpr bool kParallelRecursiveMark = true;
// Don't attempt to parallelize mark stack processing unless the mark stack is at least n
// elements. This is temporary until we reduce the overhead caused by allocating tasks, etc.. Not
// having this can add overhead in ProcessReferences since we may end up doing many calls of
// ProcessMarkStack with very small mark stacks.
-constexpr size_t kMinimumParallelMarkStackSize = 128;
-constexpr bool kParallelProcessMarkStack = true;
+static constexpr size_t kMinimumParallelMarkStackSize = 128;
+static constexpr bool kParallelProcessMarkStack = true;
// Profiling and information flags.
-constexpr bool kCountClassesMarked = false;
-constexpr bool kProfileLargeObjects = false;
-constexpr bool kMeasureOverhead = false;
-constexpr bool kCountTasks = false;
-constexpr bool kCountJavaLangRefs = false;
+static constexpr bool kCountClassesMarked = false;
+static constexpr bool kProfileLargeObjects = false;
+static constexpr bool kMeasureOverhead = false;
+static constexpr bool kCountTasks = false;
+static constexpr bool kCountJavaLangRefs = false;
// Turn off kCheckLocks when profiling the GC since it slows the GC down by up to 40%.
-constexpr bool kCheckLocks = kDebugLocking;
+static constexpr bool kCheckLocks = kDebugLocking;
void MarkSweep::ImmuneSpace(space::ContinuousSpace* space) {
// Bind live to mark bitmap if necessary.
@@ -179,11 +180,11 @@
TimingLogger::ScopedSplit split("ProcessReferences", &timings_);
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
GetHeap()->ProcessReferences(timings_, clear_soft_references_, &IsMarkedCallback,
- &RecursiveMarkObjectCallback, this);
+ &MarkObjectCallback, &ProcessMarkStackPausedCallback, this);
}
bool MarkSweep::HandleDirtyObjectsPhase() {
- TimingLogger::ScopedSplit split("HandleDirtyObjectsPhase", &timings_);
+ TimingLogger::ScopedSplit split("(Paused)HandleDirtyObjectsPhase", &timings_);
Thread* self = Thread::Current();
Locks::mutator_lock_->AssertExclusiveHeld(self);
@@ -209,7 +210,7 @@
// Since SweepArray() above resets the (active) allocation
// stack. Need to revoke the thread-local allocation stacks that
// point into it.
- GetHeap()->RevokeAllThreadLocalAllocationStacks(self);
+ RevokeAllThreadLocalAllocationStacks(self);
}
timings_.StartSplit("PreSweepingGcVerification");
@@ -232,6 +233,43 @@
return is_concurrent_;
}
+void MarkSweep::PreCleanCards() {
+ // Don't do this for non concurrent GCs since they don't have any dirty cards.
+ if (kPreCleanCards && IsConcurrent()) {
+ Thread* self = Thread::Current();
+ CHECK(!Locks::mutator_lock_->IsExclusiveHeld(self));
+ // Process dirty cards and add dirty cards to mod union tables, also ages cards.
+ heap_->ProcessCards(timings_);
+ // The checkpoint root marking is required to avoid a race condition which occurs if the
+ // following happens during a reference write:
+ // 1. mutator dirties the card (write barrier)
+ // 2. GC ages the card (the above ProcessCards call)
+ // 3. GC scans the object (the RecursiveMarkDirtyObjects call below)
+ // 4. mutator writes the value (corresponding to the write barrier in 1.)
+ // This causes the GC to age the card but not necessarily mark the reference which the mutator
+ // wrote into the object stored in the card.
+ // Having the checkpoint fixes this issue since it ensures that the card mark and the
+ // reference write are visible to the GC before the card is scanned (this is due to locks being
+ // acquired / released in the checkpoint code).
+ // The other roots are also marked to help reduce the pause.
+ MarkThreadRoots(self);
+ // TODO: Only mark the dirty roots.
+ MarkNonThreadRoots();
+ MarkConcurrentRoots();
+ // Process the newly aged cards.
+ RecursiveMarkDirtyObjects(false, accounting::CardTable::kCardDirty - 1);
+ // TODO: Empty allocation stack to reduce the number of objects we need to test / mark as live
+ // in the next GC.
+ }
+}
+
+void MarkSweep::RevokeAllThreadLocalAllocationStacks(Thread* self) {
+ if (kUseThreadLocalAllocationStack) {
+ Locks::mutator_lock_->AssertExclusiveHeld(self);
+ heap_->RevokeAllThreadLocalAllocationStacks(self);
+ }
+}
+
void MarkSweep::MarkingPhase() {
TimingLogger::ScopedSplit split("MarkingPhase", &timings_);
Thread* self = Thread::Current();
@@ -251,9 +289,7 @@
if (Locks::mutator_lock_->IsExclusiveHeld(self)) {
// If we exclusively hold the mutator lock, all threads must be suspended.
MarkRoots();
- if (kUseThreadLocalAllocationStack) {
- heap_->RevokeAllThreadLocalAllocationStacks(self);
- }
+ RevokeAllThreadLocalAllocationStacks(self);
} else {
MarkThreadRoots(self);
// At this point the live stack should no longer have any mutators which push into it.
@@ -263,6 +299,8 @@
MarkConcurrentRoots();
UpdateAndMarkModUnion();
MarkReachableObjects();
+ // Pre-clean dirtied cards to reduce pauses.
+ PreCleanCards();
}
void MarkSweep::UpdateAndMarkModUnion() {
@@ -313,20 +351,24 @@
TimingLogger::ScopedSplit split("UnMarkAllocStack", &timings_);
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
accounting::ObjectStack* allocation_stack = GetHeap()->allocation_stack_.get();
- // The allocation stack contains things allocated since the start of the GC. These may have been
- // marked during this GC meaning they won't be eligible for reclaiming in the next sticky GC.
- // Remove these objects from the mark bitmaps so that they will be eligible for sticky
- // collection.
- // There is a race here which is safely handled. Another thread such as the hprof could
- // have flushed the alloc stack after we resumed the threads. This is safe however, since
- // reseting the allocation stack zeros it out with madvise. This means that we will either
- // read NULLs or attempt to unmark a newly allocated object which will not be marked in the
- // first place.
- mirror::Object** end = allocation_stack->End();
- for (mirror::Object** it = allocation_stack->Begin(); it != end; ++it) {
- const Object* obj = *it;
- if (obj != NULL) {
- UnMarkObjectNonNull(obj);
+ if (!kPreCleanCards) {
+ // The allocation stack contains things allocated since the start of the GC. These may have
+ // been marked during this GC meaning they won't be eligible for reclaiming in the next
+ // sticky GC. Unmark these objects so that they are eligible for reclaiming in the next
+ // sticky GC.
+ // There is a race here which is safely handled. Another thread such as the hprof could
+ // have flushed the alloc stack after we resumed the threads. This is safe however, since
+ // reseting the allocation stack zeros it out with madvise. This means that we will either
+ // read NULLs or attempt to unmark a newly allocated object which will not be marked in the
+ // first place.
+ // We can't do this if we pre-clean cards since we will unmark objects which are no longer on
+ // a dirty card since we aged cards during the pre-cleaning process.
+ mirror::Object** end = allocation_stack->End();
+ for (mirror::Object** it = allocation_stack->Begin(); it != end; ++it) {
+ const Object* obj = *it;
+ if (obj != nullptr) {
+ UnMarkObjectNonNull(obj);
+ }
}
}
}
@@ -400,10 +442,9 @@
}
}
-mirror::Object* MarkSweep::RecursiveMarkObjectCallback(mirror::Object* obj, void* arg) {
+mirror::Object* MarkSweep::MarkObjectCallback(mirror::Object* obj, void* arg) {
MarkSweep* mark_sweep = reinterpret_cast<MarkSweep*>(arg);
mark_sweep->MarkObject(obj);
- mark_sweep->ProcessMarkStack(true);
return obj;
}
@@ -534,25 +575,14 @@
void MarkSweep::MarkRootParallelCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
RootType /*root_type*/) {
- DCHECK(root != NULL);
- DCHECK(arg != NULL);
reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNullParallel(*root);
}
void MarkSweep::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
RootType /*root_type*/) {
- DCHECK(root != nullptr);
- DCHECK(arg != nullptr);
reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(*root);
}
-mirror::Object* MarkSweep::MarkObjectCallback(mirror::Object* object, void* arg) {
- DCHECK(object != nullptr);
- DCHECK(arg != nullptr);
- reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(object);
- return object;
-}
-
void MarkSweep::VerifyRootCallback(const Object* root, void* arg, size_t vreg,
const StackVisitor* visitor) {
reinterpret_cast<MarkSweep*>(arg)->VerifyRoot(root, vreg, visitor);
@@ -957,7 +987,7 @@
}
void MarkSweep::ReMarkRoots() {
- timings_.StartSplit("ReMarkRoots");
+ timings_.StartSplit("(Paused)ReMarkRoots");
Runtime::Current()->VisitRoots(MarkRootCallback, this, true, true);
timings_.EndSplit();
}
@@ -1208,6 +1238,11 @@
ScanObjectVisit(obj, visitor);
}
+void MarkSweep::ProcessMarkStackPausedCallback(void* arg) {
+ DCHECK(arg != nullptr);
+ reinterpret_cast<MarkSweep*>(arg)->ProcessMarkStack(true);
+}
+
void MarkSweep::ProcessMarkStackParallel(size_t thread_count) {
Thread* self = Thread::Current();
ThreadPool* thread_pool = GetHeap()->GetThreadPool();
@@ -1231,7 +1266,7 @@
// Scan anything that's on the mark stack.
void MarkSweep::ProcessMarkStack(bool paused) {
- timings_.StartSplit("ProcessMarkStack");
+ timings_.StartSplit(paused ? "(Paused)ProcessMarkStack" : "ProcessMarkStack");
size_t thread_count = GetThreadCount(paused);
if (kParallelProcessMarkStack && thread_count > 1 &&
mark_stack_->Size() >= kMinimumParallelMarkStackSize) {
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 6a48cf7..29fafd6 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -135,6 +135,11 @@
virtual void UpdateAndMarkModUnion()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Pre clean cards to reduce how much work is needed in the pause.
+ void PreCleanCards()
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Sweeps unmarked objects to complete the garbage collection.
virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -176,7 +181,7 @@
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
Locks::mutator_lock_);
- static mirror::Object* RecursiveMarkObjectCallback(mirror::Object* obj, void* arg)
+ static mirror::Object* MarkObjectCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -185,9 +190,8 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- static mirror::Object* MarkObjectCallback(mirror::Object* object, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ static void ProcessMarkStackPausedCallback(void* arg)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
static void MarkRootParallelCallback(mirror::Object** root, void* arg, uint32_t thread_id,
RootType root_type)
@@ -330,6 +334,10 @@
void ClearWhiteReferences(mirror::Object** list)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
+ // Used to get around thread safety annotations. The call is from MarkingPhase and is guarded by
+ // IsExclusiveHeld.
+ void RevokeAllThreadLocalAllocationStacks(Thread* self) NO_THREAD_SAFETY_ANALYSIS;
+
// Whether or not we count how many of each type of object were scanned.
static const bool kCountScannedTypes = false;
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index d64ec61..fe8c253 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -163,7 +163,7 @@
TimingLogger::ScopedSplit split("ProcessReferences", &timings_);
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
GetHeap()->ProcessReferences(timings_, clear_soft_references_, &MarkedForwardingAddressCallback,
- &RecursiveMarkObjectCallback, this);
+ &MarkObjectCallback, &ProcessMarkStackCallback, this);
}
void SemiSpace::MarkingPhase() {
@@ -310,7 +310,7 @@
}
// Recursively process the mark stack.
- ProcessMarkStack(true);
+ ProcessMarkStack();
}
void SemiSpace::ReclaimPhase() {
@@ -571,13 +571,15 @@
return forward_address;
}
-mirror::Object* SemiSpace::RecursiveMarkObjectCallback(mirror::Object* root, void* arg) {
+void SemiSpace::ProcessMarkStackCallback(void* arg) {
+ DCHECK(arg != nullptr);
+ reinterpret_cast<SemiSpace*>(arg)->ProcessMarkStack();
+}
+
+mirror::Object* SemiSpace::MarkObjectCallback(mirror::Object* root, void* arg) {
DCHECK(root != nullptr);
DCHECK(arg != nullptr);
- SemiSpace* semi_space = reinterpret_cast<SemiSpace*>(arg);
- mirror::Object* ret = semi_space->MarkObject(root);
- semi_space->ProcessMarkStack(true);
- return ret;
+ return reinterpret_cast<SemiSpace*>(arg)->MarkObject(root);
}
void SemiSpace::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
@@ -587,12 +589,6 @@
*root = reinterpret_cast<SemiSpace*>(arg)->MarkObject(*root);
}
-Object* SemiSpace::MarkObjectCallback(Object* object, void* arg) {
- DCHECK(object != nullptr);
- DCHECK(arg != nullptr);
- return reinterpret_cast<SemiSpace*>(arg)->MarkObject(object);
-}
-
// Marks all objects in the root set.
void SemiSpace::MarkRoots() {
timings_.StartSplit("MarkRoots");
@@ -670,17 +666,17 @@
// case since it does not dirty cards and use additional memory.
// Since we do not change the actual object, we can safely use non-transactional mode. Also
// disable check as we could run inside a transaction.
- obj->SetFieldObjectWithoutWriteBarrier<false, false>(offset, new_address, false);
+ obj->SetFieldObjectWithoutWriteBarrier<false, false, kVerifyNone>(offset, new_address, false);
}
}, kMovingClasses);
- mirror::Class* klass = obj->GetClass();
- if (UNLIKELY(klass->IsReferenceClass())) {
+ mirror::Class* klass = obj->GetClass<kVerifyNone>();
+ if (UNLIKELY(klass->IsReferenceClass<kVerifyNone>())) {
DelayReferenceReferent(klass, obj);
}
}
// Scan anything that's on the mark stack.
-void SemiSpace::ProcessMarkStack(bool paused) {
+void SemiSpace::ProcessMarkStack() {
space::MallocSpace* promo_dest_space = NULL;
accounting::SpaceBitmap* live_bitmap = NULL;
if (generational_ && !whole_heap_collection_) {
@@ -694,7 +690,7 @@
DCHECK(mark_bitmap != nullptr);
DCHECK_EQ(live_bitmap, mark_bitmap);
}
- timings_.StartSplit(paused ? "(paused)ProcessMarkStack" : "ProcessMarkStack");
+ timings_.StartSplit("ProcessMarkStack");
while (!mark_stack_->IsEmpty()) {
Object* obj = mark_stack_->PopBack();
if (generational_ && !whole_heap_collection_ && promo_dest_space->HasAddress(obj)) {
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index 89fe326..ba97376 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -146,12 +146,12 @@
RootType /*root_type*/)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
- static mirror::Object* MarkObjectCallback(mirror::Object* objecgt, void* arg)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
-
- static mirror::Object* RecursiveMarkObjectCallback(mirror::Object* root, void* arg)
+ static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
+ static void ProcessMarkStackCallback(void* arg)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+
virtual mirror::Object* MarkNonForwardedObject(mirror::Object* obj)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
@@ -174,10 +174,6 @@
// Returns true if we should sweep the space.
virtual bool ShouldSweepSpace(space::ContinuousSpace* space) const;
- // Returns how many threads we should use for the current GC phase based on if we are paused,
- // whether or not we care about pauses.
- size_t GetThreadCount(bool paused) const;
-
// Returns true if an object is inside of the immune region (assumed to be marked).
bool IsImmune(const mirror::Object* obj) const ALWAYS_INLINE {
return obj >= immune_begin_ && obj < immune_end_;
@@ -237,7 +233,7 @@
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
// Recursively blackens objects on the mark stack.
- void ProcessMarkStack(bool paused)
+ void ProcessMarkStack()
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
void EnqueueFinalizerReferences(mirror::Object** ref)
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 9c91b0e..3d591f0 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -28,6 +28,7 @@
#include "runtime.h"
#include "thread.h"
#include "thread-inl.h"
+#include "verify_object-inl.h"
namespace art {
namespace gc {
@@ -98,12 +99,8 @@
if (AllocatorMayHaveConcurrentGC(allocator) && concurrent_gc_) {
CheckConcurrentGC(self, new_num_bytes_allocated, obj);
}
- if (kIsDebugBuild) {
- if (kDesiredHeapVerification > kNoHeapVerification) {
- VerifyObject(obj);
- }
- self->VerifyStack();
- }
+ VerifyObject(obj);
+ self->VerifyStack();
return obj;
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 9e5e3ab..58db7a8 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -135,7 +135,7 @@
* searching.
*/
max_allocation_stack_size_(kGCALotMode ? kGcAlotInterval
- : (kDesiredHeapVerification > kVerifyAllFast) ? KB : MB),
+ : (kVerifyObjectSupport > kVerifyObjectModeFast) ? KB : MB),
current_allocator_(kAllocatorTypeDlMalloc),
current_non_moving_allocator_(kAllocatorTypeNonMoving),
bump_pointer_space_(nullptr),
@@ -150,7 +150,7 @@
target_utilization_(target_utilization),
total_wait_time_(0),
total_allocation_time_(0),
- verify_object_mode_(kHeapVerificationNotPermitted),
+ verify_object_mode_(kVerifyObjectModeDisabled),
disable_moving_gc_count_(0),
running_on_valgrind_(RUNNING_ON_VALGRIND),
use_tlab_(use_tlab) {
@@ -314,9 +314,7 @@
bool Heap::IsCompilingBoot() const {
for (const auto& space : continuous_spaces_) {
- if (space->IsImageSpace()) {
- return false;
- } else if (space->IsZygoteSpace()) {
+ if (space->IsImageSpace() || space->IsZygoteSpace()) {
return false;
}
}
@@ -610,41 +608,44 @@
struct SoftReferenceArgs {
IsMarkedCallback* is_marked_callback_;
- MarkObjectCallback* recursive_mark_callback_;
+ MarkObjectCallback* mark_callback_;
void* arg_;
};
mirror::Object* Heap::PreserveSoftReferenceCallback(mirror::Object* obj, void* arg) {
SoftReferenceArgs* args = reinterpret_cast<SoftReferenceArgs*>(arg);
// TODO: Not preserve all soft references.
- return args->recursive_mark_callback_(obj, args->arg_);
+ return args->mark_callback_(obj, args->arg_);
}
// Process reference class instances and schedule finalizations.
void Heap::ProcessReferences(TimingLogger& timings, bool clear_soft,
IsMarkedCallback* is_marked_callback,
- MarkObjectCallback* recursive_mark_object_callback, void* arg) {
+ MarkObjectCallback* mark_object_callback,
+ ProcessMarkStackCallback* process_mark_stack_callback, void* arg) {
// Unless we are in the zygote or required to clear soft references with white references,
// preserve some white referents.
if (!clear_soft && !Runtime::Current()->IsZygote()) {
SoftReferenceArgs soft_reference_args;
soft_reference_args.is_marked_callback_ = is_marked_callback;
- soft_reference_args.recursive_mark_callback_ = recursive_mark_object_callback;
+ soft_reference_args.mark_callback_ = mark_object_callback;
soft_reference_args.arg_ = arg;
soft_reference_queue_.PreserveSomeSoftReferences(&PreserveSoftReferenceCallback,
&soft_reference_args);
+ process_mark_stack_callback(arg);
}
- timings.StartSplit("ProcessReferences");
+ timings.StartSplit("(Paused)ProcessReferences");
// Clear all remaining soft and weak references with white referents.
soft_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg);
weak_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg);
timings.EndSplit();
// Preserve all white objects with finalize methods and schedule them for finalization.
- timings.StartSplit("EnqueueFinalizerReferences");
+ timings.StartSplit("(Paused)EnqueueFinalizerReferences");
finalizer_reference_queue_.EnqueueFinalizerReferences(cleared_references_, is_marked_callback,
- recursive_mark_object_callback, arg);
+ mark_object_callback, arg);
+ process_mark_stack_callback(arg);
timings.EndSplit();
- timings.StartSplit("ProcessReferences");
+ timings.StartSplit("(Paused)ProcessReferences");
// Clear all f-reachable soft and weak references with white referents.
soft_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg);
weak_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg);
@@ -820,14 +821,16 @@
return false;
}
if (bump_pointer_space_ != nullptr && bump_pointer_space_->HasAddress(obj)) {
- mirror::Class* klass = obj->GetClass();
+ mirror::Class* klass = obj->GetClass<kVerifyNone>();
if (obj == klass) {
// This case happens for java.lang.Class.
return true;
}
return VerifyClassClass(klass) && IsLiveObjectLocked(klass);
} else if (temp_space_ != nullptr && temp_space_->HasAddress(obj)) {
- return false;
+ // If we are in the allocated region of the temp space, then we are probably live (e.g. during
+ // a GC). When a GC isn't running End() - Begin() is 0 which means no objects are contained.
+ return temp_space_->Contains(obj);
}
space::ContinuousSpace* c_space = FindContinuousSpaceFromObject(obj, true);
space::DiscontinuousSpace* d_space = NULL;
@@ -883,25 +886,6 @@
return false;
}
-void Heap::VerifyObjectImpl(mirror::Object* obj) {
- if (Thread::Current() == NULL ||
- Runtime::Current()->GetThreadList()->GetLockOwner() == Thread::Current()->GetTid()) {
- return;
- }
- VerifyObjectBody(obj);
-}
-
-bool Heap::VerifyClassClass(const mirror::Class* c) const {
- // Note: we don't use the accessors here as they have internal sanity checks that we don't want
- // to run
- const byte* raw_addr =
- reinterpret_cast<const byte*>(c) + mirror::Object::ClassOffset().Int32Value();
- mirror::Class* c_c = reinterpret_cast<mirror::HeapReference<mirror::Class> const *>(raw_addr)->AsMirrorPtr();
- raw_addr = reinterpret_cast<const byte*>(c_c) + mirror::Object::ClassOffset().Int32Value();
- mirror::Class* c_c_c = reinterpret_cast<mirror::HeapReference<mirror::Class> const *>(raw_addr)->AsMirrorPtr();
- return c_c == c_c_c;
-}
-
void Heap::DumpSpaces(std::ostream& stream) {
for (const auto& space : continuous_spaces_) {
accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap();
@@ -920,36 +904,30 @@
}
void Heap::VerifyObjectBody(mirror::Object* obj) {
- CHECK(IsAligned<kObjectAlignment>(obj)) << "Object isn't aligned: " << obj;
+ if (this == nullptr && verify_object_mode_ == kVerifyObjectModeDisabled) {
+ return;
+ }
// Ignore early dawn of the universe verifications.
if (UNLIKELY(static_cast<size_t>(num_bytes_allocated_.Load()) < 10 * KB)) {
return;
}
- const byte* raw_addr = reinterpret_cast<const byte*>(obj) +
- mirror::Object::ClassOffset().Int32Value();
- mirror::Class* c = reinterpret_cast<mirror::HeapReference<mirror::Class> const *>(raw_addr)->AsMirrorPtr();
- if (UNLIKELY(c == NULL)) {
- LOG(FATAL) << "Null class in object: " << obj;
- } else if (UNLIKELY(!IsAligned<kObjectAlignment>(c))) {
- LOG(FATAL) << "Class isn't aligned: " << c << " in object: " << obj;
- }
+ CHECK(IsAligned<kObjectAlignment>(obj)) << "Object isn't aligned: " << obj;
+ mirror::Class* c = obj->GetFieldObject<mirror::Class, kVerifyNone>(
+ mirror::Object::ClassOffset(), false);
+ CHECK(c != nullptr) << "Null class in object " << obj;
+ CHECK(IsAligned<kObjectAlignment>(c)) << "Class " << c << " not aligned in object " << obj;
CHECK(VerifyClassClass(c));
- if (verify_object_mode_ > kVerifyAllFast) {
- // TODO: the bitmap tests below are racy if VerifyObjectBody is called without the
- // heap_bitmap_lock_.
+ if (verify_object_mode_ > kVerifyObjectModeFast) {
+ // Note: the bitmap tests below are racy since we don't hold the heap bitmap lock.
if (!IsLiveObjectLocked(obj)) {
DumpSpaces();
LOG(FATAL) << "Object is dead: " << obj;
}
- if (!IsLiveObjectLocked(c)) {
- LOG(FATAL) << "Class of object is dead: " << c << " in object: " << obj;
- }
}
}
void Heap::VerificationCallback(mirror::Object* obj, void* arg) {
- DCHECK(obj != NULL);
reinterpret_cast<Heap*>(arg)->VerifyObjectBody(obj);
}
@@ -1787,7 +1765,7 @@
if (bitmap == nullptr) {
LOG(ERROR) << "Object " << obj << " has no bitmap";
- if (!heap_->VerifyClassClass(obj->GetClass())) {
+ if (!VerifyClassClass(obj->GetClass())) {
LOG(ERROR) << "Object " << obj << " failed class verification!";
}
} else {
@@ -2032,12 +2010,6 @@
}
void Heap::RevokeAllThreadLocalAllocationStacks(Thread* self) {
- if (!Runtime::Current()->IsStarted()) {
- // There's no thread list if the runtime hasn't started (eg
- // dex2oat or a test). Just revoke for self.
- self->RevokeThreadLocalAllocationStack();
- return;
- }
// This must be called only during the pause.
CHECK(Locks::mutator_lock_->IsExclusiveHeld(self));
MutexLock mu(self, *Locks::runtime_shutdown_lock_);
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 80a5a1a..5d44ee1 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -37,6 +37,7 @@
#include "reference_queue.h"
#include "safe_map.h"
#include "thread_pool.h"
+#include "verify_object.h"
namespace art {
@@ -99,15 +100,6 @@
kAllocatorTypeLOS, // Large object space, also doesn't have entrypoints.
};
-// How we want to sanity check the heap's correctness.
-enum HeapVerificationMode {
- kHeapVerificationNotPermitted, // Too early in runtime start-up for heap to be verified.
- kNoHeapVerification, // Production default.
- kVerifyAllFast, // Sanity check all heap accesses with quick(er) tests.
- kVerifyAll // Sanity check all heap accesses.
-};
-static constexpr HeapVerificationMode kDesiredHeapVerification = kNoHeapVerification;
-
// If true, use rosalloc/RosAllocSpace instead of dlmalloc/DlMallocSpace
static constexpr bool kUseRosAlloc = true;
@@ -208,23 +200,16 @@
void ChangeCollector(CollectorType collector_type);
// The given reference is believed to be to an object in the Java heap, check the soundness of it.
- void VerifyObjectImpl(mirror::Object* o);
- void VerifyObject(mirror::Object* o) {
- if (o != nullptr && this != nullptr && verify_object_mode_ > kNoHeapVerification) {
- VerifyObjectImpl(o);
- }
- }
- // Check that c.getClass() == c.getClass().getClass().
- bool VerifyClassClass(const mirror::Class* c) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // TODO: NO_THREAD_SAFETY_ANALYSIS since we call this everywhere and it is impossible to find a
+ // proper lock ordering for it.
+ void VerifyObjectBody(mirror::Object* o) NO_THREAD_SAFETY_ANALYSIS;
// Check sanity of all live references.
void VerifyHeap() LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
bool VerifyHeapReferences()
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
bool VerifyMissingCardMarks()
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
// A weaker test than IsLiveObject or VerifyObject that doesn't require the heap lock,
// and doesn't abort on error, allowing the caller to report more
@@ -339,27 +324,28 @@
static mirror::Object* PreserveSoftReferenceCallback(mirror::Object* obj, void* arg);
void ProcessReferences(TimingLogger& timings, bool clear_soft,
IsMarkedCallback* is_marked_callback,
- MarkObjectCallback* recursive_mark_object_callback, void* arg)
+ MarkObjectCallback* mark_object_callback,
+ ProcessMarkStackCallback* process_mark_stack_callback,
+ void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
// Enable verification of object references when the runtime is sufficiently initialized.
void EnableObjectValidation() {
- verify_object_mode_ = kDesiredHeapVerification;
- if (verify_object_mode_ > kNoHeapVerification) {
+ verify_object_mode_ = kVerifyObjectSupport;
+ if (verify_object_mode_ > kVerifyObjectModeDisabled) {
VerifyHeap();
}
}
// Disable object reference verification for image writing.
void DisableObjectValidation() {
- verify_object_mode_ = kHeapVerificationNotPermitted;
+ verify_object_mode_ = kVerifyObjectModeDisabled;
}
// Other checks may be performed if we know the heap should be in a sane state.
bool IsObjectValidationEnabled() const {
- return kDesiredHeapVerification > kNoHeapVerification &&
- verify_object_mode_ > kHeapVerificationNotPermitted;
+ return verify_object_mode_ > kVerifyObjectModeDisabled;
}
// Returns true if low memory mode is enabled.
@@ -474,6 +460,10 @@
void FlushAllocStack()
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ // Revoke all the thread-local allocation stacks.
+ void RevokeAllThreadLocalAllocationStacks(Thread* self)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Mark all the objects in the allocation stack in the specified bitmap.
void MarkAllocStack(accounting::SpaceBitmap* bitmap1, accounting::SpaceBitmap* bitmap2,
accounting::ObjectSet* large_objects, accounting::ObjectStack* stack)
@@ -660,19 +650,12 @@
LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
void RemoveSpace(space::Space* space) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
- // No thread saftey analysis since we call this everywhere and it is impossible to find a proper
- // lock ordering for it.
- void VerifyObjectBody(mirror::Object *obj) NO_THREAD_SAFETY_ANALYSIS;
-
static void VerificationCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(GlobalSychronization::heap_bitmap_lock_);
// Swap the allocation stack with the live stack.
void SwapStacks(Thread* self);
- // Revoke all the thread-local allocation stacks.
- void RevokeAllThreadLocalAllocationStacks(Thread* self);
-
// Clear cards and update the mod union table.
void ProcessCards(TimingLogger& timings);
@@ -914,7 +897,7 @@
AtomicInteger total_allocation_time_;
// The current state of heap verification, may be enabled or disabled.
- HeapVerificationMode verify_object_mode_;
+ VerifyObjectMode verify_object_mode_;
// Compacting GC disable count, prevents compacting GC from running iff > 0.
size_t disable_moving_gc_count_ GUARDED_BY(gc_complete_lock_);
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index b02b8bb..4b86339 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -43,7 +43,8 @@
ScopedObjectAccess soa(Thread::Current());
// garbage is created during ClassLinker::Init
- SirtRef<mirror::Class> c(soa.Self(), class_linker_->FindSystemClass("[Ljava/lang/Object;"));
+ SirtRef<mirror::Class> c(soa.Self(), class_linker_->FindSystemClass(soa.Self(),
+ "[Ljava/lang/Object;"));
for (size_t i = 0; i < 1024; ++i) {
SirtRef<mirror::ObjectArray<mirror::Object> > array(soa.Self(),
mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), c.get(), 2048));
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index 931ed21..1493019 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -53,10 +53,10 @@
return nullptr;
}
- // Protect memory beyond the initial size.
+ // Protect memory beyond the starting size. morecore will add r/w permissions when necessory
byte* end = mem_map->Begin() + starting_size;
- if (capacity - initial_size > 0) {
- CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), name);
+ if (capacity - starting_size > 0) {
+ CHECK_MEMORY_CALL(mprotect, (end, capacity - starting_size, PROT_NONE), name);
}
// Everything is set so record in immutable structure and leave
diff --git a/runtime/gc/space/dlmalloc_space_test.cc b/runtime/gc/space/dlmalloc_space_test.cc
new file mode 100644
index 0000000..964c31b
--- /dev/null
+++ b/runtime/gc/space/dlmalloc_space_test.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "space_test.h"
+#include "dlmalloc_space.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
+ size_t capacity, byte* requested_begin) {
+ return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin);
+}
+
+TEST_SPACE_CREATE_FN(DlMallocSpace, CreateDlMallocSpace)
+
+
+} // namespace space
+} // namespace gc
+} // namespace art
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index ebad8dd..12c5451 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -16,9 +16,6 @@
#include "image_space.h"
-#include <sys/types.h>
-#include <sys/wait.h>
-
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "gc/accounting/space_bitmap-inl.h"
@@ -89,52 +86,14 @@
arg_vector.push_back("--host");
}
+ const std::vector<std::string>& compiler_options = Runtime::Current()->GetImageCompilerOptions();
+ for (size_t i = 0; compiler_options.size(); ++i) {
+ arg_vector.push_back(compiler_options[i].c_str());
+ }
+
std::string command_line(Join(arg_vector, ' '));
LOG(INFO) << "GenerateImage: " << command_line;
-
- // Convert the args to char pointers.
- std::vector<char*> char_args;
- for (std::vector<std::string>::iterator it = arg_vector.begin(); it != arg_vector.end();
- ++it) {
- char_args.push_back(const_cast<char*>(it->c_str()));
- }
- char_args.push_back(NULL);
-
- // fork and exec dex2oat
- pid_t pid = fork();
- if (pid == 0) {
- // no allocation allowed between fork and exec
-
- // change process groups, so we don't get reaped by ProcessManager
- setpgid(0, 0);
-
- execv(dex2oat.c_str(), &char_args[0]);
-
- PLOG(FATAL) << "execv(" << dex2oat << ") failed";
- return false;
- } else {
- if (pid == -1) {
- *error_msg = StringPrintf("Failed to generate image '%s' because fork failed: %s",
- image_file_name.c_str(), strerror(errno));
- return false;
- }
-
- // wait for dex2oat to finish
- int status;
- pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
- if (got_pid != pid) {
- *error_msg = StringPrintf("Failed to generate image '%s' because waitpid failed: "
- "wanted %d, got %d: %s",
- image_file_name.c_str(), pid, got_pid, strerror(errno));
- return false;
- }
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- *error_msg = StringPrintf("Failed to generate image '%s' because dex2oat failed: %s",
- image_file_name.c_str(), command_line.c_str());
- return false;
- }
- }
- return true;
+ return Exec(arg_vector, error_msg);
}
ImageSpace* ImageSpace::Create(const char* original_image_file_name) {
diff --git a/runtime/gc/space/large_object_space_test.cc b/runtime/gc/space/large_object_space_test.cc
new file mode 100644
index 0000000..845b9e3
--- /dev/null
+++ b/runtime/gc/space/large_object_space_test.cc
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "space_test.h"
+#include "large_object_space.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+class LargeObjectSpaceTest : public SpaceTest {
+ public:
+ void LargeObjectTest();
+};
+
+
+void LargeObjectSpaceTest::LargeObjectTest() {
+ size_t rand_seed = 0;
+ for (size_t i = 0; i < 2; ++i) {
+ LargeObjectSpace* los = nullptr;
+ if (i == 0) {
+ los = space::LargeObjectMapSpace::Create("large object space");
+ } else {
+ los = space::FreeListSpace::Create("large object space", nullptr, 128 * MB);
+ }
+
+ static const size_t num_allocations = 64;
+ static const size_t max_allocation_size = 0x100000;
+ std::vector<std::pair<mirror::Object*, size_t> > requests;
+
+ for (size_t phase = 0; phase < 2; ++phase) {
+ while (requests.size() < num_allocations) {
+ size_t request_size = test_rand(&rand_seed) % max_allocation_size;
+ size_t allocation_size = 0;
+ mirror::Object* obj = los->Alloc(Thread::Current(), request_size, &allocation_size);
+ ASSERT_TRUE(obj != nullptr);
+ ASSERT_EQ(allocation_size, los->AllocationSize(obj));
+ ASSERT_GE(allocation_size, request_size);
+ // Fill in our magic value.
+ byte magic = (request_size & 0xFF) | 1;
+ memset(obj, magic, request_size);
+ requests.push_back(std::make_pair(obj, request_size));
+ }
+
+ // "Randomly" shuffle the requests.
+ for (size_t k = 0; k < 10; ++k) {
+ for (size_t j = 0; j < requests.size(); ++j) {
+ std::swap(requests[j], requests[test_rand(&rand_seed) % requests.size()]);
+ }
+ }
+
+ // Free 1 / 2 the allocations the first phase, and all the second phase.
+ size_t limit = !phase ? requests.size() / 2 : 0;
+ while (requests.size() > limit) {
+ mirror::Object* obj = requests.back().first;
+ size_t request_size = requests.back().second;
+ requests.pop_back();
+ byte magic = (request_size & 0xFF) | 1;
+ for (size_t k = 0; k < request_size; ++k) {
+ ASSERT_EQ(reinterpret_cast<const byte*>(obj)[k], magic);
+ }
+ ASSERT_GE(los->Free(Thread::Current(), obj), request_size);
+ }
+ }
+
+ size_t bytes_allocated = 0;
+ // Checks that the coalescing works.
+ mirror::Object* obj = los->Alloc(Thread::Current(), 100 * MB, &bytes_allocated);
+ EXPECT_TRUE(obj != nullptr);
+ los->Free(Thread::Current(), obj);
+
+ EXPECT_EQ(0U, los->GetBytesAllocated());
+ EXPECT_EQ(0U, los->GetObjectsAllocated());
+ delete los;
+ }
+}
+
+
+TEST_F(LargeObjectSpaceTest, LargeObjectTest) {
+ LargeObjectTest();
+}
+
+} // namespace space
+} // namespace gc
+} // namespace art
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index f90e6c7..ee31112 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -111,7 +111,8 @@
}
void MallocSpace::RegisterRecentFree(mirror::Object* ptr) {
- recent_freed_objects_[recent_free_pos_] = std::make_pair(ptr, ptr->GetClass());
+ // No verification since the object is dead.
+ recent_freed_objects_[recent_free_pos_] = std::make_pair(ptr, ptr->GetClass<kVerifyNone>());
recent_free_pos_ = (recent_free_pos_ + 1) & kRecentFreeMask;
}
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 86e441e..cc6c1d9 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -56,10 +56,10 @@
return NULL;
}
- // Protect memory beyond the initial size.
+ // Protect memory beyond the starting size. MoreCore will add r/w permissions when necessory
byte* end = mem_map->Begin() + starting_size;
- if (capacity - initial_size > 0) {
- CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), name);
+ if (capacity - starting_size > 0) {
+ CHECK_MEMORY_CALL(mprotect, (end, capacity - starting_size, PROT_NONE), name);
}
// Everything is set so record in immutable structure and leave
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index 2377423..72e84f6 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -60,7 +60,8 @@
// TODO: NO_THREAD_SAFETY_ANALYSIS because SizeOf() requires that mutator_lock is held.
void* obj_ptr = const_cast<void*>(reinterpret_cast<const void*>(obj));
// obj is a valid object. Use its class in the header to get the size.
- size_t size = obj->SizeOf();
+ // Don't use verification since the object may be dead if we are sweeping.
+ size_t size = obj->SizeOf<kVerifyNone>();
size_t size_by_size = rosalloc_->UsableSize(size);
if (kIsDebugBuild) {
size_t size_by_ptr = rosalloc_->UsableSize(obj_ptr);
diff --git a/runtime/gc/space/rosalloc_space_test.cc b/runtime/gc/space/rosalloc_space_test.cc
new file mode 100644
index 0000000..3eac795
--- /dev/null
+++ b/runtime/gc/space/rosalloc_space_test.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "space_test.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
+ size_t capacity, byte* requested_begin) {
+ return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin,
+ Runtime::Current()->GetHeap()->IsLowMemoryMode());
+}
+
+TEST_SPACE_CREATE_FN(RosAllocSpace, CreateRosAllocSpace)
+
+
+} // namespace space
+} // namespace gc
+} // namespace art
diff --git a/runtime/gc/space/space_test.cc b/runtime/gc/space/space_test.cc
deleted file mode 100644
index 6d07a60..0000000
--- a/runtime/gc/space/space_test.cc
+++ /dev/null
@@ -1,631 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "dlmalloc_space.h"
-#include "large_object_space.h"
-#include "zygote_space.h"
-
-#include "common_test.h"
-#include "globals.h"
-#include "UniquePtr.h"
-#include "mirror/array-inl.h"
-#include "mirror/object-inl.h"
-
-#include <stdint.h>
-
-namespace art {
-namespace gc {
-namespace space {
-
-class SpaceTest : public CommonTest {
- public:
- void AddSpace(ContinuousSpace* space) {
- // For RosAlloc, revoke the thread local runs before moving onto a
- // new alloc space.
- Runtime::Current()->GetHeap()->RevokeAllThreadLocalBuffers();
- Runtime::Current()->GetHeap()->AddSpace(space);
- }
- void InstallClass(mirror::Object* o, size_t size) NO_THREAD_SAFETY_ANALYSIS {
- // Note the minimum size, which is the size of a zero-length byte array.
- EXPECT_GE(size, SizeOfZeroLengthByteArray());
- SirtRef<mirror::ClassLoader> null_loader(Thread::Current(), NULL);
- mirror::Class* byte_array_class = Runtime::Current()->GetClassLinker()->FindClass("[B", null_loader);
- EXPECT_TRUE(byte_array_class != NULL);
- o->SetClass(byte_array_class);
- mirror::Array* arr = o->AsArray();
- size_t header_size = SizeOfZeroLengthByteArray();
- int32_t length = size - header_size;
- arr->SetLength(length);
- EXPECT_EQ(arr->SizeOf(), size);
- }
-
- static size_t SizeOfZeroLengthByteArray() {
- return mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimByte)).Uint32Value();
- }
-
- static MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
- size_t capacity, byte* requested_begin) {
- return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin);
- }
- static MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
- size_t capacity, byte* requested_begin) {
- return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin,
- Runtime::Current()->GetHeap()->IsLowMemoryMode());
- }
-
- typedef MallocSpace* (*CreateSpaceFn)(const std::string& name, size_t initial_size, size_t growth_limit,
- size_t capacity, byte* requested_begin);
- void InitTestBody(CreateSpaceFn create_space);
- void ZygoteSpaceTestBody(CreateSpaceFn create_space);
- void AllocAndFreeTestBody(CreateSpaceFn create_space);
- void AllocAndFreeListTestBody(CreateSpaceFn create_space);
-
- void SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t object_size,
- int round, size_t growth_limit);
- void SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, CreateSpaceFn create_space);
-};
-
-static size_t test_rand(size_t* seed) {
- *seed = *seed * 1103515245 + 12345;
- return *seed;
-}
-
-void SpaceTest::InitTestBody(CreateSpaceFn create_space) {
- {
- // Init < max == growth
- UniquePtr<Space> space(create_space("test", 16 * MB, 32 * MB, 32 * MB, NULL));
- EXPECT_TRUE(space.get() != NULL);
- }
- {
- // Init == max == growth
- UniquePtr<Space> space(create_space("test", 16 * MB, 16 * MB, 16 * MB, NULL));
- EXPECT_TRUE(space.get() != NULL);
- }
- {
- // Init > max == growth
- UniquePtr<Space> space(create_space("test", 32 * MB, 16 * MB, 16 * MB, NULL));
- EXPECT_TRUE(space.get() == NULL);
- }
- {
- // Growth == init < max
- UniquePtr<Space> space(create_space("test", 16 * MB, 16 * MB, 32 * MB, NULL));
- EXPECT_TRUE(space.get() != NULL);
- }
- {
- // Growth < init < max
- UniquePtr<Space> space(create_space("test", 16 * MB, 8 * MB, 32 * MB, NULL));
- EXPECT_TRUE(space.get() == NULL);
- }
- {
- // Init < growth < max
- UniquePtr<Space> space(create_space("test", 8 * MB, 16 * MB, 32 * MB, NULL));
- EXPECT_TRUE(space.get() != NULL);
- }
- {
- // Init < max < growth
- UniquePtr<Space> space(create_space("test", 8 * MB, 32 * MB, 16 * MB, NULL));
- EXPECT_TRUE(space.get() == NULL);
- }
-}
-
-TEST_F(SpaceTest, Init_DlMallocSpace) {
- InitTestBody(SpaceTest::CreateDlMallocSpace);
-}
-TEST_F(SpaceTest, Init_RosAllocSpace) {
- InitTestBody(SpaceTest::CreateRosAllocSpace);
-}
-
-// TODO: This test is not very good, we should improve it.
-// The test should do more allocations before the creation of the ZygoteSpace, and then do
-// allocations after the ZygoteSpace is created. The test should also do some GCs to ensure that
-// the GC works with the ZygoteSpace.
-void SpaceTest::ZygoteSpaceTestBody(CreateSpaceFn create_space) {
- size_t dummy = 0;
- MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, NULL));
- ASSERT_TRUE(space != NULL);
-
- // Make space findable to the heap, will also delete space when runtime is cleaned up
- AddSpace(space);
- Thread* self = Thread::Current();
-
- // Succeeds, fits without adjusting the footprint limit.
- mirror::Object* ptr1 = space->Alloc(self, 1 * MB, &dummy);
- EXPECT_TRUE(ptr1 != NULL);
- InstallClass(ptr1, 1 * MB);
-
- // Fails, requires a higher footprint limit.
- mirror::Object* ptr2 = space->Alloc(self, 8 * MB, &dummy);
- EXPECT_TRUE(ptr2 == NULL);
-
- // Succeeds, adjusts the footprint.
- size_t ptr3_bytes_allocated;
- mirror::Object* ptr3 = space->AllocWithGrowth(self, 8 * MB, &ptr3_bytes_allocated);
- EXPECT_TRUE(ptr3 != NULL);
- EXPECT_LE(8U * MB, ptr3_bytes_allocated);
- InstallClass(ptr3, 8 * MB);
-
- // Fails, requires a higher footprint limit.
- mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy);
- EXPECT_TRUE(ptr4 == NULL);
-
- // Also fails, requires a higher allowed footprint.
- mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &dummy);
- EXPECT_TRUE(ptr5 == NULL);
-
- // Release some memory.
- ScopedObjectAccess soa(self);
- size_t free3 = space->AllocationSize(ptr3);
- EXPECT_EQ(free3, ptr3_bytes_allocated);
- EXPECT_EQ(free3, space->Free(self, ptr3));
- EXPECT_LE(8U * MB, free3);
-
- // Succeeds, now that memory has been freed.
- mirror::Object* ptr6 = space->AllocWithGrowth(self, 9 * MB, &dummy);
- EXPECT_TRUE(ptr6 != NULL);
- InstallClass(ptr6, 9 * MB);
-
- // Final clean up.
- size_t free1 = space->AllocationSize(ptr1);
- space->Free(self, ptr1);
- EXPECT_LE(1U * MB, free1);
-
- // Make sure that the zygote space isn't directly at the start of the space.
- space->Alloc(self, 1U * MB, &dummy);
-
- gc::Heap* heap = Runtime::Current()->GetHeap();
- space::Space* old_space = space;
- heap->RemoveSpace(old_space);
- space::ZygoteSpace* zygote_space = space->CreateZygoteSpace("alloc space",
- heap->IsLowMemoryMode(),
- &space);
- delete old_space;
- // Add the zygote space.
- AddSpace(zygote_space);
-
- // Make space findable to the heap, will also delete space when runtime is cleaned up
- AddSpace(space);
-
- // Succeeds, fits without adjusting the footprint limit.
- ptr1 = space->Alloc(self, 1 * MB, &dummy);
- EXPECT_TRUE(ptr1 != NULL);
- InstallClass(ptr1, 1 * MB);
-
- // Fails, requires a higher footprint limit.
- ptr2 = space->Alloc(self, 8 * MB, &dummy);
- EXPECT_TRUE(ptr2 == NULL);
-
- // Succeeds, adjusts the footprint.
- ptr3 = space->AllocWithGrowth(self, 2 * MB, &dummy);
- EXPECT_TRUE(ptr3 != NULL);
- InstallClass(ptr3, 2 * MB);
- space->Free(self, ptr3);
-
- // Final clean up.
- free1 = space->AllocationSize(ptr1);
- space->Free(self, ptr1);
- EXPECT_LE(1U * MB, free1);
-}
-
-TEST_F(SpaceTest, ZygoteSpace_DlMallocSpace) {
- ZygoteSpaceTestBody(SpaceTest::CreateDlMallocSpace);
-}
-
-TEST_F(SpaceTest, ZygoteSpace_RosAllocSpace) {
- ZygoteSpaceTestBody(SpaceTest::CreateRosAllocSpace);
-}
-
-void SpaceTest::AllocAndFreeTestBody(CreateSpaceFn create_space) {
- size_t dummy = 0;
- MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, NULL));
- ASSERT_TRUE(space != NULL);
- Thread* self = Thread::Current();
-
- // Make space findable to the heap, will also delete space when runtime is cleaned up
- AddSpace(space);
-
- // Succeeds, fits without adjusting the footprint limit.
- mirror::Object* ptr1 = space->Alloc(self, 1 * MB, &dummy);
- EXPECT_TRUE(ptr1 != NULL);
- InstallClass(ptr1, 1 * MB);
-
- // Fails, requires a higher footprint limit.
- mirror::Object* ptr2 = space->Alloc(self, 8 * MB, &dummy);
- EXPECT_TRUE(ptr2 == NULL);
-
- // Succeeds, adjusts the footprint.
- size_t ptr3_bytes_allocated;
- mirror::Object* ptr3 = space->AllocWithGrowth(self, 8 * MB, &ptr3_bytes_allocated);
- EXPECT_TRUE(ptr3 != NULL);
- EXPECT_LE(8U * MB, ptr3_bytes_allocated);
- InstallClass(ptr3, 8 * MB);
-
- // Fails, requires a higher footprint limit.
- mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy);
- EXPECT_TRUE(ptr4 == NULL);
-
- // Also fails, requires a higher allowed footprint.
- mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &dummy);
- EXPECT_TRUE(ptr5 == NULL);
-
- // Release some memory.
- ScopedObjectAccess soa(self);
- size_t free3 = space->AllocationSize(ptr3);
- EXPECT_EQ(free3, ptr3_bytes_allocated);
- space->Free(self, ptr3);
- EXPECT_LE(8U * MB, free3);
-
- // Succeeds, now that memory has been freed.
- mirror::Object* ptr6 = space->AllocWithGrowth(self, 9 * MB, &dummy);
- EXPECT_TRUE(ptr6 != NULL);
- InstallClass(ptr6, 9 * MB);
-
- // Final clean up.
- size_t free1 = space->AllocationSize(ptr1);
- space->Free(self, ptr1);
- EXPECT_LE(1U * MB, free1);
-}
-
-TEST_F(SpaceTest, AllocAndFree_DlMallocSpace) {
- AllocAndFreeTestBody(SpaceTest::CreateDlMallocSpace);
-}
-TEST_F(SpaceTest, AllocAndFree_RosAllocSpace) {
- AllocAndFreeTestBody(SpaceTest::CreateRosAllocSpace);
-}
-
-TEST_F(SpaceTest, LargeObjectTest) {
- size_t rand_seed = 0;
- for (size_t i = 0; i < 2; ++i) {
- LargeObjectSpace* los = NULL;
- if (i == 0) {
- los = space::LargeObjectMapSpace::Create("large object space");
- } else {
- los = space::FreeListSpace::Create("large object space", NULL, 128 * MB);
- }
-
- static const size_t num_allocations = 64;
- static const size_t max_allocation_size = 0x100000;
- std::vector<std::pair<mirror::Object*, size_t> > requests;
-
- for (size_t phase = 0; phase < 2; ++phase) {
- while (requests.size() < num_allocations) {
- size_t request_size = test_rand(&rand_seed) % max_allocation_size;
- size_t allocation_size = 0;
- mirror::Object* obj = los->Alloc(Thread::Current(), request_size, &allocation_size);
- ASSERT_TRUE(obj != NULL);
- ASSERT_EQ(allocation_size, los->AllocationSize(obj));
- ASSERT_GE(allocation_size, request_size);
- // Fill in our magic value.
- byte magic = (request_size & 0xFF) | 1;
- memset(obj, magic, request_size);
- requests.push_back(std::make_pair(obj, request_size));
- }
-
- // "Randomly" shuffle the requests.
- for (size_t k = 0; k < 10; ++k) {
- for (size_t j = 0; j < requests.size(); ++j) {
- std::swap(requests[j], requests[test_rand(&rand_seed) % requests.size()]);
- }
- }
-
- // Free 1 / 2 the allocations the first phase, and all the second phase.
- size_t limit = !phase ? requests.size() / 2 : 0;
- while (requests.size() > limit) {
- mirror::Object* obj = requests.back().first;
- size_t request_size = requests.back().second;
- requests.pop_back();
- byte magic = (request_size & 0xFF) | 1;
- for (size_t k = 0; k < request_size; ++k) {
- ASSERT_EQ(reinterpret_cast<const byte*>(obj)[k], magic);
- }
- ASSERT_GE(los->Free(Thread::Current(), obj), request_size);
- }
- }
-
- size_t bytes_allocated = 0;
- // Checks that the coalescing works.
- mirror::Object* obj = los->Alloc(Thread::Current(), 100 * MB, &bytes_allocated);
- EXPECT_TRUE(obj != NULL);
- los->Free(Thread::Current(), obj);
-
- EXPECT_EQ(0U, los->GetBytesAllocated());
- EXPECT_EQ(0U, los->GetObjectsAllocated());
- delete los;
- }
-}
-
-void SpaceTest::AllocAndFreeListTestBody(CreateSpaceFn create_space) {
- MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, NULL));
- ASSERT_TRUE(space != NULL);
-
- // Make space findable to the heap, will also delete space when runtime is cleaned up
- AddSpace(space);
- Thread* self = Thread::Current();
-
- // Succeeds, fits without adjusting the max allowed footprint.
- mirror::Object* lots_of_objects[1024];
- for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
- size_t allocation_size = 0;
- size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray();
- lots_of_objects[i] = space->Alloc(self, size_of_zero_length_byte_array, &allocation_size);
- EXPECT_TRUE(lots_of_objects[i] != nullptr);
- InstallClass(lots_of_objects[i], size_of_zero_length_byte_array);
- EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i]));
- }
-
- // Release memory and check pointers are NULL.
- {
- ScopedObjectAccess soa(self);
- space->FreeList(self, arraysize(lots_of_objects), lots_of_objects);
- for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
- EXPECT_TRUE(lots_of_objects[i] == nullptr);
- }
- }
-
- // Succeeds, fits by adjusting the max allowed footprint.
- for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
- size_t allocation_size = 0;
- lots_of_objects[i] = space->AllocWithGrowth(self, 1024, &allocation_size);
- EXPECT_TRUE(lots_of_objects[i] != nullptr);
- InstallClass(lots_of_objects[i], 1024);
- EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i]));
- }
-
- // Release memory and check pointers are NULL
- {
- ScopedObjectAccess soa(self);
- space->FreeList(self, arraysize(lots_of_objects), lots_of_objects);
- for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
- EXPECT_TRUE(lots_of_objects[i] == nullptr);
- }
- }
-}
-
-TEST_F(SpaceTest, AllocAndFreeList_DlMallocSpace) {
- AllocAndFreeListTestBody(SpaceTest::CreateDlMallocSpace);
-}
-TEST_F(SpaceTest, AllocAndFreeList_RosAllocSpace) {
- AllocAndFreeListTestBody(SpaceTest::CreateRosAllocSpace);
-}
-
-void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t object_size,
- int round, size_t growth_limit) {
- if (((object_size > 0 && object_size >= static_cast<intptr_t>(growth_limit))) ||
- ((object_size < 0 && -object_size >= static_cast<intptr_t>(growth_limit)))) {
- // No allocation can succeed
- return;
- }
-
- // The space's footprint equals amount of resources requested from system
- size_t footprint = space->GetFootprint();
-
- // The space must at least have its book keeping allocated
- EXPECT_GT(footprint, 0u);
-
- // But it shouldn't exceed the initial size
- EXPECT_LE(footprint, growth_limit);
-
- // space's size shouldn't exceed the initial size
- EXPECT_LE(space->Size(), growth_limit);
-
- // this invariant should always hold or else the space has grown to be larger than what the
- // space believes its size is (which will break invariants)
- EXPECT_GE(space->Size(), footprint);
-
- // Fill the space with lots of small objects up to the growth limit
- size_t max_objects = (growth_limit / (object_size > 0 ? object_size : 8)) + 1;
- UniquePtr<mirror::Object*[]> lots_of_objects(new mirror::Object*[max_objects]);
- size_t last_object = 0; // last object for which allocation succeeded
- size_t amount_allocated = 0; // amount of space allocated
- Thread* self = Thread::Current();
- size_t rand_seed = 123456789;
- for (size_t i = 0; i < max_objects; i++) {
- size_t alloc_fails = 0; // number of failed allocations
- size_t max_fails = 30; // number of times we fail allocation before giving up
- for (; alloc_fails < max_fails; alloc_fails++) {
- size_t alloc_size;
- if (object_size > 0) {
- alloc_size = object_size;
- } else {
- alloc_size = test_rand(&rand_seed) % static_cast<size_t>(-object_size);
- // Note the minimum size, which is the size of a zero-length byte array.
- size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray();
- if (alloc_size < size_of_zero_length_byte_array) {
- alloc_size = size_of_zero_length_byte_array;
- }
- }
- mirror::Object* object;
- size_t bytes_allocated = 0;
- if (round <= 1) {
- object = space->Alloc(self, alloc_size, &bytes_allocated);
- } else {
- object = space->AllocWithGrowth(self, alloc_size, &bytes_allocated);
- }
- footprint = space->GetFootprint();
- EXPECT_GE(space->Size(), footprint); // invariant
- if (object != NULL) { // allocation succeeded
- InstallClass(object, alloc_size);
- lots_of_objects.get()[i] = object;
- size_t allocation_size = space->AllocationSize(object);
- EXPECT_EQ(bytes_allocated, allocation_size);
- if (object_size > 0) {
- EXPECT_GE(allocation_size, static_cast<size_t>(object_size));
- } else {
- EXPECT_GE(allocation_size, 8u);
- }
- amount_allocated += allocation_size;
- break;
- }
- }
- if (alloc_fails == max_fails) {
- last_object = i;
- break;
- }
- }
- CHECK_NE(last_object, 0u); // we should have filled the space
- EXPECT_GT(amount_allocated, 0u);
-
- // We shouldn't have gone past the growth_limit
- EXPECT_LE(amount_allocated, growth_limit);
- EXPECT_LE(footprint, growth_limit);
- EXPECT_LE(space->Size(), growth_limit);
-
- // footprint and size should agree with amount allocated
- EXPECT_GE(footprint, amount_allocated);
- EXPECT_GE(space->Size(), amount_allocated);
-
- // Release storage in a semi-adhoc manner
- size_t free_increment = 96;
- while (true) {
- // Give the space a haircut
- space->Trim();
-
- // Bounds sanity
- footprint = space->GetFootprint();
- EXPECT_LE(amount_allocated, growth_limit);
- EXPECT_GE(footprint, amount_allocated);
- EXPECT_LE(footprint, growth_limit);
- EXPECT_GE(space->Size(), amount_allocated);
- EXPECT_LE(space->Size(), growth_limit);
-
- if (free_increment == 0) {
- break;
- }
-
- {
- // Free some objects
- ScopedObjectAccess soa(self);
- for (size_t i = 0; i < last_object; i += free_increment) {
- mirror::Object* object = lots_of_objects.get()[i];
- if (object == NULL) {
- continue;
- }
- size_t allocation_size = space->AllocationSize(object);
- if (object_size > 0) {
- EXPECT_GE(allocation_size, static_cast<size_t>(object_size));
- } else {
- EXPECT_GE(allocation_size, 8u);
- }
- space->Free(self, object);
- lots_of_objects.get()[i] = NULL;
- amount_allocated -= allocation_size;
- footprint = space->GetFootprint();
- EXPECT_GE(space->Size(), footprint); // invariant
- }
-
- free_increment >>= 1;
- }
- }
- // The space has become empty here before allocating a large object
- // below. For RosAlloc, revoke thread-local runs, which are kept
- // even when empty for a performance reason, so that they won't
- // cause the following large object allocation to fail due to
- // potential fragmentation. Note they are normally revoked at each
- // GC (but no GC here.)
- space->RevokeAllThreadLocalBuffers();
-
- // All memory was released, try a large allocation to check freed memory is being coalesced
- mirror::Object* large_object;
- size_t three_quarters_space = (growth_limit / 2) + (growth_limit / 4);
- size_t bytes_allocated = 0;
- if (round <= 1) {
- large_object = space->Alloc(self, three_quarters_space, &bytes_allocated);
- } else {
- large_object = space->AllocWithGrowth(self, three_quarters_space, &bytes_allocated);
- }
- EXPECT_TRUE(large_object != NULL);
- InstallClass(large_object, three_quarters_space);
-
- // Sanity check footprint
- footprint = space->GetFootprint();
- EXPECT_LE(footprint, growth_limit);
- EXPECT_GE(space->Size(), footprint);
- EXPECT_LE(space->Size(), growth_limit);
-
- // Clean up
- {
- ScopedObjectAccess soa(self);
- space->Free(self, large_object);
- }
- // Sanity check footprint
- footprint = space->GetFootprint();
- EXPECT_LE(footprint, growth_limit);
- EXPECT_GE(space->Size(), footprint);
- EXPECT_LE(space->Size(), growth_limit);
-}
-
-void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, CreateSpaceFn create_space) {
- if (object_size < SizeOfZeroLengthByteArray()) {
- // Too small for the object layout/model.
- return;
- }
- size_t initial_size = 4 * MB;
- size_t growth_limit = 8 * MB;
- size_t capacity = 16 * MB;
- MallocSpace* space(create_space("test", initial_size, growth_limit, capacity, NULL));
- ASSERT_TRUE(space != NULL);
-
- // Basic sanity
- EXPECT_EQ(space->Capacity(), growth_limit);
- EXPECT_EQ(space->NonGrowthLimitCapacity(), capacity);
-
- // Make space findable to the heap, will also delete space when runtime is cleaned up
- AddSpace(space);
-
- // In this round we don't allocate with growth and therefore can't grow past the initial size.
- // This effectively makes the growth_limit the initial_size, so assert this.
- SizeFootPrintGrowthLimitAndTrimBody(space, object_size, 1, initial_size);
- SizeFootPrintGrowthLimitAndTrimBody(space, object_size, 2, growth_limit);
- // Remove growth limit
- space->ClearGrowthLimit();
- EXPECT_EQ(space->Capacity(), capacity);
- SizeFootPrintGrowthLimitAndTrimBody(space, object_size, 3, capacity);
-}
-
-#define TEST_SizeFootPrintGrowthLimitAndTrim(name, size) \
- TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_##name##_DlMallocSpace) { \
- SizeFootPrintGrowthLimitAndTrimDriver(size, SpaceTest::CreateDlMallocSpace); \
- } \
- TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_RandomAllocationsWithMax_##name##_DlMallocSpace) { \
- SizeFootPrintGrowthLimitAndTrimDriver(-size, SpaceTest::CreateDlMallocSpace); \
- } \
- TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_##name##_RosAllocSpace) { \
- SizeFootPrintGrowthLimitAndTrimDriver(size, SpaceTest::CreateRosAllocSpace); \
- } \
- TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_RandomAllocationsWithMax_##name##_RosAllocSpace) { \
- SizeFootPrintGrowthLimitAndTrimDriver(-size, SpaceTest::CreateRosAllocSpace); \
- }
-
-// Each size test is its own test so that we get a fresh heap each time
-TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_12B_DlMallocSpace) {
- SizeFootPrintGrowthLimitAndTrimDriver(12, SpaceTest::CreateDlMallocSpace);
-}
-TEST_F(SpaceTest, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_12B_RosAllocSpace) {
- SizeFootPrintGrowthLimitAndTrimDriver(12, SpaceTest::CreateRosAllocSpace);
-}
-TEST_SizeFootPrintGrowthLimitAndTrim(16B, 16)
-TEST_SizeFootPrintGrowthLimitAndTrim(24B, 24)
-TEST_SizeFootPrintGrowthLimitAndTrim(32B, 32)
-TEST_SizeFootPrintGrowthLimitAndTrim(64B, 64)
-TEST_SizeFootPrintGrowthLimitAndTrim(128B, 128)
-TEST_SizeFootPrintGrowthLimitAndTrim(1KB, 1 * KB)
-TEST_SizeFootPrintGrowthLimitAndTrim(4KB, 4 * KB)
-TEST_SizeFootPrintGrowthLimitAndTrim(1MB, 1 * MB)
-TEST_SizeFootPrintGrowthLimitAndTrim(4MB, 4 * MB)
-TEST_SizeFootPrintGrowthLimitAndTrim(8MB, 8 * MB)
-
-} // namespace space
-} // namespace gc
-} // namespace art
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
new file mode 100644
index 0000000..093967e
--- /dev/null
+++ b/runtime/gc/space/space_test.h
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_SPACE_SPACE_TEST_H_
+#define ART_RUNTIME_GC_SPACE_SPACE_TEST_H_
+
+#include "zygote_space.h"
+
+#include "common_test.h"
+#include "globals.h"
+#include "UniquePtr.h"
+#include "mirror/array-inl.h"
+#include "mirror/object-inl.h"
+
+#include <stdint.h>
+
+namespace art {
+namespace gc {
+namespace space {
+
+class SpaceTest : public CommonTest {
+ public:
+ void AddSpace(ContinuousSpace* space) {
+ // For RosAlloc, revoke the thread local runs before moving onto a
+ // new alloc space.
+ Runtime::Current()->GetHeap()->RevokeAllThreadLocalBuffers();
+ Runtime::Current()->GetHeap()->AddSpace(space);
+ }
+ void InstallClass(SirtRef<mirror::Object>& o, size_t size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Note the minimum size, which is the size of a zero-length byte array.
+ EXPECT_GE(size, SizeOfZeroLengthByteArray());
+ Thread* self = Thread::Current();
+ SirtRef<mirror::ClassLoader> null_loader(self, nullptr);
+ mirror::Class* byte_array_class = Runtime::Current()->GetClassLinker()->FindClass(self, "[B",
+ null_loader);
+ EXPECT_TRUE(byte_array_class != nullptr);
+ o->SetClass(byte_array_class);
+ mirror::Array* arr = o->AsArray<kVerifyNone>();
+ size_t header_size = SizeOfZeroLengthByteArray();
+ int32_t length = size - header_size;
+ arr->SetLength(length);
+ EXPECT_EQ(arr->SizeOf<kVerifyNone>(), size);
+ }
+
+ static size_t SizeOfZeroLengthByteArray() {
+ return mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimByte)).Uint32Value();
+ }
+
+ typedef MallocSpace* (*CreateSpaceFn)(const std::string& name, size_t initial_size, size_t growth_limit,
+ size_t capacity, byte* requested_begin);
+ void InitTestBody(CreateSpaceFn create_space);
+ void ZygoteSpaceTestBody(CreateSpaceFn create_space);
+ void AllocAndFreeTestBody(CreateSpaceFn create_space);
+ void AllocAndFreeListTestBody(CreateSpaceFn create_space);
+
+ void SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t object_size,
+ int round, size_t growth_limit);
+ void SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, CreateSpaceFn create_space);
+};
+
+static size_t test_rand(size_t* seed) {
+ *seed = *seed * 1103515245 + 12345;
+ return *seed;
+}
+
+void SpaceTest::InitTestBody(CreateSpaceFn create_space) {
+ {
+ // Init < max == growth
+ UniquePtr<Space> space(create_space("test", 16 * MB, 32 * MB, 32 * MB, nullptr));
+ EXPECT_TRUE(space.get() != nullptr);
+ }
+ {
+ // Init == max == growth
+ UniquePtr<Space> space(create_space("test", 16 * MB, 16 * MB, 16 * MB, nullptr));
+ EXPECT_TRUE(space.get() != nullptr);
+ }
+ {
+ // Init > max == growth
+ UniquePtr<Space> space(create_space("test", 32 * MB, 16 * MB, 16 * MB, nullptr));
+ EXPECT_TRUE(space.get() == nullptr);
+ }
+ {
+ // Growth == init < max
+ UniquePtr<Space> space(create_space("test", 16 * MB, 16 * MB, 32 * MB, nullptr));
+ EXPECT_TRUE(space.get() != nullptr);
+ }
+ {
+ // Growth < init < max
+ UniquePtr<Space> space(create_space("test", 16 * MB, 8 * MB, 32 * MB, nullptr));
+ EXPECT_TRUE(space.get() == nullptr);
+ }
+ {
+ // Init < growth < max
+ UniquePtr<Space> space(create_space("test", 8 * MB, 16 * MB, 32 * MB, nullptr));
+ EXPECT_TRUE(space.get() != nullptr);
+ }
+ {
+ // Init < max < growth
+ UniquePtr<Space> space(create_space("test", 8 * MB, 32 * MB, 16 * MB, nullptr));
+ EXPECT_TRUE(space.get() == nullptr);
+ }
+}
+
+// TODO: This test is not very good, we should improve it.
+// The test should do more allocations before the creation of the ZygoteSpace, and then do
+// allocations after the ZygoteSpace is created. The test should also do some GCs to ensure that
+// the GC works with the ZygoteSpace.
+void SpaceTest::ZygoteSpaceTestBody(CreateSpaceFn create_space) {
+ size_t dummy = 0;
+ MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, nullptr));
+ ASSERT_TRUE(space != nullptr);
+
+ // Make space findable to the heap, will also delete space when runtime is cleaned up
+ AddSpace(space);
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ // Succeeds, fits without adjusting the footprint limit.
+ SirtRef<mirror::Object> ptr1(self, space->Alloc(self, 1 * MB, &dummy));
+ EXPECT_TRUE(ptr1.get() != nullptr);
+ InstallClass(ptr1, 1 * MB);
+
+ // Fails, requires a higher footprint limit.
+ mirror::Object* ptr2 = space->Alloc(self, 8 * MB, &dummy);
+ EXPECT_TRUE(ptr2 == nullptr);
+
+ // Succeeds, adjusts the footprint.
+ size_t ptr3_bytes_allocated;
+ SirtRef<mirror::Object> ptr3(self, space->AllocWithGrowth(self, 8 * MB, &ptr3_bytes_allocated));
+ EXPECT_TRUE(ptr3.get() != nullptr);
+ EXPECT_LE(8U * MB, ptr3_bytes_allocated);
+ InstallClass(ptr3, 8 * MB);
+
+ // Fails, requires a higher footprint limit.
+ mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy);
+ EXPECT_TRUE(ptr4 == nullptr);
+
+ // Also fails, requires a higher allowed footprint.
+ mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &dummy);
+ EXPECT_TRUE(ptr5 == nullptr);
+
+ // Release some memory.
+ size_t free3 = space->AllocationSize(ptr3.get());
+ EXPECT_EQ(free3, ptr3_bytes_allocated);
+ EXPECT_EQ(free3, space->Free(self, ptr3.reset(nullptr)));
+ EXPECT_LE(8U * MB, free3);
+
+ // Succeeds, now that memory has been freed.
+ SirtRef<mirror::Object> ptr6(self, space->AllocWithGrowth(self, 9 * MB, &dummy));
+ EXPECT_TRUE(ptr6.get() != nullptr);
+ InstallClass(ptr6, 9 * MB);
+
+ // Final clean up.
+ size_t free1 = space->AllocationSize(ptr1.get());
+ space->Free(self, ptr1.reset(nullptr));
+ EXPECT_LE(1U * MB, free1);
+
+ // Make sure that the zygote space isn't directly at the start of the space.
+ space->Alloc(self, 1U * MB, &dummy);
+
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ space::Space* old_space = space;
+ heap->RemoveSpace(old_space);
+ space::ZygoteSpace* zygote_space = space->CreateZygoteSpace("alloc space",
+ heap->IsLowMemoryMode(),
+ &space);
+ delete old_space;
+ // Add the zygote space.
+ AddSpace(zygote_space);
+
+ // Make space findable to the heap, will also delete space when runtime is cleaned up
+ AddSpace(space);
+
+ // Succeeds, fits without adjusting the footprint limit.
+ ptr1.reset(space->Alloc(self, 1 * MB, &dummy));
+ EXPECT_TRUE(ptr1.get() != nullptr);
+ InstallClass(ptr1, 1 * MB);
+
+ // Fails, requires a higher footprint limit.
+ ptr2 = space->Alloc(self, 8 * MB, &dummy);
+ EXPECT_TRUE(ptr2 == nullptr);
+
+ // Succeeds, adjusts the footprint.
+ ptr3.reset(space->AllocWithGrowth(self, 2 * MB, &dummy));
+ EXPECT_TRUE(ptr3.get() != nullptr);
+ InstallClass(ptr3, 2 * MB);
+ space->Free(self, ptr3.reset(nullptr));
+
+ // Final clean up.
+ free1 = space->AllocationSize(ptr1.get());
+ space->Free(self, ptr1.reset(nullptr));
+ EXPECT_LE(1U * MB, free1);
+}
+
+void SpaceTest::AllocAndFreeTestBody(CreateSpaceFn create_space) {
+ size_t dummy = 0;
+ MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, nullptr));
+ ASSERT_TRUE(space != nullptr);
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ // Make space findable to the heap, will also delete space when runtime is cleaned up
+ AddSpace(space);
+
+ // Succeeds, fits without adjusting the footprint limit.
+ SirtRef<mirror::Object> ptr1(self, space->Alloc(self, 1 * MB, &dummy));
+ EXPECT_TRUE(ptr1.get() != nullptr);
+ InstallClass(ptr1, 1 * MB);
+
+ // Fails, requires a higher footprint limit.
+ mirror::Object* ptr2 = space->Alloc(self, 8 * MB, &dummy);
+ EXPECT_TRUE(ptr2 == nullptr);
+
+ // Succeeds, adjusts the footprint.
+ size_t ptr3_bytes_allocated;
+ SirtRef<mirror::Object> ptr3(self, space->AllocWithGrowth(self, 8 * MB, &ptr3_bytes_allocated));
+ EXPECT_TRUE(ptr3.get() != nullptr);
+ EXPECT_LE(8U * MB, ptr3_bytes_allocated);
+ InstallClass(ptr3, 8 * MB);
+
+ // Fails, requires a higher footprint limit.
+ mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy);
+ EXPECT_TRUE(ptr4 == nullptr);
+
+ // Also fails, requires a higher allowed footprint.
+ mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &dummy);
+ EXPECT_TRUE(ptr5 == nullptr);
+
+ // Release some memory.
+ size_t free3 = space->AllocationSize(ptr3.get());
+ EXPECT_EQ(free3, ptr3_bytes_allocated);
+ space->Free(self, ptr3.reset(nullptr));
+ EXPECT_LE(8U * MB, free3);
+
+ // Succeeds, now that memory has been freed.
+ SirtRef<mirror::Object> ptr6(self, space->AllocWithGrowth(self, 9 * MB, &dummy));
+ EXPECT_TRUE(ptr6.get() != nullptr);
+ InstallClass(ptr6, 9 * MB);
+
+ // Final clean up.
+ size_t free1 = space->AllocationSize(ptr1.get());
+ space->Free(self, ptr1.reset(nullptr));
+ EXPECT_LE(1U * MB, free1);
+}
+
+void SpaceTest::AllocAndFreeListTestBody(CreateSpaceFn create_space) {
+ MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, nullptr));
+ ASSERT_TRUE(space != nullptr);
+
+ // Make space findable to the heap, will also delete space when runtime is cleaned up
+ AddSpace(space);
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ // Succeeds, fits without adjusting the max allowed footprint.
+ mirror::Object* lots_of_objects[1024];
+ for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
+ size_t allocation_size = 0;
+ size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray();
+ lots_of_objects[i] = space->Alloc(self, size_of_zero_length_byte_array, &allocation_size);
+ EXPECT_TRUE(lots_of_objects[i] != nullptr);
+ SirtRef<mirror::Object> obj(self, lots_of_objects[i]);
+ InstallClass(obj, size_of_zero_length_byte_array);
+ lots_of_objects[i] = obj.get();
+ EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i]));
+ }
+
+ // Release memory and check pointers are nullptr.
+ space->FreeList(self, arraysize(lots_of_objects), lots_of_objects);
+ for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
+ EXPECT_TRUE(lots_of_objects[i] == nullptr);
+ }
+
+ // Succeeds, fits by adjusting the max allowed footprint.
+ for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
+ size_t allocation_size = 0;
+ lots_of_objects[i] = space->AllocWithGrowth(self, 1024, &allocation_size);
+ EXPECT_TRUE(lots_of_objects[i] != nullptr);
+ SirtRef<mirror::Object> obj(self, lots_of_objects[i]);
+ InstallClass(obj, 1024);
+ lots_of_objects[i] = obj.get();
+ EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i]));
+ }
+
+ // Release memory and check pointers are nullptr
+ // TODO: This isn't compaction safe, fix.
+ space->FreeList(self, arraysize(lots_of_objects), lots_of_objects);
+ for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
+ EXPECT_TRUE(lots_of_objects[i] == nullptr);
+ }
+}
+
+void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t object_size,
+ int round, size_t growth_limit) {
+ if (((object_size > 0 && object_size >= static_cast<intptr_t>(growth_limit))) ||
+ ((object_size < 0 && -object_size >= static_cast<intptr_t>(growth_limit)))) {
+ // No allocation can succeed
+ return;
+ }
+
+ // The space's footprint equals amount of resources requested from system
+ size_t footprint = space->GetFootprint();
+
+ // The space must at least have its book keeping allocated
+ EXPECT_GT(footprint, 0u);
+
+ // But it shouldn't exceed the initial size
+ EXPECT_LE(footprint, growth_limit);
+
+ // space's size shouldn't exceed the initial size
+ EXPECT_LE(space->Size(), growth_limit);
+
+ // this invariant should always hold or else the space has grown to be larger than what the
+ // space believes its size is (which will break invariants)
+ EXPECT_GE(space->Size(), footprint);
+
+ // Fill the space with lots of small objects up to the growth limit
+ size_t max_objects = (growth_limit / (object_size > 0 ? object_size : 8)) + 1;
+ UniquePtr<mirror::Object*[]> lots_of_objects(new mirror::Object*[max_objects]);
+ size_t last_object = 0; // last object for which allocation succeeded
+ size_t amount_allocated = 0; // amount of space allocated
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ size_t rand_seed = 123456789;
+ for (size_t i = 0; i < max_objects; i++) {
+ size_t alloc_fails = 0; // number of failed allocations
+ size_t max_fails = 30; // number of times we fail allocation before giving up
+ for (; alloc_fails < max_fails; alloc_fails++) {
+ size_t alloc_size;
+ if (object_size > 0) {
+ alloc_size = object_size;
+ } else {
+ alloc_size = test_rand(&rand_seed) % static_cast<size_t>(-object_size);
+ // Note the minimum size, which is the size of a zero-length byte array.
+ size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray();
+ if (alloc_size < size_of_zero_length_byte_array) {
+ alloc_size = size_of_zero_length_byte_array;
+ }
+ }
+ SirtRef<mirror::Object> object(self, nullptr);
+ size_t bytes_allocated = 0;
+ if (round <= 1) {
+ object.reset(space->Alloc(self, alloc_size, &bytes_allocated));
+ } else {
+ object.reset(space->AllocWithGrowth(self, alloc_size, &bytes_allocated));
+ }
+ footprint = space->GetFootprint();
+ EXPECT_GE(space->Size(), footprint); // invariant
+ if (object.get() != nullptr) { // allocation succeeded
+ InstallClass(object, alloc_size);
+ lots_of_objects[i] = object.get();
+ size_t allocation_size = space->AllocationSize(object.get());
+ EXPECT_EQ(bytes_allocated, allocation_size);
+ if (object_size > 0) {
+ EXPECT_GE(allocation_size, static_cast<size_t>(object_size));
+ } else {
+ EXPECT_GE(allocation_size, 8u);
+ }
+ amount_allocated += allocation_size;
+ break;
+ }
+ }
+ if (alloc_fails == max_fails) {
+ last_object = i;
+ break;
+ }
+ }
+ CHECK_NE(last_object, 0u); // we should have filled the space
+ EXPECT_GT(amount_allocated, 0u);
+
+ // We shouldn't have gone past the growth_limit
+ EXPECT_LE(amount_allocated, growth_limit);
+ EXPECT_LE(footprint, growth_limit);
+ EXPECT_LE(space->Size(), growth_limit);
+
+ // footprint and size should agree with amount allocated
+ EXPECT_GE(footprint, amount_allocated);
+ EXPECT_GE(space->Size(), amount_allocated);
+
+ // Release storage in a semi-adhoc manner
+ size_t free_increment = 96;
+ while (true) {
+ {
+ ScopedThreadStateChange tsc(self, kNative);
+ // Give the space a haircut.
+ space->Trim();
+ }
+
+ // Bounds sanity
+ footprint = space->GetFootprint();
+ EXPECT_LE(amount_allocated, growth_limit);
+ EXPECT_GE(footprint, amount_allocated);
+ EXPECT_LE(footprint, growth_limit);
+ EXPECT_GE(space->Size(), amount_allocated);
+ EXPECT_LE(space->Size(), growth_limit);
+
+ if (free_increment == 0) {
+ break;
+ }
+
+ // Free some objects
+ for (size_t i = 0; i < last_object; i += free_increment) {
+ mirror::Object* object = lots_of_objects.get()[i];
+ if (object == nullptr) {
+ continue;
+ }
+ size_t allocation_size = space->AllocationSize(object);
+ if (object_size > 0) {
+ EXPECT_GE(allocation_size, static_cast<size_t>(object_size));
+ } else {
+ EXPECT_GE(allocation_size, 8u);
+ }
+ space->Free(self, object);
+ lots_of_objects.get()[i] = nullptr;
+ amount_allocated -= allocation_size;
+ footprint = space->GetFootprint();
+ EXPECT_GE(space->Size(), footprint); // invariant
+ }
+
+ free_increment >>= 1;
+ }
+
+ // The space has become empty here before allocating a large object
+ // below. For RosAlloc, revoke thread-local runs, which are kept
+ // even when empty for a performance reason, so that they won't
+ // cause the following large object allocation to fail due to
+ // potential fragmentation. Note they are normally revoked at each
+ // GC (but no GC here.)
+ space->RevokeAllThreadLocalBuffers();
+
+ // All memory was released, try a large allocation to check freed memory is being coalesced
+ SirtRef<mirror::Object> large_object(self, nullptr);
+ size_t three_quarters_space = (growth_limit / 2) + (growth_limit / 4);
+ size_t bytes_allocated = 0;
+ if (round <= 1) {
+ large_object.reset(space->Alloc(self, three_quarters_space, &bytes_allocated));
+ } else {
+ large_object.reset(space->AllocWithGrowth(self, three_quarters_space, &bytes_allocated));
+ }
+ EXPECT_TRUE(large_object.get() != nullptr);
+ InstallClass(large_object, three_quarters_space);
+
+ // Sanity check footprint
+ footprint = space->GetFootprint();
+ EXPECT_LE(footprint, growth_limit);
+ EXPECT_GE(space->Size(), footprint);
+ EXPECT_LE(space->Size(), growth_limit);
+
+ // Clean up
+ space->Free(self, large_object.reset(nullptr));
+
+ // Sanity check footprint
+ footprint = space->GetFootprint();
+ EXPECT_LE(footprint, growth_limit);
+ EXPECT_GE(space->Size(), footprint);
+ EXPECT_LE(space->Size(), growth_limit);
+}
+
+void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, CreateSpaceFn create_space) {
+ if (object_size < SizeOfZeroLengthByteArray()) {
+ // Too small for the object layout/model.
+ return;
+ }
+ size_t initial_size = 4 * MB;
+ size_t growth_limit = 8 * MB;
+ size_t capacity = 16 * MB;
+ MallocSpace* space(create_space("test", initial_size, growth_limit, capacity, nullptr));
+ ASSERT_TRUE(space != nullptr);
+
+ // Basic sanity
+ EXPECT_EQ(space->Capacity(), growth_limit);
+ EXPECT_EQ(space->NonGrowthLimitCapacity(), capacity);
+
+ // Make space findable to the heap, will also delete space when runtime is cleaned up
+ AddSpace(space);
+
+ // In this round we don't allocate with growth and therefore can't grow past the initial size.
+ // This effectively makes the growth_limit the initial_size, so assert this.
+ SizeFootPrintGrowthLimitAndTrimBody(space, object_size, 1, initial_size);
+ SizeFootPrintGrowthLimitAndTrimBody(space, object_size, 2, growth_limit);
+ // Remove growth limit
+ space->ClearGrowthLimit();
+ EXPECT_EQ(space->Capacity(), capacity);
+ SizeFootPrintGrowthLimitAndTrimBody(space, object_size, 3, capacity);
+}
+
+#define TEST_SizeFootPrintGrowthLimitAndTrim(name, spaceName, spaceFn, size) \
+ TEST_F(spaceName##Test, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_##name) { \
+ SizeFootPrintGrowthLimitAndTrimDriver(size, spaceFn); \
+ } \
+ TEST_F(spaceName##Test, SizeFootPrintGrowthLimitAndTrim_RandomAllocationsWithMax_##name) { \
+ SizeFootPrintGrowthLimitAndTrimDriver(-size, spaceFn); \
+ }
+
+#define TEST_SPACE_CREATE_FN(spaceName, spaceFn) \
+ class spaceName##Test : public SpaceTest { \
+ }; \
+ \
+ TEST_F(spaceName##Test, Init) { \
+ InitTestBody(spaceFn); \
+ } \
+ TEST_F(spaceName##Test, ZygoteSpace) { \
+ ZygoteSpaceTestBody(spaceFn); \
+ } \
+ TEST_F(spaceName##Test, AllocAndFree) { \
+ AllocAndFreeTestBody(spaceFn); \
+ } \
+ TEST_F(spaceName##Test, AllocAndFreeList) { \
+ AllocAndFreeListTestBody(spaceFn); \
+ } \
+ TEST_F(spaceName##Test, SizeFootPrintGrowthLimitAndTrim_AllocationsOf_12B) { \
+ SizeFootPrintGrowthLimitAndTrimDriver(12, spaceFn); \
+ } \
+ TEST_SizeFootPrintGrowthLimitAndTrim(16B, spaceName, spaceFn, 16) \
+ TEST_SizeFootPrintGrowthLimitAndTrim(24B, spaceName, spaceFn, 24) \
+ TEST_SizeFootPrintGrowthLimitAndTrim(32B, spaceName, spaceFn, 32) \
+ TEST_SizeFootPrintGrowthLimitAndTrim(64B, spaceName, spaceFn, 64) \
+ TEST_SizeFootPrintGrowthLimitAndTrim(128B, spaceName, spaceFn, 128) \
+ TEST_SizeFootPrintGrowthLimitAndTrim(1KB, spaceName, spaceFn, 1 * KB) \
+ TEST_SizeFootPrintGrowthLimitAndTrim(4KB, spaceName, spaceFn, 4 * KB) \
+ TEST_SizeFootPrintGrowthLimitAndTrim(1MB, spaceName, spaceFn, 1 * MB) \
+ TEST_SizeFootPrintGrowthLimitAndTrim(4MB, spaceName, spaceFn, 4 * MB) \
+ TEST_SizeFootPrintGrowthLimitAndTrim(8MB, spaceName, spaceFn, 8 * MB)
+
+} // namespace space
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_SPACE_SPACE_TEST_H_
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index b6c6cb4..78e1992 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -48,7 +48,7 @@
static const size_t kTableMax = 20;
IndirectReferenceTable irt(kTableInitial, kTableMax, kGlobal);
- mirror::Class* c = class_linker_->FindSystemClass("Ljava/lang/Object;");
+ mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
ASSERT_TRUE(c != NULL);
mirror::Object* obj0 = c->AllocObject(soa.Self());
ASSERT_TRUE(obj0 != NULL);
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 59ffdc1..9d05169 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -145,12 +145,10 @@
static void InstrumentationInstallStack(Thread* thread, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
struct InstallStackVisitor : public StackVisitor {
- InstallStackVisitor(Thread* thread, Context* context, uintptr_t instrumentation_exit_pc,
- bool is_deoptimization_enabled)
+ InstallStackVisitor(Thread* thread, Context* context, uintptr_t instrumentation_exit_pc)
: StackVisitor(thread, context), instrumentation_stack_(thread->GetInstrumentationStack()),
existing_instrumentation_frames_count_(instrumentation_stack_->size()),
instrumentation_exit_pc_(instrumentation_exit_pc),
- is_deoptimization_enabled_(is_deoptimization_enabled),
reached_existing_instrumentation_frames_(false), instrumentation_stack_depth_(0),
last_return_pc_(0) {
}
@@ -218,7 +216,6 @@
const size_t existing_instrumentation_frames_count_;
std::vector<uint32_t> dex_pcs_;
const uintptr_t instrumentation_exit_pc_;
- const bool is_deoptimization_enabled_;
bool reached_existing_instrumentation_frames_;
size_t instrumentation_stack_depth_;
uintptr_t last_return_pc_;
@@ -232,12 +229,11 @@
Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
UniquePtr<Context> context(Context::Create());
uintptr_t instrumentation_exit_pc = GetQuickInstrumentationExitPc();
- InstallStackVisitor visitor(thread, context.get(), instrumentation_exit_pc,
- instrumentation->IsDeoptimizationEnabled());
+ InstallStackVisitor visitor(thread, context.get(), instrumentation_exit_pc);
visitor.WalkStack(true);
CHECK_EQ(visitor.dex_pcs_.size(), thread->GetInstrumentationStack()->size());
- if (!instrumentation->IsDeoptimizationEnabled()) {
+ if (!instrumentation->ShouldNotifyMethodEnterExitEvents()) {
// Create method enter events for all methods currently on the thread's stack. We only do this
// if no debugger is attached to prevent from posting events twice.
typedef std::deque<InstrumentationStackFrame>::const_reverse_iterator It;
@@ -295,7 +291,7 @@
CHECK(m == instrumentation_frame.method_) << PrettyMethod(m);
}
SetReturnPc(instrumentation_frame.return_pc_);
- if (!instrumentation_->IsDeoptimizationEnabled()) {
+ if (!instrumentation_->ShouldNotifyMethodEnterExitEvents()) {
// Create the method exit events. As the methods didn't really exit the result is 0.
// We only do this if no debugger is attached to prevent from posting events twice.
instrumentation_->MethodExitEvent(thread_, instrumentation_frame.this_object_, m,
@@ -586,9 +582,12 @@
void Instrumentation::EnableDeoptimization() {
CHECK(deoptimized_methods_.empty());
+ CHECK_EQ(deoptimization_enabled_, false);
+ deoptimization_enabled_ = true;
}
void Instrumentation::DisableDeoptimization() {
+ CHECK_EQ(deoptimization_enabled_, true);
// If we deoptimized everything, undo it.
if (interpreter_stubs_installed_) {
UndeoptimizeEverything();
@@ -599,10 +598,12 @@
Undeoptimize(*it_begin);
}
CHECK(deoptimized_methods_.empty());
+ deoptimization_enabled_ = false;
}
-bool Instrumentation::IsDeoptimizationEnabled() const {
- return interpreter_stubs_installed_ || !deoptimized_methods_.empty();
+// Indicates if instrumentation should notify method enter/exit events to the listeners.
+bool Instrumentation::ShouldNotifyMethodEnterExitEvents() const {
+ return deoptimization_enabled_ || interpreter_stubs_installed_;
}
void Instrumentation::DeoptimizeEverything() {
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index f01add1..1ce72bd 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -105,6 +105,7 @@
have_method_entry_listeners_(false), have_method_exit_listeners_(false),
have_method_unwind_listeners_(false), have_dex_pc_listeners_(false),
have_exception_caught_listeners_(false),
+ deoptimization_enabled_(false),
interpreter_handler_table_(kMainHandlerTable),
quick_alloc_entry_points_instrumentation_counter_(0) {}
@@ -124,7 +125,7 @@
// Deoptimization.
void EnableDeoptimization() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
void DisableDeoptimization() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsDeoptimizationEnabled() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool ShouldNotifyMethodEnterExitEvents() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Executes everything with interpreter.
void DeoptimizeEverything()
@@ -345,6 +346,7 @@
// only.
// TODO we need to visit these methods as roots.
std::set<mirror::ArtMethod*> deoptimized_methods_;
+ bool deoptimization_enabled_;
// Current interpreter handler table. This is updated each time the thread state flags are
// modified.
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index e37fb61..83a1fbc 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -31,7 +31,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// If both register locations contains the same value, the register probably holds a reference.
int32_t src_value = shadow_frame.GetVReg(src_reg);
- mirror::Object* o = shadow_frame.GetVRegReference<false>(src_reg);
+ mirror::Object* o = shadow_frame.GetVRegReference<kVerifyNone>(src_reg);
if (src_value == reinterpret_cast<intptr_t>(o)) {
new_shadow_frame->SetVRegReference(dest_reg, o);
} else {
@@ -280,7 +280,7 @@
std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str()));
SirtRef<ClassLoader> class_loader(self, nullptr); // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader();
- Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
+ Class* found = Runtime::Current()->GetClassLinker()->FindClass(self, descriptor.c_str(),
class_loader);
CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: "
<< PrettyDescriptor(descriptor);
@@ -289,7 +289,7 @@
SirtRef<ClassLoader> class_loader(self, down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset)));
std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset + 1)->AsString()->ToModifiedUtf8().c_str()));
- Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
+ Class* found = Runtime::Current()->GetClassLinker()->FindClass(self, descriptor.c_str(),
class_loader);
result->SetL(found);
} else if (name == "java.lang.Object java.lang.Class.newInstance()") {
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index e45cb6e..1ec795f 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -154,8 +154,9 @@
* release it in the "clear" call.
*/
// ObjectId GetWaitForEventThread();
- void SetWaitForEventThread(ObjectId threadId);
- void ClearWaitForEventThread();
+ void SetWaitForEventThread(ObjectId threadId)
+ LOCKS_EXCLUDED(event_thread_lock_, process_request_lock_);
+ void ClearWaitForEventThread() LOCKS_EXCLUDED(event_thread_lock);
/*
* These notify the debug code that something interesting has happened. This
@@ -346,7 +347,7 @@
// Used to synchronize request processing and event sending (to avoid sending an event before
// sending the reply of a command being processed).
- Mutex process_request_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ Mutex process_request_lock_ ACQUIRED_AFTER(event_thread_lock_);
ConditionVariable process_request_cond_ GUARDED_BY(process_request_lock_);
bool processing_request_ GUARDED_BY(process_request_lock_);
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 0ff78d0..4b170ba 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -1685,6 +1685,12 @@
SetWaitForEventThread(0);
/*
+ * We do not want events to be sent while we process a request. Indicate the JDWP thread starts
+ * to process a request so other threads wait for it to finish before sending an event.
+ */
+ StartProcessingRequest();
+
+ /*
* Tell the VM that we're running and shouldn't be interrupted by GC.
* Do this after anything that can stall indefinitely.
*/
@@ -1779,9 +1785,15 @@
Thread* self = Thread::Current();
CHECK_NE(self, GetDebugThread()) << "Events should not be posted by debug thread";
MutexLock mu(self, process_request_lock_);
+ bool waited = false;
while (processing_request_) {
+ VLOG(jdwp) << StringPrintf("wait for processing request");
+ waited = true;
process_request_cond_.Wait(self);
}
+ if (waited) {
+ VLOG(jdwp) << StringPrintf("finished waiting for processing request");
+ }
CHECK_EQ(processing_request_, false);
}
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index ba49c45..500585d 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -386,10 +386,14 @@
JdwpNetStateBase* netStateBase = reinterpret_cast<JdwpNetStateBase*>(netState);
JDWP::Request request(netStateBase->input_buffer_, netStateBase->input_count_);
- StartProcessingRequest();
ExpandBuf* pReply = expandBufAlloc();
ProcessRequest(request, pReply);
ssize_t cc = netStateBase->WritePacket(pReply);
+
+ /*
+ * We processed this request and sent its reply. Notify other threads waiting for us they can now
+ * send events.
+ */
EndProcessingRequest();
if (cc != (ssize_t) expandBufGetLength(pReply)) {
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 362df8c..76aa734 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -27,7 +27,7 @@
#include "base/mutex.h"
#include "base/stl_util.h"
#include "base/stringpiece.h"
-#include "class_linker.h"
+#include "class_linker-inl.h"
#include "dex_file-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "interpreter/interpreter.h"
@@ -284,7 +284,7 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
if (sig[1] != '\0') {
SirtRef<mirror::ClassLoader> class_loader(soa.Self(), c->GetClassLoader());
- field_type = class_linker->FindClass(sig, class_loader);
+ field_type = class_linker->FindClass(soa.Self(), sig, class_loader);
} else {
field_type = class_linker->FindPrimitiveClass(*sig);
}
@@ -651,9 +651,9 @@
mirror::Class* c = nullptr;
if (runtime->IsStarted()) {
SirtRef<mirror::ClassLoader> class_loader(soa.Self(), GetClassLoader(soa));
- c = class_linker->FindClass(descriptor.c_str(), class_loader);
+ c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader);
} else {
- c = class_linker->FindSystemClass(descriptor.c_str());
+ c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str());
}
return soa.AddLocalReference<jclass>(c);
}
@@ -2140,13 +2140,8 @@
PrettyDescriptor(element_class).c_str());
return nullptr;
}
- std::string descriptor("[");
- descriptor += ClassHelper(element_class).GetDescriptor();
-
- // Find the class.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- SirtRef<mirror::ClassLoader> class_loader(soa.Self(), element_class->GetClassLoader());
- array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+ array_class = class_linker->FindArrayClass(soa.Self(), element_class);
if (UNLIKELY(array_class == nullptr)) {
return nullptr;
}
@@ -2509,8 +2504,7 @@
JniAbortF("NewDirectByteBuffer", "non-zero capacity for nullptr pointer: %" PRId64, capacity);
}
- // At the moment, the Java side is limited to 32 bits.
- CHECK_LE(reinterpret_cast<uintptr_t>(address), 0xffffffff);
+ // At the moment, the capacity is limited to 32 bits.
CHECK_LE(capacity, 0xffffffff);
jlong address_arg = reinterpret_cast<jlong>(address);
jint capacity_arg = static_cast<jint>(capacity);
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 4c58c84..63bc45c 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -107,7 +107,8 @@
class_name);
}
- mirror::Class* c = class_linker_->FindClass(DotToDescriptor(class_name).c_str(), class_loader);
+ mirror::Class* c = class_linker_->FindClass(self, DotToDescriptor(class_name).c_str(),
+ class_loader);
CHECK(c != NULL);
*method = is_static ? c->FindDirectMethod(method_name, method_signature)
@@ -127,7 +128,7 @@
mirror::Object* receiver;
JniInternalTestMakeExecutable(&method, &receiver, is_static, "nop", "()V");
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("V", 1);
JValue result;
if (!is_static) {
@@ -143,7 +144,7 @@
mirror::Object* receiver;
JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(B)B");
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("BB", 2);
uint32_t* args = arg_array.GetArray();
JValue result;
@@ -179,7 +180,7 @@
mirror::Object* receiver;
JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(I)I");
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("II", 2);
uint32_t* args = arg_array.GetArray();
JValue result;
@@ -215,7 +216,7 @@
mirror::Object* receiver;
JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(D)D");
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("DD", 2);
uint32_t* args = arg_array.GetArray();
JValue value;
JValue result;
@@ -259,7 +260,7 @@
mirror::Object* receiver;
JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(II)I");
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("III", 3);
uint32_t* args = arg_array.GetArray();
JValue result;
@@ -305,7 +306,7 @@
mirror::Object* receiver;
JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(III)I");
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("IIII", 4);
uint32_t* args = arg_array.GetArray();
JValue result;
@@ -361,7 +362,7 @@
mirror::Object* receiver;
JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIII)I");
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("IIIII", 5);
uint32_t* args = arg_array.GetArray();
JValue result;
@@ -422,7 +423,7 @@
mirror::Object* receiver;
JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIIII)I");
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("IIIIII", 6);
uint32_t* args = arg_array.GetArray();
JValue result;
@@ -488,7 +489,7 @@
mirror::Object* receiver;
JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DD)D");
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("DDD", 3);
uint32_t* args = arg_array.GetArray();
JValue value;
JValue value2;
@@ -559,7 +560,7 @@
mirror::Object* receiver;
JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDD)D");
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("DDDD", 4);
uint32_t* args = arg_array.GetArray();
JValue value;
JValue value2;
@@ -617,7 +618,7 @@
mirror::Object* receiver;
JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDD)D");
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("DDDDD", 5);
uint32_t* args = arg_array.GetArray();
JValue value;
JValue value2;
@@ -684,7 +685,7 @@
mirror::Object* receiver;
JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDDD)D");
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("DDDDDD", 6);
uint32_t* args = arg_array.GetArray();
JValue value;
JValue value2;
@@ -1778,13 +1779,13 @@
class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader));
CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V");
- mirror::Class* klass = class_linker_->FindClass("LMain;", class_loader);
+ mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
ASSERT_TRUE(klass != NULL);
mirror::ArtMethod* method = klass->FindDirectMethod("main", "([Ljava/lang/String;)V");
ASSERT_TRUE(method != NULL);
- ArgArray arg_array(NULL, 0);
+ ArgArray arg_array("VL", 2);
arg_array.Append(0U);
JValue result;
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 7c5de5e..90aaccd 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -27,10 +27,13 @@
namespace art {
namespace mirror {
+template<VerifyObjectFlags kVerifyFlags>
inline size_t Array::SizeOf() {
// This is safe from overflow because the array was already allocated, so we know it's sane.
- size_t component_size = GetClass()->GetComponentSize();
- int32_t component_count = GetLength();
+ size_t component_size = GetClass<kVerifyFlags>()->GetComponentSize();
+ // Don't need to check this since we already check this in GetClass.
+ int32_t component_count =
+ GetLength<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>();
size_t header_size = sizeof(Object) + (component_size == sizeof(int64_t) ? 8 : 4);
size_t data_size = component_count * component_size;
return header_size + data_size;
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index 2180857..715f072 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -18,6 +18,7 @@
#include "class.h"
#include "class-inl.h"
+#include "class_linker-inl.h"
#include "common_throws.h"
#include "dex_file-inl.h"
#include "gc/accounting/card_table-inl.h"
@@ -85,20 +86,22 @@
}
}
- // Generate the full name of the array class.
- std::string descriptor(num_dimensions, '[');
- descriptor += ClassHelper(element_class.get()).GetDescriptor();
-
// Find/generate the array class.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- SirtRef<mirror::ClassLoader> class_loader(self, element_class->GetClassLoader());
SirtRef<mirror::Class> array_class(self,
- class_linker->FindClass(descriptor.c_str(), class_loader));
+ class_linker->FindArrayClass(self, element_class.get()));
if (UNLIKELY(array_class.get() == nullptr)) {
CHECK(self->IsExceptionPending());
return nullptr;
}
- // create the array
+ for (int32_t i = 1; i < dimensions->GetLength(); ++i) {
+ array_class.reset(class_linker->FindArrayClass(self, array_class.get()));
+ if (UNLIKELY(array_class.get() == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ }
+ // Create the array.
Array* new_array = RecursiveCreateMultiArray(self, array_class, 0, dimensions);
if (UNLIKELY(new_array == nullptr)) {
CHECK(self->IsExceptionPending());
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 7555975..c4f9a75 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -53,17 +53,18 @@
const SirtRef<IntArray>& dimensions)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
size_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
int32_t GetLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetField32(OFFSET_OF_OBJECT_MEMBER(Array, length_), false);
+ return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Array, length_), false);
}
void SetLength(int32_t length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
CHECK_GE(length, 0);
// We use non transactional version since we can't undo this write. We also disable checking
// since it would fail during a transaction.
- SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Array, length_), length, false, false);
+ SetField32<false, false, kVerifyNone>(OFFSET_OF_OBJECT_MEMBER(Array, length_), length, false);
}
static MemberOffset LengthOffset() {
@@ -94,8 +95,10 @@
// Returns true if the index is valid. If not, throws an ArrayIndexOutOfBoundsException and
// returns false.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool CheckIsValidIndex(int32_t index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (UNLIKELY(static_cast<uint32_t>(index) >= static_cast<uint32_t>(GetLength()))) {
+ if (UNLIKELY(static_cast<uint32_t>(index) >=
+ static_cast<uint32_t>(GetLength<kVerifyFlags>()))) {
ThrowArrayIndexOutOfBoundsException(index);
return false;
}
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index d347724..d5eccaf 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -215,6 +215,13 @@
DCHECK(!result || IsRuntimeMethod());
return result;
}
+
+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, false);
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index d5f7597..fe27992 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -37,6 +37,10 @@
extern "C" void art_portable_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, char);
extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
const char*);
+#ifdef __x86_64__
+extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
+ const char*);
+#endif
// TODO: get global references for these
Class* ArtMethod::java_lang_reflect_ArtMethod_ = NULL;
@@ -276,7 +280,15 @@
: GetEntryPointFromPortableCompiledCode());
}
if (!IsPortableCompiled()) {
+#ifdef __x86_64__
+ if (!IsStatic()) {
+ (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
+ } else {
+ (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
+ }
+#else
(*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
+#endif
} else {
(*art_portable_invoke_stub)(this, args, args_size, self, result, shorty[0]);
}
@@ -348,10 +360,5 @@
RegisterNative(self, GetJniDlsymLookupStub(), false);
}
-void ArtMethod::SetNativeMethod(const void* native_method) {
- SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_),
- native_method, false);
-}
-
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index 71cc7af..e17dc5f 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -214,40 +214,48 @@
void Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
EntryPointFromInterpreter* GetEntryPointFromInterpreter() {
- return GetFieldPtr<EntryPointFromInterpreter*>(
+ return GetFieldPtr<EntryPointFromInterpreter*, kVerifyFlags>(
OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_), false);
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetEntryPointFromInterpreter(EntryPointFromInterpreter* entry_point_from_interpreter) {
- SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_),
- entry_point_from_interpreter, false);
+ SetFieldPtr<false, true, kVerifyFlags>(
+ OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_),
+ entry_point_from_interpreter, false);
}
static MemberOffset EntryPointFromPortableCompiledCodeOffset() {
return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_portable_compiled_code_));
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
const void* GetEntryPointFromPortableCompiledCode() {
- return GetFieldPtr<const void*>(EntryPointFromPortableCompiledCodeOffset(), false);
+ return GetFieldPtr<const void*, kVerifyFlags>(
+ EntryPointFromPortableCompiledCodeOffset(), false);
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetEntryPointFromPortableCompiledCode(const void* entry_point_from_portable_compiled_code) {
- SetFieldPtr<false>(EntryPointFromPortableCompiledCodeOffset(),
- entry_point_from_portable_compiled_code, false);
+ SetFieldPtr<false, true, kVerifyFlags>(
+ EntryPointFromPortableCompiledCodeOffset(), entry_point_from_portable_compiled_code, false);
}
static MemberOffset EntryPointFromQuickCompiledCodeOffset() {
return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_quick_compiled_code_));
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
const void* GetEntryPointFromQuickCompiledCode() {
- return GetFieldPtr<const void*>(EntryPointFromQuickCompiledCodeOffset(), false);
+ return GetFieldPtr<const void*, kVerifyFlags>(EntryPointFromQuickCompiledCodeOffset(), false);
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetEntryPointFromQuickCompiledCode(const void* entry_point_from_quick_compiled_code) {
- SetFieldPtr<false>(EntryPointFromQuickCompiledCodeOffset(),
- entry_point_from_quick_compiled_code, false);
+ SetFieldPtr<false, true, kVerifyFlags>(
+ EntryPointFromQuickCompiledCodeOffset(), entry_point_from_quick_compiled_code, false);
}
@@ -279,9 +287,10 @@
false);
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetMappingTable(const uint8_t* mapping_table) {
- SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_),
- mapping_table, false);
+ SetFieldPtr<false, true, kVerifyFlags>(
+ OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_), mapping_table, false);
}
uint32_t GetOatMappingTableOffset();
@@ -294,8 +303,10 @@
false);
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetVmapTable(const uint8_t* vmap_table) {
- SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_), vmap_table, false);
+ SetFieldPtr<false, true, kVerifyFlags>(
+ OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_), vmap_table, false);
}
uint32_t GetOatVmapTableOffset();
@@ -305,17 +316,19 @@
const uint8_t* GetNativeGcMap() {
return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), false);
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetNativeGcMap(const uint8_t* data) {
- SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data, false);
+ SetFieldPtr<false, true, kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data,
+ false);
}
// When building the oat need a convenient place to stuff the offset of the native GC map.
void SetOatNativeGcMapOffset(uint32_t gc_map_offset);
uint32_t GetOatNativeGcMapOffset();
- size_t GetFrameSizeInBytes() {
- DCHECK_EQ(sizeof(size_t), sizeof(uint32_t));
- size_t result = GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_frame_size_in_bytes_), false);
+ uint32_t GetFrameSizeInBytes() {
+ uint32_t result = GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_frame_size_in_bytes_),
+ false);
DCHECK_LE(static_cast<size_t>(kStackAlignment), result);
return result;
}
@@ -350,6 +363,7 @@
return reinterpret_cast<const void*>(GetField32(NativeMethodOffset(), false));
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetNativeMethod(const void*);
static MemberOffset GetMethodIndexOffset() {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index cba221d..e82c393 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -85,6 +85,7 @@
return (GetDirectMethods() != NULL) ? GetDirectMethods()->GetLength() : 0;
}
+template<VerifyObjectFlags kVerifyFlags>
inline ObjectArray<ArtMethod>* Class::GetVirtualMethods() {
DCHECK(IsLoaded() || IsErroneous());
return GetFieldObject<ObjectArray<ArtMethod> >(
@@ -103,8 +104,9 @@
return (GetVirtualMethods() != NULL) ? GetVirtualMethods()->GetLength() : 0;
}
+template<VerifyObjectFlags kVerifyFlags>
inline ArtMethod* Class::GetVirtualMethod(uint32_t i) {
- DCHECK(IsResolved() || IsErroneous());
+ DCHECK(IsResolved<kVerifyFlags>() || IsErroneous<kVerifyFlags>());
return GetVirtualMethods()->Get(i);
}
@@ -415,14 +417,16 @@
}
}
+template<VerifyObjectFlags kVerifyFlags>
inline uint32_t Class::GetAccessFlags() {
// Check class is loaded or this is java.lang.String that has a
// circularity issue during loading the names of its members
- DCHECK(IsLoaded() || IsErroneous() ||
+ DCHECK(IsLoaded<kVerifyFlags>() ||
+ IsErroneous<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>() ||
this == String::GetJavaLangString() ||
this == ArtField::GetJavaLangReflectArtField() ||
this == ArtMethod::GetJavaLangReflectArtMethod());
- return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), false);
+ return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), false);
}
inline String* Class::GetName() {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 3208de9..6dbb29d 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -72,7 +72,7 @@
<< PrettyClass(this) << " " << old_status << " -> " << new_status;
}
}
- if (new_status == kStatusError) {
+ if (UNLIKELY(new_status == kStatusError)) {
CHECK_NE(GetStatus(), kStatusError)
<< "Attempt to set as erroneous an already erroneous class " << PrettyClass(this);
@@ -95,7 +95,8 @@
// clear exception to call FindSystemClass
self->ClearException();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- Class* eiie_class = class_linker->FindSystemClass("Ljava/lang/ExceptionInInitializerError;");
+ Class* eiie_class = class_linker->FindSystemClass(self,
+ "Ljava/lang/ExceptionInInitializerError;");
CHECK(!self->IsExceptionPending());
// Only verification errors, not initialization problems, should set a verify error.
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index cd8504b..43db996 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -120,9 +120,11 @@
kStatusMax = 10,
};
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
Status GetStatus() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK_EQ(sizeof(Status), sizeof(uint32_t));
- return static_cast<Status>(GetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), true));
+ COMPILE_ASSERT(sizeof(Status) == sizeof(uint32_t), size_of_status_not_uint32);
+ return static_cast<Status>(GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, status_),
+ true));
}
void SetStatus(Status new_status, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -132,45 +134,54 @@
}
// Returns true if the class has failed to link.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsErroneous() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetStatus() == kStatusError;
+ return GetStatus<kVerifyFlags>() == kStatusError;
}
// Returns true if the class has been loaded.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsIdxLoaded() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetStatus() >= kStatusIdx;
+ return GetStatus<kVerifyFlags>() >= kStatusIdx;
}
// Returns true if the class has been loaded.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsLoaded() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetStatus() >= kStatusLoaded;
+ return GetStatus<kVerifyFlags>() >= kStatusLoaded;
}
// Returns true if the class has been linked.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsResolved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetStatus() >= kStatusResolved;
+ return GetStatus<kVerifyFlags>() >= kStatusResolved;
}
// Returns true if the class was compile-time verified.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsCompileTimeVerified() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetStatus() >= kStatusRetryVerificationAtRuntime;
+ return GetStatus<kVerifyFlags>() >= kStatusRetryVerificationAtRuntime;
}
// Returns true if the class has been verified.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsVerified() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetStatus() >= kStatusVerified;
+ return GetStatus<kVerifyFlags>() >= kStatusVerified;
}
// Returns true if the class is initializing.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsInitializing() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetStatus() >= kStatusInitializing;
+ return GetStatus<kVerifyFlags>() >= kStatusInitializing;
}
// Returns true if the class is initialized.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsInitialized() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetStatus() == kStatusInitialized;
+ return GetStatus<kVerifyFlags>() == kStatusInitialized;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -217,24 +228,29 @@
return (GetAccessFlags() & kAccSynthetic) != 0;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return (GetAccessFlags() & kAccClassIsReference) != 0;
+ return (GetAccessFlags<kVerifyFlags>() & kAccClassIsReference) != 0;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsWeakReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return (GetAccessFlags() & kAccClassIsWeakReference) != 0;
+ return (GetAccessFlags<kVerifyFlags>() & kAccClassIsWeakReference) != 0;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsSoftReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return (GetAccessFlags() & kAccReferenceFlagsMask) == kAccClassIsReference;
+ return (GetAccessFlags<kVerifyFlags>() & kAccReferenceFlagsMask) == kAccClassIsReference;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsFinalizerReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return (GetAccessFlags() & kAccClassIsFinalizerReference) != 0;
+ return (GetAccessFlags<kVerifyFlags>() & kAccClassIsFinalizerReference) != 0;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPhantomReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return (GetAccessFlags() & kAccClassIsPhantomReference) != 0;
+ return (GetAccessFlags<kVerifyFlags>() & kAccClassIsPhantomReference) != 0;
}
// Can references of this type be assigned to by things of another type? For non-array types
@@ -260,7 +276,7 @@
// Computes the name, then sets the cached value.
String* ComputeName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsProxyClass() {
+ bool IsProxyClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Read access flags without using getter as whether something is a proxy can be check in
// any loaded state
// TODO: switch to a check if the super class is java.lang.reflect.Proxy?
@@ -268,10 +284,11 @@
return (access_flags & kAccClassIsProxy) != 0;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
Primitive::Type GetPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK_EQ(sizeof(Primitive::Type), sizeof(int32_t));
return static_cast<Primitive::Type>(
- GetField32(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), false));
+ GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), false));
}
void SetPrimitiveType(Primitive::Type new_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -280,48 +297,61 @@
}
// Returns true if the class is a primitive type.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPrimitive() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetPrimitiveType() != Primitive::kPrimNot;
+ return GetPrimitiveType<kVerifyFlags>() != Primitive::kPrimNot;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPrimitiveBoolean() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetPrimitiveType() == Primitive::kPrimBoolean;
+ return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimBoolean;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPrimitiveByte() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetPrimitiveType() == Primitive::kPrimByte;
+ return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimByte;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPrimitiveChar() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetPrimitiveType() == Primitive::kPrimChar;
+ return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimChar;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPrimitiveShort() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetPrimitiveType() == Primitive::kPrimShort;
+ return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimShort;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPrimitiveInt() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return GetPrimitiveType() == Primitive::kPrimInt;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPrimitiveLong() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetPrimitiveType() == Primitive::kPrimLong;
+ return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimLong;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPrimitiveFloat() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetPrimitiveType() == Primitive::kPrimFloat;
+ return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimFloat;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPrimitiveDouble() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetPrimitiveType() == Primitive::kPrimDouble;
+ return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimDouble;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPrimitiveVoid() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetPrimitiveType() == Primitive::kPrimVoid;
+ return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimVoid;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPrimitiveArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return IsArrayClass() && GetComponentType()->IsPrimitive();
+ return IsArrayClass<kVerifyFlags>() &&
+ GetComponentType<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>()->
+ IsPrimitive();
}
// Depth of class from java.lang.Object
@@ -333,8 +363,9 @@
return depth;
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetComponentType() != NULL;
+ return GetComponentType<kVerifyFlags>() != NULL;
}
bool IsClassClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -351,8 +382,9 @@
return OFFSET_OF_OBJECT_MEMBER(Class, component_type_);
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
Class* GetComponentType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetFieldObject<Class>(ComponentTypeOffset(), false);
+ return GetFieldObject<Class, kVerifyFlags>(ComponentTypeOffset(), false);
}
void SetComponentType(Class* new_component_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -376,8 +408,9 @@
return (!IsPrimitive() && !IsInterface() && !IsAbstract()) || ((IsAbstract()) && IsArrayClass());
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsObjectArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetComponentType() != NULL && !GetComponentType()->IsPrimitive();
+ return GetComponentType<kVerifyFlags>() != nullptr && !GetComponentType<kVerifyFlags>()->IsPrimitive();
}
// Creates a raw object instance but does not invoke the default constructor.
@@ -396,12 +429,14 @@
return IsClassClass() || IsArrayClass();
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
uint32_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), false);
+ return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), false);
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
uint32_t GetClassSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), false);
+ return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), false);
}
void SetClassSize(uint32_t new_class_size)
@@ -549,6 +584,7 @@
// Returns the number of static, private, and constructor methods.
uint32_t NumDirectMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ObjectArray<ArtMethod>* GetVirtualMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetVirtualMethods(ObjectArray<ArtMethod>* new_virtual_methods)
@@ -557,6 +593,7 @@
// Returns the number of non-inherited virtual methods.
uint32_t NumVirtualMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ArtMethod* GetVirtualMethod(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ArtMethod* GetVirtualMethodDuringLinking(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -683,9 +720,11 @@
false);
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
uint32_t GetReferenceInstanceOffsets() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(IsResolved() || IsErroneous());
- return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_), false);
+ DCHECK(IsResolved<kVerifyFlags>() || IsErroneous<kVerifyFlags>());
+ return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_),
+ false);
}
void SetReferenceInstanceOffsets(uint32_t new_reference_offsets)
@@ -725,8 +764,10 @@
// TODO: uint16_t
void SetStaticField(uint32_t i, ArtField* f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
uint32_t GetReferenceStaticOffsets() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_), false);
+ return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_),
+ false);
}
void SetReferenceStaticOffsets(uint32_t new_reference_offsets)
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 70291c1..df8104d 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -32,18 +32,21 @@
namespace art {
namespace mirror {
+template<VerifyObjectFlags kVerifyFlags>
inline Class* Object::GetClass() {
- return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(Object, klass_), false);
+ return GetFieldObject<Class, kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Object, klass_), false);
}
+template<VerifyObjectFlags kVerifyFlags>
inline void Object::SetClass(Class* new_klass) {
// new_klass may be NULL prior to class linker initialization.
// We don't mark the card as this occurs as part of object allocation. Not all objects have
// backing cards, such as large objects.
// We use non transactional version since we can't undo this write. We also disable checking as
// we may run in transaction mode here.
- SetFieldObjectWithoutWriteBarrier<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, klass_),
- new_klass, false, false);
+ SetFieldObjectWithoutWriteBarrier<false, false,
+ static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>(
+ OFFSET_OF_OBJECT_MEMBER(Object, klass_), new_klass, false);
}
inline LockWord Object::GetLockWord() {
@@ -89,176 +92,222 @@
Monitor::Wait(self, this, ms, ns, true, kTimedWaiting);
}
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::VerifierInstanceOf(Class* klass) {
DCHECK(klass != NULL);
- DCHECK(GetClass() != NULL);
+ DCHECK(GetClass<kVerifyFlags>() != NULL);
return klass->IsInterface() || InstanceOf(klass);
}
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::InstanceOf(Class* klass) {
DCHECK(klass != NULL);
- DCHECK(GetClass() != NULL);
- return klass->IsAssignableFrom(GetClass());
+ DCHECK(GetClass<kVerifyNone>() != NULL);
+ return klass->IsAssignableFrom(GetClass<kVerifyFlags>());
}
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsClass() {
- Class* java_lang_Class = GetClass()->GetClass();
- return GetClass() == java_lang_Class;
+ Class* java_lang_Class = GetClass<kVerifyFlags>()->GetClass();
+ return GetClass<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>() ==
+ java_lang_Class;
}
+template<VerifyObjectFlags kVerifyFlags>
inline Class* Object::AsClass() {
- DCHECK(IsClass());
+ DCHECK(IsClass<kVerifyFlags>());
return down_cast<Class*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsObjectArray() {
- return IsArrayInstance() && !GetClass()->GetComponentType()->IsPrimitive();
+ constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+ return IsArrayInstance<kVerifyFlags>() &&
+ !GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitive();
}
-template<class T>
+template<class T, VerifyObjectFlags kVerifyFlags>
inline ObjectArray<T>* Object::AsObjectArray() {
- DCHECK(IsObjectArray());
+ DCHECK(IsObjectArray<kVerifyFlags>());
return down_cast<ObjectArray<T>*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsArrayInstance() {
- return GetClass()->IsArrayClass();
+ return GetClass<kVerifyFlags>()->IsArrayClass();
}
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsArtField() {
- return GetClass()->IsArtFieldClass();
+ return GetClass<kVerifyFlags>()->IsArtFieldClass();
}
-inline ArtField* Object::AsArtField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(IsArtField());
+template<VerifyObjectFlags kVerifyFlags>
+inline ArtField* Object::AsArtField() {
+ DCHECK(IsArtField<kVerifyFlags>());
return down_cast<ArtField*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsArtMethod() {
- return GetClass()->IsArtMethodClass();
+ return GetClass<kVerifyFlags>()->IsArtMethodClass();
}
+template<VerifyObjectFlags kVerifyFlags>
inline ArtMethod* Object::AsArtMethod() {
- DCHECK(IsArtMethod());
+ DCHECK(IsArtMethod<kVerifyFlags>());
return down_cast<ArtMethod*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsReferenceInstance() {
- return GetClass()->IsReferenceClass();
+ return GetClass<kVerifyFlags>()->IsReferenceClass();
}
+template<VerifyObjectFlags kVerifyFlags>
inline Array* Object::AsArray() {
- DCHECK(IsArrayInstance());
+ DCHECK(IsArrayInstance<kVerifyFlags>());
return down_cast<Array*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline BooleanArray* Object::AsBooleanArray() {
- DCHECK(GetClass()->IsArrayClass());
- DCHECK(GetClass()->GetComponentType()->IsPrimitiveBoolean());
+ constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+ DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+ DCHECK(GetClass<kNewFlags>()->GetComponentType()->IsPrimitiveBoolean());
return down_cast<BooleanArray*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline ByteArray* Object::AsByteArray() {
- DCHECK(GetClass()->IsArrayClass());
- DCHECK(GetClass()->GetComponentType()->IsPrimitiveByte());
+ static const VerifyObjectFlags kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+ DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+ DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveByte());
return down_cast<ByteArray*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline ByteArray* Object::AsByteSizedArray() {
- DCHECK(GetClass()->IsArrayClass());
- DCHECK(GetClass()->GetComponentType()->IsPrimitiveByte() ||
- GetClass()->GetComponentType()->IsPrimitiveBoolean());
+ constexpr VerifyObjectFlags kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+ DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+ DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveByte() ||
+ GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveBoolean());
return down_cast<ByteArray*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline CharArray* Object::AsCharArray() {
- DCHECK(GetClass()->IsArrayClass());
- DCHECK(GetClass()->GetComponentType()->IsPrimitiveChar());
+ constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+ DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+ DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveChar());
return down_cast<CharArray*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline ShortArray* Object::AsShortArray() {
- DCHECK(GetClass()->IsArrayClass());
- DCHECK(GetClass()->GetComponentType()->IsPrimitiveShort());
+ constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+ DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+ DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveShort());
return down_cast<ShortArray*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline ShortArray* Object::AsShortSizedArray() {
- DCHECK(GetClass()->IsArrayClass());
- DCHECK(GetClass()->GetComponentType()->IsPrimitiveShort() ||
- GetClass()->GetComponentType()->IsPrimitiveChar());
+ constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+ DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+ DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveShort() ||
+ GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveChar());
return down_cast<ShortArray*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline IntArray* Object::AsIntArray() {
- DCHECK(GetClass()->IsArrayClass());
- DCHECK(GetClass()->GetComponentType()->IsPrimitiveInt() ||
- GetClass()->GetComponentType()->IsPrimitiveFloat());
+ constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+ DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+ DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveInt() ||
+ GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveFloat());
return down_cast<IntArray*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline LongArray* Object::AsLongArray() {
- DCHECK(GetClass()->IsArrayClass());
- DCHECK(GetClass()->GetComponentType()->IsPrimitiveLong() ||
- GetClass()->GetComponentType()->IsPrimitiveDouble());
+ constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+ DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+ DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveLong() ||
+ GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveDouble());
return down_cast<LongArray*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline FloatArray* Object::AsFloatArray() {
- DCHECK(GetClass()->IsArrayClass());
- DCHECK(GetClass()->GetComponentType()->IsPrimitiveFloat());
+ constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+ DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+ DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveFloat());
return down_cast<FloatArray*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline DoubleArray* Object::AsDoubleArray() {
- DCHECK(GetClass()->IsArrayClass());
- DCHECK(GetClass()->GetComponentType()->IsPrimitiveDouble());
+ constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+ DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
+ DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveDouble());
return down_cast<DoubleArray*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline String* Object::AsString() {
- DCHECK(GetClass()->IsStringClass());
+ DCHECK(GetClass<kVerifyFlags>()->IsStringClass());
return down_cast<String*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline Throwable* Object::AsThrowable() {
- DCHECK(GetClass()->IsThrowableClass());
+ DCHECK(GetClass<kVerifyFlags>()->IsThrowableClass());
return down_cast<Throwable*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsWeakReferenceInstance() {
- return GetClass()->IsWeakReferenceClass();
+ return GetClass<kVerifyFlags>()->IsWeakReferenceClass();
}
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsSoftReferenceInstance() {
- return GetClass()->IsSoftReferenceClass();
+ return GetClass<kVerifyFlags>()->IsSoftReferenceClass();
}
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsFinalizerReferenceInstance() {
- return GetClass()->IsFinalizerReferenceClass();
+ return GetClass<kVerifyFlags>()->IsFinalizerReferenceClass();
}
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsPhantomReferenceInstance() {
- return GetClass()->IsPhantomReferenceClass();
+ return GetClass<kVerifyFlags>()->IsPhantomReferenceClass();
}
+template<VerifyObjectFlags kVerifyFlags>
inline size_t Object::SizeOf() {
size_t result;
- if (IsArrayInstance()) {
- result = AsArray()->SizeOf();
- } else if (IsClass()) {
- result = AsClass()->SizeOf();
+ constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
+ if (IsArrayInstance<kVerifyFlags>()) {
+ result = AsArray<kNewFlags>()->SizeOf<>();
+ } else if (IsClass<kNewFlags>()) {
+ result = AsClass<kNewFlags>()->SizeOf<kNewFlags>();
} else {
- result = GetClass()->GetObjectSize();
+ result = GetClass<kNewFlags>()->GetObjectSize();
}
- DCHECK_GE(result, sizeof(Object)) << " class=" << PrettyTypeOf(GetClass());
- DCHECK(!IsArtField() || result == sizeof(ArtField));
- DCHECK(!IsArtMethod() || result == sizeof(ArtMethod));
+ DCHECK_GE(result, sizeof(Object)) << " class=" << PrettyTypeOf(GetClass<kNewFlags>());
+ DCHECK(!IsArtField<kNewFlags>() || result == sizeof(ArtField));
+ DCHECK(!IsArtMethod<kNewFlags>() || result == sizeof(ArtMethod));
return result;
}
+template<VerifyObjectFlags kVerifyFlags>
inline int32_t Object::GetField32(MemberOffset field_offset, bool is_volatile) {
- VerifyObject(this);
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
const int32_t* word_addr = reinterpret_cast<const int32_t*>(raw_addr);
if (UNLIKELY(is_volatile)) {
@@ -270,9 +319,8 @@
}
}
-template<bool kTransactionActive, bool kCheckTransaction>
-inline void Object::SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile,
- bool this_is_valid) {
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile) {
if (kCheckTransaction) {
DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
}
@@ -280,7 +328,7 @@
Runtime::Current()->RecordWriteField32(this, field_offset, GetField32(field_offset, is_volatile),
is_volatile);
}
- if (this_is_valid) {
+ if (kVerifyFlags & kVerifyThis) {
VerifyObject(this);
}
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
@@ -294,7 +342,7 @@
}
}
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
inline bool Object::CasField32(MemberOffset field_offset, int32_t old_value, int32_t new_value) {
if (kCheckTransaction) {
DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
@@ -302,14 +350,19 @@
if (kTransactionActive) {
Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true);
}
- VerifyObject(this);
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw_addr);
return __sync_bool_compare_and_swap(addr, old_value, new_value);
}
+template<VerifyObjectFlags kVerifyFlags>
inline int64_t Object::GetField64(MemberOffset field_offset, bool is_volatile) {
- VerifyObject(this);
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
const int64_t* addr = reinterpret_cast<const int64_t*>(raw_addr);
if (UNLIKELY(is_volatile)) {
@@ -321,9 +374,8 @@
}
}
-template<bool kTransactionActive, bool kCheckTransaction>
-inline void Object::SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile,
- bool this_is_valid) {
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile) {
if (kCheckTransaction) {
DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
}
@@ -331,7 +383,7 @@
Runtime::Current()->RecordWriteField64(this, field_offset, GetField64(field_offset, is_volatile),
is_volatile);
}
- if (this_is_valid) {
+ if (kVerifyFlags & kVerifyThis) {
VerifyObject(this);
}
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
@@ -349,7 +401,7 @@
}
}
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
inline bool Object::CasField64(MemberOffset field_offset, int64_t old_value, int64_t new_value) {
if (kCheckTransaction) {
DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
@@ -357,15 +409,19 @@
if (kTransactionActive) {
Runtime::Current()->RecordWriteField64(this, field_offset, old_value, true);
}
- VerifyObject(this);
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
volatile int64_t* addr = reinterpret_cast<volatile int64_t*>(raw_addr);
return QuasiAtomic::Cas64(old_value, new_value, addr);
}
-template<class T>
+template<class T, VerifyObjectFlags kVerifyFlags>
inline T* Object::GetFieldObject(MemberOffset field_offset, bool is_volatile) {
- VerifyObject(this);
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
HeapReference<T>* objref_addr = reinterpret_cast<HeapReference<T>*>(raw_addr);
HeapReference<T> objref = *objref_addr;
@@ -374,13 +430,15 @@
QuasiAtomic::MembarLoadLoad(); // Ensure loads don't re-order.
}
T* result = objref.AsMirrorPtr();
- VerifyObject(result);
+ if (kVerifyFlags & kVerifyReads) {
+ VerifyObject(result);
+ }
return result;
}
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value,
- bool is_volatile, bool this_is_valid) {
+ bool is_volatile) {
if (kCheckTransaction) {
DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
}
@@ -389,10 +447,12 @@
GetFieldObject<Object>(field_offset, is_volatile),
true);
}
- if (this_is_valid) {
+ if (kVerifyFlags & kVerifyThis) {
VerifyObject(this);
}
- VerifyObject(new_value);
+ if (kVerifyFlags & kVerifyWrites) {
+ VerifyObject(new_value);
+ }
HeapReference<Object> objref(HeapReference<Object>::FromMirrorPtr(new_value));
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
HeapReference<Object>* objref_addr = reinterpret_cast<HeapReference<Object>*>(raw_addr);
@@ -405,27 +465,43 @@
}
}
-template<bool kTransactionActive, bool kCheckTransaction>
-inline void Object::SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile,
- bool this_is_valid) {
- SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction>(field_offset, new_value,
- is_volatile,
- this_is_valid);
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile) {
+ SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+ field_offset, new_value, is_volatile);
if (new_value != nullptr) {
CheckFieldAssignment(field_offset, new_value);
Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value);
}
}
-template<bool kTransactionActive, bool kCheckTransaction>
-inline bool Object::CasFieldObject(MemberOffset field_offset, Object* old_value, Object* new_value) {
+template <VerifyObjectFlags kVerifyFlags>
+inline HeapReference<Object>* Object::GetFieldObjectReferenceAddr(MemberOffset field_offset) {
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ return reinterpret_cast<HeapReference<Object>*>(reinterpret_cast<byte*>(this) +
+ field_offset.Int32Value());
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldObject(MemberOffset field_offset, Object* old_value,
+ Object* new_value) {
if (kCheckTransaction) {
DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
}
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ if (kVerifyFlags & kVerifyWrites) {
+ VerifyObject(new_value);
+ }
+ if (kVerifyFlags & kVerifyReads) {
+ VerifyObject(old_value);
+ }
if (kTransactionActive) {
Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
}
- VerifyObject(this);
byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw_addr);
HeapReference<Object> old_ref(HeapReference<Object>::FromMirrorPtr(old_value));
@@ -437,12 +513,6 @@
return success;
}
-inline void Object::VerifyObject(Object* obj) {
- if (kIsDebugBuild) {
- Runtime::Current()->GetHeap()->VerifyObject(obj);
- }
-}
-
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index 1251852..f1485e5 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -141,10 +141,9 @@
void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, Object* new_value) {
Class* c = GetClass();
- if (Runtime::Current()->GetClassLinker() == NULL ||
- !Runtime::Current()->IsStarted() ||
- !Runtime::Current()->GetHeap()->IsObjectValidationEnabled() ||
- !c->IsResolved()) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime->GetClassLinker() == nullptr || !runtime->IsStarted() ||
+ !runtime->GetHeap()->IsObjectValidationEnabled() || !c->IsResolved()) {
return;
}
for (Class* cur = c; cur != NULL; cur = cur->GetSuperClass()) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index eb118c7..7487dd2 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -24,6 +24,7 @@
#include "object_reference.h"
#include "offsets.h"
#include "runtime.h"
+#include "verify_object.h"
namespace art {
@@ -59,7 +60,8 @@
#define OFFSET_OF_OBJECT_MEMBER(type, field) \
MemberOffset(OFFSETOF_MEMBER(type, field))
-constexpr bool kCheckFieldAssignments = false;
+// Checks that we don't do field assignments which violate the typing system.
+static constexpr bool kCheckFieldAssignments = false;
// C++ mirror of java.lang.Object
class MANAGED Object {
@@ -68,16 +70,20 @@
return OFFSET_OF_OBJECT_MEMBER(Object, klass_);
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
Class* GetClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetClass(Class* new_klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// The verifier treats all interfaces as java.lang.Object and relies on runtime checks in
// invoke-interface to detect incompatible interface types.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool VerifierInstanceOf(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool InstanceOf(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
size_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Object* Clone(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -95,135 +101,147 @@
mirror::Object* MonitorEnter(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCK_FUNCTION(monitor_lock_);
-
bool MonitorExit(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
UNLOCK_FUNCTION(monitor_lock_);
-
void Notify(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
void NotifyAll(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
void Wait(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
void Wait(Thread* self, int64_t timeout, int32_t nanos) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
Class* AsClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsObjectArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- template<class T>
+ template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ObjectArray<T>* AsObjectArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsArrayInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
Array* AsArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
BooleanArray* AsBooleanArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ByteArray* AsByteArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ByteArray* AsByteSizedArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
CharArray* AsCharArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ShortArray* AsShortArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ShortArray* AsShortSizedArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
IntArray* AsIntArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
LongArray* AsLongArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
FloatArray* AsFloatArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
DoubleArray* AsDoubleArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
String* AsString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
Throwable* AsThrowable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsArtMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ArtMethod* AsArtMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsArtField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ArtField* AsArtField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsReferenceInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsWeakReferenceInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsSoftReferenceInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsFinalizerReferenceInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPhantomReferenceInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Accessor for Java type fields.
- template<class T> T* GetFieldObject(MemberOffset field_offset, bool is_volatile)
+ template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ T* GetFieldObject(MemberOffset field_offset, bool is_volatile)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<bool kTransactionActive, bool kCheckTransaction = true>
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value,
- bool is_volatile, bool this_is_valid = true)
+ bool is_volatile)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<bool kTransactionActive, bool kCheckTransaction = true>
- void SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile,
- bool this_is_valid = true)
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<bool kTransactionActive, bool kCheckTransaction = true>
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool CasFieldObject(MemberOffset field_offset, Object* old_value, Object* new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- HeapReference<Object>* GetFieldObjectReferenceAddr(MemberOffset field_offset) ALWAYS_INLINE {
- VerifyObject(this);
- return reinterpret_cast<HeapReference<Object>*>(reinterpret_cast<byte*>(this) +
- field_offset.Int32Value());
- }
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ HeapReference<Object>* GetFieldObjectReferenceAddr(MemberOffset field_offset);
- int32_t GetField32(MemberOffset field_offset, bool is_volatile);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ int32_t GetField32(MemberOffset field_offset, bool is_volatile)
+ NO_THREAD_SAFETY_ANALYSIS;
- template<bool kTransactionActive, bool kCheckTransaction = true>
- void SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile,
- bool this_is_valid = true);
-
- template<bool kTransactionActive, bool kCheckTransaction = true>
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile);
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool CasField32(MemberOffset field_offset, int32_t old_value, int32_t new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
int64_t GetField64(MemberOffset field_offset, bool is_volatile);
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ void SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile);
- template<bool kTransactionActive, bool kCheckTransaction = true>
- void SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile,
- bool this_is_valid = true);
-
- template<bool kTransactionActive, bool kCheckTransaction = true>
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool CasField64(MemberOffset field_offset, int64_t old_value, int64_t new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<bool kTransactionActive, bool kCheckTransaction = true, typename T>
- void SetFieldPtr(MemberOffset field_offset, T new_value, bool is_volatile,
- bool this_is_valid = true) {
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename T>
+ void SetFieldPtr(MemberOffset field_offset, T new_value, bool is_volatile) {
#ifndef __LP64__
- SetField32<kTransactionActive, kCheckTransaction>(field_offset,
- reinterpret_cast<int32_t>(new_value),
- is_volatile, this_is_valid);
+ SetField32<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+ field_offset, reinterpret_cast<int32_t>(new_value), is_volatile);
#else
- SetField64<kTransactionActive, kCheckTransaction>(field_offset,
- reinterpret_cast<int64_t>(new_value),
- is_volatile, this_is_valid);
+ SetField64<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+ field_offset, reinterpret_cast<int64_t>(new_value), is_volatile);
#endif
}
protected:
// Accessors for non-Java type fields
- template<class T>
- T GetFieldPtr(MemberOffset field_offset, bool is_volatile) {
+ template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ T GetFieldPtr(MemberOffset field_offset, bool is_volatile) NO_THREAD_SAFETY_ANALYSIS {
#ifndef __LP64__
- return reinterpret_cast<T>(GetField32(field_offset, is_volatile));
+ return reinterpret_cast<T>(GetField32<kVerifyFlags>(field_offset, is_volatile));
#else
- return reinterpret_cast<T>(GetField64(field_offset, is_volatile));
+ return reinterpret_cast<T>(GetField64<kVerifyFlags>(field_offset, is_volatile));
#endif
}
private:
- static void VerifyObject(Object* obj) ALWAYS_INLINE;
// Verify the type correctness of stores to fields.
void CheckFieldAssignmentImpl(MemberOffset field_offset, Object* new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index 521b6ce..a427957 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -58,10 +58,10 @@
return GetFieldObject<T>(OffsetOfElement(i), false);
}
-template<class T>
+template<class T> template<VerifyObjectFlags kVerifyFlags>
inline bool ObjectArray<T>::CheckAssignable(T* object) {
if (object != NULL) {
- Class* element_class = GetClass()->GetComponentType();
+ Class* element_class = GetClass<kVerifyFlags>()->GetComponentType();
if (UNLIKELY(!object->InstanceOf(element_class))) {
ThrowArrayStoreException(object);
return false;
@@ -80,31 +80,33 @@
}
template<class T>
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
inline void ObjectArray<T>::Set(int32_t i, T* object) {
- if (LIKELY(CheckIsValidIndex(i) && CheckAssignable(object))) {
- SetFieldObject<kTransactionActive, kCheckTransaction>(OffsetOfElement(i), object, false);
+ if (LIKELY(CheckIsValidIndex(i) && CheckAssignable<kVerifyFlags>(object))) {
+ SetFieldObject<kTransactionActive, kCheckTransaction, kVerifyFlags>(OffsetOfElement(i), object,
+ false);
} else {
DCHECK(Thread::Current()->IsExceptionPending());
}
}
template<class T>
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
inline void ObjectArray<T>::SetWithoutChecks(int32_t i, T* object) {
- DCHECK(CheckIsValidIndex(i));
- DCHECK(CheckAssignable(object));
- SetFieldObject<kTransactionActive, kCheckTransaction>(OffsetOfElement(i), object, false);
+ DCHECK(CheckIsValidIndex<kVerifyFlags>(i));
+ DCHECK(CheckAssignable<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>(object));
+ SetFieldObject<kTransactionActive, kCheckTransaction, kVerifyFlags>(OffsetOfElement(i), object,
+ false);
}
template<class T>
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
inline void ObjectArray<T>::SetWithoutChecksAndWriteBarrier(int32_t i, T* object) {
- DCHECK(CheckIsValidIndex(i));
+ DCHECK(CheckIsValidIndex<kVerifyFlags>(i));
// TODO: enable this check. It fails when writing the image in ImageWriter::FixupObjectArray.
// DCHECK(CheckAssignable(object));
- SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction>(OffsetOfElement(i),
- object, false);
+ SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction, kVerifyFlags>(
+ OffsetOfElement(i), object, false);
}
template<class T>
diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h
index 668b276..7f9e716 100644
--- a/runtime/mirror/object_array.h
+++ b/runtime/mirror/object_array.h
@@ -37,22 +37,27 @@
// Returns true if the object can be stored into the array. If not, throws
// an ArrayStoreException and returns false.
- bool CheckAssignable(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // TODO fix thread safety analysis: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool CheckAssignable(T* object) NO_THREAD_SAFETY_ANALYSIS;
void Set(int32_t i, T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// TODO fix thread safety analysis: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
- template<bool kTransactionActive, bool kCheckTransaction = true>
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void Set(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
// Set element without bound and element type checks, to be used in limited
// circumstances, such as during boot image writing.
// TODO fix thread safety analysis broken by the use of template. This should be
// SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
- template<bool kTransactionActive, bool kCheckTransaction = true>
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetWithoutChecks(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
// TODO fix thread safety analysis broken by the use of template. This should be
// SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
- template<bool kTransactionActive, bool kCheckTransaction = true>
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetWithoutChecksAndWriteBarrier(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS;
T* GetWithoutChecks(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 40c3748..34fb15e 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -123,7 +123,8 @@
EXPECT_TRUE(oa->Get(0) == oa.get());
EXPECT_TRUE(oa->Get(1) == oa.get());
- Class* aioobe = class_linker_->FindSystemClass("Ljava/lang/ArrayIndexOutOfBoundsException;");
+ Class* aioobe = class_linker_->FindSystemClass(soa.Self(),
+ "Ljava/lang/ArrayIndexOutOfBoundsException;");
EXPECT_TRUE(oa->Get(-1) == NULL);
EXPECT_TRUE(soa.Self()->IsExceptionPending());
@@ -138,21 +139,23 @@
ASSERT_TRUE(oa->GetClass() != NULL);
ClassHelper oa_ch(oa->GetClass());
ASSERT_EQ(2U, oa_ch.NumDirectInterfaces());
- EXPECT_EQ(class_linker_->FindSystemClass("Ljava/lang/Cloneable;"), oa_ch.GetDirectInterface(0));
- EXPECT_EQ(class_linker_->FindSystemClass("Ljava/io/Serializable;"), oa_ch.GetDirectInterface(1));
+ EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Cloneable;"),
+ oa_ch.GetDirectInterface(0));
+ EXPECT_EQ(class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;"),
+ oa_ch.GetDirectInterface(1));
}
TEST_F(ObjectTest, AllocArray) {
ScopedObjectAccess soa(Thread::Current());
- Class* c = class_linker_->FindSystemClass("[I");
+ Class* c = class_linker_->FindSystemClass(soa.Self(), "[I");
SirtRef<Array> a(soa.Self(), Array::Alloc<true>(soa.Self(), c, 1));
ASSERT_TRUE(c == a->GetClass());
- c = class_linker_->FindSystemClass("[Ljava/lang/Object;");
+ c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
a.reset(Array::Alloc<true>(soa.Self(), c, 1));
ASSERT_TRUE(c == a->GetClass());
- c = class_linker_->FindSystemClass("[[Ljava/lang/Object;");
+ c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;");
a.reset(Array::Alloc<true>(soa.Self(), c, 1));
ASSERT_TRUE(c == a->GetClass());
}
@@ -173,7 +176,7 @@
EXPECT_EQ(T(123), a->Get(0));
EXPECT_EQ(T(321), a->Get(1));
- Class* aioobe = cl->FindSystemClass("Ljava/lang/ArrayIndexOutOfBoundsException;");
+ Class* aioobe = cl->FindSystemClass(soa.Self(), "Ljava/lang/ArrayIndexOutOfBoundsException;");
EXPECT_EQ(0, a->Get(-1));
EXPECT_TRUE(soa.Self()->IsExceptionPending());
@@ -214,7 +217,7 @@
TEST_F(ObjectTest, CheckAndAllocArrayFromCode) {
// pretend we are trying to call 'new char[3]' from String.toCharArray
ScopedObjectAccess soa(Thread::Current());
- Class* java_util_Arrays = class_linker_->FindSystemClass("Ljava/util/Arrays;");
+ Class* java_util_Arrays = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/Arrays;");
ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V");
const DexFile::StringId* string_id = java_lang_dex_file_->FindStringId("[I");
ASSERT_TRUE(string_id != NULL);
@@ -233,11 +236,11 @@
TEST_F(ObjectTest, CreateMultiArray) {
ScopedObjectAccess soa(Thread::Current());
- SirtRef<Class> c(soa.Self(), class_linker_->FindSystemClass("I"));
+ SirtRef<Class> c(soa.Self(), class_linker_->FindSystemClass(soa.Self(), "I"));
SirtRef<IntArray> dims(soa.Self(), IntArray::Alloc(soa.Self(), 1));
dims->Set<false>(0, 1);
Array* multi = Array::CreateMultiArray(soa.Self(), c, dims);
- EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[I"));
+ EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[I"));
EXPECT_EQ(1, multi->GetLength());
dims->Set<false>(0, -1);
@@ -253,11 +256,11 @@
dims->Set<false>(0, i);
dims->Set<false>(1, j);
multi = Array::CreateMultiArray(soa.Self(), c, dims);
- EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[[I"));
+ EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[[I"));
EXPECT_EQ(i, multi->GetLength());
for (int k = 0; k < i; ++k) {
Array* outer = multi->AsObjectArray<Array>()->Get(k);
- EXPECT_TRUE(outer->GetClass() == class_linker_->FindSystemClass("[I"));
+ EXPECT_TRUE(outer->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[I"));
EXPECT_EQ(j, outer->GetLength());
}
}
@@ -272,8 +275,7 @@
CHECK(dex_file != NULL);
SirtRef<mirror::ClassLoader> loader(soa.Self(), soa.Decode<ClassLoader*>(class_loader));
- Class* klass =
- class_linker_->FindClass("LStaticsFromCode;", loader);
+ Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", loader);
ArtMethod* clinit = klass->FindClassInitializer();
const DexFile::StringId* klass_string_id = dex_file->FindStringId("LStaticsFromCode;");
ASSERT_TRUE(klass_string_id != NULL);
@@ -404,9 +406,9 @@
SirtRef<ClassLoader> class_loader_1(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader_1));
SirtRef<ClassLoader> class_loader_2(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader_2));
- Class* klass1 = linker->FindClass("LProtoCompare;", class_loader_1);
+ Class* klass1 = linker->FindClass(soa.Self(), "LProtoCompare;", class_loader_1);
ASSERT_TRUE(klass1 != NULL);
- Class* klass2 = linker->FindClass("LProtoCompare2;", class_loader_2);
+ Class* klass2 = linker->FindClass(soa.Self(), "LProtoCompare2;", class_loader_2);
ASSERT_TRUE(klass2 != NULL);
ArtMethod* m1_1 = klass1->GetVirtualMethod(0);
@@ -472,8 +474,8 @@
jobject jclass_loader = LoadDex("XandY");
SirtRef<ClassLoader> class_loader(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader));
- Class* X = class_linker_->FindClass("LX;", class_loader);
- Class* Y = class_linker_->FindClass("LY;", class_loader);
+ Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader);
+ Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader);
ASSERT_TRUE(X != NULL);
ASSERT_TRUE(Y != NULL);
@@ -487,16 +489,16 @@
EXPECT_TRUE(y->InstanceOf(X));
EXPECT_TRUE(y->InstanceOf(Y));
- Class* java_lang_Class = class_linker_->FindSystemClass("Ljava/lang/Class;");
- Class* Object_array_class = class_linker_->FindSystemClass("[Ljava/lang/Object;");
+ Class* java_lang_Class = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Class;");
+ Class* Object_array_class = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
EXPECT_FALSE(java_lang_Class->InstanceOf(Object_array_class));
EXPECT_TRUE(Object_array_class->InstanceOf(java_lang_Class));
// All array classes implement Cloneable and Serializable.
Object* array = ObjectArray<Object>::Alloc(soa.Self(), Object_array_class, 1);
- Class* java_lang_Cloneable = class_linker_->FindSystemClass("Ljava/lang/Cloneable;");
- Class* java_io_Serializable = class_linker_->FindSystemClass("Ljava/io/Serializable;");
+ Class* java_lang_Cloneable = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Cloneable;");
+ Class* java_io_Serializable = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;");
EXPECT_TRUE(array->InstanceOf(java_lang_Cloneable));
EXPECT_TRUE(array->InstanceOf(java_io_Serializable));
}
@@ -505,8 +507,8 @@
ScopedObjectAccess soa(Thread::Current());
jobject jclass_loader = LoadDex("XandY");
SirtRef<ClassLoader> class_loader(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader));
- Class* X = class_linker_->FindClass("LX;", class_loader);
- Class* Y = class_linker_->FindClass("LY;", class_loader);
+ Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader);
+ Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader);
EXPECT_TRUE(X->IsAssignableFrom(X));
EXPECT_TRUE(X->IsAssignableFrom(Y));
@@ -514,8 +516,8 @@
EXPECT_TRUE(Y->IsAssignableFrom(Y));
// class final String implements CharSequence, ..
- Class* string = class_linker_->FindSystemClass("Ljava/lang/String;");
- Class* charseq = class_linker_->FindSystemClass("Ljava/lang/CharSequence;");
+ Class* string = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;");
+ Class* charseq = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/CharSequence;");
// Can String be assigned to CharSequence without a cast?
EXPECT_TRUE(charseq->IsAssignableFrom(string));
// Can CharSequence be assigned to String without a cast?
@@ -542,36 +544,36 @@
ScopedObjectAccess soa(Thread::Current());
jobject jclass_loader = LoadDex("XandY");
SirtRef<ClassLoader> class_loader(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader));
- Class* X = class_linker_->FindClass("LX;", class_loader);
- Class* Y = class_linker_->FindClass("LY;", class_loader);
+ Class* X = class_linker_->FindClass(soa.Self(), "LX;", class_loader);
+ Class* Y = class_linker_->FindClass(soa.Self(), "LY;", class_loader);
ASSERT_TRUE(X != NULL);
ASSERT_TRUE(Y != NULL);
- Class* YA = class_linker_->FindClass("[LY;", class_loader);
- Class* YAA = class_linker_->FindClass("[[LY;", class_loader);
+ Class* YA = class_linker_->FindClass(soa.Self(), "[LY;", class_loader);
+ Class* YAA = class_linker_->FindClass(soa.Self(), "[[LY;", class_loader);
ASSERT_TRUE(YA != NULL);
ASSERT_TRUE(YAA != NULL);
- Class* XAA = class_linker_->FindClass("[[LX;", class_loader);
+ Class* XAA = class_linker_->FindClass(soa.Self(), "[[LX;", class_loader);
ASSERT_TRUE(XAA != NULL);
- Class* O = class_linker_->FindSystemClass("Ljava/lang/Object;");
- Class* OA = class_linker_->FindSystemClass("[Ljava/lang/Object;");
- Class* OAA = class_linker_->FindSystemClass("[[Ljava/lang/Object;");
- Class* OAAA = class_linker_->FindSystemClass("[[[Ljava/lang/Object;");
+ Class* O = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
+ Class* OA = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
+ Class* OAA = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;");
+ Class* OAAA = class_linker_->FindSystemClass(soa.Self(), "[[[Ljava/lang/Object;");
ASSERT_TRUE(O != NULL);
ASSERT_TRUE(OA != NULL);
ASSERT_TRUE(OAA != NULL);
ASSERT_TRUE(OAAA != NULL);
- Class* S = class_linker_->FindSystemClass("Ljava/io/Serializable;");
- Class* SA = class_linker_->FindSystemClass("[Ljava/io/Serializable;");
- Class* SAA = class_linker_->FindSystemClass("[[Ljava/io/Serializable;");
+ Class* S = class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;");
+ Class* SA = class_linker_->FindSystemClass(soa.Self(), "[Ljava/io/Serializable;");
+ Class* SAA = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/io/Serializable;");
ASSERT_TRUE(S != NULL);
ASSERT_TRUE(SA != NULL);
ASSERT_TRUE(SAA != NULL);
- Class* IA = class_linker_->FindSystemClass("[I");
+ Class* IA = class_linker_->FindSystemClass(soa.Self(), "[I");
ASSERT_TRUE(IA != NULL);
EXPECT_TRUE(YAA->IsAssignableFrom(YAA)); // identity
@@ -616,7 +618,7 @@
// TODO: check that s.count == 3.
// Ensure that we handle superclass fields correctly...
- c = class_linker_->FindSystemClass("Ljava/lang/StringBuilder;");
+ c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/StringBuilder;");
ASSERT_TRUE(c != NULL);
// No StringBuilder.count...
EXPECT_TRUE(c->FindDeclaredInstanceField("count", "I") == NULL);
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index def3292..5779442 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -16,7 +16,7 @@
#include <limits.h>
-#include "class_linker.h"
+#include "class_linker-inl.h"
#include "common_throws.h"
#include "debugger.h"
#include "dex_file-inl.h"
@@ -57,22 +57,22 @@
jint length) {
ScopedFastNativeObjectAccess soa(env);
mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
- if (element_class == NULL) {
+ if (UNLIKELY(element_class == nullptr)) {
ThrowNullPointerException(NULL, "element class == null");
- return NULL;
+ return nullptr;
}
- if (length < 0) {
+ if (UNLIKELY(length < 0)) {
ThrowNegativeArraySizeException(length);
- return NULL;
+ return nullptr;
}
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- std::string descriptor;
- descriptor += "[";
- descriptor += ClassHelper(element_class).GetDescriptor();
- SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr);
- mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+ Runtime* runtime = Runtime::Current();
+ mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(soa.Self(), element_class);
+ if (UNLIKELY(array_class == nullptr)) {
+ return nullptr;
+ }
+ gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentNonMovingAllocator();
mirror::Array* result = mirror::Array::Alloc<true>(soa.Self(), array_class, length,
- Runtime::Current()->GetHeap()->GetCurrentNonMovingAllocator());
+ allocator);
return soa.AddLocalReference<jobject>(result);
}
diff --git a/runtime/native/dalvik_system_Zygote.cc b/runtime/native/dalvik_system_Zygote.cc
index 29c0bc0..4d009db 100644
--- a/runtime/native/dalvik_system_Zygote.cc
+++ b/runtime/native/dalvik_system_Zygote.cc
@@ -217,10 +217,6 @@
static void DropCapabilitiesBoundingSet() {
for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
- if (i == CAP_NET_RAW) {
- // Don't break /system/bin/ping
- continue;
- }
int rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
if (rc == -1) {
if (errno == EINVAL) {
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 3e3f608..8bf36e7 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -43,7 +43,8 @@
}
// "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
-static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize, jobject javaLoader) {
+static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize,
+ jobject javaLoader) {
ScopedObjectAccess soa(env);
ScopedUtfChars name(env, javaName);
if (name.c_str() == nullptr) {
@@ -64,7 +65,8 @@
SirtRef<mirror::ClassLoader> class_loader(soa.Self(),
soa.Decode<mirror::ClassLoader*>(javaLoader));
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- SirtRef<mirror::Class> c(soa.Self(), class_linker->FindClass(descriptor.c_str(), class_loader));
+ SirtRef<mirror::Class> c(soa.Self(), class_linker->FindClass(soa.Self(), descriptor.c_str(),
+ class_loader));
if (c.get() == nullptr) {
ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
env->ExceptionClear();
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index c401d50..f95664b 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -20,6 +20,7 @@
#include "scoped_fast_native_object_access.h"
#include "scoped_thread_state_change.h"
#include "ScopedLocalRef.h"
+#include "verify_object-inl.h"
namespace art {
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index 011e165..2665a08 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -24,6 +24,7 @@
#include "ScopedUtfChars.h"
#include "thread.h"
#include "thread_list.h"
+#include "verify_object-inl.h"
namespace art {
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index 2197597..fc30aa6 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "class_linker.h"
+#include "class_linker-inl.h"
#include "common_throws.h"
#include "dex_file-inl.h"
#include "jni_internal.h"
@@ -50,12 +50,8 @@
ThrowNegativeArraySizeException(length);
return NULL;
}
- std::string descriptor("[");
- descriptor += ClassHelper(element_class).GetDescriptor();
-
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- SirtRef<mirror::ClassLoader> class_loader(soa.Self(), element_class->GetClassLoader());
- mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+ mirror::Class* array_class = class_linker->FindArrayClass(soa.Self(), element_class);
if (UNLIKELY(array_class == NULL)) {
CHECK(soa.Self()->IsExceptionPending());
return NULL;
diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc
index 809369a..1266c41 100644
--- a/runtime/native/java_lang_reflect_Proxy.cc
+++ b/runtime/native/java_lang_reflect_Proxy.cc
@@ -20,6 +20,7 @@
#include "mirror/object_array.h"
#include "mirror/string.h"
#include "scoped_thread_state_change.h"
+#include "verify_object-inl.h"
namespace art {
diff --git a/runtime/native/scoped_fast_native_object_access.h b/runtime/native/scoped_fast_native_object_access.h
index 1658d96..b5ee748 100644
--- a/runtime/native/scoped_fast_native_object_access.h
+++ b/runtime/native/scoped_fast_native_object_access.h
@@ -21,6 +21,7 @@
#include "jni_internal.h"
#include "thread-inl.h"
#include "mirror/art_method.h"
+#include "verify_object.h"
namespace art {
@@ -79,9 +80,7 @@
return NULL;
}
- if (kIsDebugBuild) {
- Runtime::Current()->GetHeap()->VerifyObject(obj);
- }
+ VerifyObject(obj);
DCHECK_NE((reinterpret_cast<uintptr_t>(obj) & 0xffff0000), 0xebad0000);
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 81d4540..945cd77 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -22,7 +22,7 @@
namespace art {
const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '1', '4', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '1', '5', '\0' };
OatHeader::OatHeader() {
memset(this, 0, sizeof(*this));
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 00a8506..61f023c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -84,6 +84,7 @@
// This won't work for portable runtime execution because it doesn't process relocations.
UniquePtr<File> file(OS::OpenFileForReading(filename.c_str()));
if (file.get() == NULL) {
+ *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
return NULL;
}
return OpenElfFile(file.get(), location, requested_base, false, executable, error_msg);
diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h
index 6af338b..468ba08 100644
--- a/runtime/object_callbacks.h
+++ b/runtime/object_callbacks.h
@@ -60,6 +60,7 @@
// address the object (if the object didn't move, returns the object input parameter).
typedef mirror::Object* (IsMarkedCallback)(mirror::Object* object, void* arg)
__attribute__((warn_unused_result));
+typedef void (ProcessMarkStackCallback)(void* arg);
} // namespace art
diff --git a/runtime/object_utils.h b/runtime/object_utils.h
index a981fab..4eac291 100644
--- a/runtime/object_utils.h
+++ b/runtime/object_utils.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_OBJECT_UTILS_H_
#define ART_RUNTIME_OBJECT_UTILS_H_
-#include "class_linker-inl.h"
+#include "class_linker.h"
#include "dex_file.h"
#include "monitor.h"
#include "mirror/art_field.h"
@@ -158,10 +158,10 @@
DCHECK(!klass_->IsPrimitive());
if (klass_->IsArrayClass()) {
if (idx == 0) {
- return GetClassLinker()->FindSystemClass("Ljava/lang/Cloneable;");
+ return GetClassLinker()->FindSystemClass(Thread::Current(), "Ljava/lang/Cloneable;");
} else {
DCHECK_EQ(1U, idx);
- return GetClassLinker()->FindSystemClass("Ljava/io/Serializable;");
+ return GetClassLinker()->FindSystemClass(Thread::Current(), "Ljava/io/Serializable;");
}
} else if (klass_->IsProxyClass()) {
return klass_->GetIfTable()->GetInterface(idx);
@@ -251,7 +251,7 @@
mirror::Class* GetType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t field_index = field_->GetDexFieldIndex();
if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) {
- return GetClassLinker()->FindSystemClass(GetTypeDescriptor());
+ return GetClassLinker()->FindSystemClass(Thread::Current(), GetTypeDescriptor());
}
const DexFile& dex_file = GetDexFile();
const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e66e5af..1ef15f7 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -77,13 +77,6 @@
is_zygote_(false),
is_concurrent_gc_enabled_(true),
is_explicit_gc_disabled_(false),
- compiler_filter_(kSpeed),
- huge_method_threshold_(0),
- large_method_threshold_(0),
- small_method_threshold_(0),
- tiny_method_threshold_(0),
- num_dex_methods_threshold_(0),
- sea_ir_mode_(false),
default_stack_size_(0),
heap_(nullptr),
max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
@@ -452,14 +445,6 @@
parsed->hook_exit_ = exit;
parsed->hook_abort_ = NULL; // We don't call abort(3) by default; see Runtime::Abort.
- parsed->compiler_filter_ = Runtime::kDefaultCompilerFilter;
- parsed->huge_method_threshold_ = Runtime::kDefaultHugeMethodThreshold;
- parsed->large_method_threshold_ = Runtime::kDefaultLargeMethodThreshold;
- parsed->small_method_threshold_ = Runtime::kDefaultSmallMethodThreshold;
- parsed->tiny_method_threshold_ = Runtime::kDefaultTinyMethodThreshold;
- parsed->num_dex_methods_threshold_ = Runtime::kDefaultNumDexMethodsThreshold;
-
- parsed->sea_ir_mode_ = false;
// gLogVerbosity.class_linker = true; // TODO: don't check this in!
// gLogVerbosity.compiler = true; // TODO: don't check this in!
// gLogVerbosity.verifier = true; // TODO: don't check this in!
@@ -721,28 +706,22 @@
} else if (StartsWith(option, "-Xprofile-backoff:")) {
parsed->profile_backoff_coefficient_ = ParseDoubleOrDie(
option, ':', 1.0, 10.0, ignore_unrecognized, parsed->profile_backoff_coefficient_);
- } else if (option == "-compiler-filter:interpret-only") {
- parsed->compiler_filter_ = kInterpretOnly;
- } else if (option == "-compiler-filter:space") {
- parsed->compiler_filter_ = kSpace;
- } else if (option == "-compiler-filter:balanced") {
- parsed->compiler_filter_ = kBalanced;
- } else if (option == "-compiler-filter:speed") {
- parsed->compiler_filter_ = kSpeed;
- } else if (option == "-compiler-filter:everything") {
- parsed->compiler_filter_ = kEverything;
- } else if (option == "-sea_ir") {
- parsed->sea_ir_mode_ = true;
- } else if (StartsWith(option, "-huge-method-max:")) {
- parsed->huge_method_threshold_ = ParseIntegerOrDie(option, ':');
- } else if (StartsWith(option, "-large-method-max:")) {
- parsed->large_method_threshold_ = ParseIntegerOrDie(option, ':');
- } else if (StartsWith(option, "-small-method-max:")) {
- parsed->small_method_threshold_ = ParseIntegerOrDie(option, ':');
- } else if (StartsWith(option, "-tiny-method-max:")) {
- parsed->tiny_method_threshold_ = ParseIntegerOrDie(option, ':');
- } else if (StartsWith(option, "-num-dex-methods-max:")) {
- parsed->num_dex_methods_threshold_ = ParseIntegerOrDie(option, ':');
+ } else if (option == "-Xcompiler-option") {
+ i++;
+ if (i == options.size()) {
+ // TODO: usage
+ LOG(FATAL) << "Missing required compiler option for " << option;
+ return NULL;
+ }
+ parsed->compiler_options_.push_back(options[i].first);
+ } else if (option == "-Ximage-compiler-option") {
+ i++;
+ if (i == options.size()) {
+ // TODO: usage
+ LOG(FATAL) << "Missing required compiler option for " << option;
+ return NULL;
+ }
+ parsed->image_compiler_options_.push_back(options[i].first);
} else {
if (!ignore_unrecognized) {
// TODO: print usage via vfprintf
@@ -988,14 +967,6 @@
is_zygote_ = options->is_zygote_;
is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
- compiler_filter_ = options->compiler_filter_;
- huge_method_threshold_ = options->huge_method_threshold_;
- large_method_threshold_ = options->large_method_threshold_;
- small_method_threshold_ = options->small_method_threshold_;
- tiny_method_threshold_ = options->tiny_method_threshold_;
- num_dex_methods_threshold_ = options->num_dex_methods_threshold_;
-
- sea_ir_mode_ = options->sea_ir_mode_;
vfprintf_ = options->hook_vfprintf_;
exit_ = options->hook_exit_;
abort_ = options->hook_abort_;
@@ -1486,12 +1457,18 @@
(1 << art::x86_64::RSI) | (1 << art::x86_64::RDX) | (1 << art::x86_64::RCX) |
(1 << art::x86_64::R8) | (1 << art::x86_64::R9);
uint32_t core_spills = ref_spills | (type == kRefsAndArgs ? arg_spills : 0) |
- (1 << art::x86::kNumberOfCpuRegisters); // fake return address callee save
+ (1 << art::x86_64::kNumberOfCpuRegisters); // fake return address callee save
+ uint32_t fp_arg_spills =
+ (1 << art::x86_64::XMM0) | (1 << art::x86_64::XMM1) | (1 << art::x86_64::XMM2) |
+ (1 << art::x86_64::XMM3) | (1 << art::x86_64::XMM4) | (1 << art::x86_64::XMM5) |
+ (1 << art::x86_64::XMM6) | (1 << art::x86_64::XMM7);
+ uint32_t fp_spills = (type == kRefsAndArgs ? fp_arg_spills : 0);
size_t frame_size = RoundUp((__builtin_popcount(core_spills) /* gprs */ +
+ __builtin_popcount(fp_spills) /* fprs */ +
1 /* Method* */) * kPointerSize, kStackAlignment);
method->SetFrameSizeInBytes(frame_size);
method->SetCoreSpillMask(core_spills);
- method->SetFpSpillMask(0);
+ method->SetFpSpillMask(fp_spills);
} else {
UNIMPLEMENTED(FATAL) << instruction_set;
}
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 159de2e..8924921 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -72,26 +72,6 @@
public:
typedef std::vector<std::pair<std::string, const void*> > Options;
- enum CompilerFilter {
- kInterpretOnly, // Compile nothing.
- kSpace, // Maximize space savings.
- kBalanced, // Try to get the best performance return on compilation investment.
- kSpeed, // Maximize runtime performance.
- kEverything // Force compilation (Note: excludes compilaton of class initializers).
- };
-
- // Guide heuristics to determine whether to compile method if profile data not available.
-#if ART_SMALL_MODE
- static const CompilerFilter kDefaultCompilerFilter = kInterpretOnly;
-#else
- static const CompilerFilter kDefaultCompilerFilter = kSpeed;
-#endif
- static const size_t kDefaultHugeMethodThreshold = 10000;
- static const size_t kDefaultLargeMethodThreshold = 600;
- static const size_t kDefaultSmallMethodThreshold = 60;
- static const size_t kDefaultTinyMethodThreshold = 20;
- static const size_t kDefaultNumDexMethodsThreshold = 900;
-
class ParsedOptions {
public:
// returns null if problem parsing and ignore_unrecognized is false
@@ -140,13 +120,8 @@
void (*hook_exit_)(jint status);
void (*hook_abort_)();
std::vector<std::string> properties_;
- CompilerFilter compiler_filter_;
- size_t huge_method_threshold_;
- size_t large_method_threshold_;
- size_t small_method_threshold_;
- size_t tiny_method_threshold_;
- size_t num_dex_methods_threshold_;
- bool sea_ir_mode_;
+ std::vector<std::string> compiler_options_;
+ std::vector<std::string> image_compiler_options_;
bool profile_;
std::string profile_output_filename_;
int profile_period_s_;
@@ -178,42 +153,12 @@
return is_explicit_gc_disabled_;
}
-#ifdef ART_SEA_IR_MODE
- bool IsSeaIRMode() const {
- return sea_ir_mode_;
- }
-#endif
-
- void SetSeaIRMode(bool sea_ir_mode) {
- sea_ir_mode_ = sea_ir_mode;
+ const std::vector<std::string>& GetCompilerOptions() const {
+ return compiler_options_;
}
- CompilerFilter GetCompilerFilter() const {
- return compiler_filter_;
- }
-
- void SetCompilerFilter(CompilerFilter compiler_filter) {
- compiler_filter_ = compiler_filter;
- }
-
- size_t GetHugeMethodThreshold() const {
- return huge_method_threshold_;
- }
-
- size_t GetLargeMethodThreshold() const {
- return large_method_threshold_;
- }
-
- size_t GetSmallMethodThreshold() const {
- return small_method_threshold_;
- }
-
- size_t GetTinyMethodThreshold() const {
- return tiny_method_threshold_;
- }
-
- size_t GetNumDexMethodsThreshold() const {
- return num_dex_methods_threshold_;
+ const std::vector<std::string>& GetImageCompilerOptions() const {
+ return image_compiler_options_;
}
const std::string& GetHostPrefix() const {
@@ -429,6 +374,10 @@
return callee_save_methods_[type];
}
+ static size_t GetCalleeSaveMethodOffset(CalleeSaveType type) {
+ return OFFSETOF_MEMBER(Runtime, callee_save_methods_[type]);
+ }
+
void SetCalleeSaveMethod(mirror::ArtMethod* method, CalleeSaveType type);
mirror::ArtMethod* CreateCalleeSaveMethod(InstructionSet instruction_set,
@@ -521,14 +470,8 @@
bool is_concurrent_gc_enabled_;
bool is_explicit_gc_disabled_;
- CompilerFilter compiler_filter_;
- size_t huge_method_threshold_;
- size_t large_method_threshold_;
- size_t small_method_threshold_;
- size_t tiny_method_threshold_;
- size_t num_dex_methods_threshold_;
-
- bool sea_ir_mode_;
+ std::vector<std::string> compiler_options_;
+ std::vector<std::string> image_compiler_options_;
// The host prefix is used during cross compilation. It is removed
// from the start of host paths such as:
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index 1ca6c4e..f0f5ed2 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -19,6 +19,7 @@
#include "base/casts.h"
#include "thread-inl.h"
+#include "verify_object.h"
namespace art {
@@ -121,6 +122,7 @@
: ScopedThreadStateChange(ThreadForEnv(env), kRunnable),
env_(down_cast<JNIEnvExt*>(env)), vm_(env_->vm) {
self_->VerifyStack();
+ Locks::mutator_lock_->AssertSharedHeld(self_);
}
explicit ScopedObjectAccessUnchecked(Thread* self)
@@ -129,6 +131,7 @@
env_(down_cast<JNIEnvExt*>(self->GetJniEnv())),
vm_(env_ != NULL ? env_->vm : NULL) {
self_->VerifyStack();
+ Locks::mutator_lock_->AssertSharedHeld(self_);
}
// Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't
@@ -138,6 +141,7 @@
// Here purely to force inlining.
~ScopedObjectAccessUnchecked() ALWAYS_INLINE {
+ Locks::mutator_lock_->AssertSharedHeld(self_);
}
JNIEnvExt* Env() const {
@@ -165,9 +169,7 @@
return NULL;
}
- if (kIsDebugBuild) {
- Runtime::Current()->GetHeap()->VerifyObject(obj);
- }
+ VerifyObject(obj);
DCHECK_NE((reinterpret_cast<uintptr_t>(obj) & 0xffff0000), 0xebad0000);
@@ -251,14 +253,12 @@
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE
: ScopedObjectAccessUnchecked(env) {
- Locks::mutator_lock_->AssertSharedHeld(Self());
}
explicit ScopedObjectAccess(Thread* self)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
: ScopedObjectAccessUnchecked(self) {
- Locks::mutator_lock_->AssertSharedHeld(Self());
}
~ScopedObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE {
diff --git a/runtime/sirt_ref.h b/runtime/sirt_ref.h
index 3c5e4f8..b22e816 100644
--- a/runtime/sirt_ref.h
+++ b/runtime/sirt_ref.h
@@ -45,8 +45,11 @@
return down_cast<T*>(sirt_.GetReference(0));
}
- void reset(T* object = nullptr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Returns the old reference.
+ T* reset(T* object = nullptr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ T* old_ref = get();
sirt_.SetReference(0, object);
+ return old_ref;
}
private:
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 864b86a..a6a0b29 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -25,14 +25,11 @@
#include "runtime.h"
#include "thread_list.h"
#include "throw_location.h"
+#include "verify_object-inl.h"
#include "vmap_table.h"
namespace art {
-bool ShadowFrame::VerifyReference(const mirror::Object* val) const {
- return !Runtime::Current()->GetHeap()->IsInTempSpace(val);
-}
-
mirror::Object* ShadowFrame::GetThisObject() const {
mirror::ArtMethod* m = GetMethod();
if (m->IsStatic()) {
diff --git a/runtime/stack.h b/runtime/stack.h
index 7e9889e..6a62922 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -22,7 +22,9 @@
#include "base/casts.h"
#include "base/macros.h"
#include "arch/context.h"
+#include "mirror/object.h"
#include "mirror/object_reference.h"
+#include "verify_object.h"
#include <stdint.h>
#include <string>
@@ -213,26 +215,20 @@
return *reinterpret_cast<unaligned_double*>(vreg);
}
- template <bool kChecked = false>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
mirror::Object* GetVRegReference(size_t i) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK_LT(i, NumberOfVRegs());
+ mirror::Object* ref;
if (HasReferenceArray()) {
- mirror::Object* ref = References()[i].AsMirrorPtr();
- if (kChecked) {
- CHECK(VerifyReference(ref)) << "VReg " << i << "(" << ref
- << ") is in protected space, reference array " << true;
- }
- return ref;
+ ref = References()[i].AsMirrorPtr();
} else {
const uint32_t* vreg_ptr = &vregs_[i];
- mirror::Object* ref =
- reinterpret_cast<const StackReference<mirror::Object>*>(vreg_ptr)->AsMirrorPtr();
- if (kChecked) {
- CHECK(VerifyReference(ref)) << "VReg " << i
- << "(" << ref << ") is in protected space, reference array " << false;
- }
- return ref;
+ ref = reinterpret_cast<const StackReference<mirror::Object>*>(vreg_ptr)->AsMirrorPtr();
}
+ if (kVerifyFlags & kVerifyReads) {
+ VerifyObject(ref);
+ }
+ return ref;
}
// Get view of vregs as range of consecutive arguments starting at i.
@@ -290,10 +286,12 @@
}
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetVRegReference(size_t i, mirror::Object* val) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK_LT(i, NumberOfVRegs());
- DCHECK(!kMovingCollector || VerifyReference(val))
- << "VReg " << i << "(" << val << ") is in protected space";
+ if (kVerifyFlags & kVerifyWrites) {
+ VerifyObject(val);
+ }
uint32_t* vreg = &vregs_[i];
reinterpret_cast<StackReference<mirror::Object>*>(vreg)->Assign(val);
if (HasReferenceArray()) {
@@ -374,8 +372,6 @@
return reinterpret_cast<const StackReference<mirror::Object>*>(vreg_end);
}
- bool VerifyReference(const mirror::Object* val) const;
-
StackReference<mirror::Object>* References() {
return const_cast<StackReference<mirror::Object>*>(const_cast<const ShadowFrame*>(this)->References());
}
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index c0bf377..f7e88cc 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -152,9 +152,10 @@
}
inline void Thread::VerifyStack() {
- gc::Heap* heap = Runtime::Current()->GetHeap();
- if (heap->IsObjectValidationEnabled()) {
- VerifyStackImpl();
+ if (kVerifyStack) {
+ if (Runtime::Current()->GetHeap()->IsObjectValidationEnabled()) {
+ VerifyStackImpl();
+ }
}
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 16655fb..4fe9169 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -66,6 +66,7 @@
#include "thread_list.h"
#include "utils.h"
#include "verifier/dex_gc_map.h"
+#include "verify_object-inl.h"
#include "vmap_table.h"
#include "well_known_classes.h"
@@ -1004,6 +1005,15 @@
}
}
+void Thread::AssertNoPendingExceptionForNewException(const char* msg) const {
+ if (UNLIKELY(IsExceptionPending())) {
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::Throwable* exception = GetException(nullptr);
+ LOG(FATAL) << "Throwing new exception " << msg << " with unexpected pending exception: "
+ << exception->Dump();
+ }
+}
+
static void MonitorExitVisitor(mirror::Object** object, void* arg, uint32_t /*thread_id*/,
RootType /*root_type*/)
NO_THREAD_SAFETY_ANALYSIS {
@@ -1217,8 +1227,8 @@
if (UNLIKELY(result == nullptr)) {
JniAbortF(nullptr, "use of deleted %s %p", ToStr<IndirectRefKind>(kind).c_str(), obj);
} else {
- if (kIsDebugBuild && (result != kInvalidIndirectRefObject)) {
- Runtime::Current()->GetHeap()->VerifyObject(result);
+ if (result != kInvalidIndirectRefObject) {
+ VerifyObject(result);
}
}
return result;
@@ -1506,7 +1516,8 @@
void Thread::ThrowNewException(const ThrowLocation& throw_location, const char* exception_class_descriptor,
const char* msg) {
- AssertNoPendingException(); // Callers should either clear or call ThrowNewWrappedException.
+ // Callers should either clear or call ThrowNewWrappedException.
+ AssertNoPendingExceptionForNewException(msg);
ThrowNewWrappedException(throw_location, exception_class_descriptor, msg);
}
@@ -1528,7 +1539,7 @@
}
SirtRef<mirror::ClassLoader> class_loader(this, cl);
SirtRef<mirror::Class>
- exception_class(this, runtime->GetClassLinker()->FindClass(exception_class_descriptor,
+ exception_class(this, runtime->GetClassLinker()->FindClass(this, exception_class_descriptor,
class_loader));
if (UNLIKELY(exception_class.get() == nullptr)) {
CHECK(IsExceptionPending());
@@ -2000,9 +2011,7 @@
};
void Thread::SetClassLoaderOverride(mirror::ClassLoader* class_loader_override) {
- if (kIsDebugBuild) {
- Runtime::Current()->GetHeap()->VerifyObject(class_loader_override);
- }
+ VerifyObject(class_loader_override);
class_loader_override_ = class_loader_override;
}
@@ -2037,11 +2046,9 @@
}
}
-static void VerifyRoot(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
- RootType /*root_type*/) {
- DCHECK(root != nullptr);
- DCHECK(arg != nullptr);
- reinterpret_cast<gc::Heap*>(arg)->VerifyObject(*root);
+static void VerifyRoot(mirror::Object** root, void* /*arg*/, uint32_t /*thread_id*/,
+ RootType /*root_type*/) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ VerifyObject(*root);
}
void Thread::VerifyStackImpl() {
diff --git a/runtime/thread.h b/runtime/thread.h
index 48912d1..f9d31af 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -285,6 +285,7 @@
}
void AssertNoPendingException() const;
+ void AssertNoPendingExceptionForNewException(const char* msg) const;
void SetException(const ThrowLocation& throw_location, mirror::Throwable* new_exception)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -375,7 +376,8 @@
return class_loader_override_;
}
- void SetClassLoaderOverride(mirror::ClassLoader* class_loader_override);
+ void SetClassLoaderOverride(mirror::ClassLoader* class_loader_override)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Create the internal representation of a stack trace, that is more time
// and space efficient to compute than the StackTraceElement[]
@@ -391,7 +393,7 @@
void VisitRoots(RootCallback* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VerifyStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE void VerifyStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
//
// Offsets of various members of native Thread class, used by compiled code.
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 74e6f1c..d311945 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -269,6 +269,7 @@
void ThreadList::SuspendAll() {
Thread* self = Thread::Current();
+ DCHECK(self != nullptr);
VLOG(threads) << *self << " SuspendAll starting...";
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 019a322..fcda6c9 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -157,7 +157,6 @@
}
void Transaction::VisitRoots(RootCallback* callback, void* arg) {
- LOG(INFO) << "Transaction::VisitRoots";
MutexLock mu(Thread::Current(), log_lock_);
VisitObjectLogs(callback, arg);
VisitArrayLogs(callback, arg);
@@ -310,8 +309,10 @@
if (field_value.kind == ObjectLog::kReference) {
mirror::Object* obj =
reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value));
- callback(&obj, arg, 0, kRootUnknown);
- field_value.value = reinterpret_cast<uintptr_t>(obj);
+ if (obj != nullptr) {
+ callback(&obj, arg, 0, kRootUnknown);
+ field_value.value = reinterpret_cast<uintptr_t>(obj);
+ }
}
}
}
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index dcfa24b..9dc7b44 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -29,7 +29,7 @@
TEST_F(TransactionTest, Object_class) {
ScopedObjectAccess soa(Thread::Current());
SirtRef<mirror::Class> sirt_klass(soa.Self(),
- class_linker_->FindSystemClass("Ljava/lang/Object;"));
+ class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
ASSERT_TRUE(sirt_klass.get() != nullptr);
Transaction transaction;
@@ -47,7 +47,8 @@
TEST_F(TransactionTest, Object_monitor) {
ScopedObjectAccess soa(Thread::Current());
SirtRef<mirror::Class> sirt_klass(soa.Self(),
- class_linker_->FindSystemClass("Ljava/lang/Object;"));
+ class_linker_->FindSystemClass(soa.Self(),
+ "Ljava/lang/Object;"));
ASSERT_TRUE(sirt_klass.get() != nullptr);
SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
ASSERT_TRUE(sirt_obj.get() != nullptr);
@@ -74,7 +75,8 @@
TEST_F(TransactionTest, Array_length) {
ScopedObjectAccess soa(Thread::Current());
SirtRef<mirror::Class> sirt_klass(soa.Self(),
- class_linker_->FindSystemClass("[Ljava/lang/Object;"));
+ class_linker_->FindSystemClass(soa.Self(),
+ "[Ljava/lang/Object;"));
ASSERT_TRUE(sirt_klass.get() != nullptr);
constexpr int32_t kArraySize = 2;
@@ -101,7 +103,8 @@
ASSERT_TRUE(class_loader.get() != nullptr);
SirtRef<mirror::Class> sirt_klass(soa.Self(),
- class_linker_->FindClass("LStaticFieldsTest;", class_loader));
+ class_linker_->FindClass(soa.Self(), "LStaticFieldsTest;",
+ class_loader));
ASSERT_TRUE(sirt_klass.get() != nullptr);
class_linker_->EnsureInitialized(sirt_klass, true, true);
ASSERT_TRUE(sirt_klass->IsInitialized());
@@ -147,13 +150,16 @@
ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
ASSERT_EQ(doubleField->GetDouble(sirt_klass.get()), static_cast<double>(0.0));
- mirror::ArtField* objectField = sirt_klass->FindDeclaredStaticField("objectField", "Ljava/lang/Object;");
+ mirror::ArtField* objectField = sirt_klass->FindDeclaredStaticField("objectField",
+ "Ljava/lang/Object;");
ASSERT_TRUE(objectField != nullptr);
ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot);
ASSERT_EQ(objectField->GetObject(sirt_klass.get()), nullptr);
// Create a java.lang.Object instance to set objectField.
- SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+ SirtRef<mirror::Class> object_klass(soa.Self(),
+ class_linker_->FindSystemClass(soa.Self(),
+ "Ljava/lang/Object;"));
ASSERT_TRUE(object_klass.get() != nullptr);
SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
ASSERT_TRUE(sirt_obj.get() != nullptr);
@@ -193,7 +199,8 @@
ASSERT_TRUE(class_loader.get() != nullptr);
SirtRef<mirror::Class> sirt_klass(soa.Self(),
- class_linker_->FindClass("LInstanceFieldsTest;", class_loader));
+ class_linker_->FindClass(soa.Self(), "LInstanceFieldsTest;",
+ class_loader));
ASSERT_TRUE(sirt_klass.get() != nullptr);
class_linker_->EnsureInitialized(sirt_klass, true, true);
ASSERT_TRUE(sirt_klass->IsInitialized());
@@ -243,13 +250,16 @@
ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble);
ASSERT_EQ(doubleField->GetDouble(sirt_instance.get()), static_cast<double>(0.0));
- mirror::ArtField* objectField = sirt_klass->FindDeclaredInstanceField("objectField", "Ljava/lang/Object;");
+ mirror::ArtField* objectField = sirt_klass->FindDeclaredInstanceField("objectField",
+ "Ljava/lang/Object;");
ASSERT_TRUE(objectField != nullptr);
ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot);
ASSERT_EQ(objectField->GetObject(sirt_instance.get()), nullptr);
// Create a java.lang.Object instance to set objectField.
- SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+ SirtRef<mirror::Class> object_klass(soa.Self(),
+ class_linker_->FindSystemClass(soa.Self(),
+ "Ljava/lang/Object;"));
ASSERT_TRUE(object_klass.get() != nullptr);
SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
ASSERT_TRUE(sirt_obj.get() != nullptr);
@@ -290,7 +300,8 @@
ASSERT_TRUE(class_loader.get() != nullptr);
SirtRef<mirror::Class> sirt_klass(soa.Self(),
- class_linker_->FindClass("LStaticArrayFieldsTest;", class_loader));
+ class_linker_->FindClass(soa.Self(), "LStaticArrayFieldsTest;",
+ class_loader));
ASSERT_TRUE(sirt_klass.get() != nullptr);
class_linker_->EnsureInitialized(sirt_klass, true, true);
ASSERT_TRUE(sirt_klass->IsInitialized());
@@ -352,15 +363,19 @@
ASSERT_EQ(doubleArray->GetLength(), 1);
ASSERT_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f));
- mirror::ArtField* objectArrayField = sirt_klass->FindDeclaredStaticField("objectArrayField", "[Ljava/lang/Object;");
+ mirror::ArtField* objectArrayField = sirt_klass->FindDeclaredStaticField("objectArrayField",
+ "[Ljava/lang/Object;");
ASSERT_TRUE(objectArrayField != nullptr);
- mirror::ObjectArray<mirror::Object>* objectArray = objectArrayField->GetObject(sirt_klass.get())->AsObjectArray<mirror::Object>();
+ mirror::ObjectArray<mirror::Object>* objectArray =
+ objectArrayField->GetObject(sirt_klass.get())->AsObjectArray<mirror::Object>();
ASSERT_TRUE(objectArray != nullptr);
ASSERT_EQ(objectArray->GetLength(), 1);
ASSERT_EQ(objectArray->GetWithoutChecks(0), nullptr);
// Create a java.lang.Object instance to set objectField.
- SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;"));
+ SirtRef<mirror::Class> object_klass(soa.Self(),
+ class_linker_->FindSystemClass(soa.Self(),
+ "Ljava/lang/Object;"));
ASSERT_TRUE(object_klass.get() != nullptr);
SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self()));
ASSERT_TRUE(sirt_obj.get() != nullptr);
@@ -400,7 +415,9 @@
ASSERT_TRUE(class_loader.get() != nullptr);
SirtRef<mirror::Class> sirt_klass(soa.Self(),
- class_linker_->FindClass("LTransaction$EmptyStatic;", class_loader));
+ class_linker_->FindClass(soa.Self(),
+ "LTransaction$EmptyStatic;",
+ class_loader));
ASSERT_TRUE(sirt_klass.get() != nullptr);
class_linker_->VerifyClass(sirt_klass);
ASSERT_TRUE(sirt_klass->IsVerified());
@@ -419,7 +436,9 @@
ASSERT_TRUE(class_loader.get() != nullptr);
SirtRef<mirror::Class> sirt_klass(soa.Self(),
- class_linker_->FindClass("LTransaction$StaticFieldClass;", class_loader));
+ class_linker_->FindClass(soa.Self(),
+ "LTransaction$StaticFieldClass;",
+ class_loader));
ASSERT_TRUE(sirt_klass.get() != nullptr);
class_linker_->VerifyClass(sirt_klass);
ASSERT_TRUE(sirt_klass->IsVerified());
@@ -441,22 +460,25 @@
// Load and verify java.lang.ExceptionInInitializerError and java.lang.InternalError which will
// be thrown by class initialization due to native call.
SirtRef<mirror::Class> sirt_klass(soa.Self(),
- class_linker_->FindSystemClass("Ljava/lang/ExceptionInInitializerError;"));
+ class_linker_->FindSystemClass(soa.Self(),
+ "Ljava/lang/ExceptionInInitializerError;"));
ASSERT_TRUE(sirt_klass.get() != nullptr);
class_linker_->VerifyClass(sirt_klass);
ASSERT_TRUE(sirt_klass->IsVerified());
- sirt_klass.reset(class_linker_->FindSystemClass("Ljava/lang/InternalError;"));
+ sirt_klass.reset(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/InternalError;"));
ASSERT_TRUE(sirt_klass.get() != nullptr);
class_linker_->VerifyClass(sirt_klass);
ASSERT_TRUE(sirt_klass->IsVerified());
// Load and verify Transaction$NativeSupport used in class initialization.
- sirt_klass.reset(class_linker_->FindClass("LTransaction$NativeSupport;", class_loader));
+ sirt_klass.reset(class_linker_->FindClass(soa.Self(), "LTransaction$NativeSupport;",
+ class_loader));
ASSERT_TRUE(sirt_klass.get() != nullptr);
class_linker_->VerifyClass(sirt_klass);
ASSERT_TRUE(sirt_klass->IsVerified());
- sirt_klass.reset(class_linker_->FindClass("LTransaction$BlacklistedClass;", class_loader));
+ sirt_klass.reset(class_linker_->FindClass(soa.Self(), "LTransaction$BlacklistedClass;",
+ class_loader));
ASSERT_TRUE(sirt_klass.get() != nullptr);
class_linker_->VerifyClass(sirt_klass);
ASSERT_TRUE(sirt_klass->IsVerified());
diff --git a/runtime/utils.cc b/runtime/utils.cc
index aad21bc..68d8417 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -21,9 +21,12 @@
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
+#include <sys/wait.h>
+
#include <unistd.h>
#include "UniquePtr.h"
+#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "dex_file-inl.h"
#include "mirror/art_field-inl.h"
@@ -1203,4 +1206,57 @@
sizeof(OatHeader::kOatMagic)) == 0);
}
+bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) {
+ const std::string command_line(Join(arg_vector, ' '));
+
+ CHECK_GE(arg_vector.size(), 1U) << command_line;
+
+ // Convert the args to char pointers.
+ const char* program = arg_vector[0].c_str();
+ std::vector<char*> args;
+ for (size_t i = 0; i < arg_vector.size(); ++i) {
+ const std::string& arg = arg_vector[i];
+ char* arg_str = const_cast<char*>(arg.c_str());
+ CHECK(arg_str != nullptr) << i;
+ args.push_back(arg_str);
+ }
+ args.push_back(NULL);
+
+ // fork and exec
+ pid_t pid = fork();
+ if (pid == 0) {
+ // no allocation allowed between fork and exec
+
+ // change process groups, so we don't get reaped by ProcessManager
+ setpgid(0, 0);
+
+ execv(program, &args[0]);
+
+ *error_msg = StringPrintf("Failed to execv(%s): %s", command_line.c_str(), strerror(errno));
+ return false;
+ } else {
+ if (pid == -1) {
+ *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
+ command_line.c_str(), strerror(errno));
+ return false;
+ }
+
+ // wait for subprocess to finish
+ int status;
+ pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+ if (got_pid != pid) {
+ *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
+ "wanted %d, got %d: %s",
+ command_line.c_str(), pid, got_pid, strerror(errno));
+ return false;
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
+ command_line.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace art
diff --git a/runtime/utils.h b/runtime/utils.h
index e2d8966..0bb06de 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -396,6 +396,9 @@
bool IsDexMagic(uint32_t magic);
bool IsOatMagic(uint32_t magic);
+// Wrapper on fork/execv to run a command in a subprocess.
+bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg);
+
class VoidFunctor {
public:
template <typename A>
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index b43177b..0d237e2 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -99,7 +99,7 @@
SirtRef<mirror::ShortArray> a(soa.Self(), mirror::ShortArray::Alloc(soa.Self(), 2));
EXPECT_EQ("short[]", PrettyTypeOf(a.get()));
- mirror::Class* c = class_linker_->FindSystemClass("[Ljava/lang/String;");
+ mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
ASSERT_TRUE(c != NULL);
mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
EXPECT_EQ("java.lang.String[]", PrettyTypeOf(o));
@@ -109,7 +109,7 @@
TEST_F(UtilsTest, PrettyClass) {
ScopedObjectAccess soa(Thread::Current());
EXPECT_EQ("null", PrettyClass(NULL));
- mirror::Class* c = class_linker_->FindSystemClass("[Ljava/lang/String;");
+ mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
ASSERT_TRUE(c != NULL);
mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
EXPECT_EQ("java.lang.Class<java.lang.String[]>", PrettyClass(o->GetClass()));
@@ -118,7 +118,7 @@
TEST_F(UtilsTest, PrettyClassAndClassLoader) {
ScopedObjectAccess soa(Thread::Current());
EXPECT_EQ("null", PrettyClassAndClassLoader(NULL));
- mirror::Class* c = class_linker_->FindSystemClass("[Ljava/lang/String;");
+ mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;");
ASSERT_TRUE(c != NULL);
mirror::Object* o = mirror::ObjectArray<mirror::String>::Alloc(soa.Self(), c, 0);
EXPECT_EQ("java.lang.Class<java.lang.String[],null>", PrettyClassAndClassLoader(o->GetClass()));
@@ -128,7 +128,8 @@
ScopedObjectAccess soa(Thread::Current());
EXPECT_EQ("null", PrettyField(NULL));
- mirror::Class* java_lang_String = class_linker_->FindSystemClass("Ljava/lang/String;");
+ mirror::Class* java_lang_String = class_linker_->FindSystemClass(soa.Self(),
+ "Ljava/lang/String;");
mirror::ArtField* f;
f = java_lang_String->FindDeclaredInstanceField("count", "I");
@@ -197,7 +198,7 @@
TEST_F(UtilsTest, JniShortName_JniLongName) {
ScopedObjectAccess soa(Thread::Current());
- mirror::Class* c = class_linker_->FindSystemClass("Ljava/lang/String;");
+ mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/String;");
ASSERT_TRUE(c != NULL);
mirror::ArtMethod* m;
@@ -349,4 +350,26 @@
CheckGetDalvikCacheFilenameOrDie("/system/framework/boot.art", "system@framework@boot.art");
}
+TEST_F(UtilsTest, ExecSuccess) {
+ std::vector<std::string> command;
+ if (kIsTargetBuild) {
+ command.push_back("/system/bin/id");
+ } else {
+ command.push_back("/usr/bin/id");
+ }
+ std::string error_msg;
+ EXPECT_TRUE(Exec(command, &error_msg));
+ EXPECT_EQ(0U, error_msg.size()) << error_msg;
+}
+
+// TODO: Disabled due to hang tearing down CommonTest.
+// Renable after splitting into RuntimeTest and CompilerTest.
+TEST_F(UtilsTest, DISABLED_ExecError) {
+ std::vector<std::string> command;
+ command.push_back("bogus");
+ std::string error_msg;
+ EXPECT_FALSE(Exec(command, &error_msg));
+ EXPECT_NE(0U, error_msg.size());
+}
+
} // namespace art
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index ab943a6..ffa8b9e 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3129,14 +3129,15 @@
this_class = actual_arg_type.GetClass();
} else {
const std::string& descriptor(actual_arg_type.GetDescriptor());
+ Thread* self = Thread::Current();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- this_class = class_linker->FindClass(descriptor.c_str(), *class_loader_);
+ this_class = class_linker->FindClass(self, descriptor.c_str(), *class_loader_);
if (this_class == NULL) {
Thread* self = Thread::Current();
self->ClearException();
// Look for a system class
SirtRef<mirror::ClassLoader> null_class_loader(self, nullptr);
- this_class = class_linker->FindClass(descriptor.c_str(), null_class_loader);
+ this_class = class_linker->FindClass(self, descriptor.c_str(), null_class_loader);
}
}
if (this_class == NULL) {
@@ -3638,7 +3639,7 @@
// Returns the access field of a quick field access (iget/iput-quick) or NULL
// if it cannot be found.
mirror::ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst,
- RegisterLine* reg_line) {
+ RegisterLine* reg_line) {
DCHECK(inst->Opcode() == Instruction::IGET_QUICK ||
inst->Opcode() == Instruction::IGET_WIDE_QUICK ||
inst->Opcode() == Instruction::IGET_OBJECT_QUICK ||
@@ -3654,12 +3655,12 @@
const std::string& descriptor(object_type.GetDescriptor());
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Thread* self = Thread::Current();
- object_class = class_linker->FindClass(descriptor.c_str(), *class_loader_);
+ object_class = class_linker->FindClass(self, descriptor.c_str(), *class_loader_);
if (object_class == NULL) {
self->ClearException();
// Look for a system class
SirtRef<mirror::ClassLoader> null_class_loader(self, nullptr);
- object_class = class_linker->FindClass(descriptor.c_str(), null_class_loader);
+ object_class = class_linker->FindClass(self, descriptor.c_str(), null_class_loader);
}
}
if (object_class == NULL) {
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index a56abba..ffa2455 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -30,7 +30,7 @@
void VerifyClass(const std::string& descriptor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ASSERT_TRUE(descriptor != NULL);
- mirror::Class* klass = class_linker_->FindSystemClass(descriptor.c_str());
+ mirror::Class* klass = class_linker_->FindSystemClass(Thread::Current(), descriptor.c_str());
// Verify the class
std::string error_msg;
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 630ef8a..63f0ff4 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -18,6 +18,7 @@
#include "base/casts.h"
+#include "class_linker-inl.h"
#include "dex_file-inl.h"
#include "mirror/class.h"
#include "mirror/class-inl.h"
@@ -928,11 +929,7 @@
}
mirror::Class* common_elem = ClassJoin(s_ct, t_ct);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- Thread* self = Thread::Current();
- SirtRef<mirror::ClassLoader> class_loader(self, s->GetClassLoader());
- std::string descriptor("[");
- descriptor += ClassHelper(common_elem).GetDescriptor();
- mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+ mirror::Class* array_class = class_linker->FindArrayClass(Thread::Current(), common_elem);
DCHECK(array_class != NULL);
return array_class;
} else {
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 5e894ed..9dd57b8 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -17,6 +17,7 @@
#include "reg_type_cache-inl.h"
#include "base/casts.h"
+#include "class_linker-inl.h"
#include "dex_file-inl.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
@@ -140,10 +141,11 @@
// Class was not found, must create new type.
// Try resolving class
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- SirtRef<mirror::ClassLoader> class_loader(Thread::Current(), loader);
+ Thread* self = Thread::Current();
+ SirtRef<mirror::ClassLoader> class_loader(self, loader);
mirror::Class* klass = NULL;
if (can_load_classes_) {
- klass = class_linker->FindClass(descriptor, class_loader);
+ klass = class_linker->FindClass(self, descriptor, class_loader);
} else {
klass = class_linker->LookupClass(descriptor, loader);
if (klass != NULL && !klass->IsLoaded()) {
@@ -277,7 +279,8 @@
mirror::Class* klass = NULL;
// Try loading the class from linker.
if (!descriptor.empty()) {
- klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(descriptor.c_str());
+ klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(),
+ descriptor.c_str());
}
Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
RegTypeCache::primitive_count_++;
diff --git a/runtime/verify_object-inl.h b/runtime/verify_object-inl.h
new file mode 100644
index 0000000..e211c83
--- /dev/null
+++ b/runtime/verify_object-inl.h
@@ -0,0 +1,61 @@
+/*
+ * 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_RUNTIME_VERIFY_OBJECT_INL_H_
+#define ART_RUNTIME_VERIFY_OBJECT_INL_H_
+
+#include "verify_object.h"
+
+#include "gc/heap.h"
+#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
+
+namespace art {
+
+inline void VerifyObject(mirror::Object* obj) {
+ if (kVerifyObjectSupport > kVerifyObjectModeDisabled && obj != nullptr) {
+ if (kVerifyObjectSupport > kVerifyObjectModeFast) {
+ // Slow object verification, try the heap right away.
+ Runtime::Current()->GetHeap()->VerifyObjectBody(obj);
+ } else {
+ // Fast object verification, only call the heap if our quick sanity tests fail. The heap will
+ // print the diagnostic message.
+ bool failed = !IsAligned<kObjectAlignment>(obj);
+ if (!failed) {
+ mirror::Class* c = obj->GetClass<kVerifyNone>();
+ failed = failed || c == nullptr;
+ failed = failed ||!IsAligned<kObjectAlignment>(c);
+ failed = failed ||!VerifyClassClass(c);
+ }
+ if (UNLIKELY(failed)) {
+ Runtime::Current()->GetHeap()->VerifyObjectBody(obj);
+ }
+ }
+ }
+}
+
+inline bool VerifyClassClass(mirror::Class* c) {
+ if (UNLIKELY(c == nullptr)) {
+ return false;
+ }
+ // Note: We pass in flags to ensure that the accessors don't call VerifyObject.
+ mirror::Class* c_c = c->GetClass<kVerifyNone>();
+ return c_c != nullptr && c_c == c_c->GetClass<kVerifyNone>();
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_VERIFY_OBJECT_INL_H_
diff --git a/runtime/verify_object.h b/runtime/verify_object.h
new file mode 100644
index 0000000..b39df4a
--- /dev/null
+++ b/runtime/verify_object.h
@@ -0,0 +1,62 @@
+/*
+ * 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_RUNTIME_VERIFY_OBJECT_H_
+#define ART_RUNTIME_VERIFY_OBJECT_H_
+
+#include "locks.h"
+
+#include <stdint.h>
+
+namespace art {
+
+namespace mirror {
+ class Class;
+ class Object;
+} // namespace mirror
+
+// How we want to sanity check the heap's correctness.
+enum VerifyObjectMode {
+ kVerifyObjectModeDisabled, // Heap verification is disabled.
+ kVerifyObjectModeFast, // Sanity heap accesses quickly by using VerifyClassClass.
+ kVerifyObjectModeAll // Sanity heap accesses thoroughly.
+};
+
+enum VerifyObjectFlags {
+ kVerifyNone = 0x0,
+ // Verify self when we are doing an operation.
+ kVerifyThis = 0x1,
+ // Verify reads from objects.
+ kVerifyReads = 0x2,
+ // Verify writes to objects.
+ kVerifyWrites = 0x4,
+ // Verify all things.
+ kVerifyAll = kVerifyThis | kVerifyReads | kVerifyWrites,
+};
+
+static constexpr bool kVerifyStack = kIsDebugBuild;
+static constexpr VerifyObjectFlags kDefaultVerifyFlags = kVerifyNone;
+static constexpr VerifyObjectMode kVerifyObjectSupport =
+ kDefaultVerifyFlags != 0 ? kVerifyObjectModeFast : kVerifyObjectModeDisabled;
+
+ALWAYS_INLINE inline void VerifyObject(mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS;
+
+// Check that c.getClass() == c.getClass().getClass().
+ALWAYS_INLINE inline bool VerifyClassClass(mirror::Class* c) NO_THREAD_SAFETY_ANALYSIS;
+
+} // namespace art
+
+#endif // ART_RUNTIME_VERIFY_OBJECT_H_
diff --git a/runtime/vmap_table.h b/runtime/vmap_table.h
index abc50b9..2fbaebe 100644
--- a/runtime/vmap_table.h
+++ b/runtime/vmap_table.h
@@ -25,6 +25,10 @@
class VmapTable {
public:
+ // For efficient encoding of special values, entries are adjusted by 2.
+ static constexpr uint16_t kEntryAdjustment = 2u;
+ static constexpr uint16_t kAdjustedFpMarker = static_cast<uint16_t>(0xffffu + kEntryAdjustment);
+
explicit VmapTable(const uint8_t* table) : table_(table) {
}
@@ -33,11 +37,11 @@
const uint8_t* table = table_;
size_t size = DecodeUnsignedLeb128(&table);
CHECK_LT(n, size);
- uint16_t entry = DecodeUnsignedLeb128(&table);
+ uint16_t adjusted_entry = DecodeUnsignedLeb128(&table);
for (size_t i = 0; i < n; ++i) {
- entry = DecodeUnsignedLeb128(&table);
+ adjusted_entry = DecodeUnsignedLeb128(&table);
}
- return entry;
+ return adjusted_entry - kEntryAdjustment;
}
size_t Size() const {
@@ -58,16 +62,17 @@
bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
bool in_floats = false;
const uint8_t* table = table_;
+ uint16_t adjusted_vreg = vreg + kEntryAdjustment;
size_t end = DecodeUnsignedLeb128(&table);
for (size_t i = 0; i < end; ++i) {
// Stop if we find what we are are looking for.
- uint16_t entry = DecodeUnsignedLeb128(&table);
- if ((entry == vreg) && (in_floats == is_float)) {
+ uint16_t adjusted_entry = DecodeUnsignedLeb128(&table);
+ if ((adjusted_entry == adjusted_vreg) && (in_floats == is_float)) {
*vmap_offset = i;
return true;
}
// 0xffff is the marker for LR (return PC on x86), following it are spilled float registers.
- if (entry == 0xffff) {
+ if (adjusted_entry == kAdjustedFpMarker) {
in_floats = true;
}
}
@@ -89,7 +94,7 @@
if (UNLIKELY(is_float)) {
const uint8_t* table = table_;
DecodeUnsignedLeb128(&table); // Skip size.
- while (DecodeUnsignedLeb128(&table) != 0xffff) {
+ while (DecodeUnsignedLeb128(&table) != kAdjustedFpMarker) {
matches++;
}
matches++;
diff --git a/test/030-bad-finalizer/expected.txt b/test/030-bad-finalizer/expected.txt
index 88b1896..ee9cfff 100644
--- a/test/030-bad-finalizer/expected.txt
+++ b/test/030-bad-finalizer/expected.txt
@@ -1,7 +1,4 @@
-Constructed object.
-Nulled. Requestion gc.
+About to null reference and request GC.
Finalizer started and spinning...
Finalizer done spinning.
Finalizer sleeping forever now.
-Requesting another GC.
-Requesting another GC.
diff --git a/test/030-bad-finalizer/src/BadFinalizer.java b/test/030-bad-finalizer/src/BadFinalizer.java
deleted file mode 100644
index 6911a02..0000000
--- a/test/030-bad-finalizer/src/BadFinalizer.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-/**
- * Class with a bad finalizer.
- */
-public class BadFinalizer {
- public static void snooze(int ms) {
- try {
- Thread.sleep(ms);
- } catch (InterruptedException ie) {
- System.out.println("Snooze: " + ie.getMessage());
- }
- }
-
- protected void finalize() {
- System.out.println("Finalizer started and spinning...");
- int j = 0;
-
- /* spin for a bit */
- long start, end;
- start = System.nanoTime();
- for (int i = 0; i < 1000000; i++)
- j++;
- end = System.nanoTime();
- System.out.println("Finalizer done spinning.");
-
- System.out.println("Finalizer sleeping forever now.");
- while (true) {
- snooze(10000);
- }
- }
-}
diff --git a/test/030-bad-finalizer/src/Main.java b/test/030-bad-finalizer/src/Main.java
index 330e344..942ee25 100644
--- a/test/030-bad-finalizer/src/Main.java
+++ b/test/030-bad-finalizer/src/Main.java
@@ -21,19 +21,47 @@
public static void main(String[] args) {
BadFinalizer bf = new BadFinalizer();
- System.out.println("Constructed object.");
+ System.out.println("About to null reference and request GC.");
bf = null;
-
- System.out.println("Nulled. Requestion gc.");
Runtime.getRuntime().gc();
for (int i = 0; i < 8; i++) {
- BadFinalizer.snooze(4000);
- System.out.println("Requesting another GC.");
+ snooze(4000);
Runtime.getRuntime().gc();
}
- System.out.println("Done waiting.");
+ System.out.println("UNREACHABLE");
System.exit(0);
}
+
+ public static void snooze(int ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException ie) {
+ }
+ }
+
+ /**
+ * Class with a bad finalizer.
+ */
+ public static class BadFinalizer {
+ protected void finalize() {
+ System.out.println("Finalizer started and spinning...");
+ int j = 0;
+
+ /* spin for a bit */
+ long start, end;
+ start = System.nanoTime();
+ for (int i = 0; i < 1000000; i++) {
+ j++;
+ }
+ end = System.nanoTime();
+ System.out.println("Finalizer done spinning.");
+
+ System.out.println("Finalizer sleeping forever now.");
+ while (true) {
+ snooze(10000);
+ }
+ }
+ }
}
diff --git a/test/036-finalizer/expected.txt b/test/036-finalizer/expected.txt
index f9b29b0..a2a74fc 100644
--- a/test/036-finalizer/expected.txt
+++ b/test/036-finalizer/expected.txt
@@ -1,14 +1,13 @@
-wimp: wahoo
+wimp: [FinalizerTest message=wahoo, finalized=false]
gc
-finalizer executed: wahoo
wimp: null
finalize
wimp: null
sleep
-reborn: wahoo
+reborn: [FinalizerTest message=wahoo, finalized=true]
wimp: null
reset reborn
gc + finalize
sleep
-reborn: nothing
+reborn: [FinalizerTest message=nothing, finalized=false]
wimp: null
diff --git a/test/036-finalizer/src/FinalizerTest.java b/test/036-finalizer/src/FinalizerTest.java
deleted file mode 100644
index b0d014d..0000000
--- a/test/036-finalizer/src/FinalizerTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-import java.lang.ref.WeakReference;
-
-public class FinalizerTest {
- public static FinalizerTest mNothing = new FinalizerTest("nothing");
- public static FinalizerTest mReborn = mNothing;
-
- public String mMsg = "default";
-
- public FinalizerTest(String msg) {
- mMsg = msg;
- }
-
- public String toString() {
- return mMsg;
- }
-
- protected void finalize() {
- System.out.println("finalizer executed: " + mMsg);
- mReborn = this;
- }
-}
diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java
index 6195aff..328425f 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -15,6 +15,8 @@
*/
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
/**
* Some finalizer tests.
@@ -31,18 +33,19 @@
}
}
- public static WeakReference makeRef() {
+ public static WeakReference<FinalizerTest> makeRef() {
/*
* Make ft in another thread, so there is no danger of
* a conservative reference leaking onto the main thread's
* stack.
*/
- final WeakReference[] wimp = new WeakReference[1];
+ final List<WeakReference<FinalizerTest>> wimp =
+ new ArrayList<WeakReference<FinalizerTest>>();
Thread t = new Thread() {
public void run() {
FinalizerTest ft = new FinalizerTest("wahoo");
- wimp[0] = new WeakReference(ft);
+ wimp.add(new WeakReference<FinalizerTest>(ft));
ft = null;
}
};
@@ -55,10 +58,10 @@
throw new RuntimeException(ie);
}
- return wimp[0];
+ return wimp.get(0);
}
- public static String wimpString(final WeakReference wimp) {
+ public static String wimpString(final WeakReference<FinalizerTest> wimp) {
/*
* Do the work in another thread, so there is no danger of a
* conservative reference to ft leaking onto the main thread's
@@ -68,7 +71,7 @@
final String[] s = new String[1];
Thread t = new Thread() {
public void run() {
- Object ref = wimp.get();
+ FinalizerTest ref = wimp.get();
if (ref != null) {
s[0] = ref.toString();
}
@@ -87,7 +90,7 @@
}
public static void main(String[] args) {
- WeakReference wimp = makeRef();
+ WeakReference<FinalizerTest> wimp = makeRef();
System.out.println("wimp: " + wimpString(wimp));
@@ -118,4 +121,26 @@
System.out.println("reborn: " + FinalizerTest.mReborn);
System.out.println("wimp: " + wimpString(wimp));
}
+
+ public static class FinalizerTest {
+ public static FinalizerTest mNothing = new FinalizerTest("nothing");
+ public static FinalizerTest mReborn = mNothing;
+
+ private final String message;
+ private boolean finalized = false;
+
+ public FinalizerTest(String message) {
+ this.message = message;
+ }
+
+ public String toString() {
+ return "[FinalizerTest message=" + message +
+ ", finalized=" + finalized + "]";
+ }
+
+ protected void finalize() {
+ finalized = true;
+ mReborn = this;
+ }
+ }
}
diff --git a/test/048-server-socket/expected.txt b/test/048-server-socket/expected.txt
deleted file mode 100644
index 23c3e84..0000000
--- a/test/048-server-socket/expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-opened!
-closed!
-reopened!
-done
diff --git a/test/048-server-socket/info.txt b/test/048-server-socket/info.txt
deleted file mode 100644
index 08127da..0000000
--- a/test/048-server-socket/info.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a miscellaneous test that was imported into the new-at-the-time
-runtime test framework. The test is intended to exercise basic features,
-and as such cannot be build on top of junit, since failure of such basic
-features might disrupt junit.
-
-TODO: Real description goes here.
diff --git a/test/048-server-socket/src/Main.java b/test/048-server-socket/src/Main.java
deleted file mode 100644
index 5b287ca..0000000
--- a/test/048-server-socket/src/Main.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-import java.net.ServerSocket;
-import java.io.IOException;
-
-
-/**
- * Quick server socket test.
- */
-public class Main {
- private static void snooze(int sec) {
- try {
- Thread.sleep(sec * 1000);
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- }
- }
-
- public static void main(String[] args) {
- ServerSocket socket;
-
- try {
- socket = new ServerSocket(7890);
- } catch (IOException ioe) {
- System.out.println("couldn't open socket " + ioe.getMessage());
- return;
- }
-
- System.out.println("opened!");
- snooze(1);
-
- try {
- socket.close();
- } catch (IOException ioe) {
- System.out.println("couldn't close socket " + ioe.getMessage());
- return;
- }
-
- System.out.println("closed!");
- snooze(1);
-
- try {
- socket = new ServerSocket(7890);
- } catch (IOException ioe) {
- System.out.println("couldn't reopen socket " + ioe.getMessage());
- return;
- }
-
- System.out.println("reopened!");
- System.out.println("done");
- }
-}
diff --git a/test/083-compiler-regressions/expected.txt b/test/083-compiler-regressions/expected.txt
index e2846dd..57065ef 100644
--- a/test/083-compiler-regressions/expected.txt
+++ b/test/083-compiler-regressions/expected.txt
@@ -9,5 +9,7 @@
wideGetterSetterTest passes
wideIdentityTest passes
returnConstantTest passes
+LVNTests.testNPE1 passes
+LVNTests.testNPE2 passes
longDivTest passes
longModTest passes
diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java
index 3307e50..6829388 100644
--- a/test/083-compiler-regressions/src/Main.java
+++ b/test/083-compiler-regressions/src/Main.java
@@ -40,6 +40,8 @@
wideGetterSetterTest();
wideIdentityTest();
returnConstantTest();
+ LVNTests.testNPE1();
+ LVNTests.testNPE2();
ZeroTests.longDivTest();
ZeroTests.longModTest();
}
@@ -8440,6 +8442,52 @@
public long wideIdent5(int a6, int a5, int a4, int a3, int a2, long a1) {
return a1;
- }
+ }
+}
+class LVNTests {
+ private LVNTests link = null;
+ private int value = 0;
+
+ private void setLink(LVNTests l) {
+ link = l;
+ }
+
+ private static void causeNPE1(LVNTests lhs, LVNTests rhs) {
+ LVNTests link1 = lhs.link;
+ rhs.link = null;
+ LVNTests link2 = lhs.link;
+ int value1 = link1.value;
+ int value2 = link2.value;
+ System.out.println("LVNTests.testNPE1 fails with " + value1 + " and " + value2);
+ }
+
+ public static void testNPE1() {
+ LVNTests t = new LVNTests();
+ t.link = new LVNTests();
+ try {
+ causeNPE1(t, t);
+ } catch (NullPointerException e) {
+ System.out.println("LVNTests.testNPE1 passes");
+ }
+ }
+
+ private static void causeNPE2(LVNTests lhs, LVNTests rhs) {
+ LVNTests link1 = lhs.link;
+ rhs.setLink(null);
+ LVNTests link2 = lhs.link;
+ int value1 = link1.value;
+ int value2 = link2.value;
+ System.out.println("LVNTests.testNPE2 fails with " + value1 + " and " + value2);
+ }
+
+ public static void testNPE2() {
+ LVNTests t = new LVNTests();
+ t.link = new LVNTests();
+ try {
+ causeNPE2(t, t);
+ } catch (NullPointerException e) {
+ System.out.println("LVNTests.testNPE2 passes");
+ }
+ }
}