Merge "ART: Bitvector extensions for dumping and size handling"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 4b655b5..12af0c4 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -17,6 +17,8 @@
LOCAL_PATH := art
RUNTIME_GTEST_COMMON_SRC_FILES := \
+ runtime/arch/arch_test.cc \
+ runtime/arch/stub_test.cc \
runtime/barrier_test.cc \
runtime/base/bit_field_test.cc \
runtime/base/bit_vector_test.cc \
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index fdf09a5..8bba84a 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -132,34 +132,24 @@
class CommonCompilerTest : public CommonRuntimeTest {
public:
- static void MakeExecutable(const std::vector<uint8_t>& code) {
- CHECK_NE(code.size(), 0U);
- MakeExecutable(&code[0], code.size());
- }
-
// Create an OatMethod based on pointers (for unit tests).
OatFile::OatMethod CreateOatMethod(const void* code,
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- const uint8_t* mapping_table,
- const uint8_t* vmap_table,
const uint8_t* gc_map) {
+ CHECK(code != nullptr);
const byte* base;
- uint32_t code_offset, mapping_table_offset, vmap_table_offset, gc_map_offset;
- if (mapping_table == nullptr && vmap_table == nullptr && gc_map == nullptr) {
+ uint32_t code_offset, gc_map_offset;
+ if (gc_map == nullptr) {
base = reinterpret_cast<const byte*>(code); // Base of data points at code.
base -= kPointerSize; // Move backward so that code_offset != 0.
code_offset = kPointerSize;
- mapping_table_offset = 0;
- vmap_table_offset = 0;
gc_map_offset = 0;
} else {
// TODO: 64bit support.
base = nullptr; // Base of data in oat file, ie 0.
code_offset = PointerToLowMemUInt32(code);
- mapping_table_offset = PointerToLowMemUInt32(mapping_table);
- vmap_table_offset = PointerToLowMemUInt32(vmap_table);
gc_map_offset = PointerToLowMemUInt32(gc_map);
}
return OatFile::OatMethod(base,
@@ -167,8 +157,6 @@
frame_size_in_bytes,
core_spill_mask,
fp_spill_mask,
- mapping_table_offset,
- vmap_table_offset,
gc_map_offset);
}
@@ -185,19 +173,44 @@
}
if (compiled_method != nullptr) {
const std::vector<uint8_t>* code = compiled_method->GetQuickCode();
- if (code == nullptr) {
+ const void* code_ptr;
+ if (code != nullptr) {
+ uint32_t code_size = code->size();
+ CHECK_NE(0u, code_size);
+ const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
+ uint32_t vmap_table_offset = vmap_table.empty() ? 0u
+ : sizeof(OatMethodHeader) + vmap_table.size();
+ const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
+ uint32_t mapping_table_offset = mapping_table.empty() ? 0u
+ : sizeof(OatMethodHeader) + vmap_table.size() + mapping_table.size();
+ OatMethodHeader method_header(vmap_table_offset, mapping_table_offset, code_size);
+
+ header_code_and_maps_chunks_.push_back(std::vector<uint8_t>());
+ std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back();
+ size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size();
+ size_t code_offset = compiled_method->AlignCode(size - code_size);
+ size_t padding = code_offset - (size - code_size);
+ chunk->reserve(padding + size);
+ chunk->resize(sizeof(method_header));
+ memcpy(&(*chunk)[0], &method_header, sizeof(method_header));
+ chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end());
+ chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end());
+ chunk->insert(chunk->begin(), padding, 0);
+ chunk->insert(chunk->end(), code->begin(), code->end());
+ CHECK_EQ(padding + size, chunk->size());
+ code_ptr = &(*chunk)[code_offset];
+ } else {
code = compiled_method->GetPortableCode();
+ code_ptr = &(*code)[0];
}
- MakeExecutable(*code);
- const void* method_code = CompiledMethod::CodePointer(&(*code)[0],
+ MakeExecutable(code_ptr, code->size());
+ const void* method_code = CompiledMethod::CodePointer(code_ptr,
compiled_method->GetInstructionSet());
LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code;
OatFile::OatMethod oat_method = CreateOatMethod(method_code,
compiled_method->GetFrameSizeInBytes(),
compiled_method->GetCoreSpillMask(),
compiled_method->GetFpSpillMask(),
- &compiled_method->GetMappingTable()[0],
- &compiled_method->GetVmapTable()[0],
nullptr);
oat_method.LinkMethod(method);
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
@@ -211,8 +224,6 @@
kStackAlignment,
0,
0,
- nullptr,
- nullptr,
nullptr);
oat_method.LinkMethod(method);
method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
@@ -230,8 +241,6 @@
sirt_size,
callee_save_method->GetCoreSpillMask(),
callee_save_method->GetFpSpillMask(),
- nullptr,
- nullptr,
nullptr);
oat_method.LinkMethod(method);
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
@@ -436,6 +445,9 @@
private:
UniquePtr<MemMap> image_reservation_;
+
+ // Chunks must not move their storage after being created - use the node-based std::list.
+ std::list<std::vector<uint8_t> > header_code_and_maps_chunks_;
};
} // namespace art
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 51419f4..937e258 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -42,6 +42,11 @@
MIR* mir;
for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ // Skip pass if BB has MIR without SSA representation.
+ if (mir->ssa_rep == NULL) {
+ return;
+ }
+
uint64_t df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
DecodedInstruction *d_insn = &mir->dalvikInsn;
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index aa9b2a4..313174d 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -19,12 +19,17 @@
#include "dex/quick/mir_to_lir-inl.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "mirror/array.h"
+#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
#include "verifier/method_verifier.h"
#include <functional>
namespace art {
+// Shortcuts to repeatedly used long types.
+typedef mirror::ObjectArray<mirror::Object> ObjArray;
+typedef mirror::ObjectArray<mirror::Class> ClassArray;
+
/*
* This source files contains "gen" codegen routines that should
* be applicable to most targets. Only mid-level support utilities
@@ -519,8 +524,8 @@
r_base = TargetReg(kArg0);
LockTemp(r_base);
LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base);
- LoadRefDisp(r_base, mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
- sizeof(int32_t*) * field_info.StorageIndex(), r_base);
+ int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value();
+ LoadRefDisp(r_base, offset_of_field, r_base);
// r_base now points at static storage (Class*) or NULL if the type is not yet resolved.
if (!field_info.IsInitialized() &&
(mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) {
@@ -608,8 +613,8 @@
r_base = TargetReg(kArg0);
LockTemp(r_base);
LoadRefDisp(r_method, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base);
- LoadRefDisp(r_base, mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
- sizeof(int32_t*) * field_info.StorageIndex(), r_base);
+ int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value();
+ LoadRefDisp(r_base, offset_of_field, r_base);
// r_base now points at static storage (Class*) or NULL if the type is not yet resolved.
if (!field_info.IsInitialized() &&
(mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) {
@@ -865,9 +870,7 @@
int32_t dex_cache_offset =
mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value();
Load32Disp(rl_method.reg, dex_cache_offset, res_reg);
- int32_t offset_of_type =
- mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*)
- * type_idx);
+ int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
Load32Disp(res_reg, offset_of_type, rl_result.reg);
if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file,
type_idx) || SLOW_TYPE_PATH) {
@@ -914,8 +917,8 @@
void Mir2Lir::GenConstString(uint32_t string_idx, RegLocation rl_dest) {
/* NOTE: Most strings should be available at compile time */
- int32_t offset_of_string = mirror::Array::DataOffset(sizeof(mirror::String*)).Int32Value() +
- (sizeof(mirror::String*) * string_idx);
+ int32_t offset_of_string = mirror::ObjectArray<mirror::String>::OffsetOfElement(string_idx).
+ Int32Value();
if (!cu_->compiler_driver->CanAssumeStringIsPresentInDexCache(
*cu_->dex_file, string_idx) || SLOW_STRING_PATH) {
// slow path, resolve string if not in dex cache
@@ -1079,9 +1082,7 @@
LoadRefDisp(check_class, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
check_class);
LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class);
- int32_t offset_of_type =
- mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() +
- (sizeof(mirror::Class*) * type_idx);
+ int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
LoadRefDisp(check_class, offset_of_type, check_class);
}
@@ -1139,9 +1140,7 @@
LoadValueDirectFixed(rl_src, TargetReg(kArg0)); // kArg0 <= ref
LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
class_reg);
- int32_t offset_of_type =
- mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() + (sizeof(mirror::Class*)
- * type_idx);
+ int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
LoadRefDisp(class_reg, offset_of_type, class_reg);
if (!can_assume_type_is_in_dex_cache) {
// Need to test presence of type in dex cache at runtime
@@ -1275,9 +1274,7 @@
// Load dex cache entry into class_reg (kArg2)
LoadRefDisp(TargetReg(kArg1), mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
class_reg);
- int32_t offset_of_type =
- mirror::Array::DataOffset(sizeof(mirror::Class*)).Int32Value() +
- (sizeof(mirror::Class*) * type_idx);
+ int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
LoadRefDisp(class_reg, offset_of_type, class_reg);
if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx)) {
// Need to test presence of type in dex cache at runtime
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 3400b01..c35d400 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -680,14 +680,6 @@
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<kVerifyNone>(mapping_table);
-
- uint32_t vmap_table_offset = orig->GetOatVmapTableOffset();
- const byte* vmap_table = GetOatAddress(vmap_table_offset);
- 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<kVerifyNone>(reinterpret_cast<const uint8_t*>(native_gc_map));
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 766ef7b..b5d3923 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -176,7 +176,8 @@
// If this test is failing and you have to update these constants,
// it is time to update OatHeader::kOatVersion
EXPECT_EQ(80U, sizeof(OatHeader));
- EXPECT_EQ(28U, sizeof(OatMethodOffsets));
+ EXPECT_EQ(20U, sizeof(OatMethodOffsets));
+ EXPECT_EQ(12U, sizeof(OatMethodHeader));
}
TEST_F(OatTest, OatHeaderIsValid) {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 2114fe9..bbc9c3e 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -155,12 +155,15 @@
}
static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
- return oat_class->method_offsets_[method_offsets_index].mapping_table_offset_;
+ uint32_t offset = oat_class->method_headers_[method_offsets_index].mapping_table_offset_;
+ return offset == 0u ? 0u :
+ (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
}
static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset)
ALWAYS_INLINE {
- oat_class->method_offsets_[method_offsets_index].mapping_table_offset_ = offset;
+ oat_class->method_headers_[method_offsets_index].mapping_table_offset_ =
+ (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
}
static const char* Name() ALWAYS_INLINE {
@@ -174,12 +177,15 @@
}
static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
- return oat_class->method_offsets_[method_offsets_index].vmap_table_offset_;
+ uint32_t offset = oat_class->method_headers_[method_offsets_index].vmap_table_offset_;
+ return offset == 0u ? 0u :
+ (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
}
static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset)
ALWAYS_INLINE {
- oat_class->method_offsets_[method_offsets_index].vmap_table_offset_ = offset;
+ oat_class->method_headers_[method_offsets_index].vmap_table_offset_ =
+ (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
}
static const char* Name() ALWAYS_INLINE {
@@ -368,17 +374,22 @@
}
}
+ DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size());
+ OatMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_];
+ method_header->code_size_ = code_size;
+
// Deduplicate code arrays.
- auto code_iter = dedupe_map_.find(quick_code);
+ auto code_iter = dedupe_map_.find(compiled_method);
if (code_iter != dedupe_map_.end()) {
quick_code_offset = code_iter->second;
+ FixupMethodHeader(method_header, quick_code_offset - thumb_offset);
} else {
- dedupe_map_.Put(quick_code, quick_code_offset);
- OatMethodHeader method_header(code_size);
- offset_ += sizeof(method_header); // Method header is prepended before code.
- writer_->oat_header_->UpdateChecksum(&method_header, sizeof(method_header));
- offset_ += code_size;
+ dedupe_map_.Put(compiled_method, quick_code_offset);
+ FixupMethodHeader(method_header, quick_code_offset - thumb_offset);
+ writer_->oat_header_->UpdateChecksum(method_header, sizeof(*method_header));
+ offset_ += sizeof(*method_header); // Method header is prepended before code.
writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
+ offset_ += code_size;
}
}
frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
@@ -420,9 +431,22 @@
}
private:
+ static void FixupMethodHeader(OatMethodHeader* method_header, uint32_t code_offset) {
+ // The code offset was 0 when the mapping/vmap table offset was set, so it's set
+ // to 0-offset and we need to adjust it by code_offset.
+ if (method_header->mapping_table_offset_ != 0u) {
+ method_header->mapping_table_offset_ += code_offset;
+ DCHECK_LT(method_header->mapping_table_offset_, code_offset);
+ }
+ if (method_header->vmap_table_offset_ != 0u) {
+ method_header->vmap_table_offset_ += code_offset;
+ DCHECK_LT(method_header->vmap_table_offset_, code_offset);
+ }
+ }
+
// Deduplication is already done on a pointer basis by the compiler driver,
// so we can simply compare the pointers to find out if things are duplicated.
- SafeMap<const std::vector<uint8_t>*, uint32_t> dedupe_map_;
+ SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_;
};
template <typename DataAccess>
@@ -477,7 +501,7 @@
OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
- OatMethodOffsets offsets(0u, kStackAlignment, 0u, 0u, 0u, 0u, 0u);
+ OatMethodOffsets offsets(0u, kStackAlignment, 0u, 0u, 0u);
if (compiled_method != nullptr) {
DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
offsets = oat_class->method_offsets_[method_offsets_index_];
@@ -511,8 +535,6 @@
offsets.frame_size_in_bytes_ = callee_save_method->GetFrameSizeInBytes() + sirt_size;
offsets.core_spill_mask_ = callee_save_method->GetCoreSpillMask();
offsets.fp_spill_mask_ = callee_save_method->GetFpSpillMask();
- DCHECK_EQ(offsets.mapping_table_offset_, 0u);
- DCHECK_EQ(offsets.vmap_table_offset_, 0u);
DCHECK_EQ(offsets.gc_map_offset_, 0u);
}
@@ -528,10 +550,8 @@
method->SetFrameSizeInBytes(offsets.frame_size_in_bytes_);
method->SetCoreSpillMask(offsets.core_spill_mask_);
method->SetFpSpillMask(offsets.fp_spill_mask_);
- method->SetOatMappingTableOffset(offsets.mapping_table_offset_);
// Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking.
method->SetQuickOatCodeOffset(offsets.code_offset_);
- method->SetOatVmapTableOffset(offsets.vmap_table_offset_);
method->SetOatNativeGcMapOffset(offsets.gc_map_offset_);
return true;
@@ -584,7 +604,7 @@
offset_ + sizeof(OatMethodHeader) + compiled_method->CodeDelta())
<< PrettyMethod(it.GetMemberIndex(), *dex_file_);
if (method_offsets.code_offset_ >= offset_) {
- OatMethodHeader method_header(code_size);
+ const OatMethodHeader& method_header = oat_class->method_headers_[method_offsets_index_];
if (!out->WriteFully(&method_header, sizeof(method_header))) {
ReportWriteFailure("method header", it);
return false;
@@ -1153,6 +1173,7 @@
status_ = status;
method_offsets_.resize(num_non_null_compiled_methods);
+ method_headers_.resize(num_non_null_compiled_methods);
uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_);
if (type_ == kOatClassSomeCompiled) {
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 1abacd8..7cdd532 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -225,12 +225,13 @@
// not is kOatClassBitmap, the bitmap will be NULL.
BitVector* method_bitmap_;
- // OatMethodOffsets for each CompiledMethod present in the
- // OatClass. Note that some may be missing if
+ // OatMethodOffsets and OatMethodHeaders for each CompiledMethod
+ // present in the OatClass. Note that some may be missing if
// OatClass::compiled_methods_ contains NULL values (and
// oat_method_offsets_offsets_from_oat_class_ should contain 0
// values in this case).
std::vector<OatMethodOffsets> method_offsets_;
+ std::vector<OatMethodHeader> method_headers_;
private:
DISALLOW_COPY_AND_ASSIGN(OatClass);
@@ -299,6 +300,22 @@
uint32_t size_oat_class_method_bitmaps_;
uint32_t size_oat_class_method_offsets_;
+ struct CodeOffsetsKeyComparator {
+ bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
+ if (lhs->GetQuickCode() != rhs->GetQuickCode()) {
+ return lhs->GetQuickCode() < rhs->GetQuickCode();
+ }
+ // If the code is the same, all other fields are likely to be the same as well.
+ if (UNLIKELY(&lhs->GetMappingTable() != &rhs->GetMappingTable())) {
+ return &lhs->GetMappingTable() < &rhs->GetMappingTable();
+ }
+ if (UNLIKELY(&lhs->GetVmapTable() != &rhs->GetVmapTable())) {
+ return &lhs->GetVmapTable() < &rhs->GetVmapTable();
+ }
+ return false;
+ }
+ };
+
DISALLOW_COPY_AND_ASSIGN(OatWriter);
};
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 7d02c7c..9507e12 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -1493,7 +1493,7 @@
}
void X86_64Assembler::EmitOptionalRex32(CpuRegister reg) {
- EmitOptionalRex(false, false, reg.NeedsRex(), false, false);
+ EmitOptionalRex(false, false, false, false, reg.NeedsRex());
}
void X86_64Assembler::EmitOptionalRex32(CpuRegister dst, CpuRegister src) {
@@ -1540,8 +1540,9 @@
}
void X86_64Assembler::EmitRex64(CpuRegister reg) {
- EmitOptionalRex(false, true, reg.NeedsRex(), false, false);
+ EmitOptionalRex(false, true, false, false, reg.NeedsRex());
}
+
void X86_64Assembler::EmitRex64(CpuRegister dst, CpuRegister src) {
EmitOptionalRex(false, true, dst.NeedsRex(), false, src.NeedsRex());
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ac76c35..d3e56da 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -110,7 +110,7 @@
UsageError(" Example: --oat-file=/system/framework/boot.oat");
UsageError("");
UsageError(" --oat-fd=<number>: specifies the oat output destination via a file descriptor.");
- UsageError(" Example: --oat-file=/system/framework/boot.oat");
+ UsageError(" Example: --oat-fd=6");
UsageError("");
UsageError(" --oat-location=<oat-name>: specifies a symbolic name for the file corresponding");
UsageError(" to the file descriptor specified by --oat-fd.");
@@ -909,7 +909,6 @@
profile_file = option.substr(strlen("--profile-file=")).data();
VLOG(compiler) << "dex2oat: profile file is " << profile_file;
} else if (option == "--no-profile-file") {
- LOG(INFO) << "dex2oat: no profile file supplied (explictly)";
// No profile
} else if (option == "--print-pass-names") {
PassDriver::PrintPassNames();
@@ -1079,7 +1078,7 @@
}
timings.StartSplit("dex2oat Setup");
- LOG(INFO) << "dex2oat: " << CommandLine();
+ LOG(INFO) << CommandLine();
Runtime::Options runtime_options;
std::vector<const DexFile*> boot_class_path;
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index 68e77d4..5cc6acf 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -145,6 +145,9 @@
}
} while (have_prefixes);
uint8_t rex = (supports_rex_ && (*instr >= 0x40) && (*instr <= 0x4F)) ? *instr : 0;
+ if (rex != 0) {
+ instr++;
+ }
bool has_modrm = false;
bool reg_is_opcode = false;
size_t immediate_bytes = 0;
@@ -735,7 +738,7 @@
std::ostringstream args;
if (reg_in_opcode) {
DCHECK(!has_modrm);
- DumpReg(args, rex, *instr & 0x7, false, prefix[2], GPR);
+ DumpBaseReg(args, rex, *instr & 0x7);
}
instr++;
uint32_t address_bits = 0;
@@ -746,14 +749,18 @@
uint8_t reg_or_opcode = (modrm >> 3) & 7;
uint8_t rm = modrm & 7;
std::ostringstream address;
- if (mod == 0 && rm == 5) { // fixed address
- address_bits = *reinterpret_cast<const uint32_t*>(instr);
- address << StringPrintf("[0x%x]", address_bits);
+ if (mod == 0 && rm == 5) {
+ if (!supports_rex_) { // Absolute address.
+ address_bits = *reinterpret_cast<const uint32_t*>(instr);
+ address << StringPrintf("[0x%x]", address_bits);
+ } else { // 64-bit RIP relative addressing.
+ address << StringPrintf("[RIP + 0x%x]", *reinterpret_cast<const uint32_t*>(instr));
+ }
instr += 4;
} else if (rm == 4 && mod != 3) { // SIB
uint8_t sib = *instr;
instr++;
- uint8_t ss = (sib >> 6) & 3;
+ uint8_t scale = (sib >> 6) & 3;
uint8_t index = (sib >> 3) & 7;
uint8_t base = sib & 7;
address << "[";
@@ -765,11 +772,22 @@
}
if (index != 4) {
DumpIndexReg(address, rex, index);
- if (ss != 0) {
- address << StringPrintf(" * %d", 1 << ss);
+ if (scale != 0) {
+ address << StringPrintf(" * %d", 1 << scale);
}
}
- if (mod == 1) {
+ if (mod == 0) {
+ if (base == 5) {
+ if (index != 4) {
+ address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(instr));
+ } else {
+ // 64-bit low 32-bit absolute address, redundant absolute address encoding on 32-bit.
+ address_bits = *reinterpret_cast<const uint32_t*>(instr);
+ address << StringPrintf("%d", address_bits);
+ }
+ instr += 4;
+ }
+ } else if (mod == 1) {
address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(instr));
instr++;
} else if (mod == 2) {
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 5054f96..1a67952 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -44,6 +44,7 @@
#include "mirror/object_array-inl.h"
#include "noop_compiler_callbacks.h"
#include "oat.h"
+#include "oat_file-inl.h"
#include "object_utils.h"
#include "os.h"
#include "runtime.h"
diff --git a/runtime/Android.mk b/runtime/Android.mk
index d433fd5..bc971a9 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -36,10 +36,10 @@
base/unix_file/string_file.cc \
check_jni.cc \
catch_block_stack_visitor.cc \
- catch_finder.cc \
class_linker.cc \
common_throws.cc \
debugger.cc \
+ deoptimize_stack_visitor.cc \
dex_file.cc \
dex_file_verifier.cc \
dex_instruction.cc \
@@ -129,6 +129,7 @@
os_linux.cc \
parsed_options.cc \
primitive.cc \
+ quick_exception_handler.cc \
quick/inline_method_analyser.cc \
reference_table.cc \
reflection.cc \
diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc
new file mode 100644
index 0000000..47c6d28
--- /dev/null
+++ b/runtime/arch/arch_test.cc
@@ -0,0 +1,498 @@
+/*
+ * 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 <stdint.h>
+
+#include "common_runtime_test.h"
+#include "mirror/art_method.h"
+
+namespace art {
+
+class ArchTest : public CommonRuntimeTest {
+ protected:
+ static void CheckFrameSize(InstructionSet isa, Runtime::CalleeSaveType type, uint32_t save_size)
+ NO_THREAD_SAFETY_ANALYSIS {
+ Runtime* r = Runtime::Current();
+
+ Thread* t = Thread::Current();
+ t->TransitionFromSuspendedToRunnable(); // So we can create callee-save methods.
+
+ mirror::ArtMethod* save_method = r->CreateCalleeSaveMethod(isa, type);
+ EXPECT_EQ(save_method->GetFrameSizeInBytes(), save_size) << "Expected and real size differs for "
+ << type << " core spills=" << std::hex << save_method->GetCoreSpillMask() << " fp spills="
+ << save_method->GetFpSpillMask() << std::dec;
+
+ t->TransitionFromRunnableToSuspended(ThreadState::kNative); // So we can shut down.
+ }
+};
+
+
+TEST_F(ArchTest, ARM) {
+#include "arch/arm/asm_support_arm.h"
+#undef ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_
+
+
+#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kArm, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for SaveAll";
+#endif
+#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kArm, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for RefsOnly";
+#endif
+#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kArm, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for RefsAndArgs";
+#endif
+
+
+#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef THREAD_SELF_OFFSET
+#undef THREAD_SELF_OFFSET
+#endif
+#ifdef THREAD_CARD_TABLE_OFFSET
+#undef THREAD_CARD_TABLE_OFFSET
+#endif
+#ifdef THREAD_EXCEPTION_OFFSET
+#undef THREAD_EXCEPTION_OFFSET
+#endif
+#ifdef THREAD_ID_OFFSET
+#undef THREAD_ID_OFFSET
+#endif
+#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#endif
+}
+
+
+TEST_F(ArchTest, ARM64) {
+#include "arch/arm64/asm_support_arm64.h"
+#undef ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_
+
+
+#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kArm64, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for SaveAll";
+#endif
+#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kArm64, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for RefsOnly";
+#endif
+#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kArm64, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for RefsAndArgs";
+#endif
+
+
+#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef THREAD_SELF_OFFSET
+#undef THREAD_SELF_OFFSET
+#endif
+#ifdef THREAD_CARD_TABLE_OFFSET
+#undef THREAD_CARD_TABLE_OFFSET
+#endif
+#ifdef THREAD_EXCEPTION_OFFSET
+#undef THREAD_EXCEPTION_OFFSET
+#endif
+#ifdef THREAD_ID_OFFSET
+#undef THREAD_ID_OFFSET
+#endif
+#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#endif
+}
+
+
+TEST_F(ArchTest, MIPS) {
+#include "arch/mips/asm_support_mips.h"
+#undef ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_H_
+
+
+#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kMips, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for SaveAll";
+#endif
+#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kMips, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for RefsOnly";
+#endif
+#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kMips, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for RefsAndArgs";
+#endif
+
+
+#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef THREAD_SELF_OFFSET
+#undef THREAD_SELF_OFFSET
+#endif
+#ifdef THREAD_CARD_TABLE_OFFSET
+#undef THREAD_CARD_TABLE_OFFSET
+#endif
+#ifdef THREAD_EXCEPTION_OFFSET
+#undef THREAD_EXCEPTION_OFFSET
+#endif
+#ifdef THREAD_ID_OFFSET
+#undef THREAD_ID_OFFSET
+#endif
+#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#endif
+}
+
+
+TEST_F(ArchTest, X86) {
+#include "arch/x86/asm_support_x86.h"
+#undef ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
+
+
+#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kX86, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for SaveAll";
+#endif
+#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kX86, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for RefsOnly";
+#endif
+#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kX86, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for RefsAndArgs";
+#endif
+
+
+#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef THREAD_SELF_OFFSET
+#undef THREAD_SELF_OFFSET
+#endif
+#ifdef THREAD_CARD_TABLE_OFFSET
+#undef THREAD_CARD_TABLE_OFFSET
+#endif
+#ifdef THREAD_EXCEPTION_OFFSET
+#undef THREAD_EXCEPTION_OFFSET
+#endif
+#ifdef THREAD_ID_OFFSET
+#undef THREAD_ID_OFFSET
+#endif
+#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#endif
+}
+
+
+TEST_F(ArchTest, X86_64) {
+#include "arch/x86_64/asm_support_x86_64.h"
+#undef ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_
+
+
+#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kX86_64, Runtime::kSaveAll, FRAME_SIZE_SAVE_ALL_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for SaveAll";
+#endif
+#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kX86_64, Runtime::kRefsOnly, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for RefsOnly";
+#endif
+#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+ CheckFrameSize(InstructionSet::kX86_64, Runtime::kRefsAndArgs, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE);
+#else
+ LOG(WARNING) << "No frame size for RefsAndArgs";
+#endif
+
+
+#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef THREAD_SELF_OFFSET
+#undef THREAD_SELF_OFFSET
+#endif
+#ifdef THREAD_CARD_TABLE_OFFSET
+#undef THREAD_CARD_TABLE_OFFSET
+#endif
+#ifdef THREAD_EXCEPTION_OFFSET
+#undef THREAD_EXCEPTION_OFFSET
+#endif
+#ifdef THREAD_ID_OFFSET
+#undef THREAD_ID_OFFSET
+#endif
+#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#endif
+}
+
+
+TEST_F(ArchTest, ThreadOffsets) {
+#if defined(__arm__)
+#include "arch/arm/asm_support_arm.h"
+#undef ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_
+#elif defined(__aarch64__)
+#include "arch/arm64/asm_support_arm64.h"
+#undef ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_
+#elif defined(__mips__)
+#include "arch/mips/asm_support_mips.h"
+#undef ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_H_
+#elif defined(__i386__)
+#include "arch/x86/asm_support_x86.h"
+#undef ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
+#elif defined(__x86_64__)
+#include "arch/x86_64/asm_support_x86_64.h"
+#undef ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_
+#else
+ // This happens for the host test.
+#ifdef __LP64__
+#include "arch/x86_64/asm_support_x86_64.h"
+#undef ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_
+#else
+#include "arch/x86/asm_support_x86.h"
+#undef ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
+#endif
+#endif
+
+ // Ugly hack, change when possible.
+#ifdef __LP64__
+#define POINTER_SIZE 8
+#else
+#define POINTER_SIZE 4
+#endif
+
+#if defined(THREAD_SELF_OFFSET)
+ ThreadOffset<POINTER_SIZE> self_offset = Thread::SelfOffset<POINTER_SIZE>();
+ EXPECT_EQ(self_offset.Int32Value(), THREAD_SELF_OFFSET);
+#else
+ LOG(INFO) << "No Thread Self Offset found.";
+#endif
+
+#if defined(THREAD_CARD_TABLE_OFFSET)
+ ThreadOffset<POINTER_SIZE> card_offset = Thread::CardTableOffset<POINTER_SIZE>();
+ EXPECT_EQ(card_offset.Int32Value(), THREAD_CARD_TABLE_OFFSET);
+#else
+ LOG(INFO) << "No Thread Card Table Offset found.";
+#endif
+
+#if defined(THREAD_EXCEPTION_OFFSET)
+ ThreadOffset<POINTER_SIZE> exc_offset = Thread::ExceptionOffset<POINTER_SIZE>();
+ EXPECT_EQ(exc_offset.Int32Value(), THREAD_EXCEPTION_OFFSET);
+#else
+ LOG(INFO) << "No Thread Exception Offset found.";
+#endif
+
+#if defined(THREAD_ID_OFFSET)
+ ThreadOffset<POINTER_SIZE> id_offset = Thread::ThinLockIdOffset<POINTER_SIZE>();
+ EXPECT_EQ(id_offset.Int32Value(), THREAD_ID_OFFSET);
+#else
+ LOG(INFO) << "No Thread ID Offset found.";
+#endif
+
+
+ // Undefine everything for the next test
+#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef THREAD_SELF_OFFSET
+#undef THREAD_SELF_OFFSET
+#endif
+#ifdef THREAD_CARD_TABLE_OFFSET
+#undef THREAD_CARD_TABLE_OFFSET
+#endif
+#ifdef THREAD_EXCEPTION_OFFSET
+#undef THREAD_EXCEPTION_OFFSET
+#endif
+#ifdef THREAD_ID_OFFSET
+#undef THREAD_ID_OFFSET
+#endif
+#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#endif
+}
+
+
+TEST_F(ArchTest, CalleeSaveMethodOffsets) {
+#if defined(__arm__)
+#include "arch/arm/asm_support_arm.h"
+#undef ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_
+#elif defined(__aarch64__)
+#include "arch/arm64/asm_support_arm64.h"
+#undef ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_
+#elif defined(__mips__)
+#include "arch/mips/asm_support_mips.h"
+#undef ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_H_
+#elif defined(__i386__)
+#include "arch/x86/asm_support_x86.h"
+#undef ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
+#elif defined(__x86_64__)
+#include "arch/x86_64/asm_support_x86_64.h"
+#undef ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_
+#else
+ // This happens for the host test.
+#ifdef __LP64__
+#include "arch/x86_64/asm_support_x86_64.h"
+#undef ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_
+#else
+#include "arch/x86/asm_support_x86.h"
+#undef ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
+#endif
+#endif
+
+
+#if defined(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET)
+ EXPECT_EQ(Runtime::GetCalleeSaveMethodOffset(Runtime::kSaveAll),
+ static_cast<size_t>(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET));
+#else
+ LOG(INFO) << "No Runtime Save-all Offset found.";
+#endif
+
+#if defined(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET)
+ EXPECT_EQ(Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsOnly),
+ static_cast<size_t>(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET));
+#else
+ LOG(INFO) << "No Runtime Refs-only Offset found.";
+#endif
+
+#if defined(RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET)
+ EXPECT_EQ(Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsAndArgs),
+ static_cast<size_t>(RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET));
+#else
+ LOG(INFO) << "No Runtime Refs-and-Args Offset found.";
+#endif
+
+
+ // Undefine everything for the next test
+#ifdef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#undef RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET
+#endif
+#ifdef THREAD_SELF_OFFSET
+#undef THREAD_SELF_OFFSET
+#endif
+#ifdef THREAD_CARD_TABLE_OFFSET
+#undef THREAD_CARD_TABLE_OFFSET
+#endif
+#ifdef THREAD_EXCEPTION_OFFSET
+#undef THREAD_EXCEPTION_OFFSET
+#endif
+#ifdef THREAD_ID_OFFSET
+#undef THREAD_ID_OFFSET
+#endif
+#ifdef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#undef FRAME_SIZE_SAVE_ALL_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_ONLY_CALLEE_SAVE
+#endif
+#ifdef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#undef FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE
+#endif
+}
+
+} // namespace art
diff --git a/runtime/arch/arm/asm_support_arm.S b/runtime/arch/arm/asm_support_arm.S
index fb85feb..594252a 100644
--- a/runtime/arch/arm/asm_support_arm.S
+++ b/runtime/arch/arm/asm_support_arm.S
@@ -19,6 +19,13 @@
#include "asm_support_arm.h"
+// Define special registers.
+
+// Register holding suspend check count down.
+#define rSUSPEND r4
+// Register holding Thread::Current().
+#define rSELF r9
+
.cfi_sections .debug_frame
.syntax unified
.arch armv7-a
diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h
index 4b64076..a73d522 100644
--- a/runtime/arch/arm/asm_support_arm.h
+++ b/runtime/arch/arm/asm_support_arm.h
@@ -19,10 +19,6 @@
#include "asm_support.h"
-// Register holding suspend check count down.
-#define rSUSPEND r4
-// Register holding Thread::Current().
-#define rSELF r9
// Offset of field Thread::tls32_.state_and_flags verified in InitCpu
#define THREAD_FLAGS_OFFSET 0
// Offset of field Thread::tls32_.thin_lock_thread_id verified in InitCpu
@@ -32,4 +28,8 @@
// Offset of field Thread::tlsPtr_.exception verified in InitCpu
#define THREAD_EXCEPTION_OFFSET 116
+#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 176
+#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 32
+#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 48
+
#endif // ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 4903732..bc80644 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -46,6 +46,11 @@
sub sp, #12 @ 3 words of space, bottom word will hold Method*
.pad #12
.cfi_adjust_cfa_offset 12
+
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 36 + 128 + 12)
+#error "SAVE_ALL_CALLEE_SAVE_FRAME(ARM) size not as expected."
+#endif
.endm
/*
@@ -66,6 +71,11 @@
sub sp, #4 @ bottom word will hold Method*
.pad #4
.cfi_adjust_cfa_offset 4
+
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 28 + 4)
+#error "REFS_ONLY_CALLEE_SAVE_FRAME(ARM) size not as expected."
+#endif
.endm
.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
@@ -114,6 +124,11 @@
sub sp, #8 @ 2 words of space, bottom word will hold Method*
.pad #8
.cfi_adjust_cfa_offset 8
+
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 40 + 8)
+#error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(ARM64) size not as expected."
+#endif
.endm
.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
@@ -492,11 +507,22 @@
blx lr
.Lcheck_assignability:
push {r0-r2, lr} @ save arguments
+ .save {r0-r2, lr}
+ .cfi_adjust_cfa_offset 16
+ .cfi_rel_offset r0, 0
+ .cfi_rel_offset r1, 4
+ .cfi_rel_offset r2, 8
+ .cfi_rel_offset lr, 12
mov r1, ip
mov r0, r3
bl artIsAssignableFromCode
cbz r0, .Lthrow_array_store_exception
pop {r0-r2, lr}
+ .cfi_restore r0
+ .cfi_restore r1
+ .cfi_restore r2
+ .cfi_restore lr
+ .cfi_adjust_cfa_offset -16
add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
str r2, [r3, r1, lsl #2]
ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
@@ -505,6 +531,11 @@
blx lr
.Lthrow_array_store_exception:
pop {r0-r2, lr}
+ .cfi_restore r0
+ .cfi_restore r1
+ .cfi_restore r2
+ .cfi_restore lr
+ .cfi_adjust_cfa_offset -16
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
mov r1, r2
mov r2, r9 @ pass Thread::Current
diff --git a/runtime/arch/arm64/asm_support_arm64.S b/runtime/arch/arm64/asm_support_arm64.S
index 634f777..9614c29 100644
--- a/runtime/arch/arm64/asm_support_arm64.S
+++ b/runtime/arch/arm64/asm_support_arm64.S
@@ -19,6 +19,19 @@
#include "asm_support_arm64.h"
+// Define special registers.
+
+// Register holding Thread::Current().
+#define xSELF x18
+// Frame Pointer
+#define xFP x29
+// Link Register
+#define xLR x30
+// Define the intraprocedural linkage temporary registers.
+#define xIP0 x16
+#define xIP1 x17
+
+
.cfi_sections .debug_frame
.macro ENTRY name
diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h
index a7e68ed..b18e415 100644
--- a/runtime/arch/arm64/asm_support_arm64.h
+++ b/runtime/arch/arm64/asm_support_arm64.h
@@ -28,15 +28,6 @@
// Offset of field Runtime::callee_save_methods_[kRefsAndArgs]
#define RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET 16
-// Register holding Thread::Current().
-#define xSELF x18
-// Frame Pointer
-#define xFP x29
-// Link Register
-#define xLR x30
-// Define the intraprocedural linkage temporary registers.
-#define xIP0 x16
-#define xIP1 x17
// Offset of field Thread::suspend_count_ verified in InitCpu
#define THREAD_FLAGS_OFFSET 0
// Offset of field Thread::card_table_ verified in InitCpu
@@ -46,4 +37,8 @@
// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu
#define THREAD_ID_OFFSET 12
+#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 368
+#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 176
+#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 304
+
#endif // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 6ce5d06..71f5bf7 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -36,6 +36,11 @@
sub sp, sp, #368
.cfi_adjust_cfa_offset 368
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 368)
+#error "SAVE_ALL_CALLEE_SAVE_FRAME(ARM64) size not as expected."
+#endif
+
// FP args
stp d1, d2, [sp, #8]
stp d2, d3, [sp, #24]
@@ -95,8 +100,61 @@
* Macro that sets up the callee save frame to conform with
* Runtime::CreateCalleeSaveMethod(kRefsOnly).
*/
+// WIP.
.macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME
- brk 0
+ adrp x9, :got:_ZN3art7Runtime9instance_E
+ ldr x9, [x9, #:got_lo12:_ZN3art7Runtime9instance_E]
+
+ // Our registers aren't intermixed - just spill in order.
+ ldr x9,[x9] // x9 = & (art::Runtime * art::Runtime.instance_) .
+
+ // x9 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] .
+ ldr x9, [x9, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET ]
+
+ sub sp, sp, #176
+ .cfi_adjust_cfa_offset 176
+
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 176)
+#error "REFS_ONLY_CALLEE_SAVE_FRAME(ARM64) size not as expected."
+#endif
+
+ // FP callee-saves
+ stp d8, d9, [sp, #8]
+ stp d10, d11, [sp, #24]
+ stp d12, d13, [sp, #40]
+ stp d14, d15, [sp, #56]
+
+ // Callee saved.
+ stp xSELF, x19, [sp, #72]
+ .cfi_rel_offset x18, 72
+ .cfi_rel_offset x19, 80
+
+ stp x20, x21, [sp, #88]
+ .cfi_rel_offset x20, 88
+ .cfi_rel_offset x21, 96
+
+ stp x22, x23, [sp, #104]
+ .cfi_rel_offset x22, 104
+ .cfi_rel_offset x23, 112
+
+ stp x24, x25, [sp, #120]
+ .cfi_rel_offset x24, 120
+ .cfi_rel_offset x25, 128
+
+ stp x26, x27, [sp, #136]
+ .cfi_rel_offset x26, 136
+ .cfi_rel_offset x27, 144
+
+ stp x28, xFP, [sp, #152] // Save FP.
+ .cfi_rel_offset x28, 152
+ .cfi_rel_offset x29, 160
+
+ str xLR, [sp, #168]
+ .cfi_rel_offset x30, 168
+
+ // Loads appropriate callee-save-method
+ str x9, [sp] // Store ArtMethod* Runtime::callee_save_methods_[kRefsAndArgs]
.endm
.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
@@ -112,6 +170,11 @@
sub sp, sp, #304
.cfi_adjust_cfa_offset 304
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 304)
+#error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(ARM64) size not as expected."
+#endif
+
stp d0, d1, [sp, #16]
stp d2, d3, [sp, #32]
stp d4, d5, [sp, #48]
@@ -325,10 +388,14 @@
DELIVER_PENDING_EXCEPTION
.endm
+// FIXME: Temporary fix for TR(XSELF).
.macro NO_ARG_RUNTIME_EXCEPTION c_name, cxx_name
.extern \cxx_name
ENTRY \c_name
- brk 0
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
+ mov x0, x19 // pass Thread::Current
+ mov x1, sp // pass SP
+ b \cxx_name // \cxx_name(Thread*, SP)
END \c_name
.endm
@@ -339,15 +406,19 @@
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context.
mov x1, x19 // pass Thread::Current.
mov x2, sp // pass SP.
- b \cxx_name // \cxx_name(Thread*, SP).
+ b \cxx_name // \cxx_name(arg, Thread*, SP).
brk 0
END \c_name
.endm
+// FIXME: Temporary fix for TR(XSELF).
.macro TWO_ARG_RUNTIME_EXCEPTION c_name, cxx_name
.extern \cxx_name
ENTRY \c_name
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
+ mov x2, x19 // pass Thread::Current
+ mov x3, sp // pass SP
+ b \cxx_name // \cxx_name(arg1, arg2, Thread*, SP)
brk 0
END \c_name
.endm
@@ -864,7 +935,58 @@
UNIMPLEMENTED art_quick_lock_object
UNIMPLEMENTED art_quick_unlock_object
-UNIMPLEMENTED art_quick_check_cast
+
+ /*
+ * Entry from managed code that calls artIsAssignableFromCode and on failure calls
+ * artThrowClassCastException.
+ */
+ .extern artThrowClassCastException
+ENTRY art_quick_check_cast
+ // Store arguments and link register
+ sub sp, sp, #32 // Stack needs to be 16b aligned on calls
+ .cfi_adjust_cfa_offset 32
+ stp x0, x1, [sp]
+ .cfi_rel_offset x0, 0
+ .cfi_rel_offset x1, 8
+ stp xSELF, xLR, [sp, #16]
+ .cfi_rel_offset x18, 16
+ .cfi_rel_offset x30, 24
+
+ // Call runtime code
+ bl artIsAssignableFromCode
+
+ // Check for exception
+ cbz x0, .Lthrow_class_cast_exception
+
+ // Restore and return
+ ldp x0, x1, [sp]
+ .cfi_restore x0
+ .cfi_restore x1
+ ldp xSELF, xLR, [sp, #16]
+ .cfi_restore x18
+ .cfi_restore x30
+ add sp, sp, #32
+ .cfi_adjust_cfa_offset -32
+ ret
+
+.Lthrow_class_cast_exception:
+ // Restore
+ ldp x0, x1, [sp]
+ .cfi_restore x0
+ .cfi_restore x1
+ ldp xSELF, xLR, [sp, #16]
+ .cfi_restore x18
+ .cfi_restore x30
+ add sp, sp, #32
+ .cfi_adjust_cfa_offset -32
+
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
+ mov x2, xSELF // pass Thread::Current
+ mov x3, sp // pass SP
+ b artThrowClassCastException // (Class*, Class*, Thread*, SP)
+ brk 0 // We should not return here...
+END art_quick_check_cast
+
UNIMPLEMENTED art_quick_aput_obj_with_null_and_bound_check
UNIMPLEMENTED art_quick_aput_obj_with_bound_check
UNIMPLEMENTED art_quick_aput_obj
diff --git a/runtime/arch/mips/asm_support_mips.S b/runtime/arch/mips/asm_support_mips.S
index d110b95..d8ec9cd 100644
--- a/runtime/arch/mips/asm_support_mips.S
+++ b/runtime/arch/mips/asm_support_mips.S
@@ -19,6 +19,14 @@
#include "asm_support_mips.h"
+// Define special registers.
+
+// Register holding suspend check count down.
+#define rSUSPEND $s0
+// Register holding Thread::Current().
+#define rSELF $s1
+
+
/* Cache alignment for function entry */
.macro ENTRY name
.type \name, %function
diff --git a/runtime/arch/mips/asm_support_mips.h b/runtime/arch/mips/asm_support_mips.h
index 36ce1b6..2b4a745 100644
--- a/runtime/arch/mips/asm_support_mips.h
+++ b/runtime/arch/mips/asm_support_mips.h
@@ -19,10 +19,6 @@
#include "asm_support.h"
-// Register holding suspend check count down.
-#define rSUSPEND $s0
-// Register holding Thread::Current().
-#define rSELF $s1
// Offset of field Thread::tls32_.state_and_flags verified in InitCpu
#define THREAD_FLAGS_OFFSET 0
// Offset of field Thread::tlsPtr_.card_table verified in InitCpu
@@ -30,4 +26,8 @@
// Offset of field Thread::tlsPtr_.exception verified in InitCpu
#define THREAD_EXCEPTION_OFFSET 116
+#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 64
+#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 64
+#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 64
+
#endif // ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_H_
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index c3ae563..95fcd73 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -34,6 +34,12 @@
.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
addiu $sp, $sp, -64
.cfi_adjust_cfa_offset 64
+
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 64)
+#error "SAVE_ALL_CALLEE_SAVE_FRAME(MIPS) size not as expected."
+#endif
+
sw $ra, 60($sp)
.cfi_rel_offset 31, 60
sw $s8, 56($sp)
@@ -68,6 +74,12 @@
.macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME
addiu $sp, $sp, -64
.cfi_adjust_cfa_offset 64
+
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 64)
+#error "REFS_ONLY_CALLEE_SAVE_FRAME(MIPS) size not as expected."
+#endif
+
sw $ra, 60($sp)
.cfi_rel_offset 31, 60
sw $s8, 56($sp)
@@ -144,6 +156,12 @@
.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
addiu $sp, $sp, -64
.cfi_adjust_cfa_offset 64
+
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 64)
+#error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(MIPS) size not as expected."
+#endif
+
sw $ra, 60($sp)
.cfi_rel_offset 31, 60
sw $s8, 56($sp)
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
new file mode 100644
index 0000000..543e695
--- /dev/null
+++ b/runtime/arch/stub_test.cc
@@ -0,0 +1,357 @@
+/*
+ * 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 "common_runtime_test.h"
+
+#include <cstdio>
+
+namespace art {
+
+
+class StubTest : public CommonRuntimeTest {
+ protected:
+ // We need callee-save methods set up in the Runtime for exceptions.
+ void SetUp() OVERRIDE {
+ // Do the normal setup.
+ CommonRuntimeTest::SetUp();
+
+ {
+ // Create callee-save methods
+ ScopedObjectAccess soa(Thread::Current());
+ for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
+ Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
+ if (!runtime_->HasCalleeSaveMethod(type)) {
+ runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(kRuntimeISA, type), type);
+ }
+ }
+ }
+ }
+
+
+ size_t Invoke3(size_t arg0, size_t arg1, size_t arg2, uintptr_t code, Thread* self) {
+ // Push a transition back into managed code onto the linked list in thread.
+ ManagedStack fragment;
+ self->PushManagedStackFragment(&fragment);
+
+ size_t result;
+#if defined(__i386__)
+ // TODO: Set the thread?
+ __asm__ __volatile__(
+ "pushl $0\n\t" // Push nullptr to terminate quick stack
+ "call *%%edi\n\t" // Call the stub
+ "addl $4, %%esp" // Pop nullptr
+ : "=a" (result)
+ // Use the result from eax
+ : "a"(arg0), "c"(arg1), "d"(arg2), "D"(code)
+ // This places code into edi, arg0 into eax, arg1 into ecx, and arg2 into edx
+ : ); // clobber.
+ // TODO: Should we clobber the other registers? EBX gets clobbered by some of the stubs,
+ // but compilation fails when declaring that.
+#elif defined(__arm__)
+ __asm__ __volatile__(
+ "push {r1-r2,r9, lr}\n\t" // Save the link and thread register
+ ".cfi_adjust_cfa_offset 16\n\t"
+ "mov r0, %[arg0]\n\t" // Set arg0-arg2
+ "mov r1, %[arg1]\n\t" // TODO: Any way to use constraints like on x86?
+ "mov r2, %[arg2]\n\t"
+ // Use r9 last as we don't know whether it was used for arg0-arg2
+ "mov r9, #0\n\t" // Push nullptr to terminate stack
+ "push {r9}\n\t"
+ ".cfi_adjust_cfa_offset 4\n\t"
+ "mov r9, %[self]\n\t" // Set the thread
+ "blx %[code]\n\t" // Call the stub
+ "pop {r1}\n\t" // Pop nullptr
+ ".cfi_adjust_cfa_offset -4\n\t"
+ "pop {r1-r2,r9, lr}\n\t" // Restore the link and thread register
+ ".cfi_adjust_cfa_offset -16\n\t"
+ "mov %[result], r0\n\t" // Save the result
+ : [result] "=r" (result)
+ // Use the result from r0
+ : [arg0] "0"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self)
+ : ); // clobber.
+#elif defined(__aarch64__)
+ __asm__ __volatile__(
+ "sub sp, sp, #48\n\t" // Reserve stack space, 16B aligned
+ "stp xzr, x1, [sp]\n\t" // nullptr(end of quick stack), x1
+ "stp x2, x18, [sp, #16]\n\t" // Save x2, x18(xSELF)
+ "str x30, [sp, #32]\n\t" // Save xLR
+ "mov x0, %[arg0]\n\t" // Set arg0-arg2
+ "mov x1, %[arg1]\n\t" // TODO: Any way to use constraints like on x86?
+ "mov x2, %[arg2]\n\t"
+ // Use r18 last as we don't know whether it was used for arg0-arg2
+ "mov x18, %[self]\n\t" // Set the thread
+ "blr %[code]\n\t" // Call the stub
+ "ldp x1, x2, [sp, #8]\n\t" // Restore x1, x2
+ "ldp x18, x30, [sp, #24]\n\t" // Restore xSELF, xLR
+ "add sp, sp, #48\n\t" // Free stack space
+ "mov %[result], x0\n\t" // Save the result
+ : [result] "=r" (result)
+ // Use the result from r0
+ : [arg0] "0"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self)
+ : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17"); // clobber.
+#elif defined(__x86_64__)
+ // Note: Uses the native convention
+ // TODO: Set the thread?
+ __asm__ __volatile__(
+ "pushq $0\n\t" // Push nullptr to terminate quick stack
+ "pushq $0\n\t" // 16B alignment padding
+ "call *%%rax\n\t" // Call the stub
+ "addq $16, %%rsp" // Pop nullptr and padding
+ : "=a" (result)
+ // Use the result from rax
+ : "D"(arg0), "S"(arg1), "d"(arg2), "a"(code)
+ // This places arg0 into rdi, arg1 into rsi, arg2 into rdx, and code into rax
+ : "rcx", "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"); // clobber all
+ // TODO: Should we clobber the other registers?
+ result = 0;
+#else
+ LOG(WARNING) << "Was asked to invoke for an architecture I do not understand.";
+ result = 0;
+#endif
+ // Pop transition.
+ self->PopManagedStackFragment(fragment);
+ return result;
+ }
+};
+
+
+#if defined(__i386__) || defined(__x86_64__)
+extern "C" void art_quick_memcpy(void);
+#endif
+
+TEST_F(StubTest, Memcpy) {
+#if defined(__i386__) || defined(__x86_64__)
+ Thread* self = Thread::Current();
+
+ uint32_t orig[20];
+ uint32_t trg[20];
+ for (size_t i = 0; i < 20; ++i) {
+ orig[i] = i;
+ trg[i] = 0;
+ }
+
+ Invoke3(reinterpret_cast<size_t>(&trg[4]), reinterpret_cast<size_t>(&orig[4]),
+ 10 * sizeof(uint32_t), reinterpret_cast<uintptr_t>(&art_quick_memcpy), self);
+
+ EXPECT_EQ(orig[0], trg[0]);
+
+ for (size_t i = 1; i < 4; ++i) {
+ EXPECT_NE(orig[i], trg[i]);
+ }
+
+ for (size_t i = 4; i < 14; ++i) {
+ EXPECT_EQ(orig[i], trg[i]);
+ }
+
+ for (size_t i = 14; i < 20; ++i) {
+ EXPECT_NE(orig[i], trg[i]);
+ }
+
+ // TODO: Test overlapping?
+
+#else
+ LOG(INFO) << "Skipping memcpy as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping memcpy as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
+
+#if defined(__i386__) || defined(__arm__)
+extern "C" void art_quick_lock_object(void);
+#endif
+
+TEST_F(StubTest, LockObject) {
+#if defined(__i386__) || defined(__arm__)
+ Thread* self = Thread::Current();
+ // Create an object
+ ScopedObjectAccess soa(self);
+ // garbage is created during ClassLinker::Init
+
+ SirtRef<mirror::String> obj(soa.Self(),
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!"));
+ LockWord lock = obj->GetLockWord(false);
+ LockWord::LockState old_state = lock.GetState();
+ EXPECT_EQ(LockWord::LockState::kUnlocked, old_state);
+
+ Invoke3(reinterpret_cast<size_t>(obj.get()), 0U, 0U,
+ reinterpret_cast<uintptr_t>(&art_quick_lock_object), self);
+
+ LockWord lock_after = obj->GetLockWord(false);
+ LockWord::LockState new_state = lock_after.GetState();
+ EXPECT_EQ(LockWord::LockState::kThinLocked, new_state);
+
+ Invoke3(reinterpret_cast<size_t>(obj.get()), 0U, 0U,
+ reinterpret_cast<uintptr_t>(&art_quick_lock_object), self);
+
+ LockWord lock_after2 = obj->GetLockWord(false);
+ LockWord::LockState new_state2 = lock_after2.GetState();
+ EXPECT_EQ(LockWord::LockState::kThinLocked, new_state2);
+
+ // TODO: Improve this test. Somehow force it to go to fat locked. But that needs another thread.
+
+#else
+ LOG(INFO) << "Skipping lock_object as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping lock_object as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
+
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__)
+extern "C" void art_quick_check_cast(void);
+#endif
+
+TEST_F(StubTest, CheckCast) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__x86_64__)
+ Thread* self = Thread::Current();
+ // Find some classes.
+ ScopedObjectAccess soa(self);
+ // garbage is created during ClassLinker::Init
+
+ SirtRef<mirror::Class> c(soa.Self(), class_linker_->FindSystemClass(soa.Self(),
+ "[Ljava/lang/Object;"));
+ SirtRef<mirror::Class> c2(soa.Self(), class_linker_->FindSystemClass(soa.Self(),
+ "[Ljava/lang/String;"));
+
+ EXPECT_FALSE(self->IsExceptionPending());
+
+ Invoke3(reinterpret_cast<size_t>(c.get()), reinterpret_cast<size_t>(c.get()), 0U,
+ reinterpret_cast<uintptr_t>(&art_quick_check_cast), self);
+
+ EXPECT_FALSE(self->IsExceptionPending());
+
+ Invoke3(reinterpret_cast<size_t>(c2.get()), reinterpret_cast<size_t>(c2.get()), 0U,
+ reinterpret_cast<uintptr_t>(&art_quick_check_cast), self);
+
+ EXPECT_FALSE(self->IsExceptionPending());
+
+ Invoke3(reinterpret_cast<size_t>(c.get()), reinterpret_cast<size_t>(c2.get()), 0U,
+ reinterpret_cast<uintptr_t>(&art_quick_check_cast), self);
+
+ EXPECT_FALSE(self->IsExceptionPending());
+
+ // TODO: Make the following work. But that would require correct managed frames.
+
+ Invoke3(reinterpret_cast<size_t>(c2.get()), reinterpret_cast<size_t>(c.get()), 0U,
+ reinterpret_cast<uintptr_t>(&art_quick_check_cast), self);
+
+ EXPECT_TRUE(self->IsExceptionPending());
+ self->ClearException();
+
+#else
+ LOG(INFO) << "Skipping check_cast as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping check_cast as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
+
+#if defined(__i386__) || defined(__arm__)
+extern "C" void art_quick_aput_obj_with_null_and_bound_check(void);
+// Do not check non-checked ones, we'd need handlers and stuff...
+#endif
+
+TEST_F(StubTest, APutObj) {
+#if defined(__i386__) || defined(__arm__)
+ Thread* self = Thread::Current();
+ // Create an object
+ ScopedObjectAccess soa(self);
+ // garbage is created during ClassLinker::Init
+
+ SirtRef<mirror::Class> c(soa.Self(), class_linker_->FindSystemClass(soa.Self(),
+ "Ljava/lang/Object;"));
+ SirtRef<mirror::Class> c2(soa.Self(), class_linker_->FindSystemClass(soa.Self(),
+ "Ljava/lang/String;"));
+ SirtRef<mirror::Class> ca(soa.Self(), class_linker_->FindSystemClass(soa.Self(),
+ "[Ljava/lang/String;"));
+
+ // Build a string array of size 1
+ SirtRef<mirror::ObjectArray<mirror::Object> > array(soa.Self(),
+ mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.get(), 1));
+
+ // Build a string -> should be assignable
+ SirtRef<mirror::Object> str_obj(soa.Self(),
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!"));
+
+ // Build a generic object -> should fail assigning
+ SirtRef<mirror::Object> obj_obj(soa.Self(), c->AllocObject(soa.Self()));
+
+ // Play with it...
+
+ // 1) Success cases
+ // 1.1) Assign str_obj to array[0]
+
+ EXPECT_FALSE(self->IsExceptionPending());
+
+ Invoke3(reinterpret_cast<size_t>(array.get()), 0U, reinterpret_cast<size_t>(str_obj.get()),
+ reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+
+ EXPECT_FALSE(self->IsExceptionPending());
+
+ // 1.2) Assign null to array[0]
+
+ Invoke3(reinterpret_cast<size_t>(array.get()), 0U, reinterpret_cast<size_t>(nullptr),
+ reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+
+ EXPECT_FALSE(self->IsExceptionPending());
+
+ // TODO: Check _which_ exception is thrown. Then make 3) check that it's the right check order.
+
+ // 2) Failure cases (str into str[])
+ // 2.1) Array = null
+ // TODO: Throwing NPE needs actual DEX code
+
+// Invoke3(reinterpret_cast<size_t>(nullptr), 0U, reinterpret_cast<size_t>(str_obj.get()),
+// reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+//
+// EXPECT_TRUE(self->IsExceptionPending());
+// self->ClearException();
+
+ // 2.2) Index < 0
+
+ Invoke3(reinterpret_cast<size_t>(array.get()), static_cast<size_t>(-1),
+ reinterpret_cast<size_t>(str_obj.get()),
+ reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+
+ EXPECT_TRUE(self->IsExceptionPending());
+ self->ClearException();
+
+ // 2.3) Index > 0
+
+ Invoke3(reinterpret_cast<size_t>(array.get()), 1U, reinterpret_cast<size_t>(str_obj.get()),
+ reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+
+ EXPECT_TRUE(self->IsExceptionPending());
+ self->ClearException();
+
+ // 3) Failure cases (obj into str[])
+
+ Invoke3(reinterpret_cast<size_t>(array.get()), 0U, reinterpret_cast<size_t>(obj_obj.get()),
+ reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
+
+ EXPECT_TRUE(self->IsExceptionPending());
+ self->ClearException();
+
+ // Tests done.
+#else
+ LOG(INFO) << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
+} // namespace art
diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S
index e329530..642d9a3 100644
--- a/runtime/arch/x86/asm_support_x86.S
+++ b/runtime/arch/x86/asm_support_x86.S
@@ -19,8 +19,8 @@
#include "asm_support_x86.h"
-#if defined(__clang__)
- // Clang's as(1) doesn't let you name macro parameters.
+#if defined(__clang__) && (__clang_major__ < 4) && (__clang_minor__ < 5)
+ // Clang's as(1) doesn't let you name macro parameters prior to 3.5.
#define MACRO0(macro_name) .macro macro_name
#define MACRO1(macro_name, macro_arg1) .macro macro_name
#define MACRO2(macro_name, macro_arg1, macro_args2) .macro macro_name
diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h
index e986c41..fd5ed5a 100644
--- a/runtime/arch/x86/asm_support_x86.h
+++ b/runtime/arch/x86/asm_support_x86.h
@@ -28,4 +28,8 @@
// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu
#define THREAD_ID_OFFSET 12
+#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 32
+#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 32
+#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 32
+
#endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 12460b9..339ed2e 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -28,6 +28,11 @@
PUSH ebp
subl MACRO_LITERAL(16), %esp // Grow stack by 4 words, bottom word will hold Method*
CFI_ADJUST_CFA_OFFSET(16)
+ // Ugly compile-time check, but we only have the preprocessor.
+ // Last +4: implicit return address pushed on stack when caller made call.
+#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 3*4 + 16 + 4)
+#error "SAVE_ALL_CALLEE_SAVE_FRAME(X86) size not as expected."
+#endif
END_MACRO
/*
@@ -40,6 +45,12 @@
PUSH ebp
subl MACRO_LITERAL(16), %esp // Grow stack by 4 words, bottom word will hold Method*
CFI_ADJUST_CFA_OFFSET(16)
+
+ // Ugly compile-time check, but we only have the preprocessor.
+ // Last +4: implicit return address pushed on stack when caller made call.
+#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 3*4 + 16 + 4)
+#error "REFS_ONLY_CALLEE_SAVE_FRAME(X86) size not as expected."
+#endif
END_MACRO
MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME)
@@ -62,6 +73,12 @@
PUSH edx
PUSH ecx
PUSH eax // Align stack, eax will be clobbered by Method*
+
+ // Ugly compile-time check, but we only have the preprocessor.
+ // Last +4: implicit return address pushed on stack when caller made call.
+#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 7*4 + 4)
+#error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(X86) size not as expected."
+#endif
END_MACRO
MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME)
diff --git a/runtime/arch/x86_64/asm_support_x86_64.S b/runtime/arch/x86_64/asm_support_x86_64.S
index a9f69f5..ad65033 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.S
+++ b/runtime/arch/x86_64/asm_support_x86_64.S
@@ -19,15 +19,15 @@
#include "asm_support_x86_64.h"
-#if defined(__clang__)
- // Clang's as(1) doesn't let you name macro parameters.
+#if defined(__clang__) && (__clang_major__ < 4) && (__clang_minor__ < 5)
+ // Clang's as(1) doesn't let you name macro parameters prior to 3.5.
#define MACRO0(macro_name) .macro macro_name
#define MACRO1(macro_name, macro_arg1) .macro macro_name
#define MACRO2(macro_name, macro_arg1, macro_args2) .macro macro_name
#define MACRO3(macro_name, macro_arg1, macro_args2, macro_args3) .macro macro_name
#define END_MACRO .endmacro
- // Clang's as(1) uses $0, $1, and so on for macro arguments.
+ // Clang's as(1) uses $0, $1, and so on for macro arguments prior to 3.5.
#define VAR(name,index) SYMBOL($index)
#define PLT_VAR(name, index) SYMBOL($index)@PLT
#define REG_VAR(name,index) %$index
diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h
index 70ef3ef..109533b 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.h
+++ b/runtime/arch/x86_64/asm_support_x86_64.h
@@ -35,4 +35,8 @@
// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu
#define THREAD_ID_OFFSET 12
+#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 64
+#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 64
+#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 176
+
#endif // ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 6509a9b..a31ea58 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -39,6 +39,12 @@
movq RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
// Store ArtMethod* to bottom of stack.
movq %r10, 0(%rsp)
+
+ // Ugly compile-time check, but we only have the preprocessor.
+ // Last +8: implicit return address pushed on stack when caller made call.
+#if (FRAME_SIZE_SAVE_ALL_CALLEE_SAVE != 6*8 + 8 + 8)
+#error "SAVE_ALL_CALLEE_SAVE_FRAME(X86_64) size not as expected."
+#endif
END_MACRO
/*
@@ -63,6 +69,12 @@
movq RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10
// Store ArtMethod* to bottom of stack.
movq %r10, 0(%rsp)
+
+ // Ugly compile-time check, but we only have the preprocessor.
+ // Last +8: implicit return address pushed on stack when caller made call.
+#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 6*8 + 8 + 8)
+#error "REFS_ONLY_CALLEE_SAVE_FRAME(X86_64) size not as expected."
+#endif
END_MACRO
MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME)
@@ -114,6 +126,12 @@
movq %xmm7, 72(%rsp)
// Store ArtMethod* to bottom of stack.
movq %r10, 0(%rsp)
+
+ // Ugly compile-time check, but we only have the preprocessor.
+ // Last +8: implicit return address pushed on stack when caller made call.
+#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 11*8 + 80 + 8)
+#error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(X86_64) size not as expected."
+#endif
END_MACRO
MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME)
@@ -666,8 +684,22 @@
END_FUNCTION art_quick_is_assignable
DEFINE_FUNCTION art_quick_check_cast
- int3
- int3
+ PUSH rdi // Save args for exc
+ PUSH rsi
+ call PLT_SYMBOL(artIsAssignableFromCode) // (Class* klass, Class* ref_klass)
+ testq %rax, %rax
+ jz 1f // jump forward if not assignable
+ addq LITERAL(16), %rsp // pop arguments
+ CFI_ADJUST_CFA_OFFSET(-16)
+ ret
+1:
+ POP rsi // Pop arguments
+ POP rdi
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
+ mov %rsp, %rcx // pass SP
+ mov %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current()
+ call PLT_SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*, SP)
+ int3 // unreached
END_FUNCTION art_quick_check_cast
/*
@@ -678,7 +710,12 @@
UNIMPLEMENTED art_quick_aput_obj_with_null_and_bound_check
UNIMPLEMENTED art_quick_aput_obj_with_bound_check
UNIMPLEMENTED art_quick_aput_obj
-UNIMPLEMENTED art_quick_memcpy
+
+// TODO: This is quite silly on X86_64 now.
+DEFINE_FUNCTION art_quick_memcpy
+ call PLT_SYMBOL(memcpy) // (void*, const void*, size_t)
+ ret
+END_FUNCTION art_quick_memcpy
NO_ARG_DOWNCALL art_quick_test_suspend, artTestSuspendFromCode, ret
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index fdd0249..2bc17bf 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -206,16 +206,16 @@
os << "never contended";
} else {
os << "contended " << contention_count
- << " times, average wait of contender " << PrettyDuration(wait_time / contention_count);
+ << " total wait of contender " << PrettyDuration(wait_time)
+ << " average " << PrettyDuration(wait_time / contention_count);
SafeMap<uint64_t, size_t> most_common_blocker;
SafeMap<uint64_t, size_t> most_common_blocked;
- typedef SafeMap<uint64_t, size_t>::const_iterator It;
for (size_t i = 0; i < kContentionLogSize; ++i) {
uint64_t blocked_tid = log[i].blocked_tid;
uint64_t owner_tid = log[i].owner_tid;
uint32_t count = log[i].count;
if (count > 0) {
- It it = most_common_blocked.find(blocked_tid);
+ auto it = most_common_blocked.find(blocked_tid);
if (it != most_common_blocked.end()) {
most_common_blocked.Overwrite(blocked_tid, it->second + count);
} else {
@@ -231,10 +231,10 @@
}
uint64_t max_tid = 0;
size_t max_tid_count = 0;
- for (It it = most_common_blocked.begin(); it != most_common_blocked.end(); ++it) {
- if (it->second > max_tid_count) {
- max_tid = it->first;
- max_tid_count = it->second;
+ for (const auto& pair : most_common_blocked) {
+ if (pair.second > max_tid_count) {
+ max_tid = pair.first;
+ max_tid_count = pair.second;
}
}
if (max_tid != 0) {
@@ -242,10 +242,10 @@
}
max_tid = 0;
max_tid_count = 0;
- for (It it = most_common_blocker.begin(); it != most_common_blocker.end(); ++it) {
- if (it->second > max_tid_count) {
- max_tid = it->first;
- max_tid_count = it->second;
+ for (const auto& pair : most_common_blocker) {
+ if (pair.second > max_tid_count) {
+ max_tid = pair.first;
+ max_tid_count = pair.second;
}
}
if (max_tid != 0) {
diff --git a/runtime/catch_block_stack_visitor.cc b/runtime/catch_block_stack_visitor.cc
index 410fff9..8d10a97 100644
--- a/runtime/catch_block_stack_visitor.cc
+++ b/runtime/catch_block_stack_visitor.cc
@@ -17,27 +17,26 @@
#include "catch_block_stack_visitor.h"
#include "dex_instruction.h"
-#include "catch_finder.h"
+#include "mirror/art_method-inl.h"
+#include "quick_exception_handler.h"
#include "sirt_ref.h"
#include "verifier/method_verifier.h"
namespace art {
bool CatchBlockStackVisitor::VisitFrame() {
- catch_finder_->SetHandlerFrameId(GetFrameId());
+ exception_handler_->SetHandlerFrameId(GetFrameId());
mirror::ArtMethod* method = GetMethod();
if (method == nullptr) {
// This is the upcall, we remember the frame and last pc so that we may long jump to them.
- catch_finder_->SetHandlerQuickFramePc(GetCurrentQuickFramePc());
- catch_finder_->SetHandlerQuickFrame(GetCurrentQuickFrame());
+ exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc());
+ exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
return false; // End stack walk.
} else {
if (method->IsRuntimeMethod()) {
// Ignore callee save method.
DCHECK(method->IsCalleeSaveMethod());
return true;
- } else if (is_deoptimization_) {
- return HandleDeoptimization(method);
} else {
return HandleTryItems(method);
}
@@ -46,68 +45,21 @@
bool CatchBlockStackVisitor::HandleTryItems(mirror::ArtMethod* method) {
uint32_t dex_pc = DexFile::kDexNoIndex;
- if (method->IsNative()) {
- ++native_method_count_;
- } else {
+ if (!method->IsNative()) {
dex_pc = GetDexPc();
}
if (dex_pc != DexFile::kDexNoIndex) {
bool clear_exception = false;
- SirtRef<mirror::Class> sirt_method_to_find(Thread::Current(), to_find_);
- uint32_t found_dex_pc = method->FindCatchBlock(sirt_method_to_find, dex_pc, &clear_exception);
- to_find_ = sirt_method_to_find.get();
- catch_finder_->SetClearException(clear_exception);
+ uint32_t found_dex_pc = method->FindCatchBlock(to_find_, dex_pc, &clear_exception);
+ exception_handler_->SetClearException(clear_exception);
if (found_dex_pc != DexFile::kDexNoIndex) {
- catch_finder_->SetHandlerDexPc(found_dex_pc);
- catch_finder_->SetHandlerQuickFramePc(method->ToNativePc(found_dex_pc));
- catch_finder_->SetHandlerQuickFrame(GetCurrentQuickFrame());
+ exception_handler_->SetHandlerDexPc(found_dex_pc);
+ exception_handler_->SetHandlerQuickFramePc(method->ToNativePc(found_dex_pc));
+ exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
return false; // End stack walk.
}
}
return true; // Continue stack walk.
}
-bool CatchBlockStackVisitor::HandleDeoptimization(mirror::ArtMethod* m) {
- MethodHelper mh(m);
- const DexFile::CodeItem* code_item = mh.GetCodeItem();
- CHECK(code_item != nullptr);
- uint16_t num_regs = code_item->registers_size_;
- uint32_t dex_pc = GetDexPc();
- const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
- uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits();
- ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, new_dex_pc);
- SirtRef<mirror::DexCache> dex_cache(self_, mh.GetDexCache());
- SirtRef<mirror::ClassLoader> class_loader(self_, mh.GetClassLoader());
- verifier::MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader,
- &mh.GetClassDef(), code_item, m->GetDexMethodIndex(), m,
- m->GetAccessFlags(), false, true);
- verifier.Verify();
- std::vector<int32_t> kinds = verifier.DescribeVRegs(dex_pc);
- for (uint16_t reg = 0; reg < num_regs; ++reg) {
- VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2));
- switch (kind) {
- case kUndefined:
- new_frame->SetVReg(reg, 0xEBADDE09);
- break;
- case kConstant:
- new_frame->SetVReg(reg, kinds.at((reg * 2) + 1));
- break;
- case kReferenceVReg:
- new_frame->SetVRegReference(reg,
- reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kind)));
- break;
- default:
- new_frame->SetVReg(reg, GetVReg(m, reg, kind));
- break;
- }
- }
- if (prev_shadow_frame_ != nullptr) {
- prev_shadow_frame_->SetLink(new_frame);
- } else {
- catch_finder_->SetTopShadowFrame(new_frame);
- }
- prev_shadow_frame_ = new_frame;
- return true;
-}
-
} // namespace art
diff --git a/runtime/catch_block_stack_visitor.h b/runtime/catch_block_stack_visitor.h
index ce67e27..6f0fe11 100644
--- a/runtime/catch_block_stack_visitor.h
+++ b/runtime/catch_block_stack_visitor.h
@@ -17,39 +17,39 @@
#ifndef ART_RUNTIME_CATCH_BLOCK_STACK_VISITOR_H_
#define ART_RUNTIME_CATCH_BLOCK_STACK_VISITOR_H_
-#include "mirror/throwable.h"
-#include "thread.h"
+#include "mirror/object-inl.h"
+#include "stack.h"
+#include "sirt_ref-inl.h"
namespace art {
-class CatchFinder;
+
+namespace mirror {
+class Throwable;
+} // namespace mirror
+class Context;
+class QuickExceptionHandler;
+class Thread;
class ThrowLocation;
// Finds catch handler or prepares deoptimization.
-class CatchBlockStackVisitor : public StackVisitor {
+class CatchBlockStackVisitor FINAL : public StackVisitor {
public:
- CatchBlockStackVisitor(Thread* self, Context* context, mirror::Throwable* exception,
- bool is_deoptimization, CatchFinder* catch_finder)
+ CatchBlockStackVisitor(Thread* self, Context* context, SirtRef<mirror::Throwable>& exception,
+ QuickExceptionHandler* exception_handler)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(self, context),
- self_(self), is_deoptimization_(is_deoptimization),
- to_find_(is_deoptimization ? nullptr : exception->GetClass()),
- catch_finder_(catch_finder), native_method_count_(0), prev_shadow_frame_(nullptr) {
+ : StackVisitor(self, context), self_(self), to_find_(self, exception->GetClass()),
+ exception_handler_(exception_handler) {
}
- bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
bool HandleTryItems(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool HandleDeoptimization(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Thread* const self_;
- const bool is_deoptimization_;
// The type of the exception catch block to find.
- mirror::Class* to_find_;
- CatchFinder* const catch_finder_;
- // Number of native methods passed in crawl (equates to number of SIRTs to pop)
- uint32_t native_method_count_;
- ShadowFrame* prev_shadow_frame_;
+ SirtRef<mirror::Class> to_find_;
+ QuickExceptionHandler* const exception_handler_;
DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor);
};
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 58b82f0..338133c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -185,7 +185,8 @@
quick_resolution_trampoline_(nullptr),
portable_imt_conflict_trampoline_(nullptr),
quick_imt_conflict_trampoline_(nullptr),
- quick_generic_jni_trampoline_(nullptr) {
+ quick_generic_jni_trampoline_(nullptr),
+ quick_to_interpreter_bridge_trampoline_(nullptr) {
CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
}
@@ -1002,6 +1003,7 @@
portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline();
quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline();
quick_generic_jni_trampoline_ = oat_file.GetOatHeader().GetQuickGenericJniTrampoline();
+ quick_to_interpreter_bridge_trampoline_ = oat_file.GetOatHeader().GetQuickToInterpreterBridge();
mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
mirror::ObjectArray<mirror::DexCache>* dex_caches =
dex_caches_object->AsObjectArray<mirror::DexCache>();
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a14d1d1..9771318 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -369,6 +369,10 @@
return quick_imt_conflict_trampoline_;
}
+ const void* GetQuickToInterpreterBridgeTrampoline() const {
+ return quick_to_interpreter_bridge_trampoline_;
+ }
+
InternTable* GetInternTable() const {
return intern_table_;
}
@@ -658,6 +662,7 @@
const void* portable_imt_conflict_trampoline_;
const void* quick_imt_conflict_trampoline_;
const void* quick_generic_jni_trampoline_;
+ const void* quick_to_interpreter_bridge_trampoline_;
friend class ImageWriter; // for GetClassRoots
FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 5b72a44..1218357 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -485,8 +485,6 @@
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_portable_compiled_code_), "entryPointFromPortableCompiledCode"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_quick_compiled_code_), "entryPointFromQuickCompiledCode"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, gc_map_), "gcMap"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, quick_mapping_table_), "quickMappingTable"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, quick_vmap_table_), "quickVmapTable"));
// alphabetical 32-bit
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, access_flags_), "accessFlags"));
diff --git a/runtime/deoptimize_stack_visitor.cc b/runtime/deoptimize_stack_visitor.cc
new file mode 100644
index 0000000..f2eaf00
--- /dev/null
+++ b/runtime/deoptimize_stack_visitor.cc
@@ -0,0 +1,87 @@
+/*
+ * 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 "deoptimize_stack_visitor.h"
+
+#include "mirror/art_method-inl.h"
+#include "object_utils.h"
+#include "quick_exception_handler.h"
+#include "sirt_ref-inl.h"
+#include "verifier/method_verifier.h"
+
+namespace art {
+
+bool DeoptimizeStackVisitor::VisitFrame() {
+ exception_handler_->SetHandlerFrameId(GetFrameId());
+ mirror::ArtMethod* method = GetMethod();
+ if (method == nullptr) {
+ // This is the upcall, we remember the frame and last pc so that we may long jump to them.
+ exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc());
+ exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
+ return false; // End stack walk.
+ } else if (method->IsRuntimeMethod()) {
+ // Ignore callee save method.
+ DCHECK(method->IsCalleeSaveMethod());
+ return true;
+ } else {
+ return HandleDeoptimization(method);
+ }
+}
+
+bool DeoptimizeStackVisitor::HandleDeoptimization(mirror::ArtMethod* m) {
+ MethodHelper mh(m);
+ const DexFile::CodeItem* code_item = mh.GetCodeItem();
+ CHECK(code_item != nullptr);
+ uint16_t num_regs = code_item->registers_size_;
+ uint32_t dex_pc = GetDexPc();
+ const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
+ uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits();
+ ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, new_dex_pc);
+ SirtRef<mirror::DexCache> dex_cache(self_, mh.GetDexCache());
+ SirtRef<mirror::ClassLoader> class_loader(self_, mh.GetClassLoader());
+ verifier::MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader,
+ &mh.GetClassDef(), code_item, m->GetDexMethodIndex(), m,
+ m->GetAccessFlags(), false, true);
+ verifier.Verify();
+ std::vector<int32_t> kinds = verifier.DescribeVRegs(dex_pc);
+ for (uint16_t reg = 0; reg < num_regs; ++reg) {
+ VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2));
+ switch (kind) {
+ case kUndefined:
+ new_frame->SetVReg(reg, 0xEBADDE09);
+ break;
+ case kConstant:
+ new_frame->SetVReg(reg, kinds.at((reg * 2) + 1));
+ break;
+ case kReferenceVReg:
+ new_frame->SetVRegReference(reg,
+ reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kind)));
+ break;
+ default:
+ new_frame->SetVReg(reg, GetVReg(m, reg, kind));
+ break;
+ }
+ }
+ if (prev_shadow_frame_ != nullptr) {
+ prev_shadow_frame_->SetLink(new_frame);
+ } else {
+ exception_handler_->SetTopShadowFrame(new_frame);
+ }
+ prev_shadow_frame_ = new_frame;
+ return true;
+}
+
+} // namespace art
diff --git a/runtime/deoptimize_stack_visitor.h b/runtime/deoptimize_stack_visitor.h
new file mode 100644
index 0000000..c898e7d
--- /dev/null
+++ b/runtime/deoptimize_stack_visitor.h
@@ -0,0 +1,53 @@
+/*
+ * 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_DEOPTIMIZE_STACK_VISITOR_H_
+#define ART_RUNTIME_DEOPTIMIZE_STACK_VISITOR_H_
+
+#include "base/mutex.h"
+#include "stack.h"
+
+namespace art {
+
+namespace mirror {
+class ArtMethod;
+} // namespace mirror
+class QuickExceptionHandler;
+class Thread;
+
+// Prepares deoptimization.
+class DeoptimizeStackVisitor FINAL : public StackVisitor {
+ public:
+ DeoptimizeStackVisitor(Thread* self, Context* context, QuickExceptionHandler* exception_handler)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : StackVisitor(self, context), self_(self), exception_handler_(exception_handler),
+ prev_shadow_frame_(nullptr) {
+ }
+
+ bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+ bool HandleDeoptimization(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ Thread* const self_;
+ QuickExceptionHandler* const exception_handler_;
+ ShadowFrame* prev_shadow_frame_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeoptimizeStackVisitor);
+};
+
+} // namespace art
+#endif // ART_RUNTIME_DEOPTIMIZE_STACK_VISITOR_H_
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 8b48b36..05912bf 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -761,6 +761,10 @@
return class_linker->GetQuickGenericJniTrampoline();
}
+static inline const void* GetQuickToInterpreterBridgeTrampoline(ClassLinker* class_linker) {
+ return class_linker->GetQuickToInterpreterBridgeTrampoline();
+}
+
extern "C" void art_portable_proxy_invoke_handler();
static inline const void* GetPortableProxyInvokeHandler() {
return reinterpret_cast<void*>(art_portable_proxy_invoke_handler);
diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
index 51c647a..6448045 100644
--- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
@@ -31,7 +31,7 @@
extern "C" void artDeoptimize(Thread* self, mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
- self->SetException(ThrowLocation(), reinterpret_cast<mirror::Throwable*>(-1));
+ self->SetException(ThrowLocation(), Thread::GetDeoptimizationException());
self->QuickDeliverException();
}
diff --git a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
index 633f580..60c5377 100644
--- a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
@@ -32,6 +32,7 @@
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
const void* result = instrumentation->GetQuickCodeFor(method);
+ DCHECK(result != GetQuickToInterpreterBridgeTrampoline(Runtime::Current()->GetClassLinker()));
bool interpreter_entry = (result == GetQuickToInterpreterBridge());
instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? NULL : this_object,
method, lr, interpreter_entry);
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 208eb74..97a8367 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -49,10 +49,6 @@
dex_ = my_klass_->GetDexCache()->GetDexFile();
uint32_t code_size = 12;
- fake_code_.push_back((code_size >> 24) & 0xFF);
- fake_code_.push_back((code_size >> 16) & 0xFF);
- fake_code_.push_back((code_size >> 8) & 0xFF);
- fake_code_.push_back((code_size >> 0) & 0xFF);
for (size_t i = 0 ; i < code_size; i++) {
fake_code_.push_back(0x70 | i);
}
@@ -74,20 +70,35 @@
fake_gc_map_.push_back(0); // 0 entries.
fake_gc_map_.push_back(0);
+ const std::vector<uint8_t>& fake_vmap_table_data = fake_vmap_table_data_.GetData();
+ const std::vector<uint8_t>& fake_mapping_data = fake_mapping_data_.GetData();
+ uint32_t vmap_table_offset = sizeof(OatMethodHeader) + fake_vmap_table_data.size();
+ uint32_t mapping_table_offset = vmap_table_offset + fake_mapping_data.size();
+ OatMethodHeader method_header(vmap_table_offset, mapping_table_offset, code_size);
+ fake_header_code_and_maps_.resize(sizeof(method_header));
+ memcpy(&fake_header_code_and_maps_[0], &method_header, sizeof(method_header));
+ fake_header_code_and_maps_.insert(fake_header_code_and_maps_.begin(),
+ fake_vmap_table_data.begin(), fake_vmap_table_data.end());
+ fake_header_code_and_maps_.insert(fake_header_code_and_maps_.begin(),
+ fake_mapping_data.begin(), fake_mapping_data.end());
+ fake_header_code_and_maps_.insert(fake_header_code_and_maps_.end(),
+ fake_code_.begin(), fake_code_.end());
+
+ // NOTE: Don't align the code (it will not be executed) but check that the Thumb2
+ // adjustment will be a NOP, see ArtMethod::EntryPointToCodePointer().
+ CHECK_EQ(mapping_table_offset & 1u, 0u);
+ const uint8_t* code_ptr = &fake_header_code_and_maps_[mapping_table_offset];
+
method_f_ = my_klass_->FindVirtualMethod("f", "()I");
ASSERT_TRUE(method_f_ != NULL);
method_f_->SetFrameSizeInBytes(4 * kPointerSize);
- method_f_->SetEntryPointFromQuickCompiledCode(&fake_code_[sizeof(code_size)]);
- method_f_->SetMappingTable(&fake_mapping_data_.GetData()[0]);
- method_f_->SetVmapTable(&fake_vmap_table_data_.GetData()[0]);
+ method_f_->SetEntryPointFromQuickCompiledCode(code_ptr);
method_f_->SetNativeGcMap(&fake_gc_map_[0]);
method_g_ = my_klass_->FindVirtualMethod("g", "(I)V");
ASSERT_TRUE(method_g_ != NULL);
method_g_->SetFrameSizeInBytes(4 * kPointerSize);
- method_g_->SetEntryPointFromQuickCompiledCode(&fake_code_[sizeof(code_size)]);
- method_g_->SetMappingTable(&fake_mapping_data_.GetData()[0]);
- method_g_->SetVmapTable(&fake_vmap_table_data_.GetData()[0]);
+ method_g_->SetEntryPointFromQuickCompiledCode(code_ptr);
method_g_->SetNativeGcMap(&fake_gc_map_[0]);
}
@@ -97,6 +108,7 @@
Leb128EncodingVector fake_mapping_data_;
Leb128EncodingVector fake_vmap_table_data_;
std::vector<uint8_t> fake_gc_map_;
+ std::vector<uint8_t> fake_header_code_and_maps_;
mirror::ArtMethod* method_f_;
mirror::ArtMethod* method_g_;
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index 31a1537..66f9a3a 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -21,13 +21,17 @@
namespace accounting {
template<size_t kAlignment>
+size_t SpaceBitmap<kAlignment>::ComputeBitmapSize(uint64_t capacity) {
+ const uint64_t kBytesCoveredPerWord = kAlignment * kBitsPerWord;
+ return (RoundUp(capacity, kBytesCoveredPerWord) / kBytesCoveredPerWord) * kWordSize;
+}
+
+template<size_t kAlignment>
SpaceBitmap<kAlignment>* SpaceBitmap<kAlignment>::CreateFromMemMap(
const std::string& name, MemMap* mem_map, byte* heap_begin, size_t heap_capacity) {
CHECK(mem_map != nullptr);
uword* bitmap_begin = reinterpret_cast<uword*>(mem_map->Begin());
- const uint64_t kBytesCoveredPerWord = kAlignment * kBitsPerWord;
- size_t bitmap_size = (RoundUp(static_cast<uint64_t>(heap_capacity), kBytesCoveredPerWord) /
- kBytesCoveredPerWord) * kWordSize;
+ const size_t bitmap_size = ComputeBitmapSize(heap_capacity);
return new SpaceBitmap(name, mem_map, bitmap_begin, bitmap_size, heap_begin);
}
@@ -45,9 +49,7 @@
SpaceBitmap<kAlignment>* SpaceBitmap<kAlignment>::Create(
const std::string& name, byte* heap_begin, size_t heap_capacity) {
// Round up since heap_capacity is not necessarily a multiple of kAlignment * kBitsPerWord.
- const uint64_t kBytesCoveredPerWord = kAlignment * kBitsPerWord;
- size_t bitmap_size = (RoundUp(static_cast<uint64_t>(heap_capacity), kBytesCoveredPerWord) /
- kBytesCoveredPerWord) * kWordSize;
+ const size_t bitmap_size = ComputeBitmapSize(heap_capacity);
std::string error_msg;
UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), nullptr, bitmap_size,
PROT_READ | PROT_WRITE, false, &error_msg));
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index df3fd37..5c7cce2 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -200,6 +200,9 @@
SpaceBitmap(const std::string& name, MemMap* mem_map, uword* bitmap_begin, size_t bitmap_size,
const void* heap_begin);
+ // Helper function for computing bitmap size based on a 64 bit capacity.
+ static size_t ComputeBitmapSize(uint64_t capacity);
+
template<bool kSetBit>
bool Modify(const mirror::Object* obj);
diff --git a/runtime/gc/allocator/rosalloc-inl.h b/runtime/gc/allocator/rosalloc-inl.h
index f395314..ac0f67b 100644
--- a/runtime/gc/allocator/rosalloc-inl.h
+++ b/runtime/gc/allocator/rosalloc-inl.h
@@ -29,7 +29,7 @@
}
void* m = AllocFromRun(self, size, bytes_allocated);
// Check if the returned memory is really all zero.
- if (kCheckZeroMemory && m != NULL) {
+ if (kCheckZeroMemory && m != nullptr) {
byte* bytes = reinterpret_cast<byte*>(m);
for (size_t i = 0; i < size; ++i) {
DCHECK_EQ(bytes[i], 0);
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 0f2d6a9..821aa2d 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -32,6 +32,10 @@
extern "C" void* art_heap_rosalloc_morecore(RosAlloc* rosalloc, intptr_t increment);
+static constexpr bool kUsePrefetchDuringAllocRun = true;
+static constexpr bool kPrefetchNewRunDataByZeroing = false;
+static constexpr size_t kPrefetchStride = 64;
+
size_t RosAlloc::bracketSizes[kNumOfSizeBrackets];
size_t RosAlloc::numOfPages[kNumOfSizeBrackets];
size_t RosAlloc::numOfSlots[kNumOfSizeBrackets];
@@ -39,6 +43,9 @@
size_t RosAlloc::bulkFreeBitMapOffsets[kNumOfSizeBrackets];
size_t RosAlloc::threadLocalFreeBitMapOffsets[kNumOfSizeBrackets];
bool RosAlloc::initialized_ = false;
+size_t RosAlloc::dedicated_full_run_storage_[kPageSize / sizeof(size_t)] = { 0 };
+RosAlloc::Run* RosAlloc::dedicated_full_run_ =
+ reinterpret_cast<RosAlloc::Run*>(dedicated_full_run_storage_);
RosAlloc::RosAlloc(void* base, size_t capacity, size_t max_capacity,
PageReleaseMode page_release_mode, size_t page_release_size_threshold)
@@ -62,8 +69,9 @@
<< ", max_capacity=" << std::dec << max_capacity_;
memset(current_runs_, 0, sizeof(current_runs_));
for (size_t i = 0; i < kNumOfSizeBrackets; i++) {
- size_bracket_locks_[i] = new Mutex("an rosalloc size bracket lock",
- kRosAllocBracketLock);
+ size_bracket_lock_names[i] =
+ StringPrintf("an rosalloc size bracket %d lock", static_cast<int>(i));
+ size_bracket_locks_[i] = new Mutex(size_bracket_lock_names[i].c_str(), kRosAllocBracketLock);
}
DCHECK_EQ(footprint_, capacity_);
size_t num_of_pages = footprint_ / kPageSize;
@@ -71,7 +79,7 @@
std::string error_msg;
page_map_mem_map_.reset(MemMap::MapAnonymous("rosalloc page map", NULL, RoundUp(max_num_of_pages, kPageSize),
PROT_READ | PROT_WRITE, false, &error_msg));
- CHECK(page_map_mem_map_.get() != NULL) << "Couldn't allocate the page map : " << error_msg;
+ CHECK(page_map_mem_map_.get() != nullptr) << "Couldn't allocate the page map : " << error_msg;
page_map_ = page_map_mem_map_->Begin();
page_map_size_ = num_of_pages;
max_page_map_size_ = max_num_of_pages;
@@ -103,7 +111,7 @@
lock_.AssertHeld(self);
DCHECK(page_map_type == kPageMapRun || page_map_type == kPageMapLargeObject);
FreePageRun* res = NULL;
- size_t req_byte_size = num_pages * kPageSize;
+ const size_t req_byte_size = num_pages * kPageSize;
// Find the lowest address free page run that's large enough.
for (auto it = free_page_runs_.begin(); it != free_page_runs_.end(); ) {
FreePageRun* fpr = *it;
@@ -260,8 +268,7 @@
break;
}
if (kIsDebugBuild) {
- // Clear the first page which isn't madvised away in the debug
- // build for the magic number.
+ // Clear the first page since it is not madvised due to the magic number.
memset(res, 0, kPageSize);
}
if (kTraceRosAlloc) {
@@ -279,7 +286,7 @@
return nullptr;
}
-size_t RosAlloc::FreePages(Thread* self, void* ptr) {
+size_t RosAlloc::FreePages(Thread* self, void* ptr, bool already_zero) {
lock_.AssertHeld(self);
size_t pm_idx = ToPageMapIndex(ptr);
DCHECK_LT(pm_idx, page_map_size_);
@@ -310,10 +317,21 @@
num_pages++;
idx++;
}
+ const size_t byte_size = num_pages * kPageSize;
+ if (already_zero) {
+ if (kCheckZeroMemory) {
+ const uword* word_ptr = reinterpret_cast<uword*>(ptr);
+ for (size_t i = 0; i < byte_size / sizeof(uword); ++i) {
+ CHECK_EQ(word_ptr[i], 0U) << "words don't match at index " << i;
+ }
+ }
+ } else if (!DoesReleaseAllPages()) {
+ memset(ptr, 0, byte_size);
+ }
if (kTraceRosAlloc) {
LOG(INFO) << "RosAlloc::FreePages() : 0x" << std::hex << reinterpret_cast<intptr_t>(ptr)
- << "-0x" << (reinterpret_cast<intptr_t>(ptr) + num_pages * kPageSize)
+ << "-0x" << (reinterpret_cast<intptr_t>(ptr) + byte_size)
<< "(" << std::dec << (num_pages * kPageSize) << ")";
}
@@ -322,8 +340,8 @@
if (kIsDebugBuild) {
fpr->magic_num_ = kMagicNumFree;
}
- fpr->SetByteSize(this, num_pages * kPageSize);
- DCHECK_EQ(fpr->ByteSize(this) % kPageSize, static_cast<size_t>(0));
+ fpr->SetByteSize(this, byte_size);
+ DCHECK(IsAligned<kPageSize>(fpr->ByteSize(this)));
DCHECK(free_page_runs_.find(fpr) == free_page_runs_.end());
if (!free_page_runs_.empty()) {
@@ -349,6 +367,10 @@
if (kTraceRosAlloc) {
LOG(INFO) << "Success";
}
+ // Clear magic num since this is no longer the start of a free page run.
+ if (kIsDebugBuild) {
+ h->magic_num_ = 0;
+ }
free_page_runs_.erase(it++);
if (kTraceRosAlloc) {
LOG(INFO) << "RosAlloc::FreePages() : (coalesce) Erased run 0x" << std::hex
@@ -395,6 +417,10 @@
}
l->SetByteSize(this, l->ByteSize(this) + fpr->ByteSize(this));
DCHECK_EQ(l->ByteSize(this) % kPageSize, static_cast<size_t>(0));
+ // Clear magic num since this is no longer the start of a free page run.
+ if (kIsDebugBuild) {
+ fpr->magic_num_ = 0;
+ }
fpr = l;
} else {
// Not adjacent. Stop.
@@ -422,7 +448,7 @@
LOG(INFO) << "RosAlloc::FreePages() : Inserted run 0x" << std::hex << reinterpret_cast<intptr_t>(fpr)
<< " into free_page_runs_";
}
- return num_pages;
+ return byte_size;
}
void* RosAlloc::AllocLargeObject(Thread* self, size_t size, size_t* bytes_allocated) {
@@ -439,23 +465,19 @@
}
return nullptr;
}
- if (bytes_allocated != NULL) {
- *bytes_allocated = num_pages * kPageSize;
- }
+ const size_t total_bytes = num_pages * kPageSize;
+ *bytes_allocated = total_bytes;
if (kTraceRosAlloc) {
LOG(INFO) << "RosAlloc::AllocLargeObject() : 0x" << std::hex << reinterpret_cast<intptr_t>(r)
<< "-0x" << (reinterpret_cast<intptr_t>(r) + num_pages * kPageSize)
<< "(" << std::dec << (num_pages * kPageSize) << ")";
}
- if (!DoesReleaseAllPages()) {
- // If it does not release all pages, pages may not be zeroed out.
- memset(r, 0, size);
- }
// Check if the returned memory is really all zero.
if (kCheckZeroMemory) {
- byte* bytes = reinterpret_cast<byte*>(r);
- for (size_t i = 0; i < size; ++i) {
- DCHECK_EQ(bytes[i], 0);
+ CHECK_EQ(total_bytes % sizeof(uword), 0U);
+ const uword* words = reinterpret_cast<uword*>(r);
+ for (size_t i = 0; i < total_bytes / sizeof(uword); ++i) {
+ CHECK_EQ(words[i], 0U);
}
}
return r;
@@ -479,7 +501,7 @@
LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx];
return 0;
case kPageMapLargeObject:
- return FreePages(self, ptr) * kPageSize;
+ return FreePages(self, ptr, false);
case kPageMapLargeObjectPart:
LOG(FATAL) << "Unreachable - page map type: " << page_map_[pm_idx];
return 0;
@@ -503,9 +525,7 @@
}
}
DCHECK(run != nullptr);
- const size_t size = IndexToBracketSize(run->size_bracket_idx_);
- FreeFromRun(self, ptr, run);
- return size;
+ return FreeFromRun(self, ptr, run);
}
size_t RosAlloc::Free(Thread* self, void* ptr) {
@@ -513,42 +533,57 @@
return FreeInternal(self, ptr);
}
-RosAlloc::Run* RosAlloc::RefillRun(Thread* self, size_t idx) {
- Run* new_run;
- size_t num_pages = numOfPages[idx];
- // Get the lowest address non-full run from the binary tree.
- Run* temp = NULL;
- std::set<Run*>* bt = &non_full_runs_[idx];
- std::set<Run*>::iterator found = bt->lower_bound(temp);
- if (found != bt->end()) {
- // If there's one, use it as the current run.
- Run* non_full_run = *found;
- DCHECK(non_full_run != NULL);
- new_run = non_full_run;
- DCHECK_EQ(new_run->is_thread_local_, 0);
- bt->erase(found);
- DCHECK_EQ(non_full_run->is_thread_local_, 0);
- } else {
- // If there's none, allocate a new run and use it as the
- // current run.
- {
- MutexLock mu(self, lock_);
- new_run = reinterpret_cast<Run*>(AllocPages(self, num_pages, kPageMapRun));
- }
- if (new_run == NULL) {
- return NULL;
- }
+RosAlloc::Run* RosAlloc::AllocRun(Thread* self, size_t idx) {
+ RosAlloc::Run* new_run = nullptr;
+ {
+ MutexLock mu(self, lock_);
+ new_run = reinterpret_cast<Run*>(AllocPages(self, numOfPages[idx], kPageMapRun));
+ }
+ if (LIKELY(new_run != nullptr)) {
if (kIsDebugBuild) {
new_run->magic_num_ = kMagicNum;
}
new_run->size_bracket_idx_ = idx;
- new_run->top_slot_idx_ = 0;
- new_run->ClearBitMaps();
- new_run->to_be_bulk_freed_ = false;
+ new_run->SetAllocBitMapBitsForInvalidSlots();
+ DCHECK(!new_run->IsThreadLocal());
+ DCHECK_EQ(new_run->first_search_vec_idx_, 0U);
+ DCHECK(!new_run->to_be_bulk_freed_);
+ if (kUsePrefetchDuringAllocRun && idx <= kMaxThreadLocalSizeBracketIdx) {
+ // Take ownership of the cache lines if we are likely to be thread local run.
+ if (kPrefetchNewRunDataByZeroing) {
+ // Zeroing the data is sometimes faster than prefetching but it increases memory usage
+ // since we end up dirtying zero pages which may have been madvised.
+ new_run->ZeroData();
+ } else {
+ const size_t num_of_slots = numOfSlots[idx];
+ const size_t bracket_size = bracketSizes[idx];
+ const size_t num_of_bytes = num_of_slots * bracket_size;
+ byte* begin = reinterpret_cast<byte*>(new_run) + headerSizes[idx];
+ for (size_t i = 0; i < num_of_bytes; i += kPrefetchStride) {
+ __builtin_prefetch(begin + i);
+ }
+ }
+ }
}
return new_run;
}
+RosAlloc::Run* RosAlloc::RefillRun(Thread* self, size_t idx) {
+ // Get the lowest address non-full run from the binary tree.
+ std::set<Run*>* const bt = &non_full_runs_[idx];
+ if (!bt->empty()) {
+ // If there's one, use it as the current run.
+ auto it = bt->begin();
+ Run* non_full_run = *it;
+ DCHECK(non_full_run != nullptr);
+ DCHECK(!non_full_run->IsThreadLocal());
+ bt->erase(it);
+ return non_full_run;
+ }
+ // If there's none, allocate a new run and use it as the current run.
+ return AllocRun(self, idx);
+}
+
void* RosAlloc::AllocFromRun(Thread* self, size_t size, size_t* bytes_allocated) {
DCHECK_LE(size, kLargeSizeThreshold);
size_t bracket_size;
@@ -564,66 +599,62 @@
if (LIKELY(idx <= kMaxThreadLocalSizeBracketIdx)) {
// Use a thread-local run.
Run* thread_local_run = reinterpret_cast<Run*>(self->GetRosAllocRun(idx));
- if (UNLIKELY(thread_local_run == NULL)) {
- MutexLock mu(self, *size_bracket_locks_[idx]);
- thread_local_run = RefillRun(self, idx);
- if (UNLIKELY(thread_local_run == NULL)) {
- return NULL;
- }
- DCHECK(non_full_runs_[idx].find(thread_local_run) == non_full_runs_[idx].end());
- DCHECK(full_runs_[idx].find(thread_local_run) == full_runs_[idx].end());
- thread_local_run->is_thread_local_ = 1;
- self->SetRosAllocRun(idx, thread_local_run);
- DCHECK(!thread_local_run->IsFull());
- }
-
- DCHECK(thread_local_run != NULL);
- DCHECK_NE(thread_local_run->is_thread_local_, 0);
+ // Allow invalid since this will always fail the allocation.
+ DCHECK(non_full_runs_[idx].find(thread_local_run) == non_full_runs_[idx].end());
+ DCHECK(full_runs_[idx].find(thread_local_run) == full_runs_[idx].end());
+ DCHECK(thread_local_run != nullptr);
+ DCHECK(thread_local_run->IsThreadLocal() || thread_local_run == dedicated_full_run_);
slot_addr = thread_local_run->AllocSlot();
-
- if (UNLIKELY(slot_addr == NULL)) {
+ // The allocation must fail if the run is invalid.
+ DCHECK(thread_local_run != dedicated_full_run_ || slot_addr == nullptr)
+ << "allocated from an invalid run";
+ if (UNLIKELY(slot_addr == nullptr)) {
// The run got full. Try to free slots.
DCHECK(thread_local_run->IsFull());
MutexLock mu(self, *size_bracket_locks_[idx]);
bool is_all_free_after_merge;
+ // This is safe to do for the dedicated_full_run_ since the bitmaps are empty.
if (thread_local_run->MergeThreadLocalFreeBitMapToAllocBitMap(&is_all_free_after_merge)) {
+ DCHECK_NE(thread_local_run, dedicated_full_run_);
// Some slot got freed. Keep it.
DCHECK(!thread_local_run->IsFull());
DCHECK_EQ(is_all_free_after_merge, thread_local_run->IsAllFree());
if (is_all_free_after_merge) {
- // Reinstate the bump index mode if it's all free.
- DCHECK_EQ(thread_local_run->top_slot_idx_, numOfSlots[idx]);
- thread_local_run->top_slot_idx_ = 0;
+ // Check that the bitmap idx is back at 0 if it's all free.
+ DCHECK_EQ(thread_local_run->first_search_vec_idx_, 0U);
}
} else {
// No slots got freed. Try to refill the thread-local run.
DCHECK(thread_local_run->IsFull());
- self->SetRosAllocRun(idx, nullptr);
- thread_local_run->is_thread_local_ = 0;
- if (kIsDebugBuild) {
- full_runs_[idx].insert(thread_local_run);
- if (kTraceRosAlloc) {
- LOG(INFO) << "RosAlloc::AllocFromRun() : Inserted run 0x" << std::hex
- << reinterpret_cast<intptr_t>(thread_local_run)
- << " into full_runs_[" << std::dec << idx << "]";
+ if (thread_local_run != dedicated_full_run_) {
+ self->SetRosAllocRun(idx, dedicated_full_run_);
+ thread_local_run->SetIsThreadLocal(false);
+ if (kIsDebugBuild) {
+ full_runs_[idx].insert(thread_local_run);
+ if (kTraceRosAlloc) {
+ LOG(INFO) << "RosAlloc::AllocFromRun() : Inserted run 0x" << std::hex
+ << reinterpret_cast<intptr_t>(thread_local_run)
+ << " into full_runs_[" << std::dec << idx << "]";
+ }
}
+ DCHECK(non_full_runs_[idx].find(thread_local_run) == non_full_runs_[idx].end());
+ DCHECK(full_runs_[idx].find(thread_local_run) != full_runs_[idx].end());
}
- DCHECK(non_full_runs_[idx].find(thread_local_run) == non_full_runs_[idx].end());
- DCHECK(full_runs_[idx].find(thread_local_run) != full_runs_[idx].end());
+
thread_local_run = RefillRun(self, idx);
if (UNLIKELY(thread_local_run == NULL)) {
return NULL;
}
DCHECK(non_full_runs_[idx].find(thread_local_run) == non_full_runs_[idx].end());
DCHECK(full_runs_[idx].find(thread_local_run) == full_runs_[idx].end());
- thread_local_run->is_thread_local_ = 1;
+ thread_local_run->SetIsThreadLocal(true);
self->SetRosAllocRun(idx, thread_local_run);
DCHECK(!thread_local_run->IsFull());
}
DCHECK(thread_local_run != NULL);
DCHECK(!thread_local_run->IsFull());
- DCHECK_NE(thread_local_run->is_thread_local_, 0);
+ DCHECK(thread_local_run->IsThreadLocal());
slot_addr = thread_local_run->AllocSlot();
// Must succeed now with a new run.
DCHECK(slot_addr != NULL);
@@ -644,7 +675,7 @@
}
DCHECK(non_full_runs_[idx].find(current_run) == non_full_runs_[idx].end());
DCHECK(full_runs_[idx].find(current_run) == full_runs_[idx].end());
- current_run->is_thread_local_ = 0;
+ current_run->SetIsThreadLocal(false);
current_runs_[idx] = current_run;
DCHECK(!current_run->IsFull());
}
@@ -671,7 +702,7 @@
DCHECK(current_run != NULL);
DCHECK(non_full_runs_[idx].find(current_run) == non_full_runs_[idx].end());
DCHECK(full_runs_[idx].find(current_run) == full_runs_[idx].end());
- current_run->is_thread_local_ = 0;
+ current_run->SetIsThreadLocal(false);
current_runs_[idx] = current_run;
DCHECK(!current_run->IsFull());
slot_addr = current_run->AllocSlot();
@@ -684,27 +715,27 @@
<< "(" << std::dec << (bracket_size) << ")";
}
}
- if (LIKELY(bytes_allocated != NULL)) {
- *bytes_allocated = bracket_size;
- }
- memset(slot_addr, 0, size);
+ DCHECK(bytes_allocated != nullptr);
+ *bytes_allocated = bracket_size;
+ // Caller verifies that it is all 0.
return slot_addr;
}
-void RosAlloc::FreeFromRun(Thread* self, void* ptr, Run* run) {
+size_t RosAlloc::FreeFromRun(Thread* self, void* ptr, Run* run) {
DCHECK_EQ(run->magic_num_, kMagicNum);
DCHECK_LT(run, ptr);
DCHECK_LT(ptr, run->End());
- size_t idx = run->size_bracket_idx_;
- MutexLock mu(self, *size_bracket_locks_[idx]);
+ const size_t idx = run->size_bracket_idx_;
+ const size_t bracket_size = bracketSizes[idx];
bool run_was_full = false;
+ MutexLock mu(self, *size_bracket_locks_[idx]);
if (kIsDebugBuild) {
run_was_full = run->IsFull();
}
if (kTraceRosAlloc) {
LOG(INFO) << "RosAlloc::FreeFromRun() : 0x" << std::hex << reinterpret_cast<intptr_t>(ptr);
}
- if (LIKELY(run->is_thread_local_ != 0)) {
+ if (LIKELY(run->IsThreadLocal())) {
// It's a thread-local run. Just mark the thread-local free bit map and return.
DCHECK_LE(run->size_bracket_idx_, kMaxThreadLocalSizeBracketIdx);
DCHECK(non_full_runs_[idx].find(run) == non_full_runs_[idx].end());
@@ -715,7 +746,7 @@
<< reinterpret_cast<intptr_t>(run);
}
// A thread local run will be kept as a thread local even if it's become all free.
- return;
+ return bracket_size;
}
// Free the slot in the run.
run->FreeSlot(ptr);
@@ -735,9 +766,10 @@
}
DCHECK(non_full_runs_[idx].find(run) == non_full_runs_[idx].end());
DCHECK(full_runs_[idx].find(run) == full_runs_[idx].end());
+ run->ZeroHeader();
{
MutexLock mu(self, lock_);
- FreePages(self, run);
+ FreePages(self, run, true);
}
} else {
// It is not completely free. If it wasn't the current run or
@@ -767,6 +799,7 @@
}
}
}
+ return bracket_size;
}
std::string RosAlloc::Run::BitMapToStr(uint32_t* bit_map_base, size_t num_vec) {
@@ -792,7 +825,7 @@
<< " size_bracket_idx=" << idx
<< " is_thread_local=" << static_cast<int>(is_thread_local_)
<< " to_be_bulk_freed=" << static_cast<int>(to_be_bulk_freed_)
- << " top_slot_idx=" << top_slot_idx_
+ << " first_search_vec_idx=" << first_search_vec_idx_
<< " alloc_bit_map=" << BitMapToStr(alloc_bit_map_, num_vec)
<< " bulk_free_bit_map=" << BitMapToStr(BulkFreeBitMap(), num_vec)
<< " thread_local_bit_map=" << BitMapToStr(ThreadLocalFreeBitMap(), num_vec)
@@ -800,64 +833,52 @@
return stream.str();
}
-void* RosAlloc::Run::AllocSlot() {
- size_t idx = size_bracket_idx_;
- size_t num_slots = numOfSlots[idx];
- DCHECK_LE(top_slot_idx_, num_slots);
- if (LIKELY(top_slot_idx_ < num_slots)) {
- // If it's in bump index mode, grab the top slot and increment the top index.
- size_t slot_idx = top_slot_idx_;
- byte* slot_addr = reinterpret_cast<byte*>(this) + headerSizes[idx] + slot_idx * bracketSizes[idx];
- if (kTraceRosAlloc) {
- LOG(INFO) << "RosAlloc::Run::AllocSlot() : 0x" << std::hex << reinterpret_cast<intptr_t>(slot_addr)
- << ", bracket_size=" << std::dec << bracketSizes[idx] << ", slot_idx=" << slot_idx;
+inline void* RosAlloc::Run::AllocSlot() {
+ const size_t idx = size_bracket_idx_;
+ while (true) {
+ if (kIsDebugBuild) {
+ // Make sure that no slots leaked, the bitmap should be full for all previous vectors.
+ for (size_t i = 0; i < first_search_vec_idx_; ++i) {
+ CHECK_EQ(~alloc_bit_map_[i], 0U);
+ }
}
- top_slot_idx_++;
- size_t vec_idx = slot_idx / 32;
- size_t vec_off = slot_idx % 32;
- uint32_t* vec = &alloc_bit_map_[vec_idx];
- DCHECK_EQ((*vec & (1 << vec_off)), static_cast<uint32_t>(0));
- *vec |= 1 << vec_off;
- DCHECK_NE((*vec & (1 << vec_off)), static_cast<uint32_t>(0));
- return slot_addr;
- }
- // Not in bump index mode. Search the alloc bit map for an empty slot.
- size_t num_vec = RoundUp(num_slots, 32) / 32;
- size_t slot_idx = 0;
- bool found_slot = false;
- for (size_t v = 0; v < num_vec; v++) {
- uint32_t *vecp = &alloc_bit_map_[v];
- uint32_t ffz1 = __builtin_ffs(~*vecp);
- uint32_t ffz;
- // TODO: Use LIKELY or UNLIKELY here?
- if (LIKELY(ffz1 > 0 && (ffz = ffz1 - 1) + v * 32 < num_slots)) {
+ uint32_t* const alloc_bitmap_ptr = &alloc_bit_map_[first_search_vec_idx_];
+ uint32_t ffz1 = __builtin_ffs(~*alloc_bitmap_ptr);
+ if (LIKELY(ffz1 != 0)) {
+ const uint32_t ffz = ffz1 - 1;
+ const uint32_t slot_idx = ffz + first_search_vec_idx_ * sizeof(*alloc_bitmap_ptr) * kBitsPerByte;
+ const uint32_t mask = 1U << ffz;
+ DCHECK_LT(slot_idx, numOfSlots[idx]) << "out of range";
// Found an empty slot. Set the bit.
- DCHECK_EQ((*vecp & (1 << ffz)), static_cast<uint32_t>(0));
- *vecp |= (1 << ffz);
- DCHECK_NE((*vecp & (1 << ffz)), static_cast<uint32_t>(0));
- slot_idx = ffz + v * 32;
- found_slot = true;
- break;
+ DCHECK_EQ(*alloc_bitmap_ptr & mask, 0U);
+ *alloc_bitmap_ptr |= mask;
+ DCHECK_NE(*alloc_bitmap_ptr & mask, 0U);
+ byte* slot_addr = reinterpret_cast<byte*>(this) + headerSizes[idx] + slot_idx * bracketSizes[idx];
+ if (kTraceRosAlloc) {
+ LOG(INFO) << "RosAlloc::Run::AllocSlot() : 0x" << std::hex << reinterpret_cast<intptr_t>(slot_addr)
+ << ", bracket_size=" << std::dec << bracketSizes[idx] << ", slot_idx=" << slot_idx;
+ }
+ return slot_addr;
}
- }
- if (LIKELY(found_slot)) {
- byte* slot_addr = reinterpret_cast<byte*>(this) + headerSizes[idx] + slot_idx * bracketSizes[idx];
- if (kTraceRosAlloc) {
- LOG(INFO) << "RosAlloc::Run::AllocSlot() : 0x" << std::hex << reinterpret_cast<intptr_t>(slot_addr)
- << ", bracket_size=" << std::dec << bracketSizes[idx] << ", slot_idx=" << slot_idx;
+ const size_t num_words = RoundUp(numOfSlots[idx], 32) / 32;
+ if (first_search_vec_idx_ + 1 >= num_words) {
+ DCHECK(IsFull());
+ // Already at the last word, return null.
+ return nullptr;
}
- return slot_addr;
+ // Increase the index to the next word and try again.
+ ++first_search_vec_idx_;
}
- return NULL;
}
-inline void RosAlloc::Run::FreeSlot(void* ptr) {
- DCHECK_EQ(is_thread_local_, 0);
- byte idx = size_bracket_idx_;
- size_t offset_from_slot_base = reinterpret_cast<byte*>(ptr)
+void RosAlloc::Run::FreeSlot(void* ptr) {
+ DCHECK(!IsThreadLocal());
+ const byte idx = size_bracket_idx_;
+ const size_t bracket_size = bracketSizes[idx];
+ const size_t offset_from_slot_base = reinterpret_cast<byte*>(ptr)
- (reinterpret_cast<byte*>(this) + headerSizes[idx]);
- DCHECK_EQ(offset_from_slot_base % bracketSizes[idx], static_cast<size_t>(0));
- size_t slot_idx = offset_from_slot_base / bracketSizes[idx];
+ DCHECK_EQ(offset_from_slot_base % bracket_size, static_cast<size_t>(0));
+ size_t slot_idx = offset_from_slot_base / bracket_size;
DCHECK_LT(slot_idx, numOfSlots[idx]);
size_t vec_idx = slot_idx / 32;
if (kIsDebugBuild) {
@@ -866,9 +887,14 @@
}
size_t vec_off = slot_idx % 32;
uint32_t* vec = &alloc_bit_map_[vec_idx];
- DCHECK_NE((*vec & (1 << vec_off)), static_cast<uint32_t>(0));
- *vec &= ~(1 << vec_off);
- DCHECK_EQ((*vec & (1 << vec_off)), static_cast<uint32_t>(0));
+ first_search_vec_idx_ = std::min(first_search_vec_idx_, static_cast<uint32_t>(vec_idx));
+ const uint32_t mask = 1U << vec_off;
+ DCHECK_NE(*vec & mask, 0U);
+ *vec &= ~mask;
+ DCHECK_EQ(*vec & mask, 0U);
+ // Zero out the memory.
+ // TODO: Investigate alternate memset since ptr is guaranteed to be aligned to 16.
+ memset(ptr, 0, bracket_size);
if (kTraceRosAlloc) {
LOG(INFO) << "RosAlloc::Run::FreeSlot() : 0x" << std::hex << reinterpret_cast<intptr_t>(ptr)
<< ", bracket_size=" << std::dec << bracketSizes[idx] << ", slot_idx=" << slot_idx;
@@ -876,11 +902,11 @@
}
inline bool RosAlloc::Run::MergeThreadLocalFreeBitMapToAllocBitMap(bool* is_all_free_after_out) {
- DCHECK_NE(is_thread_local_, 0);
+ DCHECK(IsThreadLocal());
// Free slots in the alloc bit map based on the thread local free bit map.
- byte idx = size_bracket_idx_;
- size_t num_slots = numOfSlots[idx];
- size_t num_vec = RoundUp(num_slots, 32) / 32;
+ const size_t idx = size_bracket_idx_;
+ const size_t num_of_slots = numOfSlots[idx];
+ const size_t num_vec = RoundUp(num_of_slots, 32) / 32;
bool changed = false;
uint32_t* vecp = &alloc_bit_map_[0];
uint32_t* tl_free_vecp = &ThreadLocalFreeBitMap()[0];
@@ -890,6 +916,7 @@
uint32_t vec_before = *vecp;
uint32_t vec_after;
if (tl_free_vec != 0) {
+ first_search_vec_idx_ = std::min(first_search_vec_idx_, static_cast<uint32_t>(v));
vec_after = vec_before & ~tl_free_vec;
*vecp = vec_after;
changed = true;
@@ -898,7 +925,13 @@
vec_after = vec_before;
}
if (vec_after != 0) {
- is_all_free_after = false;
+ if (v == num_vec - 1) {
+ // Only not all free if a bit other than the mask bits are set.
+ is_all_free_after =
+ is_all_free_after && GetBitmapLastVectorMask(num_of_slots, num_vec) == vec_after;
+ } else {
+ is_all_free_after = false;
+ }
}
DCHECK_EQ(*tl_free_vecp, static_cast<uint32_t>(0));
}
@@ -909,16 +942,15 @@
}
inline void RosAlloc::Run::MergeBulkFreeBitMapIntoAllocBitMap() {
- DCHECK_EQ(is_thread_local_, 0);
+ DCHECK(!IsThreadLocal());
// Free slots in the alloc bit map based on the bulk free bit map.
- byte idx = size_bracket_idx_;
- size_t num_slots = numOfSlots[idx];
- size_t num_vec = RoundUp(num_slots, 32) / 32;
+ const size_t num_vec = NumberOfBitmapVectors();
uint32_t* vecp = &alloc_bit_map_[0];
uint32_t* free_vecp = &BulkFreeBitMap()[0];
for (size_t v = 0; v < num_vec; v++, vecp++, free_vecp++) {
uint32_t free_vec = *free_vecp;
if (free_vec != 0) {
+ first_search_vec_idx_ = std::min(first_search_vec_idx_, static_cast<uint32_t>(v));
*vecp &= ~free_vec;
*free_vecp = 0; // clear the bulk free bit map.
}
@@ -927,11 +959,9 @@
}
inline void RosAlloc::Run::UnionBulkFreeBitMapToThreadLocalFreeBitMap() {
- DCHECK_NE(is_thread_local_, 0);
+ DCHECK(IsThreadLocal());
// Union the thread local bit map with the bulk free bit map.
- byte idx = size_bracket_idx_;
- size_t num_slots = numOfSlots[idx];
- size_t num_vec = RoundUp(num_slots, 32) / 32;
+ size_t num_vec = NumberOfBitmapVectors();
uint32_t* to_vecp = &ThreadLocalFreeBitMap()[0];
uint32_t* from_vecp = &BulkFreeBitMap()[0];
for (size_t v = 0; v < num_vec; v++, to_vecp++, from_vecp++) {
@@ -945,66 +975,71 @@
}
inline void RosAlloc::Run::MarkThreadLocalFreeBitMap(void* ptr) {
- DCHECK_NE(is_thread_local_, 0);
+ DCHECK(IsThreadLocal());
MarkFreeBitMapShared(ptr, ThreadLocalFreeBitMap(), "MarkThreadLocalFreeBitMap");
}
-inline void RosAlloc::Run::MarkBulkFreeBitMap(void* ptr) {
- MarkFreeBitMapShared(ptr, BulkFreeBitMap(), "MarkFreeBitMap");
+inline size_t RosAlloc::Run::MarkBulkFreeBitMap(void* ptr) {
+ return MarkFreeBitMapShared(ptr, BulkFreeBitMap(), "MarkFreeBitMap");
}
-inline void RosAlloc::Run::MarkFreeBitMapShared(void* ptr, uint32_t* free_bit_map_base,
- const char* caller_name) {
- byte idx = size_bracket_idx_;
- size_t offset_from_slot_base = reinterpret_cast<byte*>(ptr)
+inline size_t RosAlloc::Run::MarkFreeBitMapShared(void* ptr, uint32_t* free_bit_map_base,
+ const char* caller_name) {
+ const byte idx = size_bracket_idx_;
+ const size_t offset_from_slot_base = reinterpret_cast<byte*>(ptr)
- (reinterpret_cast<byte*>(this) + headerSizes[idx]);
- DCHECK_EQ(offset_from_slot_base % bracketSizes[idx], static_cast<size_t>(0));
- size_t slot_idx = offset_from_slot_base / bracketSizes[idx];
+ const size_t bracket_size = bracketSizes[idx];
+ memset(ptr, 0, bracket_size);
+ DCHECK_EQ(offset_from_slot_base % bracket_size, static_cast<size_t>(0));
+ size_t slot_idx = offset_from_slot_base / bracket_size;
DCHECK_LT(slot_idx, numOfSlots[idx]);
size_t vec_idx = slot_idx / 32;
if (kIsDebugBuild) {
- size_t num_vec = RoundUp(numOfSlots[idx], 32) / 32;
+ size_t num_vec = NumberOfBitmapVectors();
DCHECK_LT(vec_idx, num_vec);
}
size_t vec_off = slot_idx % 32;
uint32_t* vec = &free_bit_map_base[vec_idx];
- DCHECK_EQ((*vec & (1 << vec_off)), static_cast<uint32_t>(0));
- *vec |= 1 << vec_off;
- DCHECK_NE((*vec & (1 << vec_off)), static_cast<uint32_t>(0));
+ const uint32_t mask = 1U << vec_off;
+ DCHECK_EQ(*vec & mask, 0U);
+ *vec |= mask;
+ DCHECK_NE(*vec & mask, 0U);
if (kTraceRosAlloc) {
LOG(INFO) << "RosAlloc::Run::" << caller_name << "() : 0x" << std::hex
<< reinterpret_cast<intptr_t>(ptr)
<< ", bracket_size=" << std::dec << bracketSizes[idx] << ", slot_idx=" << slot_idx;
}
+ return bracket_size;
+}
+
+inline uint32_t RosAlloc::Run::GetBitmapLastVectorMask(size_t num_slots, size_t num_vec) {
+ const size_t kBitsPerVec = 32;
+ DCHECK_GE(num_slots * kBitsPerVec, num_vec);
+ size_t remain = num_vec * kBitsPerVec - num_slots;
+ DCHECK_NE(remain, kBitsPerVec);
+ return ((1U << remain) - 1) << (kBitsPerVec - remain);
}
inline bool RosAlloc::Run::IsAllFree() {
- byte idx = size_bracket_idx_;
- size_t num_slots = numOfSlots[idx];
- size_t num_vec = RoundUp(num_slots, 32) / 32;
- for (size_t v = 0; v < num_vec; v++) {
+ const byte idx = size_bracket_idx_;
+ const size_t num_slots = numOfSlots[idx];
+ const size_t num_vec = NumberOfBitmapVectors();
+ DCHECK_NE(num_vec, 0U);
+ // Check the last vector after the loop since it uses a special case for the masked bits.
+ for (size_t v = 0; v < num_vec - 1; v++) {
uint32_t vec = alloc_bit_map_[v];
if (vec != 0) {
return false;
}
}
- return true;
+ // Make sure the last word is equal to the mask, all other bits must be 0.
+ return alloc_bit_map_[num_vec - 1] == GetBitmapLastVectorMask(num_slots, num_vec);
}
inline bool RosAlloc::Run::IsFull() {
- byte idx = size_bracket_idx_;
- size_t num_slots = numOfSlots[idx];
- size_t num_vec = RoundUp(num_slots, 32) / 32;
- size_t slots = 0;
- for (size_t v = 0; v < num_vec; v++, slots += 32) {
- DCHECK_GE(num_slots, slots);
- uint32_t vec = alloc_bit_map_[v];
- uint32_t mask = (num_slots - slots >= 32) ? static_cast<uint32_t>(-1)
- : (1 << (num_slots - slots)) - 1;
- if ((num_slots - slots) >= 32) {
- DCHECK_EQ(mask, static_cast<uint32_t>(-1));
- }
- if (vec != mask) {
+ const size_t num_vec = NumberOfBitmapVectors();
+ for (size_t v = 0; v < num_vec; ++v) {
+ if (~alloc_bit_map_[v] != 0) {
return false;
}
}
@@ -1012,9 +1047,7 @@
}
inline bool RosAlloc::Run::IsBulkFreeBitmapClean() {
- byte idx = size_bracket_idx_;
- size_t num_slots = numOfSlots[idx];
- size_t num_vec = RoundUp(num_slots, 32) / 32;
+ const size_t num_vec = NumberOfBitmapVectors();
for (size_t v = 0; v < num_vec; v++) {
uint32_t vec = BulkFreeBitMap()[v];
if (vec != 0) {
@@ -1025,9 +1058,7 @@
}
inline bool RosAlloc::Run::IsThreadLocalFreeBitmapClean() {
- byte idx = size_bracket_idx_;
- size_t num_slots = numOfSlots[idx];
- size_t num_vec = RoundUp(num_slots, 32) / 32;
+ const size_t num_vec = NumberOfBitmapVectors();
for (size_t v = 0; v < num_vec; v++) {
uint32_t vec = ThreadLocalFreeBitMap()[v];
if (vec != 0) {
@@ -1037,11 +1068,31 @@
return true;
}
-inline void RosAlloc::Run::ClearBitMaps() {
- byte idx = size_bracket_idx_;
- size_t num_slots = numOfSlots[idx];
- size_t num_vec = RoundUp(num_slots, 32) / 32;
- memset(alloc_bit_map_, 0, sizeof(uint32_t) * num_vec * 3);
+inline void RosAlloc::Run::SetAllocBitMapBitsForInvalidSlots() {
+ const size_t idx = size_bracket_idx_;
+ const size_t num_slots = numOfSlots[idx];
+ const size_t num_vec = RoundUp(num_slots, 32) / 32;
+ DCHECK_NE(num_vec, 0U);
+ // Make sure to set the bits at the end of the bitmap so that we don't allocate there since they
+ // don't represent valid slots.
+ alloc_bit_map_[num_vec - 1] |= GetBitmapLastVectorMask(num_slots, num_vec);
+}
+
+inline void RosAlloc::Run::ZeroHeader() {
+ const byte idx = size_bracket_idx_;
+ memset(this, 0, headerSizes[idx]);
+}
+
+inline void RosAlloc::Run::ZeroData() {
+ const byte idx = size_bracket_idx_;
+ byte* slot_begin = reinterpret_cast<byte*>(this) + headerSizes[idx];
+ memset(slot_begin, 0, numOfSlots[idx] * bracketSizes[idx]);
+}
+
+inline void RosAlloc::Run::FillAllocBitMap() {
+ size_t num_vec = NumberOfBitmapVectors();
+ memset(alloc_bit_map_, 0xFF, sizeof(uint32_t) * num_vec);
+ first_search_vec_idx_ = num_vec - 1; // No free bits in any of the bitmap words.
}
void RosAlloc::Run::InspectAllSlots(void (*handler)(void* start, void* end, size_t used_bytes, void* callback_arg),
@@ -1073,7 +1124,7 @@
// lock for better performance, assuming that the existence of an
// allocated chunk/pointer being freed in BulkFree() guarantees that
// the page map entry won't change. Disabled for now.
-static constexpr bool kReadPageMapEntryWithoutLockInBulkFree = false;
+static constexpr bool kReadPageMapEntryWithoutLockInBulkFree = true;
size_t RosAlloc::BulkFree(Thread* self, void** ptrs, size_t num_ptrs) {
size_t freed_bytes = 0;
@@ -1096,11 +1147,10 @@
#endif
for (size_t i = 0; i < num_ptrs; i++) {
void* ptr = ptrs[i];
- ptrs[i] = NULL;
DCHECK_LE(base_, ptr);
DCHECK_LT(ptr, base_ + footprint_);
size_t pm_idx = RoundDownToPageMapIndex(ptr);
- Run* run = NULL;
+ Run* run = nullptr;
if (kReadPageMapEntryWithoutLockInBulkFree) {
// Read the page map entries without locking the lock.
byte page_map_entry = page_map_[pm_idx];
@@ -1111,106 +1161,74 @@
}
if (LIKELY(page_map_entry == kPageMapRun)) {
run = reinterpret_cast<Run*>(base_ + pm_idx * kPageSize);
- DCHECK_EQ(run->magic_num_, kMagicNum);
} else if (LIKELY(page_map_entry == kPageMapRunPart)) {
size_t pi = pm_idx;
- DCHECK(page_map_[pi] == kPageMapRun || page_map_[pi] == kPageMapRunPart);
// Find the beginning of the run.
- while (page_map_[pi] != kPageMapRun) {
- pi--;
+ do {
+ --pi;
DCHECK_LT(pi, capacity_ / kPageSize);
- }
- DCHECK_EQ(page_map_[pi], kPageMapRun);
+ } while (page_map_[pi] != kPageMapRun);
run = reinterpret_cast<Run*>(base_ + pi * kPageSize);
- DCHECK_EQ(run->magic_num_, kMagicNum);
} else if (page_map_entry == kPageMapLargeObject) {
MutexLock mu(self, lock_);
- freed_bytes += FreePages(self, ptr) * kPageSize;
+ freed_bytes += FreePages(self, ptr, false);
continue;
} else {
LOG(FATAL) << "Unreachable - page map type: " << page_map_entry;
}
- DCHECK(run != nullptr);
- // Set the bit in the bulk free bit map.
- run->MarkBulkFreeBitMap(ptr);
- freed_bytes += IndexToBracketSize(run->size_bracket_idx_);
-#ifdef HAVE_ANDROID_OS
- if (!run->to_be_bulk_freed_) {
- run->to_be_bulk_freed_ = true;
- runs.push_back(run);
- }
-#else
- runs.insert(run);
-#endif
} else {
// Read the page map entries with a lock.
- bool free_from_run = false;
- {
- MutexLock mu(self, lock_);
- DCHECK_LT(pm_idx, page_map_size_);
- byte page_map_entry = page_map_[pm_idx];
- if (kTraceRosAlloc) {
- LOG(INFO) << "RosAlloc::BulkFree() : " << std::hex << ptr << ", pm_idx="
- << std::dec << pm_idx
- << ", page_map_entry=" << static_cast<int>(page_map_entry);
- }
- if (LIKELY(page_map_entry == kPageMapRun)) {
- free_from_run = true;
- run = reinterpret_cast<Run*>(base_ + pm_idx * kPageSize);
- DCHECK_EQ(run->magic_num_, kMagicNum);
- } else if (LIKELY(page_map_entry == kPageMapRunPart)) {
- free_from_run = true;
- size_t pi = pm_idx;
- DCHECK(page_map_[pi] == kPageMapRun || page_map_[pi] == kPageMapRunPart);
- // Find the beginning of the run.
- while (page_map_[pi] != kPageMapRun) {
- pi--;
- DCHECK_LT(pi, capacity_ / kPageSize);
- }
- DCHECK_EQ(page_map_[pi], kPageMapRun);
- run = reinterpret_cast<Run*>(base_ + pi * kPageSize);
- DCHECK_EQ(run->magic_num_, kMagicNum);
- } else if (page_map_entry == kPageMapLargeObject) {
- freed_bytes += FreePages(self, ptr) * kPageSize;
- } else {
- LOG(FATAL) << "Unreachable - page map type: " << page_map_entry;
- }
+ MutexLock mu(self, lock_);
+ DCHECK_LT(pm_idx, page_map_size_);
+ byte page_map_entry = page_map_[pm_idx];
+ if (kTraceRosAlloc) {
+ LOG(INFO) << "RosAlloc::BulkFree() : " << std::hex << ptr << ", pm_idx="
+ << std::dec << pm_idx
+ << ", page_map_entry=" << static_cast<int>(page_map_entry);
}
- if (LIKELY(free_from_run)) {
- DCHECK(run != NULL);
- // Set the bit in the bulk free bit map.
- run->MarkBulkFreeBitMap(ptr);
- freed_bytes += IndexToBracketSize(run->size_bracket_idx_);
-#ifdef HAVE_ANDROID_OS
- if (!run->to_be_bulk_freed_) {
- run->to_be_bulk_freed_ = true;
- runs.push_back(run);
- }
-#else
- runs.insert(run);
-#endif
+ if (LIKELY(page_map_entry == kPageMapRun)) {
+ run = reinterpret_cast<Run*>(base_ + pm_idx * kPageSize);
+ } else if (LIKELY(page_map_entry == kPageMapRunPart)) {
+ size_t pi = pm_idx;
+ // Find the beginning of the run.
+ do {
+ --pi;
+ DCHECK_LT(pi, capacity_ / kPageSize);
+ } while (page_map_[pi] != kPageMapRun);
+ run = reinterpret_cast<Run*>(base_ + pi * kPageSize);
+ } else if (page_map_entry == kPageMapLargeObject) {
+ freed_bytes += FreePages(self, ptr, false);
+ continue;
+ } else {
+ LOG(FATAL) << "Unreachable - page map type: " << page_map_entry;
}
}
+ DCHECK(run != nullptr);
+ DCHECK_EQ(run->magic_num_, kMagicNum);
+ // Set the bit in the bulk free bit map.
+ freed_bytes += run->MarkBulkFreeBitMap(ptr);
+#ifdef HAVE_ANDROID_OS
+ if (!run->to_be_bulk_freed_) {
+ run->to_be_bulk_freed_ = true;
+ runs.push_back(run);
+ }
+#else
+ runs.insert(run);
+#endif
}
// Now, iterate over the affected runs and update the alloc bit map
// based on the bulk free bit map (for non-thread-local runs) and
// union the bulk free bit map into the thread-local free bit map
// (for thread-local runs.)
-#ifdef HAVE_ANDROID_OS
- typedef std::vector<Run*>::iterator It;
-#else
- typedef hash_set<Run*, hash_run, eq_run>::iterator It;
-#endif
- for (It it = runs.begin(); it != runs.end(); ++it) {
- Run* run = *it;
+ for (Run* run : runs) {
#ifdef HAVE_ANDROID_OS
DCHECK(run->to_be_bulk_freed_);
run->to_be_bulk_freed_ = false;
#endif
size_t idx = run->size_bracket_idx_;
MutexLock mu(self, *size_bracket_locks_[idx]);
- if (run->is_thread_local_ != 0) {
+ if (run->IsThreadLocal()) {
DCHECK_LE(run->size_bracket_idx_, kMaxThreadLocalSizeBracketIdx);
DCHECK(non_full_runs_[idx].find(run) == non_full_runs_[idx].end());
DCHECK(full_runs_[idx].find(run) == full_runs_[idx].end());
@@ -1219,7 +1237,7 @@
LOG(INFO) << "RosAlloc::BulkFree() : Freed slot(s) in a thread local run 0x"
<< std::hex << reinterpret_cast<intptr_t>(run);
}
- DCHECK_NE(run->is_thread_local_, 0);
+ DCHECK(run->IsThreadLocal());
// A thread local run will be kept as a thread local even if
// it's become all free.
} else {
@@ -1269,8 +1287,9 @@
DCHECK(non_full_runs->find(run) == non_full_runs->end());
}
if (!run_was_current) {
+ run->ZeroHeader();
MutexLock mu(self, lock_);
- FreePages(self, run);
+ FreePages(self, run, true);
}
} else {
// It is not completely free. If it wasn't the current run or
@@ -1381,7 +1400,7 @@
stream << "[" << i << "]=Run (start)"
<< " idx=" << idx
<< " numOfPages=" << numOfPages[idx]
- << " thread_local=" << static_cast<int>(run->is_thread_local_)
+ << " is_thread_local=" << run->is_thread_local_
<< " is_all_free=" << (run->IsAllFree() ? 1 : 0)
<< std::endl;
break;
@@ -1556,6 +1575,8 @@
// The start of a run.
Run* run = reinterpret_cast<Run*>(base_ + i * kPageSize);
DCHECK_EQ(run->magic_num_, kMagicNum);
+ // The dedicated full run doesn't contain any real allocations, don't visit the slots in
+ // there.
run->InspectAllSlots(handler, arg);
size_t num_pages = numOfPages[run->size_bracket_idx_];
if (kIsDebugBuild) {
@@ -1605,14 +1626,16 @@
for (size_t idx = 0; idx < kNumOfSizeBrackets; idx++) {
MutexLock mu(self, *size_bracket_locks_[idx]);
Run* thread_local_run = reinterpret_cast<Run*>(thread->GetRosAllocRun(idx));
- if (thread_local_run != NULL) {
+ CHECK(thread_local_run != nullptr);
+ // Invalid means already revoked.
+ DCHECK(thread_local_run->IsThreadLocal());
+ if (thread_local_run != dedicated_full_run_) {
+ thread->SetRosAllocRun(idx, dedicated_full_run_);
DCHECK_EQ(thread_local_run->magic_num_, kMagicNum);
- DCHECK_NE(thread_local_run->is_thread_local_, 0);
- thread->SetRosAllocRun(idx, nullptr);
// Note the thread local run may not be full here.
bool dont_care;
thread_local_run->MergeThreadLocalFreeBitMapToAllocBitMap(&dont_care);
- thread_local_run->is_thread_local_ = 0;
+ thread_local_run->SetIsThreadLocal(false);
thread_local_run->MergeBulkFreeBitMapIntoAllocBitMap();
DCHECK(non_full_runs_[idx].find(thread_local_run) == non_full_runs_[idx].end());
DCHECK(full_runs_[idx].find(thread_local_run) == full_runs_[idx].end());
@@ -1628,7 +1651,8 @@
}
} else if (thread_local_run->IsAllFree()) {
MutexLock mu(self, lock_);
- FreePages(self, thread_local_run);
+ thread_local_run->ZeroHeader();
+ FreePages(self, thread_local_run, true);
} else {
non_full_runs_[idx].insert(thread_local_run);
DCHECK(non_full_runs_[idx].find(thread_local_run) != non_full_runs_[idx].end());
@@ -1648,9 +1672,8 @@
MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_);
MutexLock mu2(Thread::Current(), *Locks::thread_list_lock_);
std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
- for (auto it = thread_list.begin(); it != thread_list.end(); ++it) {
- Thread* t = *it;
- RevokeThreadLocalRuns(t);
+ for (Thread* thread : thread_list) {
+ RevokeThreadLocalRuns(thread);
}
}
@@ -1662,7 +1685,7 @@
for (size_t idx = 0; idx < kNumOfSizeBrackets; idx++) {
MutexLock mu(self, *size_bracket_locks_[idx]);
Run* thread_local_run = reinterpret_cast<Run*>(thread->GetRosAllocRun(idx));
- DCHECK(thread_local_run == nullptr);
+ DCHECK(thread_local_run == nullptr || thread_local_run == dedicated_full_run_);
}
}
}
@@ -1770,6 +1793,15 @@
<< ", threadLocalFreeBitMapOffsets[" << i << "]=" << threadLocalFreeBitMapOffsets[i];;
}
}
+ // Fill the alloc bitmap so nobody can successfully allocate from it.
+ if (kIsDebugBuild) {
+ dedicated_full_run_->magic_num_ = kMagicNum;
+ }
+ // It doesn't matter which size bracket we use since the main goal is to have the allocation
+ // fail 100% of the time you attempt to allocate into the dedicated full run.
+ dedicated_full_run_->size_bracket_idx_ = 0;
+ dedicated_full_run_->FillAllocBitMap();
+ dedicated_full_run_->SetIsThreadLocal(true);
}
void RosAlloc::BytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) {
@@ -1867,6 +1899,7 @@
<< " and the run size : page index range " << i << " to " << (i + num_pages)
<< std::endl << DumpPageMap();
}
+ // Don't verify the dedicated_full_run_ since it doesn't have any real allocations.
runs.push_back(run);
i += num_pages;
CHECK_LE(i, pm_end) << "Page map index " << i << " out of range < " << pm_end
@@ -1891,34 +1924,25 @@
void RosAlloc::Run::Verify(Thread* self, RosAlloc* rosalloc) {
DCHECK_EQ(magic_num_, kMagicNum) << "Bad magic number : " << Dump();
- size_t idx = size_bracket_idx_;
+ const size_t idx = size_bracket_idx_;
CHECK_LT(idx, kNumOfSizeBrackets) << "Out of range size bracket index : " << Dump();
byte* slot_base = reinterpret_cast<byte*>(this) + headerSizes[idx];
- size_t num_slots = numOfSlots[idx];
+ const size_t num_slots = numOfSlots[idx];
+ const size_t num_vec = RoundUp(num_slots, 32) / 32;
+ CHECK_GT(num_vec, 0U);
size_t bracket_size = IndexToBracketSize(idx);
CHECK_EQ(slot_base + num_slots * bracket_size,
reinterpret_cast<byte*>(this) + numOfPages[idx] * kPageSize)
<< "Mismatch in the end address of the run " << Dump();
// Check that the bulk free bitmap is clean. It's only used during BulkFree().
CHECK(IsBulkFreeBitmapClean()) << "The bulk free bit map isn't clean " << Dump();
- // Check the bump index mode, if it's on.
- if (top_slot_idx_ < num_slots) {
- // If the bump index mode is on (top_slot_idx_ < num_slots), then
- // all of the slots after the top index must be free.
- for (size_t i = top_slot_idx_; i < num_slots; ++i) {
- size_t vec_idx = i / 32;
- size_t vec_off = i % 32;
- uint32_t vec = alloc_bit_map_[vec_idx];
- CHECK_EQ((vec & (1 << vec_off)), static_cast<uint32_t>(0))
- << "A slot >= top_slot_idx_ isn't free " << Dump();
- }
- } else {
- CHECK_EQ(top_slot_idx_, num_slots)
- << "If the bump index mode is off, the top index == the number of slots "
- << Dump();
- }
+ uint32_t last_word_mask = GetBitmapLastVectorMask(num_slots, num_vec);
+ // Make sure all the bits at the end of the run are set so that we don't allocate there.
+ CHECK_EQ(alloc_bit_map_[num_vec - 1] & last_word_mask, last_word_mask);
+ // Ensure that the first bitmap index is valid.
+ CHECK_LT(first_search_vec_idx_, num_vec);
// Check the thread local runs, the current runs, and the run sets.
- if (is_thread_local_) {
+ if (IsThreadLocal()) {
// If it's a thread local run, then it must be pointed to by an owner thread.
bool owner_found = false;
std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
@@ -1980,7 +2004,6 @@
}
}
// Check each slot.
- size_t num_vec = RoundUp(num_slots, 32) / 32;
size_t slots = 0;
for (size_t v = 0; v < num_vec; v++, slots += 32) {
DCHECK_GE(num_slots, slots) << "Out of bounds";
@@ -1991,7 +2014,7 @@
bool is_allocated = ((vec >> i) & 0x1) != 0;
// If a thread local run, slots may be marked freed in the
// thread local free bitmap.
- bool is_thread_local_freed = is_thread_local_ && ((thread_local_free_vec >> i) & 0x1) != 0;
+ bool is_thread_local_freed = IsThreadLocal() && ((thread_local_free_vec >> i) & 0x1) != 0;
if (is_allocated && !is_thread_local_freed) {
byte* slot_addr = slot_base + (slots + i) * bracket_size;
mirror::Object* obj = reinterpret_cast<mirror::Object*>(slot_addr);
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index 0c508b7..f7fa2da 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -155,7 +155,7 @@
// +-------------------+
// | to_be_bulk_freed |
// +-------------------+
- // | top_slot_idx |
+ // | top_bitmap_idx |
// +-------------------+
// | |
// | alloc bit map |
@@ -186,12 +186,12 @@
//
class Run {
public:
- byte magic_num_; // The magic number used for debugging.
- byte size_bracket_idx_; // The index of the size bracket of this run.
- byte is_thread_local_; // True if this run is used as a thread-local run.
- byte to_be_bulk_freed_; // Used within BulkFree() to flag a run that's involved with a bulk free.
- uint32_t top_slot_idx_; // The top slot index when this run is in bump index mode.
- uint32_t alloc_bit_map_[0]; // The bit map that allocates if each slot is in use.
+ byte magic_num_; // The magic number used for debugging.
+ byte size_bracket_idx_; // The index of the size bracket of this run.
+ byte is_thread_local_; // True if this run is used as a thread-local run.
+ byte to_be_bulk_freed_; // Used within BulkFree() to flag a run that's involved with a bulk free.
+ uint32_t first_search_vec_idx_; // The index of the first bitmap vector which may contain an available slot.
+ uint32_t alloc_bit_map_[0]; // The bit map that allocates if each slot is in use.
// bulk_free_bit_map_[] : The bit map that is used for GC to
// temporarily mark the slots to free without using a lock. After
@@ -225,6 +225,16 @@
void* End() {
return reinterpret_cast<byte*>(this) + kPageSize * numOfPages[size_bracket_idx_];
}
+ // Returns the number of bitmap words per run.
+ size_t NumberOfBitmapVectors() const {
+ return RoundUp(numOfSlots[size_bracket_idx_], 32) / 32;
+ }
+ void SetIsThreadLocal(bool is_thread_local) {
+ is_thread_local_ = is_thread_local ? 1 : 0;
+ }
+ bool IsThreadLocal() const {
+ return is_thread_local_ != 0;
+ }
// Frees slots in the allocation bit map with regard to the
// thread-local free bit map. Used when a thread-local run becomes
// full.
@@ -243,10 +253,13 @@
void* AllocSlot();
// Frees a slot in a run. This is used in a non-bulk free.
void FreeSlot(void* ptr);
- // Marks the slots to free in the bulk free bit map.
- void MarkBulkFreeBitMap(void* ptr);
+ // Marks the slots to free in the bulk free bit map. Returns the bracket size.
+ size_t MarkBulkFreeBitMap(void* ptr);
// Marks the slots to free in the thread-local free bit map.
void MarkThreadLocalFreeBitMap(void* ptr);
+ // Last word mask, all of the bits in the last word which aren't valid slots are set to
+ // optimize allocation path.
+ static uint32_t GetBitmapLastVectorMask(size_t num_slots, size_t num_vec);
// Returns true if all the slots in the run are not in use.
bool IsAllFree();
// Returns true if all the slots in the run are in use.
@@ -255,8 +268,14 @@
bool IsBulkFreeBitmapClean();
// Returns true if the thread local free bit map is clean.
bool IsThreadLocalFreeBitmapClean();
- // Clear all the bit maps.
- void ClearBitMaps();
+ // Set the alloc_bit_map_ bits for slots that are past the end of the run.
+ void SetAllocBitMapBitsForInvalidSlots();
+ // Zero the run's data.
+ void ZeroData();
+ // Zero the run's header.
+ void ZeroHeader();
+ // Fill the alloc bitmap with 1s.
+ void FillAllocBitMap();
// Iterate over all the slots and apply the given function.
void InspectAllSlots(void (*handler)(void* start, void* end, size_t used_bytes, void* callback_arg), void* arg);
// Dump the run metadata for debugging.
@@ -267,8 +286,9 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_);
private:
- // The common part of MarkFreeBitMap() and MarkThreadLocalFreeBitMap().
- void MarkFreeBitMapShared(void* ptr, uint32_t* free_bit_map_base, const char* caller_name);
+ // The common part of MarkFreeBitMap() and MarkThreadLocalFreeBitMap(). Returns the bracket
+ // size.
+ size_t MarkFreeBitMapShared(void* ptr, uint32_t* free_bit_map_base, const char* caller_name);
// Turns the bit map into a string for debugging.
static std::string BitMapToStr(uint32_t* bit_map_base, size_t num_vec);
};
@@ -376,7 +396,7 @@
return byte_offset / kPageSize;
}
// Returns the page map index from an address with rounding.
- size_t RoundDownToPageMapIndex(void* addr) {
+ size_t RoundDownToPageMapIndex(void* addr) const {
DCHECK(base_ <= addr && addr < reinterpret_cast<byte*>(base_) + capacity_);
return (reinterpret_cast<uintptr_t>(addr) - reinterpret_cast<uintptr_t>(base_)) / kPageSize;
}
@@ -446,12 +466,19 @@
hash_set<Run*, hash_run, eq_run> full_runs_[kNumOfSizeBrackets];
// The set of free pages.
std::set<FreePageRun*> free_page_runs_ GUARDED_BY(lock_);
+ // The dedicated full run, it is always full and shared by all threads when revoking happens.
+ // This is an optimization since enables us to avoid a null check for revoked runs.
+ static Run* dedicated_full_run_;
+ // Using size_t to ensure that it is at least word aligned.
+ static size_t dedicated_full_run_storage_[];
// The current runs where the allocations are first attempted for
// the size brackes that do not use thread-local
// runs. current_runs_[i] is guarded by size_bracket_locks_[i].
Run* current_runs_[kNumOfSizeBrackets];
// The mutexes, one per size bracket.
Mutex* size_bracket_locks_[kNumOfSizeBrackets];
+ // Bracket lock names (since locks only have char* names).
+ std::string size_bracket_lock_names[kNumOfSizeBrackets];
// The types of page map entries.
enum {
kPageMapEmpty = 0, // Not allocated.
@@ -493,15 +520,19 @@
// Page-granularity alloc/free
void* AllocPages(Thread* self, size_t num_pages, byte page_map_type)
EXCLUSIVE_LOCKS_REQUIRED(lock_);
- // Returns how many pages were freed.
- size_t FreePages(Thread* self, void* ptr) EXCLUSIVE_LOCKS_REQUIRED(lock_);
+ // Returns how many bytes were freed.
+ size_t FreePages(Thread* self, void* ptr, bool already_zero) EXCLUSIVE_LOCKS_REQUIRED(lock_);
// Allocate/free a run slot.
void* AllocFromRun(Thread* self, size_t size, size_t* bytes_allocated)
LOCKS_EXCLUDED(lock_);
- void FreeFromRun(Thread* self, void* ptr, Run* run)
+ // Returns the bracket size.
+ size_t FreeFromRun(Thread* self, void* ptr, Run* run)
LOCKS_EXCLUDED(lock_);
+ // Used to allocate a new thread local run for a size bracket.
+ Run* AllocRun(Thread* self, size_t idx) LOCKS_EXCLUDED(lock_);
+
// Used to acquire a new/reused run for a size bracket. Used when a
// thread-local or current run gets full.
Run* RefillRun(Thread* self, size_t idx) LOCKS_EXCLUDED(lock_);
@@ -558,6 +589,9 @@
void AssertAllThreadLocalRunsAreRevoked() LOCKS_EXCLUDED(Locks::thread_list_lock_);
// Dumps the page map for debugging.
std::string DumpPageMap() EXCLUSIVE_LOCKS_REQUIRED(lock_);
+ static Run* GetDedicatedFullRun() {
+ return dedicated_full_run_;
+ }
// Callbacks for InspectAll that will count the number of bytes
// allocated and objects allocated, respectively.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 502da12..5d72bc1 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -78,9 +78,9 @@
static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB;
static constexpr size_t kMaxConcurrentRemainingBytes = 512 * KB;
// Sticky GC throughput adjustment, divided by 4. Increasing this causes sticky GC to occur more
-// relative to partial/full GC. This is desirable since sticky GCs interfere less with mutator
+// relative to partial/full GC. This may be desirable since sticky GCs interfere less with mutator
// threads (lower pauses, use less memory bandwidth).
-static constexpr double kStickyGcThroughputAdjustment = 1.25;
+static constexpr double kStickyGcThroughputAdjustment = 1.0;
// Whether or not we use the free list large object space.
static constexpr bool kUseFreeListSpaceForLOS = false;
// Whtehr or not we compact the zygote in PreZygoteFork.
@@ -595,6 +595,11 @@
if (continuous_space->IsDlMallocSpace()) {
dlmalloc_space_ = continuous_space->AsDlMallocSpace();
} else if (continuous_space->IsRosAllocSpace()) {
+ // Revoke before if we already have a rosalloc_space_ so that we don't end up with non full
+ // runs from the previous one during the revoke after.
+ if (rosalloc_space_ != nullptr) {
+ rosalloc_space_->RevokeAllThreadLocalBuffers();
+ }
rosalloc_space_ = continuous_space->AsRosAllocSpace();
}
}
@@ -615,7 +620,7 @@
}
}
-void Heap::RemoveSpace(space::Space* space) {
+void Heap::RemoveSpace(space::Space* space, bool unset_as_default) {
DCHECK(space != nullptr);
WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
if (space->IsContinuousSpace()) {
@@ -632,17 +637,19 @@
auto it = std::find(continuous_spaces_.begin(), continuous_spaces_.end(), continuous_space);
DCHECK(it != continuous_spaces_.end());
continuous_spaces_.erase(it);
- if (continuous_space == dlmalloc_space_) {
- dlmalloc_space_ = nullptr;
- } else if (continuous_space == rosalloc_space_) {
- rosalloc_space_ = nullptr;
- }
- if (continuous_space == main_space_) {
- main_space_ = nullptr;
- } else if (continuous_space == bump_pointer_space_) {
- bump_pointer_space_ = nullptr;
- } else if (continuous_space == temp_space_) {
- temp_space_ = nullptr;
+ if (unset_as_default) {
+ if (continuous_space == dlmalloc_space_) {
+ dlmalloc_space_ = nullptr;
+ } else if (continuous_space == rosalloc_space_) {
+ rosalloc_space_ = nullptr;
+ }
+ if (continuous_space == main_space_) {
+ main_space_ = nullptr;
+ } else if (continuous_space == bump_pointer_space_) {
+ bump_pointer_space_ = nullptr;
+ } else if (continuous_space == temp_space_) {
+ temp_space_ = nullptr;
+ }
}
} else {
DCHECK(space->IsDiscontinuousSpace());
@@ -725,6 +732,7 @@
os << "Total mutator paused time: " << PrettyDuration(total_paused_time) << "\n";
os << "Total time waiting for GC to complete: " << PrettyDuration(total_wait_time_) << "\n";
os << "Approximate GC data structures memory overhead: " << gc_memory_overhead_;
+ BaseMutex::DumpAll(os);
}
Heap::~Heap() {
@@ -1432,6 +1440,11 @@
if (collector_type == collector_type_) {
return;
}
+ if (Runtime::Current()->IsShuttingDown(self)) {
+ // Don't allow heap transitions to happen if the runtime is shutting down since these can
+ // cause objects to get finalized.
+ return;
+ }
// GC can be disabled if someone has a used GetPrimitiveArrayCritical but not yet released.
if (!copying_transition || disable_moving_gc_count_ == 0) {
// TODO: Not hard code in semi-space collector?
@@ -1452,6 +1465,10 @@
// pointer space last transition it will be protected.
bump_pointer_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
Compact(bump_pointer_space_, main_space_);
+ // Remove the main space so that we don't try to trim it, this doens't work for debug
+ // builds since RosAlloc attempts to read the magic number from a protected page.
+ // TODO: Clean this up by getting rid of the remove_as_default parameter.
+ RemoveSpace(main_space_, false);
}
break;
}
@@ -1460,6 +1477,7 @@
case kCollectorTypeCMS: {
if (IsMovingGc(collector_type_)) {
// Compact to the main space from the bump pointer space, don't need to swap semispaces.
+ AddSpace(main_space_, false);
main_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
Compact(main_space_, bump_pointer_space_);
}
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index ceba8b6..c37bb05 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -289,6 +289,12 @@
void RegisterGCAllocation(size_t bytes);
void RegisterGCDeAllocation(size_t bytes);
+ // Public due to usage by tests.
+ void AddSpace(space::Space* space, bool set_as_default = true)
+ LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
+ void RemoveSpace(space::Space* space, bool unset_as_default = true)
+ LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
+
// Set target ideal heap utilization ratio, implements
// dalvik.system.VMRuntime.setTargetHeapUtilization.
void SetTargetHeapUtilization(float target);
@@ -684,10 +690,6 @@
size_t GetPercentFree();
- void AddSpace(space::Space* space, bool set_as_default = true)
- LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
- void RemoveSpace(space::Space* space) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_);
-
static void VerificationCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index a5a6da0..f5c0e94 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -32,7 +32,7 @@
namespace gc {
namespace space {
-static constexpr bool kPrefetchDuringRosAllocFreeList = true;
+static constexpr bool kPrefetchDuringRosAllocFreeList = false;
static constexpr size_t kPrefetchLookAhead = 8;
// Use this only for verification, it is not safe to use since the class of the object may have
// been freed.
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
index 9896a48..28200dfb 100644
--- a/runtime/gc/space/space_test.h
+++ b/runtime/gc/space/space_test.h
@@ -39,10 +39,8 @@
}
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);
+ // By passing true, AddSpace() does the revoke.
+ Runtime::Current()->GetHeap()->AddSpace(space, true);
}
mirror::Class* GetByteArrayClass(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -349,11 +347,8 @@
EXPECT_EQ(usable_size, computed_usable_size);
}
- // Release memory and check pointers are nullptr.
+ // Release memory.
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++) {
@@ -367,12 +362,8 @@
EXPECT_EQ(usable_size, computed_usable_size);
}
- // Release memory and check pointers are nullptr
- // TODO: This isn't compaction safe, fix.
+ // Release memory.
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,
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 2cd7f49..77d29dd 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -94,6 +94,7 @@
}
if (!method->IsResolutionMethod()) {
if (quick_code == GetQuickToInterpreterBridge() ||
+ quick_code == GetQuickToInterpreterBridgeTrampoline(Runtime::Current()->GetClassLinker()) ||
(quick_code == GetQuickResolutionTrampoline(Runtime::Current()->GetClassLinker()) &&
Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly()
&& !method->IsNative() && !method->IsProxyMethod())) {
@@ -147,6 +148,7 @@
// Do not overwrite interpreter to prevent from posting method entry/exit events twice.
new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
new_quick_code = class_linker->GetQuickOatCodeFor(method);
+ DCHECK(new_quick_code != GetQuickToInterpreterBridgeTrampoline(class_linker));
if (entry_exit_stubs_installed_ && new_quick_code != GetQuickToInterpreterBridge()) {
DCHECK(new_portable_code != GetPortableToInterpreterBridge());
new_portable_code = GetPortableToInterpreterBridge();
@@ -562,7 +564,8 @@
new_quick_code = GetQuickToInterpreterBridge();
new_have_portable_code = false;
} else if (quick_code == GetQuickResolutionTrampoline(Runtime::Current()->GetClassLinker()) ||
- quick_code == GetQuickToInterpreterBridge()) {
+ quick_code == GetQuickToInterpreterBridgeTrampoline(Runtime::Current()->GetClassLinker()) ||
+ quick_code == GetQuickToInterpreterBridge()) {
DCHECK((portable_code == GetPortableResolutionTrampoline(Runtime::Current()->GetClassLinker())) ||
(portable_code == GetPortableToInterpreterBridge()));
new_portable_code = portable_code;
@@ -709,9 +712,10 @@
Runtime* runtime = Runtime::Current();
if (LIKELY(!instrumentation_stubs_installed_)) {
const void* code = method->GetEntryPointFromQuickCompiledCode();
- DCHECK(code != NULL);
- if (LIKELY(code != GetQuickResolutionTrampoline(runtime->GetClassLinker()) &&
- code != GetQuickToInterpreterBridge())) {
+ DCHECK(code != nullptr);
+ if (LIKELY(code != GetQuickResolutionTrampoline(runtime->GetClassLinker())) &&
+ LIKELY(code != GetQuickToInterpreterBridgeTrampoline(runtime->GetClassLinker())) &&
+ LIKELY(code != GetQuickToInterpreterBridge())) {
return code;
}
}
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 1bf0078..e3f3cd0 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -302,19 +302,19 @@
exit(0);
}
// Explicit definitions of ExecuteGotoImpl.
-template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+template<> SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
JValue ExecuteGotoImpl<true, false>(Thread* self, MethodHelper& mh,
const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register);
-template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+template<> SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
JValue ExecuteGotoImpl<false, false>(Thread* self, MethodHelper& mh,
const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register);
-template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+template<> SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
JValue ExecuteGotoImpl<true, true>(Thread* self, MethodHelper& mh,
const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register);
-template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+template<> SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
JValue ExecuteGotoImpl<false, true>(Thread* self, MethodHelper& mh,
const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register);
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 65bdf0e..cc1fa0c 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -169,6 +169,13 @@
return false;
}
}
+ // Report this field access to instrumentation if needed.
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasFieldReadListeners())) {
+ Object* this_object = f->IsStatic() ? nullptr : obj;
+ instrumentation->FieldReadEvent(self, this_object, shadow_frame.GetMethod(),
+ shadow_frame.GetDexPC(), f);
+ }
uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
switch (field_type) {
case Primitive::kPrimBoolean:
@@ -210,6 +217,17 @@
return false;
}
MemberOffset field_offset(inst->VRegC_22c());
+ // Report this field access to instrumentation if needed. Since we only have the offset of
+ // the field from the base of the object, we need to look for it first.
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasFieldReadListeners())) {
+ ArtField* f = ArtField::FindInstanceFieldWithOffset(obj->GetClass(),
+ field_offset.Uint32Value());
+ DCHECK(f != nullptr);
+ DCHECK(!f->IsStatic());
+ instrumentation->FieldReadEvent(Thread::Current(), obj, shadow_frame.GetMethod(),
+ shadow_frame.GetDexPC(), f);
+ }
const bool is_volatile = false; // iget-x-quick only on non volatile fields.
const uint32_t vregA = inst->VRegA_22c(inst_data);
switch (field_type) {
@@ -228,6 +246,39 @@
return true;
}
+template<Primitive::Type field_type>
+static inline JValue GetFieldValue(const ShadowFrame& shadow_frame, uint32_t vreg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ JValue field_value;
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ field_value.SetZ(static_cast<uint8_t>(shadow_frame.GetVReg(vreg)));
+ break;
+ case Primitive::kPrimByte:
+ field_value.SetB(static_cast<int8_t>(shadow_frame.GetVReg(vreg)));
+ break;
+ case Primitive::kPrimChar:
+ field_value.SetC(static_cast<uint16_t>(shadow_frame.GetVReg(vreg)));
+ break;
+ case Primitive::kPrimShort:
+ field_value.SetS(static_cast<int16_t>(shadow_frame.GetVReg(vreg)));
+ break;
+ case Primitive::kPrimInt:
+ field_value.SetI(shadow_frame.GetVReg(vreg));
+ break;
+ case Primitive::kPrimLong:
+ field_value.SetJ(shadow_frame.GetVRegLong(vreg));
+ break;
+ case Primitive::kPrimNot:
+ field_value.SetL(shadow_frame.GetVRegReference(vreg));
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ break;
+ }
+ return field_value;
+}
+
// Handles iput-XXX and sput-XXX instructions.
// Returns true on success, otherwise throws an exception and returns false.
template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check, bool transaction_active>
@@ -254,6 +305,15 @@
}
}
uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+ // Report this field access to instrumentation if needed. Since we only have the offset of
+ // the field from the base of the object, we need to look for it first.
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasFieldWriteListeners())) {
+ JValue field_value = GetFieldValue<field_type>(shadow_frame, vregA);
+ Object* this_object = f->IsStatic() ? nullptr : obj;
+ instrumentation->FieldWriteEvent(self, this_object, shadow_frame.GetMethod(),
+ shadow_frame.GetDexPC(), f, field_value);
+ }
switch (field_type) {
case Primitive::kPrimBoolean:
f->SetBoolean<transaction_active>(obj, shadow_frame.GetVReg(vregA));
@@ -309,8 +369,20 @@
return false;
}
MemberOffset field_offset(inst->VRegC_22c());
- const bool is_volatile = false; // iput-x-quick only on non volatile fields.
const uint32_t vregA = inst->VRegA_22c(inst_data);
+ // Report this field modification to instrumentation if needed. Since we only have the offset of
+ // the field from the base of the object, we need to look for it first.
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasFieldWriteListeners())) {
+ ArtField* f = ArtField::FindInstanceFieldWithOffset(obj->GetClass(),
+ field_offset.Uint32Value());
+ DCHECK(f != nullptr);
+ DCHECK(!f->IsStatic());
+ JValue field_value = GetFieldValue<field_type>(shadow_frame, vregA);
+ instrumentation->FieldWriteEvent(Thread::Current(), obj, shadow_frame.GetMethod(),
+ shadow_frame.GetDexPC(), f, field_value);
+ }
+ const bool is_volatile = false; // iput-x-quick only on non volatile fields.
switch (field_type) {
case Primitive::kPrimInt:
obj->SetField32<transaction_active>(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index adc1074..223b7a1 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -108,7 +108,7 @@
*/
struct ModBasket {
ModBasket() : pLoc(NULL), threadId(0), classId(0), excepClassId(0),
- caught(false), field(0), thisPtr(0) { }
+ caught(false), fieldTypeID(0), fieldId(0), thisPtr(0) { }
const JdwpLocation* pLoc; /* LocationOnly */
std::string className; /* ClassMatch/ClassExclude */
@@ -116,7 +116,8 @@
RefTypeId classId; /* ClassOnly */
RefTypeId excepClassId; /* ExceptionOnly */
bool caught; /* ExceptionOnly */
- FieldId field; /* FieldOnly */
+ RefTypeId fieldTypeID; /* FieldOnly */
+ FieldId fieldId; /* FieldOnly */
ObjectId thisPtr; /* InstanceOnly */
/* nothing for StepOnly -- handled differently */
};
@@ -457,7 +458,10 @@
}
break;
case MK_FIELD_ONLY:
- if (!Dbg::MatchType(basket->classId, pMod->fieldOnly.refTypeId) || pMod->fieldOnly.fieldId != basket->field) {
+ if (pMod->fieldOnly.fieldId != basket->fieldId) {
+ return false;
+ }
+ if (!Dbg::MatchType(basket->fieldTypeID, pMod->fieldOnly.refTypeId)) {
return false;
}
break;
@@ -848,7 +852,8 @@
basket.thisPtr = thisPtr;
basket.threadId = Dbg::GetThreadSelfId();
basket.className = Dbg::GetClassName(pLoc->class_id);
- basket.field = fieldId;
+ basket.fieldTypeID = typeId;
+ basket.fieldId = fieldId;
if (InvokeInProgress()) {
VLOG(jdwp) << "Not posting field event during invoke";
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 8ef375b..4843c2b 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -354,8 +354,8 @@
static JdwpError VM_Capabilities(JdwpState*, Request&, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- expandBufAdd1(reply, false); // canWatchFieldModification
- expandBufAdd1(reply, false); // canWatchFieldAccess
+ expandBufAdd1(reply, true); // canWatchFieldModification
+ expandBufAdd1(reply, true); // canWatchFieldAccess
expandBufAdd1(reply, true); // canGetBytecodes
expandBufAdd1(reply, true); // canGetSyntheticAttribute
expandBufAdd1(reply, true); // canGetOwnedMonitorInfo
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index c04aabf..fd9c40b 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -513,14 +513,16 @@
SafeMap<std::string, SharedLibrary*> libraries_;
};
-#define CHECK_NON_NULL_ARGUMENT(fn, value) \
+#define CHECK_NON_NULL_ARGUMENT(value) CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value)
+
+#define CHECK_NON_NULL_ARGUMENT_FN_NAME(name, value) \
if (UNLIKELY(value == nullptr)) { \
- JniAbortF(#fn, #value " == null"); \
+ JniAbortF(name, #value " == null"); \
}
-#define CHECK_NON_NULL_MEMCPY_ARGUMENT(fn, length, value) \
+#define CHECK_NON_NULL_MEMCPY_ARGUMENT(length, value) \
if (UNLIKELY(length != 0 && value == nullptr)) { \
- JniAbortF(#fn, #value " == null"); \
+ JniAbortF(__FUNCTION__, #value " == null"); \
}
class JNI {
@@ -535,7 +537,7 @@
}
static jclass FindClass(JNIEnv* env, const char* name) {
- CHECK_NON_NULL_ARGUMENT(FindClass, name);
+ CHECK_NON_NULL_ARGUMENT(name);
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
std::string descriptor(NormalizeJniClassDescriptor(name));
@@ -551,19 +553,19 @@
}
static jmethodID FromReflectedMethod(JNIEnv* env, jobject jlr_method) {
- CHECK_NON_NULL_ARGUMENT(FromReflectedMethod, jlr_method);
+ CHECK_NON_NULL_ARGUMENT(jlr_method);
ScopedObjectAccess soa(env);
return soa.EncodeMethod(mirror::ArtMethod::FromReflectedMethod(soa, jlr_method));
}
static jfieldID FromReflectedField(JNIEnv* env, jobject jlr_field) {
- CHECK_NON_NULL_ARGUMENT(FromReflectedField, jlr_field);
+ CHECK_NON_NULL_ARGUMENT(jlr_field);
ScopedObjectAccess soa(env);
return soa.EncodeField(mirror::ArtField::FromReflectedField(soa, jlr_field));
}
static jobject ToReflectedMethod(JNIEnv* env, jclass, jmethodID mid, jboolean) {
- CHECK_NON_NULL_ARGUMENT(ToReflectedMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
mirror::ArtMethod* m = soa.DecodeMethod(mid);
CHECK(!kMovingMethods);
@@ -578,7 +580,7 @@
}
static jobject ToReflectedField(JNIEnv* env, jclass, jfieldID fid, jboolean) {
- CHECK_NON_NULL_ARGUMENT(ToReflectedField, fid);
+ CHECK_NON_NULL_ARGUMENT(fid);
ScopedObjectAccess soa(env);
mirror::ArtField* f = soa.DecodeField(fid);
jobject art_field = soa.AddLocalReference<jobject>(f);
@@ -592,22 +594,22 @@
}
static jclass GetObjectClass(JNIEnv* env, jobject java_object) {
- CHECK_NON_NULL_ARGUMENT(GetObjectClass, java_object);
+ CHECK_NON_NULL_ARGUMENT(java_object);
ScopedObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
return soa.AddLocalReference<jclass>(o->GetClass());
}
static jclass GetSuperclass(JNIEnv* env, jclass java_class) {
- CHECK_NON_NULL_ARGUMENT(GetSuperclass, java_class);
+ CHECK_NON_NULL_ARGUMENT(java_class);
ScopedObjectAccess soa(env);
mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
return soa.AddLocalReference<jclass>(c->GetSuperClass());
}
static jboolean IsAssignableFrom(JNIEnv* env, jclass java_class1, jclass java_class2) {
- CHECK_NON_NULL_ARGUMENT(IsAssignableFrom, java_class1);
- CHECK_NON_NULL_ARGUMENT(IsAssignableFrom, java_class2);
+ CHECK_NON_NULL_ARGUMENT(java_class1);
+ CHECK_NON_NULL_ARGUMENT(java_class2);
ScopedObjectAccess soa(env);
mirror::Class* c1 = soa.Decode<mirror::Class*>(java_class1);
mirror::Class* c2 = soa.Decode<mirror::Class*>(java_class2);
@@ -615,7 +617,7 @@
}
static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass java_class) {
- CHECK_NON_NULL_ARGUMENT(IsInstanceOf, java_class);
+ CHECK_NON_NULL_ARGUMENT(java_class);
if (jobj == nullptr) {
// Note: JNI is different from regular Java instanceof in this respect
return JNI_TRUE;
@@ -639,7 +641,7 @@
}
static jint ThrowNew(JNIEnv* env, jclass c, const char* msg) {
- CHECK_NON_NULL_ARGUMENT(ThrowNew, c);
+ CHECK_NON_NULL_ARGUMENT(c);
return ThrowNewException(env, c, msg, nullptr);
}
@@ -797,7 +799,7 @@
}
static jobject AllocObject(JNIEnv* env, jclass java_class) {
- CHECK_NON_NULL_ARGUMENT(AllocObject, java_class);
+ CHECK_NON_NULL_ARGUMENT(java_class);
ScopedObjectAccess soa(env);
mirror::Class* c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class*>(java_class));
if (c == nullptr) {
@@ -809,16 +811,16 @@
static jobject NewObject(JNIEnv* env, jclass java_class, jmethodID mid, ...) {
va_list args;
va_start(args, mid);
- CHECK_NON_NULL_ARGUMENT(NewObject, java_class);
- CHECK_NON_NULL_ARGUMENT(NewObject, mid);
+ CHECK_NON_NULL_ARGUMENT(java_class);
+ CHECK_NON_NULL_ARGUMENT(mid);
jobject result = NewObjectV(env, java_class, mid, args);
va_end(args);
return result;
}
static jobject NewObjectV(JNIEnv* env, jclass java_class, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(NewObjectV, java_class);
- CHECK_NON_NULL_ARGUMENT(NewObjectV, mid);
+ CHECK_NON_NULL_ARGUMENT(java_class);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
mirror::Class* c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class*>(java_class));
if (c == nullptr) {
@@ -837,8 +839,8 @@
}
static jobject NewObjectA(JNIEnv* env, jclass java_class, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(NewObjectA, java_class);
- CHECK_NON_NULL_ARGUMENT(NewObjectA, mid);
+ CHECK_NON_NULL_ARGUMENT(java_class);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
mirror::Class* c = EnsureInitialized(soa.Self(), soa.Decode<mirror::Class*>(java_class));
if (c == nullptr) {
@@ -857,18 +859,18 @@
}
static jmethodID GetMethodID(JNIEnv* env, jclass java_class, const char* name, const char* sig) {
- CHECK_NON_NULL_ARGUMENT(GetMethodID, java_class);
- CHECK_NON_NULL_ARGUMENT(GetMethodID, name);
- CHECK_NON_NULL_ARGUMENT(GetMethodID, sig);
+ CHECK_NON_NULL_ARGUMENT(java_class);
+ CHECK_NON_NULL_ARGUMENT(name);
+ CHECK_NON_NULL_ARGUMENT(sig);
ScopedObjectAccess soa(env);
return FindMethodID(soa, java_class, name, sig, false);
}
static jmethodID GetStaticMethodID(JNIEnv* env, jclass java_class, const char* name,
const char* sig) {
- CHECK_NON_NULL_ARGUMENT(GetStaticMethodID, java_class);
- CHECK_NON_NULL_ARGUMENT(GetStaticMethodID, name);
- CHECK_NON_NULL_ARGUMENT(GetStaticMethodID, sig);
+ CHECK_NON_NULL_ARGUMENT(java_class);
+ CHECK_NON_NULL_ARGUMENT(name);
+ CHECK_NON_NULL_ARGUMENT(sig);
ScopedObjectAccess soa(env);
return FindMethodID(soa, java_class, name, sig, true);
}
@@ -876,8 +878,8 @@
static jobject CallObjectMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallObjectMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallObjectMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -885,16 +887,16 @@
}
static jobject CallObjectMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallObjectMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallObjectMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args));
return soa.AddLocalReference<jobject>(result.GetL());
}
static jobject CallObjectMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallObjectMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallObjectMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
args));
@@ -904,8 +906,8 @@
static jboolean CallBooleanMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallBooleanMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallBooleanMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -913,15 +915,15 @@
}
static jboolean CallBooleanMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallBooleanMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallBooleanMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetZ();
}
static jboolean CallBooleanMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallBooleanMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallBooleanMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
args).GetZ();
@@ -930,8 +932,8 @@
static jbyte CallByteMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallByteMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallByteMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -939,15 +941,15 @@
}
static jbyte CallByteMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallByteMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallByteMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetB();
}
static jbyte CallByteMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallByteMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallByteMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
args).GetB();
@@ -956,8 +958,8 @@
static jchar CallCharMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallCharMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallCharMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -965,15 +967,15 @@
}
static jchar CallCharMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallCharMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallCharMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetC();
}
static jchar CallCharMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallCharMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallCharMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
args).GetC();
@@ -982,8 +984,8 @@
static jdouble CallDoubleMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallDoubleMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallDoubleMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -991,15 +993,15 @@
}
static jdouble CallDoubleMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallDoubleMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallDoubleMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetD();
}
static jdouble CallDoubleMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallDoubleMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallDoubleMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
args).GetD();
@@ -1008,8 +1010,8 @@
static jfloat CallFloatMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallFloatMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallFloatMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -1017,15 +1019,15 @@
}
static jfloat CallFloatMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallFloatMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallFloatMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetF();
}
static jfloat CallFloatMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallFloatMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallFloatMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
args).GetF();
@@ -1034,8 +1036,8 @@
static jint CallIntMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallIntMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallIntMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -1043,15 +1045,15 @@
}
static jint CallIntMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallIntMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallIntMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetI();
}
static jint CallIntMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallIntMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallIntMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
args).GetI();
@@ -1060,8 +1062,8 @@
static jlong CallLongMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallLongMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallLongMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -1069,15 +1071,15 @@
}
static jlong CallLongMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallLongMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallLongMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetJ();
}
static jlong CallLongMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallLongMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallLongMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
args).GetJ();
@@ -1086,8 +1088,8 @@
static jshort CallShortMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallShortMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallShortMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -1095,15 +1097,15 @@
}
static jshort CallShortMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallShortMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallShortMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args).GetS();
}
static jshort CallShortMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallShortMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallShortMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid,
args).GetS();
@@ -1112,23 +1114,23 @@
static void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallVoidMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallVoidMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, ap);
va_end(ap);
}
static void CallVoidMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallVoidMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallVoidMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
InvokeVirtualOrInterfaceWithVarArgs(soa, obj, mid, args);
}
static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallVoidMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallVoidMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
InvokeVirtualOrInterfaceWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args);
}
@@ -1136,8 +1138,8 @@
static jobject CallNonvirtualObjectMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualObjectMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualObjectMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, obj, mid, ap));
jobject local_result = soa.AddLocalReference<jobject>(result.GetL());
@@ -1147,8 +1149,8 @@
static jobject CallNonvirtualObjectMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid,
va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualObjectMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualObjectMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, obj, mid, args));
return soa.AddLocalReference<jobject>(result.GetL());
@@ -1156,8 +1158,8 @@
static jobject CallNonvirtualObjectMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid,
jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualObjectMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualObjectMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args));
return soa.AddLocalReference<jobject>(result.GetL());
@@ -1167,8 +1169,8 @@
...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualBooleanMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualBooleanMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -1177,16 +1179,16 @@
static jboolean CallNonvirtualBooleanMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid,
va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualBooleanMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualBooleanMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, obj, mid, args).GetZ();
}
static jboolean CallNonvirtualBooleanMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid,
jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualBooleanMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualBooleanMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetZ();
}
@@ -1194,8 +1196,8 @@
static jbyte CallNonvirtualByteMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualByteMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualByteMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -1204,16 +1206,16 @@
static jbyte CallNonvirtualByteMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid,
va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualByteMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualByteMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, obj, mid, args).GetB();
}
static jbyte CallNonvirtualByteMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid,
jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualByteMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualByteMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetB();
}
@@ -1221,8 +1223,8 @@
static jchar CallNonvirtualCharMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualCharMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualCharMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -1231,16 +1233,16 @@
static jchar CallNonvirtualCharMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid,
va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualCharMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualCharMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, obj, mid, args).GetC();
}
static jchar CallNonvirtualCharMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid,
jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualCharMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualCharMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetC();
}
@@ -1248,8 +1250,8 @@
static jshort CallNonvirtualShortMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualShortMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualShortMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -1258,16 +1260,16 @@
static jshort CallNonvirtualShortMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid,
va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualShortMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualShortMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, obj, mid, args).GetS();
}
static jshort CallNonvirtualShortMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid,
jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualShortMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualShortMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetS();
}
@@ -1275,8 +1277,8 @@
static jint CallNonvirtualIntMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualIntMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualIntMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -1285,16 +1287,16 @@
static jint CallNonvirtualIntMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid,
va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualIntMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualIntMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, obj, mid, args).GetI();
}
static jint CallNonvirtualIntMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid,
jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualIntMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualIntMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetI();
}
@@ -1302,8 +1304,8 @@
static jlong CallNonvirtualLongMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualLongMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualLongMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -1312,16 +1314,16 @@
static jlong CallNonvirtualLongMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid,
va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualLongMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualLongMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, obj, mid, args).GetJ();
}
static jlong CallNonvirtualLongMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid,
jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualLongMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualLongMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetJ();
}
@@ -1329,8 +1331,8 @@
static jfloat CallNonvirtualFloatMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualFloatMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualFloatMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -1339,16 +1341,16 @@
static jfloat CallNonvirtualFloatMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid,
va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualFloatMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualFloatMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, obj, mid, args).GetF();
}
static jfloat CallNonvirtualFloatMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid,
jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualFloatMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualFloatMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetF();
}
@@ -1356,8 +1358,8 @@
static jdouble CallNonvirtualDoubleMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualDoubleMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualDoubleMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, obj, mid, ap));
va_end(ap);
@@ -1366,16 +1368,16 @@
static jdouble CallNonvirtualDoubleMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid,
va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualDoubleMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualDoubleMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, obj, mid, args).GetD();
}
static jdouble CallNonvirtualDoubleMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid,
jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualDoubleMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualDoubleMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args).GetD();
}
@@ -1383,8 +1385,8 @@
static void CallNonvirtualVoidMethod(JNIEnv* env, jobject obj, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualVoidMethod, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualVoidMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
InvokeWithVarArgs(soa, obj, mid, ap);
va_end(ap);
@@ -1392,40 +1394,40 @@
static void CallNonvirtualVoidMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid,
va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualVoidMethodV, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualVoidMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
InvokeWithVarArgs(soa, obj, mid, args);
}
static void CallNonvirtualVoidMethodA(JNIEnv* env, jobject obj, jclass, jmethodID mid,
jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualVoidMethodA, obj);
- CHECK_NON_NULL_ARGUMENT(CallNonvirtualVoidMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
InvokeWithJValues(soa, soa.Decode<mirror::Object*>(obj), mid, args);
}
static jfieldID GetFieldID(JNIEnv* env, jclass java_class, const char* name, const char* sig) {
- CHECK_NON_NULL_ARGUMENT(GetFieldID, java_class);
- CHECK_NON_NULL_ARGUMENT(GetFieldID, name);
- CHECK_NON_NULL_ARGUMENT(GetFieldID, sig);
+ CHECK_NON_NULL_ARGUMENT(java_class);
+ CHECK_NON_NULL_ARGUMENT(name);
+ CHECK_NON_NULL_ARGUMENT(sig);
ScopedObjectAccess soa(env);
return FindFieldID(soa, java_class, name, sig, false);
}
static jfieldID GetStaticFieldID(JNIEnv* env, jclass java_class, const char* name,
const char* sig) {
- CHECK_NON_NULL_ARGUMENT(GetStaticFieldID, java_class);
- CHECK_NON_NULL_ARGUMENT(GetStaticFieldID, name);
- CHECK_NON_NULL_ARGUMENT(GetFieldID, sig);
+ CHECK_NON_NULL_ARGUMENT(java_class);
+ CHECK_NON_NULL_ARGUMENT(name);
+ CHECK_NON_NULL_ARGUMENT(sig);
ScopedObjectAccess soa(env);
return FindFieldID(soa, java_class, name, sig, true);
}
static jobject GetObjectField(JNIEnv* env, jobject obj, jfieldID fid) {
- CHECK_NON_NULL_ARGUMENT(GetObjectField, obj);
- CHECK_NON_NULL_ARGUMENT(GetObjectField, fid);
+ CHECK_NON_NULL_ARGUMENT(obj);
+ CHECK_NON_NULL_ARGUMENT(fid);
ScopedObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(obj);
mirror::ArtField* f = soa.DecodeField(fid);
@@ -1433,15 +1435,15 @@
}
static jobject GetStaticObjectField(JNIEnv* env, jclass, jfieldID fid) {
- CHECK_NON_NULL_ARGUMENT(GetStaticObjectField, fid);
+ CHECK_NON_NULL_ARGUMENT(fid);
ScopedObjectAccess soa(env);
mirror::ArtField* f = soa.DecodeField(fid);
return soa.AddLocalReference<jobject>(f->GetObject(f->GetDeclaringClass()));
}
static void SetObjectField(JNIEnv* env, jobject java_object, jfieldID fid, jobject java_value) {
- CHECK_NON_NULL_ARGUMENT(SetObjectField, java_object);
- CHECK_NON_NULL_ARGUMENT(SetObjectField, fid);
+ CHECK_NON_NULL_ARGUMENT(java_object);
+ CHECK_NON_NULL_ARGUMENT(fid);
ScopedObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
mirror::Object* v = soa.Decode<mirror::Object*>(java_value);
@@ -1450,7 +1452,7 @@
}
static void SetStaticObjectField(JNIEnv* env, jclass, jfieldID fid, jobject java_value) {
- CHECK_NON_NULL_ARGUMENT(SetStaticObjectField, fid);
+ CHECK_NON_NULL_ARGUMENT(fid);
ScopedObjectAccess soa(env);
mirror::Object* v = soa.Decode<mirror::Object*>(java_value);
mirror::ArtField* f = soa.DecodeField(fid);
@@ -1458,29 +1460,29 @@
}
#define GET_PRIMITIVE_FIELD(fn, instance) \
- CHECK_NON_NULL_ARGUMENT(Get #fn Field, instance); \
- CHECK_NON_NULL_ARGUMENT(Get #fn Field, fid); \
+ CHECK_NON_NULL_ARGUMENT(instance); \
+ CHECK_NON_NULL_ARGUMENT(fid); \
ScopedObjectAccess soa(env); \
mirror::Object* o = soa.Decode<mirror::Object*>(instance); \
mirror::ArtField* f = soa.DecodeField(fid); \
return f->Get ##fn (o)
#define GET_STATIC_PRIMITIVE_FIELD(fn) \
- CHECK_NON_NULL_ARGUMENT(GetStatic #fn Field, fid); \
+ CHECK_NON_NULL_ARGUMENT(fid); \
ScopedObjectAccess soa(env); \
mirror::ArtField* f = soa.DecodeField(fid); \
return f->Get ##fn (f->GetDeclaringClass())
#define SET_PRIMITIVE_FIELD(fn, instance, value) \
- CHECK_NON_NULL_ARGUMENT(Set #fn Field, instance); \
- CHECK_NON_NULL_ARGUMENT(Set #fn Field, fid); \
+ CHECK_NON_NULL_ARGUMENT(instance); \
+ CHECK_NON_NULL_ARGUMENT(fid); \
ScopedObjectAccess soa(env); \
mirror::Object* o = soa.Decode<mirror::Object*>(instance); \
mirror::ArtField* f = soa.DecodeField(fid); \
f->Set ##fn <false>(o, value)
#define SET_STATIC_PRIMITIVE_FIELD(fn, value) \
- CHECK_NON_NULL_ARGUMENT(SetStatic #fn Field, fid); \
+ CHECK_NON_NULL_ARGUMENT(fid); \
ScopedObjectAccess soa(env); \
mirror::ArtField* f = soa.DecodeField(fid); \
f->Set ##fn <false>(f->GetDeclaringClass(), value)
@@ -1616,7 +1618,7 @@
static jobject CallStaticObjectMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallStaticObjectMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap));
jobject local_result = soa.AddLocalReference<jobject>(result.GetL());
@@ -1625,14 +1627,14 @@
}
static jobject CallStaticObjectMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticObjectMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, nullptr, mid, args));
return soa.AddLocalReference<jobject>(result.GetL());
}
static jobject CallStaticObjectMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticObjectMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithJValues(soa, nullptr, mid, args));
return soa.AddLocalReference<jobject>(result.GetL());
@@ -1641,7 +1643,7 @@
static jboolean CallStaticBooleanMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallStaticBooleanMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap));
va_end(ap);
@@ -1649,13 +1651,13 @@
}
static jboolean CallStaticBooleanMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticBooleanMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, nullptr, mid, args).GetZ();
}
static jboolean CallStaticBooleanMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticBooleanMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, nullptr, mid, args).GetZ();
}
@@ -1663,7 +1665,7 @@
static jbyte CallStaticByteMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallStaticByteMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap));
va_end(ap);
@@ -1671,13 +1673,13 @@
}
static jbyte CallStaticByteMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticByteMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, nullptr, mid, args).GetB();
}
static jbyte CallStaticByteMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticByteMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, nullptr, mid, args).GetB();
}
@@ -1685,7 +1687,7 @@
static jchar CallStaticCharMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallStaticCharMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap));
va_end(ap);
@@ -1693,13 +1695,13 @@
}
static jchar CallStaticCharMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticCharMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, nullptr, mid, args).GetC();
}
static jchar CallStaticCharMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticCharMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, nullptr, mid, args).GetC();
}
@@ -1707,7 +1709,7 @@
static jshort CallStaticShortMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallStaticShortMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap));
va_end(ap);
@@ -1715,13 +1717,13 @@
}
static jshort CallStaticShortMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticShortMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, nullptr, mid, args).GetS();
}
static jshort CallStaticShortMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticShortMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, nullptr, mid, args).GetS();
}
@@ -1729,7 +1731,7 @@
static jint CallStaticIntMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallStaticIntMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap));
va_end(ap);
@@ -1737,13 +1739,13 @@
}
static jint CallStaticIntMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticIntMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, nullptr, mid, args).GetI();
}
static jint CallStaticIntMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticIntMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, nullptr, mid, args).GetI();
}
@@ -1751,7 +1753,7 @@
static jlong CallStaticLongMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallStaticLongMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap));
va_end(ap);
@@ -1759,13 +1761,13 @@
}
static jlong CallStaticLongMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticLongMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, nullptr, mid, args).GetJ();
}
static jlong CallStaticLongMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticLongMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, nullptr, mid, args).GetJ();
}
@@ -1773,7 +1775,7 @@
static jfloat CallStaticFloatMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallStaticFloatMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap));
va_end(ap);
@@ -1781,13 +1783,13 @@
}
static jfloat CallStaticFloatMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticFloatMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, nullptr, mid, args).GetF();
}
static jfloat CallStaticFloatMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticFloatMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, nullptr, mid, args).GetF();
}
@@ -1795,7 +1797,7 @@
static jdouble CallStaticDoubleMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallStaticDoubleMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap));
va_end(ap);
@@ -1803,13 +1805,13 @@
}
static jdouble CallStaticDoubleMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticDoubleMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithVarArgs(soa, nullptr, mid, args).GetD();
}
static jdouble CallStaticDoubleMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticDoubleMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
return InvokeWithJValues(soa, nullptr, mid, args).GetD();
}
@@ -1817,20 +1819,20 @@
static void CallStaticVoidMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
- CHECK_NON_NULL_ARGUMENT(CallStaticVoidMethod, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
InvokeWithVarArgs(soa, nullptr, mid, ap);
va_end(ap);
}
static void CallStaticVoidMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticVoidMethodV, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
InvokeWithVarArgs(soa, nullptr, mid, args);
}
static void CallStaticVoidMethodA(JNIEnv* env, jclass, jmethodID mid, jvalue* args) {
- CHECK_NON_NULL_ARGUMENT(CallStaticVoidMethodA, mid);
+ CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
InvokeWithJValues(soa, nullptr, mid, args);
}
@@ -1859,26 +1861,26 @@
}
static jsize GetStringLength(JNIEnv* env, jstring java_string) {
- CHECK_NON_NULL_ARGUMENT(GetStringLength, java_string);
+ CHECK_NON_NULL_ARGUMENT(java_string);
ScopedObjectAccess soa(env);
return soa.Decode<mirror::String*>(java_string)->GetLength();
}
static jsize GetStringUTFLength(JNIEnv* env, jstring java_string) {
- CHECK_NON_NULL_ARGUMENT(GetStringLength, java_string);
+ CHECK_NON_NULL_ARGUMENT(java_string);
ScopedObjectAccess soa(env);
return soa.Decode<mirror::String*>(java_string)->GetUtfLength();
}
static void GetStringRegion(JNIEnv* env, jstring java_string, jsize start, jsize length,
jchar* buf) {
- CHECK_NON_NULL_ARGUMENT(GetStringRegion, java_string);
+ CHECK_NON_NULL_ARGUMENT(java_string);
ScopedObjectAccess soa(env);
mirror::String* s = soa.Decode<mirror::String*>(java_string);
if (start < 0 || length < 0 || start + length > s->GetLength()) {
ThrowSIOOBE(soa, start, length, s->GetLength());
} else {
- CHECK_NON_NULL_MEMCPY_ARGUMENT(GetStringRegion, length, buf);
+ CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf);
const jchar* chars = s->GetCharArray()->GetData() + s->GetOffset();
memcpy(buf, chars + start, length * sizeof(jchar));
}
@@ -1886,20 +1888,20 @@
static void GetStringUTFRegion(JNIEnv* env, jstring java_string, jsize start, jsize length,
char* buf) {
- CHECK_NON_NULL_ARGUMENT(GetStringUTFRegion, java_string);
+ CHECK_NON_NULL_ARGUMENT(java_string);
ScopedObjectAccess soa(env);
mirror::String* s = soa.Decode<mirror::String*>(java_string);
if (start < 0 || length < 0 || start + length > s->GetLength()) {
ThrowSIOOBE(soa, start, length, s->GetLength());
} else {
- CHECK_NON_NULL_MEMCPY_ARGUMENT(GetStringUTFRegion, length, buf);
+ CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf);
const jchar* chars = s->GetCharArray()->GetData() + s->GetOffset();
ConvertUtf16ToModifiedUtf8(buf, chars + start, length);
}
}
static const jchar* GetStringChars(JNIEnv* env, jstring java_string, jboolean* is_copy) {
- CHECK_NON_NULL_ARGUMENT(GetStringChars, java_string);
+ CHECK_NON_NULL_ARGUMENT(java_string);
ScopedObjectAccess soa(env);
mirror::String* s = soa.Decode<mirror::String*>(java_string);
mirror::CharArray* chars = s->GetCharArray();
@@ -1918,7 +1920,7 @@
}
static void ReleaseStringChars(JNIEnv* env, jstring java_string, const jchar* chars) {
- CHECK_NON_NULL_ARGUMENT(ReleaseStringChars, java_string);
+ CHECK_NON_NULL_ARGUMENT(java_string);
delete[] chars;
ScopedObjectAccess soa(env);
UnpinPrimitiveArray(soa, soa.Decode<mirror::String*>(java_string)->GetCharArray());
@@ -1955,7 +1957,7 @@
}
static jsize GetArrayLength(JNIEnv* env, jarray java_array) {
- CHECK_NON_NULL_ARGUMENT(GetArrayLength, java_array);
+ CHECK_NON_NULL_ARGUMENT(java_array);
ScopedObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(java_array);
if (UNLIKELY(!obj->IsArrayInstance())) {
@@ -1966,7 +1968,7 @@
}
static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray java_array, jsize index) {
- CHECK_NON_NULL_ARGUMENT(GetObjectArrayElement, java_array);
+ CHECK_NON_NULL_ARGUMENT(java_array);
ScopedObjectAccess soa(env);
mirror::ObjectArray<mirror::Object>* array =
soa.Decode<mirror::ObjectArray<mirror::Object>*>(java_array);
@@ -1975,7 +1977,7 @@
static void SetObjectArrayElement(JNIEnv* env, jobjectArray java_array, jsize index,
jobject java_value) {
- CHECK_NON_NULL_ARGUMENT(SetObjectArrayElement, java_array);
+ CHECK_NON_NULL_ARGUMENT(java_array);
ScopedObjectAccess soa(env);
mirror::ObjectArray<mirror::Object>* array =
soa.Decode<mirror::ObjectArray<mirror::Object>*>(java_array);
@@ -2070,7 +2072,7 @@
}
static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray java_array, jboolean* is_copy) {
- CHECK_NON_NULL_ARGUMENT(GetPrimitiveArrayCritical, java_array);
+ CHECK_NON_NULL_ARGUMENT(java_array);
ScopedObjectAccess soa(env);
mirror::Array* array = soa.Decode<mirror::Array*>(java_array);
gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -2087,54 +2089,54 @@
}
static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* elements, jint mode) {
- CHECK_NON_NULL_ARGUMENT(ReleasePrimitiveArrayCritical, array);
+ CHECK_NON_NULL_ARGUMENT(array);
ReleasePrimitiveArray(env, array, elements, mode);
}
static jboolean* GetBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* is_copy) {
- CHECK_NON_NULL_ARGUMENT(GetBooleanArrayElements, array);
+ CHECK_NON_NULL_ARGUMENT(array);
ScopedObjectAccess soa(env);
return GetPrimitiveArray<jbooleanArray, jboolean*, mirror::BooleanArray>(soa, array, is_copy);
}
static jbyte* GetByteArrayElements(JNIEnv* env, jbyteArray array, jboolean* is_copy) {
- CHECK_NON_NULL_ARGUMENT(GetByteArrayElements, array);
+ CHECK_NON_NULL_ARGUMENT(array);
ScopedObjectAccess soa(env);
return GetPrimitiveArray<jbyteArray, jbyte*, mirror::ByteArray>(soa, array, is_copy);
}
static jchar* GetCharArrayElements(JNIEnv* env, jcharArray array, jboolean* is_copy) {
- CHECK_NON_NULL_ARGUMENT(GetCharArrayElements, array);
+ CHECK_NON_NULL_ARGUMENT(array);
ScopedObjectAccess soa(env);
return GetPrimitiveArray<jcharArray, jchar*, mirror::CharArray>(soa, array, is_copy);
}
static jdouble* GetDoubleArrayElements(JNIEnv* env, jdoubleArray array, jboolean* is_copy) {
- CHECK_NON_NULL_ARGUMENT(GetDoubleArrayElements, array);
+ CHECK_NON_NULL_ARGUMENT(array);
ScopedObjectAccess soa(env);
return GetPrimitiveArray<jdoubleArray, jdouble*, mirror::DoubleArray>(soa, array, is_copy);
}
static jfloat* GetFloatArrayElements(JNIEnv* env, jfloatArray array, jboolean* is_copy) {
- CHECK_NON_NULL_ARGUMENT(GetFloatArrayElements, array);
+ CHECK_NON_NULL_ARGUMENT(array);
ScopedObjectAccess soa(env);
return GetPrimitiveArray<jfloatArray, jfloat*, mirror::FloatArray>(soa, array, is_copy);
}
static jint* GetIntArrayElements(JNIEnv* env, jintArray array, jboolean* is_copy) {
- CHECK_NON_NULL_ARGUMENT(GetIntArrayElements, array);
+ CHECK_NON_NULL_ARGUMENT(array);
ScopedObjectAccess soa(env);
return GetPrimitiveArray<jintArray, jint*, mirror::IntArray>(soa, array, is_copy);
}
static jlong* GetLongArrayElements(JNIEnv* env, jlongArray array, jboolean* is_copy) {
- CHECK_NON_NULL_ARGUMENT(GetLongArrayElements, array);
+ CHECK_NON_NULL_ARGUMENT(array);
ScopedObjectAccess soa(env);
return GetPrimitiveArray<jlongArray, jlong*, mirror::LongArray>(soa, array, is_copy);
}
static jshort* GetShortArrayElements(JNIEnv* env, jshortArray array, jboolean* is_copy) {
- CHECK_NON_NULL_ARGUMENT(GetShortArrayElements, array);
+ CHECK_NON_NULL_ARGUMENT(array);
ScopedObjectAccess soa(env);
return GetPrimitiveArray<jshortArray, jshort*, mirror::ShortArray>(soa, array, is_copy);
}
@@ -2290,7 +2292,7 @@
JniAbortF("RegisterNatives", "negative method count: %d", method_count);
return JNI_ERR; // Not reached.
}
- CHECK_NON_NULL_ARGUMENT(RegisterNatives, java_class);
+ CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class);
ScopedObjectAccess soa(env);
mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
if (UNLIKELY(method_count == 0)) {
@@ -2298,7 +2300,7 @@
<< PrettyDescriptor(c);
return JNI_OK;
}
- CHECK_NON_NULL_ARGUMENT(RegisterNatives, methods);
+ CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods);
for (jint i = 0; i < method_count; ++i) {
const char* name = methods[i].name;
const char* sig = methods[i].signature;
@@ -2335,7 +2337,7 @@
}
static jint UnregisterNatives(JNIEnv* env, jclass java_class) {
- CHECK_NON_NULL_ARGUMENT(UnregisterNatives, java_class);
+ CHECK_NON_NULL_ARGUMENT(java_class);
ScopedObjectAccess soa(env);
mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
@@ -2358,7 +2360,7 @@
}
static jint MonitorEnter(JNIEnv* env, jobject java_object) NO_THREAD_SAFETY_ANALYSIS {
- CHECK_NON_NULL_ARGUMENT(MonitorEnter, java_object);
+ CHECK_NON_NULL_ARGUMENT(java_object);
ScopedObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
o = o->MonitorEnter(soa.Self());
@@ -2370,7 +2372,7 @@
}
static jint MonitorExit(JNIEnv* env, jobject java_object) NO_THREAD_SAFETY_ANALYSIS {
- CHECK_NON_NULL_ARGUMENT(MonitorExit, java_object);
+ CHECK_NON_NULL_ARGUMENT(java_object);
ScopedObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
o->MonitorExit(soa.Self());
@@ -2382,7 +2384,7 @@
}
static jint GetJavaVM(JNIEnv* env, JavaVM** vm) {
- CHECK_NON_NULL_ARGUMENT(GetJavaVM, vm);
+ CHECK_NON_NULL_ARGUMENT(vm);
Runtime* runtime = Runtime::Current();
if (runtime != nullptr) {
*vm = runtime->GetJavaVM();
@@ -2422,7 +2424,7 @@
}
static jobjectRefType GetObjectRefType(JNIEnv* env, jobject java_object) {
- CHECK_NON_NULL_ARGUMENT(GetObjectRefType, java_object);
+ CHECK_NON_NULL_ARGUMENT(java_object);
// Do we definitely know what kind of reference this is?
IndirectRef ref = reinterpret_cast<IndirectRef>(java_object);
@@ -2543,12 +2545,12 @@
static void GetPrimitiveArrayRegion(ScopedObjectAccess& soa, JavaArrayT java_array,
jsize start, jsize length, JavaT* buf)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- CHECK_NON_NULL_ARGUMENT(GetPrimitiveArrayRegion, java_array);
+ CHECK_NON_NULL_ARGUMENT(java_array);
ArrayT* array = soa.Decode<ArrayT*>(java_array);
if (start < 0 || length < 0 || start + length > array->GetLength()) {
ThrowAIOOBE(soa, array, start, length, "src");
} else {
- CHECK_NON_NULL_MEMCPY_ARGUMENT(GetStringRegion, length, buf);
+ CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf);
JavaT* data = array->GetData();
memcpy(buf, data + start, length * sizeof(JavaT));
}
@@ -2558,12 +2560,12 @@
static void SetPrimitiveArrayRegion(ScopedObjectAccess& soa, JavaArrayT java_array,
jsize start, jsize length, const JavaT* buf)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- CHECK_NON_NULL_ARGUMENT(SetPrimitiveArrayRegion, java_array);
+ CHECK_NON_NULL_ARGUMENT(java_array);
ArrayT* array = soa.Decode<ArrayT*>(java_array);
if (start < 0 || length < 0 || start + length > array->GetLength()) {
ThrowAIOOBE(soa, array, start, length, "dst");
} else {
- CHECK_NON_NULL_MEMCPY_ARGUMENT(GetStringRegion, length, buf);
+ CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf);
JavaT* data = array->GetData();
memcpy(data + start, buf, length * sizeof(JavaT));
}
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 3d2fd7b..7f974d0 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -27,10 +27,11 @@
namespace art {
namespace mirror {
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, bool kDoReadBarrier>
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<kVerifyFlags>()->GetComponentSize();
+ size_t component_size =
+ GetClass<kVerifyFlags, kDoReadBarrier>()->template GetComponentSize<kDoReadBarrier>();
// Don't need to check this since we already check this in GetClass.
int32_t component_count =
GetLength<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>();
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 772d303..6bfd5c8 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -41,7 +41,7 @@
const SirtRef<IntArray>& dimensions)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kDoReadBarrier = true>
size_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
int32_t GetLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc
index f91cab1..7b0b94c 100644
--- a/runtime/mirror/art_field.cc
+++ b/runtime/mirror/art_field.cc
@@ -19,6 +19,7 @@
#include "art_field-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "object-inl.h"
+#include "object_array-inl.h"
#include "object_utils.h"
#include "runtime.h"
#include "scoped_thread_state_change.h"
@@ -69,5 +70,25 @@
}
}
+// TODO: we could speed up the search if fields are ordered by offsets.
+ArtField* ArtField::FindInstanceFieldWithOffset(mirror::Class* klass, uint32_t field_offset) {
+ DCHECK(klass != nullptr);
+ ObjectArray<ArtField>* instance_fields = klass->GetIFields();
+ if (instance_fields != nullptr) {
+ for (int32_t i = 0, e = instance_fields->GetLength(); i < e; ++i) {
+ mirror::ArtField* field = instance_fields->GetWithoutChecks(i);
+ if (field->GetOffset().Uint32Value() == field_offset) {
+ return field;
+ }
+ }
+ }
+ // We did not find field in the class: look into superclass.
+ if (klass->GetSuperClass() != NULL) {
+ return FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset);
+ } else {
+ return nullptr;
+ }
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h
index 0daa838..ba70cc6 100644
--- a/runtime/mirror/art_field.h
+++ b/runtime/mirror/art_field.h
@@ -132,6 +132,10 @@
return (GetAccessFlags() & kAccVolatile) != 0;
}
+ // Returns an instance field with this offset in the given class or nullptr if not found.
+ static ArtField* FindInstanceFieldWithOffset(mirror::Class* klass, uint32_t field_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
private:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
// The class we are a part of
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index 5d62b88..6e1f062 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -78,13 +78,11 @@
inline uint32_t ArtMethod::GetCodeSize() {
DCHECK(!IsRuntimeMethod() && !IsProxyMethod()) << PrettyMethod(this);
- uintptr_t code = reinterpret_cast<uintptr_t>(GetEntryPointFromQuickCompiledCode());
- if (code == 0) {
- return 0;
+ const void* code = EntryPointToCodePointer(GetEntryPointFromQuickCompiledCode());
+ if (code == nullptr) {
+ return 0u;
}
- // TODO: make this Thumb2 specific
- code &= ~0x1;
- return reinterpret_cast<OatMethodHeader*>(code)[-1].code_size_;
+ return reinterpret_cast<const OatMethodHeader*>(code)[-1].code_size_;
}
inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) {
@@ -124,7 +122,8 @@
return;
}
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (code == GetQuickResolutionTrampoline(class_linker)) {
+ if (code == GetQuickResolutionTrampoline(class_linker) ||
+ code == GetQuickToInterpreterBridgeTrampoline(class_linker)) {
return;
}
DCHECK(IsWithinQuickCode(pc))
@@ -154,26 +153,6 @@
SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(code_offset));
}
-inline uint32_t ArtMethod::GetOatMappingTableOffset() {
- DCHECK(!Runtime::Current()->IsStarted());
- return PointerToLowMemUInt32(GetMappingTable());
-}
-
-inline void ArtMethod::SetOatMappingTableOffset(uint32_t mapping_table_offset) {
- DCHECK(!Runtime::Current()->IsStarted());
- SetMappingTable(reinterpret_cast<const uint8_t*>(mapping_table_offset));
-}
-
-inline uint32_t ArtMethod::GetOatVmapTableOffset() {
- DCHECK(!Runtime::Current()->IsStarted());
- return PointerToLowMemUInt32(GetVmapTable());
-}
-
-inline void ArtMethod::SetOatVmapTableOffset(uint32_t vmap_table_offset) {
- DCHECK(!Runtime::Current()->IsStarted());
- SetVmapTable(reinterpret_cast<uint8_t*>(vmap_table_offset));
-}
-
inline void ArtMethod::SetOatNativeGcMapOffset(uint32_t gc_map_offset) {
DCHECK(!Runtime::Current()->IsStarted());
SetNativeGcMap(reinterpret_cast<uint8_t*>(gc_map_offset));
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index f3303a8..90bcbab 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -315,13 +315,13 @@
} else {
(*art_portable_invoke_stub)(this, args, args_size, self, result, shorty[0]);
}
- if (UNLIKELY(reinterpret_cast<intptr_t>(self->GetException(NULL)) == -1)) {
- // Unusual case where we were running LLVM generated code and an
+ if (UNLIKELY(self->GetException(nullptr) == Thread::GetDeoptimizationException())) {
+ // Unusual case where we were running generated code and an
// exception was thrown to force the activations to be removed from the
// stack. Continue execution in the interpreter.
self->ClearException();
ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(result);
- self->SetTopOfStack(NULL, 0);
+ self->SetTopOfStack(nullptr, 0);
self->SetTopOfShadowStack(shadow_frame);
interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result);
}
@@ -368,5 +368,43 @@
RegisterNative(self, GetJniDlsymLookupStub(), false);
}
+const void* ArtMethod::GetOatCodePointer() {
+ if (IsPortableCompiled() || IsNative() || IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) {
+ return nullptr;
+ }
+ Runtime* runtime = Runtime::Current();
+ const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this);
+ // On failure, instead of nullptr we get the quick-to-interpreter-bridge (but not the trampoline).
+ DCHECK(entry_point != GetQuickToInterpreterBridgeTrampoline(runtime->GetClassLinker()));
+ if (entry_point == GetQuickToInterpreterBridge()) {
+ return nullptr;
+ }
+ return EntryPointToCodePointer(entry_point);
+}
+
+const uint8_t* ArtMethod::GetMappingTable() {
+ const void* code = GetOatCodePointer();
+ if (code == nullptr) {
+ return nullptr;
+ }
+ uint32_t offset = reinterpret_cast<const OatMethodHeader*>(code)[-1].mapping_table_offset_;
+ if (UNLIKELY(offset == 0u)) {
+ return nullptr;
+ }
+ return reinterpret_cast<const uint8_t*>(code) - offset;
+}
+
+const uint8_t* ArtMethod::GetVmapTable() {
+ const void* code = GetOatCodePointer();
+ if (code == nullptr) {
+ return nullptr;
+ }
+ uint32_t offset = reinterpret_cast<const OatMethodHeader*>(code)[-1].vmap_table_offset_;
+ if (UNLIKELY(offset == 0u)) {
+ return nullptr;
+ }
+ return reinterpret_cast<const uint8_t*>(code) - offset;
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index d684266..b3b9ca7 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -21,6 +21,7 @@
#include "dex_file.h"
#include "invoke_type.h"
#include "modifiers.h"
+#include "oat.h"
#include "object.h"
#include "object_callbacks.h"
@@ -261,7 +262,6 @@
EntryPointFromQuickCompiledCodeOffset(), entry_point_from_quick_compiled_code, false);
}
-
uint32_t GetCodeSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsWithinQuickCode(uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -286,37 +286,20 @@
void SetQuickOatCodeOffset(uint32_t code_offset);
void SetPortableOatCodeOffset(uint32_t code_offset);
+ static const void* EntryPointToCodePointer(const void* entry_point) ALWAYS_INLINE {
+ uintptr_t code = reinterpret_cast<uintptr_t>(entry_point);
+ code &= ~0x1; // TODO: Make this Thumb2 specific.
+ return reinterpret_cast<const void*>(code);
+ }
+
+ // Actual pointer to compiled oat code or nullptr.
+ const void* GetOatCodePointer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Callers should wrap the uint8_t* in a MappingTable instance for convenient access.
- const uint8_t* GetMappingTable() {
- return GetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_),
- false);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void SetMappingTable(const uint8_t* mapping_table) {
- SetFieldPtr<false, true, kVerifyFlags>(
- OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_), mapping_table, false);
- }
-
- uint32_t GetOatMappingTableOffset();
-
- void SetOatMappingTableOffset(uint32_t mapping_table_offset);
+ const uint8_t* GetMappingTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Callers should wrap the uint8_t* in a VmapTable instance for convenient access.
- const uint8_t* GetVmapTable() {
- return GetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_),
- false);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void SetVmapTable(const uint8_t* vmap_table) {
- SetFieldPtr<false, true, kVerifyFlags>(
- OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_), vmap_table, false);
- }
-
- uint32_t GetOatVmapTableOffset();
-
- void SetOatVmapTableOffset(uint32_t vmap_table_offset);
+ const uint8_t* GetVmapTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const uint8_t* GetNativeGcMap() {
return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), false);
@@ -426,7 +409,9 @@
static void SetClass(Class* java_lang_reflect_ArtMethod);
+ template <bool kDoReadBarrier = true>
static Class* GetJavaLangReflectArtMethod() {
+ // This does not need a RB because it is a root.
return java_lang_reflect_ArtMethod_;
}
@@ -469,20 +454,6 @@
// offsets for the quick compiler and dex PCs for the portable.
uint64_t gc_map_;
- // --- Quick compiler meta-data. ---
- // TODO: merge and place in native heap, such as done with the code size.
-
- // Pointer to a data structure created by the quick compiler to map between dex PCs and native
- // PCs, and vice-versa.
- uint64_t quick_mapping_table_;
-
- // When a register is promoted into a register, the spill mask holds which registers hold dex
- // registers. The first promoted register's corresponding dex register is vmap_table_[1], the Nth
- // is vmap_table_[N]. vmap_table_[0] holds the length of the table.
- uint64_t quick_vmap_table_;
-
- // --- End of quick compiler meta-data. ---
-
// Access flags; low 16 bits are defined by spec.
uint32_t access_flags_;
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 025e62a..3c02aa0 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -478,6 +478,19 @@
VisitStaticFieldsReferences<kVisitClass>(this, visitor);
}
+template<bool kDoReadBarrier>
+bool Class::IsArtFieldClass() {
+ Class* java_lang_Class = GetClass<kVerifyNone, kDoReadBarrier>();
+ Class* java_lang_reflect_ArtField =
+ java_lang_Class->GetInstanceField(0)->GetClass<kVerifyNone, kDoReadBarrier>();
+ return this == java_lang_reflect_ArtField;
+}
+
+template<bool kDoReadBarrier>
+bool Class::IsArtMethodClass() {
+ return this == ArtMethod::GetJavaLangReflectArtMethod<kDoReadBarrier>();
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 6dbb29d..ad86e1f 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -328,16 +328,6 @@
return WellKnownClasses::ToClass(WellKnownClasses::java_lang_Throwable)->IsAssignableFrom(this);
}
-bool Class::IsArtFieldClass() {
- Class* java_lang_Class = GetClass();
- Class* java_lang_reflect_ArtField = java_lang_Class->GetInstanceField(0)->GetClass();
- return this == java_lang_reflect_ArtField;
-}
-
-bool Class::IsArtMethodClass() {
- return this == ArtMethod::GetJavaLangReflectArtMethod();
-}
-
void Class::SetClassLoader(ClassLoader* new_class_loader) {
if (Runtime::Current()->IsActiveTransaction()) {
SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false);
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index d955b97..226dee0 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -364,9 +364,9 @@
return depth;
}
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kDoReadBarrier = true>
bool IsArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetComponentType<kVerifyFlags>() != NULL;
+ return GetComponentType<kVerifyFlags, kDoReadBarrier>() != NULL;
}
bool IsClassClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -375,17 +375,19 @@
bool IsThrowableClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kDoReadBarrier = true>
bool IsArtFieldClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kDoReadBarrier = true>
bool IsArtMethodClass();
static MemberOffset ComponentTypeOffset() {
return OFFSET_OF_OBJECT_MEMBER(Class, component_type_);
}
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kDoReadBarrier = true>
Class* GetComponentType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetFieldObject<Class, kVerifyFlags>(ComponentTypeOffset(), false);
+ return GetFieldObject<Class, kVerifyFlags, kDoReadBarrier>(ComponentTypeOffset(), false);
}
void SetComponentType(Class* new_component_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -395,8 +397,10 @@
SetFieldObject<false, false>(ComponentTypeOffset(), new_component_type, false);
}
+ template<bool kDoReadBarrier = true>
size_t GetComponentSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return Primitive::ComponentSize(GetComponentType()->GetPrimitiveType());
+ return Primitive::ComponentSize(
+ GetComponentType<kDefaultVerifyFlags, kDoReadBarrier>()->GetPrimitiveType());
}
bool IsObjectClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -427,7 +431,7 @@
return IsClassClass() || IsArrayClass();
}
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kDoReadBarrier = true>
uint32_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), false);
}
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index b195dea..04517ec 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -34,9 +34,10 @@
namespace art {
namespace mirror {
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, bool kDoReadBarrier>
inline Class* Object::GetClass() {
- return GetFieldObject<Class, kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Object, klass_), false);
+ return GetFieldObject<Class, kVerifyFlags, kDoReadBarrier>(
+ OFFSET_OF_OBJECT_MEMBER(Object, klass_), false);
}
template<VerifyObjectFlags kVerifyFlags>
@@ -105,18 +106,45 @@
#endif
}
-inline void Object::SetReadBarrierPointer(Object* rb_pointer) {
+inline void Object::SetReadBarrierPointer(Object* rb_ptr) {
#ifdef USE_BAKER_OR_BROOKS_READ_BARRIER
DCHECK(kUseBakerOrBrooksReadBarrier);
// We don't mark the card as this occurs as part of object allocation. Not all objects have
// backing cards, such as large objects.
SetFieldObjectWithoutWriteBarrier<false, false, kVerifyNone>(
- OFFSET_OF_OBJECT_MEMBER(Object, x_rb_ptr_), rb_pointer, false);
+ OFFSET_OF_OBJECT_MEMBER(Object, x_rb_ptr_), rb_ptr, false);
#else
LOG(FATAL) << "Unreachable";
#endif
}
+inline bool Object::AtomicSetReadBarrierPointer(Object* expected_rb_ptr, Object* rb_ptr) {
+#ifdef USE_BAKER_OR_BROOKS_READ_BARRIER
+ DCHECK(kUseBakerOrBrooksReadBarrier);
+ MemberOffset offset = OFFSET_OF_OBJECT_MEMBER(Object, x_rb_ptr_);
+ byte* raw_addr = reinterpret_cast<byte*>(this) + offset.SizeValue();
+ HeapReference<Object>* ref = reinterpret_cast<HeapReference<Object>*>(raw_addr);
+ HeapReference<Object> expected_ref(HeapReference<Object>::FromMirrorPtr(expected_rb_ptr));
+ HeapReference<Object> new_ref(HeapReference<Object>::FromMirrorPtr(rb_ptr));
+ uint32_t expected_val = expected_ref.reference_;
+ uint32_t new_val;
+ do {
+ uint32_t old_val = ref->reference_;
+ if (old_val != expected_val) {
+ // Lost the race.
+ return false;
+ }
+ new_val = new_ref.reference_;
+ } while (!__sync_bool_compare_and_swap(
+ reinterpret_cast<uint32_t*>(raw_addr), expected_val, new_val));
+ DCHECK_EQ(new_val, ref->reference_);
+ return true;
+#else
+ LOG(FATAL) << "Unreachable";
+ return false;
+#endif
+}
+
inline void Object::AssertReadBarrierPointer() const {
if (kUseBakerReadBarrier) {
Object* obj = const_cast<Object*>(this);
@@ -147,16 +175,17 @@
return klass->IsAssignableFrom(GetClass<kVerifyFlags>());
}
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, bool kDoReadBarrier>
inline bool Object::IsClass() {
- Class* java_lang_Class = GetClass<kVerifyFlags>()->GetClass();
- return GetClass<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>() ==
+ Class* java_lang_Class =
+ GetClass<kVerifyFlags, kDoReadBarrier>()->template GetClass<kVerifyFlags, kDoReadBarrier>();
+ return GetClass<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis), kDoReadBarrier>() ==
java_lang_Class;
}
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, bool kDoReadBarrier>
inline Class* Object::AsClass() {
- DCHECK(IsClass<kVerifyFlags>());
+ DCHECK((IsClass<kVerifyFlags, kDoReadBarrier>()));
return down_cast<Class*>(this);
}
@@ -173,14 +202,15 @@
return down_cast<ObjectArray<T>*>(this);
}
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, bool kDoReadBarrier>
inline bool Object::IsArrayInstance() {
- return GetClass<kVerifyFlags>()->IsArrayClass();
+ return GetClass<kVerifyFlags, kDoReadBarrier>()->
+ template IsArrayClass<kVerifyFlags, kDoReadBarrier>();
}
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, bool kDoReadBarrier>
inline bool Object::IsArtField() {
- return GetClass<kVerifyFlags>()->IsArtFieldClass();
+ return GetClass<kVerifyFlags, kDoReadBarrier>()->template IsArtFieldClass<kDoReadBarrier>();
}
template<VerifyObjectFlags kVerifyFlags>
@@ -189,9 +219,9 @@
return down_cast<ArtField*>(this);
}
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, bool kDoReadBarrier>
inline bool Object::IsArtMethod() {
- return GetClass<kVerifyFlags>()->IsArtMethodClass();
+ return GetClass<kVerifyFlags, kDoReadBarrier>()->template IsArtMethodClass<kDoReadBarrier>();
}
template<VerifyObjectFlags kVerifyFlags>
@@ -211,9 +241,9 @@
return down_cast<Reference*>(this);
}
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, bool kDoReadBarrier>
inline Array* Object::AsArray() {
- DCHECK(IsArrayInstance<kVerifyFlags>());
+ DCHECK((IsArrayInstance<kVerifyFlags, kDoReadBarrier>()));
return down_cast<Array*>(this);
}
@@ -339,20 +369,21 @@
return GetClass<kVerifyFlags>()->IsPhantomReferenceClass();
}
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, bool kDoReadBarrier>
inline size_t Object::SizeOf() {
size_t result;
constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
- if (IsArrayInstance<kVerifyFlags>()) {
- result = AsArray<kNewFlags>()->template SizeOf<kNewFlags>();
- } else if (IsClass<kNewFlags>()) {
- result = AsClass<kNewFlags>()->template SizeOf<kNewFlags>();
+ if (IsArrayInstance<kVerifyFlags, kDoReadBarrier>()) {
+ result = AsArray<kNewFlags, kDoReadBarrier>()->template SizeOf<kNewFlags, kDoReadBarrier>();
+ } else if (IsClass<kNewFlags, kDoReadBarrier>()) {
+ result = AsClass<kNewFlags, kDoReadBarrier>()->template SizeOf<kNewFlags, kDoReadBarrier>();
} else {
- result = GetClass<kNewFlags>()->GetObjectSize();
+ result = GetClass<kNewFlags, kDoReadBarrier>()->GetObjectSize();
}
- DCHECK_GE(result, sizeof(Object)) << " class=" << PrettyTypeOf(GetClass<kNewFlags>());
- DCHECK(!IsArtField<kNewFlags>() || result == sizeof(ArtField));
- DCHECK(!IsArtMethod<kNewFlags>() || result == sizeof(ArtMethod));
+ DCHECK_GE(result, sizeof(Object))
+ << " class=" << PrettyTypeOf(GetClass<kNewFlags, kDoReadBarrier>());
+ DCHECK(!(IsArtField<kNewFlags, kDoReadBarrier>()) || result == sizeof(ArtField));
+ DCHECK(!(IsArtMethod<kNewFlags, kDoReadBarrier>()) || result == sizeof(ArtMethod));
return result;
}
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index fd31dfb..370b3b8 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -72,14 +72,16 @@
return OFFSET_OF_OBJECT_MEMBER(Object, klass_);
}
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kDoReadBarrier = true>
Class* GetClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetClass(Class* new_klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Object* GetReadBarrierPointer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetReadBarrierPointer(Object* rb_pointer) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetReadBarrierPointer(Object* rb_ptr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool AtomicSetReadBarrierPointer(Object* expected_rb_ptr, Object* rb_ptr)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void AssertReadBarrierPointer() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// The verifier treats all interfaces as java.lang.Object and relies on runtime checks in
@@ -89,7 +91,7 @@
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool InstanceOf(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kDoReadBarrier = true>
size_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Object* Clone(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -116,9 +118,9 @@
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>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kDoReadBarrier = true>
bool IsClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kDoReadBarrier = true>
Class* AsClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -126,9 +128,9 @@
template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ObjectArray<T>* AsObjectArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kDoReadBarrier = true>
bool IsArrayInstance() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kDoReadBarrier = true>
Array* AsArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -161,12 +163,12 @@
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
Throwable* AsThrowable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kDoReadBarrier = true>
bool IsArtMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ArtMethod* AsArtMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kDoReadBarrier = true>
bool IsArtField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ArtField* AsArtField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h
index 5ff0490..26b1fd1 100644
--- a/runtime/mirror/object_array.h
+++ b/runtime/mirror/object_array.h
@@ -83,9 +83,9 @@
template<const bool kVisitClass, typename Visitor>
void VisitReferences(const Visitor& visitor) NO_THREAD_SAFETY_ANALYSIS;
- private:
static MemberOffset OffsetOfElement(int32_t i);
+ private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ObjectArray);
};
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 6af16f4..2231070 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -262,7 +262,7 @@
static jboolean DexFile_isDexOptNeededInternal(JNIEnv* env, jclass, jstring javaFilename,
jstring javaPkgname, jboolean defer) {
const bool kVerboseLogging = false; // Spammy logging.
- const bool kDebugLogging = true; // Logging useful for debugging.
+ const bool kReasonLogging = true; // Logging of reason for returning JNI_TRUE.
ScopedUtfChars filename(env, javaFilename);
if ((filename.c_str() == nullptr) || !OS::FileExists(filename.c_str())) {
@@ -312,7 +312,7 @@
int e2 = stat(prev_profile_file.c_str(), &prevstat);
if (e1 < 0) {
// No profile file, need to run dex2oat
- if (kDebugLogging) {
+ if (kReasonLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded profile file " << profile_file << " doesn't exist";
}
return JNI_TRUE;
@@ -330,12 +330,12 @@
bool newOk = ProfileHelper::LoadTopKSamples(newTopK, profile_file, topKThreshold);
bool oldOk = ProfileHelper::LoadTopKSamples(oldTopK, prev_profile_file, topKThreshold);
if (!newOk || !oldOk) {
- if (kDebugLogging) {
+ if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded Ignoring invalid profiles: "
<< (newOk ? "" : profile_file) << " " << (oldOk ? "" : prev_profile_file);
}
} else if (newTopK.empty()) {
- if (kDebugLogging && kVerboseLogging) {
+ if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded empty profile: " << profile_file;
}
// If the new topK is empty we shouldn't optimize so we leave the changePercent at 0.0.
@@ -345,7 +345,7 @@
std::inserter(diff, diff.end()));
// TODO: consider using the usedPercentage instead of the plain diff count.
changePercent = 100.0 * static_cast<double>(diff.size()) / static_cast<double>(newTopK.size());
- if (kDebugLogging && kVerboseLogging) {
+ if (kVerboseLogging) {
std::set<std::string>::iterator end = diff.end();
for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) {
LOG(INFO) << "DexFile_isDexOptNeeded new in topK: " << *it;
@@ -354,7 +354,7 @@
}
if (changePercent > changeThreshold) {
- if (kDebugLogging) {
+ if (kReasonLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded size of new profile file " << profile_file <<
" is significantly different from old profile file " << prev_profile_file << " (top "
<< topKThreshold << "% samples changed in proportion of " << changePercent << "%)";
@@ -366,13 +366,12 @@
}
} else {
// Previous profile does not exist. Make a copy of the current one.
- if (kDebugLogging) {
+ if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded previous profile doesn't exist: " << prev_profile_file;
}
if (!defer) {
CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str());
}
- return JNI_TRUE;
}
}
@@ -389,7 +388,7 @@
error_msg.clear();
} else {
const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename.c_str(), NULL,
- kDebugLogging);
+ kReasonLogging);
if (oat_dex_file != nullptr) {
uint32_t location_checksum;
// If its not possible to read the classes.dex assume up-to-date as we won't be able to
@@ -423,7 +422,7 @@
std::string cache_location(GetDalvikCacheFilenameOrDie(filename.c_str()));
oat_file.reset(OatFile::Open(cache_location, filename.c_str(), NULL, false, &error_msg));
if (oat_file.get() == nullptr) {
- if (kDebugLogging) {
+ if (kReasonLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
<< " does not exist for " << filename.c_str() << ": " << error_msg;
}
@@ -436,7 +435,7 @@
const ImageHeader& image_header = space->AsImageSpace()->GetImageHeader();
if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() !=
image_header.GetOatChecksum()) {
- if (kDebugLogging) {
+ if (kReasonLogging) {
ScopedObjectAccess soa(env);
LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
<< " has out-of-date oat checksum compared to "
@@ -446,7 +445,7 @@
}
if (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin()
!= reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin())) {
- if (kDebugLogging) {
+ if (kReasonLogging) {
ScopedObjectAccess soa(env);
LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
<< " has out-of-date oat begin compared to "
@@ -459,7 +458,7 @@
uint32_t location_checksum;
if (!DexFile::GetChecksum(filename.c_str(), &location_checksum, &error_msg)) {
- if (kDebugLogging) {
+ if (kReasonLogging) {
LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename.c_str()
<< " (error " << error_msg << ")";
}
@@ -468,7 +467,7 @@
if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum,
&error_msg)) {
- if (kDebugLogging) {
+ if (kReasonLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
<< " has out-of-date checksum compared to " << filename.c_str()
<< " (error " << error_msg << ")";
diff --git a/runtime/oat.cc b/runtime/oat.cc
index d01dc72b..c1a48e9 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', '2', '1', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '2', '2', '\0' };
OatHeader::OatHeader() {
memset(this, 0, sizeof(*this));
@@ -348,8 +348,6 @@
frame_size_in_bytes_(0),
core_spill_mask_(0),
fp_spill_mask_(0),
- mapping_table_offset_(0),
- vmap_table_offset_(0),
gc_map_offset_(0)
{}
@@ -357,27 +355,28 @@
uint32_t frame_size_in_bytes,
uint32_t core_spill_mask,
uint32_t fp_spill_mask,
- uint32_t mapping_table_offset,
- uint32_t vmap_table_offset,
uint32_t gc_map_offset
)
: code_offset_(code_offset),
frame_size_in_bytes_(frame_size_in_bytes),
core_spill_mask_(core_spill_mask),
fp_spill_mask_(fp_spill_mask),
- mapping_table_offset_(mapping_table_offset),
- vmap_table_offset_(vmap_table_offset),
gc_map_offset_(gc_map_offset)
{}
OatMethodOffsets::~OatMethodOffsets() {}
OatMethodHeader::OatMethodHeader()
- : code_size_(0)
+ : mapping_table_offset_(0),
+ vmap_table_offset_(0),
+ code_size_(0)
{}
-OatMethodHeader::OatMethodHeader(uint32_t code_size)
- : code_size_(code_size)
+OatMethodHeader::OatMethodHeader(uint32_t vmap_table_offset, uint32_t mapping_table_offset,
+ uint32_t code_size)
+ : mapping_table_offset_(mapping_table_offset),
+ vmap_table_offset_(vmap_table_offset),
+ code_size_(code_size)
{}
OatMethodHeader::~OatMethodHeader() {}
diff --git a/runtime/oat.h b/runtime/oat.h
index 035aba1..e9dfae9 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -119,9 +119,9 @@
DISALLOW_COPY_AND_ASSIGN(OatHeader);
};
-// OatMethodOffsets are currently 7x32-bits=224-bits long, so if we can
+// OatMethodOffsets are currently 5x32-bits=160-bits long, so if we can
// save even one OatMethodOffsets struct, the more complicated encoding
-// using a bitmap pays for itself since few classes will have 224
+// using a bitmap pays for itself since few classes will have 160
// methods.
enum OatClassType {
kOatClassAllCompiled = 0, // OatClass is followed by an OatMethodOffsets for each method.
@@ -140,8 +140,6 @@
uint32_t frame_size_in_bytes,
uint32_t core_spill_mask,
uint32_t fp_spill_mask,
- uint32_t mapping_table_offset,
- uint32_t vmap_table_offset,
uint32_t gc_map_offset);
~OatMethodOffsets();
@@ -150,8 +148,6 @@
uint32_t frame_size_in_bytes_;
uint32_t core_spill_mask_;
uint32_t fp_spill_mask_;
- uint32_t mapping_table_offset_;
- uint32_t vmap_table_offset_;
uint32_t gc_map_offset_;
};
@@ -160,10 +156,15 @@
public:
OatMethodHeader();
- explicit OatMethodHeader(uint32_t code_size);
+ explicit OatMethodHeader(uint32_t mapping_table_offset, uint32_t vmap_table_offset,
+ uint32_t code_size);
~OatMethodHeader();
+ // The offset in bytes from the start of the mapping table to the end of the header.
+ uint32_t mapping_table_offset_;
+ // The offset in bytes from the start of the vmap table to the end of the header.
+ uint32_t vmap_table_offset_;
// The code size in bytes.
uint32_t code_size_;
};
diff --git a/runtime/oat_file-inl.h b/runtime/oat_file-inl.h
new file mode 100644
index 0000000..00ae797
--- /dev/null
+++ b/runtime/oat_file-inl.h
@@ -0,0 +1,60 @@
+/*
+ * 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_OAT_FILE_INL_H_
+#define ART_RUNTIME_OAT_FILE_INL_H_
+
+#include "oat_file.h"
+
+namespace art {
+
+inline uint32_t OatFile::OatMethod::GetMappingTableOffset() const {
+ const uint8_t* mapping_table = GetMappingTable();
+ return static_cast<uint32_t>(mapping_table != nullptr ? mapping_table - begin_ : 0u);
+}
+
+inline uint32_t OatFile::OatMethod::GetVmapTableOffset() const {
+ const uint8_t* vmap_table = GetVmapTable();
+ return static_cast<uint32_t>(vmap_table != nullptr ? vmap_table - begin_ : 0u);
+}
+
+inline const uint8_t* OatFile::OatMethod::GetMappingTable() const {
+ const void* code = mirror::ArtMethod::EntryPointToCodePointer(GetQuickCode());
+ if (code == nullptr) {
+ return nullptr;
+ }
+ uint32_t offset = reinterpret_cast<const OatMethodHeader*>(code)[-1].mapping_table_offset_;
+ if (UNLIKELY(offset == 0u)) {
+ return nullptr;
+ }
+ return reinterpret_cast<const uint8_t*>(code) - offset;
+}
+
+inline const uint8_t* OatFile::OatMethod::GetVmapTable() const {
+ const void* code = mirror::ArtMethod::EntryPointToCodePointer(GetQuickCode());
+ if (code == nullptr) {
+ return nullptr;
+ }
+ uint32_t offset = reinterpret_cast<const OatMethodHeader*>(code)[-1].vmap_table_offset_;
+ if (UNLIKELY(offset == 0u)) {
+ return nullptr;
+ }
+ return reinterpret_cast<const uint8_t*>(code) - offset;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_OAT_FILE_INL_H_
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 0aff8c3..56e1f05 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -464,7 +464,7 @@
// NOTE: We don't keep the number of methods and cannot do a bounds check for method_index.
if (methods_pointer_ == NULL) {
CHECK_EQ(kOatClassNoneCompiled, type_);
- return OatMethod(NULL, 0, 0, 0, 0, 0, 0, 0);
+ return OatMethod(NULL, 0, 0, 0, 0, 0);
}
size_t methods_pointer_index;
if (bitmap_ == NULL) {
@@ -473,7 +473,7 @@
} else {
CHECK_EQ(kOatClassSomeCompiled, type_);
if (!BitVector::IsBitSet(bitmap_, method_index)) {
- return OatMethod(NULL, 0, 0, 0, 0, 0, 0, 0);
+ return OatMethod(NULL, 0, 0, 0, 0, 0);
}
size_t num_set_bits = BitVector::NumSetBits(bitmap_, method_index);
methods_pointer_index = num_set_bits;
@@ -485,8 +485,6 @@
oat_method_offsets.frame_size_in_bytes_,
oat_method_offsets.core_spill_mask_,
oat_method_offsets.fp_spill_mask_,
- oat_method_offsets.mapping_table_offset_,
- oat_method_offsets.vmap_table_offset_,
oat_method_offsets.gc_map_offset_);
}
@@ -495,32 +493,13 @@
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- const uint32_t mapping_table_offset,
- const uint32_t vmap_table_offset,
const uint32_t gc_map_offset)
: begin_(base),
code_offset_(code_offset),
frame_size_in_bytes_(frame_size_in_bytes),
core_spill_mask_(core_spill_mask),
fp_spill_mask_(fp_spill_mask),
- mapping_table_offset_(mapping_table_offset),
- vmap_table_offset_(vmap_table_offset),
native_gc_map_offset_(gc_map_offset) {
- if (kIsDebugBuild) {
- if (mapping_table_offset_ != 0) { // implies non-native, non-stub code
- if (vmap_table_offset_ == 0) {
- CHECK_EQ(0U, static_cast<uint32_t>(__builtin_popcount(core_spill_mask_) +
- __builtin_popcount(fp_spill_mask_)));
- } else {
- VmapTable vmap_table(reinterpret_cast<const uint8_t*>(begin_ + vmap_table_offset_));
-
- CHECK_EQ(vmap_table.Size(), static_cast<uint32_t>(__builtin_popcount(core_spill_mask_) +
- __builtin_popcount(fp_spill_mask_)));
- }
- } else {
- CHECK_EQ(vmap_table_offset_, 0U);
- }
- }
}
OatFile::OatMethod::~OatMethod() {}
@@ -543,8 +522,6 @@
method->SetFrameSizeInBytes(frame_size_in_bytes_);
method->SetCoreSpillMask(core_spill_mask_);
method->SetFpSpillMask(fp_spill_mask_);
- method->SetMappingTable(GetMappingTable());
- method->SetVmapTable(GetVmapTable());
method->SetNativeGcMap(GetNativeGcMap()); // Used by native methods in work around JNI mode.
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 10f64cc..5f6cb1e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -87,12 +87,6 @@
uint32_t GetFpSpillMask() const {
return fp_spill_mask_;
}
- uint32_t GetMappingTableOffset() const {
- return mapping_table_offset_;
- }
- uint32_t GetVmapTableOffset() const {
- return vmap_table_offset_;
- }
uint32_t GetNativeGcMapOffset() const {
return native_gc_map_offset_;
}
@@ -122,16 +116,15 @@
}
uint32_t GetQuickCodeSize() const;
- const uint8_t* GetMappingTable() const {
- return GetOatPointer<const uint8_t*>(mapping_table_offset_);
- }
- const uint8_t* GetVmapTable() const {
- return GetOatPointer<const uint8_t*>(vmap_table_offset_);
- }
const uint8_t* GetNativeGcMap() const {
return GetOatPointer<const uint8_t*>(native_gc_map_offset_);
}
+ uint32_t GetMappingTableOffset() const;
+ uint32_t GetVmapTableOffset() const;
+ const uint8_t* GetMappingTable() const;
+ const uint8_t* GetVmapTable() const;
+
~OatMethod();
// Create an OatMethod with offsets relative to the given base address
@@ -140,8 +133,6 @@
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- const uint32_t mapping_table_offset,
- const uint32_t vmap_table_offset,
const uint32_t gc_map_offset);
private:
@@ -159,8 +150,6 @@
size_t frame_size_in_bytes_;
uint32_t core_spill_mask_;
uint32_t fp_spill_mask_;
- uint32_t mapping_table_offset_;
- uint32_t vmap_table_offset_;
uint32_t native_gc_map_offset_;
friend class OatClass;
diff --git a/runtime/catch_finder.cc b/runtime/quick_exception_handler.cc
similarity index 67%
rename from runtime/catch_finder.cc
rename to runtime/quick_exception_handler.cc
index f0293d7..d5844b6 100644
--- a/runtime/catch_finder.cc
+++ b/runtime/quick_exception_handler.cc
@@ -14,37 +14,37 @@
* limitations under the License.
*/
-#include "catch_finder.h"
+#include "quick_exception_handler.h"
+
#include "catch_block_stack_visitor.h"
+#include "deoptimize_stack_visitor.h"
+#include "entrypoints/entrypoint_utils.h"
+#include "sirt_ref-inl.h"
namespace art {
-CatchFinder::CatchFinder(Thread* self, const ThrowLocation& throw_location,
- mirror::Throwable* exception, bool is_deoptimization)
- : self_(self), context_(self->GetLongJumpContext()),
- exception_(exception), is_deoptimization_(is_deoptimization), throw_location_(throw_location),
+QuickExceptionHandler::QuickExceptionHandler(Thread* self, bool is_deoptimization)
+ : self_(self), context_(self->GetLongJumpContext()), is_deoptimization_(is_deoptimization),
method_tracing_active_(is_deoptimization ||
Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()),
- handler_quick_frame_(nullptr), handler_quick_frame_pc_(0),
- handler_dex_pc_(0), clear_exception_(false), top_shadow_frame_(nullptr),
- handler_frame_id_(kInvalidFrameId) {
- // Exception not in root sets, can't allow GC.
- last_no_assert_suspension_cause_ = self->StartAssertNoThreadSuspension("Finding catch block");
+ handler_quick_frame_(nullptr), handler_quick_frame_pc_(0), handler_dex_pc_(0),
+ clear_exception_(false), top_shadow_frame_(nullptr), handler_frame_id_(kInvalidFrameId) {
}
-void CatchFinder::FindCatch() {
+void QuickExceptionHandler::FindCatch(const ThrowLocation& throw_location,
+ mirror::Throwable* exception) {
+ DCHECK(!is_deoptimization_);
+ SirtRef<mirror::Throwable> exception_ref(self_, exception);
+
// Walk the stack to find catch handler or prepare for deoptimization.
- CatchBlockStackVisitor visitor(self_, context_, exception_, is_deoptimization_, this);
+ CatchBlockStackVisitor visitor(self_, context_, exception_ref, this);
visitor.WalkStack(true);
mirror::ArtMethod* catch_method = *handler_quick_frame_;
- if (catch_method == nullptr) {
- if (kDebugExceptionDelivery) {
+ if (kDebugExceptionDelivery) {
+ if (catch_method == nullptr) {
LOG(INFO) << "Handler is upcall";
- }
- } else {
- CHECK(!is_deoptimization_);
- if (kDebugExceptionDelivery) {
+ } else {
const DexFile& dex_file = *catch_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
int line_number = dex_file.GetLineNumFromPC(catch_method, handler_dex_pc_);
LOG(INFO) << "Handler: " << PrettyMethod(catch_method) << " (line: " << line_number << ")";
@@ -55,17 +55,23 @@
DCHECK(!self_->IsExceptionPending());
} else {
// Put exception back in root set with clear throw location.
- self_->SetException(ThrowLocation(), exception_);
+ self_->SetException(ThrowLocation(), exception_ref.get());
}
- self_->EndAssertNoThreadSuspension(last_no_assert_suspension_cause_);
- // Do instrumentation events after allowing thread suspension again.
- if (!is_deoptimization_) {
- // The debugger may suspend this thread and walk its stack. Let's do this before popping
- // instrumentation frames.
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
- instrumentation->ExceptionCaughtEvent(self_, throw_location_, catch_method, handler_dex_pc_,
- exception_);
- }
+ // The debugger may suspend this thread and walk its stack. Let's do this before popping
+ // instrumentation frames.
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ instrumentation->ExceptionCaughtEvent(self_, throw_location, catch_method, handler_dex_pc_,
+ exception_ref.get());
+}
+
+void QuickExceptionHandler::DeoptimizeStack() {
+ DCHECK(is_deoptimization_);
+
+ DeoptimizeStackVisitor visitor(self_, context_, this);
+ visitor.WalkStack(true);
+
+ // Restore deoptimization exception
+ self_->SetException(ThrowLocation(), Thread::GetDeoptimizationException());
}
// Unwinds all instrumentation stack frame prior to catch handler or upcall.
@@ -105,7 +111,7 @@
DISALLOW_COPY_AND_ASSIGN(InstrumentationStackVisitor);
};
-void CatchFinder::UpdateInstrumentationStack() {
+void QuickExceptionHandler::UpdateInstrumentationStack() {
if (method_tracing_active_) {
InstrumentationStackVisitor visitor(self_, is_deoptimization_, handler_frame_id_);
visitor.WalkStack(true);
@@ -118,7 +124,7 @@
}
}
-void CatchFinder::DoLongJump() {
+void QuickExceptionHandler::DoLongJump() {
if (is_deoptimization_) {
// TODO: proper return value.
self_->SetDeoptimizationShadowFrame(top_shadow_frame_);
diff --git a/runtime/catch_finder.h b/runtime/quick_exception_handler.h
similarity index 76%
rename from runtime/catch_finder.h
rename to runtime/quick_exception_handler.h
index ebbafe2..d06ce7c 100644
--- a/runtime/catch_finder.h
+++ b/runtime/quick_exception_handler.h
@@ -14,29 +14,39 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_CATCH_FINDER_H_
-#define ART_RUNTIME_CATCH_FINDER_H_
+#ifndef ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_
+#define ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_
-#include "mirror/art_method-inl.h"
-#include "thread.h"
+#include "base/logging.h"
+#include "base/mutex.h"
namespace art {
+namespace mirror {
+class ArtMethod;
+class Throwable;
+} // namespace mirror
+class Context;
+class Thread;
+class ThrowLocation;
+class ShadowFrame;
+
static constexpr bool kDebugExceptionDelivery = false;
static constexpr size_t kInvalidFrameId = 0xffffffff;
// Manages exception delivery for Quick backend. Not used by Portable backend.
-class CatchFinder {
+class QuickExceptionHandler {
public:
- CatchFinder(Thread* self, const ThrowLocation& throw_location, mirror::Throwable* exception,
- bool is_deoptimization)
+ QuickExceptionHandler(Thread* self, bool is_deoptimization)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ~CatchFinder() {
+ ~QuickExceptionHandler() {
LOG(FATAL) << "UNREACHABLE"; // Expected to take long jump.
}
- void FindCatch() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void FindCatch(const ThrowLocation& throw_location, mirror::Throwable* exception)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void DeoptimizeStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void UpdateInstrumentationStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void DoLongJump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -67,14 +77,9 @@
private:
Thread* const self_;
Context* const context_;
- mirror::Throwable* const exception_;
const bool is_deoptimization_;
- // Location of the throw.
- const ThrowLocation& throw_location_;
// Is method tracing active?
const bool method_tracing_active_;
- // Support for nesting no thread suspension checks.
- const char* last_no_assert_suspension_cause_;
// Quick frame with found handler or last frame if no handler found.
mirror::ArtMethod** handler_quick_frame_;
// PC to branch to for the handler.
@@ -88,8 +93,8 @@
// Frame id of the catch handler or the upcall.
size_t handler_frame_id_;
- DISALLOW_COPY_AND_ASSIGN(CatchFinder);
+ DISALLOW_COPY_AND_ASSIGN(QuickExceptionHandler);
};
} // namespace art
-#endif // ART_RUNTIME_CATCH_FINDER_H_
+#endif // ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index 404c616..7698d6a 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -38,23 +38,18 @@
Runtime* runtime = Runtime::Current();
CHECK(runtime == NULL || !runtime->IsStarted() || runtime->IsShuttingDown(self_));
} else {
- bool runnable_transition;
DCHECK_EQ(self, Thread::Current());
// Read state without locks, ok as state is effectively thread local and we're not interested
// in the suspend count (this will be handled in the runnable transitions).
old_thread_state_ = self->GetState();
- runnable_transition = old_thread_state_ == kRunnable || new_thread_state == kRunnable;
- if (!runnable_transition) {
- // A suspended transition to another effectively suspended transition, ok to use Unsafe.
- self_->SetState(new_thread_state);
- }
-
- if (runnable_transition && old_thread_state_ != new_thread_state) {
+ if (old_thread_state_ != new_thread_state) {
if (new_thread_state == kRunnable) {
self_->TransitionFromSuspendedToRunnable();
- } else {
- DCHECK_EQ(old_thread_state_, kRunnable);
+ } else if (old_thread_state_ == kRunnable) {
self_->TransitionFromRunnableToSuspended(new_thread_state);
+ } else {
+ // A suspended transition to another effectively suspended transition, ok to use Unsafe.
+ self_->SetState(new_thread_state);
}
}
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 998579d..771680b 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -32,7 +32,6 @@
#include "arch/context.h"
#include "base/mutex.h"
-#include "catch_finder.h"
#include "class_linker.h"
#include "class_linker-inl.h"
#include "cutils/atomic.h"
@@ -54,6 +53,7 @@
#include "mirror/stack_trace_element.h"
#include "monitor.h"
#include "object_utils.h"
+#include "quick_exception_handler.h"
#include "reflection.h"
#include "runtime.h"
#include "scoped_thread_state_change.h"
@@ -1018,7 +1018,8 @@
tls32_.state_and_flags.as_struct.flags = 0;
tls32_.state_and_flags.as_struct.state = kNative;
memset(&tlsPtr_.held_mutexes[0], 0, sizeof(tlsPtr_.held_mutexes));
- memset(tlsPtr_.rosalloc_runs, 0, sizeof(tlsPtr_.rosalloc_runs));
+ std::fill(tlsPtr_.rosalloc_runs, tlsPtr_.rosalloc_runs + kRosAllocNumOfSizeBrackets,
+ gc::allocator::RosAlloc::GetDedicatedFullRun());
for (uint32_t i = 0; i < kMaxCheckpoints; ++i) {
tlsPtr_.checkpoint_functions[i] = nullptr;
}
@@ -1841,7 +1842,7 @@
// Don't leave exception visible while we try to find the handler, which may cause class
// resolution.
ClearException();
- bool is_deoptimization = (exception == reinterpret_cast<mirror::Throwable*>(-1));
+ bool is_deoptimization = (exception == GetDeoptimizationException());
if (kDebugExceptionDelivery) {
if (!is_deoptimization) {
mirror::String* msg = exception->GetDetailMessage();
@@ -1852,10 +1853,14 @@
DumpStack(LOG(INFO) << "Deoptimizing: ");
}
}
- CatchFinder catch_finder(this, throw_location, exception, is_deoptimization);
- catch_finder.FindCatch();
- catch_finder.UpdateInstrumentationStack();
- catch_finder.DoLongJump();
+ QuickExceptionHandler exception_handler(this, is_deoptimization);
+ if (is_deoptimization) {
+ exception_handler.DeoptimizeStack();
+ } else {
+ exception_handler.FindCatch(throw_location, exception);
+ }
+ exception_handler.UpdateInstrumentationStack();
+ exception_handler.DoLongJump();
LOG(FATAL) << "UNREACHABLE";
}
@@ -2060,7 +2065,7 @@
if (tlsPtr_.opeer != nullptr) {
visitor(&tlsPtr_.opeer, arg, thread_id, kRootThreadObject);
}
- if (tlsPtr_.exception != nullptr) {
+ if (tlsPtr_.exception != nullptr && tlsPtr_.exception != GetDeoptimizationException()) {
visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg, thread_id, kRootNativeStack);
}
tlsPtr_.throw_location.VisitRoots(visitor, arg);
diff --git a/runtime/thread.h b/runtime/thread.h
index d25bbe9..e5e4cae 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -686,6 +686,11 @@
return tlsPtr_.single_step_control;
}
+ // Returns the fake exception used to activate deoptimization.
+ static mirror::Throwable* GetDeoptimizationException() {
+ return reinterpret_cast<mirror::Throwable*>(-1);
+ }
+
void SetDeoptimizationShadowFrame(ShadowFrame* sf);
void SetDeoptimizationReturnValue(const JValue& ret_val);
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 535c76d..91170f0 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -1176,8 +1176,11 @@
// it's effectively considered initialized the instant we reach here (in the sense that we
// can return without doing anything or call virtual methods).
{
- const RegType& reg_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor,
- false);
+ const RegType& reg_type = ResolveClassAndCheckAccess(iterator.GetTypeIdx());
+ if (!reg_type.IsNonZeroReferenceTypes()) {
+ DCHECK(HasFailures());
+ return false;
+ }
reg_line->SetRegisterType(arg_start + cur_arg, reg_type);
}
break;
@@ -2865,11 +2868,7 @@
common_super = ®_types_.JavaLangThrowable(false);
} else {
const RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex());
- if (common_super == NULL) {
- // Unconditionally assign for the first handler. We don't assert this is a Throwable
- // as that is caught at runtime
- common_super = &exception;
- } else if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception)) {
+ if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception)) {
if (exception.IsUnresolvedTypes()) {
// We don't know enough about the type. Fail here and let runtime handle it.
Fail(VERIFY_ERROR_NO_CLASS) << "unresolved exception class " << exception;
@@ -2878,6 +2877,8 @@
Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception;
return reg_types_.Conflict();
}
+ } else if (common_super == nullptr) {
+ common_super = &exception;
} else if (common_super->Equals(exception)) {
// odd case, but nothing to do
} else {
@@ -3596,29 +3597,6 @@
}
}
-// Look for an instance field with this offset.
-// TODO: we may speed up the search if offsets are sorted by doing a quick search.
-static mirror::ArtField* FindInstanceFieldWithOffset(mirror::Class* klass, uint32_t field_offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ObjectArray<mirror::ArtField>* instance_fields = klass->GetIFields();
- if (instance_fields != NULL) {
- for (int32_t i = 0, e = instance_fields->GetLength(); i < e; ++i) {
- mirror::ArtField* field = instance_fields->Get(i);
- if (field->GetOffset().Uint32Value() == field_offset) {
- return field;
- }
- }
- }
- // We did not find field in class: look into superclass.
- if (klass->GetSuperClass() != NULL) {
- return FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset);
- } else {
- VLOG(verifier) << "Failed to find instance field at offset '" << field_offset
- << "' from '" << PrettyDescriptor(klass) << "'";
- return nullptr;
- }
-}
-
mirror::ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst,
RegisterLine* reg_line) {
DCHECK(inst->Opcode() == Instruction::IGET_QUICK ||
@@ -3633,7 +3611,13 @@
return nullptr;
}
uint32_t field_offset = static_cast<uint32_t>(inst->VRegC_22c());
- return FindInstanceFieldWithOffset(object_type.GetClass(), field_offset);
+ mirror::ArtField* f = mirror::ArtField::FindInstanceFieldWithOffset(object_type.GetClass(),
+ field_offset);
+ if (f == nullptr) {
+ VLOG(verifier) << "Failed to find instance field at offset '" << field_offset
+ << "' from '" << PrettyDescriptor(object_type.GetClass()) << "'";
+ }
+ return f;
}
void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& insn_type,
diff --git a/test/046-reflect/expected.txt b/test/046-reflect/expected.txt
index 55b0dbe..ecb3599 100644
--- a/test/046-reflect/expected.txt
+++ b/test/046-reflect/expected.txt
@@ -92,6 +92,8 @@
Target constructor (IF)V : ii=7 ff=3.3333
myMethod (I)I
arg=17 anInt=7
+got expected exception for Class.newInstance
+got expected exception for Constructor.newInstance
ReflectTest done!
public method
static java.lang.Object java.util.Collections.checkType(java.lang.Object,java.lang.Class) accessible=false
diff --git a/test/046-reflect/src/Main.java b/test/046-reflect/src/Main.java
index d60fcb4..3e6d700 100644
--- a/test/046-reflect/src/Main.java
+++ b/test/046-reflect/src/Main.java
@@ -362,6 +362,27 @@
targ = cons.newInstance(args);
targ.myMethod(17);
+ try {
+ Thrower thrower = Thrower.class.newInstance();
+ System.out.println("ERROR: Class.newInstance did not throw exception");
+ } catch (UnsupportedOperationException uoe) {
+ System.out.println("got expected exception for Class.newInstance");
+ } catch (Exception e) {
+ System.out.println("ERROR: Class.newInstance got unexpected exception: " +
+ e.getClass().getName());
+ }
+
+ try {
+ Constructor<Thrower> constructor = Thrower.class.getDeclaredConstructor();
+ Thrower thrower = constructor.newInstance();
+ System.out.println("ERROR: Constructor.newInstance did not throw exception");
+ } catch (InvocationTargetException ite) {
+ System.out.println("got expected exception for Constructor.newInstance");
+ } catch (Exception e) {
+ System.out.println("ERROR: Constructor.newInstance got unexpected exception: " +
+ e.getClass().getName());
+ }
+
} catch (Exception ex) {
System.out.println("----- unexpected exception -----");
ex.printStackTrace();
@@ -669,3 +690,9 @@
public static void staticMethod() {}
public void createMethodNoisyInit(MethodNoisyInit ni) {}
}
+
+class Thrower {
+ public Thrower() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/test/083-compiler-regressions/expected.txt b/test/083-compiler-regressions/expected.txt
index db50300..7576b02 100644
--- a/test/083-compiler-regressions/expected.txt
+++ b/test/083-compiler-regressions/expected.txt
@@ -16,6 +16,7 @@
largeFrame passes
largeFrameFloat passes
mulBy1Test passes
+constantPropagationTest passes
getterSetterTest passes
identityTest passes
wideGetterSetterTest passes
diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java
index d32c037..6a12ca9 100644
--- a/test/083-compiler-regressions/src/Main.java
+++ b/test/083-compiler-regressions/src/Main.java
@@ -38,6 +38,7 @@
largeFrameTest();
largeFrameTestFloat();
mulBy1Test();
+ constantPropagationTest();
getterSetterTest();
identityTest();
wideGetterSetterTest();
@@ -766,6 +767,32 @@
}
}
+ static void constantPropagationTest() {
+ int i = 1;
+ int t = 1;
+ float z = 1F;
+ long h = 1L;
+ int g[] = new int[1];
+ int w = 1;
+ long f = 0;
+
+ for (int a = 1; a < 100; a++) {
+ try {
+ i = (int)(z);
+ h >>= (0 % t);
+ }
+ finally {
+ w = (int)(2 * (f * 6));
+ }
+ }
+
+ if (w == 0 && h == 1 && g[0] == 0) {
+ System.out.println("constantPropagationTest passes");
+ } else {
+ System.out.println("constantPropagationTest fails");
+ }
+ }
+
static void b2296099Test() throws Exception {
int x = -1190771042;
int dist = 360530809;