Merge "Optimizing: Tag basic block allocations with their source."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c88d677..326a92b 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -204,6 +204,8 @@
runtime/interpreter/unstarted_runtime_test.cc \
runtime/java_vm_ext_test.cc \
runtime/jit/jit_code_cache_test.cc \
+ runtime/lambda/closure_test.cc \
+ runtime/lambda/shorty_field_type_test.cc \
runtime/leb128_test.cc \
runtime/mem_map_test.cc \
runtime/memory_region_test.cc \
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 981ab2c..868d9a4 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -547,27 +547,28 @@
cfi_.RestoreMany(DwarfFpReg(0), fp_spill_mask_);
}
bool unspill_LR_to_PC = (core_spill_mask_ & (1 << rs_rARM_LR.GetRegNum())) != 0;
+ uint32_t core_unspill_mask = core_spill_mask_;
if (unspill_LR_to_PC) {
- core_spill_mask_ &= ~(1 << rs_rARM_LR.GetRegNum());
- core_spill_mask_ |= (1 << rs_rARM_PC.GetRegNum());
+ core_unspill_mask &= ~(1 << rs_rARM_LR.GetRegNum());
+ core_unspill_mask |= (1 << rs_rARM_PC.GetRegNum());
}
- if (core_spill_mask_ != 0u) {
- if ((core_spill_mask_ & ~(0xffu | (1u << rs_rARM_PC.GetRegNum()))) == 0u) {
+ if (core_unspill_mask != 0u) {
+ if ((core_unspill_mask & ~(0xffu | (1u << rs_rARM_PC.GetRegNum()))) == 0u) {
// Unspilling only low regs and/or PC, use 16-bit POP.
constexpr int pc_bit_shift = rs_rARM_PC.GetRegNum() - 8;
NewLIR1(kThumbPop,
- (core_spill_mask_ & ~(1u << rs_rARM_PC.GetRegNum())) |
- ((core_spill_mask_ & (1u << rs_rARM_PC.GetRegNum())) >> pc_bit_shift));
- } else if (IsPowerOfTwo(core_spill_mask_)) {
+ (core_unspill_mask & ~(1u << rs_rARM_PC.GetRegNum())) |
+ ((core_unspill_mask & (1u << rs_rARM_PC.GetRegNum())) >> pc_bit_shift));
+ } else if (IsPowerOfTwo(core_unspill_mask)) {
// kThumb2Pop cannot be used to unspill a single register.
- NewLIR1(kThumb2Pop1, CTZ(core_spill_mask_));
+ NewLIR1(kThumb2Pop1, CTZ(core_unspill_mask));
} else {
- NewLIR1(kThumb2Pop, core_spill_mask_);
+ NewLIR1(kThumb2Pop, core_unspill_mask);
}
// If we pop to PC, there is no further epilogue code.
if (!unspill_LR_to_PC) {
cfi_.AdjustCFAOffset(-num_core_spills_ * kArmPointerSize);
- cfi_.RestoreMany(DwarfCoreReg(0), core_spill_mask_);
+ cfi_.RestoreMany(DwarfCoreReg(0), core_unspill_mask);
DCHECK_EQ(cfi_.GetCurrentCFAOffset(), 0); // empty stack.
}
}
@@ -677,10 +678,11 @@
FALLTHROUGH_INTENDED;
case 1: // Get method->dex_cache_resolved_methods_
if (!use_pc_rel) {
- cg->LoadRefDisp(arg0_ref,
- ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
- arg0_ref,
- kNotVolatile);
+ cg->LoadBaseDisp(arg0_ref,
+ ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value(),
+ arg0_ref,
+ k32,
+ kNotVolatile);
}
// Set up direct code if known.
if (direct_code != 0) {
@@ -702,8 +704,8 @@
CHECK_EQ(cu->dex_file, target_method.dex_file);
if (!use_pc_rel) {
cg->LoadRefDisp(arg0_ref,
- mirror::ObjectArray<mirror::Object>::OffsetOfElement(
- target_method.dex_method_index).Int32Value(),
+ cg->GetCachePointerOffset(target_method.dex_method_index,
+ kArmPointerSize),
arg0_ref,
kNotVolatile);
} else {
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 83a6aff..036da2e 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -511,10 +511,11 @@
FALLTHROUGH_INTENDED;
case 1: // Get method->dex_cache_resolved_methods_
if (!use_pc_rel) {
- cg->LoadRefDisp(arg0_ref,
- ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
- arg0_ref,
- kNotVolatile);
+ cg->LoadBaseDisp(arg0_ref,
+ ArtMethod::DexCacheResolvedMethodsOffset(kArm64PointerSize).Int32Value(),
+ arg0_ref,
+ k64,
+ kNotVolatile);
}
// Set up direct code if known.
if (direct_code != 0) {
@@ -536,8 +537,9 @@
CHECK_EQ(cu->dex_file, target_method.dex_file);
if (!use_pc_rel) {
cg->LoadWordDisp(arg0_ref,
- mirror::Array::DataOffset(kArm64PointerSize).Uint32Value() +
- target_method.dex_method_index * kArm64PointerSize, arg0_ref);
+ cg->GetCachePointerOffset(target_method.dex_method_index,
+ kArm64PointerSize),
+ arg0_ref);
} else {
size_t offset = cg->dex_cache_arrays_layout_.MethodOffset(target_method.dex_method_index);
cg->OpPcRelDexCacheArrayLoad(cu->dex_file, offset, arg0_ref, true);
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 42b792c..af93aab 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -39,6 +39,9 @@
true, // kIntrinsicReverseBits
true, // kIntrinsicReverseBytes
true, // kIntrinsicNumberOfLeadingZeros
+ true, // kIntrinsicNumberOfTrailingZeros
+ true, // kIntrinsicRotateRight
+ true, // kIntrinsicRotateLeft
true, // kIntrinsicAbsInt
true, // kIntrinsicAbsLong
true, // kIntrinsicAbsFloat
@@ -79,6 +82,10 @@
static_assert(kIntrinsicIsStatic[kIntrinsicReverseBytes], "ReverseBytes must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicNumberOfLeadingZeros],
"NumberOfLeadingZeros must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicNumberOfTrailingZeros],
+ "NumberOfTrailingZeros must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicRotateRight], "RotateRight must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicRotateLeft], "RotateLeft must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicAbsInt], "AbsInt must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicAbsLong], "AbsLong must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicAbsFloat], "AbsFloat must be static");
@@ -232,6 +239,9 @@
"putOrderedObject", // kNameCachePutOrderedObject
"arraycopy", // kNameCacheArrayCopy
"numberOfLeadingZeros", // kNameCacheNumberOfLeadingZeros
+ "numberOfTrailingZeros", // kNameCacheNumberOfTrailingZeros
+ "rotateRight", // kNameCacheRotateRight
+ "rotateLeft", // kNameCacheRotateLeft
};
const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = {
@@ -289,6 +299,8 @@
{ kClassCacheVoid, 2, { kClassCacheLong, kClassCacheShort } },
// kProtoCacheObject_Z
{ kClassCacheBoolean, 1, { kClassCacheJavaLangObject } },
+ // kProtoCacheJI_J
+ { kClassCacheLong, 2, { kClassCacheLong, kClassCacheInt } },
// kProtoCacheObjectJII_Z
{ kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong,
kClassCacheInt, kClassCacheInt } },
@@ -379,6 +391,8 @@
INTRINSIC(JavaLangInteger, NumberOfLeadingZeros, I_I, kIntrinsicNumberOfLeadingZeros, k32),
INTRINSIC(JavaLangLong, NumberOfLeadingZeros, J_I, kIntrinsicNumberOfLeadingZeros, k64),
+ INTRINSIC(JavaLangInteger, NumberOfTrailingZeros, I_I, kIntrinsicNumberOfTrailingZeros, k32),
+ INTRINSIC(JavaLangLong, NumberOfTrailingZeros, J_I, kIntrinsicNumberOfTrailingZeros, k64),
INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0),
INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0),
@@ -468,6 +482,11 @@
INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray,
0),
+ INTRINSIC(JavaLangInteger, RotateRight, II_I, kIntrinsicRotateRight, k32),
+ INTRINSIC(JavaLangLong, RotateRight, JI_J, kIntrinsicRotateRight, k64),
+ INTRINSIC(JavaLangInteger, RotateLeft, II_I, kIntrinsicRotateLeft, k32),
+ INTRINSIC(JavaLangLong, RotateLeft, JI_J, kIntrinsicRotateLeft, k64),
+
#undef INTRINSIC
#define SPECIAL(c, n, p, o, d) \
@@ -631,7 +650,10 @@
case kIntrinsicSystemArrayCopyCharArray:
return backend->GenInlinedArrayCopyCharArray(info);
case kIntrinsicNumberOfLeadingZeros:
- return false; // not implemented in quick
+ case kIntrinsicNumberOfTrailingZeros:
+ case kIntrinsicRotateRight:
+ case kIntrinsicRotateLeft:
+ return false; // not implemented in quick.
default:
LOG(FATAL) << "Unexpected intrinsic opcode: " << intrinsic.opcode;
return false; // avoid warning "control reaches end of non-void function"
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index d6c8bfb..8458806 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -208,6 +208,9 @@
kNameCachePutOrderedObject,
kNameCacheArrayCopy,
kNameCacheNumberOfLeadingZeros,
+ kNameCacheNumberOfTrailingZeros,
+ kNameCacheRotateRight,
+ kNameCacheRotateLeft,
kNameCacheLast
};
@@ -245,6 +248,7 @@
kProtoCacheJJ_V,
kProtoCacheJS_V,
kProtoCacheObject_Z,
+ kProtoCacheJI_J,
kProtoCacheObjectJII_Z,
kProtoCacheObjectJJJ_Z,
kProtoCacheObjectJObjectObject_Z,
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index af10817..2a1d644 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -88,24 +88,30 @@
r_result));
}
+void Mir2Lir::LoadTypeFromCache(uint32_t type_index, RegStorage class_reg) {
+ if (CanUseOpPcRelDexCacheArrayLoad()) {
+ uint32_t offset = dex_cache_arrays_layout_.TypeOffset(type_index);
+ OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, class_reg, false);
+ } else {
+ RegStorage r_method = LoadCurrMethodWithHint(class_reg);
+ MemberOffset resolved_types_offset = ArtMethod::DexCacheResolvedTypesOffset(
+ GetInstructionSetPointerSize(cu_->instruction_set));
+ LoadBaseDisp(r_method, resolved_types_offset.Int32Value(), class_reg,
+ cu_->target64 ? k64 : k32, kNotVolatile);
+ int32_t offset_of_type = GetCacheOffset(type_index);
+ LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
+ }
+}
+
RegStorage Mir2Lir::GenGetOtherTypeForSgetSput(const MirSFieldLoweringInfo& field_info,
int opt_flags) {
DCHECK_NE(field_info.StorageIndex(), DexFile::kDexNoIndex);
// May do runtime call so everything to home locations.
FlushAllRegs();
+ // Using fixed register to sync with possible call to runtime support.
RegStorage r_base = TargetReg(kArg0, kRef);
LockTemp(r_base);
- if (CanUseOpPcRelDexCacheArrayLoad()) {
- uint32_t offset = dex_cache_arrays_layout_.TypeOffset(field_info.StorageIndex());
- OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, r_base, false);
- } else {
- // Using fixed register to sync with possible call to runtime support.
- RegStorage r_method = LoadCurrMethodWithHint(r_base);
- LoadRefDisp(r_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value(), r_base,
- kNotVolatile);
- int32_t offset_of_field = ObjArray::OffsetOfElement(field_info.StorageIndex()).Int32Value();
- LoadRefDisp(r_base, offset_of_field, r_base, kNotVolatile);
- }
+ LoadTypeFromCache(field_info.StorageIndex(), r_base);
// r_base now points at static storage (Class*) or null if the type is not yet resolved.
LIR* unresolved_branch = nullptr;
if (!field_info.IsClassInDexCache() && (opt_flags & MIR_CLASS_IS_IN_DEX_CACHE) == 0) {
@@ -1029,19 +1035,7 @@
} else {
rl_result = EvalLoc(rl_dest, kRefReg, true);
// We don't need access checks, load type from dex cache
- if (CanUseOpPcRelDexCacheArrayLoad()) {
- size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
- OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, rl_result.reg, false);
- } else {
- int32_t dex_cache_offset =
- ArtMethod::DexCacheResolvedTypesOffset().Int32Value();
- RegStorage res_reg = AllocTempRef();
- RegStorage r_method = LoadCurrMethodWithHint(res_reg);
- LoadRefDisp(r_method, dex_cache_offset, res_reg, kNotVolatile);
- int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
- LoadRefDisp(res_reg, offset_of_type, rl_result.reg, kNotVolatile);
- FreeTemp(res_reg);
- }
+ LoadTypeFromCache(type_idx, rl_result.reg);
if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file,
type_idx) || ForceSlowTypePath(cu_)) {
// Slow path, at runtime test if type is null and if so initialize
@@ -1054,8 +1048,7 @@
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::ObjectArray<mirror::String>::OffsetOfElement(string_idx).
- Int32Value();
+ int32_t offset_of_string = GetCacheOffset(string_idx);
if (!cu_->compiler_driver->CanAssumeStringIsPresentInDexCache(
*cu_->dex_file, string_idx) || ForceSlowStringPath(cu_)) {
// slow path, resolve string if not in dex cache
@@ -1073,7 +1066,8 @@
RegStorage r_method = LoadCurrMethodWithHint(arg0);
LoadRefDisp(r_method, ArtMethod::DeclaringClassOffset().Int32Value(), arg0, kNotVolatile);
// Declaring class to dex cache strings.
- LoadRefDisp(arg0, mirror::Class::DexCacheStringsOffset().Int32Value(), arg0, kNotVolatile);
+ LoadBaseDisp(arg0, mirror::Class::DexCacheStringsOffset().Int32Value(), arg0,
+ cu_->target64 ? k64 : k32, kNotVolatile);
LoadRefDisp(arg0, offset_of_string, ret0, kNotVolatile);
}
@@ -1091,8 +1085,8 @@
RegStorage res_reg = AllocTempRef();
LoadRefDisp(rl_method.reg, ArtMethod::DeclaringClassOffset().Int32Value(), res_reg,
kNotVolatile);
- LoadRefDisp(res_reg, mirror::Class::DexCacheStringsOffset().Int32Value(), res_reg,
- kNotVolatile);
+ LoadBaseDisp(res_reg, mirror::Class::DexCacheStringsOffset().Int32Value(), res_reg,
+ cu_->target64 ? k64 : k32, kNotVolatile);
LoadRefDisp(res_reg, offset_of_string, rl_result.reg, kNotVolatile);
FreeTemp(res_reg);
}
@@ -1176,19 +1170,10 @@
kNotVolatile);
LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class,
kNotVolatile);
- } else if (CanUseOpPcRelDexCacheArrayLoad()) {
- size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
- OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, check_class, false);
- LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class,
- kNotVolatile);
} else {
- RegStorage r_method = LoadCurrMethodWithHint(check_class);
- LoadRefDisp(r_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
- check_class, kNotVolatile);
+ LoadTypeFromCache(type_idx, check_class);
LoadRefDisp(object.reg, mirror::Object::ClassOffset().Int32Value(), object_class,
kNotVolatile);
- int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
- LoadRefDisp(check_class, offset_of_type, check_class, kNotVolatile);
}
// FIXME: what should we be comparing here? compressed or decompressed references?
@@ -1239,17 +1224,8 @@
LoadValueDirectFixed(rl_src, ref_reg); // kArg0 <= ref
}
- if (CanUseOpPcRelDexCacheArrayLoad()) {
- size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
- OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, class_reg, false);
- } else {
- RegStorage r_method = LoadCurrMethodWithHint(class_reg);
- // Load dex cache entry into class_reg (kArg2)
- LoadRefDisp(r_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
- class_reg, kNotVolatile);
- int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
- LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
- }
+ // Load dex cache entry into class_reg (kArg2)
+ LoadTypeFromCache(type_idx, class_reg);
if (!can_assume_type_is_in_dex_cache) {
GenIfNullUseHelperImm(class_reg, kQuickInitializeType, type_idx);
@@ -1370,17 +1346,7 @@
class_reg, kNotVolatile);
} else {
// Load dex cache entry into class_reg (kArg2)
- if (CanUseOpPcRelDexCacheArrayLoad()) {
- size_t offset = dex_cache_arrays_layout_.TypeOffset(type_idx);
- OpPcRelDexCacheArrayLoad(cu_->dex_file, offset, class_reg, false);
- } else {
- RegStorage r_method = LoadCurrMethodWithHint(class_reg);
-
- LoadRefDisp(r_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
- class_reg, kNotVolatile);
- int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
- LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
- }
+ LoadTypeFromCache(type_idx, class_reg);
if (!cu_->compiler_driver->CanAssumeTypeIsPresentInDexCache(*cu_->dex_file, type_idx)) {
// Need to test presence of type in dex cache at runtime
GenIfNullUseHelperImm(class_reg, kQuickInitializeType, type_idx);
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index 853980d..8863c05 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -415,10 +415,11 @@
* Bit of a hack here - in the absence of a real scheduling pass,
* emit the next instruction in static & direct invoke sequences.
*/
-static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, int state,
- const MethodReference& target_method, uint32_t, uintptr_t direct_code,
- uintptr_t direct_method, InvokeType type) {
- Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+int MipsMir2Lir::MipsNextSDCallInsn(CompilationUnit* cu, CallInfo* info, int state,
+ const MethodReference& target_method, uint32_t,
+ uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type) {
+ MipsMir2Lir* cg = static_cast<MipsMir2Lir*>(cu->cg.get());
if (info->string_init_offset != 0) {
RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
switch (state) {
@@ -469,10 +470,12 @@
cg->LoadCurrMethodDirect(arg0_ref);
break;
case 1: // Get method->dex_cache_resolved_methods_
- cg->LoadRefDisp(arg0_ref,
- ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
- arg0_ref,
- kNotVolatile);
+ cg->LoadBaseDisp(arg0_ref,
+ ArtMethod::DexCacheResolvedMethodsOffset(
+ cu->target64 ? kMips64PointerSize : kMipsPointerSize).Int32Value(),
+ arg0_ref,
+ cu->target64 ? k64 : k32,
+ kNotVolatile);
// Set up direct code if known.
if (direct_code != 0) {
if (direct_code != static_cast<uintptr_t>(-1)) {
@@ -492,8 +495,9 @@
CHECK_EQ(cu->dex_file, target_method.dex_file);
const size_t pointer_size = GetInstructionSetPointerSize(cu->instruction_set);
cg->LoadWordDisp(arg0_ref,
- mirror::Array::DataOffset(pointer_size).Uint32Value() +
- target_method.dex_method_index * pointer_size, arg0_ref);
+ cg->GetCachePointerOffset(target_method.dex_method_index,
+ pointer_size),
+ arg0_ref);
break;
}
case 3: // Grab the code from the method*
@@ -512,7 +516,7 @@
}
NextCallInsn MipsMir2Lir::GetNextSDCallInsn() {
- return NextSDCallInsn;
+ return MipsNextSDCallInsn;
}
LIR* MipsMir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info ATTRIBUTE_UNUSED) {
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 2173253..378b9a0 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -269,6 +269,11 @@
const bool fpuIs32Bit_;
private:
+ static int MipsNextSDCallInsn(CompilationUnit* cu, CallInfo* info, int state,
+ const MethodReference& target_method, uint32_t,
+ uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type);
+
void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h
index 767fe25..f96816c 100644
--- a/compiler/dex/quick/mir_to_lir-inl.h
+++ b/compiler/dex/quick/mir_to_lir-inl.h
@@ -21,6 +21,7 @@
#include "base/logging.h"
#include "dex/compiler_ir.h"
+#include "gc_root.h"
#include "utils.h"
namespace art {
@@ -278,6 +279,14 @@
}
}
+inline size_t Mir2Lir::GetCacheOffset(uint32_t index) {
+ return sizeof(GcRoot<mirror::Object>) * index;
+}
+
+inline size_t Mir2Lir::GetCachePointerOffset(uint32_t index, size_t pointer_size) {
+ return pointer_size * index;
+}
+
inline Mir2Lir::ShortyIterator::ShortyIterator(const char* shorty, bool is_static)
: cur_(shorty + 1), pending_this_(!is_static), initialized_(false) {
DCHECK(shorty != nullptr);
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 73787e9..4e3aab2 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -1771,6 +1771,11 @@
return (core_spill_mask_ & (1u << reg)) != 0;
}
+ size_t GetCacheOffset(uint32_t index);
+ size_t GetCachePointerOffset(uint32_t index, size_t pointer_size);
+
+ void LoadTypeFromCache(uint32_t type_index, RegStorage class_reg);
+
public:
// TODO: add accessors for these.
LIR* literal_list_; // Constants.
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 43167a1..9cb45a4 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -394,18 +394,19 @@
cg->LoadCurrMethodDirect(arg0_ref);
break;
case 1: // Get method->dex_cache_resolved_methods_
- cg->LoadRefDisp(arg0_ref,
- ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
- arg0_ref,
- kNotVolatile);
+ cg->LoadBaseDisp(arg0_ref,
+ ArtMethod::DexCacheResolvedMethodsOffset(
+ cu->target64 ? kX86_64PointerSize : kX86PointerSize).Int32Value(),
+ arg0_ref,
+ cu->target64 ? k64 : k32,
+ kNotVolatile);
break;
case 2: {
// Grab target method*
CHECK_EQ(cu->dex_file, target_method.dex_file);
const size_t pointer_size = GetInstructionSetPointerSize(cu->instruction_set);
cg->LoadWordDisp(arg0_ref,
- mirror::Array::DataOffset(pointer_size).Uint32Value() +
- target_method.dex_method_index * pointer_size,
+ cg->GetCachePointerOffset(target_method.dex_method_index, pointer_size),
arg0_ref);
break;
}
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index d1fe167..ecd23e9 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -3031,31 +3031,12 @@
// The LoadRefDisp(s) below will work normally, even in 64 bit mode.
RegStorage check_class = AllocTemp();
- // If Method* is already in a register, we can save a copy.
- RegLocation rl_method = mir_graph_->GetMethodLoc();
- int32_t offset_of_type = mirror::Array::DataOffset(
- sizeof(mirror::HeapReference<mirror::Class*>)).Int32Value() +
- (sizeof(mirror::HeapReference<mirror::Class*>) * type_idx);
-
- if (rl_method.location == kLocPhysReg) {
- if (use_declaring_class) {
- LoadRefDisp(rl_method.reg, ArtMethod::DeclaringClassOffset().Int32Value(),
- check_class, kNotVolatile);
- } else {
- LoadRefDisp(rl_method.reg, ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
- check_class, kNotVolatile);
- LoadRefDisp(check_class, offset_of_type, check_class, kNotVolatile);
- }
+ if (use_declaring_class) {
+ RegStorage r_method = LoadCurrMethodWithHint(check_class);
+ LoadRefDisp(r_method, ArtMethod::DeclaringClassOffset().Int32Value(),
+ check_class, kNotVolatile);
} else {
- LoadCurrMethodDirect(check_class);
- if (use_declaring_class) {
- LoadRefDisp(check_class, ArtMethod::DeclaringClassOffset().Int32Value(),
- check_class, kNotVolatile);
- } else {
- LoadRefDisp(check_class, ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
- check_class, kNotVolatile);
- LoadRefDisp(check_class, offset_of_type, check_class, kNotVolatile);
- }
+ LoadTypeFromCache(type_idx, check_class);
}
// Compare the computed class to the class in the object.
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 9ee0a36..4c1408a 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -843,16 +843,17 @@
virtual bool Visit(mirror::Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (auto& m : c->GetVirtualMethods(pointer_size)) {
- ResolveExceptionsForMethod(&m);
+ ResolveExceptionsForMethod(&m, pointer_size);
}
for (auto& m : c->GetDirectMethods(pointer_size)) {
- ResolveExceptionsForMethod(&m);
+ ResolveExceptionsForMethod(&m, pointer_size);
}
return true;
}
private:
- void ResolveExceptionsForMethod(ArtMethod* method_handle) SHARED_REQUIRES(Locks::mutator_lock_) {
+ void ResolveExceptionsForMethod(ArtMethod* method_handle, size_t pointer_size)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
if (code_item == nullptr) {
return; // native or abstract method
@@ -873,7 +874,8 @@
uint16_t encoded_catch_handler_handlers_type_idx =
DecodeUnsignedLeb128(&encoded_catch_handler_list);
// Add to set of types to resolve if not already in the dex cache resolved types
- if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
+ if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx,
+ pointer_size)) {
exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx,
method_handle->GetDexFile());
}
@@ -1138,23 +1140,20 @@
if (IsImage()) {
TimingLogger::ScopedTiming t("UpdateImageClasses", timings);
- Runtime* current = Runtime::Current();
+ Runtime* runtime = Runtime::Current();
// Suspend all threads.
- current->GetThreadList()->SuspendAll(__FUNCTION__);
+ ScopedSuspendAll ssa(__FUNCTION__);
std::string error_msg;
std::unique_ptr<ClinitImageUpdate> update(ClinitImageUpdate::Create(image_classes_.get(),
Thread::Current(),
- current->GetClassLinker(),
+ runtime->GetClassLinker(),
&error_msg));
CHECK(update.get() != nullptr) << error_msg; // TODO: Soft failure?
// Do the marking.
update->Walk();
-
- // Resume threads.
- current->GetThreadList()->ResumeAll();
}
}
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index f318921..955c575 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -308,32 +308,29 @@
dex_cache_array_starts_.Put(dex_file, size);
DexCacheArraysLayout layout(target_ptr_size_, dex_file);
DCHECK(layout.Valid());
- auto types_size = layout.TypesSize(dex_file->NumTypeIds());
- auto methods_size = layout.MethodsSize(dex_file->NumMethodIds());
- auto fields_size = layout.FieldsSize(dex_file->NumFieldIds());
- auto strings_size = layout.StringsSize(dex_file->NumStringIds());
- dex_cache_array_indexes_.Put(
- dex_cache->GetResolvedTypes(),
- DexCacheArrayLocation {size + layout.TypesOffset(), types_size, kBinRegular});
- dex_cache_array_indexes_.Put(
- dex_cache->GetResolvedMethods(),
- DexCacheArrayLocation {size + layout.MethodsOffset(), methods_size, kBinArtMethodClean});
- AddMethodPointerArray(dex_cache->GetResolvedMethods());
- dex_cache_array_indexes_.Put(
- dex_cache->GetResolvedFields(),
- DexCacheArrayLocation {size + layout.FieldsOffset(), fields_size, kBinArtField});
- pointer_arrays_.emplace(dex_cache->GetResolvedFields(), kBinArtField);
- dex_cache_array_indexes_.Put(
- dex_cache->GetStrings(),
- DexCacheArrayLocation {size + layout.StringsOffset(), strings_size, kBinRegular});
+ DCHECK_EQ(dex_file->NumTypeIds() != 0u, dex_cache->GetResolvedTypes() != nullptr);
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedTypes(), size + layout.TypesOffset());
+ DCHECK_EQ(dex_file->NumMethodIds() != 0u, dex_cache->GetResolvedMethods() != nullptr);
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedMethods(), size + layout.MethodsOffset());
+ DCHECK_EQ(dex_file->NumFieldIds() != 0u, dex_cache->GetResolvedFields() != nullptr);
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedFields(), size + layout.FieldsOffset());
+ DCHECK_EQ(dex_file->NumStringIds() != 0u, dex_cache->GetStrings() != nullptr);
+ AddDexCacheArrayRelocation(dex_cache->GetStrings(), size + layout.StringsOffset());
size += layout.Size();
- CHECK_EQ(layout.Size(), types_size + methods_size + fields_size + strings_size);
}
// Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned()
// when AssignImageBinSlot() assigns their indexes out or order.
bin_slot_sizes_[kBinDexCacheArray] = size;
}
+void ImageWriter::AddDexCacheArrayRelocation(void* array, size_t offset) {
+ if (array != nullptr) {
+ native_object_relocations_.emplace(
+ array,
+ NativeObjectRelocation { offset, kNativeObjectRelocationTypeDexCacheArray });
+ }
+}
+
void ImageWriter::AddMethodPointerArray(mirror::PointerArray* arr) {
DCHECK(arr != nullptr);
if (kIsDebugBuild) {
@@ -380,7 +377,7 @@
// so we pre-calculate their offsets separately in PrepareDexCacheArraySlots().
// Since these arrays are huge, most pages do not overlap other objects and it's not
// really important where they are for the clean/dirty separation. Due to their
- // special PC-relative addressing, we arbitrarily keep them at the beginning.
+ // special PC-relative addressing, we arbitrarily keep them at the end.
// * Class'es which are verified [their clinit runs only at runtime]
// - classes in general [because their static fields get overwritten]
// - initialized classes with all-final statics are unlikely to be ever dirty,
@@ -442,28 +439,13 @@
}
} else if (object->GetClass<kVerifyNone>()->IsStringClass()) {
bin = kBinString; // Strings are almost always immutable (except for object header).
- } else if (object->IsArrayInstance()) {
- mirror::Class* klass = object->GetClass<kVerifyNone>();
- if (klass->IsObjectArrayClass() || klass->IsIntArrayClass() || klass->IsLongArrayClass()) {
- auto it = dex_cache_array_indexes_.find(object);
- if (it != dex_cache_array_indexes_.end()) {
- bin = kBinDexCacheArray;
- // Use prepared offset defined by the DexCacheLayout.
- current_offset = it->second.offset_;
- // Override incase of cross compilation.
- object_size = it->second.length_;
- } // else bin = kBinRegular
- }
} // else bin = kBinRegular
}
size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment
- if (bin != kBinDexCacheArray) {
- DCHECK(dex_cache_array_indexes_.find(object) == dex_cache_array_indexes_.end()) << object;
- current_offset = bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned).
- // Move the current bin size up to accomodate the object we just assigned a bin slot.
- bin_slot_sizes_[bin] += offset_delta;
- }
+ current_offset = bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned).
+ // Move the current bin size up to accomodate the object we just assigned a bin slot.
+ bin_slot_sizes_[bin] += offset_delta;
BinSlot new_bin_slot(bin, current_offset);
SetImageBinSlot(object, new_bin_slot);
@@ -594,7 +576,7 @@
}
// Clear references to removed classes from the DexCaches.
- const ArtMethod* resolution_method = runtime->GetResolutionMethod();
+ ArtMethod* resolution_method = runtime->GetResolutionMethod();
ScopedAssertNoThreadSuspension sa(self, __FUNCTION__);
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); // For ClassInClassTable
@@ -610,16 +592,20 @@
dex_cache->SetResolvedType(i, nullptr);
}
}
- auto* resolved_methods = down_cast<mirror::PointerArray*>(dex_cache->GetResolvedMethods());
- for (size_t i = 0, len = resolved_methods->GetLength(); i < len; i++) {
- auto* method = resolved_methods->GetElementPtrSize<ArtMethod*>(i, target_ptr_size_);
+ ArtMethod** resolved_methods = dex_cache->GetResolvedMethods();
+ for (size_t i = 0, num = dex_cache->NumResolvedMethods(); i != num; ++i) {
+ ArtMethod* method =
+ mirror::DexCache::GetElementPtrSize(resolved_methods, i, target_ptr_size_);
if (method != nullptr) {
auto* declaring_class = method->GetDeclaringClass();
// Miranda methods may be held live by a class which was not an image class but have a
// declaring class which is an image class. Set it to the resolution method to be safe and
// prevent dangling pointers.
if (method->IsMiranda() || !IsImageClass(declaring_class)) {
- resolved_methods->SetElementPtrSize(i, resolution_method, target_ptr_size_);
+ mirror::DexCache::SetElementPtrSize(resolved_methods,
+ i,
+ resolution_method,
+ target_ptr_size_);
} else {
// Check that the class is still in the classes table.
DCHECK(class_linker->ClassInClassTable(declaring_class)) << "Class "
@@ -921,8 +907,6 @@
image_end_ += RoundUp(sizeof(ImageHeader), kObjectAlignment); // 64-bit-alignment
image_objects_offset_begin_ = image_end_;
- // Prepare bin slots for dex cache arrays.
- PrepareDexCacheArraySlots();
// Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
heap->VisitObjects(WalkFieldsCallback, this);
// Write the image runtime methods.
@@ -952,6 +936,8 @@
CHECK(m->IsRuntimeMethod());
AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean);
}
+ // Calculate size of the dex cache arrays slot and prepare offsets.
+ PrepareDexCacheArraySlots();
// Calculate bin slot offsets.
size_t bin_offset = image_objects_offset_begin_;
@@ -1018,6 +1004,11 @@
bin_slot_sizes_[kBinArtMethodDirty]);
CHECK_EQ(bin_slot_offsets_[kBinArtMethodClean], methods_section->Offset());
cur_pos = methods_section->End();
+ // Add dex cache arrays section.
+ auto* dex_cache_arrays_section = §ions[ImageHeader::kSectionDexCacheArrays];
+ *dex_cache_arrays_section = ImageSection(cur_pos, bin_slot_sizes_[kBinDexCacheArray]);
+ CHECK_EQ(bin_slot_offsets_[kBinDexCacheArray], dex_cache_arrays_section->Offset());
+ cur_pos = dex_cache_arrays_section->End();
// Round up to the alignment the string table expects. See HashSet::WriteToMemory.
cur_pos = RoundUp(cur_pos, sizeof(uint64_t));
// Calculate the size of the interned strings.
@@ -1119,6 +1110,9 @@
ArtMethod::Size(target_ptr_size_),
ArtMethod::Alignment(target_ptr_size_)));
break;
+ case kNativeObjectRelocationTypeDexCacheArray:
+ // Nothing to copy here, everything is done in FixupDexCache().
+ break;
}
}
}
@@ -1186,7 +1180,7 @@
auto* elem = arr->GetElementPtrSize<void*>(i, target_ptr_size_);
if (elem != nullptr) {
auto it = native_object_relocations_.find(elem);
- if (it == native_object_relocations_.end()) {
+ if (UNLIKELY(it == native_object_relocations_.end())) {
if (it->second.IsArtMethodRelocation()) {
auto* method = reinterpret_cast<ArtMethod*>(elem);
LOG(FATAL) << "No relocation entry for ArtMethod " << PrettyMethod(method) << " @ "
@@ -1199,6 +1193,7 @@
<< field << " idx=" << i << "/" << num_elements << " with declaring class "
<< PrettyClass(field->GetDeclaringClass());
}
+ UNREACHABLE();
} else {
elem = image_begin_ + it->second.offset;
}
@@ -1279,27 +1274,31 @@
}
};
-void* ImageWriter::NativeLocationInImage(void* obj) {
- if (obj == nullptr) {
- return nullptr;
- }
+uintptr_t ImageWriter::NativeOffsetInImage(void* obj) {
+ DCHECK(obj != nullptr);
auto it = native_object_relocations_.find(obj);
CHECK(it != native_object_relocations_.end()) << obj;
const NativeObjectRelocation& relocation = it->second;
- return reinterpret_cast<void*>(image_begin_ + relocation.offset);
+ return relocation.offset;
+}
+
+template <typename T>
+T* ImageWriter::NativeLocationInImage(T* obj) {
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ return reinterpret_cast<T*>(image_begin_ + NativeOffsetInImage(obj));
}
void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) {
// Update the field arrays.
- copy->SetSFieldsPtrUnchecked(reinterpret_cast<LengthPrefixedArray<ArtField>*>(
- NativeLocationInImage(orig->GetSFieldsPtr())));
- copy->SetIFieldsPtrUnchecked(reinterpret_cast<LengthPrefixedArray<ArtField>*>(
- NativeLocationInImage(orig->GetIFieldsPtr())));
+ copy->SetSFieldsPtrUnchecked(NativeLocationInImage(orig->GetSFieldsPtr()));
+ copy->SetIFieldsPtrUnchecked(NativeLocationInImage(orig->GetIFieldsPtr()));
// Update direct and virtual method arrays.
- copy->SetDirectMethodsPtrUnchecked(reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(
- NativeLocationInImage(orig->GetDirectMethodsPtr())));
- copy->SetVirtualMethodsPtr(reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(
- NativeLocationInImage(orig->GetVirtualMethodsPtr())));
+ copy->SetDirectMethodsPtrUnchecked(NativeLocationInImage(orig->GetDirectMethodsPtr()));
+ copy->SetVirtualMethodsPtr(NativeLocationInImage(orig->GetVirtualMethodsPtr()));
+ // Update dex cache strings.
+ copy->SetDexCacheStrings(NativeLocationInImage(orig->GetDexCacheStrings()));
// Fix up embedded tables.
if (orig->ShouldHaveEmbeddedImtAndVTable()) {
for (int32_t i = 0; i < orig->GetEmbeddedVTableLength(); ++i) {
@@ -1332,7 +1331,7 @@
}
auto* klass = orig->GetClass();
if (klass->IsIntArrayClass() || klass->IsLongArrayClass()) {
- // Is this a native dex cache array?
+ // Is this a native pointer array?
auto it = pointer_arrays_.find(down_cast<mirror::PointerArray*>(orig));
if (it != pointer_arrays_.end()) {
// Should only need to fixup every pointer array exactly once.
@@ -1340,8 +1339,6 @@
pointer_arrays_.erase(it);
return;
}
- CHECK(dex_cache_array_indexes_.find(orig) == dex_cache_array_indexes_.end())
- << "Should have been pointer array.";
}
if (orig->IsClass()) {
FixupClass(orig->AsClass<kVerifyNone>(), down_cast<mirror::Class*>(copy));
@@ -1356,17 +1353,81 @@
<< "Missing relocation for AbstractMethod.artMethod " << PrettyMethod(src_method);
dest->SetArtMethod(
reinterpret_cast<ArtMethod*>(image_begin_ + it->second.offset));
- } else if (!klass->IsArrayClass() && klass->IsSubClass(down_cast<mirror::Class*>(
- Thread::Current()->DecodeJObject(WellKnownClasses::java_lang_ClassLoader)))) {
- // If src is a ClassLoader, set the class table to null so that it gets recreated by the
- // ClassLoader.
- down_cast<mirror::ClassLoader*>(copy)->SetClassTable(nullptr);
+ } else if (!klass->IsArrayClass()) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (klass == class_linker->GetClassRoot(ClassLinker::kJavaLangDexCache)) {
+ FixupDexCache(down_cast<mirror::DexCache*>(orig), down_cast<mirror::DexCache*>(copy));
+ } else if (klass->IsSubClass(down_cast<mirror::Class*>(
+ class_linker->GetClassRoot(ClassLinker::kJavaLangClassLoader)))) {
+ // If src is a ClassLoader, set the class table to null so that it gets recreated by the
+ // ClassLoader.
+ down_cast<mirror::ClassLoader*>(copy)->SetClassTable(nullptr);
+ }
}
FixupVisitor visitor(this, copy);
orig->VisitReferences(visitor, visitor);
}
}
+void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache,
+ mirror::DexCache* copy_dex_cache) {
+ // Though the DexCache array fields are usually treated as native pointers, we set the full
+ // 64-bit values here, clearing the top 32 bits for 32-bit targets. The zero-extension is
+ // done by casting to the unsigned type uintptr_t before casting to int64_t, i.e.
+ // static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + offset))).
+ GcRoot<mirror::String>* orig_strings = orig_dex_cache->GetStrings();
+ if (orig_strings != nullptr) {
+ uintptr_t copy_strings_offset = NativeOffsetInImage(orig_strings);
+ copy_dex_cache->SetField64<false>(
+ mirror::DexCache::StringsOffset(),
+ static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + copy_strings_offset)));
+ GcRoot<mirror::String>* copy_strings =
+ reinterpret_cast<GcRoot<mirror::String>*>(image_->Begin() + copy_strings_offset);
+ for (size_t i = 0, num = orig_dex_cache->NumStrings(); i != num; ++i) {
+ copy_strings[i] = GcRoot<mirror::String>(GetImageAddress(orig_strings[i].Read()));
+ }
+ }
+ GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
+ if (orig_types != nullptr) {
+ uintptr_t copy_types_offset = NativeOffsetInImage(orig_types);
+ copy_dex_cache->SetField64<false>(
+ mirror::DexCache::ResolvedTypesOffset(),
+ static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + copy_types_offset)));
+ GcRoot<mirror::Class>* copy_types =
+ reinterpret_cast<GcRoot<mirror::Class>*>(image_->Begin() + copy_types_offset);
+ for (size_t i = 0, num = orig_dex_cache->NumResolvedTypes(); i != num; ++i) {
+ copy_types[i] = GcRoot<mirror::Class>(GetImageAddress(orig_types[i].Read()));
+ }
+ }
+ ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods();
+ if (orig_methods != nullptr) {
+ uintptr_t copy_methods_offset = NativeOffsetInImage(orig_methods);
+ copy_dex_cache->SetField64<false>(
+ mirror::DexCache::ResolvedMethodsOffset(),
+ static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + copy_methods_offset)));
+ ArtMethod** copy_methods =
+ reinterpret_cast<ArtMethod**>(image_->Begin() + copy_methods_offset);
+ for (size_t i = 0, num = orig_dex_cache->NumResolvedMethods(); i != num; ++i) {
+ ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, i, target_ptr_size_);
+ ArtMethod* copy = NativeLocationInImage(orig);
+ mirror::DexCache::SetElementPtrSize(copy_methods, i, copy, target_ptr_size_);
+ }
+ }
+ ArtField** orig_fields = orig_dex_cache->GetResolvedFields();
+ if (orig_fields != nullptr) {
+ uintptr_t copy_fields_offset = NativeOffsetInImage(orig_fields);
+ copy_dex_cache->SetField64<false>(
+ mirror::DexCache::ResolvedFieldsOffset(),
+ static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + copy_fields_offset)));
+ ArtField** copy_fields = reinterpret_cast<ArtField**>(image_->Begin() + copy_fields_offset);
+ for (size_t i = 0, num = orig_dex_cache->NumResolvedFields(); i != num; ++i) {
+ ArtField* orig = mirror::DexCache::GetElementPtrSize(orig_fields, i, target_ptr_size_);
+ ArtField* copy = NativeLocationInImage(orig);
+ mirror::DexCache::SetElementPtrSize(copy_fields, i, copy, target_ptr_size_);
+ }
+ }
+}
+
const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, bool* quick_is_interpreted) {
DCHECK(!method->IsResolutionMethod() && !method->IsImtConflictMethod() &&
!method->IsImtUnimplementedMethod() && !method->IsAbstract()) << PrettyMethod(method);
@@ -1429,8 +1490,11 @@
memcpy(copy, orig, ArtMethod::Size(target_ptr_size_));
copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked()));
- copy->SetDexCacheResolvedMethods(GetImageAddress(orig->GetDexCacheResolvedMethods()));
- copy->SetDexCacheResolvedTypes(GetImageAddress(orig->GetDexCacheResolvedTypes()));
+
+ ArtMethod** orig_resolved_methods = orig->GetDexCacheResolvedMethods(target_ptr_size_);
+ copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_);
+ GcRoot<mirror::Class>* orig_resolved_types = orig->GetDexCacheResolvedTypes(target_ptr_size_);
+ copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types), target_ptr_size_);
// OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
// oat_begin_
@@ -1533,9 +1597,11 @@
uint8_t* ImageWriter::GetOatFileBegin() const {
DCHECK_GT(intern_table_bytes_, 0u);
- return image_begin_ + RoundUp(
- image_end_ + bin_slot_sizes_[kBinArtField] + bin_slot_sizes_[kBinArtMethodDirty] +
- bin_slot_sizes_[kBinArtMethodClean] + intern_table_bytes_, kPageSize);
+ size_t native_sections_size =
+ bin_slot_sizes_[kBinArtField] + bin_slot_sizes_[kBinArtMethodDirty] +
+ bin_slot_sizes_[kBinArtMethodClean] + bin_slot_sizes_[kBinDexCacheArray] +
+ intern_table_bytes_;
+ return image_begin_ + RoundUp(image_end_ + native_sections_size, kPageSize);
}
ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocationType type) {
@@ -1549,6 +1615,8 @@
case kNativeObjectRelocationTypeArtMethodDirty:
case kNativeObjectRelocationTypeArtMethodArrayDirty:
return kBinArtMethodDirty;
+ case kNativeObjectRelocationTypeDexCacheArray:
+ return kBinDexCacheArray;
}
UNREACHABLE();
}
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 778521c..e235bc4 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -78,12 +78,13 @@
ArtMethod* GetImageMethodAddress(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
- mirror::HeapReference<mirror::Object>* GetDexCacheArrayElementImageAddress(
- const DexFile* dex_file, uint32_t offset) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ template <typename PtrType>
+ PtrType GetDexCacheArrayElementImageAddress(const DexFile* dex_file, uint32_t offset)
+ const SHARED_REQUIRES(Locks::mutator_lock_) {
auto it = dex_cache_array_starts_.find(dex_file);
DCHECK(it != dex_cache_array_starts_.end());
- return reinterpret_cast<mirror::HeapReference<mirror::Object>*>(
- image_begin_ + RoundUp(sizeof(ImageHeader), kObjectAlignment) + it->second + offset);
+ return reinterpret_cast<PtrType>(
+ image_begin_ + bin_slot_offsets_[kBinDexCacheArray] + it->second + offset);
}
uint8_t* GetOatFileBegin() const;
@@ -104,13 +105,8 @@
// Classify different kinds of bins that objects end up getting packed into during image writing.
enum Bin {
- // Dex cache arrays have a special slot for PC-relative addressing. Since they are
- // huge, and as such their dirtiness is not important for the clean/dirty separation,
- // we arbitrarily keep them at the beginning.
- kBinDexCacheArray, // Object arrays belonging to dex cache.
// Likely-clean:
kBinString, // [String] Almost always immutable (except for obj header).
- kBinArtMethodsManagedInitialized, // [ArtMethod] Not-native, and initialized. Unlikely to dirty
// Unknown mix of clean/dirty:
kBinRegular,
// Likely-dirty:
@@ -127,6 +123,10 @@
// ArtMethods may be dirty if the class has native methods or a declaring class that isn't
// initialized.
kBinArtMethodDirty,
+ // Dex cache arrays have a special slot for PC-relative addressing. Since they are
+ // huge, and as such their dirtiness is not important for the clean/dirty separation,
+ // we arbitrarily keep them at the end of the native data.
+ kBinDexCacheArray, // Arrays belonging to dex cache.
kBinSize,
// Number of bins which are for mirror objects.
kBinMirrorCount = kBinArtField,
@@ -140,6 +140,7 @@
kNativeObjectRelocationTypeArtMethodArrayClean,
kNativeObjectRelocationTypeArtMethodDirty,
kNativeObjectRelocationTypeArtMethodArrayDirty,
+ kNativeObjectRelocationTypeDexCacheArray,
};
friend std::ostream& operator<<(std::ostream& stream, const NativeObjectRelocationType& type);
@@ -193,6 +194,7 @@
SHARED_REQUIRES(Locks::mutator_lock_);
BinSlot GetImageBinSlot(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_);
+ void AddDexCacheArrayRelocation(void* array, size_t offset) SHARED_REQUIRES(Locks::mutator_lock_);
void AddMethodPointerArray(mirror::PointerArray* arr) SHARED_REQUIRES(Locks::mutator_lock_);
static void* GetImageAddressCallback(void* writer, mirror::Object* obj)
@@ -266,6 +268,8 @@
SHARED_REQUIRES(Locks::mutator_lock_);
void FixupObject(mirror::Object* orig, mirror::Object* copy)
SHARED_REQUIRES(Locks::mutator_lock_);
+ void FixupDexCache(mirror::DexCache* orig_dex_cache, mirror::DexCache* copy_dex_cache)
+ SHARED_REQUIRES(Locks::mutator_lock_);
void FixupPointerArray(mirror::Object* dst, mirror::PointerArray* arr, mirror::Class* klass,
Bin array_type) SHARED_REQUIRES(Locks::mutator_lock_);
@@ -291,7 +295,10 @@
static Bin BinTypeForNativeRelocationType(NativeObjectRelocationType type);
- void* NativeLocationInImage(void* obj);
+ uintptr_t NativeOffsetInImage(void* obj);
+
+ template <typename T>
+ T* NativeLocationInImage(T* obj);
const CompilerDriver& compiler_driver_;
@@ -313,15 +320,6 @@
// Memory mapped for generating the image.
std::unique_ptr<MemMap> image_;
- // Indexes, lengths for dex cache arrays (objects are inside of the image so that they don't
- // move).
- struct DexCacheArrayLocation {
- size_t offset_;
- size_t length_;
- Bin bin_type_;
- };
- SafeMap<mirror::Object*, DexCacheArrayLocation> dex_cache_array_indexes_;
-
// Pointer arrays that need to be updated. Since these are only some int and long arrays, we need
// to keep track. These include vtable arrays, iftable arrays, and dex caches.
std::unordered_map<mirror::PointerArray*, Bin> pointer_arrays_;
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index fdf904d..4ddd457 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -842,10 +842,10 @@
uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
if (writer_->image_writer_ != nullptr) {
- auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress(
+ auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress<const uint8_t*>(
patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset());
const uint8_t* oat_data = writer_->image_writer_->GetOatFileBegin() + file_offset_;
- return reinterpret_cast<const uint8_t*>(element) - oat_data;
+ return element - oat_data;
} else {
LOG(FATAL) << "Unimplemented.";
UNREACHABLE();
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 7fa16bb..9895953 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -46,7 +46,7 @@
explicit Temporaries(HGraph* graph) : graph_(graph), index_(0) {}
void Add(HInstruction* instruction) {
- HInstruction* temp = new (graph_->GetArena()) HTemporary(index_);
+ HInstruction* temp = new (graph_->GetArena()) HTemporary(index_, instruction->GetDexPc());
instruction->GetBlock()->AddInstruction(temp);
DCHECK(temp->GetPrevious() == instruction);
@@ -161,23 +161,25 @@
if (!dex_compilation_unit_->IsStatic()) {
// Add the implicit 'this' argument, not expressed in the signature.
- HParameterValue* parameter =
- new (arena_) HParameterValue(parameter_index++, Primitive::kPrimNot, true);
+ HParameterValue* parameter = new (arena_) HParameterValue(parameter_index++,
+ Primitive::kPrimNot,
+ true);
entry_block_->AddInstruction(parameter);
HLocal* local = GetLocalAt(locals_index++);
- entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter));
+ entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter, local->GetDexPc()));
number_of_parameters--;
}
uint32_t pos = 1;
for (int i = 0; i < number_of_parameters; i++) {
- HParameterValue* parameter =
- new (arena_) HParameterValue(parameter_index++, Primitive::GetType(shorty[pos++]));
+ HParameterValue* parameter = new (arena_) HParameterValue(parameter_index++,
+ Primitive::GetType(shorty[pos++]),
+ false);
entry_block_->AddInstruction(parameter);
HLocal* local = GetLocalAt(locals_index++);
// Store the parameter value in the local that the dex code will use
// to reference that parameter.
- entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter));
+ entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter, local->GetDexPc()));
bool is_wide = (parameter->GetType() == Primitive::kPrimLong)
|| (parameter->GetType() == Primitive::kPrimDouble);
if (is_wide) {
@@ -196,11 +198,11 @@
DCHECK(branch_target != nullptr);
DCHECK(fallthrough_target != nullptr);
PotentiallyAddSuspendCheck(branch_target, dex_pc);
- HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
- HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
- T* comparison = new (arena_) T(first, second);
+ HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
+ HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+ T* comparison = new (arena_) T(first, second, dex_pc);
current_block_->AddInstruction(comparison);
- HInstruction* ifinst = new (arena_) HIf(comparison);
+ HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
current_block_->AddInstruction(ifinst);
current_block_->AddSuccessor(branch_target);
current_block_->AddSuccessor(fallthrough_target);
@@ -215,10 +217,10 @@
DCHECK(branch_target != nullptr);
DCHECK(fallthrough_target != nullptr);
PotentiallyAddSuspendCheck(branch_target, dex_pc);
- HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
- T* comparison = new (arena_) T(value, graph_->GetIntConstant(0));
+ HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
+ T* comparison = new (arena_) T(value, graph_->GetIntConstant(0, dex_pc), dex_pc);
current_block_->AddInstruction(comparison);
- HInstruction* ifinst = new (arena_) HIf(comparison);
+ HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
current_block_->AddInstruction(ifinst);
current_block_->AddSuccessor(branch_target);
current_block_->AddSuccessor(fallthrough_target);
@@ -320,7 +322,7 @@
const DexFile::CodeItem& code_item,
const DexFile::TryItem& try_item) {
// Split the edge with a single TryBoundary instruction.
- HTryBoundary* try_boundary = new (arena_) HTryBoundary(kind);
+ HTryBoundary* try_boundary = new (arena_) HTryBoundary(kind, successor->GetDexPc());
HBasicBlock* try_entry_block = graph_->SplitEdge(predecessor, successor);
try_entry_block->AddInstruction(try_boundary);
@@ -537,7 +539,7 @@
// Branching instructions clear current_block, so we know
// the last instruction of the current block is not a branching
// instruction. We add an unconditional goto to the found block.
- current_block_->AddInstruction(new (arena_) HGoto());
+ current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
current_block_->AddSuccessor(block);
}
graph_->AddBlock(block);
@@ -633,104 +635,92 @@
}
template<typename T>
-void HGraphBuilder::Unop_12x(const Instruction& instruction, Primitive::Type type) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type);
- current_block_->AddInstruction(new (arena_) T(type, first));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+void HGraphBuilder::Unop_12x(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
+ current_block_->AddInstruction(new (arena_) T(type, first, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
}
void HGraphBuilder::Conversion_12x(const Instruction& instruction,
Primitive::Type input_type,
Primitive::Type result_type,
uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), input_type);
+ HInstruction* first = LoadLocal(instruction.VRegB(), input_type, dex_pc);
current_block_->AddInstruction(new (arena_) HTypeConversion(result_type, first, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-template<typename T>
-void HGraphBuilder::Binop_23x(const Instruction& instruction, Primitive::Type type) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type);
- HInstruction* second = LoadLocal(instruction.VRegC(), type);
- current_block_->AddInstruction(new (arena_) T(type, first, second));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
}
template<typename T>
void HGraphBuilder::Binop_23x(const Instruction& instruction,
Primitive::Type type,
uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type);
- HInstruction* second = LoadLocal(instruction.VRegC(), type);
+ HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
+ HInstruction* second = LoadLocal(instruction.VRegC(), type, dex_pc);
current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
}
template<typename T>
void HGraphBuilder::Binop_23x_shift(const Instruction& instruction,
- Primitive::Type type) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type);
- HInstruction* second = LoadLocal(instruction.VRegC(), Primitive::kPrimInt);
- current_block_->AddInstruction(new (arena_) T(type, first, second));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
+ HInstruction* second = LoadLocal(instruction.VRegC(), Primitive::kPrimInt, dex_pc);
+ current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
}
void HGraphBuilder::Binop_23x_cmp(const Instruction& instruction,
Primitive::Type type,
ComparisonBias bias,
uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type);
- HInstruction* second = LoadLocal(instruction.VRegC(), type);
+ HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
+ HInstruction* second = LoadLocal(instruction.VRegC(), type, dex_pc);
current_block_->AddInstruction(new (arena_) HCompare(type, first, second, bias, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
}
template<typename T>
-void HGraphBuilder::Binop_12x(const Instruction& instruction, Primitive::Type type) {
- HInstruction* first = LoadLocal(instruction.VRegA(), type);
- HInstruction* second = LoadLocal(instruction.VRegB(), type);
- current_block_->AddInstruction(new (arena_) T(type, first, second));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
-}
-
-template<typename T>
-void HGraphBuilder::Binop_12x_shift(const Instruction& instruction, Primitive::Type type) {
- HInstruction* first = LoadLocal(instruction.VRegA(), type);
- HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
- current_block_->AddInstruction(new (arena_) T(type, first, second));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+void HGraphBuilder::Binop_12x_shift(const Instruction& instruction, Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegA(), type, dex_pc);
+ HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+ current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
}
template<typename T>
void HGraphBuilder::Binop_12x(const Instruction& instruction,
Primitive::Type type,
uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegA(), type);
- HInstruction* second = LoadLocal(instruction.VRegB(), type);
+ HInstruction* first = LoadLocal(instruction.VRegA(), type, dex_pc);
+ HInstruction* second = LoadLocal(instruction.VRegB(), type, dex_pc);
current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
}
template<typename T>
-void HGraphBuilder::Binop_22s(const Instruction& instruction, bool reverse) {
- HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
- HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22s());
+void HGraphBuilder::Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+ HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22s(), dex_pc);
if (reverse) {
std::swap(first, second);
}
- current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+ current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
}
template<typename T>
-void HGraphBuilder::Binop_22b(const Instruction& instruction, bool reverse) {
- HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
- HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22b());
+void HGraphBuilder::Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+ HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22b(), dex_pc);
if (reverse) {
std::swap(first, second);
}
- current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+ current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
}
static bool RequiresConstructorBarrier(const DexCompilationUnit* cu, const CompilerDriver& driver) {
@@ -739,7 +729,9 @@
&& driver.RequiresConstructorBarrier(self, cu->GetDexFile(), cu->GetClassDefIndex());
}
-void HGraphBuilder::BuildReturn(const Instruction& instruction, Primitive::Type type) {
+void HGraphBuilder::BuildReturn(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
if (type == Primitive::kPrimVoid) {
if (graph_->ShouldGenerateConstructorBarrier()) {
// The compilation unit is null during testing.
@@ -747,12 +739,12 @@
DCHECK(RequiresConstructorBarrier(dex_compilation_unit_, *compiler_driver_))
<< "Inconsistent use of ShouldGenerateConstructorBarrier. Should not generate a barrier.";
}
- current_block_->AddInstruction(new (arena_) HMemoryBarrier(kStoreStore));
+ current_block_->AddInstruction(new (arena_) HMemoryBarrier(kStoreStore, dex_pc));
}
- current_block_->AddInstruction(new (arena_) HReturnVoid());
+ current_block_->AddInstruction(new (arena_) HReturnVoid(dex_pc));
} else {
- HInstruction* value = LoadLocal(instruction.VRegA(), type);
- current_block_->AddInstruction(new (arena_) HReturn(value));
+ HInstruction* value = LoadLocal(instruction.VRegA(), type, dex_pc);
+ current_block_->AddInstruction(new (arena_) HReturn(value, dex_pc));
}
current_block_->AddSuccessor(exit_block_);
current_block_ = nullptr;
@@ -1049,6 +1041,7 @@
size_t start_index = 0;
size_t argument_index = 0;
uint32_t descriptor_index = 1; // Skip the return type.
+ uint32_t dex_pc = invoke->GetDexPc();
bool is_instance_call = invoke->GetOriginalInvokeType() != InvokeType::kStatic;
bool is_string_init = invoke->IsInvokeStaticOrDirect()
@@ -1059,7 +1052,7 @@
argument_index = 0;
} else if (is_instance_call) {
Temporaries temps(graph_);
- HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot);
+ HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot, dex_pc);
HNullCheck* null_check = new (arena_) HNullCheck(arg, invoke->GetDexPc());
current_block_->AddInstruction(null_check);
temps.Add(null_check);
@@ -1088,7 +1081,7 @@
MaybeRecordStat(MethodCompilationStat::kNotCompiledMalformedOpcode);
return false;
}
- HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type);
+ HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type, dex_pc);
invoke->SetArgumentAt(argument_index, arg);
if (is_wide) {
i++;
@@ -1121,7 +1114,7 @@
// Add move-result for StringFactory method.
if (is_string_init) {
uint32_t orig_this_reg = is_range ? register_index : args[0];
- HInstruction* fake_string = LoadLocal(orig_this_reg, Primitive::kPrimNot);
+ HInstruction* fake_string = LoadLocal(orig_this_reg, Primitive::kPrimNot, dex_pc);
invoke->SetArgumentAt(argument_index, fake_string);
current_block_->AddInstruction(invoke);
PotentiallySimplifyFakeString(orig_this_reg, invoke->GetDexPc(), invoke);
@@ -1147,15 +1140,15 @@
const VerifiedMethod* verified_method =
compiler_driver_->GetVerifiedMethod(dex_file_, dex_compilation_unit_->GetDexMethodIndex());
if (verified_method != nullptr) {
- UpdateLocal(original_dex_register, actual_string);
+ UpdateLocal(original_dex_register, actual_string, dex_pc);
const SafeMap<uint32_t, std::set<uint32_t>>& string_init_map =
verified_method->GetStringInitPcRegMap();
auto map_it = string_init_map.find(dex_pc);
if (map_it != string_init_map.end()) {
std::set<uint32_t> reg_set = map_it->second;
for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) {
- HInstruction* load_local = LoadLocal(original_dex_register, Primitive::kPrimNot);
- UpdateLocal(*set_it, load_local);
+ HInstruction* load_local = LoadLocal(original_dex_register, Primitive::kPrimNot, dex_pc);
+ UpdateLocal(*set_it, load_local, dex_pc);
}
}
} else {
@@ -1189,14 +1182,14 @@
Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
- HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot);
+ HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot, dex_pc);
current_block_->AddInstruction(new (arena_) HNullCheck(object, dex_pc));
if (is_put) {
Temporaries temps(graph_);
HInstruction* null_check = current_block_->GetLastInstruction();
// We need one temporary for the null check.
temps.Add(null_check);
- HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
+ HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
current_block_->AddInstruction(new (arena_) HInstanceFieldSet(
null_check,
value,
@@ -1205,7 +1198,8 @@
resolved_field->IsVolatile(),
field_index,
*dex_file_,
- dex_compilation_unit_->GetDexCache()));
+ dex_compilation_unit_->GetDexCache(),
+ dex_pc));
} else {
current_block_->AddInstruction(new (arena_) HInstanceFieldGet(
current_block_->GetLastInstruction(),
@@ -1214,9 +1208,10 @@
resolved_field->IsVolatile(),
field_index,
*dex_file_,
- dex_compilation_unit_->GetDexCache()));
+ dex_compilation_unit_->GetDexCache(),
+ dex_pc));
- UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+ UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
}
return true;
}
@@ -1327,7 +1322,7 @@
// We need to keep the class alive before loading the value.
Temporaries temps(graph_);
temps.Add(cls);
- HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
+ HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
DCHECK_EQ(value->GetType(), field_type);
current_block_->AddInstruction(new (arena_) HStaticFieldSet(cls,
value,
@@ -1336,7 +1331,8 @@
resolved_field->IsVolatile(),
field_index,
*dex_file_,
- dex_cache_));
+ dex_cache_,
+ dex_pc));
} else {
current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls,
field_type,
@@ -1344,8 +1340,9 @@
resolved_field->IsVolatile(),
field_index,
*dex_file_,
- dex_cache_));
- UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+ dex_cache_,
+ dex_pc));
+ UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
}
return true;
}
@@ -1359,16 +1356,16 @@
bool isDiv) {
DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
- HInstruction* first = LoadLocal(first_vreg, type);
+ HInstruction* first = LoadLocal(first_vreg, type, dex_pc);
HInstruction* second = nullptr;
if (second_is_constant) {
if (type == Primitive::kPrimInt) {
- second = graph_->GetIntConstant(second_vreg_or_constant);
+ second = graph_->GetIntConstant(second_vreg_or_constant, dex_pc);
} else {
- second = graph_->GetLongConstant(second_vreg_or_constant);
+ second = graph_->GetLongConstant(second_vreg_or_constant, dex_pc);
}
} else {
- second = LoadLocal(second_vreg_or_constant, type);
+ second = LoadLocal(second_vreg_or_constant, type, dex_pc);
}
if (!second_is_constant
@@ -1385,7 +1382,7 @@
} else {
current_block_->AddInstruction(new (arena_) HRem(type, first, second, dex_pc));
}
- UpdateLocal(out_vreg, current_block_->GetLastInstruction());
+ UpdateLocal(out_vreg, current_block_->GetLastInstruction(), dex_pc);
}
void HGraphBuilder::BuildArrayAccess(const Instruction& instruction,
@@ -1399,26 +1396,26 @@
// We need one temporary for the null check, one for the index, and one for the length.
Temporaries temps(graph_);
- HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot);
+ HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot, dex_pc);
object = new (arena_) HNullCheck(object, dex_pc);
current_block_->AddInstruction(object);
temps.Add(object);
- HInstruction* length = new (arena_) HArrayLength(object);
+ HInstruction* length = new (arena_) HArrayLength(object, dex_pc);
current_block_->AddInstruction(length);
temps.Add(length);
- HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt);
+ HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt, dex_pc);
index = new (arena_) HBoundsCheck(index, length, dex_pc);
current_block_->AddInstruction(index);
temps.Add(index);
if (is_put) {
- HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type);
+ HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type, dex_pc);
// TODO: Insert a type check node if the type is Object.
current_block_->AddInstruction(new (arena_) HArraySet(
object, index, value, anticipated_type, dex_pc));
} else {
- current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type));
- UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+ current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type, dex_pc));
+ UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
}
graph_->SetHasBoundsChecks(true);
}
@@ -1429,7 +1426,7 @@
bool is_range,
uint32_t* args,
uint32_t register_index) {
- HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments);
+ HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments, dex_pc);
QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
? kQuickAllocArrayWithAccessCheck
: kQuickAllocArray;
@@ -1453,8 +1450,8 @@
Temporaries temps(graph_);
temps.Add(object);
for (size_t i = 0; i < number_of_vreg_arguments; ++i) {
- HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type);
- HInstruction* index = graph_->GetIntConstant(i);
+ HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type, dex_pc);
+ HInstruction* index = graph_->GetIntConstant(i, dex_pc);
current_block_->AddInstruction(
new (arena_) HArraySet(object, index, value, type, dex_pc));
}
@@ -1468,8 +1465,8 @@
Primitive::Type anticipated_type,
uint32_t dex_pc) {
for (uint32_t i = 0; i < element_count; ++i) {
- HInstruction* index = graph_->GetIntConstant(i);
- HInstruction* value = graph_->GetIntConstant(data[i]);
+ HInstruction* index = graph_->GetIntConstant(i, dex_pc);
+ HInstruction* value = graph_->GetIntConstant(data[i], dex_pc);
current_block_->AddInstruction(new (arena_) HArraySet(
object, index, value, anticipated_type, dex_pc));
}
@@ -1477,12 +1474,12 @@
void HGraphBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc) {
Temporaries temps(graph_);
- HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot);
+ HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot, dex_pc);
HNullCheck* null_check = new (arena_) HNullCheck(array, dex_pc);
current_block_->AddInstruction(null_check);
temps.Add(null_check);
- HInstruction* length = new (arena_) HArrayLength(null_check);
+ HInstruction* length = new (arena_) HArrayLength(null_check, dex_pc);
current_block_->AddInstruction(length);
int32_t payload_offset = instruction.VRegB_31t() + dex_pc;
@@ -1493,7 +1490,7 @@
// Implementation of this DEX instruction seems to be that the bounds check is
// done before doing any stores.
- HInstruction* last_index = graph_->GetIntConstant(payload->element_count - 1);
+ HInstruction* last_index = graph_->GetIntConstant(payload->element_count - 1, dex_pc);
current_block_->AddInstruction(new (arena_) HBoundsCheck(last_index, length, dex_pc));
switch (payload->element_width) {
@@ -1535,8 +1532,8 @@
uint32_t element_count,
uint32_t dex_pc) {
for (uint32_t i = 0; i < element_count; ++i) {
- HInstruction* index = graph_->GetIntConstant(i);
- HInstruction* value = graph_->GetLongConstant(data[i]);
+ HInstruction* index = graph_->GetIntConstant(i, dex_pc);
+ HInstruction* value = graph_->GetLongConstant(data[i], dex_pc);
current_block_->AddInstruction(new (arena_) HArraySet(
object, index, value, Primitive::kPrimLong, dex_pc));
}
@@ -1561,7 +1558,7 @@
MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
return false;
}
- HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
+ HInstruction* object = LoadLocal(reference, Primitive::kPrimNot, dex_pc);
HLoadClass* cls = new (arena_) HLoadClass(
graph_->GetCurrentMethod(),
type_index,
@@ -1575,7 +1572,7 @@
if (instruction.Opcode() == Instruction::INSTANCE_OF) {
current_block_->AddInstruction(
new (arena_) HInstanceOf(object, cls, type_known_final, dex_pc));
- UpdateLocal(destination, current_block_->GetLastInstruction());
+ UpdateLocal(destination, current_block_->GetLastInstruction(), dex_pc);
} else {
DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);
current_block_->AddInstruction(
@@ -1597,7 +1594,7 @@
SwitchTable table(instruction, dex_pc, false);
// Value to test against.
- HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
+ HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
// Retrieve number of entries.
uint16_t num_entries = table.GetNumEntries();
@@ -1622,7 +1619,7 @@
SwitchTable table(instruction, dex_pc, true);
// Value to test against.
- HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
+ HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
uint16_t num_entries = table.GetNumEntries();
@@ -1641,12 +1638,12 @@
PotentiallyAddSuspendCheck(case_target, dex_pc);
// The current case's value.
- HInstruction* this_case_value = graph_->GetIntConstant(case_value_int);
+ HInstruction* this_case_value = graph_->GetIntConstant(case_value_int, dex_pc);
// Compare value and this_case_value.
- HEqual* comparison = new (arena_) HEqual(value, this_case_value);
+ HEqual* comparison = new (arena_) HEqual(value, this_case_value, dex_pc);
current_block_->AddInstruction(comparison);
- HInstruction* ifinst = new (arena_) HIf(comparison);
+ HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
current_block_->AddInstruction(ifinst);
// Case hit: use the target offset to determine where to go.
@@ -1710,29 +1707,29 @@
switch (instruction.Opcode()) {
case Instruction::CONST_4: {
int32_t register_index = instruction.VRegA();
- HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_11n());
- UpdateLocal(register_index, constant);
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_11n(), dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
break;
}
case Instruction::CONST_16: {
int32_t register_index = instruction.VRegA();
- HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21s());
- UpdateLocal(register_index, constant);
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21s(), dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
break;
}
case Instruction::CONST: {
int32_t register_index = instruction.VRegA();
- HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_31i());
- UpdateLocal(register_index, constant);
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_31i(), dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
break;
}
case Instruction::CONST_HIGH16: {
int32_t register_index = instruction.VRegA();
- HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21h() << 16);
- UpdateLocal(register_index, constant);
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21h() << 16, dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
break;
}
@@ -1742,8 +1739,8 @@
int64_t value = instruction.VRegB_21s();
value <<= 48;
value >>= 48;
- HLongConstant* constant = graph_->GetLongConstant(value);
- UpdateLocal(register_index, constant);
+ HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
break;
}
@@ -1753,23 +1750,23 @@
int64_t value = instruction.VRegB_31i();
value <<= 32;
value >>= 32;
- HLongConstant* constant = graph_->GetLongConstant(value);
- UpdateLocal(register_index, constant);
+ HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
break;
}
case Instruction::CONST_WIDE: {
int32_t register_index = instruction.VRegA();
- HLongConstant* constant = graph_->GetLongConstant(instruction.VRegB_51l());
- UpdateLocal(register_index, constant);
+ HLongConstant* constant = graph_->GetLongConstant(instruction.VRegB_51l(), dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
break;
}
case Instruction::CONST_WIDE_HIGH16: {
int32_t register_index = instruction.VRegA();
int64_t value = static_cast<int64_t>(instruction.VRegB_21h()) << 48;
- HLongConstant* constant = graph_->GetLongConstant(value);
- UpdateLocal(register_index, constant);
+ HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
+ UpdateLocal(register_index, constant, dex_pc);
break;
}
@@ -1777,8 +1774,8 @@
case Instruction::MOVE:
case Instruction::MOVE_FROM16:
case Instruction::MOVE_16: {
- HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
- UpdateLocal(instruction.VRegA(), value);
+ HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
+ UpdateLocal(instruction.VRegA(), value, dex_pc);
break;
}
@@ -1786,22 +1783,22 @@
case Instruction::MOVE_WIDE:
case Instruction::MOVE_WIDE_FROM16:
case Instruction::MOVE_WIDE_16: {
- HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimLong);
- UpdateLocal(instruction.VRegA(), value);
+ HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimLong, dex_pc);
+ UpdateLocal(instruction.VRegA(), value, dex_pc);
break;
}
case Instruction::MOVE_OBJECT:
case Instruction::MOVE_OBJECT_16:
case Instruction::MOVE_OBJECT_FROM16: {
- HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimNot);
- UpdateLocal(instruction.VRegA(), value);
+ HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimNot, dex_pc);
+ UpdateLocal(instruction.VRegA(), value, dex_pc);
break;
}
case Instruction::RETURN_VOID_NO_BARRIER:
case Instruction::RETURN_VOID: {
- BuildReturn(instruction, Primitive::kPrimVoid);
+ BuildReturn(instruction, Primitive::kPrimVoid, dex_pc);
break;
}
@@ -1823,24 +1820,24 @@
HBasicBlock* target = FindBlockStartingAt(offset + dex_pc);
DCHECK(target != nullptr);
PotentiallyAddSuspendCheck(target, dex_pc);
- current_block_->AddInstruction(new (arena_) HGoto());
+ current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
current_block_->AddSuccessor(target);
current_block_ = nullptr;
break;
}
case Instruction::RETURN: {
- BuildReturn(instruction, return_type_);
+ BuildReturn(instruction, return_type_, dex_pc);
break;
}
case Instruction::RETURN_OBJECT: {
- BuildReturn(instruction, return_type_);
+ BuildReturn(instruction, return_type_, dex_pc);
break;
}
case Instruction::RETURN_WIDE: {
- BuildReturn(instruction, return_type_);
+ BuildReturn(instruction, return_type_, dex_pc);
break;
}
@@ -1894,32 +1891,32 @@
}
case Instruction::NEG_INT: {
- Unop_12x<HNeg>(instruction, Primitive::kPrimInt);
+ Unop_12x<HNeg>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::NEG_LONG: {
- Unop_12x<HNeg>(instruction, Primitive::kPrimLong);
+ Unop_12x<HNeg>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::NEG_FLOAT: {
- Unop_12x<HNeg>(instruction, Primitive::kPrimFloat);
+ Unop_12x<HNeg>(instruction, Primitive::kPrimFloat, dex_pc);
break;
}
case Instruction::NEG_DOUBLE: {
- Unop_12x<HNeg>(instruction, Primitive::kPrimDouble);
+ Unop_12x<HNeg>(instruction, Primitive::kPrimDouble, dex_pc);
break;
}
case Instruction::NOT_INT: {
- Unop_12x<HNot>(instruction, Primitive::kPrimInt);
+ Unop_12x<HNot>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::NOT_LONG: {
- Unop_12x<HNot>(instruction, Primitive::kPrimLong);
+ Unop_12x<HNot>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
@@ -1999,67 +1996,67 @@
}
case Instruction::ADD_INT: {
- Binop_23x<HAdd>(instruction, Primitive::kPrimInt);
+ Binop_23x<HAdd>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::ADD_LONG: {
- Binop_23x<HAdd>(instruction, Primitive::kPrimLong);
+ Binop_23x<HAdd>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::ADD_DOUBLE: {
- Binop_23x<HAdd>(instruction, Primitive::kPrimDouble);
+ Binop_23x<HAdd>(instruction, Primitive::kPrimDouble, dex_pc);
break;
}
case Instruction::ADD_FLOAT: {
- Binop_23x<HAdd>(instruction, Primitive::kPrimFloat);
+ Binop_23x<HAdd>(instruction, Primitive::kPrimFloat, dex_pc);
break;
}
case Instruction::SUB_INT: {
- Binop_23x<HSub>(instruction, Primitive::kPrimInt);
+ Binop_23x<HSub>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::SUB_LONG: {
- Binop_23x<HSub>(instruction, Primitive::kPrimLong);
+ Binop_23x<HSub>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::SUB_FLOAT: {
- Binop_23x<HSub>(instruction, Primitive::kPrimFloat);
+ Binop_23x<HSub>(instruction, Primitive::kPrimFloat, dex_pc);
break;
}
case Instruction::SUB_DOUBLE: {
- Binop_23x<HSub>(instruction, Primitive::kPrimDouble);
+ Binop_23x<HSub>(instruction, Primitive::kPrimDouble, dex_pc);
break;
}
case Instruction::ADD_INT_2ADDR: {
- Binop_12x<HAdd>(instruction, Primitive::kPrimInt);
+ Binop_12x<HAdd>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::MUL_INT: {
- Binop_23x<HMul>(instruction, Primitive::kPrimInt);
+ Binop_23x<HMul>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::MUL_LONG: {
- Binop_23x<HMul>(instruction, Primitive::kPrimLong);
+ Binop_23x<HMul>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::MUL_FLOAT: {
- Binop_23x<HMul>(instruction, Primitive::kPrimFloat);
+ Binop_23x<HMul>(instruction, Primitive::kPrimFloat, dex_pc);
break;
}
case Instruction::MUL_DOUBLE: {
- Binop_23x<HMul>(instruction, Primitive::kPrimDouble);
+ Binop_23x<HMul>(instruction, Primitive::kPrimDouble, dex_pc);
break;
}
@@ -2108,117 +2105,117 @@
}
case Instruction::AND_INT: {
- Binop_23x<HAnd>(instruction, Primitive::kPrimInt);
+ Binop_23x<HAnd>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::AND_LONG: {
- Binop_23x<HAnd>(instruction, Primitive::kPrimLong);
+ Binop_23x<HAnd>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::SHL_INT: {
- Binop_23x_shift<HShl>(instruction, Primitive::kPrimInt);
+ Binop_23x_shift<HShl>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::SHL_LONG: {
- Binop_23x_shift<HShl>(instruction, Primitive::kPrimLong);
+ Binop_23x_shift<HShl>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::SHR_INT: {
- Binop_23x_shift<HShr>(instruction, Primitive::kPrimInt);
+ Binop_23x_shift<HShr>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::SHR_LONG: {
- Binop_23x_shift<HShr>(instruction, Primitive::kPrimLong);
+ Binop_23x_shift<HShr>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::USHR_INT: {
- Binop_23x_shift<HUShr>(instruction, Primitive::kPrimInt);
+ Binop_23x_shift<HUShr>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::USHR_LONG: {
- Binop_23x_shift<HUShr>(instruction, Primitive::kPrimLong);
+ Binop_23x_shift<HUShr>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::OR_INT: {
- Binop_23x<HOr>(instruction, Primitive::kPrimInt);
+ Binop_23x<HOr>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::OR_LONG: {
- Binop_23x<HOr>(instruction, Primitive::kPrimLong);
+ Binop_23x<HOr>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::XOR_INT: {
- Binop_23x<HXor>(instruction, Primitive::kPrimInt);
+ Binop_23x<HXor>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::XOR_LONG: {
- Binop_23x<HXor>(instruction, Primitive::kPrimLong);
+ Binop_23x<HXor>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::ADD_LONG_2ADDR: {
- Binop_12x<HAdd>(instruction, Primitive::kPrimLong);
+ Binop_12x<HAdd>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::ADD_DOUBLE_2ADDR: {
- Binop_12x<HAdd>(instruction, Primitive::kPrimDouble);
+ Binop_12x<HAdd>(instruction, Primitive::kPrimDouble, dex_pc);
break;
}
case Instruction::ADD_FLOAT_2ADDR: {
- Binop_12x<HAdd>(instruction, Primitive::kPrimFloat);
+ Binop_12x<HAdd>(instruction, Primitive::kPrimFloat, dex_pc);
break;
}
case Instruction::SUB_INT_2ADDR: {
- Binop_12x<HSub>(instruction, Primitive::kPrimInt);
+ Binop_12x<HSub>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::SUB_LONG_2ADDR: {
- Binop_12x<HSub>(instruction, Primitive::kPrimLong);
+ Binop_12x<HSub>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::SUB_FLOAT_2ADDR: {
- Binop_12x<HSub>(instruction, Primitive::kPrimFloat);
+ Binop_12x<HSub>(instruction, Primitive::kPrimFloat, dex_pc);
break;
}
case Instruction::SUB_DOUBLE_2ADDR: {
- Binop_12x<HSub>(instruction, Primitive::kPrimDouble);
+ Binop_12x<HSub>(instruction, Primitive::kPrimDouble, dex_pc);
break;
}
case Instruction::MUL_INT_2ADDR: {
- Binop_12x<HMul>(instruction, Primitive::kPrimInt);
+ Binop_12x<HMul>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::MUL_LONG_2ADDR: {
- Binop_12x<HMul>(instruction, Primitive::kPrimLong);
+ Binop_12x<HMul>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::MUL_FLOAT_2ADDR: {
- Binop_12x<HMul>(instruction, Primitive::kPrimFloat);
+ Binop_12x<HMul>(instruction, Primitive::kPrimFloat, dex_pc);
break;
}
case Instruction::MUL_DOUBLE_2ADDR: {
- Binop_12x<HMul>(instruction, Primitive::kPrimDouble);
+ Binop_12x<HMul>(instruction, Primitive::kPrimDouble, dex_pc);
break;
}
@@ -2257,32 +2254,32 @@
}
case Instruction::SHL_INT_2ADDR: {
- Binop_12x_shift<HShl>(instruction, Primitive::kPrimInt);
+ Binop_12x_shift<HShl>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::SHL_LONG_2ADDR: {
- Binop_12x_shift<HShl>(instruction, Primitive::kPrimLong);
+ Binop_12x_shift<HShl>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::SHR_INT_2ADDR: {
- Binop_12x_shift<HShr>(instruction, Primitive::kPrimInt);
+ Binop_12x_shift<HShr>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::SHR_LONG_2ADDR: {
- Binop_12x_shift<HShr>(instruction, Primitive::kPrimLong);
+ Binop_12x_shift<HShr>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::USHR_INT_2ADDR: {
- Binop_12x_shift<HUShr>(instruction, Primitive::kPrimInt);
+ Binop_12x_shift<HUShr>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::USHR_LONG_2ADDR: {
- Binop_12x_shift<HUShr>(instruction, Primitive::kPrimLong);
+ Binop_12x_shift<HUShr>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
@@ -2297,92 +2294,92 @@
}
case Instruction::AND_INT_2ADDR: {
- Binop_12x<HAnd>(instruction, Primitive::kPrimInt);
+ Binop_12x<HAnd>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::AND_LONG_2ADDR: {
- Binop_12x<HAnd>(instruction, Primitive::kPrimLong);
+ Binop_12x<HAnd>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::OR_INT_2ADDR: {
- Binop_12x<HOr>(instruction, Primitive::kPrimInt);
+ Binop_12x<HOr>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::OR_LONG_2ADDR: {
- Binop_12x<HOr>(instruction, Primitive::kPrimLong);
+ Binop_12x<HOr>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::XOR_INT_2ADDR: {
- Binop_12x<HXor>(instruction, Primitive::kPrimInt);
+ Binop_12x<HXor>(instruction, Primitive::kPrimInt, dex_pc);
break;
}
case Instruction::XOR_LONG_2ADDR: {
- Binop_12x<HXor>(instruction, Primitive::kPrimLong);
+ Binop_12x<HXor>(instruction, Primitive::kPrimLong, dex_pc);
break;
}
case Instruction::ADD_INT_LIT16: {
- Binop_22s<HAdd>(instruction, false);
+ Binop_22s<HAdd>(instruction, false, dex_pc);
break;
}
case Instruction::AND_INT_LIT16: {
- Binop_22s<HAnd>(instruction, false);
+ Binop_22s<HAnd>(instruction, false, dex_pc);
break;
}
case Instruction::OR_INT_LIT16: {
- Binop_22s<HOr>(instruction, false);
+ Binop_22s<HOr>(instruction, false, dex_pc);
break;
}
case Instruction::XOR_INT_LIT16: {
- Binop_22s<HXor>(instruction, false);
+ Binop_22s<HXor>(instruction, false, dex_pc);
break;
}
case Instruction::RSUB_INT: {
- Binop_22s<HSub>(instruction, true);
+ Binop_22s<HSub>(instruction, true, dex_pc);
break;
}
case Instruction::MUL_INT_LIT16: {
- Binop_22s<HMul>(instruction, false);
+ Binop_22s<HMul>(instruction, false, dex_pc);
break;
}
case Instruction::ADD_INT_LIT8: {
- Binop_22b<HAdd>(instruction, false);
+ Binop_22b<HAdd>(instruction, false, dex_pc);
break;
}
case Instruction::AND_INT_LIT8: {
- Binop_22b<HAnd>(instruction, false);
+ Binop_22b<HAnd>(instruction, false, dex_pc);
break;
}
case Instruction::OR_INT_LIT8: {
- Binop_22b<HOr>(instruction, false);
+ Binop_22b<HOr>(instruction, false, dex_pc);
break;
}
case Instruction::XOR_INT_LIT8: {
- Binop_22b<HXor>(instruction, false);
+ Binop_22b<HXor>(instruction, false, dex_pc);
break;
}
case Instruction::RSUB_INT_LIT8: {
- Binop_22b<HSub>(instruction, true);
+ Binop_22b<HSub>(instruction, true, dex_pc);
break;
}
case Instruction::MUL_INT_LIT8: {
- Binop_22b<HMul>(instruction, false);
+ Binop_22b<HMul>(instruction, false, dex_pc);
break;
}
@@ -2401,17 +2398,17 @@
}
case Instruction::SHL_INT_LIT8: {
- Binop_22b<HShl>(instruction, false);
+ Binop_22b<HShl>(instruction, false, dex_pc);
break;
}
case Instruction::SHR_INT_LIT8: {
- Binop_22b<HShr>(instruction, false);
+ Binop_22b<HShr>(instruction, false, dex_pc);
break;
}
case Instruction::USHR_INT_LIT8: {
- Binop_22b<HUShr>(instruction, false);
+ Binop_22b<HUShr>(instruction, false, dex_pc);
break;
}
@@ -2419,9 +2416,9 @@
uint16_t type_index = instruction.VRegB_21c();
if (compiler_driver_->IsStringTypeIndex(type_index, dex_file_)) {
int32_t register_index = instruction.VRegA();
- HFakeString* fake_string = new (arena_) HFakeString();
+ HFakeString* fake_string = new (arena_) HFakeString(dex_pc);
current_block_->AddInstruction(fake_string);
- UpdateLocal(register_index, fake_string);
+ UpdateLocal(register_index, fake_string, dex_pc);
} else {
QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
? kQuickAllocObjectWithAccessCheck
@@ -2433,14 +2430,14 @@
type_index,
*dex_compilation_unit_->GetDexFile(),
entrypoint));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
}
break;
}
case Instruction::NEW_ARRAY: {
uint16_t type_index = instruction.VRegC_22c();
- HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt);
+ HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt, dex_pc);
QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
? kQuickAllocArrayWithAccessCheck
: kQuickAllocArray;
@@ -2450,7 +2447,7 @@
type_index,
*dex_compilation_unit_->GetDexFile(),
entrypoint));
- UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
+ UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction(), dex_pc);
break;
}
@@ -2490,7 +2487,7 @@
// FilledNewArray, the local needs to be updated after the array was
// filled, otherwise we might overwrite an input vreg.
HStoreLocal* update_local =
- new (arena_) HStoreLocal(GetLocalAt(instruction.VRegA()), latest_result_);
+ new (arena_) HStoreLocal(GetLocalAt(instruction.VRegA()), latest_result_, dex_pc);
HBasicBlock* block = latest_result_->GetBlock();
if (block == current_block_) {
// MoveResult and the previous instruction are in the same block.
@@ -2620,27 +2617,27 @@
ARRAY_XX(_SHORT, Primitive::kPrimShort);
case Instruction::ARRAY_LENGTH: {
- HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot);
+ HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot, dex_pc);
// No need for a temporary for the null check, it is the only input of the following
// instruction.
object = new (arena_) HNullCheck(object, dex_pc);
current_block_->AddInstruction(object);
- current_block_->AddInstruction(new (arena_) HArrayLength(object));
- UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction());
+ current_block_->AddInstruction(new (arena_) HArrayLength(object, dex_pc));
+ UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction(), dex_pc);
break;
}
case Instruction::CONST_STRING: {
current_block_->AddInstruction(
new (arena_) HLoadString(graph_->GetCurrentMethod(), instruction.VRegB_21c(), dex_pc));
- UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
+ UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
break;
}
case Instruction::CONST_STRING_JUMBO: {
current_block_->AddInstruction(
new (arena_) HLoadString(graph_->GetCurrentMethod(), instruction.VRegB_31c(), dex_pc));
- UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction());
+ UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction(), dex_pc);
break;
}
@@ -2666,19 +2663,19 @@
*dex_compilation_unit_->GetDexFile(),
IsOutermostCompilingClass(type_index),
dex_pc));
- UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
+ UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
break;
}
case Instruction::MOVE_EXCEPTION: {
- current_block_->AddInstruction(new (arena_) HLoadException());
- UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction());
- current_block_->AddInstruction(new (arena_) HClearException());
+ current_block_->AddInstruction(new (arena_) HLoadException(dex_pc));
+ UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction(), dex_pc);
+ current_block_->AddInstruction(new (arena_) HClearException(dex_pc));
break;
}
case Instruction::THROW: {
- HInstruction* exception = LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot);
+ HInstruction* exception = LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc);
current_block_->AddInstruction(new (arena_) HThrow(exception, dex_pc));
// A throw instruction must branch to the exit block.
current_block_->AddSuccessor(exit_block_);
@@ -2709,7 +2706,7 @@
case Instruction::MONITOR_ENTER: {
current_block_->AddInstruction(new (arena_) HMonitorOperation(
- LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot),
+ LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc),
HMonitorOperation::kEnter,
dex_pc));
break;
@@ -2717,7 +2714,7 @@
case Instruction::MONITOR_EXIT: {
current_block_->AddInstruction(new (arena_) HMonitorOperation(
- LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot),
+ LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc),
HMonitorOperation::kExit,
dex_pc));
break;
@@ -2748,14 +2745,18 @@
return locals_.Get(register_index);
}
-void HGraphBuilder::UpdateLocal(int register_index, HInstruction* instruction) const {
+void HGraphBuilder::UpdateLocal(int register_index,
+ HInstruction* instruction,
+ uint32_t dex_pc) const {
HLocal* local = GetLocalAt(register_index);
- current_block_->AddInstruction(new (arena_) HStoreLocal(local, instruction));
+ current_block_->AddInstruction(new (arena_) HStoreLocal(local, instruction, dex_pc));
}
-HInstruction* HGraphBuilder::LoadLocal(int register_index, Primitive::Type type) const {
+HInstruction* HGraphBuilder::LoadLocal(int register_index,
+ Primitive::Type type,
+ uint32_t dex_pc) const {
HLocal* local = GetLocalAt(register_index);
- current_block_->AddInstruction(new (arena_) HLoadLocal(local, type));
+ current_block_->AddInstruction(new (arena_) HLoadLocal(local, type, dex_pc));
return current_block_->GetLastInstruction();
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 560ed86..b0238dc 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -131,23 +131,20 @@
void InitializeLocals(uint16_t count);
HLocal* GetLocalAt(int register_index) const;
- void UpdateLocal(int register_index, HInstruction* instruction) const;
- HInstruction* LoadLocal(int register_index, Primitive::Type type) const;
+ void UpdateLocal(int register_index, HInstruction* instruction, uint32_t dex_pc) const;
+ HInstruction* LoadLocal(int register_index, Primitive::Type type, uint32_t dex_pc) const;
void PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc);
void InitializeParameters(uint16_t number_of_parameters);
bool NeedsAccessCheck(uint32_t type_index) const;
template<typename T>
- void Unop_12x(const Instruction& instruction, Primitive::Type type);
-
- template<typename T>
- void Binop_23x(const Instruction& instruction, Primitive::Type type);
+ void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
template<typename T>
void Binop_23x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
template<typename T>
- void Binop_23x_shift(const Instruction& instruction, Primitive::Type type);
+ void Binop_23x_shift(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
void Binop_23x_cmp(const Instruction& instruction,
Primitive::Type type,
@@ -155,19 +152,16 @@
uint32_t dex_pc);
template<typename T>
- void Binop_12x(const Instruction& instruction, Primitive::Type type);
-
- template<typename T>
void Binop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
template<typename T>
- void Binop_12x_shift(const Instruction& instruction, Primitive::Type type);
+ void Binop_12x_shift(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
template<typename T>
- void Binop_22b(const Instruction& instruction, bool reverse);
+ void Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc);
template<typename T>
- void Binop_22s(const Instruction& instruction, bool reverse);
+ void Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc);
template<typename T> void If_21t(const Instruction& instruction, uint32_t dex_pc);
template<typename T> void If_22t(const Instruction& instruction, uint32_t dex_pc);
@@ -185,7 +179,7 @@
bool second_is_lit,
bool is_div);
- void BuildReturn(const Instruction& instruction, Primitive::Type type);
+ void BuildReturn(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
// Builds an instance field access node and returns whether the instruction is supported.
bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index f5c4498..0bb90b2 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -129,12 +129,12 @@
}
size_t CodeGenerator::GetCacheOffset(uint32_t index) {
- return mirror::ObjectArray<mirror::Object>::OffsetOfElement(index).SizeValue();
+ return sizeof(GcRoot<mirror::Object>) * index;
}
size_t CodeGenerator::GetCachePointerOffset(uint32_t index) {
auto pointer_size = InstructionSetPointerSize(GetInstructionSet());
- return mirror::Array::DataOffset(pointer_size).Uint32Value() + pointer_size * index;
+ return pointer_size * index;
}
void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) {
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 0640179..a4c58b0 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -48,7 +48,7 @@
// with baseline.
static constexpr Register kCoreSavedRegisterForBaseline = R5;
static constexpr Register kCoreCalleeSaves[] =
- { R5, R6, R7, R8, R10, R11, PC };
+ { R5, R6, R7, R8, R10, R11, LR };
static constexpr SRegister kFpuCalleeSaves[] =
{ S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31 };
@@ -409,8 +409,8 @@
method_patches_(MethodReferenceComparator(), graph->GetArena()->Adapter()),
call_patches_(MethodReferenceComparator(), graph->GetArena()->Adapter()),
relative_call_patches_(graph->GetArena()->Adapter()) {
- // Save the PC register to mimic Quick.
- AddAllocatedRegister(Location::RegisterLocation(PC));
+ // Always save the LR register to mimic Quick.
+ AddAllocatedRegister(Location::RegisterLocation(LR));
}
void CodeGeneratorARM::Finalize(CodeAllocator* allocator) {
@@ -599,12 +599,9 @@
RecordPcInfo(nullptr, 0);
}
- // PC is in the list of callee-save to mimic Quick, but we need to push
- // LR at entry instead.
- uint32_t push_mask = (core_spill_mask_ & (~(1 << PC))) | 1 << LR;
- __ PushList(push_mask);
- __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(push_mask));
- __ cfi().RelOffsetForMany(DWARFReg(kMethodRegisterArgument), 0, push_mask, kArmWordSize);
+ __ PushList(core_spill_mask_);
+ __ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
+ __ cfi().RelOffsetForMany(DWARFReg(kMethodRegisterArgument), 0, core_spill_mask_, kArmWordSize);
if (fpu_spill_mask_ != 0) {
SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_));
__ vpushs(start_register, POPCOUNT(fpu_spill_mask_));
@@ -632,7 +629,10 @@
__ cfi().AdjustCFAOffset(-kArmPointerSize * POPCOUNT(fpu_spill_mask_));
__ cfi().RestoreMany(DWARFReg(SRegister(0)), fpu_spill_mask_);
}
- __ PopList(core_spill_mask_);
+ // Pop LR into PC to return.
+ DCHECK_NE(core_spill_mask_ & (1 << LR), 0U);
+ uint32_t pop_mask = (core_spill_mask_ & (~(1 << LR))) | 1 << PC;
+ __ PopList(pop_mask);
__ cfi().RestoreState();
__ cfi().DefCFAOffset(GetFrameSize());
}
@@ -1560,25 +1560,7 @@
return;
}
- Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
- uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
- invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
- LocationSummary* locations = invoke->GetLocations();
- Location receiver = locations->InAt(0);
- uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
- // temp = object->GetClass();
- DCHECK(receiver.IsRegister());
- __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- __ MaybeUnpoisonHeapReference(temp);
- // temp = temp->GetMethodAt(method_offset);
- uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
- kArmWordSize).Int32Value();
- __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
- // LR = temp->GetEntryPoint();
- __ LoadFromOffset(kLoadWord, LR, temp, entry_point);
- // LR();
- __ blx(LR);
+ codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
@@ -4224,9 +4206,9 @@
__ LoadFromOffset(kLoadWord,
out,
current_method,
- ArtMethod::DexCacheResolvedTypesOffset().Int32Value());
+ ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value());
__ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
- __ MaybeUnpoisonHeapReference(out);
+ // TODO: We will need a read barrier here.
SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM(
cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
@@ -4286,9 +4268,8 @@
__ LoadFromOffset(
kLoadWord, out, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
__ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
- __ MaybeUnpoisonHeapReference(out);
__ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
- __ MaybeUnpoisonHeapReference(out);
+ // TODO: We will need a read barrier here.
__ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
}
@@ -4564,7 +4545,8 @@
}
// temp = current_method->dex_cache_resolved_methods_;
__ LoadFromOffset(
- kLoadWord, reg, method_reg, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
+ kLoadWord, reg, method_reg, ArtMethod::DexCacheResolvedMethodsOffset(
+ kArmPointerSize).Int32Value());
// temp = temp[index_in_cache]
uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
__ LoadFromOffset(kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
@@ -4607,6 +4589,28 @@
DCHECK(!IsLeafMethod());
}
+void CodeGeneratorARM::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) {
+ Register temp = temp_location.AsRegister<Register>();
+ uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+ invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
+ LocationSummary* locations = invoke->GetLocations();
+ Location receiver = locations->InAt(0);
+ uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+ // temp = object->GetClass();
+ DCHECK(receiver.IsRegister());
+ __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
+ MaybeRecordImplicitNullCheck(invoke);
+ __ MaybeUnpoisonHeapReference(temp);
+ // temp = temp->GetMethodAt(method_offset);
+ uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArmWordSize).Int32Value();
+ __ LoadFromOffset(kLoadWord, temp, temp, method_offset);
+ // LR = temp->GetEntryPoint();
+ __ LoadFromOffset(kLoadWord, LR, temp, entry_point);
+ // LR();
+ __ blx(LR);
+}
+
void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size = method_patches_.size() + call_patches_.size() + relative_call_patches_.size();
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 9528cca..4a0df4e 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -327,6 +327,7 @@
Label* GetFrameEntryLabel() { return &frame_entry_label_; }
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
+ void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp);
void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 8035461..6b1457b 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2433,8 +2433,9 @@
}
// temp = current_method->dex_cache_resolved_methods_;
- __ Ldr(reg.W(), MemOperand(method_reg.X(),
- ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+ __ Ldr(reg.X(),
+ MemOperand(method_reg.X(),
+ ArtMethod::DexCacheResolvedMethodsOffset(kArm64WordSize).Int32Value()));
// temp = temp[index_in_cache];
uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
__ Ldr(reg.X(), MemOperand(reg.X(), GetCachePointerOffset(index_in_cache)));
@@ -2473,6 +2474,29 @@
DCHECK(!IsLeafMethod());
}
+void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_in) {
+ LocationSummary* locations = invoke->GetLocations();
+ Location receiver = locations->InAt(0);
+ Register temp = XRegisterFrom(temp_in);
+ size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+ invoke->GetVTableIndex(), kArm64PointerSize).SizeValue();
+ Offset class_offset = mirror::Object::ClassOffset();
+ Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize);
+
+ BlockPoolsScope block_pools(GetVIXLAssembler());
+
+ DCHECK(receiver.IsRegister());
+ __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset));
+ MaybeRecordImplicitNullCheck(invoke);
+ GetAssembler()->MaybeUnpoisonHeapReference(temp.W());
+ // temp = temp->GetMethodAt(method_offset);
+ __ Ldr(temp, MemOperand(temp, method_offset));
+ // lr = temp->GetEntryPoint();
+ __ Ldr(lr, MemOperand(temp, entry_point.SizeValue()));
+ // lr();
+ __ Blr(lr);
+}
+
void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
@@ -2566,26 +2590,7 @@
return;
}
- LocationSummary* locations = invoke->GetLocations();
- Location receiver = locations->InAt(0);
- Register temp = XRegisterFrom(invoke->GetLocations()->GetTemp(0));
- size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
- invoke->GetVTableIndex(), kArm64PointerSize).SizeValue();
- Offset class_offset = mirror::Object::ClassOffset();
- Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize);
-
- BlockPoolsScope block_pools(GetVIXLAssembler());
-
- DCHECK(receiver.IsRegister());
- __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset));
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- GetAssembler()->MaybeUnpoisonHeapReference(temp.W());
- // temp = temp->GetMethodAt(method_offset);
- __ Ldr(temp, MemOperand(temp, method_offset));
- // lr = temp->GetEntryPoint();
- __ Ldr(lr, MemOperand(temp, entry_point.SizeValue()));
- // lr();
- __ Blr(lr);
+ codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
@@ -2607,9 +2612,10 @@
__ Ldr(out, MemOperand(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
} else {
DCHECK(cls->CanCallRuntime());
- __ Ldr(out, MemOperand(current_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
- __ Ldr(out, HeapOperand(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
- GetAssembler()->MaybeUnpoisonHeapReference(out.W());
+ MemberOffset resolved_types_offset = ArtMethod::DexCacheResolvedTypesOffset(kArm64PointerSize);
+ __ Ldr(out.X(), MemOperand(current_method, resolved_types_offset.Int32Value()));
+ __ Ldr(out, MemOperand(out.X(), CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+ // TODO: We will need a read barrier here.
SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64(
cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
@@ -2668,10 +2674,9 @@
Register out = OutputRegister(load);
Register current_method = InputRegisterAt(load, 0);
__ Ldr(out, MemOperand(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
- __ Ldr(out, HeapOperand(out, mirror::Class::DexCacheStringsOffset()));
- GetAssembler()->MaybeUnpoisonHeapReference(out.W());
- __ Ldr(out, HeapOperand(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
- GetAssembler()->MaybeUnpoisonHeapReference(out.W());
+ __ Ldr(out.X(), HeapOperand(out, mirror::Class::DexCacheStringsOffset()));
+ __ Ldr(out, MemOperand(out.X(), CodeGenerator::GetCacheOffset(load->GetStringIndex())));
+ // TODO: We will need a read barrier here.
__ Cbz(out, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
}
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 18070fc..12ead7e 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -359,6 +359,7 @@
}
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
+ void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp);
void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index e4188e4..10942ef 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -2426,10 +2426,10 @@
}
// temp = temp->dex_cache_resolved_methods_;
- __ LoadFromOffset(kLoadUnsignedWord,
+ __ LoadFromOffset(kLoadDoubleword,
reg,
method_reg,
- ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
+ ArtMethod::DexCacheResolvedMethodsOffset(kMips64PointerSize).Int32Value());
// temp = temp[index_in_cache]
uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
__ LoadFromOffset(kLoadDoubleword,
@@ -2530,9 +2530,10 @@
ArtMethod::DeclaringClassOffset().Int32Value());
} else {
DCHECK(cls->CanCallRuntime());
- __ LoadFromOffset(kLoadUnsignedWord, out, current_method,
- ArtMethod::DexCacheResolvedTypesOffset().Int32Value());
+ __ LoadFromOffset(kLoadDoubleword, out, current_method,
+ ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value());
__ LoadFromOffset(kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+ // TODO: We will need a read barrier here.
SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64(
cls,
cls,
@@ -2595,8 +2596,9 @@
GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>();
__ LoadFromOffset(kLoadUnsignedWord, out, current_method,
ArtMethod::DeclaringClassOffset().Int32Value());
- __ LoadFromOffset(kLoadUnsignedWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
+ __ LoadFromOffset(kLoadDoubleword, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
__ LoadFromOffset(kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
+ // TODO: We will need a read barrier here.
__ Beqzc(out, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
}
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index e8aa61d..a5ad226 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -19,6 +19,7 @@
#include "art_method.h"
#include "code_generator_utils.h"
#include "compiled_method.h"
+#include "constant_area_fixups_x86.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "gc/accounting/card_table.h"
@@ -1548,23 +1549,11 @@
}
void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) {
- Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
- uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
- invoke->GetVTableIndex(), kX86PointerSize).Uint32Value();
- LocationSummary* locations = invoke->GetLocations();
- Location receiver = locations->InAt(0);
- uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
- // temp = object->GetClass();
- DCHECK(receiver.IsRegister());
- __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset));
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- __ MaybeUnpoisonHeapReference(temp);
- // temp = temp->GetMethodAt(method_offset);
- __ movl(temp, Address(temp, method_offset));
- // call temp->GetEntryPoint();
- __ call(Address(
- temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+ if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ return;
+ }
+ codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
@@ -2213,7 +2202,7 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::Any());
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -2275,6 +2264,16 @@
case Primitive::kPrimFloat: {
if (second.IsFpuRegister()) {
__ addss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ } else if (add->InputAt(1)->IsX86LoadFromConstantTable()) {
+ HX86LoadFromConstantTable* const_area = add->InputAt(1)->AsX86LoadFromConstantTable();
+ DCHECK(!const_area->NeedsMaterialization());
+ __ addss(first.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralFloatAddress(
+ const_area->GetConstant()->AsFloatConstant()->GetValue(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ } else {
+ DCHECK(second.IsStackSlot());
+ __ addss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
}
break;
}
@@ -2282,6 +2281,16 @@
case Primitive::kPrimDouble: {
if (second.IsFpuRegister()) {
__ addsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ } else if (add->InputAt(1)->IsX86LoadFromConstantTable()) {
+ HX86LoadFromConstantTable* const_area = add->InputAt(1)->AsX86LoadFromConstantTable();
+ DCHECK(!const_area->NeedsMaterialization());
+ __ addsd(first.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralDoubleAddress(
+ const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ } else {
+ DCHECK(second.IsDoubleStackSlot());
+ __ addsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
}
break;
}
@@ -2305,7 +2314,7 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::Any());
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -2351,12 +2360,36 @@
}
case Primitive::kPrimFloat: {
- __ subss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ if (second.IsFpuRegister()) {
+ __ subss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ } else if (sub->InputAt(1)->IsX86LoadFromConstantTable()) {
+ HX86LoadFromConstantTable* const_area = sub->InputAt(1)->AsX86LoadFromConstantTable();
+ DCHECK(!const_area->NeedsMaterialization());
+ __ subss(first.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralFloatAddress(
+ const_area->GetConstant()->AsFloatConstant()->GetValue(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ } else {
+ DCHECK(second.IsStackSlot());
+ __ subss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+ }
break;
}
case Primitive::kPrimDouble: {
- __ subsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ if (second.IsFpuRegister()) {
+ __ subsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ } else if (sub->InputAt(1)->IsX86LoadFromConstantTable()) {
+ HX86LoadFromConstantTable* const_area = sub->InputAt(1)->AsX86LoadFromConstantTable();
+ DCHECK(!const_area->NeedsMaterialization());
+ __ subsd(first.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralDoubleAddress(
+ const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ } else {
+ DCHECK(second.IsDoubleStackSlot());
+ __ subsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+ }
break;
}
@@ -2391,7 +2424,7 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::Any());
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -2507,12 +2540,38 @@
}
case Primitive::kPrimFloat: {
- __ mulss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ DCHECK(first.Equals(locations->Out()));
+ if (second.IsFpuRegister()) {
+ __ mulss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ } else if (mul->InputAt(1)->IsX86LoadFromConstantTable()) {
+ HX86LoadFromConstantTable* const_area = mul->InputAt(1)->AsX86LoadFromConstantTable();
+ DCHECK(!const_area->NeedsMaterialization());
+ __ mulss(first.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralFloatAddress(
+ const_area->GetConstant()->AsFloatConstant()->GetValue(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ } else {
+ DCHECK(second.IsStackSlot());
+ __ mulss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+ }
break;
}
case Primitive::kPrimDouble: {
- __ mulsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ DCHECK(first.Equals(locations->Out()));
+ if (second.IsFpuRegister()) {
+ __ mulsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ } else if (mul->InputAt(1)->IsX86LoadFromConstantTable()) {
+ HX86LoadFromConstantTable* const_area = mul->InputAt(1)->AsX86LoadFromConstantTable();
+ DCHECK(!const_area->NeedsMaterialization());
+ __ mulsd(first.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralDoubleAddress(
+ const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ } else {
+ DCHECK(second.IsDoubleStackSlot());
+ __ mulsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+ }
break;
}
@@ -2855,7 +2914,7 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::Any());
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -2867,7 +2926,6 @@
void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) {
LocationSummary* locations = div->GetLocations();
- Location out = locations->Out();
Location first = locations->InAt(0);
Location second = locations->InAt(1);
@@ -2879,14 +2937,36 @@
}
case Primitive::kPrimFloat: {
- DCHECK(first.Equals(out));
- __ divss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ if (second.IsFpuRegister()) {
+ __ divss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ } else if (div->InputAt(1)->IsX86LoadFromConstantTable()) {
+ HX86LoadFromConstantTable* const_area = div->InputAt(1)->AsX86LoadFromConstantTable();
+ DCHECK(!const_area->NeedsMaterialization());
+ __ divss(first.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralFloatAddress(
+ const_area->GetConstant()->AsFloatConstant()->GetValue(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ } else {
+ DCHECK(second.IsStackSlot());
+ __ divss(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+ }
break;
}
case Primitive::kPrimDouble: {
- DCHECK(first.Equals(out));
- __ divsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ if (second.IsFpuRegister()) {
+ __ divsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
+ } else if (div->InputAt(1)->IsX86LoadFromConstantTable()) {
+ HX86LoadFromConstantTable* const_area = div->InputAt(1)->AsX86LoadFromConstantTable();
+ DCHECK(!const_area->NeedsMaterialization());
+ __ divsd(first.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralDoubleAddress(
+ const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ } else {
+ DCHECK(second.IsDoubleStackSlot());
+ __ divsd(first.AsFpuRegister<XmmRegister>(), Address(ESP, second.GetStackIndex()));
+ }
break;
}
@@ -3534,7 +3614,8 @@
__ movl(reg, Address(ESP, kCurrentMethodStackOffset));
}
// temp = temp->dex_cache_resolved_methods_;
- __ movl(reg, Address(method_reg, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+ __ movl(reg, Address(method_reg,
+ ArtMethod::DexCacheResolvedMethodsOffset(kX86PointerSize).Int32Value()));
// temp = temp[index_in_cache]
uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
__ movl(reg, Address(reg, CodeGenerator::GetCachePointerOffset(index_in_cache)));
@@ -3569,6 +3650,25 @@
DCHECK(!IsLeafMethod());
}
+void CodeGeneratorX86::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_in) {
+ Register temp = temp_in.AsRegister<Register>();
+ uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+ invoke->GetVTableIndex(), kX86PointerSize).Uint32Value();
+ LocationSummary* locations = invoke->GetLocations();
+ Location receiver = locations->InAt(0);
+ uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+ // temp = object->GetClass();
+ DCHECK(receiver.IsRegister());
+ __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset));
+ MaybeRecordImplicitNullCheck(invoke);
+ __ MaybeUnpoisonHeapReference(temp);
+ // temp = temp->GetMethodAt(method_offset);
+ __ movl(temp, Address(temp, method_offset));
+ // call temp->GetEntryPoint();
+ __ call(Address(
+ temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+}
+
void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
linker_patches->reserve(method_patches_.size() + relative_call_patches_.size());
@@ -4708,9 +4808,9 @@
} else {
DCHECK(cls->CanCallRuntime());
__ movl(out, Address(
- current_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
+ current_method, ArtMethod::DexCacheResolvedTypesOffset(kX86PointerSize).Int32Value()));
__ movl(out, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
- __ MaybeUnpoisonHeapReference(out);
+ // TODO: We will need a read barrier here.
SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86(
cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
@@ -4768,9 +4868,8 @@
Register current_method = locations->InAt(0).AsRegister<Register>();
__ movl(out, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
__ movl(out, Address(out, mirror::Class::DexCacheStringsOffset().Int32Value()));
- __ MaybeUnpoisonHeapReference(out);
__ movl(out, Address(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
- __ MaybeUnpoisonHeapReference(out);
+ // TODO: We will need a read barrier here.
__ testl(out, out);
__ j(kEqual, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
@@ -5085,6 +5184,245 @@
// Will be generated at use site.
}
+void LocationsBuilderX86::VisitX86ComputeBaseMethodAddress(
+ HX86ComputeBaseMethodAddress* insn) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(insn, LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86::VisitX86ComputeBaseMethodAddress(
+ HX86ComputeBaseMethodAddress* insn) {
+ LocationSummary* locations = insn->GetLocations();
+ Register reg = locations->Out().AsRegister<Register>();
+
+ // Generate call to next instruction.
+ Label next_instruction;
+ __ call(&next_instruction);
+ __ Bind(&next_instruction);
+
+ // Remember this offset for later use with constant area.
+ codegen_->SetMethodAddressOffset(GetAssembler()->CodeSize());
+
+ // Grab the return address off the stack.
+ __ popl(reg);
+}
+
+void LocationsBuilderX86::VisitX86LoadFromConstantTable(
+ HX86LoadFromConstantTable* insn) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(insn, LocationSummary::kNoCall);
+
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::ConstantLocation(insn->GetConstant()));
+
+ // If we don't need to be materialized, we only need the inputs to be set.
+ if (!insn->NeedsMaterialization()) {
+ return;
+ }
+
+ switch (insn->GetType()) {
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+
+ case Primitive::kPrimInt:
+ locations->SetOut(Location::RequiresRegister());
+ break;
+
+ default:
+ LOG(FATAL) << "Unsupported x86 constant area type " << insn->GetType();
+ }
+}
+
+void InstructionCodeGeneratorX86::VisitX86LoadFromConstantTable(HX86LoadFromConstantTable* insn) {
+ if (!insn->NeedsMaterialization()) {
+ return;
+ }
+
+ LocationSummary* locations = insn->GetLocations();
+ Location out = locations->Out();
+ Register const_area = locations->InAt(0).AsRegister<Register>();
+ HConstant *value = insn->GetConstant();
+
+ switch (insn->GetType()) {
+ case Primitive::kPrimFloat:
+ __ movss(out.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralFloatAddress(value->AsFloatConstant()->GetValue(), const_area));
+ break;
+
+ case Primitive::kPrimDouble:
+ __ movsd(out.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralDoubleAddress(value->AsDoubleConstant()->GetValue(), const_area));
+ break;
+
+ case Primitive::kPrimInt:
+ __ movl(out.AsRegister<Register>(),
+ codegen_->LiteralInt32Address(value->AsIntConstant()->GetValue(), const_area));
+ break;
+
+ default:
+ LOG(FATAL) << "Unsupported x86 constant area type " << insn->GetType();
+ }
+}
+
+void CodeGeneratorX86::Finalize(CodeAllocator* allocator) {
+ // Generate the constant area if needed.
+ X86Assembler* assembler = GetAssembler();
+ if (!assembler->IsConstantAreaEmpty()) {
+ // Align to 4 byte boundary to reduce cache misses, as the data is 4 and 8
+ // byte values.
+ assembler->Align(4, 0);
+ constant_area_start_ = assembler->CodeSize();
+ assembler->AddConstantArea();
+ }
+
+ // And finish up.
+ CodeGenerator::Finalize(allocator);
+}
+
+/**
+ * Class to handle late fixup of offsets into constant area.
+ */
+class RIPFixup : public AssemblerFixup, public ArenaObject<kArenaAllocMisc> {
+ public:
+ RIPFixup(const CodeGeneratorX86& codegen, int offset)
+ : codegen_(codegen), offset_into_constant_area_(offset) {}
+
+ private:
+ void Process(const MemoryRegion& region, int pos) OVERRIDE {
+ // Patch the correct offset for the instruction. The place to patch is the
+ // last 4 bytes of the instruction.
+ // The value to patch is the distance from the offset in the constant area
+ // from the address computed by the HX86ComputeBaseMethodAddress instruction.
+ int32_t constant_offset = codegen_.ConstantAreaStart() + offset_into_constant_area_;
+ int32_t relative_position = constant_offset - codegen_.GetMethodAddressOffset();;
+
+ // Patch in the right value.
+ region.StoreUnaligned<int32_t>(pos - 4, relative_position);
+ }
+
+ const CodeGeneratorX86& codegen_;
+
+ // Location in constant area that the fixup refers to.
+ int offset_into_constant_area_;
+};
+
+Address CodeGeneratorX86::LiteralDoubleAddress(double v, Register reg) {
+ AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddDouble(v));
+ return Address(reg, kDummy32BitOffset, fixup);
+}
+
+Address CodeGeneratorX86::LiteralFloatAddress(float v, Register reg) {
+ AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddFloat(v));
+ return Address(reg, kDummy32BitOffset, fixup);
+}
+
+Address CodeGeneratorX86::LiteralInt32Address(int32_t v, Register reg) {
+ AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddInt32(v));
+ return Address(reg, kDummy32BitOffset, fixup);
+}
+
+Address CodeGeneratorX86::LiteralInt64Address(int64_t v, Register reg) {
+ AssemblerFixup* fixup = new (GetGraph()->GetArena()) RIPFixup(*this, __ AddInt64(v));
+ return Address(reg, kDummy32BitOffset, fixup);
+}
+
+/**
+ * Finds instructions that need the constant area base as an input.
+ */
+class ConstantHandlerVisitor : public HGraphVisitor {
+ public:
+ explicit ConstantHandlerVisitor(HGraph* graph) : HGraphVisitor(graph), base_(nullptr) {}
+
+ private:
+ void VisitAdd(HAdd* add) OVERRIDE {
+ BinaryFP(add);
+ }
+
+ void VisitSub(HSub* sub) OVERRIDE {
+ BinaryFP(sub);
+ }
+
+ void VisitMul(HMul* mul) OVERRIDE {
+ BinaryFP(mul);
+ }
+
+ void VisitDiv(HDiv* div) OVERRIDE {
+ BinaryFP(div);
+ }
+
+ void VisitReturn(HReturn* ret) OVERRIDE {
+ HConstant* value = ret->InputAt(0)->AsConstant();
+ if ((value != nullptr && Primitive::IsFloatingPointType(value->GetType()))) {
+ ReplaceInput(ret, value, 0, true);
+ }
+ }
+
+ void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE {
+ HandleInvoke(invoke);
+ }
+
+ void VisitInvokeVirtual(HInvokeVirtual* invoke) OVERRIDE {
+ HandleInvoke(invoke);
+ }
+
+ void VisitInvokeInterface(HInvokeInterface* invoke) OVERRIDE {
+ HandleInvoke(invoke);
+ }
+
+ void BinaryFP(HBinaryOperation* bin) {
+ HConstant* rhs = bin->InputAt(1)->AsConstant();
+ if (rhs != nullptr && Primitive::IsFloatingPointType(bin->GetResultType())) {
+ ReplaceInput(bin, rhs, 1, false);
+ }
+ }
+
+ void InitializeConstantAreaPointer(HInstruction* user) {
+ // Ensure we only initialize the pointer once.
+ if (base_ != nullptr) {
+ return;
+ }
+
+ HGraph* graph = GetGraph();
+ HBasicBlock* entry = graph->GetEntryBlock();
+ base_ = new (graph->GetArena()) HX86ComputeBaseMethodAddress();
+ HInstruction* insert_pos = (user->GetBlock() == entry) ? user : entry->GetLastInstruction();
+ entry->InsertInstructionBefore(base_, insert_pos);
+ DCHECK(base_ != nullptr);
+ }
+
+ void ReplaceInput(HInstruction* insn, HConstant* value, int input_index, bool materialize) {
+ InitializeConstantAreaPointer(insn);
+ HGraph* graph = GetGraph();
+ HBasicBlock* block = insn->GetBlock();
+ HX86LoadFromConstantTable* load_constant =
+ new (graph->GetArena()) HX86LoadFromConstantTable(base_, value, materialize);
+ block->InsertInstructionBefore(load_constant, insn);
+ insn->ReplaceInput(load_constant, input_index);
+ }
+
+ void HandleInvoke(HInvoke* invoke) {
+ // Ensure that we can load FP arguments from the constant area.
+ for (size_t i = 0, e = invoke->InputCount(); i < e; i++) {
+ HConstant* input = invoke->InputAt(i)->AsConstant();
+ if (input != nullptr && Primitive::IsFloatingPointType(input->GetType())) {
+ ReplaceInput(invoke, input, i, true);
+ }
+ }
+ }
+
+ // The generated HX86ComputeBaseMethodAddress in the entry block needed as an
+ // input to the HX86LoadFromConstantTable instructions.
+ HX86ComputeBaseMethodAddress* base_;
+};
+
+void ConstantAreaFixups::Run() {
+ ConstantHandlerVisitor visitor(graph_);
+ visitor.VisitInsertionOrder();
+}
+
#undef __
} // namespace x86
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 17787a8..bd7cb12 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -294,6 +294,8 @@
// Generate a call to a static or direct method.
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
+ // Generate a call to a virtual method.
+ void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp);
// Emit linker patches.
void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
@@ -325,6 +327,25 @@
return isa_features_;
}
+ void SetMethodAddressOffset(int32_t offset) {
+ method_address_offset_ = offset;
+ }
+
+ int32_t GetMethodAddressOffset() const {
+ return method_address_offset_;
+ }
+
+ int32_t ConstantAreaStart() const {
+ return constant_area_start_;
+ }
+
+ Address LiteralDoubleAddress(double v, Register reg);
+ Address LiteralFloatAddress(float v, Register reg);
+ Address LiteralInt32Address(int32_t v, Register reg);
+ Address LiteralInt64Address(int64_t v, Register reg);
+
+ void Finalize(CodeAllocator* allocator) OVERRIDE;
+
private:
// Labels for each block that will be compiled.
GrowableArray<Label> block_labels_;
@@ -339,6 +360,20 @@
ArenaDeque<MethodPatchInfo<Label>> method_patches_;
ArenaDeque<MethodPatchInfo<Label>> relative_call_patches_;
+ // Offset to the start of the constant area in the assembled code.
+ // Used for fixups to the constant area.
+ int32_t constant_area_start_;
+
+ // If there is a HX86ComputeBaseMethodAddress instruction in the graph
+ // (which shall be the sole instruction of this kind), subtracting this offset
+ // from the value contained in the out register of this HX86ComputeBaseMethodAddress
+ // instruction gives the address of the start of this method.
+ int32_t method_address_offset_;
+
+ // When we don't know the proper offset for the value, we use kDummy32BitOffset.
+ // The correct value will be inserted when processing Assembler fixups.
+ static constexpr int32_t kDummy32BitOffset = 256;
+
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86);
};
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index ff52f4f..0f3eb74 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -439,8 +439,9 @@
__ movq(reg, Address(CpuRegister(RSP), kCurrentMethodStackOffset));
}
// temp = temp->dex_cache_resolved_methods_;
- __ movl(reg, Address(CpuRegister(method_reg),
- ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
+ __ movq(reg,
+ Address(CpuRegister(method_reg),
+ ArtMethod::DexCacheResolvedMethodsOffset(kX86_64PointerSize).SizeValue()));
// temp = temp[index_in_cache]
uint32_t index_in_cache = invoke->GetTargetMethod().dex_method_index;
__ movq(reg, Address(reg, CodeGenerator::GetCachePointerOffset(index_in_cache)));
@@ -474,6 +475,25 @@
DCHECK(!IsLeafMethod());
}
+void CodeGeneratorX86_64::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_in) {
+ CpuRegister temp = temp_in.AsRegister<CpuRegister>();
+ size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+ invoke->GetVTableIndex(), kX86_64PointerSize).SizeValue();
+ LocationSummary* locations = invoke->GetLocations();
+ Location receiver = locations->InAt(0);
+ size_t class_offset = mirror::Object::ClassOffset().SizeValue();
+ // temp = object->GetClass();
+ DCHECK(receiver.IsRegister());
+ __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset));
+ MaybeRecordImplicitNullCheck(invoke);
+ __ MaybeUnpoisonHeapReference(temp);
+ // temp = temp->GetMethodAt(method_offset);
+ __ movq(temp, Address(temp, method_offset));
+ // call temp->GetEntryPoint();
+ __ call(Address(temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kX86_64WordSize).SizeValue()));
+}
+
void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
DCHECK(linker_patches->empty());
size_t size =
@@ -1708,22 +1728,7 @@
return;
}
- CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>();
- size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
- invoke->GetVTableIndex(), kX86_64PointerSize).SizeValue();
- LocationSummary* locations = invoke->GetLocations();
- Location receiver = locations->InAt(0);
- size_t class_offset = mirror::Object::ClassOffset().SizeValue();
- // temp = object->GetClass();
- DCHECK(receiver.IsRegister());
- __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset));
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- __ MaybeUnpoisonHeapReference(temp);
- // temp = temp->GetMethodAt(method_offset);
- __ movq(temp, Address(temp, method_offset));
- // call temp->GetEntryPoint();
- __ call(Address(temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
- kX86_64WordSize).SizeValue()));
+ codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
@@ -4539,10 +4544,10 @@
__ movl(out, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
} else {
DCHECK(cls->CanCallRuntime());
- __ movl(out, Address(
- current_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
+ __ movq(out, Address(
+ current_method, ArtMethod::DexCacheResolvedTypesOffset(kX86_64PointerSize).Int32Value()));
__ movl(out, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
- __ MaybeUnpoisonHeapReference(out);
+ // TODO: We will need a read barrier here.
SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86_64(
cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
@@ -4590,10 +4595,9 @@
CpuRegister out = locations->Out().AsRegister<CpuRegister>();
CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
__ movl(out, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
- __ movl(out, Address(out, mirror::Class::DexCacheStringsOffset().Int32Value()));
- __ MaybeUnpoisonHeapReference(out);
+ __ movq(out, Address(out, mirror::Class::DexCacheStringsOffset().Int32Value()));
__ movl(out, Address(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
- __ MaybeUnpoisonHeapReference(out);
+ // TODO: We will need a read barrier here.
__ testl(out, out);
__ j(kEqual, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 21357be..f9d8e04 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -305,6 +305,7 @@
}
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
+ void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp);
void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
diff --git a/compiler/optimizing/constant_area_fixups_x86.h b/compiler/optimizing/constant_area_fixups_x86.h
new file mode 100644
index 0000000..4138039
--- /dev/null
+++ b/compiler/optimizing/constant_area_fixups_x86.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_CONSTANT_AREA_FIXUPS_X86_H_
+#define ART_COMPILER_OPTIMIZING_CONSTANT_AREA_FIXUPS_X86_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+namespace x86 {
+
+class ConstantAreaFixups : public HOptimization {
+ public:
+ ConstantAreaFixups(HGraph* graph, OptimizingCompilerStats* stats)
+ : HOptimization(graph, "constant_area_fixups_x86", stats) {}
+
+ void Run() OVERRIDE;
+};
+
+} // namespace x86
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_CONSTANT_AREA_FIXUPS_X86_H_
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 8aaec68..3f5a6e7 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -51,14 +51,15 @@
global_depth_(0),
stack_(graph->GetArena()->Adapter()),
scc_(graph->GetArena()->Adapter()),
- map_(std::less<int>(), graph->GetArena()->Adapter()),
- cycle_(std::less<int>(), graph->GetArena()->Adapter()),
- induction_(std::less<int>(), graph->GetArena()->Adapter()) {
+ map_(std::less<HInstruction*>(), graph->GetArena()->Adapter()),
+ cycle_(std::less<HInstruction*>(), graph->GetArena()->Adapter()),
+ induction_(std::less<HLoopInformation*>(), graph->GetArena()->Adapter()) {
}
void HInductionVarAnalysis::Run() {
- // Detects sequence variables (generalized induction variables) during an
- // inner-loop-first traversal of all loops using Gerlek's algorithm.
+ // Detects sequence variables (generalized induction variables) during an inner-loop-first
+ // traversal of all loops using Gerlek's algorithm. The order is only relevant if outer
+ // loops would use induction information of inner loops (not currently done).
for (HPostOrderIterator it_graph(*graph_); !it_graph.Done(); it_graph.Advance()) {
HBasicBlock* graph_block = it_graph.Current();
if (graph_block->IsLoopHeader()) {
@@ -71,38 +72,40 @@
// Find strongly connected components (SSCs) in the SSA graph of this loop using Tarjan's
// algorithm. Due to the descendant-first nature, classification happens "on-demand".
global_depth_ = 0;
- CHECK(stack_.empty());
+ DCHECK(stack_.empty());
map_.clear();
for (HBlocksInLoopIterator it_loop(*loop); !it_loop.Done(); it_loop.Advance()) {
HBasicBlock* loop_block = it_loop.Current();
- CHECK(loop_block->IsInLoop());
+ DCHECK(loop_block->IsInLoop());
if (loop_block->GetLoopInformation() != loop) {
continue; // Inner loops already visited.
}
// Visit phi-operations and instructions.
for (HInstructionIterator it(loop_block->GetPhis()); !it.Done(); it.Advance()) {
HInstruction* instruction = it.Current();
- if (!IsVisitedNode(instruction->GetId())) {
+ if (!IsVisitedNode(instruction)) {
VisitNode(loop, instruction);
}
}
for (HInstructionIterator it(loop_block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* instruction = it.Current();
- if (!IsVisitedNode(instruction->GetId())) {
+ if (!IsVisitedNode(instruction)) {
VisitNode(loop, instruction);
}
}
}
- CHECK(stack_.empty());
+ DCHECK(stack_.empty());
map_.clear();
+
+ // Determine the loop's trip count.
+ VisitControl(loop);
}
void HInductionVarAnalysis::VisitNode(HLoopInformation* loop, HInstruction* instruction) {
- const int id = instruction->GetId();
const uint32_t d1 = ++global_depth_;
- map_.Put(id, NodeInfo(d1));
+ map_.Put(instruction, NodeInfo(d1));
stack_.push_back(instruction);
// Visit all descendants.
@@ -113,7 +116,7 @@
// Lower or found SCC?
if (low < d1) {
- map_.find(id)->second.depth = low;
+ map_.find(instruction)->second.depth = low;
} else {
scc_.clear();
cycle_.clear();
@@ -123,7 +126,7 @@
HInstruction* x = stack_.back();
scc_.push_back(x);
stack_.pop_back();
- map_.find(x->GetId())->second.done = true;
+ map_.find(x)->second.done = true;
if (x == instruction) {
break;
}
@@ -150,12 +153,11 @@
}
// Inspect descendant node.
- const int id = instruction->GetId();
- if (!IsVisitedNode(id)) {
+ if (!IsVisitedNode(instruction)) {
VisitNode(loop, instruction);
- return map_.find(id)->second.depth;
+ return map_.find(instruction)->second.depth;
} else {
- auto it = map_.find(id);
+ auto it = map_.find(instruction);
return it->second.done ? global_depth_ : it->second.depth;
}
}
@@ -176,8 +178,20 @@
} else if (instruction->IsMul()) {
info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
LookupInfo(loop, instruction->InputAt(1)));
+ } else if (instruction->IsShl()) {
+ info = TransferShl(LookupInfo(loop, instruction->InputAt(0)),
+ LookupInfo(loop, instruction->InputAt(1)),
+ instruction->InputAt(0)->GetType());
} else if (instruction->IsNeg()) {
info = TransferNeg(LookupInfo(loop, instruction->InputAt(0)));
+ } else if (instruction->IsBoundsCheck()) {
+ info = LookupInfo(loop, instruction->InputAt(0)); // Pass-through.
+ } else if (instruction->IsTypeConversion()) {
+ HTypeConversion* conversion = instruction->AsTypeConversion();
+ // TODO: accept different conversion scenarios.
+ if (conversion->GetResultType() == conversion->GetInputType()) {
+ info = LookupInfo(loop, conversion->GetInput());
+ }
}
// Successfully classified?
@@ -188,7 +202,7 @@
void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) {
const size_t size = scc_.size();
- CHECK_GE(size, 1u);
+ DCHECK_GE(size, 1u);
HInstruction* phi = scc_[size - 1];
if (!IsEntryPhi(loop, phi)) {
return;
@@ -204,41 +218,74 @@
if (size == 1) {
InductionInfo* update = LookupInfo(loop, internal);
if (update != nullptr) {
- AssignInfo(loop, phi, NewInductionInfo(kWrapAround, kNop, initial, update, nullptr));
+ AssignInfo(loop, phi, CreateInduction(kWrapAround, initial, update));
}
return;
}
// Inspect remainder of the cycle that resides in scc_. The cycle_ mapping assigns
- // temporary meaning to its nodes.
- cycle_.Overwrite(phi->GetId(), nullptr);
+ // temporary meaning to its nodes, seeded from the phi instruction and back.
for (size_t i = 0; i < size - 1; i++) {
- HInstruction* operation = scc_[i];
+ HInstruction* instruction = scc_[i];
InductionInfo* update = nullptr;
- if (operation->IsPhi()) {
- update = TransferCycleOverPhi(operation);
- } else if (operation->IsAdd()) {
- update = TransferCycleOverAddSub(loop, operation->InputAt(0), operation->InputAt(1), kAdd, true);
- } else if (operation->IsSub()) {
- update = TransferCycleOverAddSub(loop, operation->InputAt(0), operation->InputAt(1), kSub, true);
+ if (instruction->IsPhi()) {
+ update = SolvePhi(loop, phi, instruction);
+ } else if (instruction->IsAdd()) {
+ update = SolveAddSub(
+ loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kAdd, true);
+ } else if (instruction->IsSub()) {
+ update = SolveAddSub(
+ loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kSub, true);
}
if (update == nullptr) {
return;
}
- cycle_.Overwrite(operation->GetId(), update);
+ cycle_.Put(instruction, update);
}
- // Success if the internal link received accumulated nonzero update.
- auto it = cycle_.find(internal->GetId());
- if (it != cycle_.end() && it->second != nullptr) {
- // Classify header phi and feed the cycle "on-demand".
- AssignInfo(loop, phi, NewInductionInfo(kLinear, kNop, it->second, initial, nullptr));
- for (size_t i = 0; i < size - 1; i++) {
- ClassifyTrivial(loop, scc_[i]);
+ // Success if the internal link received a meaning.
+ auto it = cycle_.find(internal);
+ if (it != cycle_.end()) {
+ InductionInfo* induction = it->second;
+ switch (induction->induction_class) {
+ case kInvariant:
+ // Classify phi (last element in scc_) and then the rest of the cycle "on-demand".
+ // Statements are scanned in the Tarjan SCC order, with phi first.
+ AssignInfo(loop, phi, CreateInduction(kLinear, induction, initial));
+ for (size_t i = 0; i < size - 1; i++) {
+ ClassifyTrivial(loop, scc_[i]);
+ }
+ break;
+ case kPeriodic:
+ // Classify all elements in the cycle with the found periodic induction while rotating
+ // each first element to the end. Lastly, phi (last element in scc_) is classified.
+ // Statements are scanned in the reverse Tarjan SCC order, with phi last.
+ for (size_t i = 2; i <= size; i++) {
+ AssignInfo(loop, scc_[size - i], induction);
+ induction = RotatePeriodicInduction(induction->op_b, induction->op_a);
+ }
+ AssignInfo(loop, phi, induction);
+ break;
+ default:
+ break;
}
}
}
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::RotatePeriodicInduction(
+ InductionInfo* induction,
+ InductionInfo* last) {
+ // Rotates a periodic induction of the form
+ // (a, b, c, d, e)
+ // into
+ // (b, c, d, e, a)
+ // in preparation of assigning this to the previous variable in the sequence.
+ if (induction->induction_class == kInvariant) {
+ return CreateInduction(kPeriodic, induction, last);
+ }
+ return CreateInduction(kPeriodic, induction->op_a, RotatePeriodicInduction(induction->op_b, last));
+}
+
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(InductionInfo* a,
InductionInfo* b) {
// Transfer over a phi: if both inputs are identical, result is input.
@@ -251,36 +298,33 @@
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(InductionInfo* a,
InductionInfo* b,
InductionOp op) {
- // Transfer over an addition or subtraction: invariant or linear
- // inputs combine into new invariant or linear result.
+ // Transfer over an addition or subtraction: any invariant, linear, wrap-around, or periodic
+ // can be combined with an invariant to yield a similar result. Even two linear inputs can
+ // be combined. All other combinations fail, however.
if (a != nullptr && b != nullptr) {
if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
- return NewInductionInfo(kInvariant, op, a, b, nullptr);
- } else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
- return NewInductionInfo(
- kLinear,
- kNop,
- a->op_a,
- NewInductionInfo(kInvariant, op, a->op_b, b, nullptr),
- nullptr);
- } else if (a->induction_class == kInvariant && b->induction_class == kLinear) {
- InductionInfo* ba = b->op_a;
- if (op == kSub) { // negation required
- ba = NewInductionInfo(kInvariant, kNeg, nullptr, ba, nullptr);
- }
- return NewInductionInfo(
- kLinear,
- kNop,
- ba,
- NewInductionInfo(kInvariant, op, a, b->op_b, nullptr),
- nullptr);
+ return CreateInvariantOp(op, a, b);
} else if (a->induction_class == kLinear && b->induction_class == kLinear) {
- return NewInductionInfo(
- kLinear,
- kNop,
- NewInductionInfo(kInvariant, op, a->op_a, b->op_a, nullptr),
- NewInductionInfo(kInvariant, op, a->op_b, b->op_b, nullptr),
- nullptr);
+ return CreateInduction(
+ kLinear, TransferAddSub(a->op_a, b->op_a, op), TransferAddSub(a->op_b, b->op_b, op));
+ } else if (a->induction_class == kInvariant) {
+ InductionInfo* new_a = b->op_a;
+ InductionInfo* new_b = TransferAddSub(a, b->op_b, op);
+ if (b->induction_class != kLinear) {
+ DCHECK(b->induction_class == kWrapAround || b->induction_class == kPeriodic);
+ new_a = TransferAddSub(a, new_a, op);
+ } else if (op == kSub) { // Negation required.
+ new_a = TransferNeg(new_a);
+ }
+ return CreateInduction(b->induction_class, new_a, new_b);
+ } else if (b->induction_class == kInvariant) {
+ InductionInfo* new_a = a->op_a;
+ InductionInfo* new_b = TransferAddSub(a->op_b, b, op);
+ if (a->induction_class != kLinear) {
+ DCHECK(a->induction_class == kWrapAround || a->induction_class == kPeriodic);
+ new_a = TransferAddSub(new_a, b, op);
+ }
+ return CreateInduction(a->induction_class, new_a, new_b);
}
}
return nullptr;
@@ -288,141 +332,335 @@
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(InductionInfo* a,
InductionInfo* b) {
- // Transfer over a multiplication: invariant or linear
- // inputs combine into new invariant or linear result.
- // Two linear inputs would become quadratic.
+ // Transfer over a multiplication: any invariant, linear, wrap-around, or periodic
+ // can be multiplied with an invariant to yield a similar but multiplied result.
+ // Two non-invariant inputs cannot be multiplied, however.
if (a != nullptr && b != nullptr) {
if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
- return NewInductionInfo(kInvariant, kMul, a, b, nullptr);
- } else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
- return NewInductionInfo(
- kLinear,
- kNop,
- NewInductionInfo(kInvariant, kMul, a->op_a, b, nullptr),
- NewInductionInfo(kInvariant, kMul, a->op_b, b, nullptr),
- nullptr);
- } else if (a->induction_class == kInvariant && b->induction_class == kLinear) {
- return NewInductionInfo(
- kLinear,
- kNop,
- NewInductionInfo(kInvariant, kMul, a, b->op_a, nullptr),
- NewInductionInfo(kInvariant, kMul, a, b->op_b, nullptr),
- nullptr);
+ return CreateInvariantOp(kMul, a, b);
+ } else if (a->induction_class == kInvariant) {
+ return CreateInduction(b->induction_class, TransferMul(a, b->op_a), TransferMul(a, b->op_b));
+ } else if (b->induction_class == kInvariant) {
+ return CreateInduction(a->induction_class, TransferMul(a->op_a, b), TransferMul(a->op_b, b));
+ }
+ }
+ return nullptr;
+}
+
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferShl(InductionInfo* a,
+ InductionInfo* b,
+ Primitive::Type type) {
+ // Transfer over a shift left: treat shift by restricted constant as equivalent multiplication.
+ int64_t value = -1;
+ if (a != nullptr && IsIntAndGet(b, &value)) {
+ // Obtain the constant needed for the multiplication. This yields an existing instruction
+ // if the constants is already there. Otherwise, this has a side effect on the HIR.
+ // The restriction on the shift factor avoids generating a negative constant
+ // (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that generalization
+ // for shift factors outside [0,32) and [0,64) ranges is done by earlier simplification.
+ if ((type == Primitive::kPrimInt && 0 <= value && value < 31) ||
+ (type == Primitive::kPrimLong && 0 <= value && value < 63)) {
+ return TransferMul(a, CreateConstant(1 << value, type));
}
}
return nullptr;
}
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(InductionInfo* a) {
- // Transfer over a unary negation: invariant or linear input
- // yields a similar, but negated result.
+ // Transfer over a unary negation: an invariant, linear, wrap-around, or periodic input
+ // yields a similar but negated induction as result.
if (a != nullptr) {
if (a->induction_class == kInvariant) {
- return NewInductionInfo(kInvariant, kNeg, nullptr, a, nullptr);
- } else if (a->induction_class == kLinear) {
- return NewInductionInfo(
- kLinear,
- kNop,
- NewInductionInfo(kInvariant, kNeg, nullptr, a->op_a, nullptr),
- NewInductionInfo(kInvariant, kNeg, nullptr, a->op_b, nullptr),
- nullptr);
+ return CreateInvariantOp(kNeg, nullptr, a);
}
+ return CreateInduction(a->induction_class, TransferNeg(a->op_a), TransferNeg(a->op_b));
}
return nullptr;
}
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCycleOverPhi(HInstruction* phi) {
- // Transfer within a cycle over a phi: only identical inputs
- // can be combined into that input as result.
- const size_t count = phi->InputCount();
- CHECK_GT(count, 0u);
- auto ita = cycle_.find(phi->InputAt(0)->GetId());
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhi(HLoopInformation* loop,
+ HInstruction* phi,
+ HInstruction* instruction) {
+ // Solve within a cycle over a phi: identical inputs are combined into that input as result.
+ const size_t count = instruction->InputCount();
+ DCHECK_GT(count, 0u);
+ auto ita = cycle_.find(instruction->InputAt(0));
if (ita != cycle_.end()) {
InductionInfo* a = ita->second;
for (size_t i = 1; i < count; i++) {
- auto itb = cycle_.find(phi->InputAt(i)->GetId());
- if (itb == cycle_.end() ||!HInductionVarAnalysis::InductionEqual(a, itb->second)) {
+ auto itb = cycle_.find(instruction->InputAt(i));
+ if (itb == cycle_.end() || !HInductionVarAnalysis::InductionEqual(a, itb->second)) {
return nullptr;
}
}
return a;
}
- return nullptr;
-}
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCycleOverAddSub(
- HLoopInformation* loop,
- HInstruction* x,
- HInstruction* y,
- InductionOp op,
- bool first) {
- // Transfer within a cycle over an addition or subtraction: adding or
- // subtracting an invariant value adds to the stride of the induction,
- // starting with the phi value denoted by the unusual nullptr value.
- auto it = cycle_.find(x->GetId());
- if (it != cycle_.end()) {
- InductionInfo* a = it->second;
- InductionInfo* b = LookupInfo(loop, y);
- if (b != nullptr && b->induction_class == kInvariant) {
- if (a == nullptr) {
- if (op == kSub) { // negation required
- return NewInductionInfo(kInvariant, kNeg, nullptr, b, nullptr);
+ // Solve within a cycle over another entry-phi: add invariants into a periodic.
+ if (IsEntryPhi(loop, instruction)) {
+ InductionInfo* a = LookupInfo(loop, instruction->InputAt(0));
+ if (a != nullptr && a->induction_class == kInvariant) {
+ if (instruction->InputAt(1) == phi) {
+ InductionInfo* initial = LookupInfo(loop, phi->InputAt(0));
+ return CreateInduction(kPeriodic, a, initial);
+ }
+ auto it = cycle_.find(instruction->InputAt(1));
+ if (it != cycle_.end()) {
+ InductionInfo* b = it->second;
+ if (b->induction_class == kPeriodic) {
+ return CreateInduction(kPeriodic, a, b);
}
- return b;
- } else if (a->induction_class == kInvariant) {
- return NewInductionInfo(kInvariant, op, a, b, nullptr);
}
}
}
- // On failure, try alternatives.
+
+ return nullptr;
+}
+
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(HLoopInformation* loop,
+ HInstruction* phi,
+ HInstruction* instruction,
+ HInstruction* x,
+ HInstruction* y,
+ InductionOp op,
+ bool is_first_call) {
+ // Solve within a cycle over an addition or subtraction: adding or subtracting an
+ // invariant value, seeded from phi, keeps adding to the stride of the induction.
+ InductionInfo* b = LookupInfo(loop, y);
+ if (b != nullptr && b->induction_class == kInvariant) {
+ if (x == phi) {
+ return (op == kAdd) ? b : CreateInvariantOp(kNeg, nullptr, b);
+ }
+ auto it = cycle_.find(x);
+ if (it != cycle_.end()) {
+ InductionInfo* a = it->second;
+ if (a->induction_class == kInvariant) {
+ return CreateInvariantOp(op, a, b);
+ }
+ }
+ }
+
+ // Try some alternatives before failing.
if (op == kAdd) {
- // Try the other way around for an addition.
- if (first) {
- return TransferCycleOverAddSub(loop, y, x, op, false);
+ // Try the other way around for an addition if considered for first time.
+ if (is_first_call) {
+ return SolveAddSub(loop, phi, instruction, y, x, op, false);
+ }
+ } else if (op == kSub) {
+ // Solve within a tight cycle for a periodic idiom k = c - k;
+ if (y == phi && instruction == phi->InputAt(1)) {
+ InductionInfo* a = LookupInfo(loop, x);
+ if (a != nullptr && a->induction_class == kInvariant) {
+ InductionInfo* initial = LookupInfo(loop, phi->InputAt(0));
+ return CreateInduction(kPeriodic, CreateInvariantOp(kSub, a, initial), initial);
+ }
}
}
+
return nullptr;
}
-void HInductionVarAnalysis::PutInfo(int loop_id, int id, InductionInfo* info) {
- auto it = induction_.find(loop_id);
- if (it == induction_.end()) {
- it = induction_.Put(
- loop_id, ArenaSafeMap<int, InductionInfo*>(std::less<int>(), graph_->GetArena()->Adapter()));
- }
- it->second.Overwrite(id, info);
-}
-
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::GetInfo(int loop_id, int id) {
- auto it = induction_.find(loop_id);
- if (it != induction_.end()) {
- auto loop_it = it->second.find(id);
- if (loop_it != it->second.end()) {
- return loop_it->second;
+void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) {
+ HInstruction* control = loop->GetHeader()->GetLastInstruction();
+ if (control->IsIf()) {
+ HIf* ifs = control->AsIf();
+ HBasicBlock* if_true = ifs->IfTrueSuccessor();
+ HBasicBlock* if_false = ifs->IfFalseSuccessor();
+ HInstruction* if_expr = ifs->InputAt(0);
+ // Determine if loop has following structure in header.
+ // loop-header: ....
+ // if (condition) goto X
+ if (if_expr->IsCondition()) {
+ HCondition* condition = if_expr->AsCondition();
+ InductionInfo* a = LookupInfo(loop, condition->InputAt(0));
+ InductionInfo* b = LookupInfo(loop, condition->InputAt(1));
+ Primitive::Type type = condition->InputAt(0)->GetType();
+ // Determine if the loop control uses integral arithmetic and an if-exit (X outside) or an
+ // if-iterate (X inside), always expressed as if-iterate when passing into VisitCondition().
+ if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) {
+ // Loop control is not 32/64-bit integral.
+ } else if (a == nullptr || b == nullptr) {
+ // Loop control is not a sequence.
+ } else if (if_true->GetLoopInformation() != loop && if_false->GetLoopInformation() == loop) {
+ VisitCondition(loop, a, b, type, condition->GetOppositeCondition());
+ } else if (if_true->GetLoopInformation() == loop && if_false->GetLoopInformation() != loop) {
+ VisitCondition(loop, a, b, type, condition->GetCondition());
+ }
}
}
- return nullptr;
+}
+
+void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop,
+ InductionInfo* a,
+ InductionInfo* b,
+ Primitive::Type type,
+ IfCondition cmp) {
+ if (a->induction_class == kInvariant && b->induction_class == kLinear) {
+ // Swap conditions (e.g. U > i is same as i < U).
+ switch (cmp) {
+ case kCondLT: VisitCondition(loop, b, a, type, kCondGT); break;
+ case kCondLE: VisitCondition(loop, b, a, type, kCondGE); break;
+ case kCondGT: VisitCondition(loop, b, a, type, kCondLT); break;
+ case kCondGE: VisitCondition(loop, b, a, type, kCondLE); break;
+ default: break;
+ }
+ } else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
+ // Normalize a linear loop control with a constant, nonzero stride:
+ // stride > 0, either i < U or i <= U
+ // stride < 0, either i > U or i >= U
+ InductionInfo* stride = a->op_a;
+ InductionInfo* lo_val = a->op_b;
+ InductionInfo* hi_val = b;
+ int64_t value = -1;
+ if (IsIntAndGet(stride, &value)) {
+ if ((value > 0 && (cmp == kCondLT || cmp == kCondLE)) ||
+ (value < 0 && (cmp == kCondGT || cmp == kCondGE))) {
+ bool is_strict = cmp == kCondLT || cmp == kCondGT;
+ VisitTripCount(loop, lo_val, hi_val, stride, value, type, is_strict);
+ }
+ }
+ }
+}
+
+void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop,
+ InductionInfo* lo_val,
+ InductionInfo* hi_val,
+ InductionInfo* stride,
+ int32_t stride_value,
+ Primitive::Type type,
+ bool is_strict) {
+ // Any loop of the general form:
+ //
+ // for (i = L; i <= U; i += S) // S > 0
+ // or for (i = L; i >= U; i += S) // S < 0
+ // .. i ..
+ //
+ // can be normalized into:
+ //
+ // for (n = 0; n < TC; n++) // where TC = (U + S - L) / S
+ // .. L + S * n ..
+ //
+ // NOTE: The TC (trip-count) expression is only valid if the top-test path is taken at
+ // least once. Otherwise TC is 0. Also, the expression assumes the loop does not
+ // have any early-exits. Otherwise, TC is an upper bound.
+ //
+ bool cancels = is_strict && abs(stride_value) == 1; // compensation cancels conversion?
+ if (!cancels) {
+ // Convert exclusive integral inequality into inclusive integral inequality,
+ // viz. condition i < U is i <= U - 1 and condition i > U is i >= U + 1.
+ if (is_strict) {
+ const InductionOp op = stride_value > 0 ? kSub : kAdd;
+ hi_val = CreateInvariantOp(op, hi_val, CreateConstant(1, type));
+ }
+ // Compensate for stride.
+ hi_val = CreateInvariantOp(kAdd, hi_val, stride);
+ }
+
+ // Assign the trip-count expression to the loop control. Clients that use the information
+ // should be aware that due to the L <= U assumption, the expression is only valid in the
+ // loop-body proper, and not yet in the loop-header. If the loop has any early exits, the
+ // trip-count forms a conservative upper bound on the number of loop iterations.
+ InductionInfo* trip_count =
+ CreateInvariantOp(kDiv, CreateInvariantOp(kSub, hi_val, lo_val), stride);
+ AssignInfo(loop, loop->GetHeader()->GetLastInstruction(), trip_count);
}
void HInductionVarAnalysis::AssignInfo(HLoopInformation* loop,
HInstruction* instruction,
InductionInfo* info) {
- const int loopId = loop->GetHeader()->GetBlockId();
- const int id = instruction->GetId();
- PutInfo(loopId, id, info);
+ auto it = induction_.find(loop);
+ if (it == induction_.end()) {
+ it = induction_.Put(loop,
+ ArenaSafeMap<HInstruction*, InductionInfo*>(
+ std::less<HInstruction*>(), graph_->GetArena()->Adapter()));
+ }
+ it->second.Put(instruction, info);
}
-HInductionVarAnalysis::InductionInfo*
-HInductionVarAnalysis::LookupInfo(HLoopInformation* loop,
- HInstruction* instruction) {
- const int loop_id = loop->GetHeader()->GetBlockId();
- const int id = instruction->GetId();
- InductionInfo* info = GetInfo(loop_id, id);
- if (info == nullptr && IsLoopInvariant(loop, instruction)) {
- info = NewInductionInfo(kInvariant, kFetch, nullptr, nullptr, instruction);
- PutInfo(loop_id, id, info);
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::LookupInfo(HLoopInformation* loop,
+ HInstruction* instruction) {
+ auto it = induction_.find(loop);
+ if (it != induction_.end()) {
+ auto loop_it = it->second.find(instruction);
+ if (loop_it != it->second.end()) {
+ return loop_it->second;
+ }
}
- return info;
+ if (IsLoopInvariant(loop, instruction)) {
+ InductionInfo* info = CreateInvariantFetch(instruction);
+ AssignInfo(loop, instruction, info);
+ return info;
+ }
+ return nullptr;
+}
+
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateConstant(int64_t value,
+ Primitive::Type type) {
+ if (type == Primitive::kPrimInt) {
+ return CreateInvariantFetch(graph_->GetIntConstant(value));
+ }
+ DCHECK_EQ(type, Primitive::kPrimLong);
+ return CreateInvariantFetch(graph_->GetLongConstant(value));
+}
+
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInvariant(
+ InductionOp op,
+ InductionInfo* a,
+ InductionInfo* b) {
+ // Perform some light-weight simplifications during construction of a new invariant.
+ // This often safes memory and yields a more concise representation of the induction.
+ // More exhaustive simplifications are done by later phases once induction nodes are
+ // translated back into HIR code (e.g. by loop optimizations or BCE).
+ int64_t value = -1;
+ if (IsIntAndGet(a, &value)) {
+ if (value == 0) {
+ // Simplify 0 + b = b, 0 * b = 0.
+ if (op == kAdd) {
+ return b;
+ } else if (op == kMul) {
+ return a;
+ }
+ } else if (op == kMul) {
+ // Simplify 1 * b = b, -1 * b = -b
+ if (value == 1) {
+ return b;
+ } else if (value == -1) {
+ op = kNeg;
+ a = nullptr;
+ }
+ }
+ }
+ if (IsIntAndGet(b, &value)) {
+ if (value == 0) {
+ // Simplify a + 0 = a, a - 0 = a, a * 0 = 0, -0 = 0.
+ if (op == kAdd || op == kSub) {
+ return a;
+ } else if (op == kMul || op == kNeg) {
+ return b;
+ }
+ } else if (op == kMul || op == kDiv) {
+ // Simplify a * 1 = a, a / 1 = a, a * -1 = -a, a / -1 = -a
+ if (value == 1) {
+ return a;
+ } else if (value == -1) {
+ op = kNeg;
+ b = a;
+ a = nullptr;
+ }
+ }
+ } else if (b->operation == kNeg) {
+ // Simplify a + (-b) = a - b, a - (-b) = a + b, -(-b) = b.
+ if (op == kAdd) {
+ op = kSub;
+ b = b->op_b;
+ } else if (op == kSub) {
+ op = kAdd;
+ b = b->op_b;
+ } else if (op == kNeg) {
+ return b->op_b;
+ }
+ }
+ return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr);
}
bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
@@ -440,27 +678,46 @@
return info1 == info2;
}
+bool HInductionVarAnalysis::IsIntAndGet(InductionInfo* info, int64_t* value) {
+ if (info != nullptr && info->induction_class == kInvariant && info->operation == kFetch) {
+ DCHECK(info->fetch);
+ if (info->fetch->IsIntConstant()) {
+ *value = info->fetch->AsIntConstant()->GetValue();
+ return true;
+ } else if (info->fetch->IsLongConstant()) {
+ *value = info->fetch->AsLongConstant()->GetValue();
+ return true;
+ }
+ }
+ return false;
+}
+
std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
if (info != nullptr) {
if (info->induction_class == kInvariant) {
+ int64_t value = -1;
std::string inv = "(";
inv += InductionToString(info->op_a);
switch (info->operation) {
- case kNop: inv += " ? "; break;
- case kAdd: inv += " + "; break;
+ case kNop: inv += " @ "; break;
+ case kAdd: inv += " + "; break;
case kSub:
- case kNeg: inv += " - "; break;
- case kMul: inv += " * "; break;
- case kDiv: inv += " / "; break;
+ case kNeg: inv += " - "; break;
+ case kMul: inv += " * "; break;
+ case kDiv: inv += " / "; break;
case kFetch:
- CHECK(info->fetch != nullptr);
- inv += std::to_string(info->fetch->GetId()) + ":" + info->fetch->DebugName();
+ DCHECK(info->fetch);
+ if (IsIntAndGet(info, &value)) {
+ inv += std::to_string(value);
+ } else {
+ inv += std::to_string(info->fetch->GetId()) + ":" + info->fetch->DebugName();
+ }
break;
}
inv += InductionToString(info->op_b);
return inv + ")";
} else {
- CHECK(info->operation == kNop);
+ DCHECK(info->operation == kNop);
if (info->induction_class == kLinear) {
return "(" + InductionToString(info->op_a) + " * i + " +
InductionToString(info->op_b) + ")";
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 09a0a38..8eccf92 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -25,9 +25,11 @@
namespace art {
/**
- * Induction variable analysis.
+ * Induction variable analysis. This class does not have a direct public API.
+ * Instead, the results of induction variable analysis can be queried through
+ * friend classes, such as InductionVarRange.
*
- * Based on the paper by M. Gerlek et al.
+ * The analysis implementation is based on the paper by M. Gerlek et al.
* "Beyond Induction Variables: Detecting and Classifying Sequences Using a Demand-Driven SSA Form"
* (ACM Transactions on Programming Languages and Systems, Volume 17 Issue 1, Jan. 1995).
*/
@@ -35,16 +37,6 @@
public:
explicit HInductionVarAnalysis(HGraph* graph);
- // TODO: design public API useful in later phases
-
- /**
- * Returns string representation of induction found for the instruction
- * in the given loop (for testing and debugging only).
- */
- std::string InductionToString(HLoopInformation* loop, HInstruction* instruction) {
- return InductionToString(LookupInfo(loop, instruction));
- }
-
void Run() OVERRIDE;
private:
@@ -57,12 +49,10 @@
};
enum InductionClass {
- kNone,
kInvariant,
kLinear,
kWrapAround,
- kPeriodic,
- kMonotonic
+ kPeriodic
};
enum InductionOp {
@@ -79,7 +69,7 @@
* Defines a detected induction as:
* (1) invariant:
* operation: a + b, a - b, -b, a * b, a / b
- * or
+ * or:
* fetch: fetch from HIR
* (2) linear:
* nop: a * i + b
@@ -87,8 +77,6 @@
* nop: a, then defined by b
* (4) periodic
* nop: a, then defined by b (repeated when exhausted)
- * (5) monotonic
- * // TODO: determine representation
*/
struct InductionInfo : public ArenaObject<kArenaAllocMisc> {
InductionInfo(InductionClass ic,
@@ -108,17 +96,23 @@
HInstruction* fetch;
};
- inline bool IsVisitedNode(int id) const {
- return map_.find(id) != map_.end();
+ bool IsVisitedNode(HInstruction* instruction) const {
+ return map_.find(instruction) != map_.end();
}
- inline InductionInfo* NewInductionInfo(
- InductionClass c,
- InductionOp op,
- InductionInfo* a,
- InductionInfo* b,
- HInstruction* i) {
- return new (graph_->GetArena()) InductionInfo(c, op, a, b, i);
+ InductionInfo* CreateInvariantOp(InductionOp op, InductionInfo* a, InductionInfo* b) {
+ DCHECK(((op != kNeg && a != nullptr) || (op == kNeg && a == nullptr)) && b != nullptr);
+ return CreateSimplifiedInvariant(op, a, b);
+ }
+
+ InductionInfo* CreateInvariantFetch(HInstruction* f) {
+ DCHECK(f != nullptr);
+ return new (graph_->GetArena()) InductionInfo(kInvariant, kFetch, nullptr, nullptr, f);
+ }
+
+ InductionInfo* CreateInduction(InductionClass ic, InductionInfo* a, InductionInfo* b) {
+ DCHECK(a != nullptr && b != nullptr);
+ return new (graph_->GetArena()) InductionInfo(ic, kNop, a, b, nullptr);
}
// Methods for analysis.
@@ -132,36 +126,66 @@
InductionInfo* TransferPhi(InductionInfo* a, InductionInfo* b);
InductionInfo* TransferAddSub(InductionInfo* a, InductionInfo* b, InductionOp op);
InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
+ InductionInfo* TransferShl(InductionInfo* a, InductionInfo* b, Primitive::Type type);
InductionInfo* TransferNeg(InductionInfo* a);
- InductionInfo* TransferCycleOverPhi(HInstruction* phi);
- InductionInfo* TransferCycleOverAddSub(HLoopInformation* loop,
- HInstruction* x,
- HInstruction* y,
- InductionOp op,
- bool first);
+
+ // Solvers.
+ InductionInfo* SolvePhi(HLoopInformation* loop,
+ HInstruction* phi,
+ HInstruction* instruction);
+ InductionInfo* SolveAddSub(HLoopInformation* loop,
+ HInstruction* phi,
+ HInstruction* instruction,
+ HInstruction* x,
+ HInstruction* y,
+ InductionOp op,
+ bool is_first_call);
+ InductionInfo* RotatePeriodicInduction(InductionInfo* induction, InductionInfo* last);
+
+ // Trip count information.
+ void VisitControl(HLoopInformation* loop);
+ void VisitCondition(HLoopInformation* loop,
+ InductionInfo* a,
+ InductionInfo* b,
+ Primitive::Type type,
+ IfCondition cmp);
+ void VisitTripCount(HLoopInformation* loop,
+ InductionInfo* lo_val,
+ InductionInfo* hi_val,
+ InductionInfo* stride,
+ int32_t stride_value,
+ Primitive::Type type,
+ bool is_strict);
// Assign and lookup.
- void PutInfo(int loop_id, int id, InductionInfo* info);
- InductionInfo* GetInfo(int loop_id, int id);
void AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info);
InductionInfo* LookupInfo(HLoopInformation* loop, HInstruction* instruction);
- bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
- std::string InductionToString(InductionInfo* info);
+ InductionInfo* CreateConstant(int64_t value, Primitive::Type type);
+ InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b);
- // Bookkeeping during and after analysis.
- // TODO: fine tune data structures, only keep relevant data
+ // Helpers.
+ static bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
+ static bool IsIntAndGet(InductionInfo* info, int64_t* value);
+ static std::string InductionToString(InductionInfo* info);
+ // TODO: fine tune the following data structures, only keep relevant data.
+
+ // Temporary book-keeping during the analysis.
uint32_t global_depth_;
-
ArenaVector<HInstruction*> stack_;
ArenaVector<HInstruction*> scc_;
+ ArenaSafeMap<HInstruction*, NodeInfo> map_;
+ ArenaSafeMap<HInstruction*, InductionInfo*> cycle_;
- // Mappings of instruction id to node and induction information.
- ArenaSafeMap<int, NodeInfo> map_;
- ArenaSafeMap<int, InductionInfo*> cycle_;
+ /**
+ * Maintains the results of the analysis as a mapping from loops to a mapping from instructions
+ * to the induction information for that instruction in that loop.
+ */
+ ArenaSafeMap<HLoopInformation*, ArenaSafeMap<HInstruction*, InductionInfo*>> induction_;
- // Mapping from loop id to mapping of instruction id to induction information.
- ArenaSafeMap<int, ArenaSafeMap<int, InductionInfo*>> induction_;
+ friend class InductionVarAnalysisTest;
+ friend class InductionVarRange;
+ friend class InductionVarRangeTest;
DISALLOW_COPY_AND_ASSIGN(HInductionVarAnalysis);
};
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 2093e33..fca1ca5 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -63,7 +63,7 @@
// populate the loop with instructions to set up interesting scenarios.
void BuildLoopNest(int n) {
ASSERT_LE(n, 10);
- graph_->SetNumberOfVRegs(n + 2);
+ graph_->SetNumberOfVRegs(n + 3);
// Build basic blocks with entry, nested loop, exit.
entry_ = new (&allocator_) HBasicBlock(graph_);
@@ -77,47 +77,36 @@
graph_->SetExitBlock(exit_);
// Provide entry and exit instructions.
- // 0 : parameter
- // 1 : constant 0
- // 2 : constant 1
- // 3 : constant 100
- parameter_ = new (&allocator_)
- HParameterValue(0, Primitive::kPrimNot, true);
+ parameter_ = new (&allocator_) HParameterValue(0, Primitive::kPrimNot, true);
entry_->AddInstruction(parameter_);
- constant0_ = new (&allocator_) HConstant(Primitive::kPrimInt);
- entry_->AddInstruction(constant0_);
- constant1_ = new (&allocator_) HConstant(Primitive::kPrimInt);
- entry_->AddInstruction(constant1_);
- constant100_ = new (&allocator_) HConstant(Primitive::kPrimInt);
- entry_->AddInstruction(constant100_);
- exit_->AddInstruction(new (&allocator_) HExit());
+ constant0_ = graph_->GetIntConstant(0);
+ constant1_ = graph_->GetIntConstant(1);
+ constant100_ = graph_->GetIntConstant(100);
induc_ = new (&allocator_) HLocal(n);
entry_->AddInstruction(induc_);
entry_->AddInstruction(new (&allocator_) HStoreLocal(induc_, constant0_));
tmp_ = new (&allocator_) HLocal(n + 1);
entry_->AddInstruction(tmp_);
entry_->AddInstruction(new (&allocator_) HStoreLocal(tmp_, constant100_));
+ dum_ = new (&allocator_) HLocal(n + 2);
+ entry_->AddInstruction(dum_);
+ exit_->AddInstruction(new (&allocator_) HExit());
// Provide loop instructions.
for (int d = 0; d < n; d++) {
basic_[d] = new (&allocator_) HLocal(d);
entry_->AddInstruction(basic_[d]);
- loop_preheader_[d]->AddInstruction(
- new (&allocator_) HStoreLocal(basic_[d], constant0_));
- HInstruction* load = new (&allocator_)
- HLoadLocal(basic_[d], Primitive::kPrimInt);
+ loop_preheader_[d]->AddInstruction(new (&allocator_) HStoreLocal(basic_[d], constant0_));
+ HInstruction* load = new (&allocator_) HLoadLocal(basic_[d], Primitive::kPrimInt);
loop_header_[d]->AddInstruction(load);
- HInstruction* compare = new (&allocator_)
- HGreaterThanOrEqual(load, constant100_);
+ HInstruction* compare = new (&allocator_) HLessThan(load, constant100_);
loop_header_[d]->AddInstruction(compare);
loop_header_[d]->AddInstruction(new (&allocator_) HIf(compare));
load = new (&allocator_) HLoadLocal(basic_[d], Primitive::kPrimInt);
loop_body_[d]->AddInstruction(load);
- increment_[d] = new (&allocator_)
- HAdd(Primitive::kPrimInt, load, constant1_);
+ increment_[d] = new (&allocator_) HAdd(Primitive::kPrimInt, load, constant1_);
loop_body_[d]->AddInstruction(increment_[d]);
- loop_body_[d]->AddInstruction(
- new (&allocator_) HStoreLocal(basic_[d], increment_[d]));
+ loop_body_[d]->AddInstruction(new (&allocator_) HStoreLocal(basic_[d], increment_[d]));
loop_body_[d]->AddInstruction(new (&allocator_) HGoto());
}
}
@@ -149,8 +138,7 @@
// Inserts local load at depth d.
HInstruction* InsertLocalLoad(HLocal* local, int d) {
- return InsertInstruction(
- new (&allocator_) HLoadLocal(local, Primitive::kPrimInt), d);
+ return InsertInstruction(new (&allocator_) HLoadLocal(local, Primitive::kPrimInt), d);
}
// Inserts local store at depth d.
@@ -167,9 +155,10 @@
parameter_, load, constant0_, Primitive::kPrimInt, 0), d);
}
- // Returns loop information of loop at depth d.
- HLoopInformation* GetLoopInfo(int d) {
- return loop_body_[d]->GetLoopInformation();
+ // Returns induction information of instruction in loop at depth d.
+ std::string GetInductionInfo(HInstruction* instruction, int d) {
+ return HInductionVarAnalysis::InductionToString(
+ iva_->LookupInfo(loop_body_[d]->GetLoopInformation(), instruction));
}
// Performs InductionVarAnalysis (after proper set up).
@@ -194,6 +183,7 @@
HInstruction* constant100_;
HLocal* induc_; // "vreg_n", the "k"
HLocal* tmp_; // "vreg_n+1"
+ HLocal* dum_; // "vreg_n+2"
// Loop specifics.
HBasicBlock* loop_preheader_[10];
@@ -230,222 +220,159 @@
ASSERT_EQ(exit_->GetLoopInformation(), nullptr);
}
-TEST_F(InductionVarAnalysisTest, FindBasicInductionVar) {
+TEST_F(InductionVarAnalysisTest, FindBasicInduction) {
// Setup:
// for (int i = 0; i < 100; i++) {
- // a[i] = 0;
+ // a[i] = 0;
// }
BuildLoopNest(1);
HInstruction* store = InsertArrayStore(basic_[0], 0);
PerformInductionVarAnalysis();
- EXPECT_STREQ(
- "((2:Constant) * i + (1:Constant))",
- iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
- EXPECT_STREQ(
- "((2:Constant) * i + ((1:Constant) + (2:Constant)))",
- iva_->InductionToString(GetLoopInfo(0), increment_[0]).c_str());
+ EXPECT_STREQ("((1) * i + (0))", GetInductionInfo(store->InputAt(1), 0).c_str());
+ EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Trip-count.
+ EXPECT_STREQ("(100)", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
}
-TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarAdd) {
+TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
// Setup:
// for (int i = 0; i < 100; i++) {
- // k = 100 + i;
- // a[k] = 0;
+ // k = 100 + i;
+ // k = 100 - i;
+ // k = 100 * i;
+ // k = i << 1;
+ // k = - i;
// }
BuildLoopNest(1);
HInstruction *add = InsertInstruction(
- new (&allocator_) HAdd(
- Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
InsertLocalStore(induc_, add, 0);
- HInstruction* store = InsertArrayStore(induc_, 0);
- PerformInductionVarAnalysis();
-
- EXPECT_STREQ(
- "((2:Constant) * i + ((3:Constant) + (1:Constant)))",
- iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
-}
-
-TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarSub) {
- // Setup:
- // for (int i = 0; i < 100; i++) {
- // k = 100 - i;
- // a[k] = 0;
- // }
- BuildLoopNest(1);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(
- Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
InsertLocalStore(induc_, sub, 0);
- HInstruction* store = InsertArrayStore(induc_, 0);
- PerformInductionVarAnalysis();
-
- EXPECT_STREQ(
- "(( - (2:Constant)) * i + ((3:Constant) - (1:Constant)))",
- iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
-}
-
-TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarMul) {
- // Setup:
- // for (int i = 0; i < 100; i++) {
- // k = 100 * i;
- // a[k] = 0;
- // }
- BuildLoopNest(1);
HInstruction *mul = InsertInstruction(
- new (&allocator_) HMul(
- Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
+ new (&allocator_) HMul(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
InsertLocalStore(induc_, mul, 0);
- HInstruction* store = InsertArrayStore(induc_, 0);
- PerformInductionVarAnalysis();
-
- EXPECT_STREQ(
- "(((3:Constant) * (2:Constant)) * i + ((3:Constant) * (1:Constant)))",
- iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
-}
-
-TEST_F(InductionVarAnalysisTest, FindDerivedInductionVarNeg) {
- // Setup:
- // for (int i = 0; i < 100; i++) {
- // k = - i;
- // a[k] = 0;
- // }
- BuildLoopNest(1);
+ HInstruction *shl = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0), constant1_), 0);
+ InsertLocalStore(induc_, shl, 0);
HInstruction *neg = InsertInstruction(
- new (&allocator_) HNeg(
- Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0)), 0);
+ new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0)), 0);
InsertLocalStore(induc_, neg, 0);
- HInstruction* store = InsertArrayStore(induc_, 0);
PerformInductionVarAnalysis();
- EXPECT_STREQ(
- "(( - (2:Constant)) * i + ( - (1:Constant)))",
- iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
+ EXPECT_STREQ("((1) * i + (100))", GetInductionInfo(add, 0).c_str());
+ EXPECT_STREQ("(( - (1)) * i + (100))", GetInductionInfo(sub, 0).c_str());
+ EXPECT_STREQ("((100) * i + (0))", GetInductionInfo(mul, 0).c_str());
+ EXPECT_STREQ("((2) * i + (0))", GetInductionInfo(shl, 0).c_str());
+ EXPECT_STREQ("(( - (1)) * i + (0))", GetInductionInfo(neg, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindChainInduction) {
// Setup:
// k = 0;
// for (int i = 0; i < 100; i++) {
- // k = k + 100;
- // a[k] = 0;
- // k = k - 1;
- // a[k] = 0;
+ // k = k + 100;
+ // a[k] = 0;
+ // k = k - 1;
+ // a[k] = 0;
// }
BuildLoopNest(1);
HInstruction *add = InsertInstruction(
- new (&allocator_) HAdd(
- Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
InsertLocalStore(induc_, add, 0);
HInstruction* store1 = InsertArrayStore(induc_, 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(
- Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
InsertLocalStore(induc_, sub, 0);
HInstruction* store2 = InsertArrayStore(induc_, 0);
PerformInductionVarAnalysis();
- EXPECT_STREQ(
- "(((3:Constant) - (2:Constant)) * i + ((1:Constant) + (3:Constant)))",
- iva_->InductionToString(GetLoopInfo(0), store1->InputAt(1)).c_str());
- EXPECT_STREQ(
- "(((3:Constant) - (2:Constant)) * i + "
- "(((1:Constant) + (3:Constant)) - (2:Constant)))",
- iva_->InductionToString(GetLoopInfo(0), store2->InputAt(1)).c_str());
+ EXPECT_STREQ("(((100) - (1)) * i + (100))",
+ GetInductionInfo(store1->InputAt(1), 0).c_str());
+ EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1)))",
+ GetInductionInfo(store2->InputAt(1), 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) {
// Setup:
// k = 0;
// for (int i = 0; i < 100; i++) {
- // if () k = k + 1;
- // else k = k + 1;
- // a[k] = 0;
+ // if () k = k + 1;
+ // else k = k + 1;
+ // a[k] = 0;
// }
BuildLoopNest(1);
HBasicBlock* ifTrue;
HBasicBlock* ifFalse;
BuildIf(0, &ifTrue, &ifFalse);
// True-branch.
- HInstruction* load1 = new (&allocator_)
- HLoadLocal(induc_, Primitive::kPrimInt);
+ HInstruction* load1 = new (&allocator_) HLoadLocal(induc_, Primitive::kPrimInt);
ifTrue->AddInstruction(load1);
- HInstruction* inc1 = new (&allocator_)
- HAdd(Primitive::kPrimInt, load1, constant1_);
+ HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, load1, constant1_);
ifTrue->AddInstruction(inc1);
ifTrue->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc1));
// False-branch.
- HInstruction* load2 = new (&allocator_)
- HLoadLocal(induc_, Primitive::kPrimInt);
+ HInstruction* load2 = new (&allocator_) HLoadLocal(induc_, Primitive::kPrimInt);
ifFalse->AddInstruction(load2);
- HInstruction* inc2 = new (&allocator_)
- HAdd(Primitive::kPrimInt, load2, constant1_);
+ HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, load2, constant1_);
ifFalse->AddInstruction(inc2);
ifFalse->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc2));
// Merge over a phi.
HInstruction* store = InsertArrayStore(induc_, 0);
PerformInductionVarAnalysis();
- EXPECT_STREQ(
- "((2:Constant) * i + ((1:Constant) + (2:Constant)))",
- iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
+ EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) {
// Setup:
// for (int i = 0; i < 100; i++) {
- // if () k = i + 1;
- // else k = i + 1;
- // a[k] = 0;
+ // if () k = i + 1;
+ // else k = i + 1;
+ // a[k] = 0;
// }
BuildLoopNest(1);
HBasicBlock* ifTrue;
HBasicBlock* ifFalse;
BuildIf(0, &ifTrue, &ifFalse);
// True-branch.
- HInstruction* load1 = new (&allocator_)
- HLoadLocal(basic_[0], Primitive::kPrimInt);
+ HInstruction* load1 = new (&allocator_) HLoadLocal(basic_[0], Primitive::kPrimInt);
ifTrue->AddInstruction(load1);
- HInstruction* inc1 = new (&allocator_)
- HAdd(Primitive::kPrimInt, load1, constant1_);
+ HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, load1, constant1_);
ifTrue->AddInstruction(inc1);
ifTrue->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc1));
// False-branch.
- HInstruction* load2 = new (&allocator_)
- HLoadLocal(basic_[0], Primitive::kPrimInt);
+ HInstruction* load2 = new (&allocator_) HLoadLocal(basic_[0], Primitive::kPrimInt);
ifFalse->AddInstruction(load2);
- HInstruction* inc2 = new (&allocator_)
- HAdd(Primitive::kPrimInt, load2, constant1_);
+ HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, load2, constant1_);
ifFalse->AddInstruction(inc2);
ifFalse->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc2));
// Merge over a phi.
HInstruction* store = InsertArrayStore(induc_, 0);
PerformInductionVarAnalysis();
- EXPECT_STREQ(
- "((2:Constant) * i + ((1:Constant) + (2:Constant)))",
- iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
+ EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
// Setup:
// k = 0;
// for (int i = 0; i < 100; i++) {
- // a[k] = 0;
- // k = 100 - i;
+ // a[k] = 0;
+ // k = 100 - i;
// }
BuildLoopNest(1);
HInstruction* store = InsertArrayStore(induc_, 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(
- Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
InsertLocalStore(induc_, sub, 0);
PerformInductionVarAnalysis();
- EXPECT_STREQ(
- "wrap((1:Constant), "
- "(( - (2:Constant)) * i + ((3:Constant) - (1:Constant))))",
- iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
+ EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)))",
+ GetInductionInfo(store->InputAt(1), 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
@@ -453,23 +380,144 @@
// k = 0;
// t = 100;
// for (int i = 0; i < 100; i++) {
- // a[k] = 0;
- // k = t;
- // t = 100 - i;
+ // a[k] = 0;
+ // k = t;
+ // t = 100 - i;
// }
BuildLoopNest(1);
HInstruction* store = InsertArrayStore(induc_, 0);
InsertLocalStore(induc_, InsertLocalLoad(tmp_, 0), 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(
- Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
InsertLocalStore(tmp_, sub, 0);
PerformInductionVarAnalysis();
- EXPECT_STREQ(
- "wrap((1:Constant), wrap((3:Constant), "
- "(( - (2:Constant)) * i + ((3:Constant) - (1:Constant)))))",
- iva_->InductionToString(GetLoopInfo(0), store->InputAt(1)).c_str());
+ EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100))))",
+ GetInductionInfo(store->InputAt(1), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) {
+ // Setup:
+ // k = 0;
+ // for (int i = 0; i < 100; i++) {
+ // t = k + 100;
+ // t = k - 100;
+ // t = k * 100;
+ // t = k << 1;
+ // t = - k;
+ // k = i << 1;
+ // }
+ BuildLoopNest(1);
+ HInstruction *add = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+ InsertLocalStore(tmp_, add, 0);
+ HInstruction *sub = InsertInstruction(
+ new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+ InsertLocalStore(tmp_, sub, 0);
+ HInstruction *mul = InsertInstruction(
+ new (&allocator_) HMul(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+ InsertLocalStore(tmp_, mul, 0);
+ HInstruction *shl = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
+ InsertLocalStore(tmp_, shl, 0);
+ HInstruction *neg = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(induc_, 0)), 0);
+ InsertLocalStore(tmp_, neg, 0);
+ InsertLocalStore(
+ induc_,
+ InsertInstruction(
+ new (&allocator_)
+ HShl(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0), constant1_), 0), 0);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("wrap((100), ((2) * i + (100)))", GetInductionInfo(add, 0).c_str());
+ EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))))", GetInductionInfo(sub, 0).c_str());
+ EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)))", GetInductionInfo(mul, 0).c_str());
+ EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)))", GetInductionInfo(shl, 0).c_str());
+ EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)))", GetInductionInfo(neg, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
+ // Setup:
+ // k = 0;
+ // t = 100;
+ // for (int i = 0; i < 100; i++) {
+ // a[k] = 0;
+ // a[t] = 0;
+ // // Swap t <-> k.
+ // d = t;
+ // t = k;
+ // k = d;
+ // }
+ BuildLoopNest(1);
+ HInstruction* store1 = InsertArrayStore(induc_, 0);
+ HInstruction* store2 = InsertArrayStore(tmp_, 0);
+ InsertLocalStore(dum_, InsertLocalLoad(tmp_, 0), 0);
+ InsertLocalStore(tmp_, InsertLocalLoad(induc_, 0), 0);
+ InsertLocalStore(induc_, InsertLocalLoad(dum_, 0), 0);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("periodic((0), (100))", GetInductionInfo(store1->InputAt(1), 0).c_str());
+ EXPECT_STREQ("periodic((100), (0))", GetInductionInfo(store2->InputAt(1), 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) {
+ // Setup:
+ // k = 0;
+ // for (int i = 0; i < 100; i++) {
+ // a[k] = 0;
+ // k = 1 - k;
+ // }
+ BuildLoopNest(1);
+ HInstruction* store = InsertArrayStore(induc_, 0);
+ HInstruction *sub = InsertInstruction(
+ new (&allocator_) HSub(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 0)), 0);
+ InsertLocalStore(induc_, sub, 0);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("periodic((0), (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
+ EXPECT_STREQ("periodic((1), (0))", GetInductionInfo(sub, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
+ // Setup:
+ // k = 0;
+ // for (int i = 0; i < 100; i++) {
+ // k = 1 - k;
+ // t = k + 100;
+ // t = k - 100;
+ // t = k * 100;
+ // t = k << 1;
+ // t = - k;
+ // }
+ BuildLoopNest(1);
+ InsertLocalStore(
+ induc_,
+ InsertInstruction(new (&allocator_)
+ HSub(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 0)), 0), 0);
+ // Derived expressions.
+ HInstruction *add = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+ InsertLocalStore(tmp_, add, 0);
+ HInstruction *sub = InsertInstruction(
+ new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+ InsertLocalStore(tmp_, sub, 0);
+ HInstruction *mul = InsertInstruction(
+ new (&allocator_) HMul(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
+ InsertLocalStore(tmp_, mul, 0);
+ HInstruction *shl = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
+ InsertLocalStore(tmp_, shl, 0);
+ HInstruction *neg = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(induc_, 0)), 0);
+ InsertLocalStore(tmp_, neg, 0);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("periodic(((1) + (100)), (100))", GetInductionInfo(add, 0).c_str());
+ EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100)))", GetInductionInfo(sub, 0).c_str());
+ EXPECT_STREQ("periodic((100), (0))", GetInductionInfo(mul, 0).c_str());
+ EXPECT_STREQ("periodic((2), (0))", GetInductionInfo(shl, 0).c_str());
+ EXPECT_STREQ("periodic(( - (1)), (0))", GetInductionInfo(neg, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
@@ -485,29 +533,24 @@
// }
BuildLoopNest(10);
HInstruction *inc = InsertInstruction(
- new (&allocator_) HAdd(
- Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 9)), 9);
+ new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 9)), 9);
InsertLocalStore(induc_, inc, 9);
HInstruction* store = InsertArrayStore(induc_, 9);
PerformInductionVarAnalysis();
- // Match exact number of constants, but be less strict on phi number,
- // since that depends on the SSA building phase.
- std::regex r("\\(\\(2:Constant\\) \\* i \\+ "
- "\\(\\(2:Constant\\) \\+ \\(\\d+:Phi\\)\\)\\)");
+ // Avoid exact phi number, since that depends on the SSA building phase.
+ std::regex r("\\(\\(1\\) \\* i \\+ "
+ "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\)");
for (int d = 0; d < 10; d++) {
if (d == 9) {
- EXPECT_TRUE(std::regex_match(
- iva_->InductionToString(GetLoopInfo(d), store->InputAt(1)), r));
+ EXPECT_TRUE(std::regex_match(GetInductionInfo(store->InputAt(1), d), r));
} else {
- EXPECT_STREQ(
- "",
- iva_->InductionToString(GetLoopInfo(d), store->InputAt(1)).c_str());
+ EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str());
}
- EXPECT_STREQ(
- "((2:Constant) * i + ((1:Constant) + (2:Constant)))",
- iva_->InductionToString(GetLoopInfo(d), increment_[d]).c_str());
+ EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(increment_[d], d).c_str());
+ // Trip-count.
+ EXPECT_STREQ("(100)", GetInductionInfo(loop_header_[d]->GetLastInstruction(), d).c_str());
}
}
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
new file mode 100644
index 0000000..bd90334
--- /dev/null
+++ b/compiler/optimizing/induction_var_range.cc
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits.h>
+
+#include "induction_var_range.h"
+
+namespace art {
+
+static bool IsValidConstant32(int32_t c) {
+ return INT_MIN < c && c < INT_MAX;
+}
+
+static bool IsValidConstant64(int64_t c) {
+ return INT_MIN < c && c < INT_MAX;
+}
+
+/** Returns true if 32-bit addition can be done safely (and is not an unknown range). */
+static bool IsSafeAdd(int32_t c1, int32_t c2) {
+ if (IsValidConstant32(c1) && IsValidConstant32(c2)) {
+ return IsValidConstant64(static_cast<int64_t>(c1) + static_cast<int64_t>(c2));
+ }
+ return false;
+}
+
+/** Returns true if 32-bit subtraction can be done safely (and is not an unknown range). */
+static bool IsSafeSub(int32_t c1, int32_t c2) {
+ if (IsValidConstant32(c1) && IsValidConstant32(c2)) {
+ return IsValidConstant64(static_cast<int64_t>(c1) - static_cast<int64_t>(c2));
+ }
+ return false;
+}
+
+/** Returns true if 32-bit multiplication can be done safely (and is not an unknown range). */
+static bool IsSafeMul(int32_t c1, int32_t c2) {
+ if (IsValidConstant32(c1) && IsValidConstant32(c2)) {
+ return IsValidConstant64(static_cast<int64_t>(c1) * static_cast<int64_t>(c2));
+ }
+ return false;
+}
+
+/** Returns true if 32-bit division can be done safely (and is not an unknown range). */
+static bool IsSafeDiv(int32_t c1, int32_t c2) {
+ if (IsValidConstant32(c1) && IsValidConstant32(c2) && c2 != 0) {
+ return IsValidConstant64(static_cast<int64_t>(c1) / static_cast<int64_t>(c2));
+ }
+ return false;
+}
+
+/** Returns true for 32/64-bit integral constant within known range. */
+static bool IsIntAndGet(HInstruction* instruction, int32_t* value) {
+ if (instruction->IsIntConstant()) {
+ const int32_t c = instruction->AsIntConstant()->GetValue();
+ if (IsValidConstant32(c)) {
+ *value = c;
+ return true;
+ }
+ } else if (instruction->IsLongConstant()) {
+ const int64_t c = instruction->AsLongConstant()->GetValue();
+ if (IsValidConstant64(c)) {
+ *value = c;
+ return true;
+ }
+ }
+ return false;
+}
+
+//
+// Public class methods.
+//
+
+InductionVarRange::InductionVarRange(HInductionVarAnalysis* induction_analysis)
+ : induction_analysis_(induction_analysis) {
+}
+
+InductionVarRange::Value InductionVarRange::GetMinInduction(HInstruction* context,
+ HInstruction* instruction) {
+ HLoopInformation* loop = context->GetBlock()->GetLoopInformation();
+ if (loop != nullptr && induction_analysis_ != nullptr) {
+ return GetMin(induction_analysis_->LookupInfo(loop, instruction), GetTripCount(loop, context));
+ }
+ return Value(INT_MIN);
+}
+
+InductionVarRange::Value InductionVarRange::GetMaxInduction(HInstruction* context,
+ HInstruction* instruction) {
+ HLoopInformation* loop = context->GetBlock()->GetLoopInformation();
+ if (loop != nullptr && induction_analysis_ != nullptr) {
+ return GetMax(induction_analysis_->LookupInfo(loop, instruction), GetTripCount(loop, context));
+ }
+ return Value(INT_MAX);
+}
+
+//
+// Private class methods.
+//
+
+HInductionVarAnalysis::InductionInfo* InductionVarRange::GetTripCount(HLoopInformation* loop,
+ HInstruction* context) {
+ // The trip-count expression is only valid when the top-test is taken at least once,
+ // that means, when the analyzed context appears outside the loop header itself.
+ // Early-exit loops are okay, since in those cases, the trip-count is conservative.
+ if (context->GetBlock() != loop->GetHeader()) {
+ HInductionVarAnalysis::InductionInfo* trip =
+ induction_analysis_->LookupInfo(loop, loop->GetHeader()->GetLastInstruction());
+ if (trip != nullptr) {
+ // Wrap the trip-count representation in its own unusual NOP node, so that range analysis
+ // is able to determine the [0, TC - 1] interval without having to construct constants.
+ return induction_analysis_->CreateInvariantOp(HInductionVarAnalysis::kNop, trip, trip);
+ }
+ }
+ return nullptr;
+}
+
+InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
+ int32_t fail_value) {
+ // Detect constants and chase the fetch a bit deeper into the HIR tree, so that it becomes
+ // more likely range analysis will compare the same instructions as terminal nodes.
+ int32_t value;
+ if (IsIntAndGet(instruction, &value)) {
+ return Value(value);
+ } else if (instruction->IsAdd()) {
+ if (IsIntAndGet(instruction->InputAt(0), &value)) {
+ return AddValue(Value(value), GetFetch(instruction->InputAt(1), fail_value), fail_value);
+ } else if (IsIntAndGet(instruction->InputAt(1), &value)) {
+ return AddValue(GetFetch(instruction->InputAt(0), fail_value), Value(value), fail_value);
+ }
+ }
+ return Value(instruction, 1, 0);
+}
+
+InductionVarRange::Value InductionVarRange::GetMin(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip) {
+ if (info != nullptr) {
+ switch (info->induction_class) {
+ case HInductionVarAnalysis::kInvariant:
+ // Invariants.
+ switch (info->operation) {
+ case HInductionVarAnalysis::kNop: // normalized: 0
+ DCHECK_EQ(info->op_a, info->op_b);
+ return Value(0);
+ case HInductionVarAnalysis::kAdd:
+ return AddValue(GetMin(info->op_a, trip), GetMin(info->op_b, trip), INT_MIN);
+ case HInductionVarAnalysis::kSub: // second max!
+ return SubValue(GetMin(info->op_a, trip), GetMax(info->op_b, trip), INT_MIN);
+ case HInductionVarAnalysis::kNeg: // second max!
+ return SubValue(Value(0), GetMax(info->op_b, trip), INT_MIN);
+ case HInductionVarAnalysis::kMul:
+ return GetMul(info->op_a, info->op_b, trip, INT_MIN);
+ case HInductionVarAnalysis::kDiv:
+ return GetDiv(info->op_a, info->op_b, trip, INT_MIN);
+ case HInductionVarAnalysis::kFetch:
+ return GetFetch(info->fetch, INT_MIN);
+ }
+ break;
+ case HInductionVarAnalysis::kLinear:
+ // Minimum over linear induction a * i + b, for normalized 0 <= i < TC.
+ return AddValue(GetMul(info->op_a, trip, trip, INT_MIN),
+ GetMin(info->op_b, trip), INT_MIN);
+ case HInductionVarAnalysis::kWrapAround:
+ case HInductionVarAnalysis::kPeriodic:
+ // Minimum over all values in the wrap-around/periodic.
+ return MinValue(GetMin(info->op_a, trip), GetMin(info->op_b, trip));
+ }
+ }
+ return Value(INT_MIN);
+}
+
+InductionVarRange::Value InductionVarRange::GetMax(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip) {
+ if (info != nullptr) {
+ switch (info->induction_class) {
+ case HInductionVarAnalysis::kInvariant:
+ // Invariants.
+ switch (info->operation) {
+ case HInductionVarAnalysis::kNop: // normalized: TC - 1
+ DCHECK_EQ(info->op_a, info->op_b);
+ return SubValue(GetMax(info->op_b, trip), Value(1), INT_MAX);
+ case HInductionVarAnalysis::kAdd:
+ return AddValue(GetMax(info->op_a, trip), GetMax(info->op_b, trip), INT_MAX);
+ case HInductionVarAnalysis::kSub: // second min!
+ return SubValue(GetMax(info->op_a, trip), GetMin(info->op_b, trip), INT_MAX);
+ case HInductionVarAnalysis::kNeg: // second min!
+ return SubValue(Value(0), GetMin(info->op_b, trip), INT_MAX);
+ case HInductionVarAnalysis::kMul:
+ return GetMul(info->op_a, info->op_b, trip, INT_MAX);
+ case HInductionVarAnalysis::kDiv:
+ return GetDiv(info->op_a, info->op_b, trip, INT_MAX);
+ case HInductionVarAnalysis::kFetch:
+ return GetFetch(info->fetch, INT_MAX);
+ }
+ break;
+ case HInductionVarAnalysis::kLinear:
+ // Maximum over linear induction a * i + b, for normalized 0 <= i < TC.
+ return AddValue(GetMul(info->op_a, trip, trip, INT_MAX),
+ GetMax(info->op_b, trip), INT_MAX);
+ case HInductionVarAnalysis::kWrapAround:
+ case HInductionVarAnalysis::kPeriodic:
+ // Maximum over all values in the wrap-around/periodic.
+ return MaxValue(GetMax(info->op_a, trip), GetMax(info->op_b, trip));
+ }
+ }
+ return Value(INT_MAX);
+}
+
+InductionVarRange::Value InductionVarRange::GetMul(HInductionVarAnalysis::InductionInfo* info1,
+ HInductionVarAnalysis::InductionInfo* info2,
+ HInductionVarAnalysis::InductionInfo* trip,
+ int32_t fail_value) {
+ Value v1_min = GetMin(info1, trip);
+ Value v1_max = GetMax(info1, trip);
+ Value v2_min = GetMin(info2, trip);
+ Value v2_max = GetMax(info2, trip);
+ if (v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
+ // Positive range vs. positive or negative range.
+ if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
+ return (fail_value < 0) ? MulValue(v1_min, v2_min, fail_value)
+ : MulValue(v1_max, v2_max, fail_value);
+ } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
+ return (fail_value < 0) ? MulValue(v1_max, v2_min, fail_value)
+ : MulValue(v1_min, v2_max, fail_value);
+ }
+ } else if (v1_min.a_constant == 0 && v1_min.b_constant <= 0) {
+ // Negative range vs. positive or negative range.
+ if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
+ return (fail_value < 0) ? MulValue(v1_min, v2_max, fail_value)
+ : MulValue(v1_max, v2_min, fail_value);
+ } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
+ return (fail_value < 0) ? MulValue(v1_max, v2_max, fail_value)
+ : MulValue(v1_min, v2_min, fail_value);
+ }
+ }
+ return Value(fail_value);
+}
+
+InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::InductionInfo* info1,
+ HInductionVarAnalysis::InductionInfo* info2,
+ HInductionVarAnalysis::InductionInfo* trip,
+ int32_t fail_value) {
+ Value v1_min = GetMin(info1, trip);
+ Value v1_max = GetMax(info1, trip);
+ Value v2_min = GetMin(info2, trip);
+ Value v2_max = GetMax(info2, trip);
+ if (v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
+ // Positive range vs. positive or negative range.
+ if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
+ return (fail_value < 0) ? DivValue(v1_min, v2_max, fail_value)
+ : DivValue(v1_max, v2_min, fail_value);
+ } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
+ return (fail_value < 0) ? DivValue(v1_max, v2_max, fail_value)
+ : DivValue(v1_min, v2_min, fail_value);
+ }
+ } else if (v1_min.a_constant == 0 && v1_min.b_constant <= 0) {
+ // Negative range vs. positive or negative range.
+ if (v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
+ return (fail_value < 0) ? DivValue(v1_min, v2_min, fail_value)
+ : DivValue(v1_max, v2_max, fail_value);
+ } else if (v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
+ return (fail_value < 0) ? DivValue(v1_max, v2_min, fail_value)
+ : DivValue(v1_min, v2_max, fail_value);
+ }
+ }
+ return Value(fail_value);
+}
+
+InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2, int32_t fail_value) {
+ if (IsSafeAdd(v1.b_constant, v2.b_constant)) {
+ const int32_t b = v1.b_constant + v2.b_constant;
+ if (v1.a_constant == 0) {
+ return Value(v2.instruction, v2.a_constant, b);
+ } else if (v2.a_constant == 0) {
+ return Value(v1.instruction, v1.a_constant, b);
+ } else if (v1.instruction == v2.instruction && IsSafeAdd(v1.a_constant, v2.a_constant)) {
+ return Value(v1.instruction, v1.a_constant + v2.a_constant, b);
+ }
+ }
+ return Value(fail_value);
+}
+
+InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2, int32_t fail_value) {
+ if (IsSafeSub(v1.b_constant, v2.b_constant)) {
+ const int32_t b = v1.b_constant - v2.b_constant;
+ if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
+ return Value(v2.instruction, -v2.a_constant, b);
+ } else if (v2.a_constant == 0) {
+ return Value(v1.instruction, v1.a_constant, b);
+ } else if (v1.instruction == v2.instruction && IsSafeSub(v1.a_constant, v2.a_constant)) {
+ return Value(v1.instruction, v1.a_constant - v2.a_constant, b);
+ }
+ }
+ return Value(fail_value);
+}
+
+InductionVarRange::Value InductionVarRange::MulValue(Value v1, Value v2, int32_t fail_value) {
+ if (v1.a_constant == 0) {
+ if (IsSafeMul(v1.b_constant, v2.a_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
+ return Value(v2.instruction, v1.b_constant * v2.a_constant, v1.b_constant * v2.b_constant);
+ }
+ } else if (v2.a_constant == 0) {
+ if (IsSafeMul(v1.a_constant, v2.b_constant) && IsSafeMul(v1.b_constant, v2.b_constant)) {
+ return Value(v1.instruction, v1.a_constant * v2.b_constant, v1.b_constant * v2.b_constant);
+ }
+ }
+ return Value(fail_value);
+}
+
+InductionVarRange::Value InductionVarRange::DivValue(Value v1, Value v2, int32_t fail_value) {
+ if (v1.a_constant == 0 && v2.a_constant == 0) {
+ if (IsSafeDiv(v1.b_constant, v2.b_constant)) {
+ return Value(v1.b_constant / v2.b_constant);
+ }
+ }
+ return Value(fail_value);
+}
+
+InductionVarRange::Value InductionVarRange::MinValue(Value v1, Value v2) {
+ if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
+ return Value(v1.instruction, v1.a_constant, std::min(v1.b_constant, v2.b_constant));
+ }
+ return Value(INT_MIN);
+}
+
+InductionVarRange::Value InductionVarRange::MaxValue(Value v1, Value v2) {
+ if (v1.instruction == v2.instruction && v1.a_constant == v2.a_constant) {
+ return Value(v1.instruction, v1.a_constant, std::max(v1.b_constant, v2.b_constant));
+ }
+ return Value(INT_MAX);
+}
+
+} // namespace art
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
new file mode 100644
index 0000000..b079076
--- /dev/null
+++ b/compiler/optimizing/induction_var_range.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INDUCTION_VAR_RANGE_H_
+#define ART_COMPILER_OPTIMIZING_INDUCTION_VAR_RANGE_H_
+
+#include "induction_var_analysis.h"
+
+namespace art {
+
+/**
+ * This class implements induction variable based range analysis on expressions within loops.
+ * It takes the results of induction variable analysis in the constructor and provides a public
+ * API to obtain a conservative lower and upper bound value on each instruction in the HIR.
+ *
+ * For example, given a linear induction 2 * i + x where 0 <= i <= 10, range analysis yields lower
+ * bound value x and upper bound value x + 20 for the expression, thus, the range [0, x + 20].
+ */
+class InductionVarRange {
+ public:
+ /*
+ * A value that can be represented as "a * instruction + b" for 32-bit constants, where
+ * Value(INT_MIN) and Value(INT_MAX) denote an unknown lower and upper bound, respectively.
+ * Although range analysis could yield more complex values, the format is sufficiently powerful
+ * to represent useful cases and feeds directly into optimizations like bounds check elimination.
+ */
+ struct Value {
+ Value(HInstruction* i, int32_t a, int32_t b)
+ : instruction(a ? i : nullptr),
+ a_constant(a),
+ b_constant(b) {}
+ explicit Value(int32_t b) : Value(nullptr, 0, b) {}
+ HInstruction* instruction;
+ int32_t a_constant;
+ int32_t b_constant;
+ };
+
+ explicit InductionVarRange(HInductionVarAnalysis* induction);
+
+ /**
+ * Given a context denoted by the first instruction, returns a,
+ * possibly conservative, lower bound on the instruction's value.
+ */
+ Value GetMinInduction(HInstruction* context, HInstruction* instruction);
+
+ /**
+ * Given a context denoted by the first instruction, returns a,
+ * possibly conservative, upper bound on the instruction's value.
+ */
+ Value GetMaxInduction(HInstruction* context, HInstruction* instruction);
+
+ private:
+ //
+ // Private helper methods.
+ //
+
+ HInductionVarAnalysis::InductionInfo* GetTripCount(HLoopInformation* loop,
+ HInstruction* context);
+
+ static Value GetFetch(HInstruction* instruction, int32_t fail_value);
+
+ static Value GetMin(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip);
+ static Value GetMax(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip);
+ static Value GetMul(HInductionVarAnalysis::InductionInfo* info1,
+ HInductionVarAnalysis::InductionInfo* info2,
+ HInductionVarAnalysis::InductionInfo* trip, int32_t fail_value);
+ static Value GetDiv(HInductionVarAnalysis::InductionInfo* info1,
+ HInductionVarAnalysis::InductionInfo* info2,
+ HInductionVarAnalysis::InductionInfo* trip, int32_t fail_value);
+
+ static Value AddValue(Value v1, Value v2, int32_t fail_value);
+ static Value SubValue(Value v1, Value v2, int32_t fail_value);
+ static Value MulValue(Value v1, Value v2, int32_t fail_value);
+ static Value DivValue(Value v1, Value v2, int32_t fail_value);
+ static Value MinValue(Value v1, Value v2);
+ static Value MaxValue(Value v1, Value v2);
+
+ /** Results of prior induction variable analysis. */
+ HInductionVarAnalysis *induction_analysis_;
+
+ friend class InductionVarRangeTest;
+
+ DISALLOW_COPY_AND_ASSIGN(InductionVarRange);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INDUCTION_VAR_RANGE_H_
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
new file mode 100644
index 0000000..d3c3518
--- /dev/null
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits.h>
+
+#include "base/arena_allocator.h"
+#include "builder.h"
+#include "gtest/gtest.h"
+#include "induction_var_analysis.h"
+#include "induction_var_range.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+
+namespace art {
+
+using Value = InductionVarRange::Value;
+
+/**
+ * Fixture class for the InductionVarRange tests.
+ */
+class InductionVarRangeTest : public testing::Test {
+ public:
+ InductionVarRangeTest() : pool_(), allocator_(&pool_) {
+ graph_ = CreateGraph(&allocator_);
+ iva_ = new (&allocator_) HInductionVarAnalysis(graph_);
+ BuildGraph();
+ }
+
+ ~InductionVarRangeTest() { }
+
+ void ExpectEqual(Value v1, Value v2) {
+ EXPECT_EQ(v1.instruction, v2.instruction);
+ EXPECT_EQ(v1.a_constant, v2.a_constant);
+ EXPECT_EQ(v1.b_constant, v2.b_constant);
+ }
+
+ /** Constructs bare minimum graph. */
+ void BuildGraph() {
+ graph_->SetNumberOfVRegs(1);
+ HBasicBlock* entry_block = new (&allocator_) HBasicBlock(graph_);
+ HBasicBlock* exit_block = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(entry_block);
+ graph_->AddBlock(exit_block);
+ graph_->SetEntryBlock(entry_block);
+ graph_->SetExitBlock(exit_block);
+ }
+
+ /** Constructs an invariant. */
+ HInductionVarAnalysis::InductionInfo* CreateInvariant(char opc,
+ HInductionVarAnalysis::InductionInfo* a,
+ HInductionVarAnalysis::InductionInfo* b) {
+ HInductionVarAnalysis::InductionOp op;
+ switch (opc) {
+ case '+': op = HInductionVarAnalysis::kAdd; break;
+ case '-': op = HInductionVarAnalysis::kSub; break;
+ case 'n': op = HInductionVarAnalysis::kNeg; break;
+ case '*': op = HInductionVarAnalysis::kMul; break;
+ case '/': op = HInductionVarAnalysis::kDiv; break;
+ default: op = HInductionVarAnalysis::kNop; break;
+ }
+ return iva_->CreateInvariantOp(op, a, b);
+ }
+
+ /** Constructs a fetch. */
+ HInductionVarAnalysis::InductionInfo* CreateFetch(HInstruction* fetch) {
+ return iva_->CreateInvariantFetch(fetch);
+ }
+
+ /** Constructs a constant. */
+ HInductionVarAnalysis::InductionInfo* CreateConst(int32_t c) {
+ return CreateFetch(graph_->GetIntConstant(c));
+ }
+
+ /** Constructs a trip-count. */
+ HInductionVarAnalysis::InductionInfo* CreateTripCount(int32_t tc) {
+ HInductionVarAnalysis::InductionInfo* trip = CreateConst(tc);
+ return CreateInvariant('@', trip, trip);
+ }
+
+ /** Constructs a linear a * i + b induction. */
+ HInductionVarAnalysis::InductionInfo* CreateLinear(int32_t a, int32_t b) {
+ return iva_->CreateInduction(HInductionVarAnalysis::kLinear, CreateConst(a), CreateConst(b));
+ }
+
+ /** Constructs a range [lo, hi] using a periodic induction. */
+ HInductionVarAnalysis::InductionInfo* CreateRange(int32_t lo, int32_t hi) {
+ return iva_->CreateInduction(
+ HInductionVarAnalysis::kPeriodic, CreateConst(lo), CreateConst(hi));
+ }
+
+ /** Constructs a wrap-around induction consisting of a constant, followed by a range. */
+ HInductionVarAnalysis::InductionInfo* CreateWrapAround(int32_t initial, int32_t lo, int32_t hi) {
+ return iva_->CreateInduction(
+ HInductionVarAnalysis::kWrapAround, CreateConst(initial), CreateRange(lo, hi));
+ }
+
+ //
+ // Relay methods.
+ //
+
+ Value GetMin(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* induc) {
+ return InductionVarRange::GetMin(info, induc);
+ }
+
+ Value GetMax(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* induc) {
+ return InductionVarRange::GetMax(info, induc);
+ }
+
+ Value GetMul(HInductionVarAnalysis::InductionInfo* info1,
+ HInductionVarAnalysis::InductionInfo* info2, int32_t fail_value) {
+ return InductionVarRange::GetMul(info1, info2, nullptr, fail_value);
+ }
+
+ Value GetDiv(HInductionVarAnalysis::InductionInfo* info1,
+ HInductionVarAnalysis::InductionInfo* info2, int32_t fail_value) {
+ return InductionVarRange::GetDiv(info1, info2, nullptr, fail_value);
+ }
+
+ Value AddValue(Value v1, Value v2) { return InductionVarRange::AddValue(v1, v2, INT_MIN); }
+ Value SubValue(Value v1, Value v2) { return InductionVarRange::SubValue(v1, v2, INT_MIN); }
+ Value MulValue(Value v1, Value v2) { return InductionVarRange::MulValue(v1, v2, INT_MIN); }
+ Value DivValue(Value v1, Value v2) { return InductionVarRange::DivValue(v1, v2, INT_MIN); }
+ Value MinValue(Value v1, Value v2) { return InductionVarRange::MinValue(v1, v2); }
+ Value MaxValue(Value v1, Value v2) { return InductionVarRange::MaxValue(v1, v2); }
+
+ // General building fields.
+ ArenaPool pool_;
+ ArenaAllocator allocator_;
+ HGraph* graph_;
+ HInductionVarAnalysis* iva_;
+
+ // Two dummy instructions.
+ HReturnVoid x_;
+ HReturnVoid y_;
+};
+
+//
+// The actual InductionVarRange tests.
+//
+
+TEST_F(InductionVarRangeTest, GetMinMaxNull) {
+ ExpectEqual(Value(INT_MIN), GetMin(nullptr, nullptr));
+ ExpectEqual(Value(INT_MAX), GetMax(nullptr, nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxAdd) {
+ ExpectEqual(Value(12),
+ GetMin(CreateInvariant('+', CreateConst(2), CreateRange(10, 20)), nullptr));
+ ExpectEqual(Value(22),
+ GetMax(CreateInvariant('+', CreateConst(2), CreateRange(10, 20)), nullptr));
+ ExpectEqual(Value(&x_, 1, -20),
+ GetMin(CreateInvariant('+', CreateFetch(&x_), CreateRange(-20, -10)), nullptr));
+ ExpectEqual(Value(&x_, 1, -10),
+ GetMax(CreateInvariant('+', CreateFetch(&x_), CreateRange(-20, -10)), nullptr));
+ ExpectEqual(Value(&x_, 1, 10),
+ GetMin(CreateInvariant('+', CreateRange(10, 20), CreateFetch(&x_)), nullptr));
+ ExpectEqual(Value(&x_, 1, 20),
+ GetMax(CreateInvariant('+', CreateRange(10, 20), CreateFetch(&x_)), nullptr));
+ ExpectEqual(Value(5),
+ GetMin(CreateInvariant('+', CreateRange(-5, -1), CreateRange(10, 20)), nullptr));
+ ExpectEqual(Value(19),
+ GetMax(CreateInvariant('+', CreateRange(-5, -1), CreateRange(10, 20)), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxSub) {
+ ExpectEqual(Value(-18),
+ GetMin(CreateInvariant('-', CreateConst(2), CreateRange(10, 20)), nullptr));
+ ExpectEqual(Value(-8),
+ GetMax(CreateInvariant('-', CreateConst(2), CreateRange(10, 20)), nullptr));
+ ExpectEqual(Value(&x_, 1, 10),
+ GetMin(CreateInvariant('-', CreateFetch(&x_), CreateRange(-20, -10)), nullptr));
+ ExpectEqual(Value(&x_, 1, 20),
+ GetMax(CreateInvariant('-', CreateFetch(&x_), CreateRange(-20, -10)), nullptr));
+ ExpectEqual(Value(&x_, -1, 10),
+ GetMin(CreateInvariant('-', CreateRange(10, 20), CreateFetch(&x_)), nullptr));
+ ExpectEqual(Value(&x_, -1, 20),
+ GetMax(CreateInvariant('-', CreateRange(10, 20), CreateFetch(&x_)), nullptr));
+ ExpectEqual(Value(-25),
+ GetMin(CreateInvariant('-', CreateRange(-5, -1), CreateRange(10, 20)), nullptr));
+ ExpectEqual(Value(-11),
+ GetMax(CreateInvariant('-', CreateRange(-5, -1), CreateRange(10, 20)), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxNeg) {
+ ExpectEqual(Value(-20), GetMin(CreateInvariant('n', nullptr, CreateRange(10, 20)), nullptr));
+ ExpectEqual(Value(-10), GetMax(CreateInvariant('n', nullptr, CreateRange(10, 20)), nullptr));
+ ExpectEqual(Value(10), GetMin(CreateInvariant('n', nullptr, CreateRange(-20, -10)), nullptr));
+ ExpectEqual(Value(20), GetMax(CreateInvariant('n', nullptr, CreateRange(-20, -10)), nullptr));
+ ExpectEqual(Value(&x_, -1, 0), GetMin(CreateInvariant('n', nullptr, CreateFetch(&x_)), nullptr));
+ ExpectEqual(Value(&x_, -1, 0), GetMax(CreateInvariant('n', nullptr, CreateFetch(&x_)), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxMul) {
+ ExpectEqual(Value(20),
+ GetMin(CreateInvariant('*', CreateConst(2), CreateRange(10, 20)), nullptr));
+ ExpectEqual(Value(40),
+ GetMax(CreateInvariant('*', CreateConst(2), CreateRange(10, 20)), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxDiv) {
+ ExpectEqual(Value(3),
+ GetMin(CreateInvariant('/', CreateRange(12, 20), CreateConst(4)), nullptr));
+ ExpectEqual(Value(5),
+ GetMax(CreateInvariant('/', CreateRange(12, 20), CreateConst(4)), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxConstant) {
+ ExpectEqual(Value(12345), GetMin(CreateConst(12345), nullptr));
+ ExpectEqual(Value(12345), GetMax(CreateConst(12345), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxFetch) {
+ ExpectEqual(Value(&x_, 1, 0), GetMin(CreateFetch(&x_), nullptr));
+ ExpectEqual(Value(&x_, 1, 0), GetMax(CreateFetch(&x_), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxLinear) {
+ ExpectEqual(Value(20), GetMin(CreateLinear(10, 20), CreateTripCount(100)));
+ ExpectEqual(Value(1010), GetMax(CreateLinear(10, 20), CreateTripCount(100)));
+ ExpectEqual(Value(-970), GetMin(CreateLinear(-10, 20), CreateTripCount(100)));
+ ExpectEqual(Value(20), GetMax(CreateLinear(-10, 20), CreateTripCount(100)));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxWrapAround) {
+ ExpectEqual(Value(-5), GetMin(CreateWrapAround(-5, -1, 10), nullptr));
+ ExpectEqual(Value(10), GetMax(CreateWrapAround(-5, -1, 10), nullptr));
+ ExpectEqual(Value(-1), GetMin(CreateWrapAround(2, -1, 10), nullptr));
+ ExpectEqual(Value(10), GetMax(CreateWrapAround(2, -1, 10), nullptr));
+ ExpectEqual(Value(-1), GetMin(CreateWrapAround(20, -1, 10), nullptr));
+ ExpectEqual(Value(20), GetMax(CreateWrapAround(20, -1, 10), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxPeriodic) {
+ ExpectEqual(Value(-2), GetMin(CreateRange(-2, 99), nullptr));
+ ExpectEqual(Value(99), GetMax(CreateRange(-2, 99), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMulMin) {
+ ExpectEqual(Value(6), GetMul(CreateRange(2, 10), CreateRange(3, 5), INT_MIN));
+ ExpectEqual(Value(-50), GetMul(CreateRange(2, 10), CreateRange(-5, -3), INT_MIN));
+ ExpectEqual(Value(-50), GetMul(CreateRange(-10, -2), CreateRange(3, 5), INT_MIN));
+ ExpectEqual(Value(6), GetMul(CreateRange(-10, -2), CreateRange(-5, -3), INT_MIN));
+}
+
+TEST_F(InductionVarRangeTest, GetMulMax) {
+ ExpectEqual(Value(50), GetMul(CreateRange(2, 10), CreateRange(3, 5), INT_MAX));
+ ExpectEqual(Value(-6), GetMul(CreateRange(2, 10), CreateRange(-5, -3), INT_MAX));
+ ExpectEqual(Value(-6), GetMul(CreateRange(-10, -2), CreateRange(3, 5), INT_MAX));
+ ExpectEqual(Value(50), GetMul(CreateRange(-10, -2), CreateRange(-5, -3), INT_MAX));
+}
+
+TEST_F(InductionVarRangeTest, GetDivMin) {
+ ExpectEqual(Value(10), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), INT_MIN));
+ ExpectEqual(Value(-500), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), INT_MIN));
+ ExpectEqual(Value(-500), GetDiv(CreateRange(-1000, -40), CreateRange(2, 4), INT_MIN));
+ ExpectEqual(Value(10), GetDiv(CreateRange(-1000, -40), CreateRange(-4, -2), INT_MIN));
+}
+
+TEST_F(InductionVarRangeTest, GetDivMax) {
+ ExpectEqual(Value(500), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), INT_MAX));
+ ExpectEqual(Value(-10), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), INT_MAX));
+ ExpectEqual(Value(-10), GetDiv(CreateRange(-1000, -40), CreateRange(2, 4), INT_MAX));
+ ExpectEqual(Value(500), GetDiv(CreateRange(-1000, -40), CreateRange(-4, -2), INT_MAX));
+}
+
+TEST_F(InductionVarRangeTest, AddValue) {
+ ExpectEqual(Value(110), AddValue(Value(10), Value(100)));
+ ExpectEqual(Value(-5), AddValue(Value(&x_, 1, -4), Value(&x_, -1, -1)));
+ ExpectEqual(Value(&x_, 3, -5), AddValue(Value(&x_, 2, -4), Value(&x_, 1, -1)));
+ ExpectEqual(Value(INT_MIN), AddValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
+ ExpectEqual(Value(&x_, 1, 23), AddValue(Value(&x_, 1, 20), Value(3)));
+ ExpectEqual(Value(&y_, 1, 5), AddValue(Value(55), Value(&y_, 1, -50)));
+ // Unsafe.
+ ExpectEqual(Value(INT_MIN), AddValue(Value(INT_MAX - 5), Value(6)));
+}
+
+TEST_F(InductionVarRangeTest, SubValue) {
+ ExpectEqual(Value(-90), SubValue(Value(10), Value(100)));
+ ExpectEqual(Value(-3), SubValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
+ ExpectEqual(Value(&x_, 2, -3), SubValue(Value(&x_, 3, -4), Value(&x_, 1, -1)));
+ ExpectEqual(Value(INT_MIN), SubValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
+ ExpectEqual(Value(&x_, 1, 17), SubValue(Value(&x_, 1, 20), Value(3)));
+ ExpectEqual(Value(&y_, -4, 105), SubValue(Value(55), Value(&y_, 4, -50)));
+ // Unsafe.
+ ExpectEqual(Value(INT_MIN), SubValue(Value(INT_MIN + 5), Value(6)));
+}
+
+TEST_F(InductionVarRangeTest, MulValue) {
+ ExpectEqual(Value(1000), MulValue(Value(10), Value(100)));
+ ExpectEqual(Value(INT_MIN), MulValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
+ ExpectEqual(Value(INT_MIN), MulValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
+ ExpectEqual(Value(&x_, 9, 60), MulValue(Value(&x_, 3, 20), Value(3)));
+ ExpectEqual(Value(&y_, 55, -110), MulValue(Value(55), Value(&y_, 1, -2)));
+ // Unsafe.
+ ExpectEqual(Value(INT_MIN), MulValue(Value(90000), Value(-90000)));
+}
+
+TEST_F(InductionVarRangeTest, DivValue) {
+ ExpectEqual(Value(25), DivValue(Value(100), Value(4)));
+ ExpectEqual(Value(INT_MIN), DivValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
+ ExpectEqual(Value(INT_MIN), DivValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
+ ExpectEqual(Value(INT_MIN), DivValue(Value(&x_, 12, 24), Value(3)));
+ ExpectEqual(Value(INT_MIN), DivValue(Value(55), Value(&y_, 1, -50)));
+ // Unsafe.
+ ExpectEqual(Value(INT_MIN), DivValue(Value(1), Value(0)));
+}
+
+TEST_F(InductionVarRangeTest, MinValue) {
+ ExpectEqual(Value(10), MinValue(Value(10), Value(100)));
+ ExpectEqual(Value(&x_, 1, -4), MinValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
+ ExpectEqual(Value(&x_, 4, -4), MinValue(Value(&x_, 4, -4), Value(&x_, 4, -1)));
+ ExpectEqual(Value(INT_MIN), MinValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
+ ExpectEqual(Value(INT_MIN), MinValue(Value(&x_, 1, 20), Value(3)));
+ ExpectEqual(Value(INT_MIN), MinValue(Value(55), Value(&y_, 1, -50)));
+}
+
+TEST_F(InductionVarRangeTest, MaxValue) {
+ ExpectEqual(Value(100), MaxValue(Value(10), Value(100)));
+ ExpectEqual(Value(&x_, 1, -1), MaxValue(Value(&x_, 1, -4), Value(&x_, 1, -1)));
+ ExpectEqual(Value(&x_, 4, -1), MaxValue(Value(&x_, 4, -4), Value(&x_, 4, -1)));
+ ExpectEqual(Value(INT_MAX), MaxValue(Value(&x_, 1, 5), Value(&y_, 1, -7)));
+ ExpectEqual(Value(INT_MAX), MaxValue(Value(&x_, 1, 20), Value(3)));
+ ExpectEqual(Value(INT_MAX), MaxValue(Value(55), Value(&y_, 1, -50)));
+}
+
+} // namespace art
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index bd591f3..2966076 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -502,8 +502,9 @@
// TODO: we could be more precise by merging the phi inputs but that requires
// some functionality from the reference type propagation.
DCHECK(return_replacement->IsPhi());
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
ReferenceTypeInfo::TypeHandle return_handle =
- handles_->NewHandle(resolved_method->GetReturnType());
+ handles_->NewHandle(resolved_method->GetReturnType(true /* resolve */, pointer_size));
return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
return_handle, return_handle->IsFinal() /* is_exact */));
}
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 075ec1e..b71fdb8 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -16,12 +16,17 @@
#include "intrinsics.h"
+#include "art_method.h"
+#include "class_linker.h"
#include "dex/quick/dex_file_method_inliner.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "driver/compiler_driver.h"
#include "invoke_type.h"
+#include "mirror/dex_cache-inl.h"
#include "nodes.h"
#include "quick/inline_method_analyser.h"
+#include "scoped_thread_state_change.h"
+#include "thread-inl.h"
#include "utils.h"
namespace art {
@@ -120,6 +125,28 @@
LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
UNREACHABLE();
}
+ case kIntrinsicRotateRight:
+ switch (GetType(method.d.data, true)) {
+ case Primitive::kPrimInt:
+ return Intrinsics::kIntegerRotateRight;
+ case Primitive::kPrimLong:
+ return Intrinsics::kLongRotateRight;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
+ case kIntrinsicRotateLeft:
+ switch (GetType(method.d.data, true)) {
+ case Primitive::kPrimInt:
+ return Intrinsics::kIntegerRotateLeft;
+ case Primitive::kPrimLong:
+ return Intrinsics::kLongRotateLeft;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
+
+ // Misc data processing.
case kIntrinsicNumberOfLeadingZeros:
switch (GetType(method.d.data, true)) {
case Primitive::kPrimInt:
@@ -130,6 +157,16 @@
LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
UNREACHABLE();
}
+ case kIntrinsicNumberOfTrailingZeros:
+ switch (GetType(method.d.data, true)) {
+ case Primitive::kPrimInt:
+ return Intrinsics::kIntegerNumberOfTrailingZeros;
+ case Primitive::kPrimLong:
+ return Intrinsics::kLongNumberOfTrailingZeros;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
// Abs.
case kIntrinsicAbsDouble:
@@ -328,14 +365,23 @@
return Intrinsics::kNone;
}
-static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) {
+static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke, const DexFile& dex_file) {
// The DexFileMethodInliner should have checked whether the methods are agreeing with
// what we expect, i.e., static methods are called as such. Add another check here for
// our expectations:
- // Whenever the intrinsic is marked as static-or-direct, report an error if we find an
- // InvokeVirtual. The other direction is not possible: we have intrinsics for virtual
- // functions that will perform a check inline. If the precise type is known, however,
- // the instruction will be sharpened to an InvokeStaticOrDirect.
+ //
+ // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual.
+ //
+ // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization
+ // failure occured. We might be in a situation where we have inlined a method that calls an
+ // intrinsic, but that method is in a different dex file on which we do not have a
+ // verified_method that would have helped the compiler driver sharpen the call. In that case,
+ // make sure that the intrinsic is actually for some final method (or in a final class), as
+ // otherwise the intrinsics setup is broken.
+ //
+ // For the last direction, we have intrinsics for virtual functions that will perform a check
+ // inline. If the precise type is known, however, the instruction will be sharpened to an
+ // InvokeStaticOrDirect.
InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
InvokeType invoke_type = invoke->IsInvokeStaticOrDirect() ?
invoke->AsInvokeStaticOrDirect()->GetInvokeType() :
@@ -343,8 +389,22 @@
switch (intrinsic_type) {
case kStatic:
return (invoke_type == kStatic);
+
case kDirect:
- return (invoke_type == kDirect);
+ if (invoke_type == kDirect) {
+ return true;
+ }
+ if (invoke_type == kVirtual) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ScopedObjectAccess soa(Thread::Current());
+ ArtMethod* art_method =
+ class_linker->FindDexCache(soa.Self(), dex_file)->GetResolvedMethod(
+ invoke->GetDexMethodIndex(), class_linker->GetImagePointerSize());
+ return art_method != nullptr &&
+ (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
+ }
+ return false;
+
case kVirtual:
// Call might be devirtualized.
return (invoke_type == kVirtual || invoke_type == kDirect);
@@ -364,17 +424,18 @@
if (inst->IsInvoke()) {
HInvoke* invoke = inst->AsInvoke();
InlineMethod method;
- DexFileMethodInliner* inliner =
- driver_->GetMethodInlinerMap()->GetMethodInliner(&invoke->GetDexFile());
+ const DexFile& dex_file = invoke->GetDexFile();
+ DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(&dex_file);
DCHECK(inliner != nullptr);
if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) {
Intrinsics intrinsic = GetIntrinsic(method, graph_->GetInstructionSet());
if (intrinsic != Intrinsics::kNone) {
- if (!CheckInvokeType(intrinsic, invoke)) {
+ if (!CheckInvokeType(intrinsic, invoke, dex_file)) {
LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
- << intrinsic << " for "
- << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile());
+ << intrinsic << " for "
+ << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile())
+ << invoke->DebugName();
} else {
invoke->SetIntrinsic(intrinsic, NeedsEnvironmentOrCache(intrinsic));
}
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 69a3e62..cc8ddb6 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -103,11 +103,11 @@
if (invoke_->IsInvokeStaticOrDirect()) {
codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
Location::RegisterLocation(kArtMethodRegister));
- codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
} else {
- UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
- UNREACHABLE();
+ codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(),
+ Location::RegisterLocation(kArtMethodRegister));
}
+ codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
// Copy the result back to the expected output.
Location out = invoke_->GetLocations()->Out();
@@ -266,6 +266,227 @@
GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
}
+static void GenNumberOfTrailingZeros(LocationSummary* locations,
+ Primitive::Type type,
+ ArmAssembler* assembler) {
+ DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
+
+ Register out = locations->Out().AsRegister<Register>();
+
+ if (type == Primitive::kPrimLong) {
+ Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+ Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+ Label end;
+ __ rbit(out, in_reg_lo);
+ __ clz(out, out);
+ __ CompareAndBranchIfNonZero(in_reg_lo, &end);
+ __ rbit(out, in_reg_hi);
+ __ clz(out, out);
+ __ AddConstant(out, 32);
+ __ Bind(&end);
+ } else {
+ Register in = locations->InAt(0).AsRegister<Register>();
+ __ rbit(out, in);
+ __ clz(out, out);
+ }
+}
+
+void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
+ GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
+ GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+}
+
+static void GenIntegerRotate(LocationSummary* locations,
+ ArmAssembler* assembler,
+ bool is_left) {
+ Register in = locations->InAt(0).AsRegister<Register>();
+ Location rhs = locations->InAt(1);
+ Register out = locations->Out().AsRegister<Register>();
+
+ if (rhs.IsConstant()) {
+ // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
+ // so map all rotations to a +ve. equivalent in that range.
+ // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
+ uint32_t rot = rhs.GetConstant()->AsIntConstant()->GetValue() & 0x1F;
+ if (rot) {
+ // Rotate, mapping left rotations to right equivalents if necessary.
+ // (e.g. left by 2 bits == right by 30.)
+ __ Ror(out, in, is_left ? (0x20 - rot) : rot);
+ } else if (out != in) {
+ __ Mov(out, in);
+ }
+ } else {
+ if (is_left) {
+ __ rsb(out, rhs.AsRegister<Register>(), ShifterOperand(0));
+ __ Ror(out, in, out);
+ } else {
+ __ Ror(out, in, rhs.AsRegister<Register>());
+ }
+ }
+}
+
+// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
+// rotates by swapping input regs (effectively rotating by the first 32-bits of
+// a larger rotation) or flipping direction (thus treating larger right/left
+// rotations as sub-word sized rotations in the other direction) as appropriate.
+static void GenLongRotate(LocationSummary* locations,
+ ArmAssembler* assembler,
+ bool is_left) {
+ Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+ Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+ Location rhs = locations->InAt(1);
+ Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
+ Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+ if (rhs.IsConstant()) {
+ uint32_t rot = rhs.GetConstant()->AsIntConstant()->GetValue();
+ // Map all left rotations to right equivalents.
+ if (is_left) {
+ rot = 0x40 - rot;
+ }
+ // Map all rotations to +ve. equivalents on the interval [0,63].
+ rot &= 0x3F;
+ // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
+ // logic below to a simple pair of binary orr.
+ // (e.g. 34 bits == in_reg swap + 2 bits right.)
+ if (rot >= 0x20) {
+ rot -= 0x20;
+ std::swap(in_reg_hi, in_reg_lo);
+ }
+ // Rotate, or mov to out for zero or word size rotations.
+ if (rot) {
+ __ Lsr(out_reg_hi, in_reg_hi, rot);
+ __ orr(out_reg_hi, out_reg_hi, ShifterOperand(in_reg_lo, arm::LSL, 0x20 - rot));
+ __ Lsr(out_reg_lo, in_reg_lo, rot);
+ __ orr(out_reg_lo, out_reg_lo, ShifterOperand(in_reg_hi, arm::LSL, 0x20 - rot));
+ } else {
+ __ Mov(out_reg_lo, in_reg_lo);
+ __ Mov(out_reg_hi, in_reg_hi);
+ }
+ } else {
+ Register shift_left = locations->GetTemp(0).AsRegister<Register>();
+ Register shift_right = locations->GetTemp(1).AsRegister<Register>();
+ Label end;
+ Label right;
+
+ __ and_(shift_left, rhs.AsRegister<Register>(), ShifterOperand(0x1F));
+ __ Lsrs(shift_right, rhs.AsRegister<Register>(), 6);
+ __ rsb(shift_right, shift_left, ShifterOperand(0x20), AL, kCcKeep);
+
+ if (is_left) {
+ __ b(&right, CS);
+ } else {
+ __ b(&right, CC);
+ std::swap(shift_left, shift_right);
+ }
+
+ // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
+ // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
+ __ Lsl(out_reg_hi, in_reg_hi, shift_left);
+ __ Lsr(out_reg_lo, in_reg_lo, shift_right);
+ __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo));
+ __ Lsl(out_reg_lo, in_reg_lo, shift_left);
+ __ Lsr(shift_left, in_reg_hi, shift_right);
+ __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_left));
+ __ b(&end);
+
+ // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
+ // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
+ __ Bind(&right);
+ __ Lsr(out_reg_hi, in_reg_hi, shift_right);
+ __ Lsl(out_reg_lo, in_reg_lo, shift_left);
+ __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo));
+ __ Lsr(out_reg_lo, in_reg_lo, shift_right);
+ __ Lsl(shift_right, in_reg_hi, shift_left);
+ __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_right));
+
+ __ Bind(&end);
+ }
+}
+
+void IntrinsicLocationsBuilderARM::VisitIntegerRotateRight(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitIntegerRotateRight(HInvoke* invoke) {
+ GenIntegerRotate(invoke->GetLocations(), GetAssembler(), false /* is_left */);
+}
+
+void IntrinsicLocationsBuilderARM::VisitLongRotateRight(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ if (invoke->InputAt(1)->IsConstant()) {
+ locations->SetInAt(1, Location::ConstantLocation(invoke->InputAt(1)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitLongRotateRight(HInvoke* invoke) {
+ GenLongRotate(invoke->GetLocations(), GetAssembler(), false /* is_left */);
+}
+
+void IntrinsicLocationsBuilderARM::VisitIntegerRotateLeft(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitIntegerRotateLeft(HInvoke* invoke) {
+ GenIntegerRotate(invoke->GetLocations(), GetAssembler(), true /* is_left */);
+}
+
+void IntrinsicLocationsBuilderARM::VisitLongRotateLeft(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ if (invoke->InputAt(1)->IsConstant()) {
+ locations->SetInAt(1, Location::ConstantLocation(invoke->InputAt(1)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitLongRotateLeft(HInvoke* invoke) {
+ GenLongRotate(invoke->GetLocations(), GetAssembler(), true /* is_left */);
+}
+
static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
Location in = locations->InAt(0);
Location out = locations->Out();
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 0171d69..b0cfd0d 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -41,12 +41,12 @@
using helpers::FPRegisterFrom;
using helpers::HeapOperand;
using helpers::LocationFrom;
+using helpers::OperandFrom;
using helpers::RegisterFrom;
using helpers::SRegisterFrom;
using helpers::WRegisterFrom;
using helpers::XRegisterFrom;
-
namespace {
ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
@@ -112,11 +112,10 @@
if (invoke_->IsInvokeStaticOrDirect()) {
codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
LocationFrom(kArtMethodRegister));
- codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
} else {
- UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
- UNREACHABLE();
+ codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister));
}
+ codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
// Copy the result back to the expected output.
Location out = invoke_->GetLocations()->Out();
@@ -287,6 +286,131 @@
GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
}
+static void GenNumberOfTrailingZeros(LocationSummary* locations,
+ Primitive::Type type,
+ vixl::MacroAssembler* masm) {
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+
+ __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
+ __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
+ GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
+ GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
+static void GenRotateRight(LocationSummary* locations,
+ Primitive::Type type,
+ vixl::MacroAssembler* masm) {
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+ Operand rhs = OperandFrom(locations->InAt(1), type);
+
+ if (rhs.IsImmediate()) {
+ uint32_t shift = rhs.immediate() & (RegisterFrom(in, type).SizeInBits() - 1);
+ __ Ror(RegisterFrom(out, type),
+ RegisterFrom(in, type),
+ shift);
+ } else {
+ DCHECK(rhs.shift() == vixl::LSL && rhs.shift_amount() == 0);
+ __ Ror(RegisterFrom(out, type),
+ RegisterFrom(in, type),
+ rhs.reg());
+ }
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerRotateRight(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerRotateRight(HInvoke* invoke) {
+ GenRotateRight(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongRotateRight(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongRotateRight(HInvoke* invoke) {
+ GenRotateRight(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
+static void GenRotateLeft(LocationSummary* locations,
+ Primitive::Type type,
+ vixl::MacroAssembler* masm) {
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+ Operand rhs = OperandFrom(locations->InAt(1), type);
+
+ if (rhs.IsImmediate()) {
+ uint32_t regsize = RegisterFrom(in, type).SizeInBits();
+ uint32_t shift = (regsize - rhs.immediate()) & (regsize - 1);
+ __ Ror(RegisterFrom(out, type), RegisterFrom(in, type), shift);
+ } else {
+ DCHECK(rhs.shift() == vixl::LSL && rhs.shift_amount() == 0);
+ __ Neg(RegisterFrom(out, type),
+ Operand(RegisterFrom(locations->InAt(1), type)));
+ __ Ror(RegisterFrom(out, type),
+ RegisterFrom(in, type),
+ RegisterFrom(out, type));
+ }
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerRotateLeft(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerRotateLeft(HInvoke* invoke) {
+ GenRotateLeft(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongRotateLeft(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongRotateLeft(HInvoke* invoke) {
+ GenRotateLeft(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
static void GenReverse(LocationSummary* locations,
Primitive::Type type,
vixl::MacroAssembler* masm) {
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
index 7e5339e..bfe5e55 100644
--- a/compiler/optimizing/intrinsics_list.h
+++ b/compiler/optimizing/intrinsics_list.h
@@ -29,9 +29,15 @@
V(IntegerReverse, kStatic, kNeedsEnvironmentOrCache) \
V(IntegerReverseBytes, kStatic, kNeedsEnvironmentOrCache) \
V(IntegerNumberOfLeadingZeros, kStatic, kNeedsEnvironmentOrCache) \
+ V(IntegerNumberOfTrailingZeros, kStatic, kNeedsEnvironmentOrCache) \
+ V(IntegerRotateRight, kStatic, kNeedsEnvironmentOrCache) \
+ V(IntegerRotateLeft, kStatic, kNeedsEnvironmentOrCache) \
V(LongReverse, kStatic, kNeedsEnvironmentOrCache) \
V(LongReverseBytes, kStatic, kNeedsEnvironmentOrCache) \
V(LongNumberOfLeadingZeros, kStatic, kNeedsEnvironmentOrCache) \
+ V(LongNumberOfTrailingZeros, kStatic, kNeedsEnvironmentOrCache) \
+ V(LongRotateRight, kStatic, kNeedsEnvironmentOrCache) \
+ V(LongRotateLeft, kStatic, kNeedsEnvironmentOrCache) \
V(ShortReverseBytes, kStatic, kNeedsEnvironmentOrCache) \
V(MathAbsDouble, kStatic, kNeedsEnvironmentOrCache) \
V(MathAbsFloat, kStatic, kNeedsEnvironmentOrCache) \
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index be076cd..c5d88d2 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -141,11 +141,10 @@
if (invoke_->IsInvokeStaticOrDirect()) {
codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
Location::RegisterLocation(EAX));
- codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
} else {
- UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
- UNREACHABLE();
+ codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), Location::RegisterLocation(EAX));
}
+ codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
// Copy the result back to the expected output.
Location out = invoke_->GetLocations()->Out();
@@ -1957,6 +1956,12 @@
UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+UNIMPLEMENTED_INTRINSIC(IntegerNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(LongNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
+UNIMPLEMENTED_INTRINSIC(LongRotateRight)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
+UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
#undef UNIMPLEMENTED_INTRINSIC
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 1f35b59..258ae9a 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -132,11 +132,10 @@
if (invoke_->IsInvokeStaticOrDirect()) {
codegen->GenerateStaticOrDirectCall(
invoke_->AsInvokeStaticOrDirect(), Location::RegisterLocation(RDI));
- codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
} else {
- UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
- UNREACHABLE();
+ codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), Location::RegisterLocation(RDI));
}
+ codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
// Copy the result back to the expected output.
Location out = invoke_->GetLocations()->Out();
@@ -1775,6 +1774,12 @@
UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+UNIMPLEMENTED_INTRINSIC(IntegerNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(LongNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
+UNIMPLEMENTED_INTRINSIC(LongRotateRight)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
+UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
#undef UNIMPLEMENTED_INTRINSIC
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 62a460a..b3cf0b3 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -206,7 +206,7 @@
// Insert a new node between `block` and `successor` to split the
// critical edge.
HBasicBlock* new_block = SplitEdge(block, successor);
- new_block->AddInstruction(new (arena_) HGoto());
+ new_block->AddInstruction(new (arena_) HGoto(successor->GetDexPc()));
if (successor->IsLoopHeader()) {
// If we split at a back edge boundary, make the new block the back edge.
HLoopInformation* info = successor->GetLoopInformation();
@@ -227,7 +227,7 @@
if (number_of_incomings != 1) {
HBasicBlock* pre_header = new (arena_) HBasicBlock(this, header->GetDexPc());
AddBlock(pre_header);
- pre_header->AddInstruction(new (arena_) HGoto());
+ pre_header->AddInstruction(new (arena_) HGoto(header->GetDexPc()));
for (size_t pred = 0; pred < header->GetPredecessors().size(); ++pred) {
HBasicBlock* predecessor = header->GetPredecessor(pred);
@@ -408,12 +408,12 @@
}
}
-HNullConstant* HGraph::GetNullConstant() {
+HNullConstant* HGraph::GetNullConstant(uint32_t dex_pc) {
// For simplicity, don't bother reviving the cached null constant if it is
// not null and not in a block. Otherwise, we need to clear the instruction
// id and/or any invariants the graph is assuming when adding new instructions.
if ((cached_null_constant_ == nullptr) || (cached_null_constant_->GetBlock() == nullptr)) {
- cached_null_constant_ = new (arena_) HNullConstant();
+ cached_null_constant_ = new (arena_) HNullConstant(dex_pc);
InsertConstant(cached_null_constant_);
}
return cached_null_constant_;
@@ -425,7 +425,8 @@
// id and/or any invariants the graph is assuming when adding new instructions.
if ((cached_current_method_ == nullptr) || (cached_current_method_->GetBlock() == nullptr)) {
cached_current_method_ = new (arena_) HCurrentMethod(
- Is64BitInstructionSet(instruction_set_) ? Primitive::kPrimLong : Primitive::kPrimInt);
+ Is64BitInstructionSet(instruction_set_) ? Primitive::kPrimLong : Primitive::kPrimInt,
+ entry_block_->GetDexPc());
if (entry_block_->GetFirstInstruction() == nullptr) {
entry_block_->AddInstruction(cached_current_method_);
} else {
@@ -436,7 +437,7 @@
return cached_current_method_;
}
-HConstant* HGraph::GetConstant(Primitive::Type type, int64_t value) {
+HConstant* HGraph::GetConstant(Primitive::Type type, int64_t value, uint32_t dex_pc) {
switch (type) {
case Primitive::Type::kPrimBoolean:
DCHECK(IsUint<1>(value));
@@ -446,10 +447,10 @@
case Primitive::Type::kPrimShort:
case Primitive::Type::kPrimInt:
DCHECK(IsInt(Primitive::ComponentSize(type) * kBitsPerByte, value));
- return GetIntConstant(static_cast<int32_t>(value));
+ return GetIntConstant(static_cast<int32_t>(value), dex_pc);
case Primitive::Type::kPrimLong:
- return GetLongConstant(value);
+ return GetLongConstant(value, dex_pc);
default:
LOG(FATAL) << "Unsupported constant type";
@@ -943,11 +944,11 @@
int32_t value = GetInput()->AsIntConstant()->GetValue();
switch (GetResultType()) {
case Primitive::kPrimLong:
- return graph->GetLongConstant(static_cast<int64_t>(value));
+ return graph->GetLongConstant(static_cast<int64_t>(value), GetDexPc());
case Primitive::kPrimFloat:
- return graph->GetFloatConstant(static_cast<float>(value));
+ return graph->GetFloatConstant(static_cast<float>(value), GetDexPc());
case Primitive::kPrimDouble:
- return graph->GetDoubleConstant(static_cast<double>(value));
+ return graph->GetDoubleConstant(static_cast<double>(value), GetDexPc());
default:
return nullptr;
}
@@ -955,11 +956,11 @@
int64_t value = GetInput()->AsLongConstant()->GetValue();
switch (GetResultType()) {
case Primitive::kPrimInt:
- return graph->GetIntConstant(static_cast<int32_t>(value));
+ return graph->GetIntConstant(static_cast<int32_t>(value), GetDexPc());
case Primitive::kPrimFloat:
- return graph->GetFloatConstant(static_cast<float>(value));
+ return graph->GetFloatConstant(static_cast<float>(value), GetDexPc());
case Primitive::kPrimDouble:
- return graph->GetDoubleConstant(static_cast<double>(value));
+ return graph->GetDoubleConstant(static_cast<double>(value), GetDexPc());
default:
return nullptr;
}
@@ -968,22 +969,22 @@
switch (GetResultType()) {
case Primitive::kPrimInt:
if (std::isnan(value))
- return graph->GetIntConstant(0);
+ return graph->GetIntConstant(0, GetDexPc());
if (value >= kPrimIntMax)
- return graph->GetIntConstant(kPrimIntMax);
+ return graph->GetIntConstant(kPrimIntMax, GetDexPc());
if (value <= kPrimIntMin)
- return graph->GetIntConstant(kPrimIntMin);
- return graph->GetIntConstant(static_cast<int32_t>(value));
+ return graph->GetIntConstant(kPrimIntMin, GetDexPc());
+ return graph->GetIntConstant(static_cast<int32_t>(value), GetDexPc());
case Primitive::kPrimLong:
if (std::isnan(value))
- return graph->GetLongConstant(0);
+ return graph->GetLongConstant(0, GetDexPc());
if (value >= kPrimLongMax)
- return graph->GetLongConstant(kPrimLongMax);
+ return graph->GetLongConstant(kPrimLongMax, GetDexPc());
if (value <= kPrimLongMin)
- return graph->GetLongConstant(kPrimLongMin);
- return graph->GetLongConstant(static_cast<int64_t>(value));
+ return graph->GetLongConstant(kPrimLongMin, GetDexPc());
+ return graph->GetLongConstant(static_cast<int64_t>(value), GetDexPc());
case Primitive::kPrimDouble:
- return graph->GetDoubleConstant(static_cast<double>(value));
+ return graph->GetDoubleConstant(static_cast<double>(value), GetDexPc());
default:
return nullptr;
}
@@ -992,22 +993,22 @@
switch (GetResultType()) {
case Primitive::kPrimInt:
if (std::isnan(value))
- return graph->GetIntConstant(0);
+ return graph->GetIntConstant(0, GetDexPc());
if (value >= kPrimIntMax)
- return graph->GetIntConstant(kPrimIntMax);
+ return graph->GetIntConstant(kPrimIntMax, GetDexPc());
if (value <= kPrimLongMin)
- return graph->GetIntConstant(kPrimIntMin);
- return graph->GetIntConstant(static_cast<int32_t>(value));
+ return graph->GetIntConstant(kPrimIntMin, GetDexPc());
+ return graph->GetIntConstant(static_cast<int32_t>(value), GetDexPc());
case Primitive::kPrimLong:
if (std::isnan(value))
- return graph->GetLongConstant(0);
+ return graph->GetLongConstant(0, GetDexPc());
if (value >= kPrimLongMax)
- return graph->GetLongConstant(kPrimLongMax);
+ return graph->GetLongConstant(kPrimLongMax, GetDexPc());
if (value <= kPrimLongMin)
- return graph->GetLongConstant(kPrimLongMin);
- return graph->GetLongConstant(static_cast<int64_t>(value));
+ return graph->GetLongConstant(kPrimLongMin, GetDexPc());
+ return graph->GetLongConstant(static_cast<int64_t>(value), GetDexPc());
case Primitive::kPrimFloat:
- return graph->GetFloatConstant(static_cast<float>(value));
+ return graph->GetFloatConstant(static_cast<float>(value), GetDexPc());
default:
return nullptr;
}
@@ -1121,7 +1122,8 @@
DCHECK(!graph_->IsInSsaForm()) << "Support for SSA form not implemented";
DCHECK_EQ(cursor->GetBlock(), this);
- HBasicBlock* new_block = new (GetGraph()->GetArena()) HBasicBlock(GetGraph(), GetDexPc());
+ HBasicBlock* new_block = new (GetGraph()->GetArena()) HBasicBlock(GetGraph(),
+ cursor->GetDexPc());
new_block->instructions_.first_instruction_ = cursor;
new_block->instructions_.last_instruction_ = instructions_.last_instruction_;
instructions_.last_instruction_ = cursor->previous_;
@@ -1133,7 +1135,7 @@
}
new_block->instructions_.SetBlockOfInstructions(new_block);
- AddInstruction(new (GetGraph()->GetArena()) HGoto());
+ AddInstruction(new (GetGraph()->GetArena()) HGoto(new_block->GetDexPc()));
for (HBasicBlock* successor : GetSuccessors()) {
new_block->successors_.push_back(successor);
@@ -1304,7 +1306,7 @@
predecessor->RemoveSuccessor(this);
if (predecessor->GetSuccessors().size() == 1u) {
DCHECK(last_instruction->IsIf());
- predecessor->AddInstruction(new (graph_->GetArena()) HGoto());
+ predecessor->AddInstruction(new (graph_->GetArena()) HGoto(last_instruction->GetDexPc()));
} else {
// The predecessor has no remaining successors and therefore must be dead.
// We deliberately leave it without a control-flow instruction so that the
@@ -1551,13 +1553,13 @@
if (!returns_void) {
return_value = last->InputAt(0);
}
- predecessor->AddInstruction(new (allocator) HGoto());
+ predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc()));
predecessor->RemoveInstruction(last);
} else {
if (!returns_void) {
// There will be multiple returns.
return_value = new (allocator) HPhi(
- allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()));
+ allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc());
to->AddPhi(return_value->AsPhi());
}
for (HBasicBlock* predecessor : to->GetPredecessors()) {
@@ -1565,7 +1567,7 @@
if (!returns_void) {
return_value->AsPhi()->AddInput(last->InputAt(0));
}
- predecessor->AddInstruction(new (allocator) HGoto());
+ predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc()));
predecessor->RemoveInstruction(last);
}
}
@@ -1647,15 +1649,19 @@
for (HInstructionIterator it(entry_block_->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
if (current->IsNullConstant()) {
- current->ReplaceWith(outer_graph->GetNullConstant());
+ current->ReplaceWith(outer_graph->GetNullConstant(current->GetDexPc()));
} else if (current->IsIntConstant()) {
- current->ReplaceWith(outer_graph->GetIntConstant(current->AsIntConstant()->GetValue()));
+ current->ReplaceWith(outer_graph->GetIntConstant(
+ current->AsIntConstant()->GetValue(), current->GetDexPc()));
} else if (current->IsLongConstant()) {
- current->ReplaceWith(outer_graph->GetLongConstant(current->AsLongConstant()->GetValue()));
+ current->ReplaceWith(outer_graph->GetLongConstant(
+ current->AsLongConstant()->GetValue(), current->GetDexPc()));
} else if (current->IsFloatConstant()) {
- current->ReplaceWith(outer_graph->GetFloatConstant(current->AsFloatConstant()->GetValue()));
+ current->ReplaceWith(outer_graph->GetFloatConstant(
+ current->AsFloatConstant()->GetValue(), current->GetDexPc()));
} else if (current->IsDoubleConstant()) {
- current->ReplaceWith(outer_graph->GetDoubleConstant(current->AsDoubleConstant()->GetValue()));
+ current->ReplaceWith(outer_graph->GetDoubleConstant(
+ current->AsDoubleConstant()->GetValue(), current->GetDexPc()));
} else if (current->IsParameterValue()) {
if (kIsDebugBuild
&& invoke->IsInvokeStaticOrDirect()
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 06a65e3..5ec3f22 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -79,6 +79,8 @@
static constexpr InvokeType kInvalidInvokeType = static_cast<InvokeType>(-1);
+static constexpr uint32_t kNoDexPc = -1;
+
enum IfCondition {
kCondEQ,
kCondNE,
@@ -318,24 +320,24 @@
// Returns a constant of the given type and value. If it does not exist
// already, it is created and inserted into the graph. This method is only for
// integral types.
- HConstant* GetConstant(Primitive::Type type, int64_t value);
+ HConstant* GetConstant(Primitive::Type type, int64_t value, uint32_t dex_pc = kNoDexPc);
// TODO: This is problematic for the consistency of reference type propagation
// because it can be created anytime after the pass and thus it will be left
// with an invalid type.
- HNullConstant* GetNullConstant();
+ HNullConstant* GetNullConstant(uint32_t dex_pc = kNoDexPc);
- HIntConstant* GetIntConstant(int32_t value) {
- return CreateConstant(value, &cached_int_constants_);
+ HIntConstant* GetIntConstant(int32_t value, uint32_t dex_pc = kNoDexPc) {
+ return CreateConstant(value, &cached_int_constants_, dex_pc);
}
- HLongConstant* GetLongConstant(int64_t value) {
- return CreateConstant(value, &cached_long_constants_);
+ HLongConstant* GetLongConstant(int64_t value, uint32_t dex_pc = kNoDexPc) {
+ return CreateConstant(value, &cached_long_constants_, dex_pc);
}
- HFloatConstant* GetFloatConstant(float value) {
- return CreateConstant(bit_cast<int32_t, float>(value), &cached_float_constants_);
+ HFloatConstant* GetFloatConstant(float value, uint32_t dex_pc = kNoDexPc) {
+ return CreateConstant(bit_cast<int32_t, float>(value), &cached_float_constants_, dex_pc);
}
- HDoubleConstant* GetDoubleConstant(double value) {
- return CreateConstant(bit_cast<int64_t, double>(value), &cached_double_constants_);
+ HDoubleConstant* GetDoubleConstant(double value, uint32_t dex_pc = kNoDexPc) {
+ return CreateConstant(bit_cast<int64_t, double>(value), &cached_double_constants_, dex_pc);
}
HCurrentMethod* GetCurrentMethod();
@@ -374,7 +376,8 @@
template <class InstructionType, typename ValueType>
InstructionType* CreateConstant(ValueType value,
- ArenaSafeMap<ValueType, InstructionType*>* cache) {
+ ArenaSafeMap<ValueType, InstructionType*>* cache,
+ uint32_t dex_pc = kNoDexPc) {
// Try to find an existing constant of the given value.
InstructionType* constant = nullptr;
auto cached_constant = cache->find(value);
@@ -385,7 +388,7 @@
// If not found or previously deleted, create and cache a new instruction.
// Don't bother reviving a previously deleted instruction, for simplicity.
if (constant == nullptr || constant->GetBlock() == nullptr) {
- constant = new (arena_) InstructionType(value);
+ constant = new (arena_) InstructionType(value, dex_pc);
cache->Overwrite(value, constant);
InsertConstant(constant);
}
@@ -620,7 +623,6 @@
};
static constexpr size_t kNoLifetime = -1;
-static constexpr uint32_t kNoDexPc = -1;
// A block in a method. Contains the list of instructions represented
// as a double linked list. Each block knows its predecessors and
@@ -628,7 +630,7 @@
class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> {
public:
- explicit HBasicBlock(HGraph* graph, uint32_t dex_pc = kNoDexPc)
+ HBasicBlock(HGraph* graph, uint32_t dex_pc = kNoDexPc)
: graph_(graph),
predecessors_(graph->GetArena()->Adapter(kArenaAllocPredecessors)),
successors_(graph->GetArena()->Adapter(kArenaAllocSuccessors)),
@@ -703,6 +705,7 @@
int GetBlockId() const { return block_id_; }
void SetBlockId(int id) { block_id_ = id; }
+ uint32_t GetDexPc() const { return dex_pc_; }
HBasicBlock* GetDominator() const { return dominator_; }
void SetDominator(HBasicBlock* dominator) { dominator_ = dominator; }
@@ -942,7 +945,6 @@
void SetLifetimeStart(size_t start) { lifetime_start_ = start; }
void SetLifetimeEnd(size_t end) { lifetime_end_ = end; }
- uint32_t GetDexPc() const { return dex_pc_; }
bool EndsWithControlFlowInstruction() const;
bool EndsWithIf() const;
@@ -1075,7 +1077,9 @@
#define FOR_EACH_CONCRETE_INSTRUCTION_MIPS64(M)
-#define FOR_EACH_CONCRETE_INSTRUCTION_X86(M)
+#define FOR_EACH_CONCRETE_INSTRUCTION_X86(M) \
+ M(X86ComputeBaseMethodAddress, Instruction) \
+ M(X86LoadFromConstantTable, Instruction)
#define FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M)
@@ -1688,10 +1692,11 @@
class HInstruction : public ArenaObject<kArenaAllocInstruction> {
public:
- explicit HInstruction(SideEffects side_effects)
+ HInstruction(SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
: previous_(nullptr),
next_(nullptr),
block_(nullptr),
+ dex_pc_(dex_pc),
id_(-1),
ssa_index_(-1),
environment_(nullptr),
@@ -1734,9 +1739,9 @@
}
virtual bool NeedsEnvironment() const { return false; }
- virtual uint32_t GetDexPc() const {
- return kNoDexPc;
- }
+
+ uint32_t GetDexPc() const { return dex_pc_; }
+
virtual bool IsControlFlow() const { return false; }
virtual bool CanThrow() const { return false; }
@@ -1939,6 +1944,7 @@
HInstruction* previous_;
HInstruction* next_;
HBasicBlock* block_;
+ const uint32_t dex_pc_;
// An instruction gets an id when it is added to the graph.
// It reflects creation order. A negative id means the instruction
@@ -2043,8 +2049,8 @@
template<size_t N>
class HTemplateInstruction: public HInstruction {
public:
- HTemplateInstruction<N>(SideEffects side_effects)
- : HInstruction(side_effects), inputs_() {}
+ HTemplateInstruction<N>(SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+ : HInstruction(side_effects, dex_pc), inputs_() {}
virtual ~HTemplateInstruction() {}
size_t InputCount() const OVERRIDE { return N; }
@@ -2070,7 +2076,9 @@
template<>
class HTemplateInstruction<0>: public HInstruction {
public:
- explicit HTemplateInstruction(SideEffects side_effects) : HInstruction(side_effects) {}
+ explicit HTemplateInstruction<0>(SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+ : HInstruction(side_effects, dex_pc) {}
+
virtual ~HTemplateInstruction() {}
size_t InputCount() const OVERRIDE { return 0; }
@@ -2094,8 +2102,8 @@
template<intptr_t N>
class HExpression : public HTemplateInstruction<N> {
public:
- HExpression<N>(Primitive::Type type, SideEffects side_effects)
- : HTemplateInstruction<N>(side_effects), type_(type) {}
+ HExpression<N>(Primitive::Type type, SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction<N>(side_effects, dex_pc), type_(type) {}
virtual ~HExpression() {}
Primitive::Type GetType() const OVERRIDE { return type_; }
@@ -2108,7 +2116,8 @@
// instruction that branches to the exit block.
class HReturnVoid : public HTemplateInstruction<0> {
public:
- HReturnVoid() : HTemplateInstruction(SideEffects::None()) {}
+ explicit HReturnVoid(uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction(SideEffects::None(), dex_pc) {}
bool IsControlFlow() const OVERRIDE { return true; }
@@ -2122,7 +2131,8 @@
// instruction that branches to the exit block.
class HReturn : public HTemplateInstruction<1> {
public:
- explicit HReturn(HInstruction* value) : HTemplateInstruction(SideEffects::None()) {
+ explicit HReturn(HInstruction* value, uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction(SideEffects::None(), dex_pc) {
SetRawInputAt(0, value);
}
@@ -2139,7 +2149,7 @@
// exit block.
class HExit : public HTemplateInstruction<0> {
public:
- HExit() : HTemplateInstruction(SideEffects::None()) {}
+ explicit HExit(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {}
bool IsControlFlow() const OVERRIDE { return true; }
@@ -2152,7 +2162,7 @@
// Jumps from one block to another.
class HGoto : public HTemplateInstruction<0> {
public:
- HGoto() : HTemplateInstruction(SideEffects::None()) {}
+ explicit HGoto(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {}
bool IsControlFlow() const OVERRIDE { return true; }
@@ -2168,7 +2178,8 @@
class HConstant : public HExpression<0> {
public:
- explicit HConstant(Primitive::Type type) : HExpression(type, SideEffects::None()) {}
+ explicit HConstant(Primitive::Type type, uint32_t dex_pc = kNoDexPc)
+ : HExpression(type, SideEffects::None(), dex_pc) {}
bool CanBeMoved() const OVERRIDE { return true; }
@@ -2193,7 +2204,7 @@
DECLARE_INSTRUCTION(NullConstant);
private:
- HNullConstant() : HConstant(Primitive::kPrimNot) {}
+ explicit HNullConstant(uint32_t dex_pc = kNoDexPc) : HConstant(Primitive::kPrimNot, dex_pc) {}
friend class HGraph;
DISALLOW_COPY_AND_ASSIGN(HNullConstant);
@@ -2219,8 +2230,10 @@
DECLARE_INSTRUCTION(IntConstant);
private:
- explicit HIntConstant(int32_t value) : HConstant(Primitive::kPrimInt), value_(value) {}
- explicit HIntConstant(bool value) : HConstant(Primitive::kPrimInt), value_(value ? 1 : 0) {}
+ explicit HIntConstant(int32_t value, uint32_t dex_pc = kNoDexPc)
+ : HConstant(Primitive::kPrimInt, dex_pc), value_(value) {}
+ explicit HIntConstant(bool value, uint32_t dex_pc = kNoDexPc)
+ : HConstant(Primitive::kPrimInt, dex_pc), value_(value ? 1 : 0) {}
const int32_t value_;
@@ -2248,7 +2261,8 @@
DECLARE_INSTRUCTION(LongConstant);
private:
- explicit HLongConstant(int64_t value) : HConstant(Primitive::kPrimLong), value_(value) {}
+ explicit HLongConstant(int64_t value, uint32_t dex_pc = kNoDexPc)
+ : HConstant(Primitive::kPrimLong, dex_pc), value_(value) {}
const int64_t value_;
@@ -2260,7 +2274,8 @@
// two successors.
class HIf : public HTemplateInstruction<1> {
public:
- explicit HIf(HInstruction* input) : HTemplateInstruction(SideEffects::None()) {
+ explicit HIf(HInstruction* input, uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction(SideEffects::None(), dex_pc) {
SetRawInputAt(0, input);
}
@@ -2293,8 +2308,8 @@
kExit,
};
- explicit HTryBoundary(BoundaryKind kind)
- : HTemplateInstruction(SideEffects::None()), kind_(kind) {}
+ explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction(SideEffects::None(), dex_pc), kind_(kind) {}
bool IsControlFlow() const OVERRIDE { return true; }
@@ -2350,21 +2365,17 @@
// Deoptimize to interpreter, upon checking a condition.
class HDeoptimize : public HTemplateInstruction<1> {
public:
- HDeoptimize(HInstruction* cond, uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::None()),
- dex_pc_(dex_pc) {
+ explicit HDeoptimize(HInstruction* cond, uint32_t dex_pc)
+ : HTemplateInstruction(SideEffects::None(), dex_pc) {
SetRawInputAt(0, cond);
}
bool NeedsEnvironment() const OVERRIDE { return true; }
bool CanThrow() const OVERRIDE { return true; }
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
DECLARE_INSTRUCTION(Deoptimize);
private:
- uint32_t dex_pc_;
-
DISALLOW_COPY_AND_ASSIGN(HDeoptimize);
};
@@ -2373,7 +2384,8 @@
// instructions that work with the dex cache.
class HCurrentMethod : public HExpression<0> {
public:
- explicit HCurrentMethod(Primitive::Type type) : HExpression(type, SideEffects::None()) {}
+ explicit HCurrentMethod(Primitive::Type type, uint32_t dex_pc = kNoDexPc)
+ : HExpression(type, SideEffects::None(), dex_pc) {}
DECLARE_INSTRUCTION(CurrentMethod);
@@ -2383,8 +2395,8 @@
class HUnaryOperation : public HExpression<1> {
public:
- HUnaryOperation(Primitive::Type result_type, HInstruction* input)
- : HExpression(result_type, SideEffects::None()) {
+ HUnaryOperation(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
+ : HExpression(result_type, SideEffects::None(), dex_pc) {
SetRawInputAt(0, input);
}
@@ -2417,8 +2429,9 @@
HBinaryOperation(Primitive::Type result_type,
HInstruction* left,
HInstruction* right,
- SideEffects side_effects = SideEffects::None())
- : HExpression(result_type, side_effects) {
+ SideEffects side_effects = SideEffects::None(),
+ uint32_t dex_pc = kNoDexPc)
+ : HExpression(result_type, side_effects, dex_pc) {
SetRawInputAt(0, left);
SetRawInputAt(1, right);
}
@@ -2510,8 +2523,8 @@
class HCondition : public HBinaryOperation {
public:
- HCondition(HInstruction* first, HInstruction* second)
- : HBinaryOperation(Primitive::kPrimBoolean, first, second),
+ HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc),
needs_materialization_(true),
bias_(ComparisonBias::kNoBias) {}
@@ -2562,18 +2575,20 @@
// Instruction to check if two inputs are equal to each other.
class HEqual : public HCondition {
public:
- HEqual(HInstruction* first, HInstruction* second)
- : HCondition(first, second) {}
+ HEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+ : HCondition(first, second, dex_pc) {}
bool IsCommutative() const OVERRIDE { return true; }
template <typename T> bool Compute(T x, T y) const { return x == y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(Equal);
@@ -2592,18 +2607,20 @@
class HNotEqual : public HCondition {
public:
- HNotEqual(HInstruction* first, HInstruction* second)
- : HCondition(first, second) {}
+ HNotEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+ : HCondition(first, second, dex_pc) {}
bool IsCommutative() const OVERRIDE { return true; }
template <typename T> bool Compute(T x, T y) const { return x != y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(NotEqual);
@@ -2622,16 +2639,18 @@
class HLessThan : public HCondition {
public:
- HLessThan(HInstruction* first, HInstruction* second)
- : HCondition(first, second) {}
+ HLessThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+ : HCondition(first, second, dex_pc) {}
template <typename T> bool Compute(T x, T y) const { return x < y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(LessThan);
@@ -2650,16 +2669,18 @@
class HLessThanOrEqual : public HCondition {
public:
- HLessThanOrEqual(HInstruction* first, HInstruction* second)
- : HCondition(first, second) {}
+ HLessThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+ : HCondition(first, second, dex_pc) {}
template <typename T> bool Compute(T x, T y) const { return x <= y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(LessThanOrEqual);
@@ -2678,16 +2699,18 @@
class HGreaterThan : public HCondition {
public:
- HGreaterThan(HInstruction* first, HInstruction* second)
- : HCondition(first, second) {}
+ HGreaterThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+ : HCondition(first, second, dex_pc) {}
template <typename T> bool Compute(T x, T y) const { return x > y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(GreaterThan);
@@ -2706,16 +2729,18 @@
class HGreaterThanOrEqual : public HCondition {
public:
- HGreaterThanOrEqual(HInstruction* first, HInstruction* second)
- : HCondition(first, second) {}
+ HGreaterThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
+ : HCondition(first, second, dex_pc) {}
template <typename T> bool Compute(T x, T y) const { return x >= y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(GreaterThanOrEqual);
@@ -2742,9 +2767,12 @@
HInstruction* second,
ComparisonBias bias,
uint32_t dex_pc)
- : HBinaryOperation(Primitive::kPrimInt, first, second, SideEffectsForArchRuntimeCalls(type)),
- bias_(bias),
- dex_pc_(dex_pc) {
+ : HBinaryOperation(Primitive::kPrimInt,
+ first,
+ second,
+ SideEffectsForArchRuntimeCalls(type),
+ dex_pc),
+ bias_(bias) {
DCHECK_EQ(type, first->GetType());
DCHECK_EQ(type, second->GetType());
}
@@ -2753,10 +2781,12 @@
int32_t Compute(T x, T y) const { return x == y ? 0 : x > y ? 1 : -1; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
@@ -2767,7 +2797,6 @@
bool IsGtBias() { return bias_ == ComparisonBias::kGtBias; }
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) {
// MIPS64 uses a runtime call for FP comparisons.
@@ -2778,7 +2807,6 @@
private:
const ComparisonBias bias_;
- const uint32_t dex_pc_;
DISALLOW_COPY_AND_ASSIGN(HCompare);
};
@@ -2787,7 +2815,7 @@
class HLocal : public HTemplateInstruction<0> {
public:
explicit HLocal(uint16_t reg_number)
- : HTemplateInstruction(SideEffects::None()), reg_number_(reg_number) {}
+ : HTemplateInstruction(SideEffects::None(), kNoDexPc), reg_number_(reg_number) {}
DECLARE_INSTRUCTION(Local);
@@ -2803,8 +2831,8 @@
// Load a given local. The local is an input of this instruction.
class HLoadLocal : public HExpression<1> {
public:
- HLoadLocal(HLocal* local, Primitive::Type type)
- : HExpression(type, SideEffects::None()) {
+ HLoadLocal(HLocal* local, Primitive::Type type, uint32_t dex_pc = kNoDexPc)
+ : HExpression(type, SideEffects::None(), dex_pc) {
SetRawInputAt(0, local);
}
@@ -2820,7 +2848,8 @@
// and the local.
class HStoreLocal : public HTemplateInstruction<2> {
public:
- HStoreLocal(HLocal* local, HInstruction* value) : HTemplateInstruction(SideEffects::None()) {
+ HStoreLocal(HLocal* local, HInstruction* value, uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction(SideEffects::None(), dex_pc) {
SetRawInputAt(0, local);
SetRawInputAt(1, value);
}
@@ -2861,9 +2890,10 @@
DECLARE_INSTRUCTION(FloatConstant);
private:
- explicit HFloatConstant(float value) : HConstant(Primitive::kPrimFloat), value_(value) {}
- explicit HFloatConstant(int32_t value)
- : HConstant(Primitive::kPrimFloat), value_(bit_cast<float, int32_t>(value)) {}
+ explicit HFloatConstant(float value, uint32_t dex_pc = kNoDexPc)
+ : HConstant(Primitive::kPrimFloat, dex_pc), value_(value) {}
+ explicit HFloatConstant(int32_t value, uint32_t dex_pc = kNoDexPc)
+ : HConstant(Primitive::kPrimFloat, dex_pc), value_(bit_cast<float, int32_t>(value)) {}
const float value_;
@@ -2901,9 +2931,10 @@
DECLARE_INSTRUCTION(DoubleConstant);
private:
- explicit HDoubleConstant(double value) : HConstant(Primitive::kPrimDouble), value_(value) {}
- explicit HDoubleConstant(int64_t value)
- : HConstant(Primitive::kPrimDouble), value_(bit_cast<double, int64_t>(value)) {}
+ explicit HDoubleConstant(double value, uint32_t dex_pc = kNoDexPc)
+ : HConstant(Primitive::kPrimDouble, dex_pc), value_(value) {}
+ explicit HDoubleConstant(int64_t value, uint32_t dex_pc = kNoDexPc)
+ : HConstant(Primitive::kPrimDouble, dex_pc), value_(bit_cast<double, int64_t>(value)) {}
const double value_;
@@ -2950,7 +2981,6 @@
Primitive::Type GetType() const OVERRIDE { return return_type_; }
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
uint32_t GetDexMethodIndex() const { return dex_method_index_; }
const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); }
@@ -2983,11 +3013,10 @@
uint32_t dex_method_index,
InvokeType original_invoke_type)
: HInstruction(
- SideEffects::AllExceptGCDependency()), // Assume write/read on all fields/arrays.
+ SideEffects::AllExceptGCDependency(), dex_pc), // Assume write/read on all fields/arrays.
number_of_arguments_(number_of_arguments),
inputs_(arena, number_of_arguments),
return_type_(return_type),
- dex_pc_(dex_pc),
dex_method_index_(dex_method_index),
original_invoke_type_(original_invoke_type),
intrinsic_(Intrinsics::kNone),
@@ -3004,7 +3033,6 @@
uint32_t number_of_arguments_;
GrowableArray<HUserRecord<HInstruction*> > inputs_;
const Primitive::Type return_type_;
- const uint32_t dex_pc_;
const uint32_t dex_method_index_;
const InvokeType original_invoke_type_;
Intrinsics intrinsic_;
@@ -3305,15 +3333,13 @@
uint16_t type_index,
const DexFile& dex_file,
QuickEntrypointEnum entrypoint)
- : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC()),
- dex_pc_(dex_pc),
+ : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc),
type_index_(type_index),
dex_file_(dex_file),
entrypoint_(entrypoint) {
SetRawInputAt(0, current_method);
}
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
uint16_t GetTypeIndex() const { return type_index_; }
const DexFile& GetDexFile() const { return dex_file_; }
@@ -3332,7 +3358,6 @@
DECLARE_INSTRUCTION(NewInstance);
private:
- const uint32_t dex_pc_;
const uint16_t type_index_;
const DexFile& dex_file_;
const QuickEntrypointEnum entrypoint_;
@@ -3342,16 +3367,16 @@
class HNeg : public HUnaryOperation {
public:
- HNeg(Primitive::Type result_type, HInstruction* input)
- : HUnaryOperation(result_type, input) {}
+ HNeg(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
+ : HUnaryOperation(result_type, input, dex_pc) {}
template <typename T> T Compute(T x) const { return -x; }
HConstant* Evaluate(HIntConstant* x) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(Neg);
@@ -3368,8 +3393,7 @@
uint16_t type_index,
const DexFile& dex_file,
QuickEntrypointEnum entrypoint)
- : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC()),
- dex_pc_(dex_pc),
+ : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc),
type_index_(type_index),
dex_file_(dex_file),
entrypoint_(entrypoint) {
@@ -3377,7 +3401,6 @@
SetRawInputAt(1, current_method);
}
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
uint16_t GetTypeIndex() const { return type_index_; }
const DexFile& GetDexFile() const { return dex_file_; }
@@ -3394,7 +3417,6 @@
DECLARE_INSTRUCTION(NewArray);
private:
- const uint32_t dex_pc_;
const uint16_t type_index_;
const DexFile& dex_file_;
const QuickEntrypointEnum entrypoint_;
@@ -3404,18 +3426,23 @@
class HAdd : public HBinaryOperation {
public:
- HAdd(Primitive::Type result_type, HInstruction* left, HInstruction* right)
- : HBinaryOperation(result_type, left, right) {}
+ HAdd(Primitive::Type result_type,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
bool IsCommutative() const OVERRIDE { return true; }
template <typename T> T Compute(T x, T y) const { return x + y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(Add);
@@ -3426,16 +3453,21 @@
class HSub : public HBinaryOperation {
public:
- HSub(Primitive::Type result_type, HInstruction* left, HInstruction* right)
- : HBinaryOperation(result_type, left, right) {}
+ HSub(Primitive::Type result_type,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
template <typename T> T Compute(T x, T y) const { return x - y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(Sub);
@@ -3446,18 +3478,23 @@
class HMul : public HBinaryOperation {
public:
- HMul(Primitive::Type result_type, HInstruction* left, HInstruction* right)
- : HBinaryOperation(result_type, left, right) {}
+ HMul(Primitive::Type result_type,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
bool IsCommutative() const OVERRIDE { return true; }
template <typename T> T Compute(T x, T y) const { return x * y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(Mul);
@@ -3468,9 +3505,11 @@
class HDiv : public HBinaryOperation {
public:
- HDiv(Primitive::Type result_type, HInstruction* left, HInstruction* right, uint32_t dex_pc)
- : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls()),
- dex_pc_(dex_pc) {}
+ HDiv(Primitive::Type result_type,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc)
+ : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls(), dex_pc) {}
template <typename T>
T Compute(T x, T y) const {
@@ -3482,14 +3521,14 @@
}
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
-
static SideEffects SideEffectsForArchRuntimeCalls() {
// The generated code can use a runtime call.
return SideEffects::CanTriggerGC();
@@ -3498,16 +3537,16 @@
DECLARE_INSTRUCTION(Div);
private:
- const uint32_t dex_pc_;
-
DISALLOW_COPY_AND_ASSIGN(HDiv);
};
class HRem : public HBinaryOperation {
public:
- HRem(Primitive::Type result_type, HInstruction* left, HInstruction* right, uint32_t dex_pc)
- : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls()),
- dex_pc_(dex_pc) {}
+ HRem(Primitive::Type result_type,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc)
+ : HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls(), dex_pc) {}
template <typename T>
T Compute(T x, T y) const {
@@ -3519,13 +3558,14 @@
}
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
static SideEffects SideEffectsForArchRuntimeCalls() {
return SideEffects::CanTriggerGC();
@@ -3534,15 +3574,13 @@
DECLARE_INSTRUCTION(Rem);
private:
- const uint32_t dex_pc_;
-
DISALLOW_COPY_AND_ASSIGN(HRem);
};
class HDivZeroCheck : public HExpression<1> {
public:
HDivZeroCheck(HInstruction* value, uint32_t dex_pc)
- : HExpression(value->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
+ : HExpression(value->GetType(), SideEffects::None(), dex_pc) {
SetRawInputAt(0, value);
}
@@ -3558,20 +3596,19 @@
bool NeedsEnvironment() const OVERRIDE { return true; }
bool CanThrow() const OVERRIDE { return true; }
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
-
DECLARE_INSTRUCTION(DivZeroCheck);
private:
- const uint32_t dex_pc_;
-
DISALLOW_COPY_AND_ASSIGN(HDivZeroCheck);
};
class HShl : public HBinaryOperation {
public:
- HShl(Primitive::Type result_type, HInstruction* left, HInstruction* right)
- : HBinaryOperation(result_type, left, right) {}
+ HShl(Primitive::Type result_type,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
template <typename T, typename U, typename V>
T Compute(T x, U y, V max_shift_value) const {
@@ -3582,17 +3619,17 @@
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue));
+ Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
}
// There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this
// case is handled as `x << static_cast<int>(y)`.
HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue));
+ Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue));
+ Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
}
DECLARE_INSTRUCTION(Shl);
@@ -3603,8 +3640,11 @@
class HShr : public HBinaryOperation {
public:
- HShr(Primitive::Type result_type, HInstruction* left, HInstruction* right)
- : HBinaryOperation(result_type, left, right) {}
+ HShr(Primitive::Type result_type,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
template <typename T, typename U, typename V>
T Compute(T x, U y, V max_shift_value) const {
@@ -3615,17 +3655,17 @@
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue));
+ Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
}
// There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this
// case is handled as `x >> static_cast<int>(y)`.
HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue));
+ Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue));
+ Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
}
DECLARE_INSTRUCTION(Shr);
@@ -3636,8 +3676,11 @@
class HUShr : public HBinaryOperation {
public:
- HUShr(Primitive::Type result_type, HInstruction* left, HInstruction* right)
- : HBinaryOperation(result_type, left, right) {}
+ HUShr(Primitive::Type result_type,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
template <typename T, typename U, typename V>
T Compute(T x, U y, V max_shift_value) const {
@@ -3649,17 +3692,17 @@
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue));
+ Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
}
// There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this
// case is handled as `x >>> static_cast<int>(y)`.
HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue));
+ Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue));
+ Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
}
DECLARE_INSTRUCTION(UShr);
@@ -3670,8 +3713,11 @@
class HAnd : public HBinaryOperation {
public:
- HAnd(Primitive::Type result_type, HInstruction* left, HInstruction* right)
- : HBinaryOperation(result_type, left, right) {}
+ HAnd(Primitive::Type result_type,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
bool IsCommutative() const OVERRIDE { return true; }
@@ -3679,16 +3725,20 @@
auto Compute(T x, U y) const -> decltype(x & y) { return x & y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(And);
@@ -3699,8 +3749,11 @@
class HOr : public HBinaryOperation {
public:
- HOr(Primitive::Type result_type, HInstruction* left, HInstruction* right)
- : HBinaryOperation(result_type, left, right) {}
+ HOr(Primitive::Type result_type,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
bool IsCommutative() const OVERRIDE { return true; }
@@ -3708,16 +3761,20 @@
auto Compute(T x, U y) const -> decltype(x | y) { return x | y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(Or);
@@ -3728,8 +3785,11 @@
class HXor : public HBinaryOperation {
public:
- HXor(Primitive::Type result_type, HInstruction* left, HInstruction* right)
- : HBinaryOperation(result_type, left, right) {}
+ HXor(Primitive::Type result_type,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
bool IsCommutative() const OVERRIDE { return true; }
@@ -3737,16 +3797,20 @@
auto Compute(T x, U y) const -> decltype(x ^ y) { return x ^ y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue(), y->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(Xor);
@@ -3759,8 +3823,10 @@
// the calling convention.
class HParameterValue : public HExpression<0> {
public:
- HParameterValue(uint8_t index, Primitive::Type parameter_type, bool is_this = false)
- : HExpression(parameter_type, SideEffects::None()),
+ HParameterValue(uint8_t index,
+ Primitive::Type parameter_type,
+ bool is_this = false)
+ : HExpression(parameter_type, SideEffects::None(), kNoDexPc),
index_(index),
is_this_(is_this),
can_be_null_(!is_this) {}
@@ -3789,8 +3855,8 @@
class HNot : public HUnaryOperation {
public:
- HNot(Primitive::Type result_type, HInstruction* input)
- : HUnaryOperation(result_type, input) {}
+ HNot(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
+ : HUnaryOperation(result_type, input, dex_pc) {}
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
@@ -3801,10 +3867,10 @@
template <typename T> T Compute(T x) const { return ~x; }
HConstant* Evaluate(HIntConstant* x) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()));
+ return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()), GetDexPc());
}
DECLARE_INSTRUCTION(Not);
@@ -3815,8 +3881,8 @@
class HBooleanNot : public HUnaryOperation {
public:
- explicit HBooleanNot(HInstruction* input)
- : HUnaryOperation(Primitive::Type::kPrimBoolean, input) {}
+ explicit HBooleanNot(HInstruction* input, uint32_t dex_pc = kNoDexPc)
+ : HUnaryOperation(Primitive::Type::kPrimBoolean, input, dex_pc) {}
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
@@ -3830,7 +3896,7 @@
}
HConstant* Evaluate(HIntConstant* x) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()));
+ return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x ATTRIBUTE_UNUSED) const OVERRIDE {
LOG(FATAL) << DebugName() << " is not defined for long values";
@@ -3847,8 +3913,9 @@
public:
// Instantiate a type conversion of `input` to `result_type`.
HTypeConversion(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc)
- : HExpression(result_type, SideEffectsForArchRuntimeCalls(input->GetType(), result_type)),
- dex_pc_(dex_pc) {
+ : HExpression(result_type,
+ SideEffectsForArchRuntimeCalls(input->GetType(), result_type),
+ dex_pc) {
SetRawInputAt(0, input);
DCHECK_NE(input->GetType(), result_type);
}
@@ -3859,7 +3926,6 @@
// Required by the x86 and ARM code generators when producing calls
// to the runtime.
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; }
@@ -3883,8 +3949,6 @@
DECLARE_INSTRUCTION(TypeConversion);
private:
- const uint32_t dex_pc_;
-
DISALLOW_COPY_AND_ASSIGN(HTypeConversion);
};
@@ -3892,8 +3956,12 @@
class HPhi : public HInstruction {
public:
- HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type)
- : HInstruction(SideEffects::None()),
+ HPhi(ArenaAllocator* arena,
+ uint32_t reg_number,
+ size_t number_of_inputs,
+ Primitive::Type type,
+ uint32_t dex_pc = kNoDexPc)
+ : HInstruction(SideEffects::None(), dex_pc),
inputs_(arena, number_of_inputs),
reg_number_(reg_number),
type_(type),
@@ -3971,7 +4039,7 @@
class HNullCheck : public HExpression<1> {
public:
HNullCheck(HInstruction* value, uint32_t dex_pc)
- : HExpression(value->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
+ : HExpression(value->GetType(), SideEffects::None(), dex_pc) {
SetRawInputAt(0, value);
}
@@ -3987,13 +4055,10 @@
bool CanBeNull() const OVERRIDE { return false; }
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
DECLARE_INSTRUCTION(NullCheck);
private:
- const uint32_t dex_pc_;
-
DISALLOW_COPY_AND_ASSIGN(HNullCheck);
};
@@ -4036,10 +4101,11 @@
bool is_volatile,
uint32_t field_idx,
const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache)
+ Handle<mirror::DexCache> dex_cache,
+ uint32_t dex_pc = kNoDexPc)
: HExpression(
field_type,
- SideEffects::FieldReadOfType(field_type, is_volatile)),
+ SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
field_info_(field_offset, field_type, is_volatile, field_idx, dex_file, dex_cache) {
SetRawInputAt(0, value);
}
@@ -4081,9 +4147,10 @@
bool is_volatile,
uint32_t field_idx,
const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache)
+ Handle<mirror::DexCache> dex_cache,
+ uint32_t dex_pc = kNoDexPc)
: HTemplateInstruction(
- SideEffects::FieldWriteOfType(field_type, is_volatile)),
+ SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
field_info_(field_offset, field_type, is_volatile, field_idx, dex_file, dex_cache),
value_can_be_null_(true) {
SetRawInputAt(0, object);
@@ -4113,8 +4180,11 @@
class HArrayGet : public HExpression<2> {
public:
- HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type)
- : HExpression(type, SideEffects::ArrayReadOfType(type)) {
+ HArrayGet(HInstruction* array,
+ HInstruction* index,
+ Primitive::Type type,
+ uint32_t dex_pc = kNoDexPc)
+ : HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) {
SetRawInputAt(0, array);
SetRawInputAt(1, index);
}
@@ -4154,8 +4224,7 @@
uint32_t dex_pc)
: HTemplateInstruction(
SideEffects::ArrayWriteOfType(expected_component_type).Union(
- SideEffectsForArchRuntimeCalls(value->GetType()))),
- dex_pc_(dex_pc),
+ SideEffectsForArchRuntimeCalls(value->GetType())), dex_pc),
expected_component_type_(expected_component_type),
needs_type_check_(value->GetType() == Primitive::kPrimNot),
value_can_be_null_(true) {
@@ -4190,8 +4259,6 @@
bool GetValueCanBeNull() const { return value_can_be_null_; }
bool NeedsTypeCheck() const { return needs_type_check_; }
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
-
HInstruction* GetArray() const { return InputAt(0); }
HInstruction* GetIndex() const { return InputAt(1); }
HInstruction* GetValue() const { return InputAt(2); }
@@ -4214,7 +4281,6 @@
DECLARE_INSTRUCTION(ArraySet);
private:
- const uint32_t dex_pc_;
const Primitive::Type expected_component_type_;
bool needs_type_check_;
bool value_can_be_null_;
@@ -4224,8 +4290,8 @@
class HArrayLength : public HExpression<1> {
public:
- explicit HArrayLength(HInstruction* array)
- : HExpression(Primitive::kPrimInt, SideEffects::None()) {
+ explicit HArrayLength(HInstruction* array, uint32_t dex_pc = kNoDexPc)
+ : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) {
// Note that arrays do not change length, so the instruction does not
// depend on any write.
SetRawInputAt(0, array);
@@ -4249,7 +4315,7 @@
class HBoundsCheck : public HExpression<2> {
public:
HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
- : HExpression(index->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
+ : HExpression(index->GetType(), SideEffects::None(), dex_pc) {
DCHECK(index->GetType() == Primitive::kPrimInt);
SetRawInputAt(0, index);
SetRawInputAt(1, length);
@@ -4265,13 +4331,10 @@
bool CanThrow() const OVERRIDE { return true; }
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
DECLARE_INSTRUCTION(BoundsCheck);
private:
- const uint32_t dex_pc_;
-
DISALLOW_COPY_AND_ASSIGN(HBoundsCheck);
};
@@ -4284,7 +4347,8 @@
*/
class HTemporary : public HTemplateInstruction<0> {
public:
- explicit HTemporary(size_t index) : HTemplateInstruction(SideEffects::None()), index_(index) {}
+ explicit HTemporary(size_t index, uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction(SideEffects::None(), dex_pc), index_(index) {}
size_t GetIndex() const { return index_; }
@@ -4298,28 +4362,24 @@
private:
const size_t index_;
-
DISALLOW_COPY_AND_ASSIGN(HTemporary);
};
class HSuspendCheck : public HTemplateInstruction<0> {
public:
explicit HSuspendCheck(uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::CanTriggerGC()), dex_pc_(dex_pc), slow_path_(nullptr) {}
+ : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) {}
bool NeedsEnvironment() const OVERRIDE {
return true;
}
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
void SetSlowPath(SlowPathCode* slow_path) { slow_path_ = slow_path; }
SlowPathCode* GetSlowPath() const { return slow_path_; }
DECLARE_INSTRUCTION(SuspendCheck);
private:
- const uint32_t dex_pc_;
-
// Only used for code generation, in order to share the same slow path between back edges
// of a same loop.
SlowPathCode* slow_path_;
@@ -4337,11 +4397,10 @@
const DexFile& dex_file,
bool is_referrers_class,
uint32_t dex_pc)
- : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls()),
+ : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
type_index_(type_index),
dex_file_(dex_file),
is_referrers_class_(is_referrers_class),
- dex_pc_(dex_pc),
generate_clinit_check_(false),
loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
SetRawInputAt(0, current_method);
@@ -4355,7 +4414,6 @@
size_t ComputeHashCode() const OVERRIDE { return type_index_; }
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
uint16_t GetTypeIndex() const { return type_index_; }
bool IsReferrersClass() const { return is_referrers_class_; }
bool CanBeNull() const OVERRIDE { return false; }
@@ -4408,7 +4466,6 @@
const uint16_t type_index_;
const DexFile& dex_file_;
const bool is_referrers_class_;
- const uint32_t dex_pc_;
// Whether this instruction must generate the initialization check.
// Used for code generation.
bool generate_clinit_check_;
@@ -4421,9 +4478,8 @@
class HLoadString : public HExpression<1> {
public:
HLoadString(HCurrentMethod* current_method, uint32_t string_index, uint32_t dex_pc)
- : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls()),
- string_index_(string_index),
- dex_pc_(dex_pc) {
+ : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
+ string_index_(string_index) {
SetRawInputAt(0, current_method);
}
@@ -4435,7 +4491,6 @@
size_t ComputeHashCode() const OVERRIDE { return string_index_; }
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
uint32_t GetStringIndex() const { return string_index_; }
// TODO: Can we deopt or debug when we resolve a string?
@@ -4451,7 +4506,6 @@
private:
const uint32_t string_index_;
- const uint32_t dex_pc_;
DISALLOW_COPY_AND_ASSIGN(HLoadString);
};
@@ -4464,8 +4518,8 @@
HClinitCheck(HLoadClass* constant, uint32_t dex_pc)
: HExpression(
Primitive::kPrimNot,
- SideEffects::AllChanges()), // Assume write/read on all fields/arrays.
- dex_pc_(dex_pc) {
+ SideEffects::AllChanges(), // Assume write/read on all fields/arrays.
+ dex_pc) {
SetRawInputAt(0, constant);
}
@@ -4480,15 +4534,12 @@
return true;
}
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
HLoadClass* GetLoadClass() const { return InputAt(0)->AsLoadClass(); }
DECLARE_INSTRUCTION(ClinitCheck);
private:
- const uint32_t dex_pc_;
-
DISALLOW_COPY_AND_ASSIGN(HClinitCheck);
};
@@ -4500,10 +4551,11 @@
bool is_volatile,
uint32_t field_idx,
const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache)
+ Handle<mirror::DexCache> dex_cache,
+ uint32_t dex_pc = kNoDexPc)
: HExpression(
field_type,
- SideEffects::FieldReadOfType(field_type, is_volatile)),
+ SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
field_info_(field_offset, field_type, is_volatile, field_idx, dex_file, dex_cache) {
SetRawInputAt(0, cls);
}
@@ -4542,9 +4594,10 @@
bool is_volatile,
uint32_t field_idx,
const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache)
+ Handle<mirror::DexCache> dex_cache,
+ uint32_t dex_pc = kNoDexPc)
: HTemplateInstruction(
- SideEffects::FieldWriteOfType(field_type, is_volatile)),
+ SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
field_info_(field_offset, field_type, is_volatile, field_idx, dex_file, dex_cache),
value_can_be_null_(true) {
SetRawInputAt(0, cls);
@@ -4572,7 +4625,8 @@
// Implement the move-exception DEX instruction.
class HLoadException : public HExpression<0> {
public:
- HLoadException() : HExpression(Primitive::kPrimNot, SideEffects::None()) {}
+ explicit HLoadException(uint32_t dex_pc = kNoDexPc)
+ : HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc) {}
bool CanBeNull() const OVERRIDE { return false; }
@@ -4586,7 +4640,8 @@
// Must not be removed because the runtime expects the TLS to get cleared.
class HClearException : public HTemplateInstruction<0> {
public:
- HClearException() : HTemplateInstruction(SideEffects::AllWrites()) {}
+ explicit HClearException(uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction(SideEffects::AllWrites(), dex_pc) {}
DECLARE_INSTRUCTION(ClearException);
@@ -4597,7 +4652,7 @@
class HThrow : public HTemplateInstruction<1> {
public:
HThrow(HInstruction* exception, uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::CanTriggerGC()), dex_pc_(dex_pc) {
+ : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) {
SetRawInputAt(0, exception);
}
@@ -4607,13 +4662,10 @@
bool CanThrow() const OVERRIDE { return true; }
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
DECLARE_INSTRUCTION(Throw);
private:
- const uint32_t dex_pc_;
-
DISALLOW_COPY_AND_ASSIGN(HThrow);
};
@@ -4623,10 +4675,11 @@
HLoadClass* constant,
bool class_is_final,
uint32_t dex_pc)
- : HExpression(Primitive::kPrimBoolean, SideEffectsForArchRuntimeCalls(class_is_final)),
+ : HExpression(Primitive::kPrimBoolean,
+ SideEffectsForArchRuntimeCalls(class_is_final),
+ dex_pc),
class_is_final_(class_is_final),
- must_do_null_check_(true),
- dex_pc_(dex_pc) {
+ must_do_null_check_(true) {
SetRawInputAt(0, object);
SetRawInputAt(1, constant);
}
@@ -4641,8 +4694,6 @@
return false;
}
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
-
bool IsClassFinal() const { return class_is_final_; }
// Used only in code generation.
@@ -4658,7 +4709,6 @@
private:
const bool class_is_final_;
bool must_do_null_check_;
- const uint32_t dex_pc_;
DISALLOW_COPY_AND_ASSIGN(HInstanceOf);
};
@@ -4667,8 +4717,11 @@
public:
// Constructs an HBoundType with the given upper_bound.
// Ensures that the upper_bound is valid.
- HBoundType(HInstruction* input, ReferenceTypeInfo upper_bound, bool upper_can_be_null)
- : HExpression(Primitive::kPrimNot, SideEffects::None()),
+ HBoundType(HInstruction* input,
+ ReferenceTypeInfo upper_bound,
+ bool upper_can_be_null,
+ uint32_t dex_pc = kNoDexPc)
+ : HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc),
upper_bound_(upper_bound),
upper_can_be_null_(upper_can_be_null),
can_be_null_(upper_can_be_null) {
@@ -4712,10 +4765,9 @@
HLoadClass* constant,
bool class_is_final,
uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::CanTriggerGC()),
+ : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc),
class_is_final_(class_is_final),
- must_do_null_check_(true),
- dex_pc_(dex_pc) {
+ must_do_null_check_(true) {
SetRawInputAt(0, object);
SetRawInputAt(1, constant);
}
@@ -4736,7 +4788,6 @@
bool MustDoNullCheck() const { return must_do_null_check_; }
void ClearMustDoNullCheck() { must_do_null_check_ = false; }
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
bool IsClassFinal() const { return class_is_final_; }
@@ -4745,16 +4796,15 @@
private:
const bool class_is_final_;
bool must_do_null_check_;
- const uint32_t dex_pc_;
DISALLOW_COPY_AND_ASSIGN(HCheckCast);
};
class HMemoryBarrier : public HTemplateInstruction<0> {
public:
- explicit HMemoryBarrier(MemBarrierKind barrier_kind)
+ explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc)
: HTemplateInstruction(
- SideEffects::AllWritesAndReads()), // Assume write/read on all fields/arrays.
+ SideEffects::AllWritesAndReads(), dex_pc), // Assume write/read on all fields/arrays.
barrier_kind_(barrier_kind) {}
MemBarrierKind GetBarrierKind() { return barrier_kind_; }
@@ -4776,8 +4826,8 @@
HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc)
: HTemplateInstruction(
- SideEffects::AllExceptGCDependency()), // Assume write/read on all fields/arrays.
- kind_(kind), dex_pc_(dex_pc) {
+ SideEffects::AllExceptGCDependency(), dex_pc), // Assume write/read on all fields/arrays.
+ kind_(kind) {
SetRawInputAt(0, object);
}
@@ -4791,7 +4841,6 @@
return IsEnter();
}
- uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
bool IsEnter() const { return kind_ == kEnter; }
@@ -4799,7 +4848,6 @@
private:
const OperationKind kind_;
- const uint32_t dex_pc_;
private:
DISALLOW_COPY_AND_ASSIGN(HMonitorOperation);
@@ -4814,7 +4862,8 @@
*/
class HFakeString : public HTemplateInstruction<0> {
public:
- HFakeString() : HTemplateInstruction(SideEffects::None()) {}
+ explicit HFakeString(uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction(SideEffects::None(), dex_pc) {}
Primitive::Type GetType() const OVERRIDE { return Primitive::kPrimNot; }
@@ -4902,8 +4951,8 @@
class HParallelMove : public HTemplateInstruction<0> {
public:
- explicit HParallelMove(ArenaAllocator* arena)
- : HTemplateInstruction(SideEffects::None()), moves_(arena, kDefaultNumberOfMoves) {}
+ explicit HParallelMove(ArenaAllocator* arena, uint32_t dex_pc = kNoDexPc)
+ : HTemplateInstruction(SideEffects::None(), dex_pc), moves_(arena, kDefaultNumberOfMoves) {}
void AddMove(Location source,
Location destination,
@@ -4953,6 +5002,14 @@
DISALLOW_COPY_AND_ASSIGN(HParallelMove);
};
+} // namespace art
+
+#ifdef ART_ENABLE_CODEGEN_x86
+#include "nodes_x86.h"
+#endif
+
+namespace art {
+
class HGraphVisitor : public ValueObject {
public:
explicit HGraphVisitor(HGraph* graph) : graph_(graph) {}
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
new file mode 100644
index 0000000..ddc5730
--- /dev/null
+++ b/compiler/optimizing/nodes_x86.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_NODES_X86_H_
+#define ART_COMPILER_OPTIMIZING_NODES_X86_H_
+
+namespace art {
+
+// Compute the address of the method for X86 Constant area support.
+class HX86ComputeBaseMethodAddress : public HExpression<0> {
+ public:
+ // Treat the value as an int32_t, but it is really a 32 bit native pointer.
+ HX86ComputeBaseMethodAddress() : HExpression(Primitive::kPrimInt, SideEffects::None()) {}
+
+ DECLARE_INSTRUCTION(X86ComputeBaseMethodAddress);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HX86ComputeBaseMethodAddress);
+};
+
+// Load a constant value from the constant table.
+class HX86LoadFromConstantTable : public HExpression<2> {
+ public:
+ HX86LoadFromConstantTable(HX86ComputeBaseMethodAddress* method_base,
+ HConstant* constant,
+ bool needs_materialization = true)
+ : HExpression(constant->GetType(), SideEffects::None()),
+ needs_materialization_(needs_materialization) {
+ SetRawInputAt(0, method_base);
+ SetRawInputAt(1, constant);
+ }
+
+ bool NeedsMaterialization() const { return needs_materialization_; }
+
+ HX86ComputeBaseMethodAddress* GetBaseMethodAddress() const {
+ return InputAt(0)->AsX86ComputeBaseMethodAddress();
+ }
+
+ HConstant* GetConstant() const {
+ return InputAt(1)->AsConstant();
+ }
+
+ DECLARE_INSTRUCTION(X86LoadFromConstantTable);
+
+ private:
+ const bool needs_materialization_;
+
+ DISALLOW_COPY_AND_ASSIGN(HX86LoadFromConstantTable);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_NODES_X86_H_
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 91b03d4..f549ba8 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -23,6 +23,10 @@
#include "instruction_simplifier_arm64.h"
#endif
+#ifdef ART_ENABLE_CODEGEN_x86
+#include "constant_area_fixups_x86.h"
+#endif
+
#include "art_method-inl.h"
#include "base/arena_allocator.h"
#include "base/arena_containers.h"
@@ -424,6 +428,17 @@
break;
}
#endif
+#ifdef ART_ENABLE_CODEGEN_x86
+ case kX86: {
+ x86::ConstantAreaFixups* constant_area_fixups =
+ new (arena) x86::ConstantAreaFixups(graph, stats);
+ HOptimization* x86_optimizations[] = {
+ constant_area_fixups
+ };
+ RunOptimizations(x86_optimizations, arraysize(x86_optimizations), pass_observer);
+ break;
+ }
+#endif
default:
break;
}
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index b887b89..0384e46 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -636,9 +636,9 @@
ScopedObjectAccess soa(Thread::Current());
ClassLinker* cl = Runtime::Current()->GetClassLinker();
mirror::DexCache* dex_cache = cl->FindDexCache(soa.Self(), instr->GetDexFile());
- ArtMethod* method = dex_cache->GetResolvedMethod(
- instr->GetDexMethodIndex(), cl->GetImagePointerSize());
- mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false);
+ size_t pointer_size = cl->GetImagePointerSize();
+ ArtMethod* method = dex_cache->GetResolvedMethod(instr->GetDexMethodIndex(), pointer_size);
+ mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size);
SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
}
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index 7825457..a4d1837 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -495,6 +495,7 @@
virtual void clz(Register rd, Register rm, Condition cond = AL) = 0;
virtual void movw(Register rd, uint16_t imm16, Condition cond = AL) = 0;
virtual void movt(Register rd, uint16_t imm16, Condition cond = AL) = 0;
+ virtual void rbit(Register rd, Register rm, Condition cond = AL) = 0;
// Multiply instructions.
virtual void mul(Register rd, Register rn, Register rm, Condition cond = AL) = 0;
@@ -668,11 +669,14 @@
virtual void LoadLiteral(DRegister dd, Literal* literal) = 0;
// Add signed constant value to rd. May clobber IP.
- virtual void AddConstant(Register rd, int32_t value, Condition cond = AL) = 0;
virtual void AddConstant(Register rd, Register rn, int32_t value,
- Condition cond = AL) = 0;
- virtual void AddConstantSetFlags(Register rd, Register rn, int32_t value,
- Condition cond = AL) = 0;
+ Condition cond = AL, SetCc set_cc = kCcDontCare) = 0;
+ void AddConstantSetFlags(Register rd, Register rn, int32_t value, Condition cond = AL) {
+ AddConstant(rd, rn, value, cond, kCcSet);
+ }
+ void AddConstant(Register rd, int32_t value, Condition cond = AL, SetCc set_cc = kCcDontCare) {
+ AddConstant(rd, rd, value, cond, set_cc);
+ }
// Load and Store. May clobber IP.
virtual void LoadImmediate(Register rd, int32_t value, Condition cond = AL) = 0;
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index d91ddee..f7772ae 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -735,6 +735,20 @@
}
+void Arm32Assembler::rbit(Register rd, Register rm, Condition cond) {
+ CHECK_NE(rd, kNoRegister);
+ CHECK_NE(rm, kNoRegister);
+ CHECK_NE(cond, kNoCondition);
+ CHECK_NE(rd, PC);
+ CHECK_NE(rm, PC);
+ int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
+ B26 | B25 | B23 | B22 | B21 | B20 | (0xf << 16) |
+ (static_cast<int32_t>(rd) << kRdShift) |
+ (0xf << 8) | B5 | B4 | static_cast<int32_t>(rm);
+ Emit(encoding);
+}
+
+
void Arm32Assembler::EmitMulOp(Condition cond, int32_t opcode,
Register rd, Register rn,
Register rm, Register rs) {
@@ -1321,16 +1335,12 @@
UNREACHABLE();
}
-void Arm32Assembler::AddConstant(Register rd, int32_t value, Condition cond) {
- AddConstant(rd, rd, value, cond);
-}
-
void Arm32Assembler::AddConstant(Register rd, Register rn, int32_t value,
- Condition cond) {
- if (value == 0) {
+ Condition cond, SetCc set_cc) {
+ if (value == 0 && set_cc != kCcSet) {
if (rd != rn) {
- mov(rd, ShifterOperand(rn), cond);
+ mov(rd, ShifterOperand(rn), cond, set_cc);
}
return;
}
@@ -1339,55 +1349,29 @@
// the readability of generated code for some constants.
ShifterOperand shifter_op;
if (ShifterOperandCanHoldArm32(value, &shifter_op)) {
- add(rd, rn, shifter_op, cond);
+ add(rd, rn, shifter_op, cond, set_cc);
} else if (ShifterOperandCanHoldArm32(-value, &shifter_op)) {
- sub(rd, rn, shifter_op, cond);
+ sub(rd, rn, shifter_op, cond, set_cc);
} else {
CHECK(rn != IP);
if (ShifterOperandCanHoldArm32(~value, &shifter_op)) {
- mvn(IP, shifter_op, cond);
- add(rd, rn, ShifterOperand(IP), cond);
+ mvn(IP, shifter_op, cond, kCcKeep);
+ add(rd, rn, ShifterOperand(IP), cond, set_cc);
} else if (ShifterOperandCanHoldArm32(~(-value), &shifter_op)) {
- mvn(IP, shifter_op, cond);
- sub(rd, rn, ShifterOperand(IP), cond);
+ mvn(IP, shifter_op, cond, kCcKeep);
+ sub(rd, rn, ShifterOperand(IP), cond, set_cc);
} else {
movw(IP, Low16Bits(value), cond);
uint16_t value_high = High16Bits(value);
if (value_high != 0) {
movt(IP, value_high, cond);
}
- add(rd, rn, ShifterOperand(IP), cond);
+ add(rd, rn, ShifterOperand(IP), cond, set_cc);
}
}
}
-void Arm32Assembler::AddConstantSetFlags(Register rd, Register rn, int32_t value,
- Condition cond) {
- ShifterOperand shifter_op;
- if (ShifterOperandCanHoldArm32(value, &shifter_op)) {
- add(rd, rn, shifter_op, cond, kCcSet);
- } else if (ShifterOperandCanHoldArm32(-value, &shifter_op)) {
- sub(rd, rn, shifter_op, cond, kCcSet);
- } else {
- CHECK(rn != IP);
- if (ShifterOperandCanHoldArm32(~value, &shifter_op)) {
- mvn(IP, shifter_op, cond);
- add(rd, rn, ShifterOperand(IP), cond, kCcSet);
- } else if (ShifterOperandCanHoldArm32(~(-value), &shifter_op)) {
- mvn(IP, shifter_op, cond);
- sub(rd, rn, ShifterOperand(IP), cond, kCcSet);
- } else {
- movw(IP, Low16Bits(value), cond);
- uint16_t value_high = High16Bits(value);
- if (value_high != 0) {
- movt(IP, value_high, cond);
- }
- add(rd, rn, ShifterOperand(IP), cond, kCcSet);
- }
- }
-}
-
void Arm32Assembler::LoadImmediate(Register rd, int32_t value, Condition cond) {
ShifterOperand shifter_op;
if (ShifterOperandCanHoldArm32(value, &shifter_op)) {
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index b96bb74..3407369 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -87,6 +87,7 @@
void clz(Register rd, Register rm, Condition cond = AL) OVERRIDE;
void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
+ void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE;
// Multiply instructions.
void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE;
@@ -254,11 +255,8 @@
void LoadLiteral(DRegister dd, Literal* literal) OVERRIDE;
// Add signed constant value to rd. May clobber IP.
- void AddConstant(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
void AddConstant(Register rd, Register rn, int32_t value,
- Condition cond = AL) OVERRIDE;
- void AddConstantSetFlags(Register rd, Register rn, int32_t value,
- Condition cond = AL) OVERRIDE;
+ Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
// Load and Store. May clobber IP.
void LoadImmediate(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index e6412ac..2a0912e 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -883,4 +883,8 @@
DriverStr(expected, "strexd");
}
+TEST_F(AssemblerArm32Test, rbit) {
+ T3Helper(&arm::Arm32Assembler::rbit, true, "rbit{cond} {reg1}, {reg2}", "rbit");
+}
+
} // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 90ed10c..0f6c4f5 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -2426,6 +2426,25 @@
}
+void Thumb2Assembler::rbit(Register rd, Register rm, Condition cond) {
+ CHECK_NE(rd, kNoRegister);
+ CHECK_NE(rm, kNoRegister);
+ CheckCondition(cond);
+ CHECK_NE(rd, PC);
+ CHECK_NE(rm, PC);
+ CHECK_NE(rd, SP);
+ CHECK_NE(rm, SP);
+ int32_t encoding = B31 | B30 | B29 | B28 | B27 |
+ B25 | B23 | B20 |
+ static_cast<uint32_t>(rm) << 16 |
+ 0xf << 12 |
+ static_cast<uint32_t>(rd) << 8 |
+ B7 | B5 |
+ static_cast<uint32_t>(rm);
+ Emit32(encoding);
+}
+
+
void Thumb2Assembler::ldrex(Register rt, Register rn, uint16_t imm, Condition cond) {
CHECK_NE(rn, kNoRegister);
CHECK_NE(rt, kNoRegister);
@@ -3192,14 +3211,10 @@
DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size());
}
-void Thumb2Assembler::AddConstant(Register rd, int32_t value, Condition cond) {
- AddConstant(rd, rd, value, cond);
-}
-
void Thumb2Assembler::AddConstant(Register rd, Register rn, int32_t value,
- Condition cond) {
- if (value == 0) {
+ Condition cond, SetCc set_cc) {
+ if (value == 0 && set_cc != kCcSet) {
if (rd != rn) {
mov(rd, ShifterOperand(rn), cond);
}
@@ -3210,51 +3225,24 @@
// the readability of generated code for some constants.
ShifterOperand shifter_op;
if (ShifterOperandCanHold(rd, rn, ADD, value, &shifter_op)) {
- add(rd, rn, shifter_op, cond);
+ add(rd, rn, shifter_op, cond, set_cc);
} else if (ShifterOperandCanHold(rd, rn, SUB, -value, &shifter_op)) {
- sub(rd, rn, shifter_op, cond);
+ sub(rd, rn, shifter_op, cond, set_cc);
} else {
CHECK(rn != IP);
if (ShifterOperandCanHold(rd, rn, MVN, ~value, &shifter_op)) {
- mvn(IP, shifter_op, cond);
- add(rd, rn, ShifterOperand(IP), cond);
+ mvn(IP, shifter_op, cond, kCcKeep);
+ add(rd, rn, ShifterOperand(IP), cond, set_cc);
} else if (ShifterOperandCanHold(rd, rn, MVN, ~(-value), &shifter_op)) {
- mvn(IP, shifter_op, cond);
- sub(rd, rn, ShifterOperand(IP), cond);
+ mvn(IP, shifter_op, cond, kCcKeep);
+ sub(rd, rn, ShifterOperand(IP), cond, set_cc);
} else {
movw(IP, Low16Bits(value), cond);
uint16_t value_high = High16Bits(value);
if (value_high != 0) {
movt(IP, value_high, cond);
}
- add(rd, rn, ShifterOperand(IP), cond);
- }
- }
-}
-
-
-void Thumb2Assembler::AddConstantSetFlags(Register rd, Register rn, int32_t value,
- Condition cond) {
- ShifterOperand shifter_op;
- if (ShifterOperandCanHold(rd, rn, ADD, value, &shifter_op)) {
- add(rd, rn, shifter_op, cond, kCcSet);
- } else if (ShifterOperandCanHold(rd, rn, ADD, -value, &shifter_op)) {
- sub(rd, rn, shifter_op, cond, kCcSet);
- } else {
- CHECK(rn != IP);
- if (ShifterOperandCanHold(rd, rn, MVN, ~value, &shifter_op)) {
- mvn(IP, shifter_op, cond);
- add(rd, rn, ShifterOperand(IP), cond, kCcSet);
- } else if (ShifterOperandCanHold(rd, rn, MVN, ~(-value), &shifter_op)) {
- mvn(IP, shifter_op, cond);
- sub(rd, rn, ShifterOperand(IP), cond, kCcSet);
- } else {
- movw(IP, Low16Bits(value), cond);
- uint16_t value_high = High16Bits(value);
- if (value_high != 0) {
- movt(IP, value_high, cond);
- }
- add(rd, rn, ShifterOperand(IP), cond, kCcSet);
+ add(rd, rn, ShifterOperand(IP), cond, set_cc);
}
}
}
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index c802c27..a1a8927 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -111,6 +111,7 @@
void clz(Register rd, Register rm, Condition cond = AL) OVERRIDE;
void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
+ void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE;
// Multiply instructions.
void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE;
@@ -297,11 +298,8 @@
void LoadLiteral(DRegister dd, Literal* literal) OVERRIDE;
// Add signed constant value to rd. May clobber IP.
- void AddConstant(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
void AddConstant(Register rd, Register rn, int32_t value,
- Condition cond = AL) OVERRIDE;
- void AddConstantSetFlags(Register rd, Register rn, int32_t value,
- Condition cond = AL) OVERRIDE;
+ Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
// Load and Store. May clobber IP.
void LoadImmediate(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 84f5cb1..9c08ce0 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -1019,4 +1019,12 @@
DriverStr(expected, "clz");
}
+TEST_F(AssemblerThumb2Test, rbit) {
+ __ rbit(arm::R1, arm::R0);
+
+ const char* expected = "rbit r1, r0\n";
+
+ DriverStr(expected, "rbit");
+}
+
} // namespace art
diff --git a/compiler/utils/dex_cache_arrays_layout-inl.h b/compiler/utils/dex_cache_arrays_layout-inl.h
deleted file mode 100644
index fec981a..0000000
--- a/compiler/utils/dex_cache_arrays_layout-inl.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
-#define ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
-
-#include "dex_cache_arrays_layout.h"
-
-#include "base/bit_utils.h"
-#include "base/logging.h"
-#include "globals.h"
-#include "mirror/array-inl.h"
-#include "primitive.h"
-
-namespace art {
-
-inline DexCacheArraysLayout::DexCacheArraysLayout(size_t pointer_size, const DexFile* dex_file)
- : /* types_offset_ is always 0u */
- pointer_size_(pointer_size),
- methods_offset_(types_offset_ + TypesSize(dex_file->NumTypeIds())),
- strings_offset_(methods_offset_ + MethodsSize(dex_file->NumMethodIds())),
- fields_offset_(strings_offset_ + StringsSize(dex_file->NumStringIds())),
- size_(fields_offset_ + FieldsSize(dex_file->NumFieldIds())) {
- DCHECK(ValidPointerSize(pointer_size)) << pointer_size;
-}
-
-inline size_t DexCacheArraysLayout::TypeOffset(uint32_t type_idx) const {
- return types_offset_ + ElementOffset(sizeof(mirror::HeapReference<mirror::Class>), type_idx);
-}
-
-inline size_t DexCacheArraysLayout::TypesSize(size_t num_elements) const {
- return ArraySize(sizeof(mirror::HeapReference<mirror::Class>), num_elements);
-}
-
-inline size_t DexCacheArraysLayout::MethodOffset(uint32_t method_idx) const {
- return methods_offset_ + ElementOffset(pointer_size_, method_idx);
-}
-
-inline size_t DexCacheArraysLayout::MethodsSize(size_t num_elements) const {
- return ArraySize(pointer_size_, num_elements);
-}
-
-inline size_t DexCacheArraysLayout::StringOffset(uint32_t string_idx) const {
- return strings_offset_ + ElementOffset(sizeof(mirror::HeapReference<mirror::String>), string_idx);
-}
-
-inline size_t DexCacheArraysLayout::StringsSize(size_t num_elements) const {
- return ArraySize(sizeof(mirror::HeapReference<mirror::String>), num_elements);
-}
-
-inline size_t DexCacheArraysLayout::FieldOffset(uint32_t field_idx) const {
- return fields_offset_ + ElementOffset(pointer_size_, field_idx);
-}
-
-inline size_t DexCacheArraysLayout::FieldsSize(size_t num_elements) const {
- return ArraySize(pointer_size_, num_elements);
-}
-
-inline size_t DexCacheArraysLayout::ElementOffset(size_t element_size, uint32_t idx) {
- return mirror::Array::DataOffset(element_size).Uint32Value() + element_size * idx;
-}
-
-inline size_t DexCacheArraysLayout::ArraySize(size_t element_size, uint32_t num_elements) {
- size_t array_size = mirror::ComputeArraySize(num_elements, ComponentSizeShiftWidth(element_size));
- DCHECK_NE(array_size, 0u); // No overflow expected for dex cache arrays.
- return RoundUp(array_size, kObjectAlignment);
-}
-
-} // namespace art
-
-#endif // ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h
index f7c772d..9127b6b 100644
--- a/compiler/utils/swap_space.h
+++ b/compiler/utils/swap_space.h
@@ -163,7 +163,9 @@
pointer allocate(size_type n, SwapAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
DCHECK_LE(n, max_size());
if (swap_space_ == nullptr) {
- return reinterpret_cast<T*>(malloc(n * sizeof(T)));
+ T* result = reinterpret_cast<T*>(malloc(n * sizeof(T)));
+ CHECK(result != nullptr || n == 0u); // Abort if malloc() fails.
+ return result;
} else {
return reinterpret_cast<T*>(swap_space_->Alloc(n * sizeof(T)));
}
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index a03f857..e3962b4 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -1750,6 +1750,10 @@
for (int i = 1; i < length; i++) {
EmitUint8(operand.encoding_[i]);
}
+ AssemblerFixup* fixup = operand.GetFixup();
+ if (fixup != nullptr) {
+ EmitFixup(fixup);
+ }
}
@@ -2322,5 +2326,56 @@
#undef __
}
+void X86Assembler::AddConstantArea() {
+ const std::vector<int32_t>& area = constant_area_.GetBuffer();
+ // Generate the data for the literal area.
+ for (size_t i = 0, e = area.size(); i < e; i++) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitInt32(area[i]);
+ }
+}
+
+int ConstantArea::AddInt32(int32_t v) {
+ for (size_t i = 0, e = buffer_.size(); i < e; i++) {
+ if (v == buffer_[i]) {
+ return i * kEntrySize;
+ }
+ }
+
+ // Didn't match anything.
+ int result = buffer_.size() * kEntrySize;
+ buffer_.push_back(v);
+ return result;
+}
+
+int ConstantArea::AddInt64(int64_t v) {
+ int32_t v_low = Low32Bits(v);
+ int32_t v_high = High32Bits(v);
+ if (buffer_.size() > 1) {
+ // Ensure we don't pass the end of the buffer.
+ for (size_t i = 0, e = buffer_.size() - 1; i < e; i++) {
+ if (v_low == buffer_[i] && v_high == buffer_[i + 1]) {
+ return i * kEntrySize;
+ }
+ }
+ }
+
+ // Didn't match anything.
+ int result = buffer_.size() * kEntrySize;
+ buffer_.push_back(v_low);
+ buffer_.push_back(v_high);
+ return result;
+}
+
+int ConstantArea::AddDouble(double v) {
+ // Treat the value as a 64-bit integer value.
+ return AddInt64(bit_cast<int64_t, double>(v));
+}
+
+int ConstantArea::AddFloat(float v) {
+ // Treat the value as a 32-bit integer value.
+ return AddInt32(bit_cast<int32_t, float>(v));
+}
+
} // namespace x86
} // namespace art
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 0c90f28..7d7b3d3 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -86,7 +86,7 @@
protected:
// Operand can be sub classed (e.g: Address).
- Operand() : length_(0) { }
+ Operand() : length_(0), fixup_(nullptr) { }
void SetModRM(int mod_in, Register rm_in) {
CHECK_EQ(mod_in & ~3, 0);
@@ -113,11 +113,23 @@
length_ += disp_size;
}
+ AssemblerFixup* GetFixup() const {
+ return fixup_;
+ }
+
+ void SetFixup(AssemblerFixup* fixup) {
+ fixup_ = fixup;
+ }
+
private:
uint8_t length_;
uint8_t encoding_[6];
- explicit Operand(Register reg) { SetModRM(3, reg); }
+ // A fixup can be associated with the operand, in order to be applied after the
+ // code has been generated. This is used for constant area fixups.
+ AssemblerFixup* fixup_;
+
+ explicit Operand(Register reg) : fixup_(nullptr) { SetModRM(3, reg); }
// Get the operand encoding byte at the given index.
uint8_t encoding_at(int index_in) const {
@@ -136,6 +148,11 @@
Init(base_in, disp);
}
+ Address(Register base_in, int32_t disp, AssemblerFixup *fixup) {
+ Init(base_in, disp);
+ SetFixup(fixup);
+ }
+
Address(Register base_in, Offset disp) {
Init(base_in, disp.Int32Value());
}
@@ -226,6 +243,50 @@
DISALLOW_COPY_AND_ASSIGN(NearLabel);
};
+/**
+ * Class to handle constant area values.
+ */
+class ConstantArea {
+ public:
+ ConstantArea() {}
+
+ // Add a double to the constant area, returning the offset into
+ // the constant area where the literal resides.
+ int AddDouble(double v);
+
+ // Add a float to the constant area, returning the offset into
+ // the constant area where the literal resides.
+ int AddFloat(float v);
+
+ // Add an int32_t to the constant area, returning the offset into
+ // the constant area where the literal resides.
+ int AddInt32(int32_t v);
+
+ // Add an int64_t to the constant area, returning the offset into
+ // the constant area where the literal resides.
+ int AddInt64(int64_t v);
+
+ bool IsEmpty() const {
+ return buffer_.size() == 0;
+ }
+
+ const std::vector<int32_t>& GetBuffer() const {
+ return buffer_;
+ }
+
+ void AddFixup(AssemblerFixup* fixup) {
+ fixups_.push_back(fixup);
+ }
+
+ const std::vector<AssemblerFixup*>& GetFixups() const {
+ return fixups_;
+ }
+
+ private:
+ static constexpr size_t kEntrySize = sizeof(int32_t);
+ std::vector<int32_t> buffer_;
+ std::vector<AssemblerFixup*> fixups_;
+};
class X86Assembler FINAL : public Assembler {
public:
@@ -667,6 +728,29 @@
}
}
+ // Add a double to the constant area, returning the offset into
+ // the constant area where the literal resides.
+ int AddDouble(double v) { return constant_area_.AddDouble(v); }
+
+ // Add a float to the constant area, returning the offset into
+ // the constant area where the literal resides.
+ int AddFloat(float v) { return constant_area_.AddFloat(v); }
+
+ // Add an int32_t to the constant area, returning the offset into
+ // the constant area where the literal resides.
+ int AddInt32(int32_t v) { return constant_area_.AddInt32(v); }
+
+ // Add an int64_t to the constant area, returning the offset into
+ // the constant area where the literal resides.
+ int AddInt64(int64_t v) { return constant_area_.AddInt64(v); }
+
+ // Add the contents of the constant area to the assembler buffer.
+ void AddConstantArea();
+
+ // Is the constant area empty? Return true if there are no literals in the constant area.
+ bool IsConstantAreaEmpty() const { return constant_area_.IsEmpty(); }
+ void AddConstantAreaFixup(AssemblerFixup* fixup) { constant_area_.AddFixup(fixup); }
+
private:
inline void EmitUint8(uint8_t value);
inline void EmitInt32(int32_t value);
@@ -685,6 +769,8 @@
void EmitGenericShift(int rm, const Operand& operand, const Immediate& imm);
void EmitGenericShift(int rm, const Operand& operand, Register shifter);
+ ConstantArea constant_area_;
+
DISALLOW_COPY_AND_ASSIGN(X86Assembler);
};
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index b59edc9..e248604 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -44,6 +44,7 @@
#include "mapping_table.h"
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
+#include "mirror/dex_cache-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "oat.h"
@@ -1605,22 +1606,19 @@
// stack. Need to revoke the thread-local allocation stacks that
// point into it.
ScopedThreadSuspension sts(self, kNative);
- ThreadList* thread_list = Runtime::Current()->GetThreadList();
- thread_list->SuspendAll(__FUNCTION__);
+ ScopedSuspendAll ssa(__FUNCTION__);
heap->RevokeAllThreadLocalAllocationStacks(self);
- thread_list->ResumeAll();
}
{
// Mark dex caches.
- dex_cache_arrays_.clear();
+ dex_caches_.clear();
{
ReaderMutexLock mu(self, *class_linker->DexLock());
for (jobject weak_root : class_linker->GetDexCaches()) {
mirror::DexCache* dex_cache =
down_cast<mirror::DexCache*>(self->DecodeJObject(weak_root));
if (dex_cache != nullptr) {
- dex_cache_arrays_.insert(dex_cache->GetResolvedFields());
- dex_cache_arrays_.insert(dex_cache->GetResolvedMethods());
+ dex_caches_.insert(dex_cache);
}
}
}
@@ -1656,22 +1654,25 @@
const auto& bitmap_section = image_header_.GetImageSection(ImageHeader::kSectionImageBitmap);
const auto& field_section = image_header_.GetImageSection(ImageHeader::kSectionArtFields);
const auto& method_section = image_header_.GetMethodsSection();
+ const auto& dex_cache_arrays_section = image_header_.GetImageSection(
+ ImageHeader::kSectionDexCacheArrays);
const auto& intern_section = image_header_.GetImageSection(
ImageHeader::kSectionInternedStrings);
stats_.header_bytes = header_bytes;
stats_.alignment_bytes += RoundUp(header_bytes, kObjectAlignment) - header_bytes;
// Add padding between the field and method section.
// (Field section is 4-byte aligned, method section is 8-byte aligned on 64-bit targets.)
- stats_.alignment_bytes +=
- method_section.Offset() - (field_section.Offset() + field_section.Size());
- // Add padding between the method section and the intern table.
- // (Method section is 4-byte aligned on 32-bit targets, intern table is 8-byte aligned.)
- stats_.alignment_bytes +=
- intern_section.Offset() - (method_section.Offset() + method_section.Size());
+ stats_.alignment_bytes += method_section.Offset() -
+ (field_section.Offset() + field_section.Size());
+ // Add padding between the dex cache arrays section and the intern table. (Dex cache
+ // arrays section is 4-byte aligned on 32-bit targets, intern table is 8-byte aligned.)
+ stats_.alignment_bytes += intern_section.Offset() -
+ (dex_cache_arrays_section.Offset() + dex_cache_arrays_section.Size());
stats_.alignment_bytes += bitmap_section.Offset() - image_header_.GetImageSize();
stats_.bitmap_bytes += bitmap_section.Size();
stats_.art_field_bytes += field_section.Size();
stats_.art_method_bytes += method_section.Size();
+ stats_.dex_cache_arrays_bytes += dex_cache_arrays_section.Size();
stats_.interned_strings_bytes += intern_section.Size();
stats_.Dump(os, indent_os);
os << "\n";
@@ -1878,33 +1879,73 @@
}
}
} else {
- auto it = state->dex_cache_arrays_.find(obj);
- if (it != state->dex_cache_arrays_.end()) {
+ auto it = state->dex_caches_.find(obj);
+ if (it != state->dex_caches_.end()) {
+ auto* dex_cache = down_cast<mirror::DexCache*>(obj);
const auto& field_section = state->image_header_.GetImageSection(
ImageHeader::kSectionArtFields);
const auto& method_section = state->image_header_.GetMethodsSection();
- auto* arr = down_cast<mirror::PointerArray*>(obj);
- for (int32_t i = 0, length = arr->GetLength(); i < length; i++) {
- void* elem = arr->GetElementPtrSize<void*>(i, image_pointer_size);
- size_t run = 0;
- for (int32_t j = i + 1; j < length &&
- elem == arr->GetElementPtrSize<void*>(j, image_pointer_size); j++, run++) { }
- if (run == 0) {
- os << StringPrintf("%d: ", i);
- } else {
- os << StringPrintf("%d to %zd: ", i, i + run);
- i = i + run;
+ size_t num_methods = dex_cache->NumResolvedMethods();
+ if (num_methods != 0u) {
+ os << "Methods (size=" << num_methods << "):";
+ ScopedIndentation indent2(&state->vios_);
+ auto* resolved_methods = dex_cache->GetResolvedMethods();
+ for (size_t i = 0, length = dex_cache->NumResolvedMethods(); i < length; ++i) {
+ auto* elem = mirror::DexCache::GetElementPtrSize(resolved_methods, i, image_pointer_size);
+ size_t run = 0;
+ for (size_t j = i + 1;
+ j != length && elem == mirror::DexCache::GetElementPtrSize(resolved_methods,
+ j,
+ image_pointer_size);
+ ++j, ++run) {}
+ if (run == 0) {
+ os << StringPrintf("%zd: ", i);
+ } else {
+ os << StringPrintf("%zd to %zd: ", i, i + run);
+ i = i + run;
+ }
+ std::string msg;
+ if (elem == nullptr) {
+ msg = "null";
+ } else if (method_section.Contains(
+ reinterpret_cast<uint8_t*>(elem) - state->image_space_.Begin())) {
+ msg = PrettyMethod(reinterpret_cast<ArtMethod*>(elem));
+ } else {
+ msg = "<not in method section>";
+ }
+ os << StringPrintf("%p %s\n", elem, msg.c_str());
}
- auto offset = reinterpret_cast<uint8_t*>(elem) - state->image_space_.Begin();
- std::string msg;
- if (field_section.Contains(offset)) {
- msg = PrettyField(reinterpret_cast<ArtField*>(elem));
- } else if (method_section.Contains(offset)) {
- msg = PrettyMethod(reinterpret_cast<ArtMethod*>(elem));
- } else {
- msg = "Unknown type";
+ }
+ size_t num_fields = dex_cache->NumResolvedFields();
+ if (num_fields != 0u) {
+ os << "Fields (size=" << num_fields << "):";
+ ScopedIndentation indent2(&state->vios_);
+ auto* resolved_fields = dex_cache->GetResolvedFields();
+ for (size_t i = 0, length = dex_cache->NumResolvedFields(); i < length; ++i) {
+ auto* elem = mirror::DexCache::GetElementPtrSize(resolved_fields, i, image_pointer_size);
+ size_t run = 0;
+ for (size_t j = i + 1;
+ j != length && elem == mirror::DexCache::GetElementPtrSize(resolved_fields,
+ j,
+ image_pointer_size);
+ ++j, ++run) {}
+ if (run == 0) {
+ os << StringPrintf("%zd: ", i);
+ } else {
+ os << StringPrintf("%zd to %zd: ", i, i + run);
+ i = i + run;
+ }
+ std::string msg;
+ if (elem == nullptr) {
+ msg = "null";
+ } else if (field_section.Contains(
+ reinterpret_cast<uint8_t*>(elem) - state->image_space_.Begin())) {
+ msg = PrettyField(reinterpret_cast<ArtField*>(elem));
+ } else {
+ msg = "<not in field section>";
+ }
+ os << StringPrintf("%p %s\n", elem, msg.c_str());
}
- os << StringPrintf("%p %s\n", elem, msg.c_str());
}
}
}
@@ -2019,6 +2060,7 @@
size_t object_bytes;
size_t art_field_bytes;
size_t art_method_bytes;
+ size_t dex_cache_arrays_bytes;
size_t interned_strings_bytes;
size_t bitmap_bytes;
size_t alignment_bytes;
@@ -2049,6 +2091,7 @@
object_bytes(0),
art_field_bytes(0),
art_method_bytes(0),
+ dex_cache_arrays_bytes(0),
interned_strings_bytes(0),
bitmap_bytes(0),
alignment_bytes(0),
@@ -2206,24 +2249,27 @@
{
os << "art_file_bytes = " << PrettySize(file_bytes) << "\n\n"
<< "art_file_bytes = header_bytes + object_bytes + alignment_bytes\n";
- indent_os << StringPrintf("header_bytes = %8zd (%2.0f%% of art file bytes)\n"
- "object_bytes = %8zd (%2.0f%% of art file bytes)\n"
- "art_field_bytes = %8zd (%2.0f%% of art file bytes)\n"
- "art_method_bytes = %8zd (%2.0f%% of art file bytes)\n"
- "interned_string_bytes = %8zd (%2.0f%% of art file bytes)\n"
- "bitmap_bytes = %8zd (%2.0f%% of art file bytes)\n"
- "alignment_bytes = %8zd (%2.0f%% of art file bytes)\n\n",
+ indent_os << StringPrintf("header_bytes = %8zd (%2.0f%% of art file bytes)\n"
+ "object_bytes = %8zd (%2.0f%% of art file bytes)\n"
+ "art_field_bytes = %8zd (%2.0f%% of art file bytes)\n"
+ "art_method_bytes = %8zd (%2.0f%% of art file bytes)\n"
+ "dex_cache_arrays_bytes = %8zd (%2.0f%% of art file bytes)\n"
+ "interned_string_bytes = %8zd (%2.0f%% of art file bytes)\n"
+ "bitmap_bytes = %8zd (%2.0f%% of art file bytes)\n"
+ "alignment_bytes = %8zd (%2.0f%% of art file bytes)\n\n",
header_bytes, PercentOfFileBytes(header_bytes),
object_bytes, PercentOfFileBytes(object_bytes),
art_field_bytes, PercentOfFileBytes(art_field_bytes),
art_method_bytes, PercentOfFileBytes(art_method_bytes),
+ dex_cache_arrays_bytes,
+ PercentOfFileBytes(dex_cache_arrays_bytes),
interned_strings_bytes,
PercentOfFileBytes(interned_strings_bytes),
bitmap_bytes, PercentOfFileBytes(bitmap_bytes),
alignment_bytes, PercentOfFileBytes(alignment_bytes))
<< std::flush;
CHECK_EQ(file_bytes, header_bytes + object_bytes + art_field_bytes + art_method_bytes +
- interned_strings_bytes + bitmap_bytes + alignment_bytes);
+ dex_cache_arrays_bytes + interned_strings_bytes + bitmap_bytes + alignment_bytes);
}
os << "object_bytes breakdown:\n";
@@ -2309,7 +2355,7 @@
const ImageHeader& image_header_;
std::unique_ptr<OatDumper> oat_dumper_;
OatDumperOptions* oat_dumper_options_;
- std::set<mirror::Object*> dex_cache_arrays_;
+ std::set<mirror::Object*> dex_caches_;
DISALLOW_COPY_AND_ASSIGN(ImageDumper);
};
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index a71197a..88622cc 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -523,18 +523,60 @@
auto* dex_caches = down_cast<mirror::ObjectArray<mirror::DexCache>*>(
img_roots->Get(ImageHeader::kDexCaches));
for (size_t i = 0, count = dex_caches->GetLength(); i < count; ++i) {
- auto* dex_cache = dex_caches->GetWithoutChecks(i);
- auto* fields = dex_cache->GetResolvedFields();
- if (fields != nullptr) {
- CHECK(!fields->IsObjectArray());
- CHECK(fields->IsArrayInstance());
- FixupNativePointerArray(fields);
+ auto* orig_dex_cache = dex_caches->GetWithoutChecks(i);
+ auto* copy_dex_cache = RelocatedCopyOf(orig_dex_cache);
+ const size_t pointer_size = InstructionSetPointerSize(isa_);
+ // Though the DexCache array fields are usually treated as native pointers, we set the full
+ // 64-bit values here, clearing the top 32 bits for 32-bit targets. The zero-extension is
+ // done by casting to the unsigned type uintptr_t before casting to int64_t, i.e.
+ // static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + offset))).
+ GcRoot<mirror::String>* orig_strings = orig_dex_cache->GetStrings();
+ GcRoot<mirror::String>* relocated_strings = RelocatedAddressOfPointer(orig_strings);
+ copy_dex_cache->SetField64<false>(
+ mirror::DexCache::StringsOffset(),
+ static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_strings)));
+ if (orig_strings != nullptr) {
+ GcRoot<mirror::String>* copy_strings = RelocatedCopyOf(orig_strings);
+ for (size_t j = 0, num = orig_dex_cache->NumStrings(); j != num; ++j) {
+ copy_strings[j] = GcRoot<mirror::String>(RelocatedAddressOfPointer(orig_strings[j].Read()));
+ }
}
- auto* methods = dex_cache->GetResolvedMethods();
- if (methods != nullptr) {
- CHECK(!methods->IsObjectArray());
- CHECK(methods->IsArrayInstance());
- FixupNativePointerArray(methods);
+ GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
+ GcRoot<mirror::Class>* relocated_types = RelocatedAddressOfPointer(orig_types);
+ copy_dex_cache->SetField64<false>(
+ mirror::DexCache::ResolvedTypesOffset(),
+ static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_types)));
+ if (orig_types != nullptr) {
+ GcRoot<mirror::Class>* copy_types = RelocatedCopyOf(orig_types);
+ for (size_t j = 0, num = orig_dex_cache->NumResolvedTypes(); j != num; ++j) {
+ copy_types[j] = GcRoot<mirror::Class>(RelocatedAddressOfPointer(orig_types[j].Read()));
+ }
+ }
+ ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods();
+ ArtMethod** relocated_methods = RelocatedAddressOfPointer(orig_methods);
+ copy_dex_cache->SetField64<false>(
+ mirror::DexCache::ResolvedMethodsOffset(),
+ static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_methods)));
+ if (orig_methods != nullptr) {
+ ArtMethod** copy_methods = RelocatedCopyOf(orig_methods);
+ for (size_t j = 0, num = orig_dex_cache->NumResolvedMethods(); j != num; ++j) {
+ ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, j, pointer_size);
+ ArtMethod* copy = RelocatedAddressOfPointer(orig);
+ mirror::DexCache::SetElementPtrSize(copy_methods, j, copy, pointer_size);
+ }
+ }
+ ArtField** orig_fields = orig_dex_cache->GetResolvedFields();
+ ArtField** relocated_fields = RelocatedAddressOfPointer(orig_fields);
+ copy_dex_cache->SetField64<false>(
+ mirror::DexCache::ResolvedFieldsOffset(),
+ static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_fields)));
+ if (orig_fields != nullptr) {
+ ArtField** copy_fields = RelocatedCopyOf(orig_fields);
+ for (size_t j = 0, num = orig_dex_cache->NumResolvedFields(); j != num; ++j) {
+ ArtField* orig = mirror::DexCache::GetElementPtrSize(orig_fields, j, pointer_size);
+ ArtField* copy = RelocatedAddressOfPointer(orig);
+ mirror::DexCache::SetElementPtrSize(copy_fields, j, copy, pointer_size);
+ }
}
}
}
@@ -627,6 +669,7 @@
if (object->IsClass<kVerifyNone>()) {
auto* klass = object->AsClass();
auto* copy_klass = down_cast<mirror::Class*>(copy);
+ copy_klass->SetDexCacheStrings(RelocatedAddressOfPointer(klass->GetDexCacheStrings()));
copy_klass->SetSFieldsPtrUnchecked(RelocatedAddressOfPointer(klass->GetSFieldsPtr()));
copy_klass->SetIFieldsPtrUnchecked(RelocatedAddressOfPointer(klass->GetIFieldsPtr()));
copy_klass->SetDirectMethodsPtrUnchecked(
@@ -673,8 +716,10 @@
// Just update the entry points if it looks like we should.
// TODO: sanity check all the pointers' values
copy->SetDeclaringClass(RelocatedAddressOfPointer(object->GetDeclaringClass()));
- copy->SetDexCacheResolvedMethods(RelocatedAddressOfPointer(object->GetDexCacheResolvedMethods()));
- copy->SetDexCacheResolvedTypes(RelocatedAddressOfPointer(object->GetDexCacheResolvedTypes()));
+ copy->SetDexCacheResolvedMethods(
+ RelocatedAddressOfPointer(object->GetDexCacheResolvedMethods(pointer_size)), pointer_size);
+ copy->SetDexCacheResolvedTypes(
+ RelocatedAddressOfPointer(object->GetDexCacheResolvedTypes(pointer_size)), pointer_size);
copy->SetEntryPointFromQuickCompiledCodePtrSize(RelocatedAddressOfPointer(
object->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)), pointer_size);
copy->SetEntryPointFromJniPtrSize(RelocatedAddressOfPointer(
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 8f70d30..995a1d5 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -99,7 +99,11 @@
jit/jit.cc \
jit/jit_code_cache.cc \
jit/jit_instrumentation.cc \
+ jit/profiling_info.cc \
+ lambda/art_lambda_method.cc \
lambda/box_table.cc \
+ lambda/closure.cc \
+ lambda/closure_builder.cc \
jni_internal.cc \
jobject_comparator.cc \
linear_alloc.cc \
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 1498a4b..861f802 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -926,13 +926,11 @@
int3
#endif
// Might need a special macro since rsi and edx is 32b/64b mismatched.
- movl ART_METHOD_DEX_CACHE_TYPES_OFFSET(%rsi), %edx // Load dex cache resolved types array
- UNPOISON_HEAP_REF edx
+ movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rsi), %rdx // Load dex cache resolved types array
// TODO: Add read barrier when this function is used.
// Might need to break down into multiple instructions to get the base address in a register.
// Load the class
- movl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdx, %rdi, MIRROR_OBJECT_ARRAY_COMPONENT_SIZE), %edx
- UNPOISON_HEAP_REF edx
+ movl 0(%rdx, %rdi, COMPRESSED_REFERENCE_SIZE), %edx
testl %edx, %edx // Check null class
jz .Lart_quick_alloc_object_tlab_slow_path
// Check class status.
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index 5138cc9..4166e22 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -24,7 +24,7 @@
#include "gc_root-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "jvalue.h"
-#include "mirror/dex_cache.h"
+#include "mirror/dex_cache-inl.h"
#include "mirror/object-inl.h"
#include "primitive.h"
#include "thread-inl.h"
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index d38cc56..a84c20a 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -22,11 +22,13 @@
#include "art_field.h"
#include "base/logging.h"
#include "class_linker-inl.h"
+#include "common_throws.h"
#include "dex_file.h"
#include "dex_file-inl.h"
#include "gc_root-inl.h"
+#include "jit/profiling_info.h"
#include "mirror/class-inl.h"
-#include "mirror/dex_cache.h"
+#include "mirror/dex_cache-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array.h"
#include "oat.h"
@@ -95,14 +97,20 @@
return dex_method_index_;
}
-inline mirror::PointerArray* ArtMethod::GetDexCacheResolvedMethods() {
- GcRootSource gc_root_source(this);
- return dex_cache_resolved_methods_.Read(&gc_root_source);
+inline ArtMethod** ArtMethod::GetDexCacheResolvedMethods(size_t pointer_size) {
+ return GetNativePointer<ArtMethod**>(DexCacheResolvedMethodsOffset(pointer_size),
+ pointer_size);
}
inline ArtMethod* ArtMethod::GetDexCacheResolvedMethod(uint16_t method_index, size_t ptr_size) {
- auto* method = GetDexCacheResolvedMethods()->GetElementPtrSize<ArtMethod*>(
- method_index, ptr_size);
+ // NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
+ // without accessing the DexCache and we don't want to do that in release build.
+ DCHECK_LT(method_index,
+ GetInterfaceMethodIfProxy(ptr_size)->GetDeclaringClass()
+ ->GetDexCache()->NumResolvedMethods());
+ ArtMethod* method = mirror::DexCache::GetElementPtrSize(GetDexCacheResolvedMethods(ptr_size),
+ method_index,
+ ptr_size);
if (LIKELY(method != nullptr)) {
auto* declaring_class = method->GetDeclaringClass();
if (LIKELY(declaring_class == nullptr || !declaring_class->IsErroneous())) {
@@ -112,52 +120,70 @@
return nullptr;
}
-inline void ArtMethod::SetDexCacheResolvedMethod(uint16_t method_idx, ArtMethod* new_method,
+inline void ArtMethod::SetDexCacheResolvedMethod(uint16_t method_index, ArtMethod* new_method,
size_t ptr_size) {
+ // NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
+ // without accessing the DexCache and we don't want to do that in release build.
+ DCHECK_LT(method_index,
+ GetInterfaceMethodIfProxy(ptr_size)->GetDeclaringClass()
+ ->GetDexCache()->NumResolvedMethods());
DCHECK(new_method == nullptr || new_method->GetDeclaringClass() != nullptr);
- GetDexCacheResolvedMethods()->SetElementPtrSize(method_idx, new_method, ptr_size);
+ mirror::DexCache::SetElementPtrSize(GetDexCacheResolvedMethods(ptr_size),
+ method_index,
+ new_method,
+ ptr_size);
}
-inline bool ArtMethod::HasDexCacheResolvedMethods() {
- return GetDexCacheResolvedMethods() != nullptr;
+inline bool ArtMethod::HasDexCacheResolvedMethods(size_t pointer_size) {
+ return GetDexCacheResolvedMethods(pointer_size) != nullptr;
}
-inline bool ArtMethod::HasSameDexCacheResolvedMethods(mirror::PointerArray* other_cache) {
- return GetDexCacheResolvedMethods() == other_cache;
+inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod** other_cache,
+ size_t pointer_size) {
+ return GetDexCacheResolvedMethods(pointer_size) == other_cache;
}
-inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod* other) {
- return GetDexCacheResolvedMethods() == other->GetDexCacheResolvedMethods();
+inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod* other, size_t pointer_size) {
+ return GetDexCacheResolvedMethods(pointer_size) ==
+ other->GetDexCacheResolvedMethods(pointer_size);
}
-inline mirror::ObjectArray<mirror::Class>* ArtMethod::GetDexCacheResolvedTypes() {
- GcRootSource gc_root_source(this);
- return dex_cache_resolved_types_.Read(&gc_root_source);
+inline GcRoot<mirror::Class>* ArtMethod::GetDexCacheResolvedTypes(size_t pointer_size) {
+ return GetNativePointer<GcRoot<mirror::Class>*>(DexCacheResolvedTypesOffset(pointer_size),
+ pointer_size);
}
template <bool kWithCheck>
-inline mirror::Class* ArtMethod::GetDexCacheResolvedType(uint32_t type_index) {
- mirror::Class* klass = kWithCheck ?
- GetDexCacheResolvedTypes()->Get(type_index) :
- GetDexCacheResolvedTypes()->GetWithoutChecks(type_index);
+inline mirror::Class* ArtMethod::GetDexCacheResolvedType(uint32_t type_index, size_t ptr_size) {
+ if (kWithCheck) {
+ mirror::DexCache* dex_cache =
+ GetInterfaceMethodIfProxy(ptr_size)->GetDeclaringClass()->GetDexCache();
+ if (UNLIKELY(type_index >= dex_cache->NumResolvedTypes())) {
+ ThrowArrayIndexOutOfBoundsException(type_index, dex_cache->NumResolvedTypes());
+ return nullptr;
+ }
+ }
+ mirror::Class* klass = GetDexCacheResolvedTypes(ptr_size)[type_index].Read();
return (klass != nullptr && !klass->IsErroneous()) ? klass : nullptr;
}
-inline bool ArtMethod::HasDexCacheResolvedTypes() {
- return GetDexCacheResolvedTypes() != nullptr;
+inline bool ArtMethod::HasDexCacheResolvedTypes(size_t pointer_size) {
+ return GetDexCacheResolvedTypes(pointer_size) != nullptr;
}
-inline bool ArtMethod::HasSameDexCacheResolvedTypes(
- mirror::ObjectArray<mirror::Class>* other_cache) {
- return GetDexCacheResolvedTypes() == other_cache;
+inline bool ArtMethod::HasSameDexCacheResolvedTypes(GcRoot<mirror::Class>* other_cache,
+ size_t pointer_size) {
+ return GetDexCacheResolvedTypes(pointer_size) == other_cache;
}
-inline bool ArtMethod::HasSameDexCacheResolvedTypes(ArtMethod* other) {
- return GetDexCacheResolvedTypes() == other->GetDexCacheResolvedTypes();
+inline bool ArtMethod::HasSameDexCacheResolvedTypes(ArtMethod* other, size_t pointer_size) {
+ return GetDexCacheResolvedTypes(pointer_size) == other->GetDexCacheResolvedTypes(pointer_size);
}
-inline mirror::Class* ArtMethod::GetClassFromTypeIndex(uint16_t type_idx, bool resolve) {
- mirror::Class* type = GetDexCacheResolvedType(type_idx);
+inline mirror::Class* ArtMethod::GetClassFromTypeIndex(uint16_t type_idx,
+ bool resolve,
+ size_t ptr_size) {
+ mirror::Class* type = GetDexCacheResolvedType(type_idx, ptr_size);
if (type == nullptr && resolve) {
type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this);
CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
@@ -391,9 +417,9 @@
return GetDeclaringClass()->GetDexFile().GetCodeItem(GetCodeItemOffset());
}
-inline bool ArtMethod::IsResolvedTypeIdx(uint16_t type_idx) {
+inline bool ArtMethod::IsResolvedTypeIdx(uint16_t type_idx, size_t ptr_size) {
DCHECK(!IsProxyMethod());
- return GetDexCacheResolvedType(type_idx) != nullptr;
+ return GetDexCacheResolvedType(type_idx, ptr_size) != nullptr;
}
inline int32_t ArtMethod::GetLineNumFromDexPC(uint32_t dex_pc) {
@@ -467,30 +493,33 @@
return this;
}
mirror::Class* klass = GetDeclaringClass();
- auto interface_method = GetDexCacheResolvedMethods()->GetElementPtrSize<ArtMethod*>(
- GetDexMethodIndex(), pointer_size);
+ ArtMethod* interface_method = mirror::DexCache::GetElementPtrSize(
+ GetDexCacheResolvedMethods(pointer_size),
+ GetDexMethodIndex(),
+ pointer_size);
DCHECK(interface_method != nullptr);
DCHECK_EQ(interface_method,
Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this));
return interface_method;
}
-inline void ArtMethod::SetDexCacheResolvedMethods(mirror::PointerArray* new_dex_cache_methods) {
- dex_cache_resolved_methods_ = GcRoot<mirror::PointerArray>(new_dex_cache_methods);
+inline void ArtMethod::SetDexCacheResolvedMethods(ArtMethod** new_dex_cache_methods,
+ size_t ptr_size) {
+ SetNativePointer(DexCacheResolvedMethodsOffset(ptr_size), new_dex_cache_methods, ptr_size);
}
-inline void ArtMethod::SetDexCacheResolvedTypes(
- mirror::ObjectArray<mirror::Class>* new_dex_cache_types) {
- dex_cache_resolved_types_ = GcRoot<mirror::ObjectArray<mirror::Class>>(new_dex_cache_types);
+inline void ArtMethod::SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types,
+ size_t ptr_size) {
+ SetNativePointer(DexCacheResolvedTypesOffset(ptr_size), new_dex_cache_types, ptr_size);
}
-inline mirror::Class* ArtMethod::GetReturnType(bool resolve) {
+inline mirror::Class* ArtMethod::GetReturnType(bool resolve, size_t ptr_size) {
DCHECK(!IsProxyMethod());
const DexFile* dex_file = GetDexFile();
const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex());
const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
uint16_t return_type_idx = proto_id.return_type_idx_;
- mirror::Class* type = GetDexCacheResolvedType(return_type_idx);
+ mirror::Class* type = GetDexCacheResolvedType(return_type_idx, ptr_size);
if (type == nullptr && resolve) {
type = Runtime::Current()->GetClassLinker()->ResolveType(return_type_idx, this);
CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
@@ -500,19 +529,33 @@
template<typename RootVisitorType>
void ArtMethod::VisitRoots(RootVisitorType& visitor) {
+ ArtMethod* interface_method = nullptr;
+ mirror::Class* klass = declaring_class_.Read();
+ if (UNLIKELY(klass != nullptr && klass->IsProxyClass())) {
+ // For normal methods, dex cache shortcuts will be visited through the declaring class.
+ // However, for proxies we need to keep the interface method alive, so we visit its roots.
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ interface_method = mirror::DexCache::GetElementPtrSize(
+ GetDexCacheResolvedMethods(pointer_size),
+ GetDexMethodIndex(),
+ pointer_size);
+ DCHECK(interface_method != nullptr);
+ DCHECK_EQ(interface_method,
+ Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this));
+ interface_method->VisitRoots(visitor);
+ }
+
visitor.VisitRootIfNonNull(declaring_class_.AddressWithoutBarrier());
- visitor.VisitRootIfNonNull(dex_cache_resolved_methods_.AddressWithoutBarrier());
- visitor.VisitRootIfNonNull(dex_cache_resolved_types_.AddressWithoutBarrier());
+ ProfilingInfo* profiling_info = GetProfilingInfo();
+ if (hotness_count_ != 0 && !IsNative() && profiling_info != nullptr) {
+ profiling_info->VisitRoots(visitor);
+ }
}
inline void ArtMethod::CopyFrom(const ArtMethod* src, size_t image_pointer_size) {
memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src),
Size(image_pointer_size));
declaring_class_ = GcRoot<mirror::Class>(const_cast<ArtMethod*>(src)->GetDeclaringClass());
- dex_cache_resolved_methods_ = GcRoot<mirror::PointerArray>(
- const_cast<ArtMethod*>(src)->GetDexCacheResolvedMethods());
- dex_cache_resolved_types_ = GcRoot<mirror::ObjectArray<mirror::Class>>(
- const_cast<ArtMethod*>(src)->GetDexCacheResolvedTypes());
}
} // namespace art
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index e46402d..5dbea52 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -30,6 +30,7 @@
#include "interpreter/interpreter.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
+#include "jit/profiling_info.h"
#include "jni_internal.h"
#include "mapping_table.h"
#include "mirror/abstract_method.h"
@@ -125,8 +126,9 @@
} else {
// Method didn't override superclass method so search interfaces
if (IsProxyMethod()) {
- result = GetDexCacheResolvedMethods()->GetElementPtrSize<ArtMethod*>(
- GetDexMethodIndex(), pointer_size);
+ result = mirror::DexCache::GetElementPtrSize(GetDexCacheResolvedMethods(pointer_size),
+ GetDexMethodIndex(),
+ pointer_size);
CHECK_EQ(result,
Runtime::Current()->GetClassLinker()->FindMethodForProxy(GetDeclaringClass(), this));
} else {
@@ -261,6 +263,7 @@
// Default to handler not found.
uint32_t found_dex_pc = DexFile::kDexNoIndex;
// Iterate over the catch handlers associated with dex_pc.
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) {
uint16_t iter_type_idx = it.GetHandlerTypeIndex();
// Catch all case
@@ -269,7 +272,9 @@
break;
}
// Does this catch exception type apply?
- mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, true);
+ mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx,
+ true /* resolve */,
+ pointer_size);
if (UNLIKELY(iter_exception_type == nullptr)) {
// Now have a NoClassDefFoundError as exception. Ignore in case the exception class was
// removed by a pro-guard like tool.
@@ -575,4 +580,16 @@
return oat_method.GetVmapTable();
}
+ProfilingInfo* ArtMethod::CreateProfilingInfo() {
+ ProfilingInfo* info = ProfilingInfo::Create(this);
+ MemberOffset offset = ArtMethod::EntryPointFromJniOffset(sizeof(void*));
+ uintptr_t pointer = reinterpret_cast<uintptr_t>(this) + offset.Uint32Value();
+ if (!reinterpret_cast<Atomic<ProfilingInfo*>*>(pointer)->
+ CompareExchangeStrongSequentiallyConsistent(nullptr, info)) {
+ return GetProfilingInfo();
+ } else {
+ return info;
+ }
+}
+
} // namespace art
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 6cdc4a6..3f2161f 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_ART_METHOD_H_
#define ART_RUNTIME_ART_METHOD_H_
+#include "base/casts.h"
#include "dex_file.h"
#include "gc_root.h"
#include "invoke_type.h"
@@ -32,6 +33,7 @@
namespace art {
union JValue;
+class ProfilingInfo;
class ScopedObjectAccessAlreadyRunnable;
class StringPiece;
class ShadowFrame;
@@ -212,41 +214,35 @@
dex_method_index_ = new_idx;
}
- static MemberOffset DexCacheResolvedMethodsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_);
- }
-
- static MemberOffset DexCacheResolvedTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_);
- }
-
- ALWAYS_INLINE mirror::PointerArray* GetDexCacheResolvedMethods()
+ ALWAYS_INLINE ArtMethod** GetDexCacheResolvedMethods(size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- ALWAYS_INLINE ArtMethod* GetDexCacheResolvedMethod(uint16_t method_idx, size_t ptr_size)
+ ALWAYS_INLINE ArtMethod* GetDexCacheResolvedMethod(uint16_t method_index, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- ALWAYS_INLINE void SetDexCacheResolvedMethod(uint16_t method_idx, ArtMethod* new_method,
+ ALWAYS_INLINE void SetDexCacheResolvedMethod(uint16_t method_index,
+ ArtMethod* new_method,
size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- ALWAYS_INLINE void SetDexCacheResolvedMethods(mirror::PointerArray* new_dex_cache_methods)
+ ALWAYS_INLINE void SetDexCacheResolvedMethods(ArtMethod** new_dex_cache_methods, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool HasDexCacheResolvedMethods() SHARED_REQUIRES(Locks::mutator_lock_);
- bool HasSameDexCacheResolvedMethods(ArtMethod* other)
+ bool HasDexCacheResolvedMethods(size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_);
+ bool HasSameDexCacheResolvedMethods(ArtMethod* other, size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool HasSameDexCacheResolvedMethods(mirror::PointerArray* other_cache)
+ bool HasSameDexCacheResolvedMethods(ArtMethod** other_cache, size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
template <bool kWithCheck = true>
- mirror::Class* GetDexCacheResolvedType(uint32_t type_idx)
+ mirror::Class* GetDexCacheResolvedType(uint32_t type_idx, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- void SetDexCacheResolvedTypes(mirror::ObjectArray<mirror::Class>* new_dex_cache_types)
+ void SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool HasDexCacheResolvedTypes() SHARED_REQUIRES(Locks::mutator_lock_);
- bool HasSameDexCacheResolvedTypes(ArtMethod* other) SHARED_REQUIRES(Locks::mutator_lock_);
- bool HasSameDexCacheResolvedTypes(mirror::ObjectArray<mirror::Class>* other_cache)
+ bool HasDexCacheResolvedTypes(size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_);
+ bool HasSameDexCacheResolvedTypes(ArtMethod* other, size_t pointer_size)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ bool HasSameDexCacheResolvedTypes(GcRoot<mirror::Class>* other_cache, size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
// Get the Class* from the type index into this method's dex cache.
- mirror::Class* GetClassFromTypeIndex(uint16_t type_idx, bool resolve)
+ mirror::Class* GetClassFromTypeIndex(uint16_t type_idx, bool resolve, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
// Find the method that this method overrides.
@@ -267,7 +263,7 @@
return GetEntryPointFromQuickCompiledCodePtrSize(sizeof(void*));
}
ALWAYS_INLINE const void* GetEntryPointFromQuickCompiledCodePtrSize(size_t pointer_size) {
- return GetEntryPoint<const void*>(
+ return GetNativePointer<const void*>(
EntryPointFromQuickCompiledCodeOffset(pointer_size), pointer_size);
}
@@ -277,8 +273,8 @@
}
ALWAYS_INLINE void SetEntryPointFromQuickCompiledCodePtrSize(
const void* entry_point_from_quick_compiled_code, size_t pointer_size) {
- SetEntryPoint(EntryPointFromQuickCompiledCodeOffset(pointer_size),
- entry_point_from_quick_compiled_code, pointer_size);
+ SetNativePointer(EntryPointFromQuickCompiledCodeOffset(pointer_size),
+ entry_point_from_quick_compiled_code, pointer_size);
}
uint32_t GetCodeSize() SHARED_REQUIRES(Locks::mutator_lock_);
@@ -374,6 +370,16 @@
void UnregisterNative() SHARED_REQUIRES(Locks::mutator_lock_);
+ static MemberOffset DexCacheResolvedMethodsOffset(size_t pointer_size) {
+ return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
+ PtrSizedFields, dex_cache_resolved_methods_) / sizeof(void*) * pointer_size);
+ }
+
+ static MemberOffset DexCacheResolvedTypesOffset(size_t pointer_size) {
+ return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
+ PtrSizedFields, dex_cache_resolved_types_) / sizeof(void*) * pointer_size);
+ }
+
static MemberOffset EntryPointFromJniOffset(size_t pointer_size) {
return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
PtrSizedFields, entry_point_from_jni_) / sizeof(void*) * pointer_size);
@@ -384,18 +390,27 @@
PtrSizedFields, entry_point_from_quick_compiled_code_) / sizeof(void*) * pointer_size);
}
+ ProfilingInfo* CreateProfilingInfo() SHARED_REQUIRES(Locks::mutator_lock_);
+
+ ProfilingInfo* GetProfilingInfo() {
+ return reinterpret_cast<ProfilingInfo*>(GetEntryPointFromJni());
+ }
+
void* GetEntryPointFromJni() {
return GetEntryPointFromJniPtrSize(sizeof(void*));
}
+
ALWAYS_INLINE void* GetEntryPointFromJniPtrSize(size_t pointer_size) {
- return GetEntryPoint<void*>(EntryPointFromJniOffset(pointer_size), pointer_size);
+ return GetNativePointer<void*>(EntryPointFromJniOffset(pointer_size), pointer_size);
}
void SetEntryPointFromJni(const void* entrypoint) SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsNative());
SetEntryPointFromJniPtrSize(entrypoint, sizeof(void*));
}
+
ALWAYS_INLINE void SetEntryPointFromJniPtrSize(const void* entrypoint, size_t pointer_size) {
- SetEntryPoint(EntryPointFromJniOffset(pointer_size), entrypoint, pointer_size);
+ SetNativePointer(EntryPointFromJniOffset(pointer_size), entrypoint, pointer_size);
}
// Is this a CalleSaveMethod or ResolutionMethod and therefore doesn't adhere to normal
@@ -464,7 +479,7 @@
const DexFile::CodeItem* GetCodeItem() SHARED_REQUIRES(Locks::mutator_lock_);
- bool IsResolvedTypeIdx(uint16_t type_idx) SHARED_REQUIRES(Locks::mutator_lock_);
+ bool IsResolvedTypeIdx(uint16_t type_idx, size_t ptr_size) SHARED_REQUIRES(Locks::mutator_lock_);
int32_t GetLineNumFromDexPC(uint32_t dex_pc) SHARED_REQUIRES(Locks::mutator_lock_);
@@ -485,7 +500,8 @@
// May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large
// number of bugs at call sites.
- mirror::Class* GetReturnType(bool resolve = true) SHARED_REQUIRES(Locks::mutator_lock_);
+ mirror::Class* GetReturnType(bool resolve, size_t ptr_size)
+ SHARED_REQUIRES(Locks::mutator_lock_);
mirror::ClassLoader* GetClassLoader() SHARED_REQUIRES(Locks::mutator_lock_);
@@ -514,20 +530,18 @@
void CopyFrom(const ArtMethod* src, size_t image_pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- ALWAYS_INLINE mirror::ObjectArray<mirror::Class>* GetDexCacheResolvedTypes()
+ ALWAYS_INLINE GcRoot<mirror::Class>* GetDexCacheResolvedTypes(size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
+ uint16_t IncrementCounter() {
+ return ++hotness_count_;
+ }
+
protected:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
// The class we are a part of.
GcRoot<mirror::Class> declaring_class_;
- // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
- GcRoot<mirror::PointerArray> dex_cache_resolved_methods_;
-
- // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
- GcRoot<mirror::ObjectArray<mirror::Class>> dex_cache_resolved_types_;
-
// Access flags; low 16 bits are defined by spec.
uint32_t access_flags_;
@@ -544,7 +558,11 @@
// Entry within a dispatch table for this method. For static/direct methods the index is into
// the declaringClass.directMethods, for virtual methods the vtable and for interface methods the
// ifTable.
- uint32_t method_index_;
+ uint16_t method_index_;
+
+ // The hotness we measure for this method. Incremented by the interpreter. Not atomic, as we allow
+ // missing increments: if the method is hot, we will see it eventually.
+ uint16_t hotness_count_;
// Fake padding field gets inserted here.
@@ -552,7 +570,14 @@
// PACKED(4) is necessary for the correctness of
// RoundUp(OFFSETOF_MEMBER(ArtMethod, ptr_sized_fields_), pointer_size).
struct PACKED(4) PtrSizedFields {
- // Pointer to JNI function registered to this method, or a function to resolve the JNI function.
+ // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
+ ArtMethod** dex_cache_resolved_methods_;
+
+ // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
+ GcRoot<mirror::Class>* dex_cache_resolved_types_;
+
+ // Pointer to JNI function registered to this method, or a function to resolve the JNI function,
+ // or the profiling data for non-native methods.
void* entry_point_from_jni_;
// Method dispatch from quick compiled code invokes this pointer which may cause bridging into
@@ -567,26 +592,26 @@
}
template<typename T>
- ALWAYS_INLINE T GetEntryPoint(MemberOffset offset, size_t pointer_size) const {
+ ALWAYS_INLINE T GetNativePointer(MemberOffset offset, size_t pointer_size) const {
+ static_assert(std::is_pointer<T>::value, "T must be a pointer type");
DCHECK(ValidPointerSize(pointer_size)) << pointer_size;
const auto addr = reinterpret_cast<uintptr_t>(this) + offset.Uint32Value();
if (pointer_size == sizeof(uint32_t)) {
return reinterpret_cast<T>(*reinterpret_cast<const uint32_t*>(addr));
} else {
auto v = *reinterpret_cast<const uint64_t*>(addr);
- DCHECK_EQ(reinterpret_cast<uint64_t>(reinterpret_cast<T>(v)), v) << "Conversion lost bits";
- return reinterpret_cast<T>(v);
+ return reinterpret_cast<T>(dchecked_integral_cast<uintptr_t>(v));
}
}
template<typename T>
- ALWAYS_INLINE void SetEntryPoint(MemberOffset offset, T new_value, size_t pointer_size) {
+ ALWAYS_INLINE void SetNativePointer(MemberOffset offset, T new_value, size_t pointer_size) {
+ static_assert(std::is_pointer<T>::value, "T must be a pointer type");
DCHECK(ValidPointerSize(pointer_size)) << pointer_size;
const auto addr = reinterpret_cast<uintptr_t>(this) + offset.Uint32Value();
if (pointer_size == sizeof(uint32_t)) {
uintptr_t ptr = reinterpret_cast<uintptr_t>(new_value);
- DCHECK_EQ(static_cast<uint32_t>(ptr), ptr) << "Conversion lost bits";
- *reinterpret_cast<uint32_t*>(addr) = static_cast<uint32_t>(ptr);
+ *reinterpret_cast<uint32_t*>(addr) = dchecked_integral_cast<uint32_t>(ptr);
} else {
*reinterpret_cast<uint64_t*>(addr) = reinterpret_cast<uintptr_t>(new_value);
}
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 5c1922e..04ff120 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -138,13 +138,13 @@
#define MIRROR_CLASS_COMPONENT_TYPE_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_COMPONENT_TYPE_OFFSET,
art::mirror::Class::ComponentTypeOffset().Int32Value())
-#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (36 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (72 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET,
art::mirror::Class::AccessFlagsOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (100 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (104 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_OFFSET,
art::mirror::Class::ObjectSizeOffset().Int32Value())
-#define MIRROR_CLASS_STATUS_OFFSET (112 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_STATUS_OFFSET (116 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_STATUS_OFFSET,
art::mirror::Class::StatusOffset().Int32Value())
@@ -184,19 +184,27 @@
ADD_TEST_EQ(MIRROR_STRING_VALUE_OFFSET, art::mirror::String::ValueOffset().Int32Value())
// Offsets within java.lang.reflect.ArtMethod.
-#define ART_METHOD_DEX_CACHE_METHODS_OFFSET 4
-ADD_TEST_EQ(ART_METHOD_DEX_CACHE_METHODS_OFFSET,
- art::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())
+#define ART_METHOD_DEX_CACHE_METHODS_OFFSET_32 20
+ADD_TEST_EQ(ART_METHOD_DEX_CACHE_METHODS_OFFSET_32,
+ art::ArtMethod::DexCacheResolvedMethodsOffset(4).Int32Value())
-#define ART_METHOD_DEX_CACHE_TYPES_OFFSET 8
-ADD_TEST_EQ(ART_METHOD_DEX_CACHE_TYPES_OFFSET,
- art::ArtMethod::DexCacheResolvedTypesOffset().Int32Value())
+#define ART_METHOD_DEX_CACHE_METHODS_OFFSET_64 24
+ADD_TEST_EQ(ART_METHOD_DEX_CACHE_METHODS_OFFSET_64,
+ art::ArtMethod::DexCacheResolvedMethodsOffset(8).Int32Value())
+
+#define ART_METHOD_DEX_CACHE_TYPES_OFFSET_32 24
+ADD_TEST_EQ(ART_METHOD_DEX_CACHE_TYPES_OFFSET_32,
+ art::ArtMethod::DexCacheResolvedTypesOffset(4).Int32Value())
+
+#define ART_METHOD_DEX_CACHE_TYPES_OFFSET_64 32
+ADD_TEST_EQ(ART_METHOD_DEX_CACHE_TYPES_OFFSET_64,
+ art::ArtMethod::DexCacheResolvedTypesOffset(8).Int32Value())
#define ART_METHOD_QUICK_CODE_OFFSET_32 32
ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_32,
art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value())
-#define ART_METHOD_QUICK_CODE_OFFSET_64 40
+#define ART_METHOD_QUICK_CODE_OFFSET_64 48
ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_64,
art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(8).Int32Value())
diff --git a/runtime/base/variant_map.h b/runtime/base/variant_map.h
index 1d7596a..82e5d2e 100644
--- a/runtime/base/variant_map.h
+++ b/runtime/base/variant_map.h
@@ -257,8 +257,7 @@
if (ptr != nullptr) {
return std::move(*ptr);
} else {
- TValue default_value = key.CreateDefaultValue();
- return std::move(default_value);
+ return key.CreateDefaultValue();
}
}
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index d2dbff6..88a3996 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -60,10 +60,11 @@
return array_class;
}
-inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx,
- ArtMethod* referrer) {
+inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx, ArtMethod* referrer) {
mirror::Class* declaring_class = referrer->GetDeclaringClass();
- mirror::String* resolved_string = declaring_class->GetDexCacheStrings()->Get(string_idx);
+ // MethodVerifier refuses methods with string_idx out of bounds.
+ DCHECK_LT(string_idx, declaring_class->GetDexCache()->NumStrings());
+ mirror::String* resolved_string = declaring_class->GetDexCacheStrings()[string_idx].Read();
if (UNLIKELY(resolved_string == nullptr)) {
StackHandleScope<1> hs(Thread::Current());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
@@ -76,9 +77,8 @@
return resolved_string;
}
-inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx,
- ArtMethod* referrer) {
- mirror::Class* resolved_type = referrer->GetDexCacheResolvedType(type_idx);
+inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, ArtMethod* referrer) {
+ mirror::Class* resolved_type = referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_);
if (UNLIKELY(resolved_type == nullptr)) {
mirror::Class* declaring_class = referrer->GetDeclaringClass();
StackHandleScope<2> hs(Thread::Current());
@@ -109,16 +109,17 @@
}
inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer) {
- ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(
- method_idx, image_pointer_size_);
+ ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx, image_pointer_size_);
if (resolved_method == nullptr || resolved_method->IsRuntimeMethod()) {
return nullptr;
}
return resolved_method;
}
-inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, uint32_t method_idx,
- ArtMethod* referrer, InvokeType type) {
+inline ArtMethod* ClassLinker::ResolveMethod(Thread* self,
+ uint32_t method_idx,
+ ArtMethod* referrer,
+ InvokeType type) {
ArtMethod* resolved_method = GetResolvedMethod(method_idx, referrer);
if (UNLIKELY(resolved_method == nullptr)) {
mirror::Class* declaring_class = referrer->GetDeclaringClass();
@@ -126,7 +127,11 @@
Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
const DexFile* dex_file = h_dex_cache->GetDexFile();
- resolved_method = ResolveMethod(*dex_file, method_idx, h_dex_cache, h_class_loader, referrer,
+ resolved_method = ResolveMethod(*dex_file,
+ method_idx,
+ h_dex_cache,
+ h_class_loader,
+ referrer,
type);
}
// Note: We cannot check here to see whether we added the method to the cache. It
@@ -160,7 +165,8 @@
}
inline mirror::Object* ClassLinker::AllocObject(Thread* self) {
- return GetClassRoot(kJavaLangObject)->Alloc<true, false>(self,
+ return GetClassRoot(kJavaLangObject)->Alloc<true, false>(
+ self,
Runtime::Current()->GetHeap()->GetCurrentAllocator());
}
@@ -176,13 +182,15 @@
inline mirror::ObjectArray<mirror::String>* ClassLinker::AllocStringArray(Thread* self,
size_t length) {
- return mirror::ObjectArray<mirror::String>::Alloc(self, GetClassRoot(kJavaLangStringArrayClass),
+ return mirror::ObjectArray<mirror::String>::Alloc(self,
+ GetClassRoot(kJavaLangStringArrayClass),
length);
}
inline mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) {
return down_cast<mirror::IfTable*>(
- mirror::IfTable::Alloc(self, GetClassRoot(kObjectArrayClass),
+ mirror::IfTable::Alloc(self,
+ GetClassRoot(kObjectArrayClass),
ifcount * mirror::IfTable::kMax));
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 287aca9..73da2cb 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -80,6 +80,7 @@
#include "handle_scope-inl.h"
#include "thread-inl.h"
#include "utils.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
#include "verifier/method_verifier.h"
#include "well_known_classes.h"
@@ -420,6 +421,7 @@
Handle<mirror::Class> java_lang_DexCache(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::DexCache::ClassSize(image_pointer_size_))));
SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get());
+ java_lang_DexCache->SetDexCacheClass();
java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize());
mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusResolved, self);
@@ -783,7 +785,8 @@
// be from multidex, which resolves correctly).
};
-static void AddDexFilesFromOat(const OatFile* oat_file, bool already_loaded,
+static void AddDexFilesFromOat(const OatFile* oat_file,
+ bool already_loaded,
std::priority_queue<DexFileAndClassPair>* heap) {
const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles();
for (const OatDexFile* oat_dex_file : oat_dex_files) {
@@ -1025,7 +1028,8 @@
return nullptr;
}
-static void SanityCheckArtMethod(ArtMethod* m, mirror::Class* expected_class,
+static void SanityCheckArtMethod(ArtMethod* m,
+ mirror::Class* expected_class,
gc::space::ImageSpace* space)
SHARED_REQUIRES(Locks::mutator_lock_) {
if (m->IsRuntimeMethod()) {
@@ -1043,9 +1047,11 @@
}
}
-static void SanityCheckArtMethodPointerArray(
- mirror::PointerArray* arr, mirror::Class* expected_class, size_t pointer_size,
- gc::space::ImageSpace* space) SHARED_REQUIRES(Locks::mutator_lock_) {
+static void SanityCheckArtMethodPointerArray(mirror::PointerArray* arr,
+ mirror::Class* expected_class,
+ size_t pointer_size,
+ gc::space::ImageSpace* space)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
CHECK(arr != nullptr);
for (int32_t j = 0; j < arr->GetLength(); ++j) {
auto* method = arr->GetElementPtrSize<ArtMethod*>(j, pointer_size);
@@ -1059,6 +1065,26 @@
}
}
+static void SanityCheckArtMethodPointerArray(
+ ArtMethod** arr,
+ size_t size,
+ size_t pointer_size,
+ gc::space::ImageSpace* space) SHARED_REQUIRES(Locks::mutator_lock_) {
+ CHECK_EQ(arr != nullptr, size != 0u);
+ if (arr != nullptr) {
+ auto offset = reinterpret_cast<uint8_t*>(arr) - space->Begin();
+ CHECK(space->GetImageHeader().GetImageSection(
+ ImageHeader::kSectionDexCacheArrays).Contains(offset));
+ }
+ for (size_t j = 0; j < size; ++j) {
+ ArtMethod* method = mirror::DexCache::GetElementPtrSize(arr, j, pointer_size);
+ // expected_class == null means we are a dex cache.
+ if (method != nullptr) {
+ SanityCheckArtMethod(method, nullptr, space);
+ }
+ }
+}
+
static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_UNUSED)
SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(obj != nullptr);
@@ -1188,8 +1214,10 @@
}
if (kSanityCheckObjects) {
- SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(), nullptr,
- image_pointer_size_, space);
+ SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(),
+ dex_cache->NumResolvedMethods(),
+ image_pointer_size_,
+ space);
}
CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
@@ -1267,7 +1295,8 @@
}
void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) {
- WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ Thread* const self = Thread::Current();
+ WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
visitor, RootInfo(kRootStickyClass));
if ((flags & kVisitRootFlagAllRoots) != 0) {
@@ -1287,9 +1316,13 @@
// Need to make sure to not copy ArtMethods without doing read barriers since the roots are
// marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy.
boot_class_table_.VisitRoots(buffered_visitor);
- for (GcRoot<mirror::ClassLoader>& root : class_loaders_) {
- // May be null for boot ClassLoader.
- root.VisitRoot(visitor, RootInfo(kRootVMInternal));
+ // TODO: Avoid marking these to enable class unloading.
+ JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
+ for (jweak weak_root : class_loaders_) {
+ mirror::Object* class_loader =
+ down_cast<mirror::ClassLoader*>(vm->DecodeWeakGlobal(self, weak_root));
+ // Don't need to update anything since the class loaders will be updated by SweepSystemWeaks.
+ visitor->VisitRootIfNonNull(&class_loader, RootInfo(kRootVMInternal));
}
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_class_roots_) {
@@ -1325,14 +1358,31 @@
}
}
+class VisitClassLoaderClassesVisitor : public ClassLoaderVisitor {
+ public:
+ explicit VisitClassLoaderClassesVisitor(ClassVisitor* visitor)
+ : visitor_(visitor),
+ done_(false) {}
+
+ void Visit(mirror::ClassLoader* class_loader)
+ SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
+ ClassTable* const class_table = class_loader->GetClassTable();
+ if (!done_ && class_table != nullptr && !class_table->Visit(visitor_)) {
+ // If the visitor ClassTable returns false it means that we don't need to continue.
+ done_ = true;
+ }
+ }
+
+ private:
+ ClassVisitor* const visitor_;
+ // If done is true then we don't need to do any more visiting.
+ bool done_;
+};
+
void ClassLinker::VisitClassesInternal(ClassVisitor* visitor) {
if (boot_class_table_.Visit(visitor)) {
- for (GcRoot<mirror::ClassLoader>& root : class_loaders_) {
- ClassTable* const class_table = root.Read()->GetClassTable();
- if (class_table != nullptr && !class_table->Visit(visitor)) {
- return;
- }
- }
+ VisitClassLoaderClassesVisitor loader_visitor(visitor);
+ VisitClassLoaders(&loader_visitor);
}
}
@@ -1451,10 +1501,17 @@
mirror::LongArray::ResetArrayClass();
mirror::ShortArray::ResetArrayClass();
STLDeleteElements(&oat_files_);
- for (GcRoot<mirror::ClassLoader>& root : class_loaders_) {
- ClassTable* const class_table = root.Read()->GetClassTable();
- delete class_table;
+ Thread* const self = Thread::Current();
+ JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
+ for (jweak weak_root : class_loaders_) {
+ auto* const class_loader = down_cast<mirror::ClassLoader*>(
+ vm->DecodeWeakGlobal(self, weak_root));
+ if (class_loader != nullptr) {
+ delete class_loader->GetClassTable();
+ }
+ vm->DeleteWeakGlobalRef(self, weak_root);
}
+ class_loaders_.clear();
}
mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) {
@@ -1476,28 +1533,44 @@
self->AssertPendingOOMException();
return nullptr;
}
- auto strings(hs.NewHandle(AllocStringArray(self, dex_file.NumStringIds())));
- if (strings.Get() == nullptr) {
- self->AssertPendingOOMException();
- return nullptr;
+ DexCacheArraysLayout layout(image_pointer_size_, &dex_file);
+ uint8_t* raw_arrays = nullptr;
+ if (dex_file.NumStringIds() != 0u || dex_file.NumTypeIds() != 0u ||
+ dex_file.NumMethodIds() != 0u || dex_file.NumFieldIds() != 0u) {
+ // NOTE: We "leak" the raw_arrays because we never destroy the dex cache.
+ DCHECK(image_pointer_size_ == 4u || image_pointer_size_ == 8u);
+ if (sizeof(void*) == 8u && image_pointer_size_ == 4u) {
+ // When cross-compiling for a 32-bit target on a 64-bit host, we need these arrays
+ // in the low 4GiB address space so that we can store pointers in 32-bit fields.
+ // This is conveniently provided by the linear allocator.
+ raw_arrays = reinterpret_cast<uint8_t*>(
+ Runtime::Current()->GetLinearAlloc()->Alloc(self, layout.Size())); // Zero-initialized.
+ } else {
+ raw_arrays = reinterpret_cast<uint8_t*>(calloc(layout.Size(), 1u)); // Zero-initialized.
+ if (raw_arrays == nullptr) {
+ return nullptr;
+ }
+ }
}
- auto types(hs.NewHandle(AllocClassArray(self, dex_file.NumTypeIds())));
- if (types.Get() == nullptr) {
- self->AssertPendingOOMException();
- return nullptr;
- }
- auto methods(hs.NewHandle(AllocPointerArray(self, dex_file.NumMethodIds())));
- if (methods.Get() == nullptr) {
- self->AssertPendingOOMException();
- return nullptr;
- }
- auto fields(hs.NewHandle(AllocPointerArray(self, dex_file.NumFieldIds())));
- if (fields.Get() == nullptr) {
- self->AssertPendingOOMException();
- return nullptr;
- }
- dex_cache->Init(&dex_file, location.Get(), strings.Get(), types.Get(), methods.Get(),
- fields.Get(), image_pointer_size_);
+ GcRoot<mirror::String>* strings = (dex_file.NumStringIds() == 0u) ? nullptr :
+ reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset());
+ GcRoot<mirror::Class>* types = (dex_file.NumTypeIds() == 0u) ? nullptr :
+ reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
+ ArtMethod** methods = (dex_file.NumMethodIds() == 0u) ? nullptr :
+ reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset());
+ ArtField** fields = (dex_file.NumFieldIds() == 0u) ? nullptr :
+ reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset());
+ dex_cache->Init(&dex_file,
+ location.Get(),
+ strings,
+ dex_file.NumStringIds(),
+ types,
+ dex_file.NumTypeIds(),
+ methods,
+ dex_file.NumMethodIds(),
+ fields,
+ dex_file.NumFieldIds(),
+ image_pointer_size_);
return dex_cache.Get();
}
@@ -1521,12 +1594,14 @@
}
mirror::ObjectArray<mirror::StackTraceElement>* ClassLinker::AllocStackTraceElementArray(
- Thread* self, size_t length) {
+ Thread* self,
+ size_t length) {
return mirror::ObjectArray<mirror::StackTraceElement>::Alloc(
self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length);
}
-mirror::Class* ClassLinker::EnsureResolved(Thread* self, const char* descriptor,
+mirror::Class* ClassLinker::EnsureResolved(Thread* self,
+ const char* descriptor,
mirror::Class* klass) {
DCHECK(klass != nullptr);
@@ -1604,7 +1679,8 @@
}
bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self, const char* descriptor,
+ Thread* self,
+ const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
mirror::Class** result) {
@@ -1714,7 +1790,8 @@
return true;
}
-mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
+mirror::Class* ClassLinker::FindClass(Thread* self,
+ const char* descriptor,
Handle<mirror::ClassLoader> class_loader) {
DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
DCHECK(self != nullptr);
@@ -1803,7 +1880,9 @@
UNREACHABLE();
}
-mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,
+mirror::Class* ClassLinker::DefineClass(Thread* self,
+ const char* descriptor,
+ size_t hash,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def) {
@@ -1972,11 +2051,18 @@
}
}
}
- return mirror::Class::ComputeClassSize(false, 0, num_8, num_16, num_32, num_64, num_ref,
+ return mirror::Class::ComputeClassSize(false,
+ 0,
+ num_8,
+ num_16,
+ num_32,
+ num_64,
+ num_ref,
image_pointer_size_);
}
-OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx,
+OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file,
+ uint16_t class_def_idx,
bool* found) {
DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16);
const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
@@ -1988,7 +2074,8 @@
return oat_dex_file->GetOatClass(class_def_idx);
}
-static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx,
+static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file,
+ uint16_t class_def_idx,
uint32_t method_idx) {
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx);
const uint8_t* class_data = dex_file.GetClassData(class_def);
@@ -2108,7 +2195,8 @@
return nullptr;
}
-const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
+const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file,
+ uint16_t class_def_idx,
uint32_t method_idx) {
bool found;
OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found);
@@ -2161,7 +2249,8 @@
it.Next();
}
bool has_oat_class;
- OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(),
+ OatFile::OatClass oat_class = FindOatClass(dex_file,
+ klass->GetDexClassDefIndex(),
&has_oat_class);
// Link the code of methods skipped by LinkCode.
for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) {
@@ -2242,8 +2331,10 @@
}
}
-void ClassLinker::SetupClass(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
- Handle<mirror::Class> klass, mirror::ClassLoader* class_loader) {
+void ClassLinker::SetupClass(const DexFile& dex_file,
+ const DexFile::ClassDef& dex_class_def,
+ Handle<mirror::Class> klass,
+ mirror::ClassLoader* class_loader) {
CHECK(klass.Get() != nullptr);
CHECK(klass->GetDexCache() != nullptr);
CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus());
@@ -2263,7 +2354,8 @@
CHECK(klass->GetDexCacheStrings() != nullptr);
}
-void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file,
+void ClassLinker::LoadClass(Thread* self,
+ const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def,
Handle<mirror::Class> klass) {
const uint8_t* class_data = dex_file.GetClassData(dex_class_def);
@@ -2314,7 +2406,8 @@
return ret;
}
-void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file,
+void ClassLinker::LoadClassMembers(Thread* self,
+ const DexFile& dex_file,
const uint8_t* class_data,
Handle<mirror::Class> klass,
const OatFile::OatClass* oat_class) {
@@ -2399,7 +2492,8 @@
self->AllowThreadSuspension();
}
-void ClassLinker::LoadField(const ClassDataItemIterator& it, Handle<mirror::Class> klass,
+void ClassLinker::LoadField(const ClassDataItemIterator& it,
+ Handle<mirror::Class> klass,
ArtField* dst) {
const uint32_t field_idx = it.GetMemberIndex();
dst->SetDexFieldIndex(field_idx);
@@ -2407,8 +2501,11 @@
dst->SetAccessFlags(it.GetFieldAccessFlags());
}
-void ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& it,
- Handle<mirror::Class> klass, ArtMethod* dst) {
+void ClassLinker::LoadMethod(Thread* self,
+ const DexFile& dex_file,
+ const ClassDataItemIterator& it,
+ Handle<mirror::Class> klass,
+ ArtMethod* dst) {
uint32_t dex_method_idx = it.GetMemberIndex();
const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);
@@ -2418,8 +2515,8 @@
dst->SetDeclaringClass(klass.Get());
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
- dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods());
- dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());
+ dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods(), image_pointer_size_);
+ dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes(), image_pointer_size_);
uint32_t access_flags = it.GetMethodAccessFlags();
@@ -2543,8 +2640,7 @@
bool allow_failure) {
// Search assuming unique-ness of dex file.
JavaVMExt* const vm = self->GetJniEnv()->vm;
- for (jobject weak_root : dex_caches_) {
- DCHECK_EQ(GetIndirectRefKind(weak_root), kWeakGlobal);
+ for (jweak weak_root : dex_caches_) {
mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(
vm->DecodeWeakGlobal(self, weak_root));
if (dex_cache != nullptr && dex_cache->GetDexFile() == &dex_file) {
@@ -2841,7 +2937,9 @@
return class_table != nullptr && class_table->Remove(descriptor);
}
-mirror::Class* ClassLinker::LookupClass(Thread* self, const char* descriptor, size_t hash,
+mirror::Class* ClassLinker::LookupClass(Thread* self,
+ const char* descriptor,
+ size_t hash,
mirror::ClassLoader* class_loader) {
{
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
@@ -2892,9 +2990,9 @@
ClassTable* const class_table = InsertClassTableForClassLoader(nullptr);
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
mirror::DexCache* dex_cache = dex_caches->Get(i);
- mirror::ObjectArray<mirror::Class>* types = dex_cache->GetResolvedTypes();
- for (int32_t j = 0; j < types->GetLength(); j++) {
- mirror::Class* klass = types->Get(j);
+ GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
+ for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
+ mirror::Class* klass = types[j].Read();
if (klass != nullptr) {
DCHECK(klass->GetClassLoader() == nullptr);
const char* descriptor = klass->GetDescriptor(&temp);
@@ -2915,15 +3013,25 @@
dex_cache_image_class_lookup_required_ = false;
}
-void ClassLinker::MoveClassTableToPreZygote() {
- WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- boot_class_table_.FreezeSnapshot();
- for (GcRoot<mirror::ClassLoader>& root : class_loaders_) {
- ClassTable* const class_table = root.Read()->GetClassTable();
+class MoveClassTableToPreZygoteVisitor : public ClassLoaderVisitor {
+ public:
+ explicit MoveClassTableToPreZygoteVisitor() {}
+
+ void Visit(mirror::ClassLoader* class_loader)
+ REQUIRES(Locks::classlinker_classes_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_) OVERRIDE {
+ ClassTable* const class_table = class_loader->GetClassTable();
if (class_table != nullptr) {
class_table->FreezeSnapshot();
}
}
+};
+
+void ClassLinker::MoveClassTableToPreZygote() {
+ WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ boot_class_table_.FreezeSnapshot();
+ MoveClassTableToPreZygoteVisitor visitor;
+ VisitClassLoadersAndRemoveClearedLoaders(&visitor);
}
mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) {
@@ -2949,25 +3057,43 @@
return nullptr;
}
+// Look up classes by hash and descriptor and put all matching ones in the result array.
+class LookupClassesVisitor : public ClassLoaderVisitor {
+ public:
+ LookupClassesVisitor(const char* descriptor, size_t hash, std::vector<mirror::Class*>* result)
+ : descriptor_(descriptor),
+ hash_(hash),
+ result_(result) {}
+
+ void Visit(mirror::ClassLoader* class_loader)
+ SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
+ ClassTable* const class_table = class_loader->GetClassTable();
+ mirror::Class* klass = class_table->Lookup(descriptor_, hash_);
+ if (klass != nullptr) {
+ result_->push_back(klass);
+ }
+ }
+
+ private:
+ const char* const descriptor_;
+ const size_t hash_;
+ std::vector<mirror::Class*>* const result_;
+};
+
void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Class*>& result) {
result.clear();
if (dex_cache_image_class_lookup_required_) {
MoveImageClassesToClassTable();
}
- WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ Thread* const self = Thread::Current();
+ ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
const size_t hash = ComputeModifiedUtf8Hash(descriptor);
mirror::Class* klass = boot_class_table_.Lookup(descriptor, hash);
if (klass != nullptr) {
result.push_back(klass);
}
- for (GcRoot<mirror::ClassLoader>& root : class_loaders_) {
- // There can only be one class with the same descriptor per class loader.
- ClassTable* const class_table = root.Read()->GetClassTable();
- klass = class_table->Lookup(descriptor, hash);
- if (klass != nullptr) {
- result.push_back(klass);
- }
- }
+ LookupClassesVisitor visitor(descriptor, hash, &result);
+ VisitClassLoaders(&visitor);
}
void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass) {
@@ -3067,7 +3193,8 @@
verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
std::string error_msg;
if (!preverified) {
- verifier_failure = verifier::MethodVerifier::VerifyClass(self, klass.Get(),
+ verifier_failure = verifier::MethodVerifier::VerifyClass(self,
+ klass.Get(),
Runtime::Current()->IsAotCompiler(),
&error_msg);
}
@@ -3131,7 +3258,8 @@
}
}
-bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class* klass,
+bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file,
+ mirror::Class* klass,
mirror::Class::Status& oat_file_class_status) {
// If we're compiling, we can only verify the class using the oat file if
// we are not compiling the image or if the class we're verifying is not part of
@@ -3253,9 +3381,12 @@
}
}
-mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, jstring name,
- jobjectArray interfaces, jobject loader,
- jobjectArray methods, jobjectArray throws) {
+mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa,
+ jstring name,
+ jobjectArray interfaces,
+ jobject loader,
+ jobjectArray methods,
+ jobjectArray throws) {
Thread* self = soa.Self();
StackHandleScope<10> hs(self);
MutableHandle<mirror::Class> klass(hs.NewHandle(
@@ -3415,7 +3546,8 @@
for (jobject weak_root : dex_caches_) {
mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(weak_root));
if (dex_cache != nullptr &&
- proxy_method->HasSameDexCacheResolvedTypes(dex_cache->GetResolvedTypes())) {
+ proxy_method->HasSameDexCacheResolvedTypes(dex_cache->GetResolvedTypes(),
+ image_pointer_size_)) {
ArtMethod* resolved_method = dex_cache->GetResolvedMethod(
proxy_method->GetDexMethodIndex(), image_pointer_size_);
CHECK(resolved_method != nullptr);
@@ -3488,8 +3620,8 @@
// The proxy method doesn't have its own dex cache or dex file and so it steals those of its
// interface prototype. The exception to this are Constructors and the Class of the Proxy itself.
- CHECK(prototype->HasSameDexCacheResolvedMethods(method));
- CHECK(prototype->HasSameDexCacheResolvedTypes(method));
+ CHECK(prototype->HasSameDexCacheResolvedMethods(method, image_pointer_size_));
+ CHECK(prototype->HasSameDexCacheResolvedTypes(method, image_pointer_size_));
auto* np = method->GetInterfaceMethodIfProxy(image_pointer_size_);
CHECK_EQ(prototype->GetDeclaringClass()->GetDexCache(), np->GetDexCache());
CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex());
@@ -3497,7 +3629,8 @@
CHECK_STREQ(np->GetName(), prototype->GetName());
CHECK_STREQ(np->GetShorty(), prototype->GetShorty());
// More complex sanity - via dex cache
- CHECK_EQ(np->GetReturnType(), prototype->GetReturnType());
+ CHECK_EQ(np->GetReturnType(true /* resolve */, image_pointer_size_),
+ prototype->GetReturnType(true /* resolve */, image_pointer_size_));
}
bool ClassLinker::CanWeInitializeClass(mirror::Class* klass, bool can_init_statics,
@@ -3734,7 +3867,8 @@
return success;
}
-bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass, Thread* self,
+bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass,
+ Thread* self,
ObjectLock<mirror::Class>& lock)
SHARED_REQUIRES(Locks::mutator_lock_) {
while (true) {
@@ -3803,7 +3937,8 @@
Handle<mirror::Class> super_klass,
ArtMethod* method,
ArtMethod* m,
- uint32_t index, uint32_t arg_type_idx)
+ uint32_t index,
+ uint32_t arg_type_idx)
SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(Thread::Current()->IsExceptionPending());
DCHECK(!m->IsProxyMethod());
@@ -3835,6 +3970,7 @@
}
static bool HasSameSignatureWithDifferentClassLoaders(Thread* self,
+ size_t pointer_size,
Handle<mirror::Class> klass,
Handle<mirror::Class> super_klass,
ArtMethod* method1,
@@ -3842,12 +3978,14 @@
SHARED_REQUIRES(Locks::mutator_lock_) {
{
StackHandleScope<1> hs(self);
- Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType()));
+ Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */,
+ pointer_size)));
if (UNLIKELY(return_type.Get() == nullptr)) {
ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1);
return false;
}
- mirror::Class* other_return_type = method2->GetReturnType();
+ mirror::Class* other_return_type = method2->GetReturnType(true /* resolve */,
+ pointer_size);
if (UNLIKELY(other_return_type == nullptr)) {
ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2);
return false;
@@ -3892,7 +4030,7 @@
StackHandleScope<1> hs(self);
uint32_t param_type_idx = types1->GetTypeItem(i).type_idx_;
Handle<mirror::Class> param_type(hs.NewHandle(
- method1->GetClassFromTypeIndex(param_type_idx, true)));
+ method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */, pointer_size)));
if (UNLIKELY(param_type.Get() == nullptr)) {
ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
method1, i, param_type_idx);
@@ -3900,7 +4038,7 @@
}
uint32_t other_param_type_idx = types2->GetTypeItem(i).type_idx_;
mirror::Class* other_param_type =
- method2->GetClassFromTypeIndex(other_param_type_idx, true);
+ method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */, pointer_size);
if (UNLIKELY(other_param_type == nullptr)) {
ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
method2, i, other_param_type_idx);
@@ -3936,7 +4074,8 @@
auto* m = klass->GetVTableEntry(i, image_pointer_size_);
auto* super_m = klass->GetSuperClass()->GetVTableEntry(i, image_pointer_size_);
if (m != super_m) {
- if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, klass, super_klass,
+ if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_,
+ klass, super_klass,
m, super_m))) {
self->AssertPendingException();
return false;
@@ -3953,7 +4092,8 @@
j, image_pointer_size_);
auto* super_m = super_klass->GetVirtualMethod(j, image_pointer_size_);
if (m != super_m) {
- if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, klass, super_klass,
+ if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_,
+ klass, super_klass,
m, super_m))) {
self->AssertPendingException();
return false;
@@ -4025,7 +4165,8 @@
ClassTable* class_table = class_loader->GetClassTable();
if (class_table == nullptr) {
class_table = new ClassTable;
- class_loaders_.push_back(class_loader);
+ Thread* const self = Thread::Current();
+ class_loaders_.push_back(self->GetJniEnv()->vm->AddWeakGlobalRef(self, class_loader));
// Don't already have a class table, add it to the class loader.
class_loader->SetClassTable(class_table);
}
@@ -4036,7 +4177,9 @@
return class_loader == nullptr ? &boot_class_table_ : class_loader->GetClassTable();
}
-bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass,
+bool ClassLinker::LinkClass(Thread* self,
+ const char* descriptor,
+ Handle<mirror::Class> klass,
Handle<mirror::ObjectArray<mirror::Class>> interfaces,
MutableHandle<mirror::Class>* h_new_class_out) {
CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus());
@@ -4186,8 +4329,10 @@
}
}
-static std::string DumpClasses(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1,
- const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2) {
+static std::string DumpClasses(const DexFile& dex_file1,
+ const DexFile::ClassDef& dex_class_def1,
+ const DexFile& dex_file2,
+ const DexFile::ClassDef& dex_class_def2) {
std::ostringstream os;
DumpClass(os, dex_file1, dex_class_def1, " (Compile time)");
DumpClass(os, dex_file2, dex_class_def2, " (Runtime)");
@@ -4197,43 +4342,59 @@
// Very simple structural check on whether the classes match. Only compares the number of
// methods and fields.
-static bool SimpleStructuralCheck(const DexFile& dex_file1, const DexFile::ClassDef& dex_class_def1,
- const DexFile& dex_file2, const DexFile::ClassDef& dex_class_def2,
+static bool SimpleStructuralCheck(const DexFile& dex_file1,
+ const DexFile::ClassDef& dex_class_def1,
+ const DexFile& dex_file2,
+ const DexFile::ClassDef& dex_class_def2,
std::string* error_msg) {
ClassDataItemIterator dex_data1(dex_file1, dex_file1.GetClassData(dex_class_def1));
ClassDataItemIterator dex_data2(dex_file2, dex_file2.GetClassData(dex_class_def2));
// Counters for current dex file.
size_t dex_virtual_methods1, dex_direct_methods1, dex_static_fields1, dex_instance_fields1;
- CountMethodsAndFields(dex_data1, &dex_virtual_methods1, &dex_direct_methods1, &dex_static_fields1,
+ CountMethodsAndFields(dex_data1,
+ &dex_virtual_methods1,
+ &dex_direct_methods1,
+ &dex_static_fields1,
&dex_instance_fields1);
// Counters for compile-time dex file.
size_t dex_virtual_methods2, dex_direct_methods2, dex_static_fields2, dex_instance_fields2;
- CountMethodsAndFields(dex_data2, &dex_virtual_methods2, &dex_direct_methods2, &dex_static_fields2,
+ CountMethodsAndFields(dex_data2,
+ &dex_virtual_methods2,
+ &dex_direct_methods2,
+ &dex_static_fields2,
&dex_instance_fields2);
if (dex_virtual_methods1 != dex_virtual_methods2) {
std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
- *error_msg = StringPrintf("Virtual method count off: %zu vs %zu\n%s", dex_virtual_methods1,
- dex_virtual_methods2, class_dump.c_str());
+ *error_msg = StringPrintf("Virtual method count off: %zu vs %zu\n%s",
+ dex_virtual_methods1,
+ dex_virtual_methods2,
+ class_dump.c_str());
return false;
}
if (dex_direct_methods1 != dex_direct_methods2) {
std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
- *error_msg = StringPrintf("Direct method count off: %zu vs %zu\n%s", dex_direct_methods1,
- dex_direct_methods2, class_dump.c_str());
+ *error_msg = StringPrintf("Direct method count off: %zu vs %zu\n%s",
+ dex_direct_methods1,
+ dex_direct_methods2,
+ class_dump.c_str());
return false;
}
if (dex_static_fields1 != dex_static_fields2) {
std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
- *error_msg = StringPrintf("Static field count off: %zu vs %zu\n%s", dex_static_fields1,
- dex_static_fields2, class_dump.c_str());
+ *error_msg = StringPrintf("Static field count off: %zu vs %zu\n%s",
+ dex_static_fields1,
+ dex_static_fields2,
+ class_dump.c_str());
return false;
}
if (dex_instance_fields1 != dex_instance_fields2) {
std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
- *error_msg = StringPrintf("Instance field count off: %zu vs %zu\n%s", dex_instance_fields1,
- dex_instance_fields2, class_dump.c_str());
+ *error_msg = StringPrintf("Instance field count off: %zu vs %zu\n%s",
+ dex_instance_fields1,
+ dex_instance_fields2,
+ class_dump.c_str());
return false;
}
@@ -4341,7 +4502,8 @@
// Verify
if (!klass->CanAccess(interface)) {
// TODO: the RI seemed to ignore this in my testing.
- ThrowIllegalAccessError(klass.Get(), "Interface %s implemented by class %s is inaccessible",
+ ThrowIllegalAccessError(klass.Get(),
+ "Interface %s implemented by class %s is inaccessible",
PrettyDescriptor(interface).c_str(),
PrettyDescriptor(klass.Get()).c_str());
return false;
@@ -4370,7 +4532,8 @@
}
// Verify
if (super->IsFinal() || super->IsInterface()) {
- ThrowIncompatibleClassChangeError(klass.Get(), "Superclass %s of %s is %s",
+ ThrowIncompatibleClassChangeError(klass.Get(),
+ "Superclass %s of %s is %s",
PrettyDescriptor(super).c_str(),
PrettyDescriptor(klass.Get()).c_str(),
super->IsFinal() ? "declared final" : "an interface");
@@ -4419,7 +4582,8 @@
}
// Populate the class vtable and itable. Compute return type indices.
-bool ClassLinker::LinkMethods(Thread* self, Handle<mirror::Class> klass,
+bool ClassLinker::LinkMethods(Thread* self,
+ Handle<mirror::Class> klass,
Handle<mirror::ObjectArray<mirror::Class>> interfaces,
ArtMethod** out_imt) {
self->AllowThreadSuspension();
@@ -4489,12 +4653,17 @@
class LinkVirtualHashTable {
public:
- LinkVirtualHashTable(Handle<mirror::Class> klass, size_t hash_size, uint32_t* hash_table,
+ LinkVirtualHashTable(Handle<mirror::Class> klass,
+ size_t hash_size,
+ uint32_t* hash_table,
size_t image_pointer_size)
- : klass_(klass), hash_size_(hash_size), hash_table_(hash_table),
+ : klass_(klass),
+ hash_size_(hash_size),
+ hash_table_(hash_table),
image_pointer_size_(image_pointer_size) {
std::fill(hash_table_, hash_table_ + hash_size_, invalid_index_);
}
+
void Add(uint32_t virtual_method_index) SHARED_REQUIRES(Locks::mutator_lock_) {
ArtMethod* local_method = klass_->GetVirtualMethodDuringLinking(
virtual_method_index, image_pointer_size_);
@@ -4509,6 +4678,7 @@
}
hash_table_[index] = virtual_method_index;
}
+
uint32_t FindAndRemove(MethodNameAndSignatureComparator* comparator)
SHARED_REQUIRES(Locks::mutator_lock_) {
const char* name = comparator->GetName();
@@ -4536,6 +4706,7 @@
}
return GetNotFoundIndex();
}
+
static uint32_t GetNotFoundIndex() {
return invalid_index_;
}
@@ -4693,7 +4864,8 @@
return true;
}
-bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass,
+bool ClassLinker::LinkInterfaceMethods(Thread* self,
+ Handle<mirror::Class> klass,
Handle<mirror::ObjectArray<mirror::Class>> interfaces,
ArtMethod** out_imt) {
StackHandleScope<3> hs(self);
@@ -4701,8 +4873,9 @@
const bool has_superclass = klass->HasSuperClass();
const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
const bool have_interfaces = interfaces.Get() != nullptr;
- const size_t num_interfaces =
- have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();
+ const size_t num_interfaces = have_interfaces
+ ? interfaces->GetLength()
+ : klass->NumDirectInterfaces();
const size_t method_alignment = ArtMethod::Alignment(image_pointer_size_);
const size_t method_size = ArtMethod::Size(image_pointer_size_);
if (num_interfaces == 0) {
@@ -4729,12 +4902,14 @@
}
size_t ifcount = super_ifcount + num_interfaces;
for (size_t i = 0; i < num_interfaces; i++) {
- mirror::Class* interface = have_interfaces ?
- interfaces->GetWithoutChecks(i) : mirror::Class::GetDirectInterface(self, klass, i);
+ mirror::Class* interface = have_interfaces
+ ? interfaces->GetWithoutChecks(i)
+ : mirror::Class::GetDirectInterface(self, klass, i);
DCHECK(interface != nullptr);
if (UNLIKELY(!interface->IsInterface())) {
std::string temp;
- ThrowIncompatibleClassChangeError(klass.Get(), "Class %s implements non-interface class %s",
+ ThrowIncompatibleClassChangeError(klass.Get(),
+ "Class %s implements non-interface class %s",
PrettyDescriptor(klass.Get()).c_str(),
PrettyDescriptor(interface->GetDescriptor(&temp)).c_str());
return false;
@@ -4914,8 +5089,7 @@
continue;
}
for (size_t j = 0; j < num_methods; ++j) {
- auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(
- j, image_pointer_size_);
+ auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j, image_pointer_size_);
MethodNameAndSignatureComparator interface_name_comparator(
interface_method->GetInterfaceMethodIfProxy(image_pointer_size_));
int32_t k;
@@ -5088,8 +5262,8 @@
// Check that there are no stale methods are in the dex cache array.
if (kIsDebugBuild) {
auto* resolved_methods = klass->GetDexCache()->GetResolvedMethods();
- for (size_t i = 0, count = resolved_methods->GetLength(); i < count; ++i) {
- auto* m = resolved_methods->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_);
+ for (size_t i = 0, count = klass->GetDexCache()->NumResolvedMethods(); i < count; ++i) {
+ auto* m = mirror::DexCache::GetElementPtrSize(resolved_methods, i, image_pointer_size_);
CHECK(move_table.find(m) == move_table.end()) << PrettyMethod(m);
}
}
@@ -5153,7 +5327,9 @@
}
};
-bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_static,
+bool ClassLinker::LinkFields(Thread* self,
+ Handle<mirror::Class> klass,
+ bool is_static,
size_t* class_size) {
self->AllowThreadSuspension();
const size_t num_fields = is_static ? klass->NumStaticFields() : klass->NumInstanceFields();
@@ -5352,7 +5528,8 @@
klass->SetReferenceInstanceOffsets(reference_offsets);
}
-mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, uint32_t string_idx,
+mirror::String* ClassLinker::ResolveString(const DexFile& dex_file,
+ uint32_t string_idx,
Handle<mirror::DexCache> dex_cache) {
DCHECK(dex_cache.Get() != nullptr);
mirror::String* resolved = dex_cache->GetResolvedString(string_idx);
@@ -5366,7 +5543,8 @@
return string;
}
-mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx,
+mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
+ uint16_t type_idx,
mirror::Class* referrer) {
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
@@ -5374,7 +5552,8 @@
return ResolveType(dex_file, type_idx, dex_cache, class_loader);
}
-mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx,
+mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
+ uint16_t type_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
DCHECK(dex_cache.Get() != nullptr);
@@ -5407,10 +5586,12 @@
return resolved;
}
-ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx,
+ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file,
+ uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
- ArtMethod* referrer, InvokeType type) {
+ ArtMethod* referrer,
+ InvokeType type) {
DCHECK(dex_cache.Get() != nullptr);
// Check for hit in the dex cache.
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
@@ -5499,7 +5680,9 @@
mirror::Class* methods_class = resolved->GetDeclaringClass();
mirror::Class* referring_class = referrer->GetDeclaringClass();
if (!referring_class->CanAccess(methods_class)) {
- ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class, resolved,
+ ThrowIllegalAccessErrorClassForMethodDispatch(referring_class,
+ methods_class,
+ resolved,
type);
exception_generated = true;
} else if (!referring_class->CanAccessMember(methods_class, resolved->GetAccessFlags())) {
@@ -5594,9 +5777,11 @@
return resolved;
}
-ArtField* ClassLinker::ResolveField(const DexFile& dex_file, uint32_t field_idx,
+ArtField* ClassLinker::ResolveField(const DexFile& dex_file,
+ uint32_t field_idx,
Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, bool is_static) {
+ Handle<mirror::ClassLoader> class_loader,
+ bool is_static) {
DCHECK(dex_cache.Get() != nullptr);
ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
if (resolved != nullptr) {
@@ -5635,7 +5820,8 @@
return resolved;
}
-ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx,
+ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file,
+ uint32_t field_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader) {
DCHECK(dex_cache.Get() != nullptr);
@@ -5665,7 +5851,8 @@
return resolved;
}
-const char* ClassLinker::MethodShorty(uint32_t method_idx, ArtMethod* referrer,
+const char* ClassLinker::MethodShorty(uint32_t method_idx,
+ ArtMethod* referrer,
uint32_t* length) {
mirror::Class* declaring_class = referrer->GetDeclaringClass();
mirror::DexCache* dex_cache = declaring_class->GetDexCache();
@@ -5745,26 +5932,33 @@
<< NumNonZygoteClasses() << "\n";
}
-size_t ClassLinker::NumZygoteClasses() const {
- size_t sum = boot_class_table_.NumZygoteClasses();
- for (const GcRoot<mirror::ClassLoader>& root : class_loaders_) {
- ClassTable* const class_table = root.Read()->GetClassTable();
+class CountClassesVisitor : public ClassLoaderVisitor {
+ public:
+ CountClassesVisitor() : num_zygote_classes(0), num_non_zygote_classes(0) {}
+
+ void Visit(mirror::ClassLoader* class_loader)
+ SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
+ ClassTable* const class_table = class_loader->GetClassTable();
if (class_table != nullptr) {
- sum += class_table->NumZygoteClasses();
+ num_zygote_classes += class_table->NumZygoteClasses();
+ num_non_zygote_classes += class_table->NumNonZygoteClasses();
}
}
- return sum;
+
+ size_t num_zygote_classes;
+ size_t num_non_zygote_classes;
+};
+
+size_t ClassLinker::NumZygoteClasses() const {
+ CountClassesVisitor visitor;
+ VisitClassLoaders(&visitor);
+ return visitor.num_zygote_classes + boot_class_table_.NumZygoteClasses();
}
size_t ClassLinker::NumNonZygoteClasses() const {
- size_t sum = boot_class_table_.NumNonZygoteClasses();
- for (const GcRoot<mirror::ClassLoader>& root : class_loaders_) {
- ClassTable* const class_table = root.Read()->GetClassTable();
- if (class_table != nullptr) {
- sum += class_table->NumNonZygoteClasses();
- }
- }
- return sum;
+ CountClassesVisitor visitor;
+ VisitClassLoaders(&visitor);
+ return visitor.num_non_zygote_classes + boot_class_table_.NumNonZygoteClasses();
}
size_t ClassLinker::NumLoadedClasses() {
@@ -5977,4 +6171,35 @@
find_array_class_cache_next_victim_ = 0;
}
+void ClassLinker::VisitClassLoadersAndRemoveClearedLoaders(ClassLoaderVisitor* visitor) {
+ Thread* const self = Thread::Current();
+ Locks::classlinker_classes_lock_->AssertExclusiveHeld(self);
+ JavaVMExt* const vm = self->GetJniEnv()->vm;
+ for (auto it = class_loaders_.begin(); it != class_loaders_.end();) {
+ const jweak weak_root = *it;
+ mirror::ClassLoader* const class_loader = down_cast<mirror::ClassLoader*>(
+ vm->DecodeWeakGlobal(self, weak_root));
+ if (class_loader != nullptr) {
+ visitor->Visit(class_loader);
+ ++it;
+ } else {
+ // Remove the cleared weak reference from the array.
+ vm->DeleteWeakGlobalRef(self, weak_root);
+ it = class_loaders_.erase(it);
+ }
+ }
+}
+
+void ClassLinker::VisitClassLoaders(ClassLoaderVisitor* visitor) const {
+ Thread* const self = Thread::Current();
+ JavaVMExt* const vm = self->GetJniEnv()->vm;
+ for (jweak weak_root : class_loaders_) {
+ mirror::ClassLoader* const class_loader = down_cast<mirror::ClassLoader*>(
+ vm->DecodeWeakGlobal(self, weak_root));
+ if (class_loader != nullptr) {
+ visitor->Visit(class_loader);
+ }
+ }
+}
+
} // namespace art
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 2a7162b..fee7066 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -59,6 +59,13 @@
enum VisitRootFlags : uint8_t;
+class ClassLoaderVisitor {
+ public:
+ virtual ~ClassLoaderVisitor() {}
+ virtual void Visit(mirror::ClassLoader* class_loader)
+ SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0;
+};
+
class ClassLinker {
public:
// Well known mirror::Class roots accessed via GetClassRoot.
@@ -108,16 +115,19 @@
// Initialize class linker by bootstraping from dex files.
void InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
// Initialize class linker from one or more images.
void InitFromImage() SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
// Finds a class by its descriptor, loading it if necessary.
// If class_loader is null, searches boot_class_path_.
- mirror::Class* FindClass(Thread* self, const char* descriptor,
+ mirror::Class* FindClass(Thread* self,
+ const char* descriptor,
Handle<mirror::ClassLoader> class_loader)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
// Finds a class in the path class loader, loading it if necessary without using JNI. Hash
// function is supposed to be ComputeModifiedUtf8Hash(descriptor). Returns true if the
@@ -125,19 +135,24 @@
// was encountered while walking the parent chain (currently only BootClassLoader and
// PathClassLoader are supported).
bool FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self, const char* descriptor, size_t hash,
+ Thread* self,
+ const char* descriptor,
+ size_t hash,
Handle<mirror::ClassLoader> class_loader,
mirror::Class** result)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
// Finds a class by its descriptor using the "system" class loader, ie by searching the
// boot_class_path_.
mirror::Class* FindSystemClass(Thread* self, const char* descriptor)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
// Finds the array class given for the element class.
mirror::Class* FindArrayClass(Thread* self, mirror::Class** element_class)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
// Returns true if the class linker is initialized.
bool IsInitialized() const {
@@ -145,14 +160,20 @@
}
// Define a new a class based on a ClassDef from a DexFile
- mirror::Class* DefineClass(Thread* self, const char* descriptor, size_t hash,
+ mirror::Class* DefineClass(Thread* self,
+ const char* descriptor,
+ size_t hash,
Handle<mirror::ClassLoader> class_loader,
- const DexFile& dex_file, const DexFile::ClassDef& dex_class_def)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ const DexFile& dex_file,
+ const DexFile::ClassDef& dex_class_def)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
// Finds a class by its descriptor, returning null if it isn't wasn't loaded
// by the given 'class_loader'.
- mirror::Class* LookupClass(Thread* self, const char* descriptor, size_t hash,
+ mirror::Class* LookupClass(Thread* self,
+ const char* descriptor,
+ size_t hash,
mirror::ClassLoader* class_loader)
REQUIRES(!Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -167,16 +188,18 @@
// General class unloading is not supported, this is used to prune
// unwanted classes during image writing.
bool RemoveClass(const char* descriptor, mirror::ClassLoader* class_loader)
- REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ REQUIRES(!Locks::classlinker_classes_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
void DumpAllClasses(int flags)
- REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ REQUIRES(!Locks::classlinker_classes_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
- void DumpForSigQuit(std::ostream& os)
- REQUIRES(!Locks::classlinker_classes_lock_);
+ void DumpForSigQuit(std::ostream& os) REQUIRES(!Locks::classlinker_classes_lock_);
size_t NumLoadedClasses()
- REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ REQUIRES(!Locks::classlinker_classes_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Resolve a String with the given index from the DexFile, storing the
// result in the DexCache. The referrer is used to identify the
@@ -194,41 +217,50 @@
// result in the DexCache. The referrer is used to identity the
// target DexCache and ClassLoader to use for resolution.
mirror::Class* ResolveType(const DexFile& dex_file, uint16_t type_idx, mirror::Class* referrer)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
// Resolve a Type with the given index from the DexFile, storing the
// result in the DexCache. The referrer is used to identify the
// target DexCache and ClassLoader to use for resolution.
mirror::Class* ResolveType(uint16_t type_idx, ArtMethod* referrer)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
mirror::Class* ResolveType(uint16_t type_idx, ArtField* referrer)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
// Resolve a type with the given ID from the DexFile, storing the
// result in DexCache. The ClassLoader is used to search for the
// type, since it may be referenced from but not contained within
// the given DexFile.
- mirror::Class* ResolveType(const DexFile& dex_file, uint16_t type_idx,
+ mirror::Class* ResolveType(const DexFile& dex_file,
+ uint16_t type_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
// Resolve a method with a given ID from the DexFile, storing the
// result in DexCache. The ClassLinker and ClassLoader are used as
// in ResolveType. What is unique is the method type argument which
// is used to determine if this method is a direct, static, or
// virtual method.
- ArtMethod* ResolveMethod(const DexFile& dex_file, uint32_t method_idx,
+ ArtMethod* ResolveMethod(const DexFile& dex_file,
+ uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, ArtMethod* referrer,
+ Handle<mirror::ClassLoader> class_loader,
+ ArtMethod* referrer,
InvokeType type)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
ArtMethod* GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer)
SHARED_REQUIRES(Locks::mutator_lock_);
ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
ArtMethod* ResolveMethodWithoutInvokeType(const DexFile& dex_file,
uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
@@ -241,7 +273,8 @@
ArtField* GetResolvedField(uint32_t field_idx, mirror::DexCache* dex_cache)
SHARED_REQUIRES(Locks::mutator_lock_);
ArtField* ResolveField(uint32_t field_idx, ArtMethod* referrer, bool is_static)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
// Resolve a field with a given ID from the DexFile, storing the
// result in DexCache. The ClassLinker and ClassLoader are used as
@@ -251,16 +284,19 @@
ArtField* ResolveField(const DexFile& dex_file, uint32_t field_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader, bool is_static)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
// Resolve a field with a given ID from the DexFile, storing the
// result in DexCache. The ClassLinker and ClassLoader are used as
// in ResolveType. No is_static argument is provided so that Java
// field resolution semantics are followed.
- ArtField* ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx,
+ ArtField* ResolveFieldJLS(const DexFile& dex_file,
+ uint32_t field_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
// Get shorty from method index without resolution. Used to do handlerization.
const char* MethodShorty(uint32_t method_idx, ArtMethod* referrer, uint32_t* length)
@@ -269,19 +305,25 @@
// Returns true on success, false if there's an exception pending.
// can_run_clinit=false allows the compiler to attempt to init a class,
// given the restriction that no <clinit> execution is possible.
- bool EnsureInitialized(Thread* self, Handle<mirror::Class> c, bool can_init_fields,
+ bool EnsureInitialized(Thread* self,
+ Handle<mirror::Class> c,
+ bool can_init_fields,
bool can_init_parents)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
// Initializes classes that have instances in the image but that have
// <clinit> methods so they could not be initialized by the compiler.
- void RunRootClinits() SHARED_REQUIRES(Locks::mutator_lock_)
+ void RunRootClinits()
+ SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!dex_lock_, !Roles::uninterruptible_);
mirror::DexCache* RegisterDexFile(const DexFile& dex_file)
- REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ REQUIRES(!dex_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
void RegisterDexFile(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
- REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ REQUIRES(!dex_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
const OatFile* RegisterOatFile(const OatFile* oat_file)
REQUIRES(!dex_lock_);
@@ -295,26 +337,32 @@
REQUIRES(!dex_lock_);
void VisitClasses(ClassVisitor* visitor)
- REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ REQUIRES(!Locks::classlinker_classes_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Less efficient variant of VisitClasses that copies the class_table_ into secondary storage
// so that it can visit individual classes without holding the doesn't hold the
// Locks::classlinker_classes_lock_. As the Locks::classlinker_classes_lock_ isn't held this code
// can race with insertion and deletion of classes while the visitor is being called.
void VisitClassesWithoutClassesLock(ClassVisitor* visitor)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
void VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags)
- REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ REQUIRES(!Locks::classlinker_classes_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
void VisitRoots(RootVisitor* visitor, VisitRootFlags flags)
- REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ REQUIRES(!dex_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
mirror::DexCache* FindDexCache(Thread* self,
const DexFile& dex_file,
bool allow_failure = false)
- REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ REQUIRES(!dex_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
void FixupDexCaches(ArtMethod* resolution_method)
- REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ REQUIRES(!dex_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Finds or creates the oat file holding dex_location. Then loads and returns
// all corresponding dex files (there may be more than one dex file loaded
@@ -331,52 +379,68 @@
// could end up starving GC if we need to generate or relocate any oat
// files.
std::vector<std::unique_ptr<const DexFile>> OpenDexFilesFromOat(
- const char* dex_location, const char* oat_location,
+ const char* dex_location,
+ const char* oat_location,
std::vector<std::string>* error_msgs)
REQUIRES(!dex_lock_, !Locks::mutator_lock_);
// Allocate an instance of a java.lang.Object.
- mirror::Object* AllocObject(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_)
+ mirror::Object* AllocObject(Thread* self)
+ SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
// TODO: replace this with multiple methods that allocate the correct managed type.
template <class T>
mirror::ObjectArray<T>* AllocObjectArray(Thread* self, size_t length)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Roles::uninterruptible_);
mirror::ObjectArray<mirror::Class>* AllocClassArray(Thread* self, size_t length)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Roles::uninterruptible_);
mirror::ObjectArray<mirror::String>* AllocStringArray(Thread* self, size_t length)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Roles::uninterruptible_);
LengthPrefixedArray<ArtField>* AllocArtFieldArray(Thread* self, size_t length);
LengthPrefixedArray<ArtMethod>* AllocArtMethodArray(Thread* self, size_t length);
mirror::PointerArray* AllocPointerArray(Thread* self, size_t length)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Roles::uninterruptible_);
mirror::IfTable* AllocIfTable(Thread* self, size_t ifcount)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Roles::uninterruptible_);
- mirror::ObjectArray<mirror::StackTraceElement>* AllocStackTraceElementArray(
- Thread* self, size_t length) SHARED_REQUIRES(Locks::mutator_lock_)
- REQUIRES(!Roles::uninterruptible_);
+ mirror::ObjectArray<mirror::StackTraceElement>* AllocStackTraceElementArray(Thread* self,
+ size_t length)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Roles::uninterruptible_);
void VerifyClass(Thread* self, Handle<mirror::Class> klass)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
- bool VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class* klass,
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
+ bool VerifyClassUsingOatFile(const DexFile& dex_file,
+ mirror::Class* klass,
mirror::Class::Status& oat_file_class_status)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
void ResolveClassExceptionHandlerTypes(const DexFile& dex_file,
Handle<mirror::Class> klass)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
void ResolveMethodExceptionHandlerTypes(const DexFile& dex_file, ArtMethod* klass)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
- mirror::Class* CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, jstring name,
- jobjectArray interfaces, jobject loader, jobjectArray methods,
+ mirror::Class* CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa,
+ jstring name,
+ jobjectArray interfaces,
+ jobject loader,
+ jobjectArray methods,
jobjectArray throws)
SHARED_REQUIRES(Locks::mutator_lock_);
std::string GetDescriptorForProxy(mirror::Class* proxy_class)
@@ -390,7 +454,8 @@
SHARED_REQUIRES(Locks::mutator_lock_);
// Get the oat code for a method from a method index.
- const void* GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
+ const void* GetQuickOatCodeFor(const DexFile& dex_file,
+ uint16_t class_def_idx,
uint32_t method_idx)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -462,7 +527,8 @@
// Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files.
// Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
jobject CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
size_t GetImagePointerSize() const {
DCHECK(ValidPointerSize(image_pointer_size_)) << image_pointer_size_;
@@ -481,8 +547,18 @@
void DropFindArrayClassCache() SHARED_REQUIRES(Locks::mutator_lock_);
private:
+ // The RemoveClearedLoaders version removes cleared weak global class loaders and frees their
+ // class tables. This version can only be called with reader access to the
+ // classlinker_classes_lock_ since it modifies the class_loaders_ list.
+ void VisitClassLoadersAndRemoveClearedLoaders(ClassLoaderVisitor* visitor)
+ REQUIRES(Locks::classlinker_classes_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ void VisitClassLoaders(ClassLoaderVisitor* visitor) const
+ SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+
+
void VisitClassesInternal(ClassVisitor* visitor)
- REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
// Returns the number of zygote and image classes.
size_t NumZygoteClasses() const
@@ -495,36 +571,48 @@
SHARED_REQUIRES(Locks::mutator_lock_);
OatFile& GetImageOatFile(gc::space::ImageSpace* space)
- REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ REQUIRES(!dex_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
- void FinishInit(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_)
+ void FinishInit(Thread* self)
+ SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!dex_lock_, !Roles::uninterruptible_);
// For early bootstrapping by Init
mirror::Class* AllocClass(Thread* self, mirror::Class* java_lang_Class, uint32_t class_size)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Roles::uninterruptible_);
// Alloc* convenience functions to avoid needing to pass in mirror::Class*
// values that are known to the ClassLinker such as
// kObjectArrayClass and kJavaLangString etc.
mirror::Class* AllocClass(Thread* self, uint32_t class_size)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Roles::uninterruptible_);
mirror::DexCache* AllocDexCache(Thread* self, const DexFile& dex_file)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Roles::uninterruptible_);
mirror::Class* CreatePrimitiveClass(Thread* self, Primitive::Type type)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Roles::uninterruptible_);
mirror::Class* InitializePrimitiveClass(mirror::Class* primitive_class, Primitive::Type type)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Roles::uninterruptible_);
- mirror::Class* CreateArrayClass(Thread* self, const char* descriptor, size_t hash,
+ mirror::Class* CreateArrayClass(Thread* self,
+ const char* descriptor,
+ size_t hash,
Handle<mirror::ClassLoader> class_loader)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
void AppendToBootClassPath(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
// Precomputes size needed for Class, in the case of a non-temporary class this size must be
// sufficient to hold all static fields.
@@ -533,22 +621,30 @@
// Setup the classloader, class def index, type idx so that we can insert this class in the class
// table.
- void SetupClass(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
- Handle<mirror::Class> klass, mirror::ClassLoader* class_loader)
+ void SetupClass(const DexFile& dex_file,
+ const DexFile::ClassDef& dex_class_def,
+ Handle<mirror::Class> klass,
+ mirror::ClassLoader* class_loader)
SHARED_REQUIRES(Locks::mutator_lock_);
- void LoadClass(Thread* self, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
+ void LoadClass(Thread* self,
+ const DexFile& dex_file,
+ const DexFile::ClassDef& dex_class_def,
Handle<mirror::Class> klass)
SHARED_REQUIRES(Locks::mutator_lock_);
- void LoadClassMembers(Thread* self, const DexFile& dex_file, const uint8_t* class_data,
- Handle<mirror::Class> klass, const OatFile::OatClass* oat_class)
+ void LoadClassMembers(Thread* self,
+ const DexFile& dex_file,
+ const uint8_t* class_data,
+ Handle<mirror::Class> klass,
+ const OatFile::OatClass* oat_class)
SHARED_REQUIRES(Locks::mutator_lock_);
- void LoadField(const ClassDataItemIterator& it, Handle<mirror::Class> klass,
- ArtField* dst)
+ void LoadField(const ClassDataItemIterator& it, Handle<mirror::Class> klass, ArtField* dst)
SHARED_REQUIRES(Locks::mutator_lock_);
- void LoadMethod(Thread* self, const DexFile& dex_file, const ClassDataItemIterator& it,
+ void LoadMethod(Thread* self,
+ const DexFile& dex_file,
+ const ClassDataItemIterator& it,
Handle<mirror::Class> klass, ArtMethod* dst)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -560,40 +656,50 @@
SHARED_REQUIRES(Locks::mutator_lock_);
void RegisterDexFileLocked(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
- REQUIRES(dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
- mirror::DexCache* FindDexCacheLocked(Thread* self,
- const DexFile& dex_file,
- bool allow_failure)
+ REQUIRES(dex_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ mirror::DexCache* FindDexCacheLocked(Thread* self, const DexFile& dex_file, bool allow_failure)
REQUIRES(dex_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool InitializeClass(Thread* self, Handle<mirror::Class> klass, bool can_run_clinit,
+ bool InitializeClass(Thread* self,
+ Handle<mirror::Class> klass,
+ bool can_run_clinit,
bool can_init_parents)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
- bool WaitForInitializeClass(Handle<mirror::Class> klass, Thread* self,
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
+ bool WaitForInitializeClass(Handle<mirror::Class> klass,
+ Thread* self,
ObjectLock<mirror::Class>& lock);
bool ValidateSuperClassDescriptors(Handle<mirror::Class> klass)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool IsSameDescriptorInDifferentClassContexts(Thread* self, const char* descriptor,
+ bool IsSameDescriptorInDifferentClassContexts(Thread* self,
+ const char* descriptor,
Handle<mirror::ClassLoader> class_loader1,
Handle<mirror::ClassLoader> class_loader2)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool IsSameMethodSignatureInDifferentClassContexts(Thread* self, ArtMethod* method,
- mirror::Class* klass1, mirror::Class* klass2)
+ bool IsSameMethodSignatureInDifferentClassContexts(Thread* self,
+ ArtMethod* method,
+ mirror::Class* klass1,
+ mirror::Class* klass2)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass,
+ bool LinkClass(Thread* self,
+ const char* descriptor,
+ Handle<mirror::Class> klass,
Handle<mirror::ObjectArray<mirror::Class>> interfaces,
MutableHandle<mirror::Class>* h_new_class_out)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::classlinker_classes_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!Locks::classlinker_classes_lock_);
bool LinkSuperClass(Handle<mirror::Class> klass)
SHARED_REQUIRES(Locks::mutator_lock_);
bool LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
bool LinkMethods(Thread* self,
Handle<mirror::Class> klass,
@@ -604,7 +710,8 @@
bool LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass,
+ bool LinkInterfaceMethods(Thread* self,
+ Handle<mirror::Class> klass,
Handle<mirror::ObjectArray<mirror::Class>> interfaces,
ArtMethod** out_imt)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -615,7 +722,8 @@
SHARED_REQUIRES(Locks::mutator_lock_);
bool LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_static, size_t* class_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- void LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class,
+ void LinkCode(ArtMethod* method,
+ const OatFile::OatClass* oat_class,
uint32_t class_def_method_index)
SHARED_REQUIRES(Locks::mutator_lock_);
void CreateReferenceInstanceOffsets(Handle<mirror::Class> klass)
@@ -628,13 +736,14 @@
// For use by ImageWriter to find DexCaches for its roots
ReaderWriterMutex* DexLock()
- SHARED_REQUIRES(Locks::mutator_lock_) LOCK_RETURNED(dex_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ LOCK_RETURNED(dex_lock_) {
return &dex_lock_;
}
size_t GetDexCacheCount() SHARED_REQUIRES(Locks::mutator_lock_, dex_lock_) {
return dex_caches_.size();
}
- const std::list<jobject>& GetDexCaches() SHARED_REQUIRES(Locks::mutator_lock_, dex_lock_) {
+ const std::list<jweak>& GetDexCaches() SHARED_REQUIRES(Locks::mutator_lock_, dex_lock_) {
return dex_caches_;
}
@@ -662,7 +771,8 @@
SHARED_REQUIRES(Locks::mutator_lock_, Locks::classlinker_classes_lock_);
// Insert a new class table if not found.
ClassTable* InsertClassTableForClassLoader(mirror::ClassLoader* class_loader)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::classlinker_classes_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(Locks::classlinker_classes_lock_);
// EnsureResolved is called to make sure that a class in the class_table_ has been resolved
// before returning it to the caller. Its the responsibility of the thread that placed the class
@@ -671,7 +781,9 @@
// retire a class, the version of the class in the table is returned and this may differ from
// the class passed in.
mirror::Class* EnsureResolved(Thread* self, const char* descriptor, mirror::Class* klass)
- WARN_UNUSED SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
+ WARN_UNUSED
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
void FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -686,7 +798,8 @@
// class.
// Note: Currently we only store the descriptor, so we cannot throw the exact throwable, only
// a recreation with a custom string.
- void ThrowEarlierClassFailure(mirror::Class* c) SHARED_REQUIRES(Locks::mutator_lock_)
+ void ThrowEarlierClassFailure(mirror::Class* c)
+ SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!dex_lock_);
// Check for duplicate class definitions of the given oat file against all open oat files.
@@ -709,12 +822,12 @@
mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
// JNI weak globals to allow dex caches to get unloaded. We lazily delete weak globals when we
// register new dex files.
- std::list<jobject> dex_caches_ GUARDED_BY(dex_lock_);
+ std::list<jweak> dex_caches_ GUARDED_BY(dex_lock_);
std::vector<const OatFile*> oat_files_ GUARDED_BY(dex_lock_);
- // This contains the class laoders which have class tables. It is populated by
- // InsertClassTableForClassLoader.
- std::vector<GcRoot<mirror::ClassLoader>> class_loaders_
+ // This contains the class loaders which have class tables. It is populated by
+ // InsertClassTableForClassLoader. Weak roots to enable class unloading.
+ std::list<jweak> class_loaders_
GUARDED_BY(Locks::classlinker_classes_lock_);
// Boot class path table. Since the class loader for this is null.
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index c3191fa..b4ea3b3 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -165,12 +165,14 @@
EXPECT_TRUE(method->GetName() != nullptr);
EXPECT_TRUE(method->GetSignature() != Signature::NoSignature());
- EXPECT_TRUE(method->HasDexCacheResolvedMethods());
- EXPECT_TRUE(method->HasDexCacheResolvedTypes());
+ EXPECT_TRUE(method->HasDexCacheResolvedMethods(sizeof(void*)));
+ EXPECT_TRUE(method->HasDexCacheResolvedTypes(sizeof(void*)));
EXPECT_TRUE(method->HasSameDexCacheResolvedMethods(
- method->GetDeclaringClass()->GetDexCache()->GetResolvedMethods()));
+ method->GetDeclaringClass()->GetDexCache()->GetResolvedMethods(),
+ sizeof(void*)));
EXPECT_TRUE(method->HasSameDexCacheResolvedTypes(
- method->GetDeclaringClass()->GetDexCache()->GetResolvedTypes()));
+ method->GetDeclaringClass()->GetDexCache()->GetResolvedTypes(),
+ sizeof(void*)));
}
void AssertField(mirror::Class* klass, ArtField* field)
@@ -357,8 +359,9 @@
// Verify the dex cache has resolution methods in all resolved method slots
mirror::DexCache* dex_cache = class_linker_->FindDexCache(Thread::Current(), dex);
auto* resolved_methods = dex_cache->GetResolvedMethods();
- for (size_t i = 0; i < static_cast<size_t>(resolved_methods->GetLength()); i++) {
- EXPECT_TRUE(resolved_methods->GetElementPtrSize<ArtMethod*>(i, sizeof(void*)) != nullptr)
+ for (size_t i = 0, num_methods = dex_cache->NumResolvedMethods(); i != num_methods; ++i) {
+ EXPECT_TRUE(
+ mirror::DexCache::GetElementPtrSize(resolved_methods, i, sizeof(void*)) != nullptr)
<< dex.GetLocation() << " i=" << i;
}
}
@@ -565,6 +568,10 @@
addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_fields_), "numResolvedFields");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_methods_), "numResolvedMethods");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_types_), "numResolvedTypes");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_strings_), "numStrings");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_types_), "resolvedTypes");
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 6b18d90..727392e 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -58,10 +58,10 @@
REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
// Returns the number of classes in previous snapshots.
- size_t NumZygoteClasses() const REQUIRES(Locks::classlinker_classes_lock_);
+ size_t NumZygoteClasses() const SHARED_REQUIRES(Locks::classlinker_classes_lock_);
// Returns all off the classes in the lastest snapshot.
- size_t NumNonZygoteClasses() const REQUIRES(Locks::classlinker_classes_lock_);
+ size_t NumNonZygoteClasses() const SHARED_REQUIRES(Locks::classlinker_classes_lock_);
// Update a class in the table with the new class. Returns the existing class which was replaced.
mirror::Class* UpdateClass(const char* descriptor, mirror::Class* new_klass, size_t hash)
@@ -79,7 +79,7 @@
// Return false if the callback told us to exit.
bool Visit(ClassVisitor* visitor)
- REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
mirror::Class* Lookup(const char* descriptor, size_t hash)
SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index ffca01d..d5691af 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -218,6 +218,17 @@
<< " " << dex_pc_offset;
}
+ // We only care about invokes in the Jit.
+ void InvokeVirtualOrInterface(Thread* thread ATTRIBUTE_UNUSED,
+ mirror::Object*,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtMethod*)
+ OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ LOG(ERROR) << "Unexpected invoke event in debugger " << PrettyMethod(method)
+ << " " << dex_pc;
+ }
+
private:
static bool IsReturn(ArtMethod* method, uint32_t dex_pc)
SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -567,7 +578,7 @@
}
Runtime* runtime = Runtime::Current();
- runtime->GetThreadList()->SuspendAll(__FUNCTION__);
+ ScopedSuspendAll ssa(__FUNCTION__);
Thread* self = Thread::Current();
ThreadState old_state = self->SetStateUnsafe(kRunnable);
CHECK_NE(old_state, kRunnable);
@@ -577,8 +588,6 @@
instrumentation_events_ = 0;
gDebuggerActive = true;
CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
- runtime->GetThreadList()->ResumeAll();
-
LOG(INFO) << "Debugger is active";
}
@@ -591,32 +600,32 @@
// to kRunnable to avoid scoped object access transitions. Remove the debugger as a listener
// and clear the object registry.
Runtime* runtime = Runtime::Current();
- runtime->GetThreadList()->SuspendAll(__FUNCTION__);
Thread* self = Thread::Current();
- ThreadState old_state = self->SetStateUnsafe(kRunnable);
-
- // Debugger may not be active at this point.
- if (IsDebuggerActive()) {
- {
- // Since we're going to disable deoptimization, we clear the deoptimization requests queue.
- // This prevents us from having any pending deoptimization request when the debugger attaches
- // to us again while no event has been requested yet.
- MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
- deoptimization_requests_.clear();
- full_deoptimization_event_count_ = 0U;
+ {
+ ScopedSuspendAll ssa(__FUNCTION__);
+ ThreadState old_state = self->SetStateUnsafe(kRunnable);
+ // Debugger may not be active at this point.
+ if (IsDebuggerActive()) {
+ {
+ // Since we're going to disable deoptimization, we clear the deoptimization requests queue.
+ // This prevents us from having any pending deoptimization request when the debugger attaches
+ // to us again while no event has been requested yet.
+ MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
+ deoptimization_requests_.clear();
+ full_deoptimization_event_count_ = 0U;
+ }
+ if (instrumentation_events_ != 0) {
+ runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener,
+ instrumentation_events_);
+ instrumentation_events_ = 0;
+ }
+ if (RequiresDeoptimization()) {
+ runtime->GetInstrumentation()->DisableDeoptimization(kDbgInstrumentationKey);
+ }
+ gDebuggerActive = false;
}
- if (instrumentation_events_ != 0) {
- runtime->GetInstrumentation()->RemoveListener(&gDebugInstrumentationListener,
- instrumentation_events_);
- instrumentation_events_ = 0;
- }
- if (RequiresDeoptimization()) {
- runtime->GetInstrumentation()->DisableDeoptimization(kDbgInstrumentationKey);
- }
- gDebuggerActive = false;
+ CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
}
- CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
- runtime->GetThreadList()->ResumeAll();
{
ScopedObjectAccess soa(self);
@@ -740,9 +749,8 @@
MonitorInfo monitor_info;
{
ScopedThreadSuspension sts(self, kSuspended);
- Runtime::Current()->GetThreadList()->SuspendAll(__FUNCTION__);
+ ScopedSuspendAll ssa(__FUNCTION__);
monitor_info = MonitorInfo(o);
- Runtime::Current()->GetThreadList()->ResumeAll();
}
if (monitor_info.owner_ != nullptr) {
expandBufAddObjectId(reply, gRegistry->Add(monitor_info.owner_->GetPeer()));
@@ -3150,8 +3158,7 @@
CHECK_EQ(self->GetState(), kRunnable);
ScopedThreadSuspension sts(self, kWaitingForDeoptimization);
// We need to suspend mutator threads first.
- Runtime* const runtime = Runtime::Current();
- runtime->GetThreadList()->SuspendAll(__FUNCTION__);
+ ScopedSuspendAll ssa(__FUNCTION__);
const ThreadState old_state = self->SetStateUnsafe(kRunnable);
{
MutexLock mu(self, *Locks::deoptimization_lock_);
@@ -3163,7 +3170,6 @@
deoptimization_requests_.clear();
}
CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
- runtime->GetThreadList()->ResumeAll();
}
static bool IsMethodPossiblyInlined(Thread* self, ArtMethod* m)
@@ -3490,6 +3496,62 @@
return instrumentation->IsDeoptimized(m);
}
+struct NeedsDeoptimizationVisitor : public StackVisitor {
+ public:
+ explicit NeedsDeoptimizationVisitor(Thread* self)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ : StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ needs_deoptimization_(false) {}
+
+ bool VisitFrame() OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ // The visitor is meant to be used when handling exception from compiled code only.
+ CHECK(!IsShadowFrame()) << "We only expect to visit compiled frame: " << PrettyMethod(GetMethod());
+ ArtMethod* method = GetMethod();
+ if (method == nullptr) {
+ // We reach an upcall and don't need to deoptimize this part of the stack (ManagedFragment)
+ // so we can stop the visit.
+ DCHECK(!needs_deoptimization_);
+ return false;
+ }
+ if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) {
+ // We found a compiled frame in the stack but instrumentation is set to interpret
+ // everything: we need to deoptimize.
+ needs_deoptimization_ = true;
+ return false;
+ }
+ if (Runtime::Current()->GetInstrumentation()->IsDeoptimized(method)) {
+ // We found a deoptimized method in the stack.
+ needs_deoptimization_ = true;
+ return false;
+ }
+ return true;
+ }
+
+ bool NeedsDeoptimization() const {
+ return needs_deoptimization_;
+ }
+
+ private:
+ // Do we need to deoptimize the stack?
+ bool needs_deoptimization_;
+
+ DISALLOW_COPY_AND_ASSIGN(NeedsDeoptimizationVisitor);
+};
+
+// Do we need to deoptimize the stack to handle an exception?
+bool Dbg::IsForcedInterpreterNeededForExceptionImpl(Thread* thread) {
+ const SingleStepControl* const ssc = thread->GetSingleStepControl();
+ if (ssc != nullptr) {
+ // We deopt to step into the catch handler.
+ return true;
+ }
+ // Deoptimization is required if at least one method in the stack needs it. However we
+ // skip frames that will be unwound (thus not executed).
+ NeedsDeoptimizationVisitor visitor(thread);
+ visitor.WalkStack(true); // includes upcall.
+ return visitor.NeedsDeoptimization();
+}
+
// Scoped utility class to suspend a thread so that we may do tasks such as walk its stack. Doesn't
// cause suspension if the thread is the current thread.
class ScopedDebuggerThreadSuspension {
@@ -3821,7 +3883,9 @@
if (shorty[i + 1] == 'L') {
// Did we really get an argument of an appropriate reference type?
mirror::Class* parameter_type =
- m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, true);
+ m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_,
+ true /* resolve */,
+ sizeof(void*));
mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error);
if (error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
@@ -4655,13 +4719,9 @@
// Need to acquire the mutator lock before the heap bitmap lock with exclusive access since
// RosAlloc's internal logic doesn't know to release and reacquire the heap bitmap lock.
ScopedThreadSuspension sts(self, kSuspended);
- ThreadList* tl = Runtime::Current()->GetThreadList();
- tl->SuspendAll(__FUNCTION__);
- {
- ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
- space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
- }
- tl->ResumeAll();
+ ScopedSuspendAll ssa(__FUNCTION__);
+ ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
} else if (space->IsBumpPointerSpace()) {
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
context.SetChunkOverhead(0);
@@ -4671,13 +4731,11 @@
heap->IncrementDisableMovingGC(self);
{
ScopedThreadSuspension sts(self, kSuspended);
- ThreadList* tl = Runtime::Current()->GetThreadList();
- tl->SuspendAll(__FUNCTION__);
+ ScopedSuspendAll ssa(__FUNCTION__);
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
context.SetChunkOverhead(0);
space->AsRegionSpace()->Walk(BumpPointerSpaceCallback, &context);
HeapChunkContext::HeapChunkJavaCallback(nullptr, nullptr, 0, &context);
- tl->ResumeAll();
}
heap->DecrementDisableMovingGC(self);
} else {
diff --git a/runtime/debugger.h b/runtime/debugger.h
index a9fa6ce..8278fc6 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -576,6 +576,19 @@
return IsForcedInterpreterNeededForUpcallImpl(thread, m);
}
+ // Indicates whether we need to force the use of interpreter when handling an
+ // exception. This allows to deoptimize the stack and continue execution with
+ // the interpreter.
+ // Note: the interpreter will start by handling the exception when executing
+ // the deoptimized frames.
+ static bool IsForcedInterpreterNeededForException(Thread* thread)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (!IsDebuggerActive()) {
+ return false;
+ }
+ return IsForcedInterpreterNeededForExceptionImpl(thread);
+ }
+
// Single-stepping.
static JDWP::JdwpError ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize size,
JDWP::JdwpStepDepth depth)
@@ -734,6 +747,9 @@
static bool IsForcedInterpreterNeededForUpcallImpl(Thread* thread, ArtMethod* m)
SHARED_REQUIRES(Locks::mutator_lock_);
+ static bool IsForcedInterpreterNeededForExceptionImpl(Thread* thread)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
// Indicates whether the debugger is making requests.
static bool gDebuggerActive;
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index a2af9d0..ae62e2b 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -1228,7 +1228,9 @@
AnnotationValue annotation_value;
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::Class> h_klass(hs.NewHandle(klass));
- Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType()));
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ Handle<mirror::Class> return_type(hs.NewHandle(
+ method->GetReturnType(true /* resolve */, pointer_size)));
if (!ProcessAnnotationValue(h_klass, &annotation, &annotation_value, return_type, kAllObjects)) {
return nullptr;
}
@@ -1491,7 +1493,9 @@
if (annotation_method == nullptr) {
return nullptr;
}
- Handle<mirror::Class> method_return(hs.NewHandle(annotation_method->GetReturnType()));
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ Handle<mirror::Class> method_return(hs.NewHandle(
+ annotation_method->GetReturnType(true /* resolve */, pointer_size)));
AnnotationValue annotation_value;
if (!ProcessAnnotationValue(klass, annotation, &annotation_value, method_return, kAllObjects)) {
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 98d4e59..47e5c12 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -1275,6 +1275,8 @@
// pointer to the OatDexFile it was loaded from. Otherwise oat_dex_file_ is
// null.
const OatDexFile* oat_dex_file_;
+
+ friend class DexFileVerifierTest;
};
struct DexFileReference {
@@ -1459,6 +1461,9 @@
uint32_t GetMethodCodeItemOffset() const {
return method_.code_off_;
}
+ const uint8_t* DataPointer() const {
+ return ptr_pos_;
+ }
const uint8_t* EndDataPointer() const {
CHECK(!HasNext());
return ptr_pos_;
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index eec4983..09416cc 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -16,7 +16,9 @@
#include "dex_file_verifier.h"
+#include <inttypes.h>
#include <zlib.h>
+
#include <memory>
#include "base/stringprintf.h"
@@ -444,66 +446,86 @@
return true;
}
-bool DexFileVerifier::CheckClassDataItemField(uint32_t idx, uint32_t access_flags,
+bool DexFileVerifier::CheckClassDataItemField(uint32_t idx,
+ uint32_t access_flags,
+ uint32_t class_access_flags,
+ uint16_t class_type_index,
bool expect_static) {
+ // Check for overflow.
if (!CheckIndex(idx, header_->field_ids_size_, "class_data_item field_idx")) {
return false;
}
+ // Check that it's the right class.
+ uint16_t my_class_index =
+ (reinterpret_cast<const DexFile::FieldId*>(begin_ + header_->field_ids_off_) + idx)->
+ class_idx_;
+ if (class_type_index != my_class_index) {
+ ErrorStringPrintf("Field's class index unexpected, %" PRIu16 "vs %" PRIu16,
+ my_class_index,
+ class_type_index);
+ return false;
+ }
+
+ // Check that it falls into the right class-data list.
bool is_static = (access_flags & kAccStatic) != 0;
if (UNLIKELY(is_static != expect_static)) {
ErrorStringPrintf("Static/instance field not in expected list");
return false;
}
- if (UNLIKELY((access_flags & ~kAccJavaFlagsMask) != 0)) {
- ErrorStringPrintf("Bad class_data_item field access_flags %x", access_flags);
+ // Check field access flags.
+ std::string error_msg;
+ if (!CheckFieldAccessFlags(access_flags, class_access_flags, &error_msg)) {
+ ErrorStringPrintf("%s", error_msg.c_str());
return false;
}
return true;
}
-bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags,
+bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx,
+ uint32_t access_flags,
+ uint32_t class_access_flags,
+ uint16_t class_type_index,
uint32_t code_offset,
- std::unordered_set<uint32_t>& direct_method_indexes,
+ std::unordered_set<uint32_t>* direct_method_indexes,
bool expect_direct) {
+ DCHECK(direct_method_indexes != nullptr);
+ // Check for overflow.
if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) {
return false;
}
- bool is_direct = (access_flags & (kAccStatic | kAccPrivate | kAccConstructor)) != 0;
- bool expect_code = (access_flags & (kAccNative | kAccAbstract)) == 0;
- bool is_synchronized = (access_flags & kAccSynchronized) != 0;
- bool allow_synchronized = (access_flags & kAccNative) != 0;
-
- if (UNLIKELY(is_direct != expect_direct)) {
- ErrorStringPrintf("Direct/virtual method not in expected list");
+ // Check that it's the right class.
+ uint16_t my_class_index =
+ (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + idx)->
+ class_idx_;
+ if (class_type_index != my_class_index) {
+ ErrorStringPrintf("Method's class index unexpected, %" PRIu16 "vs %" PRIu16,
+ my_class_index,
+ class_type_index);
return false;
}
+ // Check that it's not defined as both direct and virtual.
if (expect_direct) {
- direct_method_indexes.insert(idx);
- } else if (direct_method_indexes.find(idx) != direct_method_indexes.end()) {
+ direct_method_indexes->insert(idx);
+ } else if (direct_method_indexes->find(idx) != direct_method_indexes->end()) {
ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx);
return false;
}
- constexpr uint32_t access_method_mask = kAccJavaFlagsMask | kAccConstructor |
- kAccDeclaredSynchronized;
- if (UNLIKELY(((access_flags & ~access_method_mask) != 0) ||
- (is_synchronized && !allow_synchronized))) {
- ErrorStringPrintf("Bad class_data_item method access_flags %x", access_flags);
- return false;
- }
-
- if (UNLIKELY(expect_code && (code_offset == 0))) {
- ErrorStringPrintf("Unexpected zero value for class_data_item method code_off with access "
- "flags %x", access_flags);
- return false;
- } else if (UNLIKELY(!expect_code && (code_offset != 0))) {
- ErrorStringPrintf("Unexpected non-zero value %x for class_data_item method code_off"
- " with access flags %x", code_offset, access_flags);
+ // Check method access flags.
+ bool has_code = (code_offset != 0);
+ std::string error_msg;
+ if (!CheckMethodAccessFlags(idx,
+ access_flags,
+ class_access_flags,
+ has_code,
+ expect_direct,
+ &error_msg)) {
+ ErrorStringPrintf("%s", error_msg.c_str());
return false;
}
@@ -689,60 +711,185 @@
return true;
}
+bool DexFileVerifier::FindClassFlags(uint32_t index,
+ bool is_field,
+ uint16_t* class_type_index,
+ uint32_t* class_access_flags) {
+ DCHECK(class_type_index != nullptr);
+ DCHECK(class_access_flags != nullptr);
+
+ // First check if the index is valid.
+ if (index >= (is_field ? header_->field_ids_size_ : header_->method_ids_size_)) {
+ return false;
+ }
+
+ // Next get the type index.
+ if (is_field) {
+ *class_type_index =
+ (reinterpret_cast<const DexFile::FieldId*>(begin_ + header_->field_ids_off_) + index)->
+ class_idx_;
+ } else {
+ *class_type_index =
+ (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + index)->
+ class_idx_;
+ }
+
+ // Check if that is valid.
+ if (*class_type_index >= header_->type_ids_size_) {
+ return false;
+ }
+
+ // Now search for the class def. This is basically a specialized version of the DexFile code, as
+ // we should not trust that this is a valid DexFile just yet.
+ const DexFile::ClassDef* class_def_begin =
+ reinterpret_cast<const DexFile::ClassDef*>(begin_ + header_->class_defs_off_);
+ for (size_t i = 0; i < header_->class_defs_size_; ++i) {
+ const DexFile::ClassDef* class_def = class_def_begin + i;
+ if (class_def->class_idx_ == *class_type_index) {
+ *class_access_flags = class_def->access_flags_;
+ return true;
+ }
+ }
+
+ // Didn't find the class-def, not defined here...
+ return false;
+}
+
+bool DexFileVerifier::CheckOrderAndGetClassFlags(bool is_field,
+ const char* type_descr,
+ uint32_t curr_index,
+ uint32_t prev_index,
+ bool* have_class,
+ uint16_t* class_type_index,
+ uint32_t* class_access_flags) {
+ if (curr_index < prev_index) {
+ ErrorStringPrintf("out-of-order %s indexes %" PRIu32 " and %" PRIu32,
+ type_descr,
+ prev_index,
+ curr_index);
+ return false;
+ }
+
+ if (!*have_class) {
+ *have_class = FindClassFlags(curr_index, is_field, class_type_index, class_access_flags);
+ if (!*have_class) {
+ // Should have really found one.
+ ErrorStringPrintf("could not find declaring class for %s index %" PRIu32,
+ type_descr,
+ curr_index);
+ return false;
+ }
+ }
+ return true;
+}
+
+template <bool kStatic>
+bool DexFileVerifier::CheckIntraClassDataItemFields(ClassDataItemIterator* it,
+ bool* have_class,
+ uint16_t* class_type_index,
+ uint32_t* class_access_flags) {
+ DCHECK(it != nullptr);
+ // These calls use the raw access flags to check whether the whole dex field is valid.
+ uint32_t prev_index = 0;
+ for (; kStatic ? it->HasNextStaticField() : it->HasNextInstanceField(); it->Next()) {
+ uint32_t curr_index = it->GetMemberIndex();
+ if (!CheckOrderAndGetClassFlags(true,
+ kStatic ? "static field" : "instance field",
+ curr_index,
+ prev_index,
+ have_class,
+ class_type_index,
+ class_access_flags)) {
+ return false;
+ }
+ prev_index = curr_index;
+
+ if (!CheckClassDataItemField(curr_index,
+ it->GetRawMemberAccessFlags(),
+ *class_access_flags,
+ *class_type_index,
+ kStatic)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+template <bool kDirect>
+bool DexFileVerifier::CheckIntraClassDataItemMethods(
+ ClassDataItemIterator* it,
+ std::unordered_set<uint32_t>* direct_method_indexes,
+ bool* have_class,
+ uint16_t* class_type_index,
+ uint32_t* class_access_flags) {
+ uint32_t prev_index = 0;
+ for (; kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); it->Next()) {
+ uint32_t curr_index = it->GetMemberIndex();
+ if (!CheckOrderAndGetClassFlags(false,
+ kDirect ? "direct method" : "virtual method",
+ curr_index,
+ prev_index,
+ have_class,
+ class_type_index,
+ class_access_flags)) {
+ return false;
+ }
+ prev_index = curr_index;
+
+ if (!CheckClassDataItemMethod(curr_index,
+ it->GetRawMemberAccessFlags(),
+ *class_access_flags,
+ *class_type_index,
+ it->GetMethodCodeItemOffset(),
+ direct_method_indexes,
+ kDirect)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
bool DexFileVerifier::CheckIntraClassDataItem() {
ClassDataItemIterator it(*dex_file_, ptr_);
std::unordered_set<uint32_t> direct_method_indexes;
- // These calls use the raw access flags to check whether the whole dex field is valid.
- uint32_t prev_index = 0;
- for (; it.HasNextStaticField(); it.Next()) {
- uint32_t curr_index = it.GetMemberIndex();
- if (curr_index < prev_index) {
- ErrorStringPrintf("out-of-order static field indexes %d and %d", prev_index, curr_index);
- return false;
- }
- prev_index = curr_index;
- if (!CheckClassDataItemField(curr_index, it.GetRawMemberAccessFlags(), true)) {
- return false;
- }
+ // This code is complicated by the fact that we don't directly know which class this belongs to.
+ // So we need to explicitly search with the first item we find (either field or method), and then,
+ // as the lookup is expensive, cache the result.
+ bool have_class = false;
+ uint16_t class_type_index;
+ uint32_t class_access_flags;
+
+ // Check fields.
+ if (!CheckIntraClassDataItemFields<true>(&it,
+ &have_class,
+ &class_type_index,
+ &class_access_flags)) {
+ return false;
}
- prev_index = 0;
- for (; it.HasNextInstanceField(); it.Next()) {
- uint32_t curr_index = it.GetMemberIndex();
- if (curr_index < prev_index) {
- ErrorStringPrintf("out-of-order instance field indexes %d and %d", prev_index, curr_index);
- return false;
- }
- prev_index = curr_index;
- if (!CheckClassDataItemField(curr_index, it.GetRawMemberAccessFlags(), false)) {
- return false;
- }
+ if (!CheckIntraClassDataItemFields<false>(&it,
+ &have_class,
+ &class_type_index,
+ &class_access_flags)) {
+ return false;
}
- prev_index = 0;
- for (; it.HasNextDirectMethod(); it.Next()) {
- uint32_t curr_index = it.GetMemberIndex();
- if (curr_index < prev_index) {
- ErrorStringPrintf("out-of-order direct method indexes %d and %d", prev_index, curr_index);
- return false;
- }
- prev_index = curr_index;
- if (!CheckClassDataItemMethod(curr_index, it.GetRawMemberAccessFlags(),
- it.GetMethodCodeItemOffset(), direct_method_indexes, true)) {
- return false;
- }
+
+ // Check methods.
+ if (!CheckIntraClassDataItemMethods<true>(&it,
+ &direct_method_indexes,
+ &have_class,
+ &class_type_index,
+ &class_access_flags)) {
+ return false;
}
- prev_index = 0;
- for (; it.HasNextVirtualMethod(); it.Next()) {
- uint32_t curr_index = it.GetMemberIndex();
- if (curr_index < prev_index) {
- ErrorStringPrintf("out-of-order virtual method indexes %d and %d", prev_index, curr_index);
- return false;
- }
- prev_index = curr_index;
- if (!CheckClassDataItemMethod(curr_index, it.GetRawMemberAccessFlags(),
- it.GetMethodCodeItemOffset(), direct_method_indexes, false)) {
- return false;
- }
+ if (!CheckIntraClassDataItemMethods<false>(&it,
+ &direct_method_indexes,
+ &have_class,
+ &class_type_index,
+ &class_access_flags)) {
+ return false;
}
ptr_ = it.EndDataPointer();
@@ -2149,4 +2296,259 @@
va_end(ap);
}
+// Fields and methods may have only one of public/protected/private.
+static bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) {
+ size_t count = (((flags & kAccPublic) == 0) ? 0 : 1) +
+ (((flags & kAccProtected) == 0) ? 0 : 1) +
+ (((flags & kAccPrivate) == 0) ? 0 : 1);
+ return count <= 1;
+}
+
+bool DexFileVerifier::CheckFieldAccessFlags(uint32_t field_access_flags,
+ uint32_t class_access_flags,
+ std::string* error_msg) {
+ // Generally sort out >16-bit flags.
+ if ((field_access_flags & ~kAccJavaFlagsMask) != 0) {
+ *error_msg = StringPrintf("Bad class_data_item field access_flags %x", field_access_flags);
+ return false;
+ }
+
+ // Flags allowed on fields, in general. Other lower-16-bit flags are to be ignored.
+ constexpr uint32_t kFieldAccessFlags = kAccPublic |
+ kAccPrivate |
+ kAccProtected |
+ kAccStatic |
+ kAccFinal |
+ kAccVolatile |
+ kAccTransient |
+ kAccSynthetic |
+ kAccEnum;
+
+ // Fields may have only one of public/protected/final.
+ if (!CheckAtMostOneOfPublicProtectedPrivate(field_access_flags)) {
+ *error_msg = StringPrintf("Field may have only one of public/protected/private, %x",
+ field_access_flags);
+ return false;
+ }
+
+ // Interfaces have a pretty restricted list.
+ if ((class_access_flags & kAccInterface) != 0) {
+ // Interface fields must be public final static.
+ constexpr uint32_t kPublicFinalStatic = kAccPublic | kAccFinal | kAccStatic;
+ if ((field_access_flags & kPublicFinalStatic) != kPublicFinalStatic) {
+ *error_msg = StringPrintf("Interface field is not public final static: %x",
+ field_access_flags);
+ return false;
+ }
+ // Interface fields may be synthetic, but may not have other flags.
+ constexpr uint32_t kDisallowed = ~(kPublicFinalStatic | kAccSynthetic);
+ if ((field_access_flags & kFieldAccessFlags & kDisallowed) != 0) {
+ *error_msg = StringPrintf("Interface field has disallowed flag: %x", field_access_flags);
+ return false;
+ }
+ return true;
+ }
+
+ // Volatile fields may not be final.
+ constexpr uint32_t kVolatileFinal = kAccVolatile | kAccFinal;
+ if ((field_access_flags & kVolatileFinal) == kVolatileFinal) {
+ *error_msg = "Fields may not be volatile and final";
+ return false;
+ }
+
+ return true;
+}
+
+// Try to find the name of the method with the given index. We do not want to rely on DexFile
+// infrastructure at this point, so do it all by hand. begin and header correspond to begin_ and
+// header_ of the DexFileVerifier. str will contain the pointer to the method name on success
+// (flagged by the return value), otherwise error_msg will contain an error string.
+static bool FindMethodName(uint32_t method_index,
+ const uint8_t* begin,
+ const DexFile::Header* header,
+ const char** str,
+ std::string* error_msg) {
+ if (method_index >= header->method_ids_size_) {
+ *error_msg = "Method index not available for method flags verification";
+ return false;
+ }
+ uint32_t string_idx =
+ (reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) +
+ method_index)->name_idx_;
+ if (string_idx >= header->string_ids_size_) {
+ *error_msg = "String index not available for method flags verification";
+ return false;
+ }
+ uint32_t string_off =
+ (reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + string_idx)->
+ string_data_off_;
+ if (string_off >= header->file_size_) {
+ *error_msg = "String offset out of bounds for method flags verification";
+ return false;
+ }
+ const uint8_t* str_data_ptr = begin + string_off;
+ DecodeUnsignedLeb128(&str_data_ptr);
+ *str = reinterpret_cast<const char*>(str_data_ptr);
+ return true;
+}
+
+bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index,
+ uint32_t method_access_flags,
+ uint32_t class_access_flags,
+ bool has_code,
+ bool expect_direct,
+ std::string* error_msg) {
+ // Generally sort out >16-bit flags, except dex knows Constructor and DeclaredSynchronized.
+ constexpr uint32_t kAllMethodFlags =
+ kAccJavaFlagsMask | kAccConstructor | kAccDeclaredSynchronized;
+ if ((method_access_flags & ~kAllMethodFlags) != 0) {
+ *error_msg = StringPrintf("Bad class_data_item method access_flags %x", method_access_flags);
+ return false;
+ }
+
+ // Flags allowed on fields, in general. Other lower-16-bit flags are to be ignored.
+ constexpr uint32_t kMethodAccessFlags = kAccPublic |
+ kAccPrivate |
+ kAccProtected |
+ kAccStatic |
+ kAccFinal |
+ kAccSynthetic |
+ kAccSynchronized |
+ kAccBridge |
+ kAccVarargs |
+ kAccNative |
+ kAccAbstract |
+ kAccStrict;
+
+ // Methods may have only one of public/protected/final.
+ if (!CheckAtMostOneOfPublicProtectedPrivate(method_access_flags)) {
+ *error_msg = StringPrintf("Method may have only one of public/protected/private, %x",
+ method_access_flags);
+ return false;
+ }
+
+ // Try to find the name, to check for constructor properties.
+ const char* str;
+ if (!FindMethodName(method_index, begin_, header_, &str, error_msg)) {
+ return false;
+ }
+ bool is_init_by_name = false;
+ constexpr const char* kInitName = "<init>";
+ size_t str_offset = (reinterpret_cast<const uint8_t*>(str) - begin_);
+ if (header_->file_size_ - str_offset >= sizeof(kInitName)) {
+ is_init_by_name = strcmp(kInitName, str) == 0;
+ }
+ bool is_clinit_by_name = false;
+ constexpr const char* kClinitName = "<clinit>";
+ if (header_->file_size_ - str_offset >= sizeof(kClinitName)) {
+ is_clinit_by_name = strcmp(kClinitName, str) == 0;
+ }
+ bool is_constructor = is_init_by_name || is_clinit_by_name;
+
+ // Only methods named "<clinit>" or "<init>" may be marked constructor. Note: we cannot enforce
+ // the reverse for backwards compatibility reasons.
+ if (((method_access_flags & kAccConstructor) != 0) && !is_constructor) {
+ *error_msg = StringPrintf("Method %" PRIu32 " is marked constructor, but doesn't match name",
+ method_index);
+ return false;
+ }
+ // Check that the static constructor (= static initializer) is named "<clinit>" and that the
+ // instance constructor is called "<init>".
+ if (is_constructor) {
+ bool is_static = (method_access_flags & kAccStatic) != 0;
+ if (is_static ^ is_clinit_by_name) {
+ *error_msg = StringPrintf("Constructor %" PRIu32 " is not flagged correctly wrt/ static.",
+ method_index);
+ return false;
+ }
+ }
+ // Check that static and private methods, as well as constructors, are in the direct methods list,
+ // and other methods in the virtual methods list.
+ bool is_direct = (method_access_flags & (kAccStatic | kAccPrivate)) != 0 || is_constructor;
+ if (is_direct != expect_direct) {
+ *error_msg = StringPrintf("Direct/virtual method %" PRIu32 " not in expected list %d",
+ method_index,
+ expect_direct);
+ return false;
+ }
+
+
+ // From here on out it is easier to mask out the bits we're supposed to ignore.
+ method_access_flags &= kMethodAccessFlags;
+
+ // If there aren't any instructions, make sure that's expected.
+ if (!has_code) {
+ // Only native or abstract methods may not have code.
+ if ((method_access_flags & (kAccNative | kAccAbstract)) == 0) {
+ *error_msg = StringPrintf("Method %" PRIu32 " has no code, but is not marked native or "
+ "abstract",
+ method_index);
+ return false;
+ }
+ // Constructors must always have code.
+ if (is_constructor) {
+ *error_msg = StringPrintf("Constructor %u must not be abstract or native", method_index);
+ return false;
+ }
+ if ((method_access_flags & kAccAbstract) != 0) {
+ // Abstract methods are not allowed to have the following flags.
+ constexpr uint32_t kForbidden =
+ kAccPrivate | kAccStatic | kAccFinal | kAccNative | kAccStrict | kAccSynchronized;
+ if ((method_access_flags & kForbidden) != 0) {
+ *error_msg = StringPrintf("Abstract method %" PRIu32 " has disallowed access flags %x",
+ method_index,
+ method_access_flags);
+ return false;
+ }
+ // Abstract methods must be in an abstract class or interface.
+ if ((class_access_flags & (kAccInterface | kAccAbstract)) == 0) {
+ *error_msg = StringPrintf("Method %" PRIu32 " is abstract, but the declaring class "
+ "is neither abstract nor an interface", method_index);
+ return false;
+ }
+ }
+ // Interfaces are special.
+ if ((class_access_flags & kAccInterface) != 0) {
+ // Interface methods must be public and abstract.
+ if ((method_access_flags & (kAccPublic | kAccAbstract)) != (kAccPublic | kAccAbstract)) {
+ *error_msg = StringPrintf("Interface method %" PRIu32 " is not public and abstract",
+ method_index);
+ return false;
+ }
+ // At this point, we know the method is public and abstract. This means that all the checks
+ // for invalid combinations above applies. In addition, interface methods must not be
+ // protected. This is caught by the check for only-one-of-public-protected-private.
+ }
+ return true;
+ }
+
+ // When there's code, the method must not be native or abstract.
+ if ((method_access_flags & (kAccNative | kAccAbstract)) != 0) {
+ *error_msg = StringPrintf("Method %" PRIu32 " has code, but is marked native or abstract",
+ method_index);
+ return false;
+ }
+
+ // Only the static initializer may have code in an interface.
+ if (((class_access_flags & kAccInterface) != 0) && !is_clinit_by_name) {
+ *error_msg = StringPrintf("Non-clinit interface method %" PRIu32 " should not have code",
+ method_index);
+ return false;
+ }
+
+ // Instance constructors must not be synchronized and a few other flags.
+ if (is_init_by_name) {
+ static constexpr uint32_t kInitAllowed =
+ kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic;
+ if ((method_access_flags & ~kInitAllowed) != 0) {
+ *error_msg = StringPrintf("Constructor %" PRIu32 " flagged inappropriately %x",
+ method_index,
+ method_access_flags);
+ return false;
+ }
+ }
+
+ return true;
+}
+
} // namespace art
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index ccc40d4..4f15357 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -57,16 +57,48 @@
uint32_t ReadUnsignedLittleEndian(uint32_t size);
bool CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item,
uint32_t* handler_offsets, uint32_t handlers_size);
- bool CheckClassDataItemField(uint32_t idx, uint32_t access_flags, bool expect_static);
- bool CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags, uint32_t code_offset,
- std::unordered_set<uint32_t>& direct_method_indexes,
+ bool CheckClassDataItemField(uint32_t idx,
+ uint32_t access_flags,
+ uint32_t class_access_flags,
+ uint16_t class_type_index,
+ bool expect_static);
+ bool CheckClassDataItemMethod(uint32_t idx,
+ uint32_t access_flags,
+ uint32_t class_access_flags,
+ uint16_t class_type_index,
+ uint32_t code_offset,
+ std::unordered_set<uint32_t>* direct_method_indexes,
bool expect_direct);
+ bool CheckOrderAndGetClassFlags(bool is_field,
+ const char* type_descr,
+ uint32_t curr_index,
+ uint32_t prev_index,
+ bool* have_class,
+ uint16_t* class_type_index,
+ uint32_t* class_access_flags);
+
bool CheckPadding(size_t offset, uint32_t aligned_offset);
bool CheckEncodedValue();
bool CheckEncodedArray();
bool CheckEncodedAnnotation();
bool CheckIntraClassDataItem();
+ // Check all fields of the given type from the given iterator. Load the class data from the first
+ // field, if necessary (and return it), or use the given values.
+ template <bool kStatic>
+ bool CheckIntraClassDataItemFields(ClassDataItemIterator* it,
+ bool* have_class,
+ uint16_t* class_type_index,
+ uint32_t* class_access_flags);
+ // Check all methods of the given type from the given iterator. Load the class data from the first
+ // method, if necessary (and return it), or use the given values.
+ template <bool kDirect>
+ bool CheckIntraClassDataItemMethods(ClassDataItemIterator* it,
+ std::unordered_set<uint32_t>* direct_method_indexes,
+ bool* have_class,
+ uint16_t* class_type_index,
+ uint32_t* class_access_flags);
+
bool CheckIntraCodeItem();
bool CheckIntraStringDataItem();
bool CheckIntraDebugInfoItem();
@@ -112,6 +144,31 @@
void ErrorStringPrintf(const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
+ // Retrieve class index and class access flag from the given member. index is the member index,
+ // which is taken as either a field or a method index (as designated by is_field). The result,
+ // if the member and declaring class could be found, is stored in class_type_index and
+ // class_access_flags.
+ // This is an expensive lookup, as we have to find the class-def by type index, which is a
+ // linear search. The output values should thus be cached by the caller.
+ bool FindClassFlags(uint32_t index,
+ bool is_field,
+ uint16_t* class_type_index,
+ uint32_t* class_access_flags);
+
+ // Check validity of the given access flags, interpreted for a field in the context of a class
+ // with the given second access flags.
+ static bool CheckFieldAccessFlags(uint32_t field_access_flags,
+ uint32_t class_access_flags,
+ std::string* error_msg);
+ // Check validity of the given method and access flags, in the context of a class with the given
+ // second access flags.
+ bool CheckMethodAccessFlags(uint32_t method_index,
+ uint32_t method_access_flags,
+ uint32_t class_access_flags,
+ bool has_code,
+ bool expect_direct,
+ std::string* error_msg);
+
const DexFile* const dex_file_;
const uint8_t* const begin_;
const size_t size_;
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 9f1ffec..1b529c9 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -18,18 +18,20 @@
#include "sys/mman.h"
#include "zlib.h"
+#include <functional>
#include <memory>
#include "base/unix_file/fd_file.h"
+#include "base/bit_utils.h"
#include "base/macros.h"
#include "common_runtime_test.h"
+#include "dex_file-inl.h"
+#include "leb128.h"
#include "scoped_thread_state_change.h"
#include "thread-inl.h"
namespace art {
-class DexFileVerifierTest : public CommonRuntimeTest {};
-
static const uint8_t kBase64Map[256] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
@@ -101,6 +103,64 @@
return dst.release();
}
+static void FixUpChecksum(uint8_t* dex_file) {
+ DexFile::Header* header = reinterpret_cast<DexFile::Header*>(dex_file);
+ uint32_t expected_size = header->file_size_;
+ uint32_t adler_checksum = adler32(0L, Z_NULL, 0);
+ const uint32_t non_sum = sizeof(DexFile::Header::magic_) + sizeof(DexFile::Header::checksum_);
+ const uint8_t* non_sum_ptr = dex_file + non_sum;
+ adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum);
+ header->checksum_ = adler_checksum;
+}
+
+// Custom deleter. Necessary to clean up the memory we use (to be able to mutate).
+struct DexFileDeleter {
+ void operator()(DexFile* in) {
+ if (in != nullptr) {
+ delete in->Begin();
+ delete in;
+ }
+ }
+};
+
+using DexFileUniquePtr = std::unique_ptr<DexFile, DexFileDeleter>;
+
+class DexFileVerifierTest : public CommonRuntimeTest {
+ protected:
+ void VerifyModification(const char* dex_file_base64_content,
+ const char* location,
+ std::function<void(DexFile*)> f,
+ const char* expected_error) {
+ DexFileUniquePtr dex_file(WrapAsDexFile(dex_file_base64_content));
+ f(dex_file.get());
+ FixUpChecksum(const_cast<uint8_t*>(dex_file->Begin()));
+
+ std::string error_msg;
+ bool success = DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ location,
+ &error_msg);
+ if (expected_error == nullptr) {
+ EXPECT_TRUE(success) << error_msg;
+ } else {
+ EXPECT_FALSE(success) << "Expected " << expected_error;
+ if (!success) {
+ EXPECT_NE(error_msg.find(expected_error), std::string::npos) << error_msg;
+ }
+ }
+ }
+
+ private:
+ static DexFile* WrapAsDexFile(const char* dex_file_content_in_base_64) {
+ // Decode base64.
+ size_t length;
+ uint8_t* dex_bytes = DecodeBase64(dex_file_content_in_base_64, &length);
+ CHECK(dex_bytes != nullptr);
+ return new DexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr);
+ }
+};
+
static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
const char* location,
std::string* error_msg) {
@@ -133,7 +193,6 @@
return dex_file;
}
-
// For reference.
static const char kGoodTestDex[] =
"ZGV4CjAzNQDrVbyVkxX1HljTznNf95AglkUAhQuFtmKkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAN"
@@ -157,92 +216,1003 @@
ASSERT_TRUE(raw.get() != nullptr) << error_msg;
}
-static void FixUpChecksum(uint8_t* dex_file) {
- DexFile::Header* header = reinterpret_cast<DexFile::Header*>(dex_file);
- uint32_t expected_size = header->file_size_;
- uint32_t adler_checksum = adler32(0L, Z_NULL, 0);
- const uint32_t non_sum = sizeof(DexFile::Header::magic_) + sizeof(DexFile::Header::checksum_);
- const uint8_t* non_sum_ptr = dex_file + non_sum;
- adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum);
- header->checksum_ = adler_checksum;
-}
-
-static std::unique_ptr<const DexFile> FixChecksumAndOpen(uint8_t* bytes, size_t length,
- const char* location,
- std::string* error_msg) {
- // Check data.
- CHECK(bytes != nullptr);
-
- // Fixup of checksum.
- FixUpChecksum(bytes);
-
- // write to provided file
- std::unique_ptr<File> file(OS::CreateEmptyFile(location));
- CHECK(file.get() != nullptr);
- if (!file->WriteFully(bytes, length)) {
- PLOG(FATAL) << "Failed to write base64 as dex file";
- }
- if (file->FlushCloseOrErase() != 0) {
- PLOG(FATAL) << "Could not flush and close test file.";
- }
- file.reset();
-
- // read dex file
- ScopedObjectAccess soa(Thread::Current());
- std::vector<std::unique_ptr<const DexFile>> tmp;
- if (!DexFile::Open(location, location, error_msg, &tmp)) {
- return nullptr;
- }
- EXPECT_EQ(1U, tmp.size());
- std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
- EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
- EXPECT_TRUE(dex_file->IsReadOnly());
- return dex_file;
-}
-
-static bool ModifyAndLoad(const char* dex_file_content, const char* location, size_t offset,
- uint8_t new_val, std::string* error_msg) {
- // Decode base64.
- size_t length;
- std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(dex_file_content, &length));
- CHECK(dex_bytes.get() != nullptr);
-
- // Make modifications.
- dex_bytes.get()[offset] = new_val;
-
- // Fixup and load.
- std::unique_ptr<const DexFile> file(FixChecksumAndOpen(dex_bytes.get(), length, location,
- error_msg));
- return file.get() != nullptr;
-}
-
TEST_F(DexFileVerifierTest, MethodId) {
- {
- // Class error.
- ScratchFile tmp;
- std::string error_msg;
- bool success = !ModifyAndLoad(kGoodTestDex, tmp.GetFilename().c_str(), 220, 0xFFU, &error_msg);
- ASSERT_TRUE(success);
- ASSERT_NE(error_msg.find("inter_method_id_item class_idx"), std::string::npos) << error_msg;
+ // Class idx error.
+ VerifyModification(
+ kGoodTestDex,
+ "method_id_class_idx",
+ [](DexFile* dex_file) {
+ DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
+ method_id->class_idx_ = 0xFF;
+ },
+ "could not find declaring class for direct method index 0");
+
+ // Proto idx error.
+ VerifyModification(
+ kGoodTestDex,
+ "method_id_proto_idx",
+ [](DexFile* dex_file) {
+ DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
+ method_id->proto_idx_ = 0xFF;
+ },
+ "inter_method_id_item proto_idx");
+
+ // Name idx error.
+ VerifyModification(
+ kGoodTestDex,
+ "method_id_name_idx",
+ [](DexFile* dex_file) {
+ DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
+ method_id->name_idx_ = 0xFF;
+ },
+ "String index not available for method flags verification");
+}
+
+// Method flags test class generated from the following smali code. The declared-synchronized
+// flags are there to enforce a 3-byte uLEB128 encoding so we don't have to relayout
+// the code, but we need to remove them before doing tests.
+//
+// .class public LMethodFlags;
+// .super Ljava/lang/Object;
+//
+// .method public static constructor <clinit>()V
+// .registers 1
+// return-void
+// .end method
+//
+// .method public constructor <init>()V
+// .registers 1
+// return-void
+// .end method
+//
+// .method private declared-synchronized foo()V
+// .registers 1
+// return-void
+// .end method
+//
+// .method public declared-synchronized bar()V
+// .registers 1
+// return-void
+// .end method
+
+static const char kMethodFlagsTestDex[] =
+ "ZGV4CjAzNQCyOQrJaDBwiIWv5MIuYKXhxlLLsQcx5SwgAgAAcAAAAHhWNBIAAAAAAAAAAJgBAAAH"
+ "AAAAcAAAAAMAAACMAAAAAQAAAJgAAAAAAAAAAAAAAAQAAACkAAAAAQAAAMQAAAA8AQAA5AAAAOQA"
+ "AADuAAAA9gAAAAUBAAAZAQAAHAEAACEBAAACAAAAAwAAAAQAAAAEAAAAAgAAAAAAAAAAAAAAAAAA"
+ "AAAAAAABAAAAAAAAAAUAAAAAAAAABgAAAAAAAAABAAAAAQAAAAAAAAD/////AAAAAHoBAAAAAAAA"
+ "CDxjbGluaXQ+AAY8aW5pdD4ADUxNZXRob2RGbGFnczsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgAD"
+ "YmFyAANmb28AAAAAAAAAAQAAAAAAAAAAAAAAAQAAAA4AAAABAAEAAAAAAAAAAAABAAAADgAAAAEA"
+ "AQAAAAAAAAAAAAEAAAAOAAAAAQABAAAAAAAAAAAAAQAAAA4AAAADAQCJgASsAgGBgATAAgKCgAjU"
+ "AgKBgAjoAgAACwAAAAAAAAABAAAAAAAAAAEAAAAHAAAAcAAAAAIAAAADAAAAjAAAAAMAAAABAAAA"
+ "mAAAAAUAAAAEAAAApAAAAAYAAAABAAAAxAAAAAIgAAAHAAAA5AAAAAMQAAABAAAAKAEAAAEgAAAE"
+ "AAAALAEAAAAgAAABAAAAegEAAAAQAAABAAAAmAEAAA==";
+
+// Find the method data for the first method with the given name (from class 0). Note: the pointer
+// is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index
+// delta.
+static const uint8_t* FindMethodData(const DexFile* dex_file, const char* name) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(0);
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+
+ ClassDataItemIterator it(*dex_file, class_data);
+
+ const uint8_t* trailing = class_data;
+ // Need to manually decode the four entries. DataPointer() doesn't work for this, as the first
+ // element has already been loaded into the iterator.
+ DecodeUnsignedLeb128(&trailing);
+ DecodeUnsignedLeb128(&trailing);
+ DecodeUnsignedLeb128(&trailing);
+ DecodeUnsignedLeb128(&trailing);
+
+ // Skip all fields.
+ while (it.HasNextStaticField() || it.HasNextInstanceField()) {
+ trailing = it.DataPointer();
+ it.Next();
}
- {
- // Proto error.
- ScratchFile tmp;
- std::string error_msg;
- bool success = !ModifyAndLoad(kGoodTestDex, tmp.GetFilename().c_str(), 222, 0xFFU, &error_msg);
- ASSERT_TRUE(success);
- ASSERT_NE(error_msg.find("inter_method_id_item proto_idx"), std::string::npos) << error_msg;
+ while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) {
+ uint32_t method_index = it.GetMemberIndex();
+ uint32_t name_index = dex_file->GetMethodId(method_index).name_idx_;
+ const DexFile::StringId& string_id = dex_file->GetStringId(name_index);
+ const char* str = dex_file->GetStringData(string_id);
+ if (strcmp(name, str) == 0) {
+ DecodeUnsignedLeb128(&trailing);
+ return trailing;
+ }
+
+ trailing = it.DataPointer();
+ it.Next();
}
- {
- // Name error.
- ScratchFile tmp;
- std::string error_msg;
- bool success = !ModifyAndLoad(kGoodTestDex, tmp.GetFilename().c_str(), 224, 0xFFU, &error_msg);
- ASSERT_TRUE(success);
- ASSERT_NE(error_msg.find("inter_method_id_item name_idx"), std::string::npos) << error_msg;
+ return nullptr;
+}
+
+// Set the method flags to the given value.
+static void SetMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) {
+ uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method));
+ CHECK(method_flags_ptr != nullptr) << method;
+
+ // Unroll this, as we only have three bytes, anyways.
+ uint8_t base1 = static_cast<uint8_t>(mask & 0x7F);
+ *(method_flags_ptr++) = (base1 | 0x80);
+ mask >>= 7;
+
+ uint8_t base2 = static_cast<uint8_t>(mask & 0x7F);
+ *(method_flags_ptr++) = (base2 | 0x80);
+ mask >>= 7;
+
+ uint8_t base3 = static_cast<uint8_t>(mask & 0x7F);
+ *method_flags_ptr = base3;
+}
+
+static uint32_t GetMethodFlags(DexFile* dex_file, const char* method) {
+ const uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method));
+ CHECK(method_flags_ptr != nullptr) << method;
+ return DecodeUnsignedLeb128(&method_flags_ptr);
+}
+
+// Apply the given mask to method flags.
+static void ApplyMaskToMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) {
+ uint32_t value = GetMethodFlags(dex_file, method);
+ value &= mask;
+ SetMethodFlags(dex_file, method, value);
+}
+
+// Apply the given mask to method flags.
+static void OrMaskToMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) {
+ uint32_t value = GetMethodFlags(dex_file, method);
+ value |= mask;
+ SetMethodFlags(dex_file, method, value);
+}
+
+// Set code_off to 0 for the method.
+static void RemoveCode(DexFile* dex_file, const char* method) {
+ const uint8_t* ptr = FindMethodData(dex_file, method);
+ // Next is flags, pass.
+ DecodeUnsignedLeb128(&ptr);
+
+ // Figure out how many bytes the code_off is.
+ const uint8_t* tmp = ptr;
+ DecodeUnsignedLeb128(&tmp);
+ size_t bytes = tmp - ptr;
+
+ uint8_t* mod = const_cast<uint8_t*>(ptr);
+ for (size_t i = 1; i < bytes; ++i) {
+ *(mod++) = 0x80;
}
+ *mod = 0x00;
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsBase) {
+ // Check that it's OK when the wrong declared-synchronized flag is removed from "foo."
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_ok",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+ },
+ nullptr);
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsConstructors) {
+ // Make sure we still accept constructors without their flags.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_missing_constructor_tag_ok",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccConstructor);
+ ApplyMaskToMethodFlags(dex_file, "<clinit>", ~kAccConstructor);
+ },
+ nullptr);
+
+ constexpr const char* kConstructors[] = { "<clinit>", "<init>"};
+ for (size_t i = 0; i < 2; ++i) {
+ // Constructor with code marked native.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_constructor_native",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative);
+ },
+ "has code, but is marked native or abstract");
+ // Constructor with code marked abstract.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_constructor_abstract",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract);
+ },
+ "has code, but is marked native or abstract");
+ // Constructor as-is without code.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_constructor_nocode",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ RemoveCode(dex_file, kConstructors[i]);
+ },
+ "has no code, but is not marked native or abstract");
+ // Constructor without code marked native.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_constructor_native_nocode",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative);
+ RemoveCode(dex_file, kConstructors[i]);
+ },
+ "must not be abstract or native");
+ // Constructor without code marked abstract.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_constructor_abstract_nocode",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract);
+ RemoveCode(dex_file, kConstructors[i]);
+ },
+ "must not be abstract or native");
+ }
+ // <init> may only have (modulo ignored):
+ // kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic
+ static constexpr uint32_t kInitAllowed[] = {
+ 0,
+ kAccPrivate,
+ kAccProtected,
+ kAccPublic,
+ kAccStrict,
+ kAccVarargs,
+ kAccSynthetic
+ };
+ for (size_t i = 0; i < arraysize(kInitAllowed); ++i) {
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "init_allowed_flags",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "<init>", kInitAllowed[i]);
+ },
+ nullptr);
+ }
+ // Only one of public-private-protected.
+ for (size_t i = 1; i < 8; ++i) {
+ if (POPCOUNT(i) < 2) {
+ continue;
+ }
+ // Technically the flags match, but just be defensive here.
+ uint32_t mask = ((i & 1) != 0 ? kAccPrivate : 0) |
+ ((i & 2) != 0 ? kAccProtected : 0) |
+ ((i & 4) != 0 ? kAccPublic : 0);
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "init_one_of_ppp",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "<init>", mask);
+ },
+ "Method may have only one of public/protected/private");
+ }
+ // <init> doesn't allow
+ // kAccStatic | kAccFinal | kAccSynchronized | kAccBridge
+ // Need to handle static separately as it has its own error message.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "init_not_allowed_flags",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "<init>", kAccStatic);
+ },
+ "Constructor 1 is not flagged correctly wrt/ static");
+ static constexpr uint32_t kInitNotAllowed[] = {
+ kAccFinal,
+ kAccSynchronized,
+ kAccBridge
+ };
+ for (size_t i = 0; i < arraysize(kInitNotAllowed); ++i) {
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "init_not_allowed_flags",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "<init>", kInitNotAllowed[i]);
+ },
+ "Constructor 1 flagged inappropriately");
+ }
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsMethods) {
+ constexpr const char* kMethods[] = { "foo", "bar"};
+ for (size_t i = 0; i < arraysize(kMethods); ++i) {
+ // Make sure we reject non-constructors marked as constructors.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_non_constructor",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kMethods[i], kAccConstructor);
+ },
+ "is marked constructor, but doesn't match name");
+
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_native_with_code",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kMethods[i], kAccNative);
+ },
+ "has code, but is marked native or abstract");
+
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_abstract_with_code",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract);
+ },
+ "has code, but is marked native or abstract");
+
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_non_abstract_native_no_code",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ RemoveCode(dex_file, kMethods[i]);
+ },
+ "has no code, but is not marked native or abstract");
+
+ // Abstract methods may not have the following flags.
+ constexpr uint32_t kAbstractDisallowed[] = {
+ kAccPrivate,
+ kAccStatic,
+ kAccFinal,
+ kAccNative,
+ kAccStrict,
+ kAccSynchronized,
+ };
+ for (size_t j = 0; j < arraysize(kAbstractDisallowed); ++j) {
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_abstract_and_disallowed_no_code",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ RemoveCode(dex_file, kMethods[i]);
+
+ // Can't check private and static with foo, as it's in the virtual list and gives a
+ // different error.
+ if (((GetMethodFlags(dex_file, kMethods[i]) & kAccPublic) != 0) &&
+ ((kAbstractDisallowed[j] & (kAccPrivate | kAccStatic)) != 0)) {
+ // Use another breaking flag.
+ OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAccFinal);
+ } else {
+ OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAbstractDisallowed[j]);
+ }
+ },
+ "has disallowed access flags");
+ }
+
+ // Only one of public-private-protected.
+ for (size_t j = 1; j < 8; ++j) {
+ if (POPCOUNT(j) < 2) {
+ continue;
+ }
+ // Technically the flags match, but just be defensive here.
+ uint32_t mask = ((j & 1) != 0 ? kAccPrivate : 0) |
+ ((j & 2) != 0 ? kAccProtected : 0) |
+ ((j & 4) != 0 ? kAccPublic : 0);
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_one_of_ppp",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, kMethods[i], ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, kMethods[i], mask);
+ },
+ "Method may have only one of public/protected/private");
+ }
+ }
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsIgnoredOK) {
+ constexpr const char* kMethods[] = { "<clinit>", "<init>", "foo", "bar"};
+ for (size_t i = 0; i < arraysize(kMethods); ++i) {
+ // All interesting method flags, other flags are to be ignored.
+ constexpr uint32_t kAllMethodFlags =
+ kAccPublic |
+ kAccPrivate |
+ kAccProtected |
+ kAccStatic |
+ kAccFinal |
+ kAccSynchronized |
+ kAccBridge |
+ kAccVarargs |
+ kAccNative |
+ kAccAbstract |
+ kAccStrict |
+ kAccSynthetic;
+ constexpr uint32_t kIgnoredMask = ~kAllMethodFlags & 0xFFFF;
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_ignored",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kMethods[i], kIgnoredMask);
+ },
+ nullptr);
+ }
+}
+
+// Set of dex files for interface method tests. As it's not as easy to mutate method names, it's
+// just easier to break up bad cases.
+
+// Interface with an instance constructor.
+//
+// .class public interface LInterfaceMethodFlags;
+// .super Ljava/lang/Object;
+//
+// .method public static constructor <clinit>()V
+// .registers 1
+// return-void
+// .end method
+//
+// .method public constructor <init>()V
+// .registers 1
+// return-void
+// .end method
+static const char kMethodFlagsInterfaceWithInit[] =
+ "ZGV4CjAzNQDRNt+hZ6X3I+xe66iVlCW7h9I38HmN4SvUAQAAcAAAAHhWNBIAAAAAAAAAAEwBAAAF"
+ "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAAAIAQAAzAAAAMwA"
+ "AADWAAAA3gAAAPYAAAAKAQAAAgAAAAMAAAAEAAAABAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAQAA"
+ "AAAAAAABAgAAAQAAAAAAAAD/////AAAAADoBAAAAAAAACDxjbGluaXQ+AAY8aW5pdD4AFkxJbnRl"
+ "cmZhY2VNZXRob2RGbGFnczsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgAAAAAAAAAAAQAAAAAAAAAA"
+ "AAAAAQAAAA4AAAABAAEAAAAAAAAAAAABAAAADgAAAAIAAImABJQCAYGABKgCAAALAAAAAAAAAAEA"
+ "AAAAAAAAAQAAAAUAAABwAAAAAgAAAAMAAACEAAAAAwAAAAEAAACQAAAABQAAAAIAAACcAAAABgAA"
+ "AAEAAACsAAAAAiAAAAUAAADMAAAAAxAAAAEAAAAQAQAAASAAAAIAAAAUAQAAACAAAAEAAAA6AQAA"
+ "ABAAAAEAAABMAQAA";
+
+// Standard interface. Use declared-synchronized again for 3B encoding.
+//
+// .class public interface LInterfaceMethodFlags;
+// .super Ljava/lang/Object;
+//
+// .method public static constructor <clinit>()V
+// .registers 1
+// return-void
+// .end method
+//
+// .method public abstract declared-synchronized foo()V
+// .end method
+static const char kMethodFlagsInterface[] =
+ "ZGV4CjAzNQCOM0odZ5bws1d9GSmumXaK5iE/7XxFpOm8AQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF"
+ "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADwAAAAzAAAAMwA"
+ "AADWAAAA7gAAAAIBAAAFAQAAAQAAAAIAAAADAAAAAwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAABAAA"
+ "AAAAAAABAgAAAQAAAAAAAAD/////AAAAACIBAAAAAAAACDxjbGluaXQ+ABZMSW50ZXJmYWNlTWV0"
+ "aG9kRmxhZ3M7ABJMamF2YS9sYW5nL09iamVjdDsAAVYAA2ZvbwAAAAAAAAABAAAAAAAAAAAAAAAB"
+ "AAAADgAAAAEBAImABJACAYGICAAAAAALAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAMA"
+ "AACEAAAAAwAAAAEAAACQAAAABQAAAAIAAACcAAAABgAAAAEAAACsAAAAAiAAAAUAAADMAAAAAxAA"
+ "AAEAAAAMAQAAASAAAAEAAAAQAQAAACAAAAEAAAAiAQAAABAAAAEAAAA0AQAA";
+
+// To simplify generation of interesting "sub-states" of src_value, allow a "simple" mask to apply
+// to a src_value, such that mask bit 0 applies to the lowest set bit in src_value, and so on.
+static uint32_t ApplyMaskShifted(uint32_t src_value, uint32_t mask) {
+ uint32_t result = 0;
+ uint32_t mask_index = 0;
+ while (src_value != 0) {
+ uint32_t index = CTZ(src_value);
+ if (((src_value & (1 << index)) != 0) &&
+ ((mask & (1 << mask_index)) != 0)) {
+ result |= (1 << index);
+ }
+ src_value &= ~(1 << index);
+ mask_index++;
+ }
+ return result;
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsInterfaces) {
+ // Reject interface with <init>.
+ VerifyModification(
+ kMethodFlagsInterfaceWithInit,
+ "method_flags_interface_with_init",
+ [](DexFile* dex_file ATTRIBUTE_UNUSED) {},
+ "Non-clinit interface method 1 should not have code");
+
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_ok",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ },
+ nullptr);
+
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_non_public",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ },
+ "Interface method 1 is not public and abstract");
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_non_abstract",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccAbstract);
+ },
+ "Method 1 has no code, but is not marked native or abstract");
+
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_static",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, "foo", kAccStatic);
+ },
+ "Direct/virtual method 1 not in expected list 0");
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_private",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "foo", kAccPrivate);
+ },
+ "Direct/virtual method 1 not in expected list 0");
+
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_non_public",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ },
+ "Interface method 1 is not public and abstract");
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_protected",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "foo", kAccProtected);
+ },
+ "Interface method 1 is not public and abstract");
+
+ constexpr uint32_t kAllMethodFlags =
+ kAccPublic |
+ kAccPrivate |
+ kAccProtected |
+ kAccStatic |
+ kAccFinal |
+ kAccSynchronized |
+ kAccBridge |
+ kAccVarargs |
+ kAccNative |
+ kAccAbstract |
+ kAccStrict |
+ kAccSynthetic;
+ constexpr uint32_t kInterfaceMethodFlags =
+ kAccPublic | kAccAbstract | kAccVarargs | kAccBridge | kAccSynthetic;
+ constexpr uint32_t kInterfaceDisallowed = kAllMethodFlags &
+ ~kInterfaceMethodFlags &
+ // Already tested, needed to be separate.
+ ~kAccStatic &
+ ~kAccPrivate &
+ ~kAccProtected;
+ static_assert(kInterfaceDisallowed != 0, "There should be disallowed flags.");
+
+ uint32_t bits = POPCOUNT(kInterfaceDisallowed);
+ for (uint32_t i = 1; i < (1u << bits); ++i) {
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_non_abstract",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i);
+ if ((mask & kAccProtected) != 0) {
+ mask &= ~kAccProtected;
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ }
+ OrMaskToMethodFlags(dex_file, "foo", mask);
+ },
+ "Abstract method 1 has disallowed access flags");
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+
+// Field flags.
+
+// Find the method data for the first method with the given name (from class 0). Note: the pointer
+// is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index
+// delta.
+static const uint8_t* FindFieldData(const DexFile* dex_file, const char* name) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(0);
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+
+ ClassDataItemIterator it(*dex_file, class_data);
+
+ const uint8_t* trailing = class_data;
+ // Need to manually decode the four entries. DataPointer() doesn't work for this, as the first
+ // element has already been loaded into the iterator.
+ DecodeUnsignedLeb128(&trailing);
+ DecodeUnsignedLeb128(&trailing);
+ DecodeUnsignedLeb128(&trailing);
+ DecodeUnsignedLeb128(&trailing);
+
+ while (it.HasNextStaticField() || it.HasNextInstanceField()) {
+ uint32_t field_index = it.GetMemberIndex();
+ uint32_t name_index = dex_file->GetFieldId(field_index).name_idx_;
+ const DexFile::StringId& string_id = dex_file->GetStringId(name_index);
+ const char* str = dex_file->GetStringData(string_id);
+ if (strcmp(name, str) == 0) {
+ DecodeUnsignedLeb128(&trailing);
+ return trailing;
+ }
+
+ trailing = it.DataPointer();
+ it.Next();
+ }
+
+ return nullptr;
+}
+
+// Set the method flags to the given value.
+static void SetFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) {
+ uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field));
+ CHECK(field_flags_ptr != nullptr) << field;
+
+ // Unroll this, as we only have three bytes, anyways.
+ uint8_t base1 = static_cast<uint8_t>(mask & 0x7F);
+ *(field_flags_ptr++) = (base1 | 0x80);
+ mask >>= 7;
+
+ uint8_t base2 = static_cast<uint8_t>(mask & 0x7F);
+ *(field_flags_ptr++) = (base2 | 0x80);
+ mask >>= 7;
+
+ uint8_t base3 = static_cast<uint8_t>(mask & 0x7F);
+ *field_flags_ptr = base3;
+}
+
+static uint32_t GetFieldFlags(DexFile* dex_file, const char* field) {
+ const uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field));
+ CHECK(field_flags_ptr != nullptr) << field;
+ return DecodeUnsignedLeb128(&field_flags_ptr);
+}
+
+// Apply the given mask to method flags.
+static void ApplyMaskToFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) {
+ uint32_t value = GetFieldFlags(dex_file, field);
+ value &= mask;
+ SetFieldFlags(dex_file, field, value);
+}
+
+// Apply the given mask to method flags.
+static void OrMaskToFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) {
+ uint32_t value = GetFieldFlags(dex_file, field);
+ value |= mask;
+ SetFieldFlags(dex_file, field, value);
+}
+
+// Standard class. Use declared-synchronized again for 3B encoding.
+//
+// .class public LFieldFlags;
+// .super Ljava/lang/Object;
+//
+// .field declared-synchronized public foo:I
+//
+// .field declared-synchronized public static bar:I
+
+static const char kFieldFlagsTestDex[] =
+ "ZGV4CjAzNQBtLw7hydbfv4TdXidZyzAB70W7w3vnYJRwAQAAcAAAAHhWNBIAAAAAAAAAAAABAAAF"
+ "AAAAcAAAAAMAAACEAAAAAAAAAAAAAAACAAAAkAAAAAAAAAAAAAAAAQAAAKAAAACwAAAAwAAAAMAA"
+ "AADDAAAA0QAAAOUAAADqAAAAAAAAAAEAAAACAAAAAQAAAAMAAAABAAAABAAAAAEAAAABAAAAAgAA"
+ "AAAAAAD/////AAAAAPQAAAAAAAAAAUkADExGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7"
+ "AANiYXIAA2ZvbwAAAAAAAAEBAAAAiYAIAYGACAkAAAAAAAAAAQAAAAAAAAABAAAABQAAAHAAAAAC"
+ "AAAAAwAAAIQAAAAEAAAAAgAAAJAAAAAGAAAAAQAAAKAAAAACIAAABQAAAMAAAAADEAAAAQAAAPAA"
+ "AAAAIAAAAQAAAPQAAAAAEAAAAQAAAAABAAA=";
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsBase) {
+ // Check that it's OK when the wrong declared-synchronized flag is removed from "foo."
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_ok",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+ },
+ nullptr);
+}
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsWrongList) {
+ // Mark the field so that it should appear in the opposite list (instance vs static).
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_wrong_list",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToFieldFlags(dex_file, "foo", kAccStatic);
+ },
+ "Static/instance field not in expected list");
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_wrong_list",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccStatic);
+ },
+ "Static/instance field not in expected list");
+}
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsPPP) {
+ static const char* kFields[] = { "foo", "bar" };
+ for (size_t i = 0; i < arraysize(kFields); ++i) {
+ // Should be OK to remove public.
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_non_public",
+ [&](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic);
+ },
+ nullptr);
+ constexpr uint32_t kAccFlags = kAccPublic | kAccPrivate | kAccProtected;
+ uint32_t bits = POPCOUNT(kAccFlags);
+ for (uint32_t j = 1; j < (1u << bits); ++j) {
+ if (POPCOUNT(j) < 2) {
+ continue;
+ }
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_ppp",
+ [&](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic);
+ uint32_t mask = ApplyMaskShifted(kAccFlags, j);
+ OrMaskToFieldFlags(dex_file, kFields[i], mask);
+ },
+ "Field may have only one of public/protected/private");
+ }
+ }
+}
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsIgnoredOK) {
+ constexpr const char* kFields[] = { "foo", "bar"};
+ for (size_t i = 0; i < arraysize(kFields); ++i) {
+ // All interesting method flags, other flags are to be ignored.
+ constexpr uint32_t kAllFieldFlags =
+ kAccPublic |
+ kAccPrivate |
+ kAccProtected |
+ kAccStatic |
+ kAccFinal |
+ kAccVolatile |
+ kAccTransient |
+ kAccSynthetic |
+ kAccEnum;
+ constexpr uint32_t kIgnoredMask = ~kAllFieldFlags & 0xFFFF;
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_ignored",
+ [&](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToFieldFlags(dex_file, kFields[i], kIgnoredMask);
+ },
+ nullptr);
+ }
+}
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsVolatileFinal) {
+ constexpr const char* kFields[] = { "foo", "bar"};
+ for (size_t i = 0; i < arraysize(kFields); ++i) {
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_final_and_volatile",
+ [&](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToFieldFlags(dex_file, kFields[i], kAccVolatile | kAccFinal);
+ },
+ "Fields may not be volatile and final");
+ }
+}
+
+// Standard interface. Needs to be separate from class as interfaces do not allow instance fields.
+// Use declared-synchronized again for 3B encoding.
+//
+// .class public interface LInterfaceFieldFlags;
+// .super Ljava/lang/Object;
+//
+// .field declared-synchronized public static final foo:I
+
+static const char kFieldFlagsInterfaceTestDex[] =
+ "ZGV4CjAzNQCVMHfEimR1zZPk6hl6O9GPAYqkl3u0umFkAQAAcAAAAHhWNBIAAAAAAAAAAPQAAAAE"
+ "AAAAcAAAAAMAAACAAAAAAAAAAAAAAAABAAAAjAAAAAAAAAAAAAAAAQAAAJQAAACwAAAAtAAAALQA"
+ "AAC3AAAAzgAAAOIAAAAAAAAAAQAAAAIAAAABAAAAAwAAAAEAAAABAgAAAgAAAAAAAAD/////AAAA"
+ "AOwAAAAAAAAAAUkAFUxJbnRlcmZhY2VGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7AANm"
+ "b28AAAAAAAABAAAAAJmACAkAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAAAwAAAIAAAAAE"
+ "AAAAAQAAAIwAAAAGAAAAAQAAAJQAAAACIAAABAAAALQAAAADEAAAAQAAAOgAAAAAIAAAAQAAAOwA"
+ "AAAAEAAAAQAAAPQAAAA=";
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsInterface) {
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ },
+ nullptr);
+
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_non_public",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ },
+ "Interface field is not public final static");
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_non_final",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccFinal);
+ },
+ "Interface field is not public final static");
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_protected",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToFieldFlags(dex_file, "foo", kAccProtected);
+ },
+ "Interface field is not public final static");
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_private",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToFieldFlags(dex_file, "foo", kAccPrivate);
+ },
+ "Interface field is not public final static");
+
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_synthetic",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ OrMaskToFieldFlags(dex_file, "foo", kAccSynthetic);
+ },
+ nullptr);
+
+ constexpr uint32_t kAllFieldFlags =
+ kAccPublic |
+ kAccPrivate |
+ kAccProtected |
+ kAccStatic |
+ kAccFinal |
+ kAccVolatile |
+ kAccTransient |
+ kAccSynthetic |
+ kAccEnum;
+ constexpr uint32_t kInterfaceFieldFlags = kAccPublic | kAccStatic | kAccFinal | kAccSynthetic;
+ constexpr uint32_t kInterfaceDisallowed = kAllFieldFlags &
+ ~kInterfaceFieldFlags &
+ ~kAccProtected &
+ ~kAccPrivate;
+ static_assert(kInterfaceDisallowed != 0, "There should be disallowed flags.");
+
+ uint32_t bits = POPCOUNT(kInterfaceDisallowed);
+ for (uint32_t i = 1; i < (1u << bits); ++i) {
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_disallowed",
+ [&](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i);
+ if ((mask & kAccProtected) != 0) {
+ mask &= ~kAccProtected;
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ }
+ OrMaskToFieldFlags(dex_file, "foo", mask);
+ },
+ "Interface field has disallowed flag");
+ }
+}
+
+// Standard bad interface. Needs to be separate from class as interfaces do not allow instance
+// fields. Use declared-synchronized again for 3B encoding.
+//
+// .class public interface LInterfaceFieldFlags;
+// .super Ljava/lang/Object;
+//
+// .field declared-synchronized public final foo:I
+
+static const char kFieldFlagsInterfaceBadTestDex[] =
+ "ZGV4CjAzNQByMUnqYKHBkUpvvNp+9CnZ2VyDkKnRN6VkAQAAcAAAAHhWNBIAAAAAAAAAAPQAAAAE"
+ "AAAAcAAAAAMAAACAAAAAAAAAAAAAAAABAAAAjAAAAAAAAAAAAAAAAQAAAJQAAACwAAAAtAAAALQA"
+ "AAC3AAAAzgAAAOIAAAAAAAAAAQAAAAIAAAABAAAAAwAAAAEAAAABAgAAAgAAAAAAAAD/////AAAA"
+ "AOwAAAAAAAAAAUkAFUxJbnRlcmZhY2VGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7AANm"
+ "b28AAAAAAAAAAQAAAJGACAkAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAAAwAAAIAAAAAE"
+ "AAAAAQAAAIwAAAAGAAAAAQAAAJQAAAACIAAABAAAALQAAAADEAAAAQAAAOgAAAAAIAAAAQAAAOwA"
+ "AAAAEAAAAQAAAPQAAAA=";
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsInterfaceNonStatic) {
+ VerifyModification(
+ kFieldFlagsInterfaceBadTestDex,
+ "field_flags_interface_non_static",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ },
+ "Interface field is not public final static");
}
// Generated from:
@@ -305,15 +1275,14 @@
ASSERT_TRUE(raw.get() != nullptr) << error_msg;
}
- {
- // Modify the debug information entry.
- ScratchFile tmp;
- std::string error_msg;
- bool success = !ModifyAndLoad(kDebugInfoTestDex, tmp.GetFilename().c_str(), 416, 0x14U,
- &error_msg);
- ASSERT_TRUE(success);
- ASSERT_NE(error_msg.find("DBG_START_LOCAL type_idx"), std::string::npos) << error_msg;
- }
+ // Modify the debug information entry.
+ VerifyModification(
+ kDebugInfoTestDex,
+ "debug_start_type_idx",
+ [](DexFile* dex_file) {
+ *(const_cast<uint8_t*>(dex_file->Begin()) + 416) = 0x14U;
+ },
+ "DBG_START_LOCAL type_idx");
}
} // namespace art
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 66e88ba..cc3eefe 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -120,9 +120,11 @@
inline mirror::Class* CheckObjectAlloc(uint32_t type_idx,
ArtMethod* method,
Thread* self, bool* slow_path) {
- mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ size_t pointer_size = class_linker->GetImagePointerSize();
+ mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size);
if (UNLIKELY(klass == nullptr)) {
- klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
+ klass = class_linker->ResolveType(type_idx, method);
*slow_path = true;
if (klass == nullptr) {
DCHECK(self->IsExceptionPending());
@@ -258,9 +260,11 @@
*slow_path = true;
return nullptr; // Failure
}
- mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ size_t pointer_size = class_linker->GetImagePointerSize();
+ mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size);
if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve
- klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
+ klass = class_linker->ResolveType(type_idx, method);
*slow_path = true;
if (klass == nullptr) { // Error
DCHECK(Thread::Current()->IsExceptionPending());
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index eaf33f6..94aced2 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -43,9 +43,11 @@
ThrowNegativeArraySizeException(component_count);
return nullptr; // Failure
}
- mirror::Class* klass = referrer->GetDexCacheResolvedType<false>(type_idx);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ size_t pointer_size = class_linker->GetImagePointerSize();
+ mirror::Class* klass = referrer->GetDexCacheResolvedType<false>(type_idx, pointer_size);
if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve
- klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, referrer);
+ klass = class_linker->ResolveType(type_idx, referrer);
if (klass == nullptr) { // Error
DCHECK(self->IsExceptionPending());
return nullptr; // Failure
@@ -214,7 +216,8 @@
return;
}
// Make sure that the result is an instance of the type this method was expected to return.
- mirror::Class* return_type = self->GetCurrentMethod(nullptr)->GetReturnType();
+ mirror::Class* return_type = self->GetCurrentMethod(nullptr)->GetReturnType(true /* resolve */,
+ sizeof(void*));
if (!o->InstanceOf(return_type)) {
Runtime::Current()->GetJavaVM()->JniAbortF(nullptr,
@@ -277,7 +280,9 @@
StackHandleScope<1> hs(soa.Self());
auto h_interface_method(hs.NewHandle(soa.Decode<mirror::Method*>(interface_method_jobj)));
// This can cause thread suspension.
- mirror::Class* result_type = h_interface_method->GetArtMethod()->GetReturnType();
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ mirror::Class* result_type =
+ h_interface_method->GetArtMethod()->GetReturnType(true /* resolve */, pointer_size);
mirror::Object* result_ref = soa.Decode<mirror::Object*>(result);
JValue result_unboxed;
if (!UnboxPrimitiveForResult(result_ref, result_type, &result_unboxed)) {
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 9311791..28c62a8 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -33,7 +33,7 @@
SHARED_REQUIRES(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
if (kUseTlabFastPath && !instrumented_bool && allocator_type == gc::kAllocatorTypeTLAB) { \
- mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx); \
+ mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, sizeof(void*)); \
if (LIKELY(klass != nullptr && klass->IsInitialized() && !klass->IsFinalizable())) { \
size_t byte_count = klass->GetObjectSize(); \
byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 0c7caf3..1302c5f 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -980,7 +980,7 @@
// FindVirtualMethodFor... This is ok for FindDexMethodIndexInOtherDexFile that only cares
// about the name and signature.
uint32_t update_dex_cache_method_index = called->GetDexMethodIndex();
- if (!called->HasSameDexCacheResolvedMethods(caller)) {
+ if (!called->HasSameDexCacheResolvedMethods(caller, sizeof(void*))) {
// Calling from one dex file to another, need to compute the method index appropriate to
// the caller's dex file. Since we get here only if the original called was a runtime
// method, we've got the correct dex_file and a dex_method_idx from above.
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 7ddc7cc..089f453 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -421,12 +421,11 @@
if (heap_bitmap_exclusive_locked) {
Locks::heap_bitmap_lock_->ExclusiveUnlock(self);
}
- Locks::mutator_lock_->SharedUnlock(self);
- ThreadList* tl = Runtime::Current()->GetThreadList();
- tl->SuspendAll(__FUNCTION__);
- mark_sweep_->VerifyRoots();
- tl->ResumeAll();
- Locks::mutator_lock_->SharedLock(self);
+ {
+ ScopedThreadSuspension(self, kSuspended);
+ ScopedSuspendAll ssa(__FUNCTION__);
+ mark_sweep_->VerifyRoots();
+ }
if (heap_bitmap_exclusive_locked) {
Locks::heap_bitmap_lock_->ExclusiveLock(self);
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 9292c7a..cfe7713 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -639,10 +639,9 @@
background_collector_type_ = foreground_collector_type_;
}
TransitionCollector(foreground_collector_type_);
- ThreadList* tl = Runtime::Current()->GetThreadList();
- Thread* self = Thread::Current();
+ Thread* const self = Thread::Current();
ScopedThreadStateChange tsc(self, kSuspended);
- tl->SuspendAll(__FUNCTION__);
+ ScopedSuspendAll ssa(__FUNCTION__);
// Something may have caused the transition to fail.
if (!IsMovingGc(collector_type_) && non_moving_space_ != main_space_) {
CHECK(main_space_ != nullptr);
@@ -657,7 +656,6 @@
non_moving_space_ = main_space_;
CHECK(!non_moving_space_->CanMoveObjects());
}
- tl->ResumeAll();
}
std::string Heap::SafeGetClassDescriptor(mirror::Class* klass) {
@@ -889,11 +887,9 @@
IncrementDisableMovingGC(self);
{
ScopedThreadSuspension sts(self, kWaitingForVisitObjects);
- ThreadList* tl = Runtime::Current()->GetThreadList();
- tl->SuspendAll(__FUNCTION__);
+ ScopedSuspendAll ssa(__FUNCTION__);
VisitObjectsInternalRegionSpace(callback, arg);
VisitObjectsInternal(callback, arg);
- tl->ResumeAll();
}
DecrementDisableMovingGC(self);
} else {
@@ -1267,12 +1263,13 @@
// Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
// about pauses.
Runtime* runtime = Runtime::Current();
- runtime->GetThreadList()->SuspendAll(__FUNCTION__);
- uint64_t start_time = NanoTime();
- size_t count = runtime->GetMonitorList()->DeflateMonitors();
- VLOG(heap) << "Deflating " << count << " monitors took "
- << PrettyDuration(NanoTime() - start_time);
- runtime->GetThreadList()->ResumeAll();
+ {
+ ScopedSuspendAll ssa(__FUNCTION__);
+ uint64_t start_time = NanoTime();
+ size_t count = runtime->GetMonitorList()->DeflateMonitors();
+ VLOG(heap) << "Deflating " << count << " monitors took "
+ << PrettyDuration(NanoTime() - start_time);
+ }
ATRACE_END();
}
TrimIndirectReferenceTables(self);
@@ -1749,19 +1746,15 @@
}
size_t Heap::GetObjectsAllocated() const {
- Thread* self = Thread::Current();
+ Thread* const self = Thread::Current();
ScopedThreadStateChange tsc(self, kWaitingForGetObjectsAllocated);
- auto* tl = Runtime::Current()->GetThreadList();
// Need SuspendAll here to prevent lock violation if RosAlloc does it during InspectAll.
- tl->SuspendAll(__FUNCTION__);
+ ScopedSuspendAll ssa(__FUNCTION__);
+ ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
size_t total = 0;
- {
- ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
- for (space::AllocSpace* space : alloc_spaces_) {
- total += space->GetObjectsAllocated();
- }
+ for (space::AllocSpace* space : alloc_spaces_) {
+ total += space->GetObjectsAllocated();
}
- tl->ResumeAll();
return total;
}
@@ -1911,7 +1904,6 @@
// Inc requested homogeneous space compaction.
count_requested_homogeneous_space_compaction_++;
// Store performed homogeneous space compaction at a new request arrival.
- ThreadList* tl = Runtime::Current()->GetThreadList();
ScopedThreadStateChange tsc(self, kWaitingPerformingGc);
Locks::mutator_lock_->AssertNotHeld(self);
{
@@ -1938,34 +1930,34 @@
FinishGC(self, collector::kGcTypeNone);
return HomogeneousSpaceCompactResult::kErrorVMShuttingDown;
}
- // Suspend all threads.
- tl->SuspendAll(__FUNCTION__);
- uint64_t start_time = NanoTime();
- // Launch compaction.
- space::MallocSpace* to_space = main_space_backup_.release();
- space::MallocSpace* from_space = main_space_;
- to_space->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
- const uint64_t space_size_before_compaction = from_space->Size();
- AddSpace(to_space);
- // Make sure that we will have enough room to copy.
- CHECK_GE(to_space->GetFootprintLimit(), from_space->GetFootprintLimit());
- collector::GarbageCollector* collector = Compact(to_space, from_space,
- kGcCauseHomogeneousSpaceCompact);
- const uint64_t space_size_after_compaction = to_space->Size();
- main_space_ = to_space;
- main_space_backup_.reset(from_space);
- RemoveSpace(from_space);
- SetSpaceAsDefault(main_space_); // Set as default to reset the proper dlmalloc space.
- // Update performed homogeneous space compaction count.
- count_performed_homogeneous_space_compaction_++;
- // Print statics log and resume all threads.
- uint64_t duration = NanoTime() - start_time;
- VLOG(heap) << "Heap homogeneous space compaction took " << PrettyDuration(duration) << " size: "
- << PrettySize(space_size_before_compaction) << " -> "
- << PrettySize(space_size_after_compaction) << " compact-ratio: "
- << std::fixed << static_cast<double>(space_size_after_compaction) /
- static_cast<double>(space_size_before_compaction);
- tl->ResumeAll();
+ collector::GarbageCollector* collector;
+ {
+ ScopedSuspendAll ssa(__FUNCTION__);
+ uint64_t start_time = NanoTime();
+ // Launch compaction.
+ space::MallocSpace* to_space = main_space_backup_.release();
+ space::MallocSpace* from_space = main_space_;
+ to_space->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
+ const uint64_t space_size_before_compaction = from_space->Size();
+ AddSpace(to_space);
+ // Make sure that we will have enough room to copy.
+ CHECK_GE(to_space->GetFootprintLimit(), from_space->GetFootprintLimit());
+ collector = Compact(to_space, from_space, kGcCauseHomogeneousSpaceCompact);
+ const uint64_t space_size_after_compaction = to_space->Size();
+ main_space_ = to_space;
+ main_space_backup_.reset(from_space);
+ RemoveSpace(from_space);
+ SetSpaceAsDefault(main_space_); // Set as default to reset the proper dlmalloc space.
+ // Update performed homogeneous space compaction count.
+ count_performed_homogeneous_space_compaction_++;
+ // Print statics log and resume all threads.
+ uint64_t duration = NanoTime() - start_time;
+ VLOG(heap) << "Heap homogeneous space compaction took " << PrettyDuration(duration) << " size: "
+ << PrettySize(space_size_before_compaction) << " -> "
+ << PrettySize(space_size_after_compaction) << " compact-ratio: "
+ << std::fixed << static_cast<double>(space_size_after_compaction) /
+ static_cast<double>(space_size_before_compaction);
+ }
// Finish GC.
reference_processor_->EnqueueClearedReferences(self);
GrowForUtilization(semi_space_collector_);
@@ -1983,7 +1975,6 @@
uint64_t start_time = NanoTime();
uint32_t before_allocated = num_bytes_allocated_.LoadSequentiallyConsistent();
Runtime* const runtime = Runtime::Current();
- ThreadList* const tl = runtime->GetThreadList();
Thread* const self = Thread::Current();
ScopedThreadStateChange tsc(self, kWaitingPerformingGc);
Locks::mutator_lock_->AssertNotHeld(self);
@@ -2021,84 +2012,91 @@
return;
}
collector::GarbageCollector* collector = nullptr;
- tl->SuspendAll(__FUNCTION__);
- switch (collector_type) {
- case kCollectorTypeSS: {
- if (!IsMovingGc(collector_type_)) {
- // Create the bump pointer space from the backup space.
- CHECK(main_space_backup_ != nullptr);
- std::unique_ptr<MemMap> mem_map(main_space_backup_->ReleaseMemMap());
- // We are transitioning from non moving GC -> moving GC, since we copied from the bump
- // pointer space last transition it will be protected.
- CHECK(mem_map != nullptr);
- mem_map->Protect(PROT_READ | PROT_WRITE);
- bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space",
- mem_map.release());
- AddSpace(bump_pointer_space_);
- collector = Compact(bump_pointer_space_, main_space_, kGcCauseCollectorTransition);
- // Use the now empty main space mem map for the bump pointer temp space.
- mem_map.reset(main_space_->ReleaseMemMap());
- // Unset the pointers just in case.
- if (dlmalloc_space_ == main_space_) {
- dlmalloc_space_ = nullptr;
- } else if (rosalloc_space_ == main_space_) {
- rosalloc_space_ = nullptr;
- }
- // 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.
- RemoveSpace(main_space_);
- RemoveRememberedSet(main_space_);
- delete main_space_; // Delete the space since it has been removed.
- main_space_ = nullptr;
- RemoveRememberedSet(main_space_backup_.get());
- main_space_backup_.reset(nullptr); // Deletes the space.
- temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2",
- mem_map.release());
- AddSpace(temp_space_);
- }
- break;
- }
- case kCollectorTypeMS:
- // Fall through.
- case kCollectorTypeCMS: {
- if (IsMovingGc(collector_type_)) {
- CHECK(temp_space_ != nullptr);
- std::unique_ptr<MemMap> mem_map(temp_space_->ReleaseMemMap());
- RemoveSpace(temp_space_);
- temp_space_ = nullptr;
- mem_map->Protect(PROT_READ | PROT_WRITE);
- CreateMainMallocSpace(mem_map.get(), kDefaultInitialSize,
- std::min(mem_map->Size(), growth_limit_), mem_map->Size());
- mem_map.release();
- // Compact to the main space from the bump pointer space, don't need to swap semispaces.
- AddSpace(main_space_);
- collector = Compact(main_space_, bump_pointer_space_, kGcCauseCollectorTransition);
- mem_map.reset(bump_pointer_space_->ReleaseMemMap());
- RemoveSpace(bump_pointer_space_);
- bump_pointer_space_ = nullptr;
- const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
- // Temporarily unprotect the backup mem map so rosalloc can write the debug magic number.
- if (kIsDebugBuild && kUseRosAlloc) {
+ {
+ ScopedSuspendAll ssa(__FUNCTION__);
+ switch (collector_type) {
+ case kCollectorTypeSS: {
+ if (!IsMovingGc(collector_type_)) {
+ // Create the bump pointer space from the backup space.
+ CHECK(main_space_backup_ != nullptr);
+ std::unique_ptr<MemMap> mem_map(main_space_backup_->ReleaseMemMap());
+ // We are transitioning from non moving GC -> moving GC, since we copied from the bump
+ // pointer space last transition it will be protected.
+ CHECK(mem_map != nullptr);
mem_map->Protect(PROT_READ | PROT_WRITE);
+ bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space",
+ mem_map.release());
+ AddSpace(bump_pointer_space_);
+ collector = Compact(bump_pointer_space_, main_space_, kGcCauseCollectorTransition);
+ // Use the now empty main space mem map for the bump pointer temp space.
+ mem_map.reset(main_space_->ReleaseMemMap());
+ // Unset the pointers just in case.
+ if (dlmalloc_space_ == main_space_) {
+ dlmalloc_space_ = nullptr;
+ } else if (rosalloc_space_ == main_space_) {
+ rosalloc_space_ = nullptr;
+ }
+ // 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.
+ RemoveSpace(main_space_);
+ RemoveRememberedSet(main_space_);
+ delete main_space_; // Delete the space since it has been removed.
+ main_space_ = nullptr;
+ RemoveRememberedSet(main_space_backup_.get());
+ main_space_backup_.reset(nullptr); // Deletes the space.
+ temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2",
+ mem_map.release());
+ AddSpace(temp_space_);
}
- main_space_backup_.reset(CreateMallocSpaceFromMemMap(
- mem_map.get(), kDefaultInitialSize, std::min(mem_map->Size(), growth_limit_),
- mem_map->Size(), name, true));
- if (kIsDebugBuild && kUseRosAlloc) {
- mem_map->Protect(PROT_NONE);
- }
- mem_map.release();
+ break;
}
- break;
+ case kCollectorTypeMS:
+ // Fall through.
+ case kCollectorTypeCMS: {
+ if (IsMovingGc(collector_type_)) {
+ CHECK(temp_space_ != nullptr);
+ std::unique_ptr<MemMap> mem_map(temp_space_->ReleaseMemMap());
+ RemoveSpace(temp_space_);
+ temp_space_ = nullptr;
+ mem_map->Protect(PROT_READ | PROT_WRITE);
+ CreateMainMallocSpace(mem_map.get(),
+ kDefaultInitialSize,
+ std::min(mem_map->Size(), growth_limit_),
+ mem_map->Size());
+ mem_map.release();
+ // Compact to the main space from the bump pointer space, don't need to swap semispaces.
+ AddSpace(main_space_);
+ collector = Compact(main_space_, bump_pointer_space_, kGcCauseCollectorTransition);
+ mem_map.reset(bump_pointer_space_->ReleaseMemMap());
+ RemoveSpace(bump_pointer_space_);
+ bump_pointer_space_ = nullptr;
+ const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
+ // Temporarily unprotect the backup mem map so rosalloc can write the debug magic number.
+ if (kIsDebugBuild && kUseRosAlloc) {
+ mem_map->Protect(PROT_READ | PROT_WRITE);
+ }
+ main_space_backup_.reset(CreateMallocSpaceFromMemMap(
+ mem_map.get(),
+ kDefaultInitialSize,
+ std::min(mem_map->Size(), growth_limit_),
+ mem_map->Size(),
+ name,
+ true));
+ if (kIsDebugBuild && kUseRosAlloc) {
+ mem_map->Protect(PROT_NONE);
+ }
+ mem_map.release();
+ }
+ break;
+ }
+ default: {
+ LOG(FATAL) << "Attempted to transition to invalid collector type "
+ << static_cast<size_t>(collector_type);
+ break;
+ }
}
- default: {
- LOG(FATAL) << "Attempted to transition to invalid collector type "
- << static_cast<size_t>(collector_type);
- break;
- }
+ ChangeCollector(collector_type);
}
- ChangeCollector(collector_type);
- tl->ResumeAll();
// Can't call into java code with all threads suspended.
reference_processor_->EnqueueClearedReferences(self);
uint64_t duration = NanoTime() - start_time;
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index d8072ea..49126d2 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -303,17 +303,13 @@
void* arg, bool do_null_callback_at_end) NO_THREAD_SAFETY_ANALYSIS {
// TODO: NO_THREAD_SAFETY_ANALYSIS.
Thread* self = Thread::Current();
- ThreadList* tl = Runtime::Current()->GetThreadList();
- tl->SuspendAll(__FUNCTION__);
- {
- MutexLock mu(self, *Locks::runtime_shutdown_lock_);
- MutexLock mu2(self, *Locks::thread_list_lock_);
- rosalloc_->InspectAll(callback, arg);
- if (do_null_callback_at_end) {
- callback(nullptr, nullptr, 0, arg); // Indicate end of a space.
- }
+ ScopedSuspendAll ssa(__FUNCTION__);
+ MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+ MutexLock mu2(self, *Locks::thread_list_lock_);
+ rosalloc_->InspectAll(callback, arg);
+ if (do_null_callback_at_end) {
+ callback(nullptr, nullptr, 0, arg); // Indicate end of a space.
}
- tl->ResumeAll();
}
void RosAllocSpace::InspectAllRosAlloc(void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg),
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index 83471e6..477e67b 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -90,16 +90,16 @@
virtual ~RootVisitor() { }
// Single root version, not overridable.
- ALWAYS_INLINE void VisitRoot(mirror::Object** roots, const RootInfo& info)
+ ALWAYS_INLINE void VisitRoot(mirror::Object** root, const RootInfo& info)
SHARED_REQUIRES(Locks::mutator_lock_) {
- VisitRoots(&roots, 1, info);
+ VisitRoots(&root, 1, info);
}
// Single root version, not overridable.
- ALWAYS_INLINE void VisitRootIfNonNull(mirror::Object** roots, const RootInfo& info)
+ ALWAYS_INLINE void VisitRootIfNonNull(mirror::Object** root, const RootInfo& info)
SHARED_REQUIRES(Locks::mutator_lock_) {
- if (*roots != nullptr) {
- VisitRoot(roots, info);
+ if (*root != nullptr) {
+ VisitRoot(root, info);
}
}
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index e2094dc..dfc1f5f 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -1403,10 +1403,11 @@
// comment in Heap::VisitObjects().
heap->IncrementDisableMovingGC(self);
}
- Runtime::Current()->GetThreadList()->SuspendAll(__FUNCTION__, true /* long suspend */);
- Hprof hprof(filename, fd, direct_to_ddms);
- hprof.Dump();
- Runtime::Current()->GetThreadList()->ResumeAll();
+ {
+ ScopedSuspendAll ssa(__FUNCTION__, true /* long suspend */);
+ Hprof hprof(filename, fd, direct_to_ddms);
+ hprof.Dump();
+ }
if (heap->IsGcConcurrentAndMoving()) {
heap->DecrementDisableMovingGC(self);
}
diff --git a/runtime/image.cc b/runtime/image.cc
index 8df17c6..42b348a 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '0', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '1', '\0' };
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index e2d59f9..20e4159 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -168,6 +168,7 @@
kSectionObjects,
kSectionArtFields,
kSectionArtMethods,
+ kSectionDexCacheArrays,
kSectionInternedStrings,
kSectionImageBitmap,
kSectionCount, // Number of elements in enum.
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 63c02ed..7e2a84d 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -407,6 +407,10 @@
backward_branch_listeners_.push_back(listener);
have_backward_branch_listeners_ = true;
}
+ if (HasEvent(kInvokeVirtualOrInterface, events)) {
+ invoke_virtual_or_interface_listeners_.push_back(listener);
+ have_invoke_virtual_or_interface_listeners_ = true;
+ }
if (HasEvent(kDexPcMoved, events)) {
std::list<InstrumentationListener*>* modified;
if (have_dex_pc_listeners_) {
@@ -466,13 +470,17 @@
have_method_exit_listeners_ = !method_exit_listeners_.empty();
}
if (HasEvent(kMethodUnwind, events) && have_method_unwind_listeners_) {
- method_unwind_listeners_.remove(listener);
- have_method_unwind_listeners_ = !method_unwind_listeners_.empty();
+ method_unwind_listeners_.remove(listener);
+ have_method_unwind_listeners_ = !method_unwind_listeners_.empty();
}
if (HasEvent(kBackwardBranch, events) && have_backward_branch_listeners_) {
- backward_branch_listeners_.remove(listener);
- have_backward_branch_listeners_ = !backward_branch_listeners_.empty();
- }
+ backward_branch_listeners_.remove(listener);
+ have_backward_branch_listeners_ = !backward_branch_listeners_.empty();
+ }
+ if (HasEvent(kInvokeVirtualOrInterface, events) && have_invoke_virtual_or_interface_listeners_) {
+ invoke_virtual_or_interface_listeners_.remove(listener);
+ have_invoke_virtual_or_interface_listeners_ = !invoke_virtual_or_interface_listeners_.empty();
+ }
if (HasEvent(kDexPcMoved, events) && have_dex_pc_listeners_) {
std::list<InstrumentationListener*>* modified =
new std::list<InstrumentationListener*>(*dex_pc_listeners_.get());
@@ -602,19 +610,17 @@
void Instrumentation::SetEntrypointsInstrumented(bool instrumented) {
Thread* self = Thread::Current();
Runtime* runtime = Runtime::Current();
- ThreadList* tl = runtime->GetThreadList();
Locks::mutator_lock_->AssertNotHeld(self);
Locks::instrument_entrypoints_lock_->AssertHeld(self);
if (runtime->IsStarted()) {
- tl->SuspendAll(__FUNCTION__);
- }
- {
+ ScopedSuspendAll ssa(__FUNCTION__);
MutexLock mu(self, *Locks::runtime_shutdown_lock_);
SetQuickAllocEntryPointsInstrumented(instrumented);
ResetQuickAllocEntryPoints();
- }
- if (runtime->IsStarted()) {
- tl->ResumeAll();
+ } else {
+ MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+ SetQuickAllocEntryPointsInstrumented(instrumented);
+ ResetQuickAllocEntryPoints();
}
}
@@ -908,6 +914,16 @@
}
}
+void Instrumentation::InvokeVirtualOrInterfaceImpl(Thread* thread,
+ mirror::Object* this_object,
+ ArtMethod* caller,
+ uint32_t dex_pc,
+ ArtMethod* callee) const {
+ for (InstrumentationListener* listener : invoke_virtual_or_interface_listeners_) {
+ listener->InvokeVirtualOrInterface(thread, this_object, caller, dex_pc, callee);
+ }
+}
+
void Instrumentation::FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
ArtMethod* method, uint32_t dex_pc,
ArtField* field) const {
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 93ff567..6711ac3 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -97,6 +97,14 @@
// Call-back for when we get a backward branch.
virtual void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset)
SHARED_REQUIRES(Locks::mutator_lock_) = 0;
+
+ // Call-back for when we get an invokevirtual or an invokeinterface.
+ virtual void InvokeVirtualOrInterface(Thread* thread,
+ mirror::Object* this_object,
+ ArtMethod* caller,
+ uint32_t dex_pc,
+ ArtMethod* callee)
+ SHARED_REQUIRES(Locks::mutator_lock_) = 0;
};
// Instrumentation is a catch-all for when extra information is required from the runtime. The
@@ -114,6 +122,7 @@
kFieldWritten = 0x20,
kExceptionCaught = 0x40,
kBackwardBranch = 0x80,
+ kInvokeVirtualOrInterface = 0x100,
};
enum class InstrumentationLevel {
@@ -257,6 +266,10 @@
return have_backward_branch_listeners_;
}
+ bool HasInvokeVirtualOrInterfaceListeners() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ return have_invoke_virtual_or_interface_listeners_;
+ }
+
bool IsActive() const SHARED_REQUIRES(Locks::mutator_lock_) {
return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ ||
have_field_read_listeners_ || have_field_write_listeners_ ||
@@ -325,6 +338,17 @@
}
}
+ void InvokeVirtualOrInterface(Thread* thread,
+ mirror::Object* this_object,
+ ArtMethod* caller,
+ uint32_t dex_pc,
+ ArtMethod* callee) const
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (UNLIKELY(HasInvokeVirtualOrInterfaceListeners())) {
+ InvokeVirtualOrInterfaceImpl(thread, this_object, caller, dex_pc, callee);
+ }
+ }
+
// Inform listeners that an exception was caught.
void ExceptionCaughtEvent(Thread* thread, mirror::Throwable* exception_object) const
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -385,6 +409,12 @@
SHARED_REQUIRES(Locks::mutator_lock_);
void BackwardBranchImpl(Thread* thread, ArtMethod* method, int32_t offset) const
SHARED_REQUIRES(Locks::mutator_lock_);
+ void InvokeVirtualOrInterfaceImpl(Thread* thread,
+ mirror::Object* this_object,
+ ArtMethod* caller,
+ uint32_t dex_pc,
+ ArtMethod* callee) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
void FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
ArtMethod* method, uint32_t dex_pc,
ArtField* field) const
@@ -451,6 +481,9 @@
// Do we have any backward branch listeners? Short-cut to avoid taking the instrumentation_lock_.
bool have_backward_branch_listeners_ GUARDED_BY(Locks::mutator_lock_);
+ // Do we have any invoke listeners? Short-cut to avoid taking the instrumentation_lock_.
+ bool have_invoke_virtual_or_interface_listeners_ GUARDED_BY(Locks::mutator_lock_);
+
// Contains the instrumentation level required by each client of the instrumentation identified
// by a string key.
typedef SafeMap<const char*, InstrumentationLevel> InstrumentationLevelTable;
@@ -461,6 +494,8 @@
std::list<InstrumentationListener*> method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> backward_branch_listeners_ GUARDED_BY(Locks::mutator_lock_);
+ std::list<InstrumentationListener*> invoke_virtual_or_interface_listeners_
+ GUARDED_BY(Locks::mutator_lock_);
std::shared_ptr<std::list<InstrumentationListener*>> dex_pc_listeners_
GUARDED_BY(Locks::mutator_lock_);
std::shared_ptr<std::list<InstrumentationListener*>> field_read_listeners_
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index 56fe9ef..d98d246 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -36,7 +36,8 @@
: received_method_enter_event(false), received_method_exit_event(false),
received_method_unwind_event(false), received_dex_pc_moved_event(false),
received_field_read_event(false), received_field_written_event(false),
- received_exception_caught_event(false), received_backward_branch_event(false) {}
+ received_exception_caught_event(false), received_backward_branch_event(false),
+ received_invoke_virtual_or_interface_event(false) {}
virtual ~TestInstrumentationListener() {}
@@ -105,6 +106,15 @@
received_backward_branch_event = true;
}
+ void InvokeVirtualOrInterface(Thread* thread ATTRIBUTE_UNUSED,
+ mirror::Object* this_object ATTRIBUTE_UNUSED,
+ ArtMethod* caller ATTRIBUTE_UNUSED,
+ uint32_t dex_pc ATTRIBUTE_UNUSED,
+ ArtMethod* callee ATTRIBUTE_UNUSED)
+ OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ received_invoke_virtual_or_interface_event = true;
+ }
+
void Reset() {
received_method_enter_event = false;
received_method_exit_event = false;
@@ -114,6 +124,7 @@
received_field_written_event = false;
received_exception_caught_event = false;
received_backward_branch_event = false;
+ received_invoke_virtual_or_interface_event = false;
}
bool received_method_enter_event;
@@ -124,6 +135,7 @@
bool received_field_written_event;
bool received_exception_caught_event;
bool received_backward_branch_event;
+ bool received_invoke_virtual_or_interface_event;
private:
DISALLOW_COPY_AND_ASSIGN(TestInstrumentationListener);
@@ -138,13 +150,9 @@
void CheckConfigureStubs(const char* key, Instrumentation::InstrumentationLevel level) {
ScopedObjectAccess soa(Thread::Current());
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
- {
- ScopedThreadSuspension sts(soa.Self(), kSuspended);
- Runtime* runtime = Runtime::Current();
- runtime->GetThreadList()->SuspendAll("Instrumentation::ConfigureStubs");
- instr->ConfigureStubs(key, level);
- runtime->GetThreadList()->ResumeAll();
- }
+ ScopedThreadSuspension sts(soa.Self(), kSuspended);
+ ScopedSuspendAll ssa("Instrumentation::ConfigureStubs");
+ instr->ConfigureStubs(key, level);
}
Instrumentation::InstrumentationLevel GetCurrentInstrumentationLevel() {
@@ -162,10 +170,8 @@
TestInstrumentationListener listener;
{
ScopedThreadSuspension sts(soa.Self(), kSuspended);
- Runtime* runtime = Runtime::Current();
- runtime->GetThreadList()->SuspendAll("Add instrumentation listener");
+ ScopedSuspendAll ssa("Add instrumentation listener");
instr->AddListener(&listener, instrumentation_event);
- runtime->GetThreadList()->ResumeAll();
}
ArtMethod* const event_method = nullptr;
@@ -181,10 +187,8 @@
listener.Reset();
{
ScopedThreadSuspension sts(soa.Self(), kSuspended);
- Runtime* runtime = Runtime::Current();
- runtime->GetThreadList()->SuspendAll("Remove instrumentation listener");
+ ScopedSuspendAll ssa("Remove instrumentation listener");
instr->RemoveListener(&listener, instrumentation_event);
- runtime->GetThreadList()->ResumeAll();
}
// Check the listener is not registered and is not notified of the event.
@@ -199,12 +203,11 @@
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
ScopedThreadSuspension sts(self, kSuspended);
- runtime->GetThreadList()->SuspendAll("Single method deoptimization");
+ ScopedSuspendAll ssa("Single method deoptimization");
if (enable_deoptimization) {
instrumentation->EnableDeoptimization();
}
instrumentation->Deoptimize(method);
- runtime->GetThreadList()->ResumeAll();
}
void UndeoptimizeMethod(Thread* self, ArtMethod* method,
@@ -213,12 +216,11 @@
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
ScopedThreadSuspension sts(self, kSuspended);
- runtime->GetThreadList()->SuspendAll("Single method undeoptimization");
+ ScopedSuspendAll ssa("Single method undeoptimization");
instrumentation->Undeoptimize(method);
if (disable_deoptimization) {
instrumentation->DisableDeoptimization(key);
}
- runtime->GetThreadList()->ResumeAll();
}
void DeoptimizeEverything(Thread* self, const char* key, bool enable_deoptimization)
@@ -226,12 +228,11 @@
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
ScopedThreadSuspension sts(self, kSuspended);
- runtime->GetThreadList()->SuspendAll("Full deoptimization");
+ ScopedSuspendAll ssa("Full deoptimization");
if (enable_deoptimization) {
instrumentation->EnableDeoptimization();
}
instrumentation->DeoptimizeEverything(key);
- runtime->GetThreadList()->ResumeAll();
}
void UndeoptimizeEverything(Thread* self, const char* key, bool disable_deoptimization)
@@ -239,12 +240,11 @@
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
ScopedThreadSuspension sts(self, kSuspended);
- runtime->GetThreadList()->SuspendAll("Full undeoptimization");
+ ScopedSuspendAll ssa("Full undeoptimization");
instrumentation->UndeoptimizeEverything(key);
if (disable_deoptimization) {
instrumentation->DisableDeoptimization(key);
}
- runtime->GetThreadList()->ResumeAll();
}
void EnableMethodTracing(Thread* self, const char* key, bool needs_interpreter)
@@ -252,9 +252,8 @@
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
ScopedThreadSuspension sts(self, kSuspended);
- runtime->GetThreadList()->SuspendAll("EnableMethodTracing");
+ ScopedSuspendAll ssa("EnableMethodTracing");
instrumentation->EnableMethodTracing(key, needs_interpreter);
- runtime->GetThreadList()->ResumeAll();
}
void DisableMethodTracing(Thread* self, const char* key)
@@ -262,9 +261,8 @@
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
ScopedThreadSuspension sts(self, kSuspended);
- runtime->GetThreadList()->SuspendAll("EnableMethodTracing");
+ ScopedSuspendAll ssa("EnableMethodTracing");
instrumentation->DisableMethodTracing(key);
- runtime->GetThreadList()->ResumeAll();
}
private:
@@ -287,6 +285,8 @@
return instr->HasExceptionCaughtListeners();
case instrumentation::Instrumentation::kBackwardBranch:
return instr->HasBackwardBranchListeners();
+ case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
+ return instr->HasInvokeVirtualOrInterfaceListeners();
default:
LOG(FATAL) << "Unknown instrumentation event " << event_type;
UNREACHABLE();
@@ -330,6 +330,9 @@
case instrumentation::Instrumentation::kBackwardBranch:
instr->BackwardBranch(self, method, dex_pc);
break;
+ case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
+ instr->InvokeVirtualOrInterface(self, obj, method, dex_pc, method);
+ break;
default:
LOG(FATAL) << "Unknown instrumentation event " << event_type;
UNREACHABLE();
@@ -355,6 +358,8 @@
return listener.received_exception_caught_event;
case instrumentation::Instrumentation::kBackwardBranch:
return listener.received_backward_branch_event;
+ case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
+ return listener.received_invoke_virtual_or_interface_event;
default:
LOG(FATAL) << "Unknown instrumentation event " << event_type;
UNREACHABLE();
@@ -418,6 +423,10 @@
TestEvent(instrumentation::Instrumentation::kBackwardBranch);
}
+TEST_F(InstrumentationTest, InvokeVirtualOrInterfaceEvent) {
+ TestEvent(instrumentation::Instrumentation::kInvokeVirtualOrInterface);
+}
+
TEST_F(InstrumentationTest, DeoptimizeDirectMethod) {
ScopedObjectAccess soa(Thread::Current());
jobject class_loader = LoadDex("Instrumentation");
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 3ff7017..179353e 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -22,7 +22,7 @@
#include "gc/collector/garbage_collector.h"
#include "gc/space/image_space.h"
#include "gc/weak_root_state.h"
-#include "mirror/dex_cache.h"
+#include "mirror/dex_cache-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
#include "mirror/string-inl.h"
@@ -165,8 +165,7 @@
mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>();
for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
mirror::DexCache* dex_cache = dex_caches->Get(i);
- const DexFile* dex_file = dex_cache->GetDexFile();
- const size_t num_strings = dex_file->NumStringIds();
+ const size_t num_strings = dex_cache->NumStrings();
for (size_t j = 0; j < num_strings; ++j) {
mirror::String* image_string = dex_cache->GetResolvedString(j);
if (image_string != nullptr) {
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 6c6232c..3ac80c6 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -399,14 +399,19 @@
JValue value;
// Set value to last known result in case the shadow frame chain is empty.
value.SetJ(ret_val->GetJ());
+ // Are we executing the first shadow frame?
+ bool first = true;
while (shadow_frame != nullptr) {
self->SetTopOfShadowStack(shadow_frame);
const DexFile::CodeItem* code_item = shadow_frame->GetMethod()->GetCodeItem();
const uint32_t dex_pc = shadow_frame->GetDexPC();
uint32_t new_dex_pc;
if (UNLIKELY(self->IsExceptionPending())) {
+ // If we deoptimize from the QuickExceptionHandler, we already reported the exception to
+ // the instrumentation. To prevent from reporting it a second time, we simply pass a
+ // null Instrumentation*.
const instrumentation::Instrumentation* const instrumentation =
- Runtime::Current()->GetInstrumentation();
+ first ? nullptr : Runtime::Current()->GetInstrumentation();
uint32_t found_dex_pc = FindNextInstructionFollowingException(self, *shadow_frame, dex_pc,
instrumentation);
new_dex_pc = found_dex_pc; // the dex pc of a matching catch handler
@@ -424,6 +429,7 @@
ShadowFrame* old_frame = shadow_frame;
shadow_frame = shadow_frame->GetLink();
ShadowFrame::DeleteDeoptimizedFrame(old_frame);
+ first = false;
}
ret_val->SetJ(value.GetJ());
}
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index f923b84..6602840 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -414,20 +414,21 @@
#undef EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL
#undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL
+// We accept a null Instrumentation* meaning we must not report anything to the instrumentation.
uint32_t FindNextInstructionFollowingException(
Thread* self, ShadowFrame& shadow_frame, uint32_t dex_pc,
const instrumentation::Instrumentation* instrumentation) {
self->VerifyStack();
StackHandleScope<2> hs(self);
Handle<mirror::Throwable> exception(hs.NewHandle(self->GetException()));
- if (instrumentation->HasExceptionCaughtListeners()
+ if (instrumentation != nullptr && instrumentation->HasExceptionCaughtListeners()
&& self->IsExceptionThrownByCurrentMethod(exception.Get())) {
instrumentation->ExceptionCaughtEvent(self, exception.Get());
}
bool clear_exception = false;
uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(
hs.NewHandle(exception->GetClass()), dex_pc, &clear_exception);
- if (found_dex_pc == DexFile::kDexNoIndex) {
+ if (found_dex_pc == DexFile::kDexNoIndex && instrumentation != nullptr) {
// Exception is not caught by the current method. We will unwind to the
// caller. Notify any instrumentation listener.
instrumentation->MethodUnwindEvent(self, shadow_frame.GetThisObject(),
@@ -616,9 +617,10 @@
case 'L': {
Object* o = shadow_frame.GetVRegReference(src_reg);
if (do_assignability_check && o != nullptr) {
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
Class* arg_type =
new_shadow_frame->GetMethod()->GetClassFromTypeIndex(
- params->GetTypeItem(shorty_pos).type_idx_, true);
+ params->GetTypeItem(shorty_pos).type_idx_, true /* resolve */, pointer_size);
if (arg_type == nullptr) {
CHECK(self->IsExceptionPending());
return false;
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 6468659..7398778 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -265,6 +265,13 @@
result->SetJ(0);
return false;
} else {
+ if (type == kVirtual || type == kInterface) {
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) {
+ instrumentation->InvokeVirtualOrInterface(
+ self, receiver, sf_method, shadow_frame.GetDexPC(), called_method);
+ }
+ }
return DoCall<is_range, do_access_check>(called_method, self, shadow_frame, inst, inst_data,
result);
}
@@ -297,6 +304,11 @@
result->SetJ(0);
return false;
} else {
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) {
+ instrumentation->InvokeVirtualOrInterface(
+ self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method);
+ }
// No need to check since we've been quickened.
return DoCall<is_range, false>(called_method, self, shadow_frame, inst, inst_data, result);
}
@@ -344,7 +356,9 @@
}
ArtMethod* method = shadow_frame.GetMethod();
mirror::Class* declaring_class = method->GetDeclaringClass();
- mirror::String* s = declaring_class->GetDexCacheStrings()->Get(string_idx);
+ // MethodVerifier refuses methods with string_idx out of bounds.
+ DCHECK_LT(string_idx, declaring_class->GetDexCache()->NumStrings());
+ mirror::String* s = declaring_class->GetDexCacheStrings()[string_idx].Read();
if (UNLIKELY(s == nullptr)) {
StackHandleScope<1> hs(self);
Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index 7027cbf..72e2ba0 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -327,7 +327,9 @@
const uint8_t vreg_index = inst->VRegA_11x(inst_data);
Object* obj_result = shadow_frame.GetVRegReference(vreg_index);
if (do_assignability_check && obj_result != nullptr) {
- Class* return_type = shadow_frame.GetMethod()->GetReturnType();
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ Class* return_type = shadow_frame.GetMethod()->GetReturnType(true /* resolve */,
+ pointer_size);
obj_result = shadow_frame.GetVRegReference(vreg_index);
if (return_type == nullptr) {
// Return the pending exception.
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 544f788..b5cc11e 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -225,7 +225,9 @@
const size_t ref_idx = inst->VRegA_11x(inst_data);
Object* obj_result = shadow_frame.GetVRegReference(ref_idx);
if (do_assignability_check && obj_result != nullptr) {
- Class* return_type = shadow_frame.GetMethod()->GetReturnType();
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ Class* return_type = shadow_frame.GetMethod()->GetReturnType(true /* resolve */,
+ pointer_size);
// Re-load since it might have moved.
obj_result = shadow_frame.GetVRegReference(ref_idx);
if (return_type == nullptr) {
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index d6c798a..63e8887 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -580,9 +580,12 @@
}
inline bool JavaVMExt::MayAccessWeakGlobalsUnlocked(Thread* self) const {
- return kUseReadBarrier
- ? self->GetWeakRefAccessEnabled()
- : allow_accessing_weak_globals_.LoadSequentiallyConsistent();
+ if (kUseReadBarrier) {
+ // self can be null during a runtime shutdown. ~Runtime()->~ClassLinker()->DecodeWeakGlobal().
+ return self != nullptr ? self->GetWeakRefAccessEnabled() : true;
+ } else {
+ return allow_accessing_weak_globals_.LoadSequentiallyConsistent();
+ }
}
mirror::Object* JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) {
@@ -592,6 +595,7 @@
// This only applies in the case where MayAccessWeakGlobals goes from false to true. In the other
// case, it may be racy, this is benign since DecodeWeakGlobalLocked does the correct behavior
// if MayAccessWeakGlobals is false.
+ DCHECK_EQ(GetIndirectRefKind(ref), kWeakGlobal);
if (LIKELY(MayAccessWeakGlobalsUnlocked(self))) {
return weak_globals_.SynchronizedGet(ref);
}
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 26a4fe4..0607493 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -39,6 +39,8 @@
options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheCapacity);
jit_options->compile_threshold_ =
options.GetOrDefault(RuntimeArgumentMap::JITCompileThreshold);
+ jit_options->warmup_threshold_ =
+ options.GetOrDefault(RuntimeArgumentMap::JITWarmupThreshold);
jit_options->dump_info_on_shutdown_ =
options.Exists(RuntimeArgumentMap::DumpJITInfoOnShutdown);
return jit_options;
@@ -160,18 +162,18 @@
}
}
-void Jit::CreateInstrumentationCache(size_t compile_threshold) {
+void Jit::CreateInstrumentationCache(size_t compile_threshold, size_t warmup_threshold) {
CHECK_GT(compile_threshold, 0U);
- Runtime* const runtime = Runtime::Current();
- runtime->GetThreadList()->SuspendAll(__FUNCTION__);
+ ScopedSuspendAll ssa(__FUNCTION__);
// Add Jit interpreter instrumentation, tells the interpreter when to notify the jit to compile
// something.
- instrumentation_cache_.reset(new jit::JitInstrumentationCache(compile_threshold));
- runtime->GetInstrumentation()->AddListener(
+ instrumentation_cache_.reset(
+ new jit::JitInstrumentationCache(compile_threshold, warmup_threshold));
+ Runtime::Current()->GetInstrumentation()->AddListener(
new jit::JitInstrumentationListener(instrumentation_cache_.get()),
instrumentation::Instrumentation::kMethodEntered |
- instrumentation::Instrumentation::kBackwardBranch);
- runtime->GetThreadList()->ResumeAll();
+ instrumentation::Instrumentation::kBackwardBranch |
+ instrumentation::Instrumentation::kInvokeVirtualOrInterface);
}
} // namespace jit
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index ca6e7ea..643bc23 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -43,13 +43,14 @@
class Jit {
public:
static constexpr bool kStressMode = kIsDebugBuild;
- static constexpr size_t kDefaultCompileThreshold = kStressMode ? 1 : 1000;
+ static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 1000;
+ static constexpr size_t kDefaultWarmupThreshold = kDefaultCompileThreshold / 2;
virtual ~Jit();
static Jit* Create(JitOptions* options, std::string* error_msg);
bool CompileMethod(ArtMethod* method, Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_);
- void CreateInstrumentationCache(size_t compile_threshold);
+ void CreateInstrumentationCache(size_t compile_threshold, size_t warmup_threshold);
void CreateThreadPool();
CompilerCallbacks* GetCompilerCallbacks() {
return compiler_callbacks_;
@@ -95,6 +96,9 @@
size_t GetCompileThreshold() const {
return compile_threshold_;
}
+ size_t GetWarmupThreshold() const {
+ return warmup_threshold_;
+ }
size_t GetCodeCacheCapacity() const {
return code_cache_capacity_;
}
@@ -112,6 +116,7 @@
bool use_jit_;
size_t code_cache_capacity_;
size_t compile_threshold_;
+ size_t warmup_threshold_;
bool dump_info_on_shutdown_;
JitOptions() : use_jit_(false), code_cache_capacity_(0), compile_threshold_(0),
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index cd5f4cb..4c53162 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -82,9 +82,19 @@
return code_cache_ptr_ - size;
}
+uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) {
+ MutexLock mu(self, lock_);
+ size = RoundUp(size, sizeof(void*));
+ if (size > DataCacheRemain()) {
+ return nullptr;
+ }
+ data_cache_ptr_ += size;
+ return data_cache_ptr_ - size;
+}
+
uint8_t* JitCodeCache::AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end) {
MutexLock mu(self, lock_);
- const size_t size = end - begin;
+ const size_t size = RoundUp(end - begin, sizeof(void*));
if (size > DataCacheRemain()) {
return nullptr; // Out of space in the data cache.
}
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 9707f6f..f485e4a 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -86,6 +86,9 @@
// Reserve a region of code of size at least "size". Returns null if there is no more room.
uint8_t* ReserveCode(Thread* self, size_t size) REQUIRES(!lock_);
+ // Reserve a region of data of size at least "size". Returns null if there is no more room.
+ uint8_t* ReserveData(Thread* self, size_t size) REQUIRES(!lock_);
+
// Add a data array of size (end - begin) with the associated contents, returns null if there
// is no more room.
uint8_t* AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end)
diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc
index 258c29d..f485682 100644
--- a/runtime/jit/jit_instrumentation.cc
+++ b/runtime/jit/jit_instrumentation.cc
@@ -26,16 +26,12 @@
class JitCompileTask : public Task {
public:
- JitCompileTask(ArtMethod* method, JitInstrumentationCache* cache)
- : method_(method), cache_(cache) {
- }
+ explicit JitCompileTask(ArtMethod* method) : method_(method) {}
virtual void Run(Thread* self) OVERRIDE {
ScopedObjectAccess soa(self);
VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_);
- if (Runtime::Current()->GetJit()->CompileMethod(method_, self)) {
- cache_->SignalCompiled(self, method_);
- } else {
+ if (!Runtime::Current()->GetJit()->CompileMethod(method_, self)) {
VLOG(jit) << "Failed to compile method " << PrettyMethod(method_);
}
}
@@ -46,13 +42,14 @@
private:
ArtMethod* const method_;
- JitInstrumentationCache* const cache_;
DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
};
-JitInstrumentationCache::JitInstrumentationCache(size_t hot_method_threshold)
- : lock_("jit instrumentation lock"), hot_method_threshold_(hot_method_threshold) {
+JitInstrumentationCache::JitInstrumentationCache(size_t hot_method_threshold,
+ size_t warm_method_threshold)
+ : hot_method_threshold_(hot_method_threshold),
+ warm_method_threshold_(warm_method_threshold) {
}
void JitInstrumentationCache::CreateThreadPool() {
@@ -60,20 +57,11 @@
}
void JitInstrumentationCache::DeleteThreadPool() {
+ DCHECK(Runtime::Current()->IsShuttingDown(Thread::Current()));
thread_pool_.reset();
}
-void JitInstrumentationCache::SignalCompiled(Thread* self, ArtMethod* method) {
- ScopedObjectAccessUnchecked soa(self);
- jmethodID method_id = soa.EncodeMethod(method);
- MutexLock mu(self, lock_);
- auto it = samples_.find(method_id);
- if (it != samples_.end()) {
- samples_.erase(it);
- }
-}
-
-void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, size_t count) {
+void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, size_t) {
ScopedObjectAccessUnchecked soa(self);
// Since we don't have on-stack replacement, some methods can remain in the interpreter longer
// than we want resulting in samples even after the method is compiled.
@@ -81,34 +69,21 @@
Runtime::Current()->GetJit()->GetCodeCache()->ContainsMethod(method)) {
return;
}
- jmethodID method_id = soa.EncodeMethod(method);
- bool is_hot = false;
- {
- MutexLock mu(self, lock_);
- size_t sample_count = 0;
- auto it = samples_.find(method_id);
- if (it != samples_.end()) {
- it->second += count;
- sample_count = it->second;
- } else {
- sample_count = count;
- samples_.insert(std::make_pair(method_id, count));
- }
- // If we have enough samples, mark as hot and request Jit compilation.
- if (sample_count >= hot_method_threshold_ && sample_count - count < hot_method_threshold_) {
- is_hot = true;
+ if (thread_pool_.get() == nullptr) {
+ DCHECK(Runtime::Current()->IsShuttingDown(self));
+ return;
+ }
+ uint16_t sample_count = method->IncrementCounter();
+ if (sample_count == warm_method_threshold_) {
+ ProfilingInfo* info = method->CreateProfilingInfo();
+ if (info != nullptr) {
+ VLOG(jit) << "Start profiling " << PrettyMethod(method);
}
}
- if (is_hot) {
- if (thread_pool_.get() != nullptr) {
- thread_pool_->AddTask(self, new JitCompileTask(
- method->GetInterfaceMethodIfProxy(sizeof(void*)), this));
- thread_pool_->StartWorkers(self);
- } else {
- VLOG(jit) << "Compiling hot method " << PrettyMethod(method);
- Runtime::Current()->GetJit()->CompileMethod(
- method->GetInterfaceMethodIfProxy(sizeof(void*)), self);
- }
+ if (sample_count == hot_method_threshold_) {
+ thread_pool_->AddTask(self, new JitCompileTask(
+ method->GetInterfaceMethodIfProxy(sizeof(void*))));
+ thread_pool_->StartWorkers(self);
}
}
@@ -117,5 +92,17 @@
CHECK(instrumentation_cache_ != nullptr);
}
+void JitInstrumentationListener::InvokeVirtualOrInterface(Thread* thread,
+ mirror::Object* this_object,
+ ArtMethod* caller,
+ uint32_t dex_pc,
+ ArtMethod* callee ATTRIBUTE_UNUSED) {
+ DCHECK(this_object != nullptr);
+ ProfilingInfo* info = caller->GetProfilingInfo();
+ if (info != nullptr) {
+ info->AddInvokeInfo(thread, dex_pc, this_object->GetClass());
+ }
+}
+
} // namespace jit
} // namespace art
diff --git a/runtime/jit/jit_instrumentation.h b/runtime/jit/jit_instrumentation.h
index 0deaf8a..6fdef65 100644
--- a/runtime/jit/jit_instrumentation.h
+++ b/runtime/jit/jit_instrumentation.h
@@ -45,18 +45,15 @@
// Keeps track of which methods are hot.
class JitInstrumentationCache {
public:
- explicit JitInstrumentationCache(size_t hot_method_threshold);
+ JitInstrumentationCache(size_t hot_method_threshold, size_t warm_method_threshold);
void AddSamples(Thread* self, ArtMethod* method, size_t samples)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_);
- void SignalCompiled(Thread* self, ArtMethod* method)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void CreateThreadPool();
void DeleteThreadPool();
private:
- Mutex lock_;
- std::unordered_map<jmethodID, size_t> samples_;
size_t hot_method_threshold_;
+ size_t warm_method_threshold_;
std::unique_ptr<ThreadPool> thread_pool_;
DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationCache);
@@ -66,37 +63,43 @@
public:
explicit JitInstrumentationListener(JitInstrumentationCache* cache);
- virtual void MethodEntered(Thread* thread, mirror::Object* /*this_object*/,
- ArtMethod* method, uint32_t /*dex_pc*/)
+ void MethodEntered(Thread* thread, mirror::Object* /*this_object*/,
+ ArtMethod* method, uint32_t /*dex_pc*/)
OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
instrumentation_cache_->AddSamples(thread, method, 1);
}
- virtual void MethodExited(Thread* /*thread*/, mirror::Object* /*this_object*/,
- ArtMethod* /*method*/, uint32_t /*dex_pc*/,
- const JValue& /*return_value*/)
+ void MethodExited(Thread* /*thread*/, mirror::Object* /*this_object*/,
+ ArtMethod* /*method*/, uint32_t /*dex_pc*/,
+ const JValue& /*return_value*/)
OVERRIDE { }
- virtual void MethodUnwind(Thread* /*thread*/, mirror::Object* /*this_object*/,
- ArtMethod* /*method*/, uint32_t /*dex_pc*/) OVERRIDE { }
- virtual void FieldRead(Thread* /*thread*/, mirror::Object* /*this_object*/,
- ArtMethod* /*method*/, uint32_t /*dex_pc*/,
- ArtField* /*field*/) OVERRIDE { }
- virtual void FieldWritten(Thread* /*thread*/, mirror::Object* /*this_object*/,
- ArtMethod* /*method*/, uint32_t /*dex_pc*/,
- ArtField* /*field*/, const JValue& /*field_value*/)
+ void MethodUnwind(Thread* /*thread*/, mirror::Object* /*this_object*/,
+ ArtMethod* /*method*/, uint32_t /*dex_pc*/) OVERRIDE { }
+ void FieldRead(Thread* /*thread*/, mirror::Object* /*this_object*/,
+ ArtMethod* /*method*/, uint32_t /*dex_pc*/,
+ ArtField* /*field*/) OVERRIDE { }
+ void FieldWritten(Thread* /*thread*/, mirror::Object* /*this_object*/,
+ ArtMethod* /*method*/, uint32_t /*dex_pc*/,
+ ArtField* /*field*/, const JValue& /*field_value*/)
OVERRIDE { }
- virtual void ExceptionCaught(Thread* /*thread*/,
- mirror::Throwable* /*exception_object*/) OVERRIDE { }
+ void ExceptionCaught(Thread* /*thread*/,
+ mirror::Throwable* /*exception_object*/) OVERRIDE { }
- virtual void DexPcMoved(Thread* /*self*/, mirror::Object* /*this_object*/,
- ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { }
+ void DexPcMoved(Thread* /*self*/, mirror::Object* /*this_object*/,
+ ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { }
- // We only care about how many dex instructions were executed in the Jit.
- virtual void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset)
+ void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset)
OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
CHECK_LE(dex_pc_offset, 0);
instrumentation_cache_->AddSamples(thread, method, 1);
}
+ void InvokeVirtualOrInterface(Thread* thread,
+ mirror::Object* this_object,
+ ArtMethod* caller,
+ uint32_t dex_pc,
+ ArtMethod* callee)
+ OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_);
+
private:
JitInstrumentationCache* const instrumentation_cache_;
diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc
new file mode 100644
index 0000000..0c039f2
--- /dev/null
+++ b/runtime/jit/profiling_info.cc
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "profiling_info.h"
+
+#include "art_method-inl.h"
+#include "dex_instruction.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+
+ProfilingInfo* ProfilingInfo::Create(ArtMethod* method) {
+ // Walk over the dex instructions of the method and keep track of
+ // instructions we are interested in profiling.
+ const uint16_t* code_ptr = nullptr;
+ const uint16_t* code_end = nullptr;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ DCHECK(!method->IsNative());
+ const DexFile::CodeItem& code_item = *method->GetCodeItem();
+ code_ptr = code_item.insns_;
+ code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
+ }
+
+ uint32_t dex_pc = 0;
+ std::vector<uint32_t> entries;
+ while (code_ptr < code_end) {
+ const Instruction& instruction = *Instruction::At(code_ptr);
+ switch (instruction.Opcode()) {
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_VIRTUAL_QUICK:
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ entries.push_back(dex_pc);
+ break;
+
+ default:
+ break;
+ }
+ dex_pc += instruction.SizeInCodeUnits();
+ code_ptr += instruction.SizeInCodeUnits();
+ }
+
+ // If there is no instruction we are interested in, no need to create a `ProfilingInfo`
+ // object, it will never be filled.
+ if (entries.empty()) {
+ return nullptr;
+ }
+
+ // Allocate the `ProfilingInfo` object int the JIT's data space.
+ jit::JitCodeCache* code_cache = Runtime::Current()->GetJit()->GetCodeCache();
+ size_t profile_info_size = sizeof(ProfilingInfo) + sizeof(InlineCache) * entries.size();
+ uint8_t* data = code_cache->ReserveData(Thread::Current(), profile_info_size);
+
+ if (data == nullptr) {
+ VLOG(jit) << "Cannot allocate profiling info anymore";
+ return nullptr;
+ }
+
+ return new (data) ProfilingInfo(entries);
+}
+
+void ProfilingInfo::AddInvokeInfo(Thread* self, uint32_t dex_pc, mirror::Class* cls) {
+ InlineCache* cache = nullptr;
+ // TODO: binary search if array is too long.
+ for (size_t i = 0; i < number_of_inline_caches_; ++i) {
+ if (cache_[i].dex_pc == dex_pc) {
+ cache = &cache_[i];
+ break;
+ }
+ }
+ DCHECK(cache != nullptr);
+
+ ScopedObjectAccess soa(self);
+ for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
+ mirror::Class* existing = cache->classes_[i].Read<kWithoutReadBarrier>();
+ if (existing == cls) {
+ // Receiver type is already in the cache, nothing else to do.
+ return;
+ } else if (existing == nullptr) {
+ // Cache entry is empty, try to put `cls` in it.
+ GcRoot<mirror::Class> expected_root(nullptr);
+ GcRoot<mirror::Class> desired_root(cls);
+ if (!reinterpret_cast<Atomic<GcRoot<mirror::Class>>*>(&cache->classes_[i])->
+ CompareExchangeStrongSequentiallyConsistent(expected_root, desired_root)) {
+ // Some other thread put a class in the cache, continue iteration starting at this
+ // entry in case the entry contains `cls`.
+ --i;
+ } else {
+ // We successfully set `cls`, just return.
+ return;
+ }
+ }
+ }
+ // Unsuccessfull - cache is full, making it megamorphic.
+ DCHECK(cache->IsMegamorphic());
+}
+
+} // namespace art
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
new file mode 100644
index 0000000..73ca41a
--- /dev/null
+++ b/runtime/jit/profiling_info.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_JIT_PROFILING_INFO_H_
+#define ART_RUNTIME_JIT_PROFILING_INFO_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "gc_root.h"
+
+namespace art {
+
+class ArtMethod;
+
+namespace mirror {
+class Class;
+}
+
+/**
+ * Profiling info for a method, created and filled by the interpreter once the
+ * method is warm, and used by the compiler to drive optimizations.
+ */
+class ProfilingInfo {
+ public:
+ static ProfilingInfo* Create(ArtMethod* method);
+
+ // Add information from an executed INVOKE instruction to the profile.
+ void AddInvokeInfo(Thread* self, uint32_t dex_pc, mirror::Class* cls);
+
+ // NO_THREAD_SAFETY_ANALYSIS since we don't know what the callback requires.
+ template<typename RootVisitorType>
+ void VisitRoots(RootVisitorType& visitor) NO_THREAD_SAFETY_ANALYSIS {
+ for (size_t i = 0; i < number_of_inline_caches_; ++i) {
+ InlineCache* cache = &cache_[i];
+ for (size_t j = 0; j < InlineCache::kIndividualCacheSize; ++j) {
+ visitor.VisitRootIfNonNull(cache->classes_[j].AddressWithoutBarrier());
+ }
+ }
+ }
+
+ private:
+ // Structure to store the classes seen at runtime for a specific instruction.
+ // Once the classes_ array is full, we consider the INVOKE to be megamorphic.
+ struct InlineCache {
+ bool IsMonomorphic() const {
+ DCHECK_GE(kIndividualCacheSize, 2);
+ return !classes_[0].IsNull() && classes_[1].IsNull();
+ }
+
+ bool IsMegamorphic() const {
+ for (size_t i = 0; i < kIndividualCacheSize; ++i) {
+ if (classes_[i].IsNull()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool IsUnitialized() const {
+ return classes_[0].IsNull();
+ }
+
+ bool IsPolymorphic() const {
+ DCHECK_GE(kIndividualCacheSize, 3);
+ return !classes_[1].IsNull() && classes_[kIndividualCacheSize - 1].IsNull();
+ }
+
+ static constexpr uint16_t kIndividualCacheSize = 5;
+ uint32_t dex_pc;
+ GcRoot<mirror::Class> classes_[kIndividualCacheSize];
+ };
+
+ explicit ProfilingInfo(const std::vector<uint32_t>& entries)
+ : number_of_inline_caches_(entries.size()) {
+ memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache));
+ for (size_t i = 0; i < number_of_inline_caches_; ++i) {
+ cache_[i].dex_pc = entries[i];
+ }
+ }
+
+ // Number of instructions we are profiling in the ArtMethod.
+ const uint32_t number_of_inline_caches_;
+
+ // Dynamically allocated array of size `number_of_inline_caches_`.
+ InlineCache cache_[0];
+
+ DISALLOW_COPY_AND_ASSIGN(ProfilingInfo);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_JIT_PROFILING_INFO_H_
diff --git a/runtime/lambda/art_lambda_method.cc b/runtime/lambda/art_lambda_method.cc
new file mode 100644
index 0000000..6f9f8bb
--- /dev/null
+++ b/runtime/lambda/art_lambda_method.cc
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "lambda/art_lambda_method.h"
+
+#include "base/logging.h"
+#include "lambda/shorty_field_type.h"
+
+namespace art {
+namespace lambda {
+
+ArtLambdaMethod::ArtLambdaMethod(ArtMethod* target_method,
+ const char* captured_variables_type_descriptor,
+ const char* captured_variables_shorty,
+ bool innate_lambda)
+ : method_(target_method),
+ captured_variables_type_descriptor_(captured_variables_type_descriptor),
+ captured_variables_shorty_(captured_variables_shorty),
+ innate_lambda_(innate_lambda) {
+ DCHECK(target_method != nullptr);
+ DCHECK(captured_variables_type_descriptor != nullptr);
+ DCHECK(captured_variables_shorty != nullptr);
+
+ // Calculate the static closure size from the captured variables.
+ size_t size = sizeof(ArtLambdaMethod*); // Initial size is just this method.
+ bool static_size = true;
+ const char* shorty = captured_variables_shorty_;
+ while (shorty != nullptr && *shorty != '\0') {
+ // Each captured variable also appends to the size.
+ ShortyFieldType shorty_field{*shorty}; // NOLINT [readability/braces] [4]
+ size += shorty_field.GetStaticSize();
+ static_size &= shorty_field.IsStaticSize();
+ ++shorty;
+ }
+ closure_size_ = size;
+
+ // We determine whether or not the size is dynamic by checking for nested lambdas.
+ //
+ // This is conservative, since in theory an optimization could determine the size
+ // of the nested lambdas recursively. In practice it's probably better to flatten out
+ // nested lambdas and inline all their code if they are known statically.
+ dynamic_size_ = !static_size;
+
+ if (kIsDebugBuild) {
+ // Double check that the number of captured variables match in both strings.
+ size_t shorty_count = strlen(captured_variables_shorty);
+
+ size_t long_count = 0;
+ const char* long_type = captured_variables_type_descriptor;
+ ShortyFieldType out;
+ while ((long_type = ShortyFieldType::ParseFromFieldTypeDescriptor(long_type, &out))
+ != nullptr) {
+ ++long_count;
+ }
+
+ DCHECK_EQ(shorty_count, long_count)
+ << "number of captured variables in long type '" << captured_variables_type_descriptor
+ << "' (" << long_count << ")" << " did not match short type '"
+ << captured_variables_shorty << "' (" << shorty_count << ")";
+ }
+}
+
+} // namespace lambda
+} // namespace art
diff --git a/runtime/lambda/art_lambda_method.h b/runtime/lambda/art_lambda_method.h
new file mode 100644
index 0000000..892d8c6
--- /dev/null
+++ b/runtime/lambda/art_lambda_method.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ART_RUNTIME_LAMBDA_ART_LAMBDA_METHOD_H_
+#define ART_RUNTIME_LAMBDA_ART_LAMBDA_METHOD_H_
+
+#include "base/macros.h"
+#include "art_method.h"
+
+#include <stdint.h>
+
+namespace art {
+namespace lambda {
+
+class ArtLambdaMethod {
+ public:
+ // Construct an art lambda method.
+ // The target method is the one invoked by invoke-lambda.
+ // The type descriptor describes the types of variables captured, e.g. "ZFLObject;\FI;[Z"
+ // The shorty drops the object name and treats arrays as objects, e.g. "ZFL\L"
+ // Innate lambda means that the lambda was originally created via invoke-lambda.
+ // -- Non-innate lambdas (learned lambdas) come from a regular class that was boxed to lambda.
+ // (Ownership of strings is retained by the caller and the lifetime should exceed this class).
+ ArtLambdaMethod(ArtMethod* target_method,
+ const char* captured_variables_type_descriptor,
+ const char* captured_variables_shorty_,
+ bool innate_lambda = true);
+
+ // Get the target method for this lambda that would be used by the invoke-lambda dex instruction.
+ ArtMethod* GetArtMethod() const {
+ return method_;
+ }
+
+ // Get the compile-time size of lambda closures for this method in bytes.
+ // This is circular (that is, it includes the size of the ArtLambdaMethod pointer).
+ // One should also check if the size is dynamic since nested lambdas have a runtime size.
+ size_t GetStaticClosureSize() const {
+ return closure_size_;
+ }
+
+ // Get the type descriptor for the list of captured variables.
+ // e.g. "ZFLObject;\FI;[Z" means a captured int, float, class Object, lambda FI, array of ints
+ const char* GetCapturedVariablesTypeDescriptor() const {
+ return captured_variables_type_descriptor_;
+ }
+
+ // Get the shorty 'field' type descriptor list of captured variables.
+ // This follows the same rules as a string of ShortyFieldType in the dex specification.
+ // Every captured variable is represented by exactly one character.
+ // - Objects become 'L'.
+ // - Arrays become 'L'.
+ // - Lambdas become '\'.
+ const char* GetCapturedVariablesShortyTypeDescriptor() const {
+ return captured_variables_shorty_;
+ }
+
+ // Will the size of this lambda change at runtime?
+ // Only returns true if there is a nested lambda that we can't determine statically the size of.
+ bool IsDynamicSize() const {
+ return dynamic_size_;
+ }
+
+ // Will the size of this lambda always be constant at runtime?
+ // This generally means there's no nested lambdas, or we were able to successfully determine
+ // their size statically at compile time.
+ bool IsStaticSize() const {
+ return !IsDynamicSize();
+ }
+ // Is this a lambda that was originally created via invoke-lambda?
+ // -- Non-innate lambdas (learned lambdas) come from a regular class that was boxed to lambda.
+ bool IsInnateLambda() const {
+ return innate_lambda_;
+ }
+
+ // How many variables were captured?
+ // (Each nested lambda counts as 1 captured var regardless of how many captures it itself has).
+ size_t GetNumberOfCapturedVariables() const {
+ return strlen(captured_variables_shorty_);
+ }
+
+ private:
+ // TODO: ArtMethod, or at least the entry points should be inlined into this struct
+ // to avoid an extra indirect load when doing invokes.
+ // Target method that invoke-lambda will jump to.
+ ArtMethod* method_;
+ // How big the closure is (in bytes). Only includes the constant size.
+ size_t closure_size_;
+ // The type descriptor for the captured variables, e.g. "IS" for [int, short]
+ const char* captured_variables_type_descriptor_;
+ // The shorty type descriptor for captured vars, (e.g. using 'L' instead of 'LObject;')
+ const char* captured_variables_shorty_;
+ // Whether or not the size is dynamic. If it is, copiers need to read the Closure size at runtime.
+ bool dynamic_size_;
+ // True if this lambda was originally made with create-lambda,
+ // false if it came from a class instance (through new-instance and then unbox-lambda).
+ bool innate_lambda_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArtLambdaMethod);
+};
+
+} // namespace lambda
+} // namespace art
+
+#endif // ART_RUNTIME_LAMBDA_ART_LAMBDA_METHOD_H_
diff --git a/runtime/lambda/closure.cc b/runtime/lambda/closure.cc
new file mode 100644
index 0000000..95a17c6
--- /dev/null
+++ b/runtime/lambda/closure.cc
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "lambda/closure.h"
+
+#include "base/logging.h"
+#include "lambda/art_lambda_method.h"
+#include "runtime/mirror/object_reference.h"
+
+static constexpr const bool kClosureSupportsReferences = false;
+static constexpr const bool kClosureSupportsGarbageCollection = false;
+
+namespace art {
+namespace lambda {
+
+template <typename T>
+// TODO: can I return T __attribute__((__aligned__(1)))* here instead?
+const uint8_t* Closure::GetUnsafeAtOffset(size_t offset) const {
+ // Do not DCHECK here with existing helpers since most of them will call into this function.
+ return reinterpret_cast<const uint8_t*>(captured_) + offset;
+}
+
+size_t Closure::GetCapturedVariableSize(ShortyFieldType variable_type, size_t offset) const {
+ switch (variable_type) {
+ case ShortyFieldType::kLambda:
+ {
+ return GetClosureSize(GetUnsafeAtOffset<Closure>(offset));
+ }
+ default:
+ DCHECK(variable_type.IsStaticSize());
+ return variable_type.GetStaticSize();
+ }
+}
+
+// Templatize the flags to give the compiler a fighting chance to eliminate
+// any unnecessary code through different uses of this function.
+template <Closure::VariableInfo::Flags flags>
+inline Closure::VariableInfo Closure::ParseTypeDescriptor(const char* type_descriptor,
+ size_t upto_index) const {
+ DCHECK(type_descriptor != nullptr);
+
+ VariableInfo result;
+
+ ShortyFieldType last_type;
+ size_t offset = (flags & VariableInfo::kOffset) ? GetStartingOffset() : 0;
+ size_t prev_offset = 0;
+ size_t count = 0;
+
+ while ((type_descriptor =
+ ShortyFieldType::ParseFromFieldTypeDescriptor(type_descriptor, &last_type)) != nullptr) {
+ count++;
+
+ if (flags & VariableInfo::kOffset) {
+ // Accumulate the sizes of all preceding captured variables as the current offset only.
+ offset += prev_offset;
+ prev_offset = GetCapturedVariableSize(last_type, offset);
+ }
+
+ if ((count > upto_index)) {
+ break;
+ }
+ }
+
+ if (flags & VariableInfo::kVariableType) {
+ result.variable_type_ = last_type;
+ }
+
+ if (flags & VariableInfo::kIndex) {
+ result.index_ = count;
+ }
+
+ if (flags & VariableInfo::kCount) {
+ result.count_ = count;
+ }
+
+ if (flags & VariableInfo::kOffset) {
+ result.offset_ = offset;
+ }
+
+ // TODO: We should probably store the result of this in the ArtLambdaMethod,
+ // to avoid re-computing the data every single time for static closures.
+ return result;
+}
+
+size_t Closure::GetCapturedVariablesSize() const {
+ const size_t captured_variable_offset = offsetof(Closure, captured_);
+ DCHECK_GE(GetSize(), captured_variable_offset); // Prevent underflows.
+ return GetSize() - captured_variable_offset;
+}
+
+size_t Closure::GetSize() const {
+ const size_t static_closure_size = lambda_info_->GetStaticClosureSize();
+ if (LIKELY(lambda_info_->IsStaticSize())) {
+ return static_closure_size;
+ }
+
+ DCHECK_GE(static_closure_size, sizeof(captured_[0].dynamic_.size_));
+ const size_t dynamic_closure_size = captured_[0].dynamic_.size_;
+ // The dynamic size better be at least as big as the static size.
+ DCHECK_GE(dynamic_closure_size, static_closure_size);
+
+ return dynamic_closure_size;
+}
+
+void Closure::CopyTo(void* target, size_t target_size) const {
+ DCHECK_GE(target_size, GetSize());
+
+ // TODO: using memcpy is unsafe with read barriers, fix this once we add reference support
+ static_assert(kClosureSupportsReferences == false,
+ "Do not use memcpy with readbarrier references");
+ memcpy(target, this, GetSize());
+}
+
+size_t Closure::GetNumberOfCapturedVariables() const {
+ // TODO: refactor into art_lambda_method.h. Parsing should only be required here as a DCHECK.
+ VariableInfo variable_info =
+ ParseTypeDescriptor<VariableInfo::kCount>(GetCapturedVariablesTypeDescriptor(),
+ VariableInfo::kUpToIndexMax);
+ size_t count = variable_info.count_;
+ // Assuming each variable was 1 byte, the size should always be greater or equal than the count.
+ DCHECK_LE(count, GetCapturedVariablesSize());
+ return count;
+}
+
+const char* Closure::GetCapturedVariablesTypeDescriptor() const {
+ return lambda_info_->GetCapturedVariablesTypeDescriptor();
+}
+
+ShortyFieldType Closure::GetCapturedShortyType(size_t index) const {
+ DCHECK_LT(index, GetNumberOfCapturedVariables());
+
+ VariableInfo variable_info =
+ ParseTypeDescriptor<VariableInfo::kVariableType>(GetCapturedVariablesTypeDescriptor(),
+ index);
+
+ return variable_info.variable_type_;
+}
+
+uint32_t Closure::GetCapturedPrimitiveNarrow(size_t index) const {
+ DCHECK(GetCapturedShortyType(index).IsPrimitiveNarrow());
+
+ ShortyFieldType variable_type;
+ size_t offset;
+ GetCapturedVariableTypeAndOffset(index, &variable_type, &offset);
+
+ // TODO: Restructure to use template specialization, e.g. GetCapturedPrimitive<T>
+ // so that we can avoid this nonsense regarding memcpy always overflowing.
+ // Plus, this additional switching seems redundant since the interpreter
+ // would've done it already, and knows the exact type.
+ uint32_t result = 0;
+ static_assert(ShortyFieldTypeTraits::IsPrimitiveNarrowType<decltype(result)>(),
+ "result must be a primitive narrow type");
+ switch (variable_type) {
+ case ShortyFieldType::kBoolean:
+ CopyUnsafeAtOffset<bool>(offset, &result);
+ break;
+ case ShortyFieldType::kByte:
+ CopyUnsafeAtOffset<uint8_t>(offset, &result);
+ break;
+ case ShortyFieldType::kChar:
+ CopyUnsafeAtOffset<uint16_t>(offset, &result);
+ break;
+ case ShortyFieldType::kShort:
+ CopyUnsafeAtOffset<int16_t>(offset, &result);
+ break;
+ case ShortyFieldType::kInt:
+ CopyUnsafeAtOffset<int32_t>(offset, &result);
+ break;
+ case ShortyFieldType::kFloat:
+ // XX: Maybe there should just be a GetCapturedPrimitive<T> to avoid this shuffle?
+ // The interpreter's invoke seems to only special case references and wides,
+ // everything else is treated as a generic 32-bit pattern.
+ CopyUnsafeAtOffset<float>(offset, &result);
+ break;
+ default:
+ LOG(FATAL)
+ << "expected a valid narrow primitive shorty type but got "
+ << static_cast<char>(variable_type);
+ UNREACHABLE();
+ }
+
+ return result;
+}
+
+uint64_t Closure::GetCapturedPrimitiveWide(size_t index) const {
+ DCHECK(GetCapturedShortyType(index).IsPrimitiveWide());
+
+ ShortyFieldType variable_type;
+ size_t offset;
+ GetCapturedVariableTypeAndOffset(index, &variable_type, &offset);
+
+ // TODO: Restructure to use template specialization, e.g. GetCapturedPrimitive<T>
+ // so that we can avoid this nonsense regarding memcpy always overflowing.
+ // Plus, this additional switching seems redundant since the interpreter
+ // would've done it already, and knows the exact type.
+ uint64_t result = 0;
+ static_assert(ShortyFieldTypeTraits::IsPrimitiveWideType<decltype(result)>(),
+ "result must be a primitive wide type");
+ switch (variable_type) {
+ case ShortyFieldType::kLong:
+ CopyUnsafeAtOffset<int64_t>(offset, &result);
+ break;
+ case ShortyFieldType::kDouble:
+ CopyUnsafeAtOffset<double>(offset, &result);
+ break;
+ default:
+ LOG(FATAL)
+ << "expected a valid primitive wide shorty type but got "
+ << static_cast<char>(variable_type);
+ UNREACHABLE();
+ }
+
+ return result;
+}
+
+mirror::Object* Closure::GetCapturedObject(size_t index) const {
+ DCHECK(GetCapturedShortyType(index).IsObject());
+
+ ShortyFieldType variable_type;
+ size_t offset;
+ GetCapturedVariableTypeAndOffset(index, &variable_type, &offset);
+
+ // TODO: Restructure to use template specialization, e.g. GetCapturedPrimitive<T>
+ // so that we can avoid this nonsense regarding memcpy always overflowing.
+ // Plus, this additional switching seems redundant since the interpreter
+ // would've done it already, and knows the exact type.
+ mirror::Object* result = nullptr;
+ static_assert(ShortyFieldTypeTraits::IsObjectType<decltype(result)>(),
+ "result must be an object type");
+ switch (variable_type) {
+ case ShortyFieldType::kObject:
+ // TODO: This seems unsafe. This may need to use gcroots.
+ static_assert(kClosureSupportsGarbageCollection == false,
+ "May need GcRoots and definitely need mutator locks");
+ {
+ mirror::CompressedReference<mirror::Object> compressed_result;
+ CopyUnsafeAtOffset<uint32_t>(offset, &compressed_result);
+ result = compressed_result.AsMirrorPtr();
+ }
+ break;
+ default:
+ CHECK(false)
+ << "expected a valid shorty type but got " << static_cast<char>(variable_type);
+ UNREACHABLE();
+ }
+
+ return result;
+}
+
+size_t Closure::GetCapturedClosureSize(size_t index) const {
+ DCHECK(GetCapturedShortyType(index).IsLambda());
+ size_t offset = GetCapturedVariableOffset(index);
+
+ auto* captured_ptr = reinterpret_cast<const uint8_t*>(&captured_);
+ size_t closure_size = GetClosureSize(captured_ptr + offset);
+
+ return closure_size;
+}
+
+void Closure::CopyCapturedClosure(size_t index, void* destination, size_t destination_room) const {
+ DCHECK(GetCapturedShortyType(index).IsLambda());
+ size_t offset = GetCapturedVariableOffset(index);
+
+ auto* captured_ptr = reinterpret_cast<const uint8_t*>(&captured_);
+ size_t closure_size = GetClosureSize(captured_ptr + offset);
+
+ static_assert(ShortyFieldTypeTraits::IsLambdaType<Closure*>(),
+ "result must be a lambda type");
+
+ CopyUnsafeAtOffset<Closure>(offset, destination, closure_size, destination_room);
+}
+
+size_t Closure::GetCapturedVariableOffset(size_t index) const {
+ VariableInfo variable_info =
+ ParseTypeDescriptor<VariableInfo::kOffset>(GetCapturedVariablesTypeDescriptor(),
+ index);
+
+ size_t offset = variable_info.offset_;
+
+ return offset;
+}
+
+void Closure::GetCapturedVariableTypeAndOffset(size_t index,
+ ShortyFieldType* out_type,
+ size_t* out_offset) const {
+ DCHECK(out_type != nullptr);
+ DCHECK(out_offset != nullptr);
+
+ static constexpr const VariableInfo::Flags kVariableTypeAndOffset =
+ static_cast<VariableInfo::Flags>(VariableInfo::kVariableType | VariableInfo::kOffset);
+ VariableInfo variable_info =
+ ParseTypeDescriptor<kVariableTypeAndOffset>(GetCapturedVariablesTypeDescriptor(),
+ index);
+
+ ShortyFieldType variable_type = variable_info.variable_type_;
+ size_t offset = variable_info.offset_;
+
+ *out_type = variable_type;
+ *out_offset = offset;
+}
+
+template <typename T>
+void Closure::CopyUnsafeAtOffset(size_t offset,
+ void* destination,
+ size_t src_size,
+ size_t destination_room) const {
+ DCHECK_GE(destination_room, src_size);
+ const uint8_t* data_ptr = GetUnsafeAtOffset<T>(offset);
+ memcpy(destination, data_ptr, sizeof(T));
+}
+
+// TODO: This is kind of ugly. I would prefer an unaligned_ptr<Closure> here.
+// Unfortunately C++ doesn't let you lower the alignment (i.e. alignas(1) Closure*) is not legal.
+size_t Closure::GetClosureSize(const uint8_t* closure) {
+ DCHECK(closure != nullptr);
+
+ static_assert(!std::is_base_of<mirror::Object, Closure>::value,
+ "It might be unsafe to call memcpy on a managed object");
+
+ // Safe as long as it's not a mirror Object.
+ // TODO: Should probably wrap this in like MemCpyNative or some such which statically asserts
+ // we aren't trying to copy mirror::Object data around.
+ ArtLambdaMethod* closure_info;
+ memcpy(&closure_info, closure + offsetof(Closure, lambda_info_), sizeof(closure_info));
+
+ if (LIKELY(closure_info->IsStaticSize())) {
+ return closure_info->GetStaticClosureSize();
+ }
+
+ // The size is dynamic, so we need to read it from captured_variables_ portion.
+ size_t dynamic_size;
+ memcpy(&dynamic_size,
+ closure + offsetof(Closure, captured_[0].dynamic_.size_),
+ sizeof(dynamic_size));
+ static_assert(sizeof(dynamic_size) == sizeof(captured_[0].dynamic_.size_),
+ "Dynamic size type must match the structural type of the size");
+
+ DCHECK_GE(dynamic_size, closure_info->GetStaticClosureSize());
+ return dynamic_size;
+}
+
+size_t Closure::GetStartingOffset() const {
+ static constexpr const size_t captured_offset = offsetof(Closure, captured_);
+ if (LIKELY(lambda_info_->IsStaticSize())) {
+ return offsetof(Closure, captured_[0].static_variables_) - captured_offset;
+ } else {
+ return offsetof(Closure, captured_[0].dynamic_.variables_) - captured_offset;
+ }
+}
+
+} // namespace lambda
+} // namespace art
diff --git a/runtime/lambda/closure.h b/runtime/lambda/closure.h
new file mode 100644
index 0000000..60d117e
--- /dev/null
+++ b/runtime/lambda/closure.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ART_RUNTIME_LAMBDA_CLOSURE_H_
+#define ART_RUNTIME_LAMBDA_CLOSURE_H_
+
+#include "base/macros.h"
+#include "base/mutex.h" // For Locks::mutator_lock_.
+#include "lambda/shorty_field_type.h"
+
+#include <stdint.h>
+
+namespace art {
+class ArtMethod; // forward declaration
+
+namespace mirror {
+class Object; // forward declaration
+} // namespace mirror
+
+namespace lambda {
+class ArtLambdaMethod; // forward declaration
+class ClosureBuilder; // forward declaration
+
+// Inline representation of a lambda closure.
+// Contains the target method and the set of packed captured variables as a copy.
+//
+// The closure itself is logically immutable, although in practice any object references
+// it (recursively) contains can be moved and updated by the GC.
+struct PACKED(sizeof(ArtLambdaMethod*)) Closure {
+ // Get the size of the Closure in bytes.
+ // This is necessary in order to allocate a large enough area to copy the Closure into.
+ // Do *not* copy the closure with memcpy, since references also need to get moved.
+ size_t GetSize() const;
+
+ // Copy this closure into the target, whose memory size is specified by target_size.
+ // Any object references are fixed up during the copy (if there was a read barrier).
+ // The target_size must be at least as large as GetSize().
+ void CopyTo(void* target, size_t target_size) const;
+
+ // How many variables were captured?
+ size_t GetNumberOfCapturedVariables() const;
+
+ // Returns a type descriptor string that represents each captured variable.
+ // e.g. "Ljava/lang/Object;ZB" would mean a capture tuple of (Object, boolean, byte)
+ const char* GetCapturedVariablesTypeDescriptor() const;
+
+ // Returns the short type for the captured variable at index.
+ // Index must be less than the number of captured variables.
+ ShortyFieldType GetCapturedShortyType(size_t index) const;
+
+ // Returns the 32-bit representation of a non-wide primitive at the captured variable index.
+ // Smaller types are zero extended.
+ // Index must be less than the number of captured variables.
+ uint32_t GetCapturedPrimitiveNarrow(size_t index) const;
+ // Returns the 64-bit representation of a wide primitive at the captured variable index.
+ // Smaller types are zero extended.
+ // Index must be less than the number of captured variables.
+ uint64_t GetCapturedPrimitiveWide(size_t index) const;
+ // Returns the object reference at the captured variable index.
+ // The type at the index *must* be an object reference or a CHECK failure will occur.
+ // Index must be less than the number of captured variables.
+ mirror::Object* GetCapturedObject(size_t index) const SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Gets the size of a nested capture closure in bytes, at the captured variable index.
+ // The type at the index *must* be a lambda closure or a CHECK failure will occur.
+ size_t GetCapturedClosureSize(size_t index) const;
+
+ // Copies a nested lambda closure at the captured variable index.
+ // The destination must have enough room for the closure (see GetCapturedClosureSize).
+ void CopyCapturedClosure(size_t index, void* destination, size_t destination_room) const;
+
+ private:
+ // Read out any non-lambda value as a copy.
+ template <typename T>
+ T GetCapturedVariable(size_t index) const;
+
+ // Reconstruct the closure's captured variable info at runtime.
+ struct VariableInfo {
+ size_t index_;
+ ShortyFieldType variable_type_;
+ size_t offset_;
+ size_t count_;
+
+ enum Flags {
+ kIndex = 0x1,
+ kVariableType = 0x2,
+ kOffset = 0x4,
+ kCount = 0x8,
+ };
+
+ // Traverse to the end of the type descriptor list instead of stopping at some particular index.
+ static constexpr size_t kUpToIndexMax = static_cast<size_t>(-1);
+ };
+
+ // Parse a type descriptor, stopping at index "upto_index".
+ // Returns only the information requested in flags. All other fields are indeterminate.
+ template <VariableInfo::Flags flags>
+ inline VariableInfo ALWAYS_INLINE ParseTypeDescriptor(const char* type_descriptor,
+ size_t upto_index) const;
+
+ // Convenience function to call ParseTypeDescriptor with just the type and offset.
+ void GetCapturedVariableTypeAndOffset(size_t index,
+ ShortyFieldType* out_type,
+ size_t* out_offset) const;
+
+ // How many bytes do the captured variables take up? Runtime sizeof(captured_variables).
+ size_t GetCapturedVariablesSize() const;
+ // Get the size in bytes of the variable_type which is potentially stored at offset.
+ size_t GetCapturedVariableSize(ShortyFieldType variable_type, size_t offset) const;
+ // Get the starting offset (in bytes) for the 0th captured variable.
+ // All offsets are relative to 'captured_'.
+ size_t GetStartingOffset() const;
+ // Get the offset for this index.
+ // All offsets are relative to 'captuerd_'.
+ size_t GetCapturedVariableOffset(size_t index) const;
+
+ // Cast the data at '(char*)captured_[offset]' into T, returning its address.
+ // This value should not be de-referenced directly since its unaligned.
+ template <typename T>
+ inline const uint8_t* GetUnsafeAtOffset(size_t offset) const;
+
+ // Copy the data at the offset into the destination. DCHECKs that
+ // the destination_room is large enough (in bytes) to fit the data.
+ template <typename T>
+ inline void CopyUnsafeAtOffset(size_t offset,
+ void* destination,
+ size_t src_size = sizeof(T),
+ size_t destination_room = sizeof(T)) const;
+
+ // Get the closure size from an unaligned (i.e. interior) closure pointer.
+ static size_t GetClosureSize(const uint8_t* closure);
+
+ ///////////////////////////////////////////////////////////////////////////////////
+
+ // Compile-time known lambda information such as the type descriptor and size.
+ ArtLambdaMethod* lambda_info_;
+
+ // A contiguous list of captured variables, and possibly the closure size.
+ // The runtime size can always be determined through GetSize().
+ union {
+ // Read from here if the closure size is static (ArtLambdaMethod::IsStatic)
+ uint8_t static_variables_[0];
+ struct {
+ // Read from here if the closure size is dynamic (ArtLambdaMethod::IsDynamic)
+ size_t size_; // The lambda_info_ and the size_ itself is also included as part of the size.
+ uint8_t variables_[0];
+ } dynamic_;
+ } captured_[0];
+ // captured_ will always consist of one array element at runtime.
+ // Set to [0] so that 'size_' is not counted in sizeof(Closure).
+
+ friend class ClosureBuilder;
+ friend class ClosureTest;
+};
+
+} // namespace lambda
+} // namespace art
+
+#endif // ART_RUNTIME_LAMBDA_CLOSURE_H_
diff --git a/runtime/lambda/closure_builder-inl.h b/runtime/lambda/closure_builder-inl.h
new file mode 100644
index 0000000..41a803b
--- /dev/null
+++ b/runtime/lambda/closure_builder-inl.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_LAMBDA_CLOSURE_BUILDER_INL_H_
+#define ART_RUNTIME_LAMBDA_CLOSURE_BUILDER_INL_H_
+
+#include "lambda/closure_builder.h"
+#include <string.h>
+
+namespace art {
+namespace lambda {
+
+template <typename T, ClosureBuilder::ShortyTypeEnum kShortyType>
+void ClosureBuilder::CaptureVariablePrimitive(T value) {
+ static_assert(ShortyFieldTypeTraits::IsPrimitiveType<T>(), "T must be a primitive type");
+ const size_t type_size = ShortyFieldType(kShortyType).GetStaticSize();
+ DCHECK_EQ(type_size, sizeof(T));
+
+ // Copy the data while retaining the bit pattern. Strict-aliasing safe.
+ ShortyFieldTypeTraits::MaxType value_storage = 0;
+ memcpy(&value_storage, &value, sizeof(T));
+
+ values_.push_back(value_storage);
+ size_ += sizeof(T);
+}
+
+} // namespace lambda
+} // namespace art
+
+#endif // ART_RUNTIME_LAMBDA_CLOSURE_BUILDER_INL_H_
diff --git a/runtime/lambda/closure_builder.cc b/runtime/lambda/closure_builder.cc
new file mode 100644
index 0000000..9c37db8
--- /dev/null
+++ b/runtime/lambda/closure_builder.cc
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "lambda/closure_builder.h"
+
+#include "base/macros.h"
+#include "base/value_object.h"
+#include "lambda/art_lambda_method.h"
+#include "lambda/closure.h"
+#include "lambda/shorty_field_type.h"
+#include "runtime/mirror/object_reference.h"
+
+#include <stdint.h>
+#include <vector>
+
+namespace art {
+namespace lambda {
+
+/*
+ * GC support TODOs:
+ * (Although there's some code for storing objects, it is UNIMPLEMENTED(FATAL) because it is
+ * incomplete).
+ *
+ * 1) GC needs to be able to traverse the Closure and visit any references.
+ * It might be possible to get away with global roots in the short term.
+ *
+ * 2) Add brooks read barrier support. We can store the black/gray/white bits
+ * in the lower 2 bits of the lambda art method pointer. Whenever a closure is copied
+ * [to the stack] we'd need to add a cold path to turn it black.
+ * (since there's only 3 colors, I can use the 4th value to indicate no-refs).
+ * e.g. 0x0 = gray, 0x1 = white, 0x2 = black, 0x3 = no-nested-references
+ * - Alternatively the GC can mark reference-less closures as always-black,
+ * although it would need extra work to check for references.
+ */
+
+void ClosureBuilder::CaptureVariableObject(mirror::Object* object) {
+ auto compressed_reference = mirror::CompressedReference<mirror::Object>::FromMirrorPtr(object);
+ ShortyFieldTypeTraits::MaxType storage = 0;
+
+ static_assert(sizeof(storage) >= sizeof(compressed_reference),
+ "not enough room to store a compressed reference");
+ memcpy(&storage, &compressed_reference, sizeof(compressed_reference));
+
+ values_.push_back(storage);
+ size_ += kObjectReferenceSize;
+
+ static_assert(kObjectReferenceSize == sizeof(compressed_reference), "reference size mismatch");
+
+ // TODO: needs more work to support concurrent GC
+ if (kIsDebugBuild) {
+ if (kUseReadBarrier) {
+ UNIMPLEMENTED(FATAL) << "can't yet safely capture objects with read barrier";
+ }
+ }
+}
+
+void ClosureBuilder::CaptureVariableLambda(Closure* closure) {
+ DCHECK(closure != nullptr); // null closures not allowed, target method must be null instead.
+ values_.push_back(reinterpret_cast<ShortyFieldTypeTraits::MaxType>(closure));
+
+ if (LIKELY(is_dynamic_size_ == false)) {
+ // Write in the extra bytes to store the dynamic size the first time.
+ is_dynamic_size_ = true;
+ size_ += sizeof(Closure::captured_[0].dynamic_.size_);
+ }
+
+ // A closure may be sized dynamically, so always query it for the true size.
+ size_ += closure->GetSize();
+}
+
+size_t ClosureBuilder::GetSize() const {
+ return size_;
+}
+
+size_t ClosureBuilder::GetCaptureCount() const {
+ return values_.size();
+}
+
+Closure* ClosureBuilder::CreateInPlace(void* memory, ArtLambdaMethod* target_method) const {
+ DCHECK(memory != nullptr);
+ DCHECK(target_method != nullptr);
+ DCHECK_EQ(is_dynamic_size_, target_method->IsDynamicSize());
+
+ CHECK_EQ(target_method->GetNumberOfCapturedVariables(), values_.size())
+ << "number of variables captured at runtime does not match "
+ << "number of variables captured at compile time";
+
+ Closure* closure = new (memory) Closure;
+ closure->lambda_info_ = target_method;
+
+ static_assert(offsetof(Closure, captured_) == kInitialSize, "wrong initial size");
+
+ size_t written_size;
+ if (UNLIKELY(is_dynamic_size_)) {
+ // The closure size must be set dynamically (i.e. nested lambdas).
+ closure->captured_[0].dynamic_.size_ = GetSize();
+ size_t header_size = offsetof(Closure, captured_[0].dynamic_.variables_);
+ DCHECK_LE(header_size, GetSize());
+ size_t variables_size = GetSize() - header_size;
+ written_size =
+ WriteValues(target_method,
+ closure->captured_[0].dynamic_.variables_,
+ header_size,
+ variables_size);
+ } else {
+ // The closure size is known statically (i.e. no nested lambdas).
+ DCHECK(GetSize() == target_method->GetStaticClosureSize());
+ size_t header_size = offsetof(Closure, captured_[0].static_variables_);
+ DCHECK_LE(header_size, GetSize());
+ size_t variables_size = GetSize() - header_size;
+ written_size =
+ WriteValues(target_method,
+ closure->captured_[0].static_variables_,
+ header_size,
+ variables_size);
+ }
+
+ DCHECK_EQ(written_size, closure->GetSize());
+
+ return closure;
+}
+
+size_t ClosureBuilder::WriteValues(ArtLambdaMethod* target_method,
+ uint8_t variables[],
+ size_t header_size,
+ size_t variables_size) const {
+ size_t total_size = header_size;
+ const char* shorty_types = target_method->GetCapturedVariablesShortyTypeDescriptor();
+
+ size_t variables_offset = 0;
+ size_t remaining_size = variables_size;
+
+ const size_t shorty_count = target_method->GetNumberOfCapturedVariables();
+ for (size_t i = 0; i < shorty_count; ++i) {
+ ShortyFieldType shorty{shorty_types[i]}; // NOLINT [readability/braces] [4]
+
+ size_t var_size;
+ if (LIKELY(shorty.IsStaticSize())) {
+ // TODO: needs more work to support concurrent GC, e.g. read barriers
+ if (kUseReadBarrier == false) {
+ if (UNLIKELY(shorty.IsObject())) {
+ UNIMPLEMENTED(FATAL) << "can't yet safely write objects with read barrier";
+ }
+ } else {
+ if (UNLIKELY(shorty.IsObject())) {
+ UNIMPLEMENTED(FATAL) << "writing objects not yet supported, no GC support";
+ }
+ }
+
+ var_size = shorty.GetStaticSize();
+ DCHECK_LE(var_size, sizeof(values_[i]));
+
+ // Safe even for objects (non-read barrier case) if we never suspend
+ // while the ClosureBuilder is live.
+ // FIXME: Need to add GC support for references in a closure.
+ memcpy(&variables[variables_offset], &values_[i], var_size);
+ } else {
+ DCHECK(shorty.IsLambda())
+ << " don't support writing dynamically sized types other than lambda";
+
+ ShortyFieldTypeTraits::MaxType closure_raw = values_[i];
+ Closure* nested_closure = reinterpret_cast<Closure*>(closure_raw);
+
+ DCHECK(nested_closure != nullptr);
+ nested_closure->CopyTo(&variables[variables_offset], remaining_size);
+
+ var_size = nested_closure->GetSize();
+ }
+
+ total_size += var_size;
+ DCHECK_GE(remaining_size, var_size);
+ remaining_size -= var_size;
+
+ variables_offset += var_size;
+ }
+
+ DCHECK_EQ('\0', shorty_types[shorty_count]);
+ DCHECK_EQ(variables_offset, variables_size);
+
+ return total_size;
+}
+
+
+} // namespace lambda
+} // namespace art
diff --git a/runtime/lambda/closure_builder.h b/runtime/lambda/closure_builder.h
new file mode 100644
index 0000000..542e12a
--- /dev/null
+++ b/runtime/lambda/closure_builder.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ART_RUNTIME_LAMBDA_CLOSURE_BUILDER_H_
+#define ART_RUNTIME_LAMBDA_CLOSURE_BUILDER_H_
+
+#include "base/macros.h"
+#include "base/mutex.h" // For Locks::mutator_lock_.
+#include "base/value_object.h"
+#include "lambda/shorty_field_type.h"
+
+#include <stdint.h>
+#include <vector>
+
+namespace art {
+class ArtMethod; // forward declaration
+
+namespace mirror {
+class Object; // forward declaration
+} // namespace mirror
+
+namespace lambda {
+class ArtLambdaMethod; // forward declaration
+
+// Build a closure by capturing variables one at a time.
+// When all variables have been marked captured, the closure can be created in-place into
+// a target memory address.
+//
+// The mutator lock must be held for the duration of the lifetime of this object,
+// since it needs to temporarily store heap references into an internal list.
+class ClosureBuilder : ValueObject {
+ public:
+ using ShortyTypeEnum = decltype(ShortyFieldType::kByte);
+
+
+ // Mark this primitive value to be captured as the specified type.
+ template <typename T, ShortyTypeEnum kShortyType>
+ void CaptureVariablePrimitive(T value);
+
+ // Mark this object reference to be captured.
+ void CaptureVariableObject(mirror::Object* object) SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Mark this lambda closure to be captured.
+ void CaptureVariableLambda(Closure* closure);
+
+ // Get the size (in bytes) of the closure.
+ // This size is used to be able to allocate memory large enough to write the closure into.
+ // Call 'CreateInPlace' to actually write the closure out.
+ size_t GetSize() const;
+
+ // Returns how many variables have been captured so far.
+ size_t GetCaptureCount() const;
+
+ // Creates a closure in-place and writes out the data into 'memory'.
+ // Memory must be at least 'GetSize' bytes large.
+ // All previously marked data to be captured is now written out.
+ Closure* CreateInPlace(void* memory, ArtLambdaMethod* target_method) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Locks need to be held for entire lifetime of ClosureBuilder.
+ ClosureBuilder() SHARED_REQUIRES(Locks::mutator_lock_)
+ {}
+
+ // Locks need to be held for entire lifetime of ClosureBuilder.
+ ~ClosureBuilder() SHARED_REQUIRES(Locks::mutator_lock_)
+ {}
+
+ private:
+ // Initial size a closure starts out before any variables are written.
+ // Header size only.
+ static constexpr size_t kInitialSize = sizeof(ArtLambdaMethod*);
+
+ // Write a Closure's variables field from the captured variables.
+ // variables_size specified in bytes, and only includes enough room to write variables into.
+ // Returns the calculated actual size of the closure.
+ size_t WriteValues(ArtLambdaMethod* target_method,
+ uint8_t variables[],
+ size_t header_size,
+ size_t variables_size) const SHARED_REQUIRES(Locks::mutator_lock_);
+
+ size_t size_ = kInitialSize;
+ bool is_dynamic_size_ = false;
+ std::vector<ShortyFieldTypeTraits::MaxType> values_;
+};
+
+} // namespace lambda
+} // namespace art
+
+#endif // ART_RUNTIME_LAMBDA_CLOSURE_BUILDER_H_
diff --git a/runtime/lambda/closure_test.cc b/runtime/lambda/closure_test.cc
new file mode 100644
index 0000000..7c1bd0d
--- /dev/null
+++ b/runtime/lambda/closure_test.cc
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "art_method.h"
+#include "lambda/art_lambda_method.h"
+#include "lambda/closure.h"
+#include "lambda/closure_builder.h"
+#include "lambda/closure_builder-inl.h"
+#include "utils.h"
+
+#include <numeric>
+#include <stdint.h>
+#include <type_traits>
+#include "gtest/gtest.h"
+
+// Turn this on for some extra printfs to help with debugging, since some code is optimized out.
+static constexpr const bool kDebuggingClosureTest = true;
+
+namespace std {
+ using Closure = art::lambda::Closure;
+
+ // Specialize std::default_delete so it knows how to properly delete closures
+ // through the way we allocate them in this test.
+ //
+ // This is test-only because we don't want the rest of Art to do this.
+ template <>
+ struct default_delete<Closure> {
+ void operator()(Closure* closure) const {
+ delete[] reinterpret_cast<char*>(closure);
+ }
+ };
+} // namespace std
+
+namespace art {
+
+// Fake lock acquisition to please clang lock checker.
+// This doesn't actually acquire any locks because we don't need multiple threads in this gtest.
+struct SCOPED_CAPABILITY ScopedFakeLock {
+ explicit ScopedFakeLock(MutatorMutex& mu) ACQUIRE(mu)
+ : mu_(mu) {
+ }
+
+ ~ScopedFakeLock() RELEASE()
+ {}
+
+ MutatorMutex& mu_;
+};
+
+namespace lambda {
+
+class ClosureTest : public ::testing::Test {
+ public:
+ ClosureTest() = default;
+ ~ClosureTest() = default;
+
+ protected:
+ static void SetUpTestCase() {
+ }
+
+ virtual void SetUp() {
+ // Create a completely dummy method here.
+ // It's "OK" because the Closure never needs to look inside of the ArtMethod
+ // (it just needs to be non-null).
+ uintptr_t ignore = 0xbadbad;
+ fake_method_ = reinterpret_cast<ArtMethod*>(ignore);
+ }
+
+ static ::testing::AssertionResult IsResultSuccessful(bool result) {
+ if (result) {
+ return ::testing::AssertionSuccess();
+ } else {
+ return ::testing::AssertionFailure();
+ }
+ }
+
+ // Create a closure that captures the static variables from 'args' by-value.
+ // The lambda method's captured variables types must match the ones in 'args'.
+ // -- This creates the closure directly in-memory by using memcpy.
+ template <typename ... Args>
+ static std::unique_ptr<Closure> CreateClosureStaticVariables(ArtLambdaMethod* lambda_method,
+ Args&& ... args) {
+ constexpr size_t header_size = sizeof(ArtLambdaMethod*);
+ const size_t static_size = GetArgsSize(args ...) + header_size;
+ EXPECT_GE(static_size, sizeof(Closure));
+
+ // Can't just 'new' the Closure since we don't know the size up front.
+ char* closure_as_char_array = new char[static_size];
+ Closure* closure_ptr = new (closure_as_char_array) Closure;
+
+ // Set up the data
+ closure_ptr->lambda_info_ = lambda_method;
+ CopyArgs(closure_ptr->captured_[0].static_variables_, args ...);
+
+ // Make sure the entire thing is deleted once the unique_ptr goes out of scope.
+ return std::unique_ptr<Closure>(closure_ptr); // NOLINT [whitespace/braces] [5]
+ }
+
+ // Copy variadic arguments into the destination array with memcpy.
+ template <typename T, typename ... Args>
+ static void CopyArgs(uint8_t destination[], T&& arg, Args&& ... args) {
+ memcpy(destination, &arg, sizeof(arg));
+ CopyArgs(destination + sizeof(arg), args ...);
+ }
+
+ // Base case: Done.
+ static void CopyArgs(uint8_t destination[]) {
+ UNUSED(destination);
+ }
+
+ // Create a closure that captures the static variables from 'args' by-value.
+ // The lambda method's captured variables types must match the ones in 'args'.
+ // -- This uses ClosureBuilder interface to set up the closure indirectly.
+ template <typename ... Args>
+ static std::unique_ptr<Closure> CreateClosureStaticVariablesFromBuilder(
+ ArtLambdaMethod* lambda_method,
+ Args&& ... args) {
+ // Acquire a fake lock since closure_builder needs it.
+ ScopedFakeLock fake_lock(*Locks::mutator_lock_);
+
+ ClosureBuilder closure_builder;
+ CaptureVariableFromArgsList(/*out*/closure_builder, args ...);
+
+ EXPECT_EQ(sizeof...(args), closure_builder.GetCaptureCount());
+
+ constexpr size_t header_size = sizeof(ArtLambdaMethod*);
+ const size_t static_size = GetArgsSize(args ...) + header_size;
+ EXPECT_GE(static_size, sizeof(Closure));
+
+ // For static variables, no nested closure, so size must match exactly.
+ EXPECT_EQ(static_size, closure_builder.GetSize());
+
+ // Can't just 'new' the Closure since we don't know the size up front.
+ char* closure_as_char_array = new char[static_size];
+ Closure* closure_ptr = new (closure_as_char_array) Closure;
+
+ // The closure builder packs the captured variables into a Closure.
+ closure_builder.CreateInPlace(closure_ptr, lambda_method);
+
+ // Make sure the entire thing is deleted once the unique_ptr goes out of scope.
+ return std::unique_ptr<Closure>(closure_ptr); // NOLINT [whitespace/braces] [5]
+ }
+
+ // Call the correct ClosureBuilder::CaptureVariableXYZ function based on the type of args.
+ // Invokes for each arg in args.
+ template <typename ... Args>
+ static void CaptureVariableFromArgsList(/*out*/ClosureBuilder& closure_builder, Args ... args) {
+ int ignore[] = {
+ (CaptureVariableFromArgs(/*out*/closure_builder, args),0)... // NOLINT [whitespace/comma] [3]
+ };
+ UNUSED(ignore);
+ }
+
+ // ClosureBuilder::CaptureVariablePrimitive for types that are primitive only.
+ template <typename T>
+ typename std::enable_if<ShortyFieldTypeTraits::IsPrimitiveType<T>()>::type
+ static CaptureVariableFromArgs(/*out*/ClosureBuilder& closure_builder, T value) {
+ static_assert(ShortyFieldTypeTraits::IsPrimitiveType<T>(), "T must be a shorty primitive");
+ closure_builder.CaptureVariablePrimitive<T, ShortyFieldTypeSelectEnum<T>::value>(value);
+ }
+
+ // ClosureBuilder::CaptureVariableObject for types that are objects only.
+ template <typename T>
+ typename std::enable_if<ShortyFieldTypeTraits::IsObjectType<T>()>::type
+ static CaptureVariableFromArgs(/*out*/ClosureBuilder& closure_builder, const T* object) {
+ ScopedFakeLock fake_lock(*Locks::mutator_lock_);
+ closure_builder.CaptureVariableObject(object);
+ }
+
+ // Sum of sizeof(Args...).
+ template <typename T, typename ... Args>
+ static constexpr size_t GetArgsSize(T&& arg, Args&& ... args) {
+ return sizeof(arg) + GetArgsSize(args ...);
+ }
+
+ // Base case: Done.
+ static constexpr size_t GetArgsSize() {
+ return 0;
+ }
+
+ // Take "U" and memcpy it into a "T". T starts out as (T)0.
+ template <typename T, typename U>
+ static T ExpandingBitCast(const U& val) {
+ static_assert(sizeof(T) >= sizeof(U), "U too large");
+ T new_val = static_cast<T>(0);
+ memcpy(&new_val, &val, sizeof(U));
+ return new_val;
+ }
+
+ // Templatized extraction from closures by checking their type with enable_if.
+ template <typename T>
+ static typename std::enable_if<ShortyFieldTypeTraits::IsPrimitiveNarrowType<T>()>::type
+ ExpectCapturedVariable(const Closure* closure, size_t index, T value) {
+ EXPECT_EQ(ExpandingBitCast<uint32_t>(value), closure->GetCapturedPrimitiveNarrow(index))
+ << " with index " << index;
+ }
+
+ template <typename T>
+ static typename std::enable_if<ShortyFieldTypeTraits::IsPrimitiveWideType<T>()>::type
+ ExpectCapturedVariable(const Closure* closure, size_t index, T value) {
+ EXPECT_EQ(ExpandingBitCast<uint64_t>(value), closure->GetCapturedPrimitiveWide(index))
+ << " with index " << index;
+ }
+
+ // Templatized SFINAE for Objects so we can get better error messages.
+ template <typename T>
+ static typename std::enable_if<ShortyFieldTypeTraits::IsObjectType<T>()>::type
+ ExpectCapturedVariable(const Closure* closure, size_t index, const T* object) {
+ EXPECT_EQ(object, closure->GetCapturedObject(index))
+ << " with index " << index;
+ }
+
+ template <typename ... Args>
+ void TestPrimitive(const char *descriptor, Args ... args) {
+ const char* shorty = descriptor;
+
+ SCOPED_TRACE(descriptor);
+
+ ASSERT_EQ(strlen(shorty), sizeof...(args))
+ << "test error: descriptor must have same # of types as the # of captured variables";
+
+ // Important: This fake lambda method needs to out-live any Closures we create with it.
+ ArtLambdaMethod lambda_method{fake_method_, // NOLINT [whitespace/braces] [5]
+ descriptor, // NOLINT [whitespace/blank_line] [2]
+ shorty,
+ };
+
+ std::unique_ptr<Closure> closure_a;
+ std::unique_ptr<Closure> closure_b;
+
+ // Test the closure twice when it's constructed in different ways.
+ {
+ // Create the closure in a "raw" manner, that is directly with memcpy
+ // since we know the underlying data format.
+ // This simulates how the compiler would lay out the data directly.
+ SCOPED_TRACE("raw closure");
+ std::unique_ptr<Closure> closure_raw = CreateClosureStaticVariables(&lambda_method, args ...);
+
+ if (kDebuggingClosureTest) {
+ std::cerr << "closure raw address: " << closure_raw.get() << std::endl;
+ }
+ TestPrimitiveWithClosure(closure_raw.get(), descriptor, shorty, args ...);
+ closure_a = std::move(closure_raw);
+ }
+
+ {
+ // Create the closure with the ClosureBuilder, which is done indirectly.
+ // This simulates how the interpreter would create the closure dynamically at runtime.
+ SCOPED_TRACE("closure from builder");
+ std::unique_ptr<Closure> closure_built =
+ CreateClosureStaticVariablesFromBuilder(&lambda_method, args ...);
+ if (kDebuggingClosureTest) {
+ std::cerr << "closure built address: " << closure_built.get() << std::endl;
+ }
+ TestPrimitiveWithClosure(closure_built.get(), descriptor, shorty, args ...);
+ closure_b = std::move(closure_built);
+ }
+
+ // The closures should be identical memory-wise as well.
+ EXPECT_EQ(closure_a->GetSize(), closure_b->GetSize());
+ EXPECT_TRUE(memcmp(closure_a.get(),
+ closure_b.get(),
+ std::min(closure_a->GetSize(), closure_b->GetSize())) == 0);
+ }
+
+ template <typename ... Args>
+ static void TestPrimitiveWithClosure(Closure* closure,
+ const char* descriptor,
+ const char* shorty,
+ Args ... args) {
+ EXPECT_EQ(sizeof(ArtLambdaMethod*) + GetArgsSize(args...), closure->GetSize());
+ EXPECT_EQ(sizeof...(args), closure->GetNumberOfCapturedVariables());
+ EXPECT_STREQ(descriptor, closure->GetCapturedVariablesTypeDescriptor());
+ TestPrimitiveExpects(closure, shorty, /*index*/0, args ...);
+ }
+
+ // Call EXPECT_EQ for each argument in the closure's #GetCapturedX.
+ template <typename T, typename ... Args>
+ static void TestPrimitiveExpects(
+ const Closure* closure, const char* shorty, size_t index, T arg, Args ... args) {
+ ASSERT_EQ(ShortyFieldType(shorty[index]).GetStaticSize(), sizeof(T))
+ << "Test error: Type mismatch at index " << index;
+ ExpectCapturedVariable(closure, index, arg);
+ EXPECT_EQ(ShortyFieldType(shorty[index]), closure->GetCapturedShortyType(index));
+ TestPrimitiveExpects(closure, shorty, index + 1, args ...);
+ }
+
+ // Base case for EXPECT_EQ.
+ static void TestPrimitiveExpects(const Closure* closure, const char* shorty, size_t index) {
+ UNUSED(closure, shorty, index);
+ }
+
+ ArtMethod* fake_method_;
+};
+
+TEST_F(ClosureTest, TestTrivial) {
+ ArtLambdaMethod lambda_method{fake_method_, // NOLINT [whitespace/braces] [5]
+ "", // No captured variables // NOLINT [whitespace/blank_line] [2]
+ "", // No captured variables
+ };
+
+ std::unique_ptr<Closure> closure = CreateClosureStaticVariables(&lambda_method);
+
+ EXPECT_EQ(sizeof(ArtLambdaMethod*), closure->GetSize());
+ EXPECT_EQ(0u, closure->GetNumberOfCapturedVariables());
+} // TEST_F
+
+TEST_F(ClosureTest, TestPrimitiveSingle) {
+ TestPrimitive("Z", true);
+ TestPrimitive("B", int8_t(0xde));
+ TestPrimitive("C", uint16_t(0xbeef));
+ TestPrimitive("S", int16_t(0xdead));
+ TestPrimitive("I", int32_t(0xdeadbeef));
+ TestPrimitive("F", 0.123f);
+ TestPrimitive("J", int64_t(0xdeadbeef00c0ffee));
+ TestPrimitive("D", 123.456);
+} // TEST_F
+
+TEST_F(ClosureTest, TestPrimitiveMany) {
+ TestPrimitive("ZZ", true, false);
+ TestPrimitive("ZZZ", true, false, true);
+ TestPrimitive("BBBB", int8_t(0xde), int8_t(0xa0), int8_t(0xff), int8_t(0xcc));
+ TestPrimitive("CC", uint16_t(0xbeef), uint16_t(0xdead));
+ TestPrimitive("SSSS", int16_t(0xdead), int16_t(0xc0ff), int16_t(0xf000), int16_t(0xbaba));
+ TestPrimitive("III", int32_t(0xdeadbeef), int32_t(0xc0ffee), int32_t(0xbeefdead));
+ TestPrimitive("FF", 0.123f, 555.666f);
+ TestPrimitive("JJJ", int64_t(0xdeadbeef00c0ffee), int64_t(0x123), int64_t(0xc0ffee));
+ TestPrimitive("DD", 123.456, 777.888);
+} // TEST_F
+
+TEST_F(ClosureTest, TestPrimitiveMixed) {
+ TestPrimitive("ZZBBCCSSIIFFJJDD",
+ true, false,
+ int8_t(0xde), int8_t(0xa0),
+ uint16_t(0xbeef), uint16_t(0xdead),
+ int16_t(0xdead), int16_t(0xc0ff),
+ int32_t(0xdeadbeef), int32_t(0xc0ffee),
+ 0.123f, 555.666f,
+ int64_t(0xdeadbeef00c0ffee), int64_t(0x123),
+ 123.456, 777.888);
+} // TEST_F
+
+} // namespace lambda
+} // namespace art
diff --git a/runtime/lambda/shorty_field_type.h b/runtime/lambda/shorty_field_type.h
new file mode 100644
index 0000000..46ddaa9
--- /dev/null
+++ b/runtime/lambda/shorty_field_type.h
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ART_RUNTIME_LAMBDA_SHORTY_FIELD_TYPE_H_
+#define ART_RUNTIME_LAMBDA_SHORTY_FIELD_TYPE_H_
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/value_object.h"
+#include "globals.h"
+#include "runtime/primitive.h"
+
+#include <ostream>
+
+namespace art {
+
+namespace mirror {
+class Object; // forward declaration
+} // namespace mirror
+
+namespace lambda {
+
+struct Closure; // forward declaration
+
+// TODO: Refactor together with primitive.h
+
+// The short form of a field type descriptor. Corresponds to ShortyFieldType in dex specification.
+// Only types usable by a field (and locals) are allowed (i.e. no void type).
+// Note that arrays and objects are treated both as 'L'.
+//
+// This is effectively a 'char' enum-like zero-cost type-safe wrapper with extra helper functions.
+struct ShortyFieldType : ValueObject {
+ // Use as if this was an enum class, e.g. 'ShortyFieldType::kBoolean'.
+ enum : char {
+ // Primitives (Narrow):
+ kBoolean = 'Z',
+ kByte = 'B',
+ kChar = 'C',
+ kShort = 'S',
+ kInt = 'I',
+ kFloat = 'F',
+ // Primitives (Wide):
+ kLong = 'J',
+ kDouble = 'D',
+ // Managed types:
+ kObject = 'L', // This can also be an array (which is otherwise '[' in a non-shorty).
+ kLambda = '\\',
+ }; // NOTE: This is an anonymous enum so we can get exhaustive switch checking from the compiler.
+
+ // Implicitly construct from the enum above. Value must be one of the enum list members above.
+ // Always safe to use, does not do any DCHECKs.
+ inline constexpr ShortyFieldType(decltype(kByte) c) : value_(c) {
+ }
+
+ // Default constructor. The initial value is undefined. Initialize before calling methods.
+ // This is very unsafe but exists as a convenience to having undefined values.
+ explicit ShortyFieldType() : value_(StaticCastValue(0)) {
+ }
+
+ // Explicitly construct from a char. Value must be one of the enum list members above.
+ // Conversion is potentially unsafe, so DCHECKing is performed.
+ explicit inline ShortyFieldType(char c) : value_(StaticCastValue(c)) {
+ if (kIsDebugBuild) {
+ // Verify at debug-time that our conversion is safe.
+ ShortyFieldType ignored;
+ DCHECK(MaybeCreate(c, &ignored)) << "unknown shorty field type '" << c << "'";
+ }
+ }
+
+ // Attempts to parse the character in 'shorty_field_type' into its strongly typed version.
+ // Returns false if the character was out of range of the grammar.
+ static bool MaybeCreate(char shorty_field_type, ShortyFieldType* out) {
+ DCHECK(out != nullptr);
+ switch (shorty_field_type) {
+ case kBoolean:
+ case kByte:
+ case kChar:
+ case kShort:
+ case kInt:
+ case kFloat:
+ case kLong:
+ case kDouble:
+ case kObject:
+ case kLambda:
+ *out = ShortyFieldType(static_cast<decltype(kByte)>(shorty_field_type));
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ // Convert the first type in a field type descriptor string into a shorty.
+ // Arrays are converted into objects.
+ // Does not work for 'void' types (as they are illegal in a field type descriptor).
+ static ShortyFieldType CreateFromFieldTypeDescriptor(const char* field_type_descriptor) {
+ DCHECK(field_type_descriptor != nullptr);
+ char c = *field_type_descriptor;
+ if (UNLIKELY(c == kArray)) { // Arrays are treated as object references.
+ c = kObject;
+ }
+ return ShortyFieldType{c}; // NOLINT [readability/braces] [4]
+ }
+
+ // Parse the first type in the field type descriptor string into a shorty.
+ // See CreateFromFieldTypeDescriptor for more details.
+ //
+ // Returns the pointer offset into the middle of the field_type_descriptor
+ // that would either point to the next shorty type, or to null if there are
+ // no more types.
+ //
+ // DCHECKs that each of the nested types is a valid shorty field type. This
+ // means the type descriptor must be already valid.
+ static const char* ParseFromFieldTypeDescriptor(const char* field_type_descriptor,
+ ShortyFieldType* out_type) {
+ DCHECK(field_type_descriptor != nullptr);
+
+ if (UNLIKELY(field_type_descriptor[0] == '\0')) {
+ // Handle empty strings by immediately returning null.
+ return nullptr;
+ }
+
+ // All non-empty strings must be a valid list of field type descriptors, otherwise
+ // the DCHECKs will kick in and the program will crash.
+ const char shorter_type = *field_type_descriptor;
+
+ ShortyFieldType safe_type;
+ bool type_set = MaybeCreate(shorter_type, &safe_type);
+
+ // Lambda that keeps skipping characters until it sees ';'.
+ // Stops one character -after- the ';'.
+ auto skip_until_semicolon = [&field_type_descriptor]() {
+ while (*field_type_descriptor != ';' && *field_type_descriptor != '\0') {
+ ++field_type_descriptor;
+ }
+ DCHECK_NE(*field_type_descriptor, '\0')
+ << " type descriptor terminated too early: " << field_type_descriptor;
+ ++field_type_descriptor; // Skip the ';'
+ };
+
+ ++field_type_descriptor;
+ switch (shorter_type) {
+ case kObject:
+ skip_until_semicolon();
+
+ DCHECK(type_set);
+ DCHECK(safe_type == kObject);
+ break;
+ case kArray:
+ // Strip out all of the leading [[[[[s, we don't care if it's a multi-dimensional array.
+ while (*field_type_descriptor == '[' && *field_type_descriptor != '\0') {
+ ++field_type_descriptor;
+ }
+ DCHECK_NE(*field_type_descriptor, '\0')
+ << " type descriptor terminated too early: " << field_type_descriptor;
+ // Either a primitive, object, or closure left. No more arrays.
+ {
+ // Now skip all the characters that form the array's interior-most element type
+ // (which itself is guaranteed not to be an array).
+ ShortyFieldType array_interior_type;
+ type_set = MaybeCreate(*field_type_descriptor, &array_interior_type);
+ DCHECK(type_set) << " invalid remaining type descriptor " << field_type_descriptor;
+
+ // Handle array-of-objects case like [[[[[LObject; and array-of-closures like [[[[[\Foo;
+ if (*field_type_descriptor == kObject || *field_type_descriptor == kLambda) {
+ skip_until_semicolon();
+ } else {
+ // Handle primitives which are exactly one character we can skip.
+ DCHECK(array_interior_type.IsPrimitive());
+ ++field_type_descriptor;
+ }
+ }
+
+ safe_type = kObject;
+ type_set = true;
+ break;
+ case kLambda:
+ skip_until_semicolon();
+
+ DCHECK(safe_type == kLambda);
+ DCHECK(type_set);
+ break;
+ default:
+ DCHECK_NE(kVoid, shorter_type) << "cannot make a ShortyFieldType from a void type";
+ break;
+ }
+
+ DCHECK(type_set) << "invalid shorty type descriptor " << shorter_type;
+
+ *out_type = safe_type;
+ return type_set ? field_type_descriptor : nullptr;
+ }
+
+ // Explicitly convert to a char.
+ inline explicit operator char() const {
+ return value_;
+ }
+
+ // Is this a primitive?
+ inline bool IsPrimitive() const {
+ return IsPrimitiveNarrow() || IsPrimitiveWide();
+ }
+
+ // Is this a narrow primitive (i.e. can fit into 1 virtual register)?
+ inline bool IsPrimitiveNarrow() const {
+ switch (value_) {
+ case kBoolean:
+ case kByte:
+ case kChar:
+ case kShort:
+ case kInt:
+ case kFloat:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ // Is this a wide primitive (i.e. needs exactly 2 virtual registers)?
+ inline bool IsPrimitiveWide() const {
+ switch (value_) {
+ case kLong:
+ case kDouble:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ // Is this an object reference (which can also be an array)?
+ inline bool IsObject() const {
+ return value_ == kObject;
+ }
+
+ // Is this a lambda?
+ inline bool IsLambda() const {
+ return value_ == kLambda;
+ }
+
+ // Is the size of this (to store inline as a field) always known at compile-time?
+ inline bool IsStaticSize() const {
+ return !IsLambda();
+ }
+
+ // Get the compile-time size (to be able to store it inline as a field or on stack).
+ // Dynamically-sized values such as lambdas return the guaranteed lower bound.
+ inline size_t GetStaticSize() const {
+ switch (value_) {
+ case kBoolean:
+ return sizeof(bool);
+ case kByte:
+ return sizeof(uint8_t);
+ case kChar:
+ return sizeof(int16_t);
+ case kShort:
+ return sizeof(uint16_t);
+ case kInt:
+ return sizeof(int32_t);
+ case kLong:
+ return sizeof(int64_t);
+ case kFloat:
+ return sizeof(float);
+ case kDouble:
+ return sizeof(double);
+ case kObject:
+ return kObjectReferenceSize;
+ case kLambda:
+ return sizeof(void*); // Large enough to store the ArtLambdaMethod
+ default:
+ DCHECK(false) << "unknown shorty field type '" << static_cast<char>(value_) << "'";
+ UNREACHABLE();
+ }
+ }
+
+ // Implicitly convert to the anonymous nested inner type. Used for exhaustive switch detection.
+ inline operator decltype(kByte)() const {
+ return value_;
+ }
+
+ // Returns a read-only static string representing the enum name, useful for printing/debug only.
+ inline const char* ToString() const {
+ switch (value_) {
+ case kBoolean:
+ return "kBoolean";
+ case kByte:
+ return "kByte";
+ case kChar:
+ return "kChar";
+ case kShort:
+ return "kShort";
+ case kInt:
+ return "kInt";
+ case kLong:
+ return "kLong";
+ case kFloat:
+ return "kFloat";
+ case kDouble:
+ return "kDouble";
+ case kObject:
+ return "kObject";
+ case kLambda:
+ return "kLambda";
+ default:
+ // Undefined behavior if we get this far. Pray the compiler gods are merciful.
+ return "<undefined>";
+ }
+ }
+
+ private:
+ static constexpr const char kArray = '[';
+ static constexpr const char kVoid = 'V';
+
+ // Helper to statically cast anything into our nested anonymous enum type.
+ template <typename T>
+ inline static decltype(kByte) StaticCastValue(const T& anything) {
+ return static_cast<decltype(value_)>(anything);
+ }
+
+ // The only field in this struct.
+ decltype(kByte) value_;
+};
+
+
+ // Print to an output stream.
+inline std::ostream& operator<<(std::ostream& ostream, ShortyFieldType shorty) {
+ return ostream << shorty.ToString();
+}
+
+static_assert(sizeof(ShortyFieldType) == sizeof(char),
+ "ShortyFieldType must be lightweight just like a char");
+
+// Compile-time trait information regarding the ShortyFieldType.
+// Used by static_asserts to verify that the templates are correctly used at compile-time.
+//
+// For example,
+// ShortyFieldTypeTraits::IsPrimitiveNarrowType<int64_t>() == true
+// ShortyFieldTypeTraits::IsObjectType<mirror::Object*>() == true
+struct ShortyFieldTypeTraits {
+ // A type guaranteed to be large enough to holds any of the shorty field types.
+ using MaxType = uint64_t;
+
+ // Type traits: Returns true if 'T' is a valid type that can be represented by a shorty field type.
+ template <typename T>
+ static inline constexpr bool IsType() {
+ return IsPrimitiveType<T>() || IsObjectType<T>() || IsLambdaType<T>();
+ }
+
+ // Returns true if 'T' is a primitive type (i.e. a built-in without nested references).
+ template <typename T>
+ static inline constexpr bool IsPrimitiveType() {
+ return IsPrimitiveNarrowType<T>() || IsPrimitiveWideType<T>();
+ }
+
+ // Returns true if 'T' is a primitive type that is narrow (i.e. can be stored into 1 vreg).
+ template <typename T>
+ static inline constexpr bool IsPrimitiveNarrowType() {
+ return IsPrimitiveNarrowTypeImpl(static_cast<T* const>(nullptr));
+ }
+
+ // Returns true if 'T' is a primitive type that is wide (i.e. needs 2 vregs for storage).
+ template <typename T>
+ static inline constexpr bool IsPrimitiveWideType() {
+ return IsPrimitiveWideTypeImpl(static_cast<T* const>(nullptr));
+ }
+
+ // Returns true if 'T' is an object (i.e. it is a managed GC reference).
+ // Note: This is equivalent to std::base_of<mirror::Object*, T>::value
+ template <typename T>
+ static inline constexpr bool IsObjectType() {
+ return IsObjectTypeImpl(static_cast<T* const>(nullptr));
+ }
+
+ // Returns true if 'T' is a lambda (i.e. it is a closure with unknown static data);
+ template <typename T>
+ static inline constexpr bool IsLambdaType() {
+ return IsLambdaTypeImpl(static_cast<T* const>(nullptr));
+ }
+
+ private:
+#define IS_VALID_TYPE_SPECIALIZATION(type, name) \
+ static inline constexpr bool Is ## name ## TypeImpl(type* const = 0) { \
+ return true; \
+ } \
+ \
+ static_assert(sizeof(MaxType) >= sizeof(type), "MaxType too small")
+
+ IS_VALID_TYPE_SPECIALIZATION(bool, PrimitiveNarrow);
+ IS_VALID_TYPE_SPECIALIZATION(int8_t, PrimitiveNarrow);
+ IS_VALID_TYPE_SPECIALIZATION(uint8_t, PrimitiveNarrow); // Not strictly true, but close enough.
+ IS_VALID_TYPE_SPECIALIZATION(int16_t, PrimitiveNarrow);
+ IS_VALID_TYPE_SPECIALIZATION(uint16_t, PrimitiveNarrow); // Chars are unsigned.
+ IS_VALID_TYPE_SPECIALIZATION(int32_t, PrimitiveNarrow);
+ IS_VALID_TYPE_SPECIALIZATION(uint32_t, PrimitiveNarrow); // Not strictly true, but close enough.
+ IS_VALID_TYPE_SPECIALIZATION(float, PrimitiveNarrow);
+ IS_VALID_TYPE_SPECIALIZATION(int64_t, PrimitiveWide);
+ IS_VALID_TYPE_SPECIALIZATION(uint64_t, PrimitiveWide); // Not strictly true, but close enough.
+ IS_VALID_TYPE_SPECIALIZATION(double, PrimitiveWide);
+ IS_VALID_TYPE_SPECIALIZATION(mirror::Object*, Object);
+ IS_VALID_TYPE_SPECIALIZATION(Closure*, Lambda);
+#undef IS_VALID_TYPE_SPECIALIZATION
+
+#define IS_VALID_TYPE_SPECIALIZATION_IMPL(name) \
+ template <typename T> \
+ static inline constexpr bool Is ## name ## TypeImpl(T* const = 0) { \
+ return false; \
+ }
+
+ IS_VALID_TYPE_SPECIALIZATION_IMPL(PrimitiveNarrow);
+ IS_VALID_TYPE_SPECIALIZATION_IMPL(PrimitiveWide);
+ IS_VALID_TYPE_SPECIALIZATION_IMPL(Object);
+ IS_VALID_TYPE_SPECIALIZATION_IMPL(Lambda);
+
+#undef IS_VALID_TYPE_SPECIALIZATION_IMPL
+};
+
+// Maps the ShortyFieldType enum into it's C++ type equivalent, into the "type" typedef.
+// For example:
+// ShortyFieldTypeSelectType<ShortyFieldType::kBoolean>::type => bool
+// ShortyFieldTypeSelectType<ShortyFieldType::kLong>::type => int64_t
+//
+// Invalid enums will not have the type defined.
+template <decltype(ShortyFieldType::kByte) Shorty>
+struct ShortyFieldTypeSelectType {
+};
+
+// Maps the C++ type into it's ShortyFieldType enum equivalent, into the "value" constexpr.
+// For example:
+// ShortyFieldTypeSelectEnum<bool>::value => ShortyFieldType::kBoolean
+// ShortyFieldTypeSelectEnum<int64_t>::value => ShortyFieldType::kLong
+//
+// Signed-ness must match for a valid select, e.g. uint64_t will not map to kLong, but int64_t will.
+// Invalid types will not have the value defined (see e.g. ShortyFieldTypeTraits::IsType<T>())
+template <typename T>
+struct ShortyFieldTypeSelectEnum {
+};
+
+#define SHORTY_FIELD_TYPE_SELECT_IMPL(cpp_type, enum_element) \
+template <> \
+struct ShortyFieldTypeSelectType<ShortyFieldType::enum_element> { \
+ using type = cpp_type; \
+}; \
+\
+template <> \
+struct ShortyFieldTypeSelectEnum<cpp_type> { \
+ static constexpr const auto value = ShortyFieldType::enum_element; \
+}; \
+
+SHORTY_FIELD_TYPE_SELECT_IMPL(bool, kBoolean);
+SHORTY_FIELD_TYPE_SELECT_IMPL(int8_t, kByte);
+SHORTY_FIELD_TYPE_SELECT_IMPL(int16_t, kShort);
+SHORTY_FIELD_TYPE_SELECT_IMPL(uint16_t, kChar);
+SHORTY_FIELD_TYPE_SELECT_IMPL(int32_t, kInt);
+SHORTY_FIELD_TYPE_SELECT_IMPL(float, kFloat);
+SHORTY_FIELD_TYPE_SELECT_IMPL(int64_t, kLong);
+SHORTY_FIELD_TYPE_SELECT_IMPL(double, kDouble);
+SHORTY_FIELD_TYPE_SELECT_IMPL(mirror::Object*, kObject);
+SHORTY_FIELD_TYPE_SELECT_IMPL(Closure*, kLambda);
+
+} // namespace lambda
+} // namespace art
+
+#endif // ART_RUNTIME_LAMBDA_SHORTY_FIELD_TYPE_H_
diff --git a/runtime/lambda/shorty_field_type_test.cc b/runtime/lambda/shorty_field_type_test.cc
new file mode 100644
index 0000000..32bade9
--- /dev/null
+++ b/runtime/lambda/shorty_field_type_test.cc
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "lambda/shorty_field_type.h"
+#include "mirror/object_reference.h"
+
+#include "utils.h"
+#include <numeric>
+#include <stdint.h>
+#include "gtest/gtest.h"
+
+#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
+ reinterpret_cast<void*>(nullptr));
+
+namespace art {
+namespace lambda {
+
+class ShortyFieldTypeTest : public ::testing::Test {
+ public:
+ ShortyFieldTypeTest() = default;
+ ~ShortyFieldTypeTest() = default;
+
+ protected:
+ static void SetUpTestCase() {
+ }
+
+ virtual void SetUp() {
+ }
+
+ static ::testing::AssertionResult IsResultSuccessful(bool result) {
+ if (result) {
+ return ::testing::AssertionSuccess();
+ } else {
+ return ::testing::AssertionFailure();
+ }
+ }
+
+ template <typename T>
+ static std::string ListToString(const T& list) {
+ std::stringstream stream;
+
+ stream << "[";
+ for (auto&& val : list) {
+ stream << val << ", ";
+ }
+ stream << "]";
+
+ return stream.str();
+ }
+
+ // Compare two vector-like types for equality.
+ template <typename T>
+ static ::testing::AssertionResult AreListsEqual(const T& expected, const T& actual) {
+ bool success = true;
+ std::stringstream stream;
+
+ if (expected.size() != actual.size()) {
+ success = false;
+ stream << "Expected list size: " << expected.size()
+ << ", but got list size: " << actual.size();
+ stream << std::endl;
+ }
+
+ for (size_t j = 0; j < std::min(expected.size(), actual.size()); ++j) {
+ if (expected[j] != actual[j]) {
+ success = false;
+ stream << "Expected element '" << j << "' to be '" << expected[j] << "', but got actual: '"
+ << actual[j] << "'.";
+ stream << std::endl;
+ }
+ }
+
+ if (success) {
+ return ::testing::AssertionSuccess();
+ }
+
+ stream << "Expected list was: " << ListToString(expected)
+ << ", actual list was: " << ListToString(actual);
+
+ return ::testing::AssertionFailure() << stream.str();
+ }
+
+ static std::vector<ShortyFieldType> ParseLongTypeDescriptorsToList(const char* type_descriptor) {
+ std::vector<ShortyFieldType> lst;
+
+ ShortyFieldType shorty;
+
+ const char* parsed = type_descriptor;
+ while ((parsed = ShortyFieldType::ParseFromFieldTypeDescriptor(parsed, &shorty)) != nullptr) {
+ lst.push_back(shorty);
+ }
+
+ return lst;
+ }
+
+ protected:
+ // Shorthands for the ShortyFieldType constants.
+ // The letters are the same as JNI letters, with kS_ being a lambda since \ is not available.
+ static constexpr ShortyFieldType kSZ = ShortyFieldType::kBoolean;
+ static constexpr ShortyFieldType kSB = ShortyFieldType::kByte;
+ static constexpr ShortyFieldType kSC = ShortyFieldType::kChar;
+ static constexpr ShortyFieldType kSS = ShortyFieldType::kShort;
+ static constexpr ShortyFieldType kSI = ShortyFieldType::kInt;
+ static constexpr ShortyFieldType kSF = ShortyFieldType::kFloat;
+ static constexpr ShortyFieldType kSJ = ShortyFieldType::kLong;
+ static constexpr ShortyFieldType kSD = ShortyFieldType::kDouble;
+ static constexpr ShortyFieldType kSL = ShortyFieldType::kObject;
+ static constexpr ShortyFieldType kS_ = ShortyFieldType::kLambda;
+};
+
+TEST_F(ShortyFieldTypeTest, TestMaybeCreate) {
+ ShortyFieldType shorty;
+
+ std::vector<char> shorties = {'Z', 'B', 'C', 'S', 'I', 'F', 'J', 'D', 'L', '\\'};
+
+ // All valid 'shorty' characters are created successfully.
+ for (const char c : shorties) {
+ EXPECT_TRUE(ShortyFieldType::MaybeCreate(c, &shorty)) << c;
+ EXPECT_EQ(c, static_cast<char>(c));
+ }
+
+ // All other characters can never be created.
+ for (unsigned char c = 0; c < std::numeric_limits<unsigned char>::max(); ++c) {
+ // Skip the valid characters.
+ if (std::find(shorties.begin(), shorties.end(), c) != shorties.end()) { continue; }
+ // All invalid characters should fail.
+ EXPECT_FALSE(ShortyFieldType::MaybeCreate(static_cast<char>(c), &shorty)) << c;
+ }
+} // TEST_F
+
+TEST_F(ShortyFieldTypeTest, TestCreateFromFieldTypeDescriptor) {
+ // Sample input.
+ std::vector<const char*> lengthies = {
+ "Z", "B", "C", "S", "I", "F", "J", "D", "LObject;", "\\Closure;",
+ "[Z", "[[B", "[[LObject;"
+ };
+
+ // Expected output.
+ std::vector<ShortyFieldType> expected = {
+ ShortyFieldType::kBoolean,
+ ShortyFieldType::kByte,
+ ShortyFieldType::kChar,
+ ShortyFieldType::kShort,
+ ShortyFieldType::kInt,
+ ShortyFieldType::kFloat,
+ ShortyFieldType::kLong,
+ ShortyFieldType::kDouble,
+ ShortyFieldType::kObject,
+ ShortyFieldType::kLambda,
+ // Arrays are always treated as objects.
+ ShortyFieldType::kObject,
+ ShortyFieldType::kObject,
+ ShortyFieldType::kObject,
+ };
+
+ // All valid lengthy types are correctly turned into the expected shorty type.
+ for (size_t i = 0; i < lengthies.size(); ++i) {
+ EXPECT_EQ(expected[i], ShortyFieldType::CreateFromFieldTypeDescriptor(lengthies[i]));
+ }
+} // TEST_F
+
+TEST_F(ShortyFieldTypeTest, TestParseFromFieldTypeDescriptor) {
+ // Sample input.
+ std::vector<const char*> lengthies = {
+ // Empty list
+ "",
+ // Primitives
+ "Z", "B", "C", "S", "I", "F", "J", "D",
+ // Non-primitives
+ "LObject;", "\\Closure;",
+ // Arrays. The biggest PITA.
+ "[Z", "[[B", "[[LObject;", "[[[[\\Closure;",
+ // Multiple things at once:
+ "ZBCSIFJD",
+ "LObject;LObject;SSI",
+ "[[ZDDZ",
+ "[[LObject;[[Z[F\\Closure;LObject;",
+ };
+
+ // Expected output.
+ std::vector<std::vector<ShortyFieldType>> expected = {
+ // Empty list
+ {},
+ // Primitives
+ {kSZ}, {kSB}, {kSC}, {kSS}, {kSI}, {kSF}, {kSJ}, {kSD},
+ // Non-primitives.
+ { ShortyFieldType::kObject }, { ShortyFieldType::kLambda },
+ // Arrays are always treated as objects.
+ { kSL }, { kSL }, { kSL }, { kSL },
+ // Multiple things at once:
+ { kSZ, kSB, kSC, kSS, kSI, kSF, kSJ, kSD },
+ { kSL, kSL, kSS, kSS, kSI },
+ { kSL, kSD, kSD, kSZ },
+ { kSL, kSL, kSL, kS_, kSL },
+ };
+
+ // Sanity check that the expected/actual lists are the same size.. when adding new entries.
+ ASSERT_EQ(expected.size(), lengthies.size());
+
+ // All valid lengthy types are correctly turned into the expected shorty type.
+ for (size_t i = 0; i < expected.size(); ++i) {
+ const std::vector<ShortyFieldType>& expected_list = expected[i];
+ std::vector<ShortyFieldType> actual_list = ParseLongTypeDescriptorsToList(lengthies[i]);
+ EXPECT_TRUE(AreListsEqual(expected_list, actual_list));
+ }
+} // TEST_F
+
+// Helper class to probe a shorty's characteristics by minimizing copy-and-paste tests.
+template <typename T, decltype(ShortyFieldType::kByte) kShortyEnum>
+struct ShortyTypeCharacteristics {
+ bool is_primitive_ = false;
+ bool is_primitive_narrow_ = false;
+ bool is_primitive_wide_ = false;
+ bool is_object_ = false;
+ bool is_lambda_ = false;
+ size_t size_ = sizeof(T);
+ bool is_dynamic_sized_ = false;
+
+ void CheckExpects() {
+ ShortyFieldType shorty = kShortyEnum;
+
+ // Test the main non-parsing-related ShortyFieldType characteristics.
+ EXPECT_EQ(is_primitive_, shorty.IsPrimitive());
+ EXPECT_EQ(is_primitive_narrow_, shorty.IsPrimitiveNarrow());
+ EXPECT_EQ(is_primitive_wide_, shorty.IsPrimitiveWide());
+ EXPECT_EQ(is_object_, shorty.IsObject());
+ EXPECT_EQ(is_lambda_, shorty.IsLambda());
+ EXPECT_EQ(size_, shorty.GetStaticSize());
+ EXPECT_EQ(is_dynamic_sized_, !shorty.IsStaticSize());
+
+ // Test compile-time ShortyFieldTypeTraits.
+ EXPECT_TRUE(ShortyFieldTypeTraits::IsType<T>());
+ EXPECT_EQ(is_primitive_, ShortyFieldTypeTraits::IsPrimitiveType<T>());
+ EXPECT_EQ(is_primitive_narrow_, ShortyFieldTypeTraits::IsPrimitiveNarrowType<T>());
+ EXPECT_EQ(is_primitive_wide_, ShortyFieldTypeTraits::IsPrimitiveWideType<T>());
+ EXPECT_EQ(is_object_, ShortyFieldTypeTraits::IsObjectType<T>());
+ EXPECT_EQ(is_lambda_, ShortyFieldTypeTraits::IsLambdaType<T>());
+
+ // Test compile-time ShortyFieldType selectors
+ static_assert(std::is_same<T, typename ShortyFieldTypeSelectType<kShortyEnum>::type>::value,
+ "ShortyFieldType Enum->Type incorrect mapping");
+ auto kActualEnum = ShortyFieldTypeSelectEnum<T>::value; // Do not ODR-use, avoid linker error.
+ EXPECT_EQ(kShortyEnum, kActualEnum);
+ }
+};
+
+TEST_F(ShortyFieldTypeTest, TestCharacteristicsAndTraits) {
+ // Boolean test
+ {
+ SCOPED_TRACE("boolean");
+ ShortyTypeCharacteristics<bool, ShortyFieldType::kBoolean> chars;
+ chars.is_primitive_ = true;
+ chars.is_primitive_narrow_ = true;
+ chars.CheckExpects();
+ }
+
+ // Byte test
+ {
+ SCOPED_TRACE("byte");
+ ShortyTypeCharacteristics<int8_t, ShortyFieldType::kByte> chars;
+ chars.is_primitive_ = true;
+ chars.is_primitive_narrow_ = true;
+ chars.CheckExpects();
+ }
+
+ // Char test
+ {
+ SCOPED_TRACE("char");
+ ShortyTypeCharacteristics<uint16_t, ShortyFieldType::kChar> chars; // Char is unsigned.
+ chars.is_primitive_ = true;
+ chars.is_primitive_narrow_ = true;
+ chars.CheckExpects();
+ }
+
+ // Short test
+ {
+ SCOPED_TRACE("short");
+ ShortyTypeCharacteristics<int16_t, ShortyFieldType::kShort> chars;
+ chars.is_primitive_ = true;
+ chars.is_primitive_narrow_ = true;
+ chars.CheckExpects();
+ }
+
+ // Int test
+ {
+ SCOPED_TRACE("int");
+ ShortyTypeCharacteristics<int32_t, ShortyFieldType::kInt> chars;
+ chars.is_primitive_ = true;
+ chars.is_primitive_narrow_ = true;
+ chars.CheckExpects();
+ }
+
+ // Long test
+ {
+ SCOPED_TRACE("long");
+ ShortyTypeCharacteristics<int64_t, ShortyFieldType::kLong> chars;
+ chars.is_primitive_ = true;
+ chars.is_primitive_wide_ = true;
+ chars.CheckExpects();
+ }
+
+ // Float test
+ {
+ SCOPED_TRACE("float");
+ ShortyTypeCharacteristics<float, ShortyFieldType::kFloat> chars;
+ chars.is_primitive_ = true;
+ chars.is_primitive_narrow_ = true;
+ chars.CheckExpects();
+ }
+
+ // Double test
+ {
+ SCOPED_TRACE("double");
+ ShortyTypeCharacteristics<double, ShortyFieldType::kDouble> chars;
+ chars.is_primitive_ = true;
+ chars.is_primitive_wide_ = true;
+ chars.CheckExpects();
+ }
+
+ // Object test
+ {
+ SCOPED_TRACE("object");
+ ShortyTypeCharacteristics<mirror::Object*, ShortyFieldType::kObject> chars;
+ chars.is_object_ = true;
+ chars.size_ = kObjectReferenceSize;
+ chars.CheckExpects();
+ EXPECT_EQ(kObjectReferenceSize, sizeof(mirror::CompressedReference<mirror::Object>));
+ }
+
+ // Lambda test
+ {
+ SCOPED_TRACE("lambda");
+ ShortyTypeCharacteristics<Closure*, ShortyFieldType::kLambda> chars;
+ chars.is_lambda_ = true;
+ chars.is_dynamic_sized_ = true;
+ chars.CheckExpects();
+ }
+}
+
+} // namespace lambda
+} // namespace art
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index b2c6e4d..10b381d 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -817,12 +817,12 @@
}
}
-inline void Class::SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings) {
- SetFieldObject<false>(DexCacheStringsOffset(), new_dex_cache_strings);
+inline void Class::SetDexCacheStrings(GcRoot<String>* new_dex_cache_strings) {
+ SetFieldPtr<false>(DexCacheStringsOffset(), new_dex_cache_strings);
}
-inline ObjectArray<String>* Class::GetDexCacheStrings() {
- return GetFieldObject<ObjectArray<String>>(DexCacheStringsOffset());
+inline GcRoot<String>* Class::GetDexCacheStrings() {
+ return GetFieldPtr<GcRoot<String>*>(DexCacheStringsOffset());
}
template<class Visitor>
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 1420e5b..9422432 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -250,6 +250,14 @@
SetClassFlags(kClassFlagClassLoader);
}
+ ALWAYS_INLINE bool IsDexCacheClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return (GetClassFlags() & kClassFlagDexCache) != 0;
+ }
+
+ ALWAYS_INLINE void SetDexCacheClass() SHARED_REQUIRES(Locks::mutator_lock_) {
+ SetClassFlags(GetClassFlags() | kClassFlagDexCache);
+ }
+
// Returns true if the class is abstract.
ALWAYS_INLINE bool IsAbstract() SHARED_REQUIRES(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccAbstract) != 0;
@@ -1077,8 +1085,8 @@
bool GetSlowPathEnabled() SHARED_REQUIRES(Locks::mutator_lock_);
void SetSlowPath(bool enabled) SHARED_REQUIRES(Locks::mutator_lock_);
- ObjectArray<String>* GetDexCacheStrings() SHARED_REQUIRES(Locks::mutator_lock_);
- void SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings)
+ GcRoot<String>* GetDexCacheStrings() SHARED_REQUIRES(Locks::mutator_lock_);
+ void SetDexCacheStrings(GcRoot<String>* new_dex_cache_strings)
SHARED_REQUIRES(Locks::mutator_lock_);
static MemberOffset DexCacheStringsOffset() {
return OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_strings_);
@@ -1173,9 +1181,6 @@
// runtime such as arrays and primitive classes).
HeapReference<DexCache> dex_cache_;
- // Short cuts to dex_cache_ member for fast compiled code access.
- HeapReference<ObjectArray<String>> dex_cache_strings_;
-
// The interface table (iftable_) contains pairs of a interface class and an array of the
// interface methods. There is one pair per interface supported by this class. That means one
// pair for each interface we support directly, indirectly via superclass, or indirectly via a
@@ -1209,9 +1214,8 @@
// virtual_ methods_ for miranda methods.
HeapReference<PointerArray> vtable_;
- // Access flags; low 16 bits are defined by VM spec.
- // Note: Shuffled back.
- uint32_t access_flags_;
+ // Short cuts to dex_cache_ member for fast compiled code access.
+ uint64_t dex_cache_strings_;
// static, private, and <init> methods. Pointer to an ArtMethod length-prefixed array.
uint64_t direct_methods_;
@@ -1234,6 +1238,9 @@
// length-prefixed array.
uint64_t virtual_methods_;
+ // Access flags; low 16 bits are defined by VM spec.
+ uint32_t access_flags_;
+
// Class flags to help speed up visiting object references.
uint32_t class_flags_;
diff --git a/runtime/mirror/class_flags.h b/runtime/mirror/class_flags.h
index eb2e2eb..139c4cb 100644
--- a/runtime/mirror/class_flags.h
+++ b/runtime/mirror/class_flags.h
@@ -41,17 +41,20 @@
// Class is ClassLoader or one of its subclasses.
static constexpr uint32_t kClassFlagClassLoader = 0x00000020;
+// Class is DexCache.
+static constexpr uint32_t kClassFlagDexCache = 0x00000040;
+
// Class is a soft/weak/phantom class.
-static constexpr uint32_t kClassFlagSoftReference = 0x00000040;
+static constexpr uint32_t kClassFlagSoftReference = 0x00000080;
// Class is a weak reference class.
-static constexpr uint32_t kClassFlagWeakReference = 0x00000080;
+static constexpr uint32_t kClassFlagWeakReference = 0x00000100;
// Class is a finalizer reference class.
-static constexpr uint32_t kClassFlagFinalizerReference = 0x00000100;
+static constexpr uint32_t kClassFlagFinalizerReference = 0x00000200;
// Class is the phantom reference class.
-static constexpr uint32_t kClassFlagPhantomReference = 0x00000200;
+static constexpr uint32_t kClassFlagPhantomReference = 0x00000400;
// Combination of flags to figure out if the class is either the weak/soft/phantom/finalizer
// reference class.
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 4b5063a..f8ccfb1 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -21,6 +21,7 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
+#include "base/casts.h"
#include "base/logging.h"
#include "mirror/class.h"
#include "runtime.h"
@@ -33,29 +34,53 @@
return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size);
}
-inline void DexCache::SetResolvedType(uint32_t type_idx, Class* resolved) {
- // TODO default transaction support.
- DCHECK(resolved == nullptr || !resolved->IsErroneous());
- GetResolvedTypes()->Set(type_idx, resolved);
+inline String* DexCache::GetResolvedString(uint32_t string_idx) {
+ DCHECK_LT(string_idx, NumStrings());
+ return GetStrings()[string_idx].Read();
}
-inline ArtField* DexCache::GetResolvedField(uint32_t idx, size_t ptr_size) {
+inline void DexCache::SetResolvedString(uint32_t string_idx, String* resolved) {
+ DCHECK_LT(string_idx, NumStrings());
+ // TODO default transaction support.
+ GetStrings()[string_idx] = GcRoot<String>(resolved);
+ // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
+}
+
+inline Class* DexCache::GetResolvedType(uint32_t type_idx) {
+ DCHECK_LT(type_idx, NumResolvedTypes());
+ return GetResolvedTypes()[type_idx].Read();
+}
+
+inline void DexCache::SetResolvedType(uint32_t type_idx, Class* resolved) {
+ DCHECK_LT(type_idx, NumResolvedTypes()); // NOTE: Unchecked, i.e. not throwing AIOOB.
+ // TODO default transaction support.
+ DCHECK(resolved == nullptr || !resolved->IsErroneous());
+ GetResolvedTypes()[type_idx] = GcRoot<Class>(resolved);
+ // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
+}
+
+inline ArtField* DexCache::GetResolvedField(uint32_t field_idx, size_t ptr_size) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
- auto* field = GetResolvedFields()->GetElementPtrSize<ArtField*>(idx, ptr_size);
+ DCHECK_LT(field_idx, NumResolvedFields()); // NOTE: Unchecked, i.e. not throwing AIOOB.
+ ArtField* field = GetElementPtrSize(GetResolvedFields(), field_idx, ptr_size);
if (field == nullptr || field->GetDeclaringClass()->IsErroneous()) {
return nullptr;
}
return field;
}
-inline void DexCache::SetResolvedField(uint32_t idx, ArtField* field, size_t ptr_size) {
+inline void DexCache::SetResolvedField(uint32_t field_idx, ArtField* field, size_t ptr_size) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
- GetResolvedFields()->SetElementPtrSize(idx, field, ptr_size);
+ DCHECK_LT(field_idx, NumResolvedFields()); // NOTE: Unchecked, i.e. not throwing AIOOB.
+ SetElementPtrSize(GetResolvedFields(), field_idx, field, ptr_size);
}
inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx, size_t ptr_size) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
- auto* method = GetResolvedMethods()->GetElementPtrSize<ArtMethod*>(method_idx, ptr_size);
+ DCHECK_LT(method_idx, NumResolvedMethods()); // NOTE: Unchecked, i.e. not throwing AIOOB.
+ ArtMethod* method = GetElementPtrSize<ArtMethod*>(GetResolvedMethods(), method_idx, ptr_size);
// Hide resolution trampoline methods from the caller
if (method != nullptr && method->IsRuntimeMethod()) {
DCHECK_EQ(method, Runtime::Current()->GetResolutionMethod());
@@ -64,9 +89,52 @@
return method;
}
-inline void DexCache::SetResolvedMethod(uint32_t idx, ArtMethod* method, size_t ptr_size) {
+inline void DexCache::SetResolvedMethod(uint32_t method_idx, ArtMethod* method, size_t ptr_size) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size);
- GetResolvedMethods()->SetElementPtrSize(idx, method, ptr_size);
+ DCHECK_LT(method_idx, NumResolvedMethods()); // NOTE: Unchecked, i.e. not throwing AIOOB.
+ SetElementPtrSize(GetResolvedMethods(), method_idx, method, ptr_size);
+}
+
+template <typename PtrType>
+inline PtrType DexCache::GetElementPtrSize(PtrType* ptr_array, size_t idx, size_t ptr_size) {
+ if (ptr_size == 8u) {
+ uint64_t element = reinterpret_cast<const uint64_t*>(ptr_array)[idx];
+ return reinterpret_cast<PtrType>(dchecked_integral_cast<uintptr_t>(element));
+ } else {
+ DCHECK_EQ(ptr_size, 4u);
+ uint32_t element = reinterpret_cast<const uint32_t*>(ptr_array)[idx];
+ return reinterpret_cast<PtrType>(dchecked_integral_cast<uintptr_t>(element));
+ }
+}
+
+template <typename PtrType>
+inline void DexCache::SetElementPtrSize(PtrType* ptr_array,
+ size_t idx,
+ PtrType ptr,
+ size_t ptr_size) {
+ if (ptr_size == 8u) {
+ reinterpret_cast<uint64_t*>(ptr_array)[idx] =
+ dchecked_integral_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr));
+ } else {
+ DCHECK_EQ(ptr_size, 4u);
+ reinterpret_cast<uint32_t*>(ptr_array)[idx] =
+ dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(ptr));
+ }
+}
+
+template <VerifyObjectFlags kVerifyFlags, typename Visitor>
+inline void DexCache::VisitReferences(mirror::Class* klass, const Visitor& visitor) {
+ // Visit instance fields first.
+ VisitInstanceFieldsReferences(klass, visitor);
+ // Visit arrays after.
+ GcRoot<mirror::String>* strings = GetStrings();
+ for (size_t i = 0, num_strings = NumStrings(); i != num_strings; ++i) {
+ visitor.VisitRootIfNonNull(strings[i].AddressWithoutBarrier());
+ }
+ GcRoot<mirror::Class>* resolved_types = GetResolvedTypes();
+ for (size_t i = 0, num_types = NumResolvedTypes(); i != num_types; ++i) {
+ visitor.VisitRootIfNonNull(resolved_types[i].AddressWithoutBarrier());
+ }
}
} // namespace mirror
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 630faee..349a319 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "dex_cache.h"
+#include "dex_cache-inl.h"
#include "art_method-inl.h"
#include "base/logging.h"
@@ -31,22 +31,34 @@
namespace art {
namespace mirror {
-void DexCache::Init(const DexFile* dex_file, String* location, ObjectArray<String>* strings,
- ObjectArray<Class>* resolved_types, PointerArray* resolved_methods,
- PointerArray* resolved_fields, size_t pointer_size) {
+void DexCache::Init(const DexFile* dex_file,
+ String* location,
+ GcRoot<String>* strings,
+ uint32_t num_strings,
+ GcRoot<Class>* resolved_types,
+ uint32_t num_resolved_types,
+ ArtMethod** resolved_methods,
+ uint32_t num_resolved_methods,
+ ArtField** resolved_fields,
+ uint32_t num_resolved_fields,
+ size_t pointer_size) {
CHECK(dex_file != nullptr);
CHECK(location != nullptr);
- CHECK(strings != nullptr);
- CHECK(resolved_types != nullptr);
- CHECK(resolved_methods != nullptr);
- CHECK(resolved_fields != nullptr);
+ CHECK_EQ(num_strings != 0u, strings != nullptr);
+ CHECK_EQ(num_resolved_types != 0u, resolved_types != nullptr);
+ CHECK_EQ(num_resolved_methods != 0u, resolved_methods != nullptr);
+ CHECK_EQ(num_resolved_fields != 0u, resolved_fields != nullptr);
SetDexFile(dex_file);
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location);
- SetFieldObject<false>(StringsOffset(), strings);
- SetFieldObject<false>(ResolvedFieldsOffset(), resolved_fields);
- SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types);
- SetFieldObject<false>(ResolvedMethodsOffset(), resolved_methods);
+ SetField64<false>(StringsOffset(), reinterpret_cast<uintptr_t>(strings));
+ SetField64<false>(ResolvedTypesOffset(), reinterpret_cast<uintptr_t>(resolved_types));
+ SetField64<false>(ResolvedMethodsOffset(), reinterpret_cast<uintptr_t>(resolved_methods));
+ SetField64<false>(ResolvedFieldsOffset(), reinterpret_cast<uintptr_t>(resolved_fields));
+ SetField32<false>(NumStringsOffset(), num_strings);
+ SetField32<false>(NumResolvedTypesOffset(), num_resolved_types);
+ SetField32<false>(NumResolvedMethodsOffset(), num_resolved_methods);
+ SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields);
Runtime* const runtime = Runtime::Current();
if (runtime->HasResolutionMethod()) {
@@ -60,9 +72,9 @@
CHECK(trampoline != nullptr);
CHECK(trampoline->IsRuntimeMethod());
auto* resolved_methods = GetResolvedMethods();
- for (size_t i = 0, length = resolved_methods->GetLength(); i < length; i++) {
- if (resolved_methods->GetElementPtrSize<ArtMethod*>(i, pointer_size) == nullptr) {
- resolved_methods->SetElementPtrSize(i, trampoline, pointer_size);
+ for (size_t i = 0, length = NumResolvedMethods(); i < length; i++) {
+ if (GetElementPtrSize<ArtMethod*>(resolved_methods, i, pointer_size) == nullptr) {
+ SetElementPtrSize(resolved_methods, i, trampoline, pointer_size);
}
}
}
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index ba49a15..3144553 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -46,8 +46,16 @@
return sizeof(DexCache);
}
- void Init(const DexFile* dex_file, String* location, ObjectArray<String>* strings,
- ObjectArray<Class>* types, PointerArray* methods, PointerArray* fields,
+ void Init(const DexFile* dex_file,
+ String* location,
+ GcRoot<String>* strings,
+ uint32_t num_strings,
+ GcRoot<Class>* resolved_types,
+ uint32_t num_resolved_types,
+ ArtMethod** resolved_methods,
+ uint32_t num_resolved_methods,
+ ArtField** resolved_fields,
+ uint32_t num_resolved_fields,
size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_);
void Fixup(ArtMethod* trampoline, size_t pointer_size)
@@ -65,6 +73,10 @@
return OFFSET_OF_OBJECT_MEMBER(DexCache, strings_);
}
+ static MemberOffset ResolvedTypesOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_);
+ }
+
static MemberOffset ResolvedFieldsOffset() {
return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_fields_);
}
@@ -73,40 +85,32 @@
return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_methods_);
}
- size_t NumStrings() SHARED_REQUIRES(Locks::mutator_lock_) {
- return GetStrings()->GetLength();
+ static MemberOffset NumStringsOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_);
}
- size_t NumResolvedTypes() SHARED_REQUIRES(Locks::mutator_lock_) {
- return GetResolvedTypes()->GetLength();
+ static MemberOffset NumResolvedTypesOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_types_);
}
- size_t NumResolvedMethods() SHARED_REQUIRES(Locks::mutator_lock_) {
- return GetResolvedMethods()->GetLength();
+ static MemberOffset NumResolvedFieldsOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_fields_);
}
- size_t NumResolvedFields() SHARED_REQUIRES(Locks::mutator_lock_) {
- return GetResolvedFields()->GetLength();
+ static MemberOffset NumResolvedMethodsOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_methods_);
}
- String* GetResolvedString(uint32_t string_idx) SHARED_REQUIRES(Locks::mutator_lock_) {
- return GetStrings()->Get(string_idx);
- }
+ String* GetResolvedString(uint32_t string_idx) ALWAYS_INLINE
+ SHARED_REQUIRES(Locks::mutator_lock_);
void SetResolvedString(uint32_t string_idx, String* resolved) ALWAYS_INLINE
- SHARED_REQUIRES(Locks::mutator_lock_) {
- // TODO default transaction support.
- GetStrings()->Set(string_idx, resolved);
- }
-
- Class* GetResolvedType(uint32_t type_idx) ALWAYS_INLINE
- SHARED_REQUIRES(Locks::mutator_lock_) {
- return GetResolvedTypes()->Get(type_idx);
- }
-
- void SetResolvedType(uint32_t type_idx, Class* resolved)
SHARED_REQUIRES(Locks::mutator_lock_);
+ Class* GetResolvedType(uint32_t type_idx) SHARED_REQUIRES(Locks::mutator_lock_);
+
+ void SetResolvedType(uint32_t type_idx, Class* resolved) SHARED_REQUIRES(Locks::mutator_lock_);
+
ALWAYS_INLINE ArtMethod* GetResolvedMethod(uint32_t method_idx, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -121,21 +125,36 @@
ALWAYS_INLINE void SetResolvedField(uint32_t idx, ArtField* field, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- ObjectArray<String>* GetStrings() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
- return GetFieldObject<ObjectArray<String>>(StringsOffset());
+ GcRoot<String>* GetStrings() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
+ return GetFieldPtr<GcRoot<String>*>(StringsOffset());
}
- ObjectArray<Class>* GetResolvedTypes() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
- return GetFieldObject<ObjectArray<Class>>(
- OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_));
+ GcRoot<Class>* GetResolvedTypes() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
+ return GetFieldPtr<GcRoot<Class>*>(ResolvedTypesOffset());
}
- PointerArray* GetResolvedMethods() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
- return GetFieldObject<PointerArray>(ResolvedMethodsOffset());
+ ArtMethod** GetResolvedMethods() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
+ return GetFieldPtr<ArtMethod**>(ResolvedMethodsOffset());
}
- PointerArray* GetResolvedFields() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
- return GetFieldObject<PointerArray>(ResolvedFieldsOffset());
+ ArtField** GetResolvedFields() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
+ return GetFieldPtr<ArtField**>(ResolvedFieldsOffset());
+ }
+
+ size_t NumStrings() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return GetField32(NumStringsOffset());
+ }
+
+ size_t NumResolvedTypes() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return GetField32(NumResolvedTypesOffset());
+ }
+
+ size_t NumResolvedMethods() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return GetField32(NumResolvedMethodsOffset());
+ }
+
+ size_t NumResolvedFields() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return GetField32(NumResolvedFieldsOffset());
}
const DexFile* GetDexFile() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -147,17 +166,36 @@
return SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file);
}
+ // NOTE: Get/SetElementPtrSize() are intended for working with ArtMethod** and ArtField**
+ // provided by GetResolvedMethods/Fields() and ArtMethod::GetDexCacheResolvedMethods(),
+ // so they need to be public.
+
+ template <typename PtrType>
+ static PtrType GetElementPtrSize(PtrType* ptr_array, size_t idx, size_t ptr_size);
+
+ template <typename PtrType>
+ static void SetElementPtrSize(PtrType* ptr_array, size_t idx, PtrType ptr, size_t ptr_size);
+
private:
+ // Visit instance fields of the dex cache as well as its associated arrays.
+ template <VerifyObjectFlags kVerifyFlags, typename Visitor>
+ void VisitReferences(mirror::Class* klass, const Visitor& visitor)
+ SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
+
HeapReference<Object> dex_;
HeapReference<String> location_;
- // Either an int array or long array based on runtime ISA since these arrays hold pointers.
- HeapReference<PointerArray> resolved_fields_;
- HeapReference<PointerArray> resolved_methods_;
- HeapReference<ObjectArray<Class>> resolved_types_;
- HeapReference<ObjectArray<String>> strings_;
- uint64_t dex_file_;
+ uint64_t dex_file_; // const DexFile*
+ uint64_t resolved_fields_; // ArtField*, array with num_resolved_fields_ elements.
+ uint64_t resolved_methods_; // ArtMethod*, array with num_resolved_methods_ elements.
+ uint64_t resolved_types_; // GcRoot<Class>*, array with num_resolved_types_ elements.
+ uint64_t strings_; // GcRoot<String>*, array with num_strings_ elements.
+ uint32_t num_resolved_fields_; // Number of elements in the resolved_fields_ array.
+ uint32_t num_resolved_methods_; // Number of elements in the resolved_methods_ array.
+ uint32_t num_resolved_types_; // Number of elements in the resolved_types_ array.
+ uint32_t num_strings_; // Number of elements in the strings_ array.
friend struct art::DexCacheOffsets; // for verifying offset information
+ friend class Object; // For VisitReferences
DISALLOW_IMPLICIT_CONSTRUCTORS(DexCache);
};
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 228fce5..8fb860f 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -43,19 +43,6 @@
EXPECT_EQ(java_lang_dex_file_->NumTypeIds(), dex_cache->NumResolvedTypes());
EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods());
EXPECT_EQ(java_lang_dex_file_->NumFieldIds(), dex_cache->NumResolvedFields());
-
- EXPECT_LE(0, dex_cache->GetStrings()->GetLength());
- EXPECT_LE(0, dex_cache->GetResolvedTypes()->GetLength());
- EXPECT_LE(0, dex_cache->GetResolvedMethods()->GetLength());
- EXPECT_LE(0u, dex_cache->NumResolvedFields());
-
- EXPECT_EQ(java_lang_dex_file_->NumStringIds(),
- static_cast<uint32_t>(dex_cache->GetStrings()->GetLength()));
- EXPECT_EQ(java_lang_dex_file_->NumTypeIds(),
- static_cast<uint32_t>(dex_cache->GetResolvedTypes()->GetLength()));
- EXPECT_EQ(java_lang_dex_file_->NumMethodIds(),
- static_cast<uint32_t>(dex_cache->GetResolvedMethods()->GetLength()));
- EXPECT_EQ(java_lang_dex_file_->NumFieldIds(), dex_cache->NumResolvedFields());
}
} // namespace mirror
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index e35ddcc..90180c5 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -27,6 +27,7 @@
#include "class_flags.h"
#include "class_linker.h"
#include "class_loader-inl.h"
+#include "dex_cache-inl.h"
#include "lock_word-inl.h"
#include "monitor.h"
#include "object_array-inl.h"
@@ -1006,6 +1007,17 @@
return down_cast<mirror::ClassLoader*>(this);
}
+template<VerifyObjectFlags kVerifyFlags>
+inline bool Object::IsDexCache() {
+ return GetClass<kVerifyFlags>()->IsDexCacheClass();
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline mirror::DexCache* Object::AsDexCache() {
+ DCHECK(IsDexCache<kVerifyFlags>());
+ return down_cast<mirror::DexCache*>(this);
+}
+
template <VerifyObjectFlags kVerifyFlags, typename Visitor, typename JavaLangRefVisitor>
inline void Object::VisitReferences(const Visitor& visitor,
const JavaLangRefVisitor& ref_visitor) {
@@ -1031,6 +1043,9 @@
} else if ((class_flags & kClassFlagReference) != 0) {
VisitInstanceFieldsReferences(klass, visitor);
ref_visitor(klass, AsReference());
+ } else if (class_flags == kClassFlagDexCache) {
+ mirror::DexCache* const dex_cache = AsDexCache<kVerifyFlags>();
+ dex_cache->VisitReferences<kVerifyFlags>(klass, visitor);
} else {
mirror::ClassLoader* const class_loader = AsClassLoader<kVerifyFlags>();
class_loader->VisitReferences<kVerifyFlags>(klass, visitor);
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 3cec29c..50490bb 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -38,6 +38,7 @@
class Array;
class Class;
class ClassLoader;
+class DexCache;
class FinalizerReference;
template<class T> class ObjectArray;
template<class T> class PrimitiveArray;
@@ -162,6 +163,11 @@
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ClassLoader* AsClassLoader() SHARED_REQUIRES(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsDexCache() SHARED_REQUIRES(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ DexCache* AsDexCache() SHARED_REQUIRES(Locks::mutator_lock_);
+
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
bool IsArrayInstance() SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index 7910f94..9e12806 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -35,7 +35,7 @@
trace = soa.Self()->CreateInternalStackTrace<false>(soa);
} else {
// Suspend thread to build stack trace.
- ScopedThreadSuspension sts(soa.Self(), kSuspended);
+ ScopedThreadSuspension sts(soa.Self(), kNative);
ThreadList* thread_list = Runtime::Current()->GetThreadList();
bool timed_out;
Thread* thread = thread_list->SuspendThreadByPeer(peer, true, false, &timed_out);
@@ -47,11 +47,9 @@
}
// Restart suspended thread.
thread_list->Resume(thread, false);
- } else {
- if (timed_out) {
- LOG(ERROR) << "Trying to get thread's stack failed as the thread failed to suspend within a "
- "generous timeout.";
- }
+ } else if (timed_out) {
+ LOG(ERROR) << "Trying to get thread's stack failed as the thread failed to suspend within a "
+ "generous timeout.";
}
}
return trace;
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index a2d9797..994ccb1 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -52,12 +52,14 @@
static jobject DexCache_getResolvedType(JNIEnv* env, jobject javaDexCache, jint type_index) {
ScopedFastNativeObjectAccess soa(env);
mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+ CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes());
return soa.AddLocalReference<jobject>(dex_cache->GetResolvedType(type_index));
}
static jobject DexCache_getResolvedString(JNIEnv* env, jobject javaDexCache, jint string_index) {
ScopedFastNativeObjectAccess soa(env);
mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+ CHECK_LT(static_cast<size_t>(string_index), dex_cache->NumStrings());
return soa.AddLocalReference<jobject>(dex_cache->GetResolvedString(string_index));
}
@@ -65,6 +67,7 @@
jobject type) {
ScopedFastNativeObjectAccess soa(env);
mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+ CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes());
dex_cache->SetResolvedType(type_index, soa.Decode<mirror::Class*>(type));
}
@@ -72,6 +75,7 @@
jobject string) {
ScopedFastNativeObjectAccess soa(env);
mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
+ CHECK_LT(static_cast<size_t>(string_index), dex_cache->NumStrings());
dex_cache->SetResolvedString(string_index, soa.Decode<mirror::String*>(string));
}
diff --git a/runtime/oat.h b/runtime/oat.h
index 1520a9b..b8b8d30 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '0', '6', '9', '\0' };
+ static constexpr uint8_t kOatVersion[] = { '0', '7', '0', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 25b5e49..50e2053 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -158,6 +158,9 @@
.Define("-Xjitthreshold:_")
.WithType<unsigned int>()
.IntoKey(M::JITCompileThreshold)
+ .Define("-Xjitwarmupthreshold:_")
+ .WithType<unsigned int>()
+ .IntoKey(M::JITWarmupThreshold)
.Define("-XX:HspaceCompactForOOMMinIntervalMs=_") // in ms
.WithType<MillisecondsToNanoseconds>() // store as ns
.IntoKey(M::HSpaceCompactForOOMMinIntervalsMs)
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index dd3703c..64c2249 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -40,6 +40,9 @@
kIntrinsicReverseBits,
kIntrinsicReverseBytes,
kIntrinsicNumberOfLeadingZeros,
+ kIntrinsicNumberOfTrailingZeros,
+ kIntrinsicRotateRight,
+ kIntrinsicRotateLeft,
kIntrinsicAbsInt,
kIntrinsicAbsLong,
kIntrinsicAbsFloat,
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 9d5ce9f..60defba 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -40,7 +40,7 @@
handler_dex_pc_(0), clear_exception_(false), handler_frame_depth_(kInvalidFrameDepth) {
}
-// Finds catch handler or prepares for deoptimization.
+// Finds catch handler.
class CatchBlockStackVisitor FINAL : public StackVisitor {
public:
CatchBlockStackVisitor(Thread* self, Context* context, Handle<mirror::Throwable>* exception,
@@ -125,7 +125,7 @@
StackHandleScope<1> hs(self_);
Handle<mirror::Throwable> exception_ref(hs.NewHandle(exception));
- // Walk the stack to find catch handler or prepare for deoptimization.
+ // Walk the stack to find catch handler.
CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this);
visitor.WalkStack(true);
@@ -146,16 +146,6 @@
// Put exception back in root set with clear throw location.
self_->SetException(exception_ref.Get());
}
- // The debugger may suspend this thread and walk its stack. Let's do this before popping
- // instrumentation frames.
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
- if (instrumentation->HasExceptionCaughtListeners()
- && self_->IsExceptionThrownByCurrentMethod(exception)) {
- instrumentation->ExceptionCaughtEvent(self_, exception_ref.Get());
- // Instrumentation may have been updated.
- method_tracing_active_ = is_deoptimization_ ||
- Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled();
- }
}
// Prepares deoptimization.
@@ -189,6 +179,12 @@
// Ignore callee save method.
DCHECK(method->IsCalleeSaveMethod());
return true;
+ } else if (method->IsNative()) {
+ // If we return from JNI with a pending exception and want to deoptimize, we need to skip
+ // the native method.
+ // The top method is a runtime method, the native method comes next.
+ CHECK_EQ(GetFrameDepth(), 1U);
+ return true;
} else {
return HandleDeoptimization(method);
}
@@ -201,7 +197,7 @@
bool HandleDeoptimization(ArtMethod* m) SHARED_REQUIRES(Locks::mutator_lock_) {
const DexFile::CodeItem* code_item = m->GetCodeItem();
- CHECK(code_item != nullptr);
+ CHECK(code_item != nullptr) << "No code item for " << PrettyMethod(m);
uint16_t num_regs = code_item->registers_size_;
uint32_t dex_pc = GetDexPc();
StackHandleScope<2> hs(self_); // Dex cache, class loader and method.
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index e934834..4db95a8 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -43,9 +43,18 @@
UNREACHABLE();
}
+ // Find the catch handler for the given exception.
void FindCatch(mirror::Throwable* exception) SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Deoptimize the stack to the upcall. For every compiled frame, we create a "copy"
+ // shadow frame that will be executed with the interpreter.
void DeoptimizeStack() SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Update the instrumentation stack by removing all methods that will be unwound
+ // by the exception being thrown.
void UpdateInstrumentationStack() SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Long jump either to a catch handler or to the upcall.
NO_RETURN void DoLongJump() SHARED_REQUIRES(Locks::mutator_lock_);
void SetHandlerQuickFrame(ArtMethod** handler_quick_frame) {
@@ -83,9 +92,10 @@
private:
Thread* const self_;
Context* const context_;
+ // Should we deoptimize the stack?
const bool is_deoptimization_;
// Is method tracing active?
- bool method_tracing_active_;
+ const bool method_tracing_active_;
// Quick frame with found handler or last frame if no handler found.
ArtMethod** handler_quick_frame_;
// PC to branch to for the handler.
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 2fe1e64..324bd9f 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -223,8 +223,11 @@
for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
mirror::Object* arg = args->Get(args_offset);
if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) {
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
mirror::Class* dst_class =
- m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_, true);
+ m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_,
+ true /* resolve */,
+ pointer_size);
if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
ThrowIllegalArgumentException(
StringPrintf("method %s argument %zd has type %s, got %s",
@@ -356,9 +359,12 @@
}
// TODO: If args contain object references, it may cause problems.
Thread* const self = Thread::Current();
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (uint32_t i = 0; i < num_params; i++) {
uint16_t type_idx = params->GetTypeItem(i).type_idx_;
- mirror::Class* param_type = m->GetClassFromTypeIndex(type_idx, true);
+ mirror::Class* param_type = m->GetClassFromTypeIndex(type_idx,
+ true /* resolve*/,
+ pointer_size);
if (param_type == nullptr) {
CHECK(self->IsExceptionPending());
LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
@@ -453,7 +459,7 @@
}
mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
uint32_t shorty_len = 0;
- const char* shorty = method->GetShorty(&shorty_len);
+ const char* shorty = method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
@@ -483,7 +489,7 @@
}
mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
uint32_t shorty_len = 0;
- const char* shorty = method->GetShorty(&shorty_len);
+ const char* shorty = method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.BuildArgArrayFromJValues(soa, receiver, args);
@@ -514,7 +520,7 @@
receiver = nullptr;
}
uint32_t shorty_len = 0;
- const char* shorty = method->GetShorty(&shorty_len);
+ const char* shorty = method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.BuildArgArrayFromJValues(soa, receiver, args);
@@ -545,7 +551,7 @@
receiver = nullptr;
}
uint32_t shorty_len = 0;
- const char* shorty = method->GetShorty(&shorty_len);
+ const char* shorty = method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty(&shorty_len);
JValue result;
ArgArray arg_array(shorty, shorty_len);
arg_array.BuildArgArrayFromVarArgs(soa, receiver, args);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 3119cce..bbadb1e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1751,7 +1751,8 @@
jit_.reset(jit::Jit::Create(jit_options_.get(), &error_msg));
if (jit_.get() != nullptr) {
compiler_callbacks_ = jit_->GetCompilerCallbacks();
- jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold());
+ jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold(),
+ jit_options_->GetWarmupThreshold());
jit_->CreateThreadPool();
} else {
LOG(WARNING) << "Failed to create JIT " << error_msg;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 02ed3a2..d88e84b 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -68,6 +68,7 @@
RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true)
RUNTIME_OPTIONS_KEY (bool, UseJIT, false)
RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold, jit::Jit::kDefaultCompileThreshold)
+RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold, jit::Jit::kDefaultWarmupThreshold)
RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheCapacity, jit::JitCodeCache::kDefaultCapacity)
RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
HSpaceCompactForOOMMinIntervalsMs,\
diff --git a/runtime/thread.cc b/runtime/thread.cc
index af5830a..86ac140 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2344,10 +2344,31 @@
// Get exception from thread.
mirror::Throwable* exception = GetException();
CHECK(exception != nullptr);
+ bool is_deoptimization = (exception == GetDeoptimizationException());
+ if (!is_deoptimization) {
+ // This is a real exception: let the instrumentation know about it.
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (instrumentation->HasExceptionCaughtListeners() &&
+ IsExceptionThrownByCurrentMethod(exception)) {
+ // Instrumentation may cause GC so keep the exception object safe.
+ StackHandleScope<1> hs(this);
+ HandleWrapper<mirror::Throwable> h_exception(hs.NewHandleWrapper(&exception));
+ instrumentation->ExceptionCaughtEvent(this, exception);
+ }
+ // Does instrumentation need to deoptimize the stack?
+ // Note: we do this *after* reporting the exception to instrumentation in case it
+ // now requires deoptimization. It may happen if a debugger is attached and requests
+ // new events (single-step, breakpoint, ...) when the exception is reported.
+ is_deoptimization = Dbg::IsForcedInterpreterNeededForException(this);
+ if (is_deoptimization) {
+ // Save the exception into the deoptimization context so it can be restored
+ // before entering the interpreter.
+ PushDeoptimizationContext(JValue(), false, exception);
+ }
+ }
// Don't leave exception visible while we try to find the handler, which may cause class
// resolution.
ClearException();
- bool is_deoptimization = (exception == GetDeoptimizationException());
QuickExceptionHandler exception_handler(this, is_deoptimization);
if (is_deoptimization) {
exception_handler.DeoptimizeStack();
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index d63781b..6176acd 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -1282,4 +1282,12 @@
allocated_ids_.reset(id);
}
+ScopedSuspendAll::ScopedSuspendAll(const char* cause, bool long_suspend) {
+ Runtime::Current()->GetThreadList()->SuspendAll(cause, long_suspend);
+}
+
+ScopedSuspendAll::~ScopedSuspendAll() {
+ Runtime::Current()->GetThreadList()->ResumeAll();
+}
+
} // namespace art
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 4c50181..c727432 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -19,6 +19,7 @@
#include "base/histogram.h"
#include "base/mutex.h"
+#include "base/value_object.h"
#include "gc_root.h"
#include "jni.h"
#include "object_callbacks.h"
@@ -60,12 +61,13 @@
REQUIRES(!Locks::thread_suspend_count_lock_);
// Suspends all threads and gets exclusive access to the mutator_lock_.
- // If long suspend is true, then other people who try to suspend will never timeout. Long suspend
- // is currenly used for hprof since large heaps take a long time.
+ // If long_suspend is true, then other threads who try to suspend will never timeout.
+ // long_suspend is currenly used for hprof since large heaps take a long time.
void SuspendAll(const char* cause, bool long_suspend = false)
EXCLUSIVE_LOCK_FUNCTION(Locks::mutator_lock_)
- REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
-
+ REQUIRES(!Locks::thread_list_lock_,
+ !Locks::thread_suspend_count_lock_,
+ !Locks::mutator_lock_);
// Suspend a thread using a peer, typically used by the debugger. Returns the thread on success,
// else null. The peer is used to identify the thread to avoid races with the thread terminating.
@@ -188,6 +190,20 @@
DISALLOW_COPY_AND_ASSIGN(ThreadList);
};
+// Helper for suspending all threads and
+class ScopedSuspendAll : public ValueObject {
+ public:
+ ScopedSuspendAll(const char* cause, bool long_suspend = false)
+ EXCLUSIVE_LOCK_FUNCTION(Locks::mutator_lock_)
+ REQUIRES(!Locks::thread_list_lock_,
+ !Locks::thread_suspend_count_lock_,
+ !Locks::mutator_lock_);
+ // No REQUIRES(mutator_lock_) since the unlock function already asserts this.
+ ~ScopedSuspendAll()
+ UNLOCK_FUNCTION(Locks::mutator_lock_)
+ REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
+};
+
} // namespace art
#endif // ART_RUNTIME_THREAD_LIST_H_
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 4ab5c0e..5a44947 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -293,13 +293,11 @@
break;
}
}
-
- runtime->GetThreadList()->SuspendAll(__FUNCTION__);
{
+ ScopedSuspendAll ssa(__FUNCTION__);
MutexLock mu(self, *Locks::thread_list_lock_);
runtime->GetThreadList()->ForEach(GetSample, the_trace);
}
- runtime->GetThreadList()->ResumeAll();
ATRACE_END();
}
@@ -348,10 +346,9 @@
// Enable count of allocs if specified in the flags.
bool enable_stats = false;
- runtime->GetThreadList()->SuspendAll(__FUNCTION__);
-
// Create Trace object.
{
+ ScopedSuspendAll ssa(__FUNCTION__);
MutexLock mu(self, *Locks::trace_lock_);
if (the_trace_ != nullptr) {
LOG(ERROR) << "Trace already in progress, ignoring this request";
@@ -375,8 +372,6 @@
}
}
- runtime->GetThreadList()->ResumeAll();
-
// Can't call this when holding the mutator lock.
if (enable_stats) {
runtime->SetStatsEnabled(true);
@@ -405,40 +400,41 @@
CHECK_PTHREAD_CALL(pthread_join, (sampling_pthread, nullptr), "sampling thread shutdown");
sampling_pthread_ = 0U;
}
- runtime->GetThreadList()->SuspendAll(__FUNCTION__);
- if (the_trace != nullptr) {
- stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0;
- if (finish_tracing) {
- the_trace->FinishTracing();
- }
+ {
+ ScopedSuspendAll ssa(__FUNCTION__);
+ if (the_trace != nullptr) {
+ stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0;
+ if (finish_tracing) {
+ the_trace->FinishTracing();
+ }
- if (the_trace->trace_mode_ == TraceMode::kSampling) {
- MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
- runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
- } else {
- runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
- runtime->GetInstrumentation()->RemoveListener(
- the_trace, instrumentation::Instrumentation::kMethodEntered |
- instrumentation::Instrumentation::kMethodExited |
- instrumentation::Instrumentation::kMethodUnwind);
- }
- if (the_trace->trace_file_.get() != nullptr) {
- // Do not try to erase, so flush and close explicitly.
- if (flush_file) {
- if (the_trace->trace_file_->Flush() != 0) {
- PLOG(WARNING) << "Could not flush trace file.";
- }
+ if (the_trace->trace_mode_ == TraceMode::kSampling) {
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
} else {
- the_trace->trace_file_->MarkUnchecked(); // Do not trigger guard.
+ runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
+ runtime->GetInstrumentation()->RemoveListener(
+ the_trace, instrumentation::Instrumentation::kMethodEntered |
+ instrumentation::Instrumentation::kMethodExited |
+ instrumentation::Instrumentation::kMethodUnwind);
}
- if (the_trace->trace_file_->Close() != 0) {
- PLOG(ERROR) << "Could not close trace file.";
+ if (the_trace->trace_file_.get() != nullptr) {
+ // Do not try to erase, so flush and close explicitly.
+ if (flush_file) {
+ if (the_trace->trace_file_->Flush() != 0) {
+ PLOG(WARNING) << "Could not flush trace file.";
+ }
+ } else {
+ the_trace->trace_file_->MarkUnchecked(); // Do not trigger guard.
+ }
+ if (the_trace->trace_file_->Close() != 0) {
+ PLOG(ERROR) << "Could not close trace file.";
+ }
}
+ delete the_trace;
}
- delete the_trace;
}
- runtime->GetThreadList()->ResumeAll();
if (stop_alloc_counting) {
// Can be racy since SetStatsEnabled is not guarded by any locks.
runtime->SetStatsEnabled(false);
@@ -492,7 +488,7 @@
}
if (the_trace != nullptr) {
- runtime->GetThreadList()->SuspendAll(__FUNCTION__);
+ ScopedSuspendAll ssa(__FUNCTION__);
stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0;
if (the_trace->trace_mode_ == TraceMode::kSampling) {
@@ -500,12 +496,12 @@
runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
} else {
runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
- runtime->GetInstrumentation()->RemoveListener(the_trace,
- instrumentation::Instrumentation::kMethodEntered |
- instrumentation::Instrumentation::kMethodExited |
- instrumentation::Instrumentation::kMethodUnwind);
+ runtime->GetInstrumentation()->RemoveListener(
+ the_trace,
+ instrumentation::Instrumentation::kMethodEntered |
+ instrumentation::Instrumentation::kMethodExited |
+ instrumentation::Instrumentation::kMethodUnwind);
}
- runtime->GetThreadList()->ResumeAll();
}
if (stop_alloc_counting) {
@@ -531,23 +527,23 @@
// Enable count of allocs if specified in the flags.
bool enable_stats = (the_trace->flags_ && kTraceCountAllocs) != 0;
- runtime->GetThreadList()->SuspendAll(__FUNCTION__);
+ {
+ ScopedSuspendAll ssa(__FUNCTION__);
- // Reenable.
- if (the_trace->trace_mode_ == TraceMode::kSampling) {
- CHECK_PTHREAD_CALL(pthread_create, (&sampling_pthread_, nullptr, &RunSamplingThread,
- reinterpret_cast<void*>(the_trace->interval_us_)), "Sampling profiler thread");
- } else {
- runtime->GetInstrumentation()->AddListener(the_trace,
- instrumentation::Instrumentation::kMethodEntered |
- instrumentation::Instrumentation::kMethodExited |
- instrumentation::Instrumentation::kMethodUnwind);
- // TODO: In full-PIC mode, we don't need to fully deopt.
- runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey);
+ // Reenable.
+ if (the_trace->trace_mode_ == TraceMode::kSampling) {
+ CHECK_PTHREAD_CALL(pthread_create, (&sampling_pthread_, nullptr, &RunSamplingThread,
+ reinterpret_cast<void*>(the_trace->interval_us_)), "Sampling profiler thread");
+ } else {
+ runtime->GetInstrumentation()->AddListener(the_trace,
+ instrumentation::Instrumentation::kMethodEntered |
+ instrumentation::Instrumentation::kMethodExited |
+ instrumentation::Instrumentation::kMethodUnwind);
+ // TODO: In full-PIC mode, we don't need to fully deopt.
+ runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey);
+ }
}
- runtime->GetThreadList()->ResumeAll();
-
// Can't call this when holding the mutator lock.
if (enable_stats) {
runtime->SetStatsEnabled(true);
@@ -806,6 +802,15 @@
LOG(ERROR) << "Unexpected backward branch event in tracing" << PrettyMethod(method);
}
+void Trace::InvokeVirtualOrInterface(Thread*,
+ mirror::Object*,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ ArtMethod*) {
+ LOG(ERROR) << "Unexpected invoke event in tracing" << PrettyMethod(method)
+ << " " << dex_pc;
+}
+
void Trace::ReadClocks(Thread* thread, uint32_t* thread_clock_diff, uint32_t* wall_clock_diff) {
if (UseThreadCpuClock()) {
uint64_t clock_base = thread->GetTraceClockBase();
diff --git a/runtime/trace.h b/runtime/trace.h
index 04be3dd..87a691d 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -166,6 +166,12 @@
SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset)
SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
+ void InvokeVirtualOrInterface(Thread* thread,
+ mirror::Object* this_object,
+ ArtMethod* caller,
+ uint32_t dex_pc,
+ ArtMethod* callee)
+ SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
// Reuse an old stack trace if it exists, otherwise allocate a new one.
static std::vector<ArtMethod*>* AllocStackTrace();
// Clear and store an old stack trace for later use.
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
new file mode 100644
index 0000000..4f662d5
--- /dev/null
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
+#define ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
+
+#include "dex_cache_arrays_layout.h"
+
+#include "base/bit_utils.h"
+#include "base/logging.h"
+#include "gc_root.h"
+#include "globals.h"
+#include "primitive.h"
+
+namespace art {
+
+inline DexCacheArraysLayout::DexCacheArraysLayout(size_t pointer_size, const DexFile* dex_file)
+ : pointer_size_(pointer_size),
+ /* types_offset_ is always 0u, so it's constexpr */
+ methods_offset_(types_offset_ +
+ RoundUp(TypesSize(dex_file->NumTypeIds()), MethodsAlignment())),
+ strings_offset_(methods_offset_ +
+ RoundUp(MethodsSize(dex_file->NumMethodIds()), StringsAlignment())),
+ fields_offset_(strings_offset_ +
+ RoundUp(StringsSize(dex_file->NumStringIds()), FieldsAlignment())),
+ size_(fields_offset_ +
+ RoundUp(FieldsSize(dex_file->NumFieldIds()), Alignment())) {
+ DCHECK(ValidPointerSize(pointer_size)) << pointer_size;
+}
+
+inline size_t DexCacheArraysLayout::Alignment() const {
+ // GcRoot<> alignment is 4, i.e. lower than or equal to the pointer alignment.
+ static_assert(alignof(GcRoot<mirror::Class>) == 4, "Expecting alignof(GcRoot<>) == 4");
+ static_assert(alignof(GcRoot<mirror::String>) == 4, "Expecting alignof(GcRoot<>) == 4");
+ DCHECK(pointer_size_ == 4u || pointer_size_ == 8u);
+ // Pointer alignment is the same as pointer size.
+ return pointer_size_;
+}
+
+inline size_t DexCacheArraysLayout::TypeOffset(uint32_t type_idx) const {
+ return types_offset_ + ElementOffset(sizeof(GcRoot<mirror::Class>), type_idx);
+}
+
+inline size_t DexCacheArraysLayout::TypesSize(size_t num_elements) const {
+ return ArraySize(sizeof(GcRoot<mirror::Class>), num_elements);
+}
+
+inline size_t DexCacheArraysLayout::TypesAlignment() const {
+ return alignof(GcRoot<mirror::Class>);
+}
+
+inline size_t DexCacheArraysLayout::MethodOffset(uint32_t method_idx) const {
+ return methods_offset_ + ElementOffset(pointer_size_, method_idx);
+}
+
+inline size_t DexCacheArraysLayout::MethodsSize(size_t num_elements) const {
+ return ArraySize(pointer_size_, num_elements);
+}
+
+inline size_t DexCacheArraysLayout::MethodsAlignment() const {
+ return pointer_size_;
+}
+
+inline size_t DexCacheArraysLayout::StringOffset(uint32_t string_idx) const {
+ return strings_offset_ + ElementOffset(sizeof(GcRoot<mirror::String>), string_idx);
+}
+
+inline size_t DexCacheArraysLayout::StringsSize(size_t num_elements) const {
+ return ArraySize(sizeof(GcRoot<mirror::String>), num_elements);
+}
+
+inline size_t DexCacheArraysLayout::StringsAlignment() const {
+ return alignof(GcRoot<mirror::String>);
+}
+
+inline size_t DexCacheArraysLayout::FieldOffset(uint32_t field_idx) const {
+ return fields_offset_ + ElementOffset(pointer_size_, field_idx);
+}
+
+inline size_t DexCacheArraysLayout::FieldsSize(size_t num_elements) const {
+ return ArraySize(pointer_size_, num_elements);
+}
+
+inline size_t DexCacheArraysLayout::FieldsAlignment() const {
+ return pointer_size_;
+}
+
+inline size_t DexCacheArraysLayout::ElementOffset(size_t element_size, uint32_t idx) {
+ return element_size * idx;
+}
+
+inline size_t DexCacheArraysLayout::ArraySize(size_t element_size, uint32_t num_elements) {
+ return element_size * num_elements;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_INL_H_
diff --git a/compiler/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h
similarity index 86%
rename from compiler/utils/dex_cache_arrays_layout.h
rename to runtime/utils/dex_cache_arrays_layout.h
index 2a109bd..d50be5a 100644
--- a/compiler/utils/dex_cache_arrays_layout.h
+++ b/runtime/utils/dex_cache_arrays_layout.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
-#define ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
+#ifndef ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
+#define ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
namespace art {
@@ -47,6 +47,8 @@
return size_;
}
+ size_t Alignment() const;
+
size_t TypesOffset() const {
return types_offset_;
}
@@ -55,6 +57,8 @@
size_t TypesSize(size_t num_elements) const;
+ size_t TypesAlignment() const;
+
size_t MethodsOffset() const {
return methods_offset_;
}
@@ -63,6 +67,8 @@
size_t MethodsSize(size_t num_elements) const;
+ size_t MethodsAlignment() const;
+
size_t StringsOffset() const {
return strings_offset_;
}
@@ -71,6 +77,8 @@
size_t StringsSize(size_t num_elements) const;
+ size_t StringsAlignment() const;
+
size_t FieldsOffset() const {
return fields_offset_;
}
@@ -79,6 +87,8 @@
size_t FieldsSize(size_t num_elements) const;
+ size_t FieldsAlignment() const;
+
private:
static constexpr size_t types_offset_ = 0u;
const size_t pointer_size_; // Must be first for construction initialization order.
@@ -87,6 +97,8 @@
const size_t fields_offset_;
const size_t size_;
+ static size_t Alignment(size_t pointer_size);
+
static size_t ElementOffset(size_t element_size, uint32_t idx);
static size_t ArraySize(size_t element_size, uint32_t num_elements);
@@ -94,4 +106,4 @@
} // namespace art
-#endif // ART_COMPILER_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
+#endif // ART_RUNTIME_UTILS_DEX_CACHE_ARRAYS_LAYOUT_H_
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 1d71c3f..3d4f04c 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -425,6 +425,7 @@
has_virtual_or_interface_invokes_(false),
verify_to_dump_(verify_to_dump),
allow_thread_suspension_(allow_thread_suspension),
+ is_constructor_(false),
link_(nullptr) {
self->PushVerifier(this);
DCHECK(class_def != nullptr);
@@ -556,15 +557,124 @@
}
bool MethodVerifier::Verify() {
- // If there aren't any instructions, make sure that's expected, then exit successfully.
- if (code_item_ == nullptr) {
- if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method";
+ // Some older code doesn't correctly mark constructors as such. Test for this case by looking at
+ // the name.
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
+ const char* method_name = dex_file_->StringDataByIdx(method_id.name_idx_);
+ bool instance_constructor_by_name = strcmp("<init>", method_name) == 0;
+ bool static_constructor_by_name = strcmp("<clinit>", method_name) == 0;
+ bool constructor_by_name = instance_constructor_by_name || static_constructor_by_name;
+ // Check that only constructors are tagged, and check for bad code that doesn't tag constructors.
+ if ((method_access_flags_ & kAccConstructor) != 0) {
+ if (!constructor_by_name) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "method is marked as constructor, but not named accordingly";
return false;
- } else {
- return true;
+ }
+ is_constructor_ = true;
+ } else if (constructor_by_name) {
+ LOG(WARNING) << "Method " << PrettyMethod(dex_method_idx_, *dex_file_)
+ << " not marked as constructor.";
+ is_constructor_ = true;
+ }
+ // If it's a constructor, check whether IsStatic() matches the name.
+ // This should have been rejected by the dex file verifier. Only do in debug build.
+ if (kIsDebugBuild) {
+ if (IsConstructor()) {
+ if (IsStatic() ^ static_constructor_by_name) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "constructor name doesn't match static flag";
+ return false;
+ }
}
}
+
+ // Methods may only have one of public/protected/private.
+ // This should have been rejected by the dex file verifier. Only do in debug build.
+ if (kIsDebugBuild) {
+ size_t access_mod_count =
+ (((method_access_flags_ & kAccPublic) == 0) ? 0 : 1) +
+ (((method_access_flags_ & kAccProtected) == 0) ? 0 : 1) +
+ (((method_access_flags_ & kAccPrivate) == 0) ? 0 : 1);
+ if (access_mod_count > 1) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "method has more than one of public/protected/private";
+ return false;
+ }
+ }
+
+ // If there aren't any instructions, make sure that's expected, then exit successfully.
+ if (code_item_ == nullptr) {
+ // This should have been rejected by the dex file verifier. Only do in debug build.
+ if (kIsDebugBuild) {
+ // Only native or abstract methods may not have code.
+ if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method";
+ return false;
+ }
+ if ((method_access_flags_ & kAccAbstract) != 0) {
+ // Abstract methods are not allowed to have the following flags.
+ static constexpr uint32_t kForbidden =
+ kAccPrivate |
+ kAccStatic |
+ kAccFinal |
+ kAccNative |
+ kAccStrict |
+ kAccSynchronized;
+ if ((method_access_flags_ & kForbidden) != 0) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "method can't be abstract and private/static/final/native/strict/synchronized";
+ return false;
+ }
+ }
+ if ((class_def_->GetJavaAccessFlags() & kAccInterface) != 0) {
+ // Interface methods must be public and abstract.
+ if ((method_access_flags_ & (kAccPublic | kAccAbstract)) != (kAccPublic | kAccAbstract)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be public and abstract";
+ return false;
+ }
+ // In addition to the above, interface methods must not be protected.
+ static constexpr uint32_t kForbidden = kAccProtected;
+ if ((method_access_flags_ & kForbidden) != 0) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods can't be protected";
+ return false;
+ }
+ }
+ // We also don't allow constructors to be abstract or native.
+ if (IsConstructor()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "constructors can't be abstract or native";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // This should have been rejected by the dex file verifier. Only do in debug build.
+ if (kIsDebugBuild) {
+ // When there's code, the method must not be native or abstract.
+ if ((method_access_flags_ & (kAccNative | kAccAbstract)) != 0) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "non-zero-length code in abstract or native method";
+ return false;
+ }
+
+ // Only the static initializer may have code in an interface.
+ if ((class_def_->GetJavaAccessFlags() & kAccInterface) != 0) {
+ // Interfaces may have static initializers for their fields.
+ if (!IsConstructor() || !IsStatic()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be abstract";
+ return false;
+ }
+ }
+
+ // Instance constructors must not be synchronized.
+ if (IsInstanceConstructor()) {
+ static constexpr uint32_t kForbidden = kAccSynchronized;
+ if ((method_access_flags_ & kForbidden) != 0) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "constructors can't be synchronized";
+ return false;
+ }
+ }
+ }
+
// Sanity-check the register counts. ins + locals = registers, so make sure that ins <= registers.
if (code_item_->ins_size_ > code_item_->registers_size_) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad register counts (ins=" << code_item_->ins_size_
@@ -2542,8 +2652,9 @@
ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_VIRTUAL, is_range, is_super);
const RegType* return_type = nullptr;
if (called_method != nullptr) {
- StackHandleScope<1> hs(self_);
- mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_,
+ pointer_size);
if (return_type_class != nullptr) {
return_type = &FromClass(called_method->GetReturnTypeDescriptor(),
return_type_class,
@@ -2584,8 +2695,9 @@
} else {
is_constructor = called_method->IsConstructor();
return_type_descriptor = called_method->GetReturnTypeDescriptor();
- StackHandleScope<1> hs(self_);
- mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_,
+ pointer_size);
if (return_type_class != nullptr) {
return_type = &FromClass(return_type_descriptor,
return_type_class,
@@ -4494,7 +4606,9 @@
const RegType& MethodVerifier::GetMethodReturnType() {
if (return_type_ == nullptr) {
if (mirror_method_ != nullptr) {
- mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_);
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_,
+ pointer_size);
if (return_type_class != nullptr) {
return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(),
return_type_class,
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 0295ef8..ba694b7 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -263,20 +263,6 @@
ArtField* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line)
SHARED_REQUIRES(Locks::mutator_lock_);
- // Is the method being verified a constructor?
- bool IsConstructor() const {
- return (method_access_flags_ & kAccConstructor) != 0;
- }
-
- // Is the method verified static?
- bool IsStatic() const {
- return (method_access_flags_ & kAccStatic) != 0;
- }
-
- bool IsInstanceConstructor() const {
- return IsConstructor() && !IsStatic();
- }
-
SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() {
return string_init_pc_reg_map_;
}
@@ -285,7 +271,21 @@
return encountered_failure_types_;
}
+ bool IsInstanceConstructor() const {
+ return IsConstructor() && !IsStatic();
+ }
+
private:
+ // Is the method being verified a constructor? See the comment on the field.
+ bool IsConstructor() const {
+ return is_constructor_;
+ }
+
+ // Is the method verified static?
+ bool IsStatic() const {
+ return (method_access_flags_ & kAccStatic) != 0;
+ }
+
// Private constructor for dumping.
MethodVerifier(Thread* self, const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader, const DexFile::ClassDef* class_def,
@@ -781,6 +781,13 @@
// FindLocksAtDexPC, resulting in deadlocks.
const bool allow_thread_suspension_;
+ // Whether the method seems to be a constructor. Note that this field exists as we can't trust
+ // the flags in the dex file. Some older code does not mark methods named "<init>" and "<clinit>"
+ // correctly.
+ //
+ // Note: this flag is only valid once Verify() has started.
+ bool is_constructor_;
+
// Link, for the method verifier root linked list.
MethodVerifier* link_;
diff --git a/test/044-proxy/expected.txt b/test/044-proxy/expected.txt
index bcce019..f86948a 100644
--- a/test/044-proxy/expected.txt
+++ b/test/044-proxy/expected.txt
@@ -93,3 +93,4 @@
Got expected exception
Proxy narrowed invocation return type passed
5.8
+callback
diff --git a/test/044-proxy/native_proxy.cc b/test/044-proxy/native_proxy.cc
new file mode 100644
index 0000000..f168719
--- /dev/null
+++ b/test/044-proxy/native_proxy.cc
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+
+#include "base/logging.h"
+
+namespace art {
+
+extern "C" JNIEXPORT void JNICALL Java_NativeProxy_nativeCall(
+ JNIEnv* env, jclass clazz ATTRIBUTE_UNUSED, jobject inf_ref) {
+ jclass native_inf_class = env->FindClass("NativeInterface");
+ CHECK(native_inf_class != nullptr);
+ jmethodID mid = env->GetMethodID(native_inf_class, "callback", "()V");
+ CHECK(mid != nullptr);
+ env->CallVoidMethod(inf_ref, mid);
+}
+
+} // namespace art
diff --git a/test/044-proxy/src/Main.java b/test/044-proxy/src/Main.java
index 9580871..05e8e5b 100644
--- a/test/044-proxy/src/Main.java
+++ b/test/044-proxy/src/Main.java
@@ -28,5 +28,6 @@
WrappedThrow.main(null);
NarrowingTest.main(null);
FloatSelect.main(null);
+ NativeProxy.main(args);
}
}
diff --git a/test/044-proxy/src/NativeProxy.java b/test/044-proxy/src/NativeProxy.java
new file mode 100644
index 0000000..b425da8
--- /dev/null
+++ b/test/044-proxy/src/NativeProxy.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Test invoking a proxy method from native code.
+ */
+
+interface NativeInterface {
+ public void callback();
+}
+
+public class NativeProxy {
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+
+ try {
+ NativeInterface inf = (NativeInterface)Proxy.newProxyInstance(
+ NativeProxy.class.getClassLoader(),
+ new Class[] { NativeInterface.class },
+ new NativeInvocationHandler());
+
+ nativeCall(inf);
+ } catch (Exception exc) {
+ throw new RuntimeException(exc);
+ }
+ }
+
+ public static class NativeInvocationHandler implements InvocationHandler {
+ public Object invoke(final Object proxy,
+ final Method method,
+ final Object[] args) throws Throwable {
+ System.out.println(method.getName());
+ return null;
+ }
+ }
+
+ public static native void nativeCall(NativeInterface inf);
+}
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index 08ccf0e..5913c40 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -78,6 +78,14 @@
test_Memory_pokeShort();
test_Memory_pokeInt();
test_Memory_pokeLong();
+ test_Integer_numberOfTrailingZeros();
+ test_Long_numberOfTrailingZeros();
+ test_Integer_rotateRight();
+ test_Long_rotateRight();
+ test_Integer_rotateLeft();
+ test_Long_rotateLeft();
+ test_Integer_rotateRightLeft();
+ test_Long_rotateRightLeft();
}
/**
@@ -1360,4 +1368,136 @@
poke_long.invoke(null, address + 1, (long)0x2122232425262728L, false);
Assert.assertTrue(Arrays.equals(ru, b));
}
+
+ public static void test_Integer_numberOfTrailingZeros() {
+ Assert.assertEquals(Integer.numberOfTrailingZeros(0), Integer.SIZE);
+ for (int i = 0; i < Integer.SIZE; i++) {
+ Assert.assertEquals(
+ Integer.numberOfTrailingZeros(0x80000000 >> i),
+ Integer.SIZE - 1 - i);
+ Assert.assertEquals(
+ Integer.numberOfTrailingZeros((0x80000000 >> i) | 0x80000000),
+ Integer.SIZE - 1 - i);
+ Assert.assertEquals(Integer.numberOfTrailingZeros(1 << i), i);
+ }
+ }
+
+ public static void test_Long_numberOfTrailingZeros() {
+ Assert.assertEquals(Long.numberOfTrailingZeros(0), Long.SIZE);
+ for (int i = 0; i < Long.SIZE; i++) {
+ Assert.assertEquals(
+ Long.numberOfTrailingZeros(0x8000000000000000L >> i),
+ Long.SIZE - 1 - i);
+ Assert.assertEquals(
+ Long.numberOfTrailingZeros((0x8000000000000000L >> i) | 0x8000000000000000L),
+ Long.SIZE - 1 - i);
+ Assert.assertEquals(Long.numberOfTrailingZeros(1L << i), i);
+ }
+ }
+
+ public static void test_Integer_rotateRight() throws Exception {
+ Assert.assertEquals(Integer.rotateRight(0x11, 0), 0x11);
+
+ Assert.assertEquals(Integer.rotateRight(0x11, 1), 0x80000008);
+ Assert.assertEquals(Integer.rotateRight(0x11, Integer.SIZE - 1), 0x22);
+ Assert.assertEquals(Integer.rotateRight(0x11, Integer.SIZE), 0x11);
+ Assert.assertEquals(Integer.rotateRight(0x11, Integer.SIZE + 1), 0x80000008);
+
+ Assert.assertEquals(Integer.rotateRight(0x11, -1), 0x22);
+ Assert.assertEquals(Integer.rotateRight(0x11, -(Integer.SIZE - 1)), 0x80000008);
+ Assert.assertEquals(Integer.rotateRight(0x11, -Integer.SIZE), 0x11);
+ Assert.assertEquals(Integer.rotateRight(0x11, -(Integer.SIZE + 1)), 0x22);
+
+ Assert.assertEquals(Integer.rotateRight(0x80000000, 1), 0x40000000);
+
+ for (int i = 0; i < Integer.SIZE; i++) {
+ Assert.assertEquals(
+ Integer.rotateRight(0xBBAAAADD, i),
+ (0xBBAAAADD >>> i) | (0xBBAAAADD << (Integer.SIZE - i)));
+ }
+ }
+
+ public static void test_Long_rotateRight() throws Exception {
+ Assert.assertEquals(Long.rotateRight(0x11, 0), 0x11);
+
+ Assert.assertEquals(Long.rotateRight(0x11, 1), 0x8000000000000008L);
+ Assert.assertEquals(Long.rotateRight(0x11, Long.SIZE - 1), 0x22);
+ Assert.assertEquals(Long.rotateRight(0x11, Long.SIZE), 0x11);
+ Assert.assertEquals(Long.rotateRight(0x11, Long.SIZE + 1), 0x8000000000000008L);
+
+ Assert.assertEquals(Long.rotateRight(0x11, -1), 0x22);
+ Assert.assertEquals(Long.rotateRight(0x11, -(Long.SIZE - 1)), 0x8000000000000008L);
+ Assert.assertEquals(Long.rotateRight(0x11, -Long.SIZE), 0x11);
+ Assert.assertEquals(Long.rotateRight(0x11, -(Long.SIZE + 1)), 0x22);
+
+ Assert.assertEquals(Long.rotateRight(0x8000000000000000L, 1), 0x4000000000000000L);
+
+ for (int i = 0; i < Long.SIZE; i++) {
+ Assert.assertEquals(
+ Long.rotateRight(0xBBAAAADDFF0000DDL, i),
+ (0xBBAAAADDFF0000DDL >>> i) | (0xBBAAAADDFF0000DDL << (Long.SIZE - i)));
+ }
+ }
+
+ public static void test_Integer_rotateLeft() throws Exception {
+ Assert.assertEquals(Integer.rotateLeft(0x11, 0), 0x11);
+
+ Assert.assertEquals(Integer.rotateLeft(0x11, 1), 0x22);
+ Assert.assertEquals(Integer.rotateLeft(0x11, Integer.SIZE - 1), 0x80000008);
+ Assert.assertEquals(Integer.rotateLeft(0x11, Integer.SIZE), 0x11);
+ Assert.assertEquals(Integer.rotateLeft(0x11, Integer.SIZE + 1), 0x22);
+
+ Assert.assertEquals(Integer.rotateLeft(0x11, -1), 0x80000008);
+ Assert.assertEquals(Integer.rotateLeft(0x11, -(Integer.SIZE - 1)), 0x22);
+ Assert.assertEquals(Integer.rotateLeft(0x11, -Integer.SIZE), 0x11);
+ Assert.assertEquals(Integer.rotateLeft(0x11, -(Integer.SIZE + 1)), 0x80000008);
+
+ Assert.assertEquals(Integer.rotateLeft(0xC0000000, 1), 0x80000001);
+
+ for (int i = 0; i < Integer.SIZE; i++) {
+ Assert.assertEquals(
+ Integer.rotateLeft(0xBBAAAADD, i),
+ (0xBBAAAADD << i) | (0xBBAAAADD >>> (Integer.SIZE - i)));
+ }
+ }
+
+ public static void test_Long_rotateLeft() throws Exception {
+ Assert.assertEquals(Long.rotateLeft(0x11, 0), 0x11);
+
+ Assert.assertEquals(Long.rotateLeft(0x11, 1), 0x22);
+ Assert.assertEquals(Long.rotateLeft(0x11, Long.SIZE - 1), 0x8000000000000008L);
+ Assert.assertEquals(Long.rotateLeft(0x11, Long.SIZE), 0x11);
+ Assert.assertEquals(Long.rotateLeft(0x11, Long.SIZE + 1), 0x22);
+
+ Assert.assertEquals(Long.rotateLeft(0x11, -1), 0x8000000000000008L);
+ Assert.assertEquals(Long.rotateLeft(0x11, -(Long.SIZE - 1)), 0x22);
+ Assert.assertEquals(Long.rotateLeft(0x11, -Long.SIZE), 0x11);
+ Assert.assertEquals(Long.rotateLeft(0x11, -(Long.SIZE + 1)), 0x8000000000000008L);
+
+ Assert.assertEquals(Long.rotateLeft(0xC000000000000000L, 1), 0x8000000000000001L);
+
+ for (int i = 0; i < Long.SIZE; i++) {
+ Assert.assertEquals(
+ Long.rotateLeft(0xBBAAAADDFF0000DDL, i),
+ (0xBBAAAADDFF0000DDL << i) | (0xBBAAAADDFF0000DDL >>> (Long.SIZE - i)));
+ }
+ }
+
+ public static void test_Integer_rotateRightLeft() throws Exception {
+ for (int i = 0; i < Integer.SIZE * 2; i++) {
+ Assert.assertEquals(Integer.rotateLeft(0xBBAAAADD, i),
+ Integer.rotateRight(0xBBAAAADD, -i));
+ Assert.assertEquals(Integer.rotateLeft(0xBBAAAADD, -i),
+ Integer.rotateRight(0xBBAAAADD, i));
+ }
+ }
+
+ public static void test_Long_rotateRightLeft() throws Exception {
+ for (int i = 0; i < Long.SIZE * 2; i++) {
+ Assert.assertEquals(Long.rotateLeft(0xBBAAAADDFF0000DDL, i),
+ Long.rotateRight(0xBBAAAADDFF0000DDL, -i));
+ Assert.assertEquals(Long.rotateLeft(0xBBAAAADDFF0000DDL, -i),
+ Long.rotateRight(0xBBAAAADDFF0000DDL, i));
+ }
+ }
}
diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc
index 9b32fc3..e3e0091 100644
--- a/test/466-get-live-vreg/get_live_vreg_jni.cc
+++ b/test/466-get-live-vreg/get_live_vreg_jni.cc
@@ -42,7 +42,7 @@
} else if (m_name.compare("testIntervalHole") == 0) {
found_method_ = true;
uint32_t value = 0;
- if (m->IsOptimized(sizeof(void*))) {
+ if (GetCurrentQuickFrame() != nullptr && m->IsOptimized(sizeof(void*))) {
CHECK_EQ(GetVReg(m, 0, kIntVReg, &value), false);
} else {
CHECK(GetVReg(m, 0, kIntVReg, &value));
diff --git a/test/497-inlining-and-class-loader/clear_dex_cache.cc b/test/497-inlining-and-class-loader/clear_dex_cache.cc
index f9b33a2..50d1a63 100644
--- a/test/497-inlining-and-class-loader/clear_dex_cache.cc
+++ b/test/497-inlining-and-class-loader/clear_dex_cache.cc
@@ -24,20 +24,45 @@
namespace {
-extern "C" JNIEXPORT jobject JNICALL Java_Main_cloneResolvedMethods(JNIEnv*, jclass, jclass cls) {
+extern "C" JNIEXPORT jobject JNICALL Java_Main_cloneResolvedMethods(JNIEnv* env,
+ jclass,
+ jclass cls) {
ScopedObjectAccess soa(Thread::Current());
- return soa.Vm()->AddGlobalRef(
- soa.Self(),
- soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetResolvedMethods()->Clone(soa.Self()));
+ mirror::DexCache* dex_cache = soa.Decode<mirror::Class*>(cls)->GetDexCache();
+ size_t num_methods = dex_cache->NumResolvedMethods();
+ ArtMethod** methods = dex_cache->GetResolvedMethods();
+ CHECK_EQ(num_methods != 0u, methods != nullptr);
+ if (num_methods == 0u) {
+ return nullptr;
+ }
+ jarray array;
+ if (sizeof(void*) == 4) {
+ array = env->NewIntArray(num_methods);
+ } else {
+ array = env->NewLongArray(num_methods);
+ }
+ CHECK(array != nullptr);
+ mirror::PointerArray* pointer_array = soa.Decode<mirror::PointerArray*>(array);
+ for (size_t i = 0; i != num_methods; ++i) {
+ ArtMethod* method = mirror::DexCache::GetElementPtrSize(methods, i, sizeof(void*));
+ pointer_array->SetElementPtrSize(i, method, sizeof(void*));
+ }
+ return array;
}
extern "C" JNIEXPORT void JNICALL Java_Main_restoreResolvedMethods(
JNIEnv*, jclass, jclass cls, jobject old_cache) {
ScopedObjectAccess soa(Thread::Current());
- mirror::PointerArray* now = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetResolvedMethods();
+ mirror::DexCache* dex_cache = soa.Decode<mirror::Class*>(cls)->GetDexCache();
+ size_t num_methods = dex_cache->NumResolvedMethods();
+ ArtMethod** methods = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetResolvedMethods();
+ CHECK_EQ(num_methods != 0u, methods != nullptr);
mirror::PointerArray* old = soa.Decode<mirror::PointerArray*>(old_cache);
- for (size_t i = 0, e = old->GetLength(); i < e; ++i) {
- now->SetElementPtrSize(i, old->GetElementPtrSize<void*>(i, sizeof(void*)), sizeof(void*));
+ CHECK_EQ(methods != nullptr, old != nullptr);
+ CHECK_EQ(num_methods, static_cast<size_t>(old->GetLength()));
+ for (size_t i = 0; i != num_methods; ++i) {
+ ArtMethod* method = old->GetElementPtrSize<ArtMethod*>(i, sizeof(void*));
+ mirror::DexCache::SetElementPtrSize(methods, i, method, sizeof(void*));
}
}
diff --git a/test/800-smali/smali/b_18380491AbstractBase.smali b/test/800-smali/smali/b_18380491AbstractBase.smali
index 7aa1b1a..cc05221 100644
--- a/test/800-smali/smali/b_18380491AbstractBase.smali
+++ b/test/800-smali/smali/b_18380491AbstractBase.smali
@@ -1,4 +1,4 @@
-.class public LB18380491ActractBase;
+.class public abstract LB18380491AbstractBase;
.super Ljava/lang/Object;
diff --git a/test/800-smali/smali/b_18380491ConcreteClass.smali b/test/800-smali/smali/b_18380491ConcreteClass.smali
index db5ef3b..1ba684f 100644
--- a/test/800-smali/smali/b_18380491ConcreteClass.smali
+++ b/test/800-smali/smali/b_18380491ConcreteClass.smali
@@ -1,10 +1,10 @@
.class public LB18380491ConcreteClass;
-.super LB18380491ActractBase;
+.super LB18380491AbstractBase;
.method public constructor <init>()V
.locals 0
- invoke-direct {p0}, LB18380491ActractBase;-><init>()V
+ invoke-direct {p0}, LB18380491AbstractBase;-><init>()V
return-void
.end method
@@ -13,7 +13,7 @@
if-eqz p1, :invoke_super_abstract
return p1
:invoke_super_abstract
- invoke-super {p0, p1}, LB18380491ActractBase;->foo(I)I
+ invoke-super {p0, p1}, LB18380491AbstractBase;->foo(I)I
move-result v0
return v0
.end method
diff --git a/test/998-scoped-primitive-array/check b/test/998-scoped-primitive-array/check
new file mode 100755
index 0000000..842bdc6
--- /dev/null
+++ b/test/998-scoped-primitive-array/check
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Check that the string "error" isn't present
+if grep error "$2"; then
+ exit 1
+else
+ exit 0
+fi
diff --git a/test/998-scoped-primitive-array/expected.txt b/test/998-scoped-primitive-array/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/998-scoped-primitive-array/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/998-scoped-primitive-array/info.txt b/test/998-scoped-primitive-array/info.txt
new file mode 100644
index 0000000..93abb7c
--- /dev/null
+++ b/test/998-scoped-primitive-array/info.txt
@@ -0,0 +1 @@
+Tests for measuring performance of ScopedPrimitiveArray.
diff --git a/test/998-scoped-primitive-array/scoped_primitive_array.cc b/test/998-scoped-primitive-array/scoped_primitive_array.cc
new file mode 100644
index 0000000..c224a06
--- /dev/null
+++ b/test/998-scoped-primitive-array/scoped_primitive_array.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include "ScopedPrimitiveArray.h"
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_measureByteArray(JNIEnv* env,
+ jclass,
+ jlong reps,
+ jbyteArray arr) {
+ jlong ret = 0;
+ for (jlong i = 0; i < reps; ++i) {
+ ScopedByteArrayRO sc(env, arr);
+ ret += sc[0] + sc[sc.size() - 1];
+ }
+ return ret;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_measureShortArray(JNIEnv* env,
+ jclass,
+ jlong reps,
+ jshortArray arr) {
+ jlong ret = 0;
+ for (jlong i = 0; i < reps; ++i) {
+ ScopedShortArrayRO sc(env, arr);
+ ret += sc[0] + sc[sc.size() - 1];
+ }
+ return ret;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_measureIntArray(JNIEnv* env,
+ jclass,
+ jlong reps,
+ jintArray arr) {
+ jlong ret = 0;
+ for (jlong i = 0; i < reps; ++i) {
+ ScopedIntArrayRO sc(env, arr);
+ ret += sc[0] + sc[sc.size() - 1];
+ }
+ return ret;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_measureLongArray(JNIEnv* env,
+ jclass,
+ jlong reps,
+ jlongArray arr) {
+ jlong ret = 0;
+ for (jlong i = 0; i < reps; ++i) {
+ ScopedLongArrayRO sc(env, arr);
+ ret += sc[0] + sc[sc.size() - 1];
+ }
+ return ret;
+}
diff --git a/test/998-scoped-primitive-array/src/Main.java b/test/998-scoped-primitive-array/src/Main.java
new file mode 100644
index 0000000..630e0dc
--- /dev/null
+++ b/test/998-scoped-primitive-array/src/Main.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public Main() {}
+
+ // Measure adds the first and last element of the array by using ScopedPrimitiveArray.
+ static native long measureByteArray(long reps, byte[] arr);
+ static native long measureShortArray(long reps, short[] arr);
+ static native long measureIntArray(long reps, int[] arr);
+ static native long measureLongArray(long reps, long[] arr);
+
+ static void checkEq(long expected, long value) {
+ if (expected != value) {
+ System.out.println("error: Expected " + expected + " but got " + value);
+ }
+ }
+
+ static void runPerfTest(long reps) {
+ for (int length = 1; length <= 8192; length *= 8) {
+ byte[] bytes = new byte[length];
+ bytes[0] = 1;
+ bytes[length - 1] = 2;
+ short[] shorts = new short[length];
+ shorts[0] = 1;
+ shorts[length - 1] = 2;
+ int[] ints = new int[length];
+ ints[0] = 1;
+ ints[length - 1] = 2;
+ long[] longs = new long[length];
+ longs[0] = 1;
+ longs[length - 1] = 2;
+ long value = 0;
+ long elapsed = 0;
+ long start = 0;
+
+ start = System.nanoTime();
+ value = measureByteArray(reps, bytes);
+ elapsed = System.nanoTime() - start;
+ System.out.println("Byte length=" + length + " ns/op=" + (double) elapsed / reps);
+ checkEq(value, reps * (long) (bytes[0] + bytes[length - 1]));
+
+ start = System.nanoTime();
+ value = measureShortArray(reps, shorts);
+ elapsed = System.nanoTime() - start;
+ System.out.println("Short length=" + length + " ns/op=" + (double) elapsed / reps);
+ checkEq(value, reps * (long) (shorts[0] + shorts[length - 1]));
+
+ start = System.nanoTime();
+ value = measureIntArray(reps, ints);
+ elapsed = System.nanoTime() - start;
+ System.out.println("Int length=" + length + " ns/op=" + (double) elapsed / reps);
+ checkEq(value, reps * (ints[0] + ints[length - 1]));
+
+ start = System.nanoTime();
+ value = measureLongArray(reps, longs);
+ elapsed = System.nanoTime() - start;
+ System.out.println("Long length=" + length + " ns/op=" + (double) elapsed / reps);
+ checkEq(value, reps * (longs[0] + longs[length - 1]));
+ }
+ }
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ long iterations = 2000000;
+ if (args.length > 1) {
+ iterations = Long.parseLong(args[1], 10);
+ }
+ runPerfTest(iterations);
+ System.out.println("Done");
+ }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index c3d1576..af945fb 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -24,6 +24,7 @@
004-ReferenceMap/stack_walk_refmap_jni.cc \
004-StackWalk/stack_walk_jni.cc \
004-UnsafeTest/unsafe_test.cc \
+ 044-proxy/native_proxy.cc \
051-thread/thread_test.cc \
088-monitor-verification/stack_inspect.cc \
116-nodex2oat/nodex2oat.cc \
@@ -38,6 +39,7 @@
461-get-reference-vreg/get_reference_vreg_jni.cc \
466-get-live-vreg/get_live_vreg_jni.cc \
497-inlining-and-class-loader/clear_dex_cache.cc \
+ 998-scoped-primitive-array/scoped_primitive_array.cc \
999-jni-perf/perf-jni.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
@@ -72,7 +74,7 @@
LOCAL_MODULE_TAGS := tests
endif
LOCAL_SRC_FILES := $(LIBARTTEST_COMMON_SRC_FILES)
- LOCAL_SHARED_LIBRARIES += libart$$(suffix) libbacktrace
+ LOCAL_SHARED_LIBRARIES += libart$$(suffix) libbacktrace libnativehelper
LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libarttest.mk
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index ad3fb41..efc0bfb 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -39,7 +39,11 @@
SECONDARY_DEX=""
TIME_OUT="gdb" # "n" (disabled), "timeout" (use timeout), "gdb" (use gdb)
# Value in seconds
-TIME_OUT_VALUE=600 # 10 minutes.
+if [ "$ART_USE_READ_BARRIER" = "true" ]; then
+ TIME_OUT_VALUE=900 # 15 minutes.
+else
+ TIME_OUT_VALUE=600 # 10 minutes.
+fi
USE_GDB="n"
USE_JVM="n"
VERIFY="y" # y=yes,n=no,s=softfail
diff --git a/test/run-test b/test/run-test
index 424c2e4..73c92d4 100755
--- a/test/run-test
+++ b/test/run-test
@@ -799,11 +799,11 @@
echo '#################### info'
cat "${td_info}" | sed 's/^/# /g'
echo '#################### diffs'
- diff --strip-trailing-cr -u "$expected" "$output" | tail -n 2000
+ diff --strip-trailing-cr -u "$expected" "$output" | tail -n 3000
echo '####################'
if [ "$strace" = "yes" ]; then
echo '#################### strace output'
- tail -n 2000 "$tmp_dir/$strace_output"
+ tail -n 3000 "$tmp_dir/$strace_output"
echo '####################'
fi
echo ' '
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index 116a611..104cba7 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -40,6 +40,9 @@
vm_command="--vm-command=$art"
image_compiler_option=""
debug="no"
+verbose="no"
+# By default, we run the whole JDWP test suite.
+test="org.apache.harmony.jpda.tests.share.AllTests"
while true; do
if [[ "$1" == "--mode=host" ]]; then
@@ -65,6 +68,19 @@
# Remove the --debug from the arguments.
args=${args/$1}
shift
+ elif [[ $1 == "--verbose" ]]; then
+ verbose="yes"
+ # Remove the --verbose from the arguments.
+ args=${args/$1}
+ shift
+ elif [[ $1 == "--test" ]]; then
+ # Remove the --test from the arguments.
+ args=${args/$1}
+ shift
+ test=$1
+ # Remove the test from the arguments.
+ args=${args/$1}
+ shift
elif [[ "$1" == "" ]]; then
break
else
@@ -78,6 +94,10 @@
art_debugee="$art_debugee -d"
vm_args="$vm_args --vm-arg -XXlib:libartd.so"
fi
+if [[ $verbose == "yes" ]]; then
+ # Enable JDWP logs in the debuggee.
+ art_debugee="$art_debugee -verbose:jdwp"
+fi
# Run the tests using vogar.
vogar $vm_command \
@@ -93,4 +113,4 @@
--vm-arg -Djpda.settings.debuggeeJavaPath="\"$art_debugee $image $debuggee_args\"" \
--classpath $test_jar \
--vm-arg -Xcompiler-option --vm-arg --debuggable \
- org.apache.harmony.jpda.tests.share.AllTests
+ $test
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index 8466bb3..7faf86e 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -30,6 +30,3 @@
echo -e "${green}List properties${nc}"
adb shell getprop
-
-echo -e "${green}Stopping framework${nc}"
-adb shell stop