Merge "Revert "Revert "ART: Key-Value Store in Oat header"""
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..341df78
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,55 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# Switching to jemalloc requires deleting these files.
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libart_*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libartd_*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libart_*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libartd_*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/runtime/compiled_class.h b/compiler/compiled_class.h
similarity index 88%
rename from runtime/compiled_class.h
rename to compiler/compiled_class.h
index c53d500..b88d613 100644
--- a/runtime/compiled_class.h
+++ b/compiler/compiled_class.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_COMPILED_CLASS_H_
-#define ART_RUNTIME_COMPILED_CLASS_H_
+#ifndef ART_COMPILER_COMPILED_CLASS_H_
+#define ART_COMPILER_COMPILED_CLASS_H_
#include "mirror/class.h"
@@ -34,4 +34,4 @@
} // namespace art
-#endif // ART_RUNTIME_COMPILED_CLASS_H_
+#endif // ART_COMPILER_COMPILED_CLASS_H_
diff --git a/compiler/compilers.cc b/compiler/compilers.cc
index f940b54..bac1f12 100644
--- a/compiler/compilers.cc
+++ b/compiler/compilers.cc
@@ -15,6 +15,7 @@
*/
#include "compilers.h"
+
#include "dex/mir_graph.h"
#include "dex/quick/mir_to_lir.h"
#include "elf_writer_quick.h"
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index 799a742..bdedadb 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -440,19 +440,23 @@
/**
* @brief Memory barrier types (see "The JSR-133 Cookbook for Compiler Writers").
- * @details Without context sensitive analysis, the most conservative set of barriers
- * must be issued to ensure the Java Memory Model. Thus the recipe is as follows:
- * -# Use StoreStore barrier before volatile store.
- * -# Use StoreLoad barrier after volatile store.
- * -# Use LoadLoad and LoadStore barrier after each volatile load.
+ * @details We define the combined barrier types that are actually required
+ * by the Java Memory Model, rather than using exactly the terminology from
+ * the JSR-133 cookbook. These should, in many cases, be replaced by acquire/release
+ * primitives. Note that the JSR-133 cookbook generally does not deal with
+ * store atomicity issues, and the recipes there are not always entirely sufficient.
+ * The current recipe is as follows:
+ * -# Use AnyStore ~= (LoadStore | StoreStore) ~= release barrier before volatile store.
+ * -# Use AnyAny barrier after volatile store. (StoreLoad is as expensive.)
+ * -# Use LoadAny barrier ~= (LoadLoad | LoadStore) ~= acquire barrierafter each volatile load.
* -# Use StoreStore barrier after all stores but before return from any constructor whose
- * class has final fields.
+ * class has final fields.
*/
enum MemBarrierKind {
- kLoadStore,
- kLoadLoad,
+ kAnyStore,
+ kLoadAny,
kStoreStore,
- kStoreLoad
+ kAnyAny
};
std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind);
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 711743d..d097500 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -96,7 +96,6 @@
~0U,
// 2 = kArm64. TODO(Arm64): enable optimizations once backend is mature enough.
(1 << kLoadStoreElimination) |
- (1 << kLoadHoisting) |
0,
// 3 = kThumb2.
0,
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 04d6898..01e17bf 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -218,7 +218,7 @@
LIR* success_target = NewLIR0(kPseudoTargetLabel);
lock_success_branch->target = success_target;
- GenMemBarrier(kLoadLoad);
+ GenMemBarrier(kLoadAny);
} else {
// Explicit null-check as slow-path is entered using an IT.
GenNullCheck(rs_r0, opt_flags);
@@ -240,7 +240,7 @@
LIR* call_inst = OpReg(kOpBlx/*ne*/, rs_rARM_LR);
OpEndIT(it);
MarkSafepointPC(call_inst);
- GenMemBarrier(kLoadLoad);
+ GenMemBarrier(kLoadAny);
}
}
@@ -269,7 +269,7 @@
MarkPossibleNullPointerException(opt_flags);
LoadConstantNoClobber(rs_r3, 0);
LIR* slow_unlock_branch = OpCmpBranch(kCondNe, rs_r1, rs_r2, NULL);
- GenMemBarrier(kStoreLoad);
+ GenMemBarrier(kAnyStore);
Store32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3);
LIR* unlock_success_branch = OpUnconditionalBranch(NULL);
@@ -298,7 +298,7 @@
OpRegReg(kOpCmp, rs_r1, rs_r2);
LIR* it = OpIT(kCondEq, "EE");
- if (GenMemBarrier(kStoreLoad)) {
+ if (GenMemBarrier(kAnyStore)) {
UpdateIT(it, "TEE");
}
Store32Disp/*eq*/(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3);
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 95071d9..f4ea592 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -764,6 +764,7 @@
UNIMPLEMENTED(FATAL) << "Should not be called.";
}
+// Generate a CAS with memory_order_seq_cst semantics.
bool ArmMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
DCHECK_EQ(cu_->instruction_set, kThumb2);
// Unused - RegLocation rl_src_unsafe = info->args[0];
@@ -818,8 +819,8 @@
}
}
- // Release store semantics, get the barrier out of the way. TODO: revisit
- GenMemBarrier(kStoreLoad);
+ // Prevent reordering with prior memory operations.
+ GenMemBarrier(kAnyStore);
RegLocation rl_object = LoadValue(rl_src_obj, kRefReg);
RegLocation rl_new_value;
@@ -908,6 +909,9 @@
FreeTemp(rl_expected.reg); // Now unneeded.
}
+ // Prevent reordering with subsequent memory operations.
+ GenMemBarrier(kLoadAny);
+
// result := (tmp1 != 0) ? 0 : 1;
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
OpRegRegImm(kOpRsub, rl_result.reg, r_tmp, 1);
@@ -987,10 +991,10 @@
int dmb_flavor;
// TODO: revisit Arm barrier kinds
switch (barrier_kind) {
- case kLoadStore: dmb_flavor = kISH; break;
- case kLoadLoad: dmb_flavor = kISH; break;
+ case kAnyStore: dmb_flavor = kISH; break;
+ case kLoadAny: dmb_flavor = kISH; break;
case kStoreStore: dmb_flavor = kISHST; break;
- case kStoreLoad: dmb_flavor = kISH; break;
+ case kAnyAny: dmb_flavor = kISH; break;
default:
LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind;
dmb_flavor = kSY; // quiet gcc.
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
index 2d5e291..9cbf7b8 100644
--- a/compiler/dex/quick/arm/utility_arm.cc
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -986,10 +986,7 @@
}
if (UNLIKELY(is_volatile == kVolatile)) {
- // Without context sensitive analysis, we must issue the most conservative barriers.
- // In this case, either a load or store may follow so we issue both barriers.
- GenMemBarrier(kLoadLoad);
- GenMemBarrier(kLoadStore);
+ GenMemBarrier(kLoadAny);
}
return load;
@@ -1091,8 +1088,8 @@
LIR* ArmMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src,
OpSize size, VolatileKind is_volatile) {
if (UNLIKELY(is_volatile == kVolatile)) {
- // There might have been a store before this volatile one so insert StoreStore barrier.
- GenMemBarrier(kStoreStore);
+ // Ensure that prior accesses become visible to other threads first.
+ GenMemBarrier(kAnyStore);
}
LIR* store;
@@ -1135,8 +1132,9 @@
}
if (UNLIKELY(is_volatile == kVolatile)) {
- // A load might follow the volatile store so insert a StoreLoad barrier.
- GenMemBarrier(kStoreLoad);
+ // Preserve order with respect to any subsequent volatile loads.
+ // We need StoreLoad, but that generally requires the most expensive barrier.
+ GenMemBarrier(kAnyAny);
}
return store;
diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h
index 7490646..ef4c0de 100644
--- a/compiler/dex/quick/arm64/arm64_lir.h
+++ b/compiler/dex/quick/arm64/arm64_lir.h
@@ -376,6 +376,7 @@
kST = 0xe,
kISH = 0xb,
kISHST = 0xa,
+ kISHLD = 0x9,
kNSH = 0x7,
kNSHST = 0x6
};
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 56dcbe5..d24f419 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -228,7 +228,7 @@
LIR* success_target = NewLIR0(kPseudoTargetLabel);
lock_success_branch->target = success_target;
- GenMemBarrier(kLoadLoad);
+ GenMemBarrier(kLoadAny);
}
/*
@@ -258,7 +258,7 @@
Load32Disp(rs_x0, mirror::Object::MonitorOffset().Int32Value(), rs_w2);
MarkPossibleNullPointerException(opt_flags);
LIR* slow_unlock_branch = OpCmpBranch(kCondNe, rs_w1, rs_w2, NULL);
- GenMemBarrier(kStoreLoad);
+ GenMemBarrier(kAnyStore);
Store32Disp(rs_x0, mirror::Object::MonitorOffset().Int32Value(), rs_wzr);
LIR* unlock_success_branch = OpUnconditionalBranch(NULL);
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index a7ca685..f7aa39f 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -740,10 +740,16 @@
int dmb_flavor;
// TODO: revisit Arm barrier kinds
switch (barrier_kind) {
- case kLoadStore: dmb_flavor = kISH; break;
- case kLoadLoad: dmb_flavor = kISH; break;
+ case kAnyStore: dmb_flavor = kISH; break;
+ case kLoadAny: dmb_flavor = kISH; break;
+ // We conjecture that kISHLD is insufficient. It is documented
+ // to provide LoadLoad | StoreStore ordering. But if this were used
+ // to implement volatile loads, we suspect that the lack of store
+ // atomicity on ARM would cause us to allow incorrect results for
+ // the canonical IRIW example. But we're not sure.
+ // We should be using acquire loads instead.
case kStoreStore: dmb_flavor = kISHST; break;
- case kStoreLoad: dmb_flavor = kISH; break;
+ case kAnyAny: dmb_flavor = kISH; break;
default:
LOG(FATAL) << "Unexpected MemBarrierKind: " << barrier_kind;
dmb_flavor = kSY; // quiet gcc.
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index ef9dbdd..be3cd8e 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -158,7 +158,6 @@
}
ResourceMask Arm64Mir2Lir::GetPCUseDefEncoding() const {
- LOG(FATAL) << "Unexpected call to GetPCUseDefEncoding for Arm64";
return kEncodeNone;
}
diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc
index 22a4ec4..097fcdc 100644
--- a/compiler/dex/quick/arm64/utility_arm64.cc
+++ b/compiler/dex/quick/arm64/utility_arm64.cc
@@ -1145,10 +1145,8 @@
LIR* load = LoadBaseDispBody(r_base, displacement, r_dest, size);
if (UNLIKELY(is_volatile == kVolatile)) {
- // Without context sensitive analysis, we must issue the most conservative barriers.
- // In this case, either a load or store may follow so we issue both barriers.
- GenMemBarrier(kLoadLoad);
- GenMemBarrier(kLoadStore);
+ // TODO: This should generate an acquire load instead of the barrier.
+ GenMemBarrier(kLoadAny);
}
return load;
@@ -1232,9 +1230,10 @@
LIR* Arm64Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src,
OpSize size, VolatileKind is_volatile) {
+ // TODO: This should generate a release store and no barriers.
if (UNLIKELY(is_volatile == kVolatile)) {
- // There might have been a store before this volatile one so insert StoreStore barrier.
- GenMemBarrier(kStoreStore);
+ // Ensure that prior accesses become visible to other threads first.
+ GenMemBarrier(kAnyStore);
}
// StoreBaseDisp() will emit correct insn for atomic store on arm64
@@ -1243,8 +1242,9 @@
LIR* store = StoreBaseDispBody(r_base, displacement, r_src, size);
if (UNLIKELY(is_volatile == kVolatile)) {
- // A load might follow the volatile store so insert a StoreLoad barrier.
- GenMemBarrier(kStoreLoad);
+ // Preserve order with respect to any subsequent volatile loads.
+ // We need StoreLoad, but that generally requires the most expensive barrier.
+ GenMemBarrier(kAnyAny);
}
return store;
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 6dc019a..c266a3c 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -629,8 +629,12 @@
field_info.StorageIndex(), r_base));
FreeTemp(r_tmp);
- // Ensure load of status and load of value don't re-order.
- GenMemBarrier(kLoadLoad);
+ // Ensure load of status and store of value don't re-order.
+ // TODO: Presumably the actual value store is control-dependent on the status load,
+ // and will thus not be reordered in any case, since stores are never speculated.
+ // Does later code "know" that the class is now initialized? If so, we still
+ // need the barrier to guard later static loads.
+ GenMemBarrier(kLoadAny);
}
FreeTemp(r_method);
}
@@ -723,7 +727,7 @@
FreeTemp(r_tmp);
// Ensure load of status and load of value don't re-order.
- GenMemBarrier(kLoadLoad);
+ GenMemBarrier(kLoadAny);
}
FreeTemp(r_method);
}
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 6c0dfe8..3a30430 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -22,6 +22,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "invoke_type.h"
#include "mirror/array.h"
+#include "mirror/class-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/string.h"
#include "mir_to_lir-inl.h"
@@ -666,25 +667,23 @@
}
case 1: // Is "this" null? [use kArg1]
cg->GenNullCheck(cg->TargetRefReg(kArg1), info->opt_flags);
- // get this->klass_ [use kArg1, set kInvokeTgt]
+ // get this->klass_ [use kArg1, set kArg0]
cg->LoadRefDisp(cg->TargetRefReg(kArg1), mirror::Object::ClassOffset().Int32Value(),
- cg->TargetPtrReg(kInvokeTgt),
+ cg->TargetRefReg(kArg0),
kNotVolatile);
cg->MarkPossibleNullPointerException(info->opt_flags);
break;
- case 2: // Get this->klass_->vtable [usr kInvokeTgt, set kInvokeTgt]
- cg->LoadRefDisp(cg->TargetPtrReg(kInvokeTgt), mirror::Class::VTableOffset().Int32Value(),
- cg->TargetPtrReg(kInvokeTgt),
- kNotVolatile);
+ case 2: {
+ // Get this->klass_.embedded_vtable[method_idx] [usr kArg0, set kArg0]
+ int32_t offset = mirror::Class::EmbeddedVTableOffset().Uint32Value() +
+ method_idx * sizeof(mirror::Class::VTableEntry);
+ // Load target method from embedded vtable to kArg0 [use kArg0, set kArg0]
+ cg->LoadRefDisp(cg->TargetRefReg(kArg0), offset, cg->TargetRefReg(kArg0), kNotVolatile);
break;
- case 3: // Get target method [use kInvokeTgt, set kArg0]
- cg->LoadRefDisp(cg->TargetPtrReg(kInvokeTgt),
- ObjArray::OffsetOfElement(method_idx).Int32Value(),
- cg->TargetRefReg(kArg0),
- kNotVolatile);
- break;
- case 4: // Get the compiled code address [uses kArg0, sets kInvokeTgt]
+ }
+ case 3:
if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
+ // Get the compiled code address [use kArg0, set kInvokeTgt]
cg->LoadWordDisp(cg->TargetRefReg(kArg0),
mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(),
cg->TargetPtrReg(kInvokeTgt));
@@ -724,27 +723,24 @@
}
case 2: // Is "this" null? [use kArg1]
cg->GenNullCheck(cg->TargetRefReg(kArg1), info->opt_flags);
- // Get this->klass_ [use kArg1, set kInvokeTgt]
+ // Get this->klass_ [use kArg1, set kArg0]
cg->LoadRefDisp(cg->TargetRefReg(kArg1), mirror::Object::ClassOffset().Int32Value(),
- cg->TargetPtrReg(kInvokeTgt),
+ cg->TargetRefReg(kArg0),
kNotVolatile);
cg->MarkPossibleNullPointerException(info->opt_flags);
break;
- case 3: // Get this->klass_->imtable [use kInvokeTgt, set kInvokeTgt]
- // NOTE: native pointer.
- cg->LoadRefDisp(cg->TargetPtrReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(),
- cg->TargetPtrReg(kInvokeTgt),
+ case 3: { // Get target method [use kInvokeTgt, set kArg0]
+ int32_t offset = mirror::Class::EmbeddedImTableOffset().Uint32Value() +
+ (method_idx % mirror::Class::kImtSize) * sizeof(mirror::Class::ImTableEntry);
+ // Load target method from embedded imtable to kArg0 [use kArg0, set kArg0]
+ cg->LoadRefDisp(cg->TargetRefReg(kArg0), offset,
+ cg->TargetRefReg(kArg0),
kNotVolatile);
break;
- case 4: // Get target method [use kInvokeTgt, set kArg0]
- // NOTE: native pointer.
- cg->LoadRefDisp(cg->TargetPtrReg(kInvokeTgt),
- ObjArray::OffsetOfElement(method_idx % ClassLinker::kImtSize).Int32Value(),
- cg->TargetRefReg(kArg0),
- kNotVolatile);
- break;
- case 5: // Get the compiled code address [use kArg0, set kInvokeTgt]
+ }
+ case 4:
if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
+ // Get the compiled code address [use kArg0, set kInvokeTgt]
cg->LoadWordDisp(cg->TargetRefReg(kArg0),
mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(),
cg->TargetPtrReg(kInvokeTgt));
@@ -1711,10 +1707,7 @@
}
if (is_volatile) {
- // Without context sensitive analysis, we must issue the most conservative barriers.
- // In this case, either a load or store may follow so we issue both barriers.
- GenMemBarrier(kLoadLoad);
- GenMemBarrier(kLoadStore);
+ GenMemBarrier(kLoadAny);
}
if (is_long) {
@@ -1737,8 +1730,7 @@
rl_src_offset = NarrowRegLoc(rl_src_offset); // ignore high half in info->args[3]
RegLocation rl_src_value = info->args[4]; // value to store
if (is_volatile || is_ordered) {
- // There might have been a store before this volatile one so insert StoreStore barrier.
- GenMemBarrier(kStoreStore);
+ GenMemBarrier(kAnyStore);
}
RegLocation rl_object = LoadValue(rl_src_obj, kRefReg);
RegLocation rl_offset = LoadValue(rl_src_offset, kCoreReg);
@@ -1767,8 +1759,9 @@
FreeTemp(rl_offset.reg);
if (is_volatile) {
- // A load might follow the volatile store so insert a StoreLoad barrier.
- GenMemBarrier(kStoreLoad);
+ // Prevent reordering with a subsequent volatile load.
+ // May also be needed to address store atomicity issues.
+ GenMemBarrier(kAnyAny);
}
if (is_object) {
MarkGCCard(rl_value.reg, rl_object.reg);
diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc
index b97ff2a..2893157 100644
--- a/compiler/dex/quick/local_optimizations.cc
+++ b/compiler/dex/quick/local_optimizations.cc
@@ -121,20 +121,22 @@
}
ResourceMask stop_def_reg_mask = this_lir->u.m.def_mask->Without(kEncodeMem);
- ResourceMask stop_use_reg_mask;
- if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) {
+
+ /*
+ * Add pc to the resource mask to prevent this instruction
+ * from sinking past branch instructions. Also take out the memory
+ * region bits since stop_mask is used to check data/control
+ * dependencies.
+ *
+ * Note: on x86(-64) and Arm64 we use the IsBranch bit, as the PC is not exposed.
+ */
+ ResourceMask pc_encoding = GetPCUseDefEncoding();
+ if (pc_encoding == kEncodeNone) {
// TODO: Stop the abuse of kIsBranch as a bit specification for ResourceMask.
- stop_use_reg_mask = ResourceMask::Bit(kIsBranch).Union(*this_lir->u.m.use_mask).Without(
- kEncodeMem);
- } else {
- /*
- * Add pc to the resource mask to prevent this instruction
- * from sinking past branch instructions. Also take out the memory
- * region bits since stop_mask is used to check data/control
- * dependencies.
- */
- stop_use_reg_mask = GetPCUseDefEncoding().Union(*this_lir->u.m.use_mask).Without(kEncodeMem);
+ pc_encoding = ResourceMask::Bit(kIsBranch);
}
+ ResourceMask stop_use_reg_mask = pc_encoding.Union(*this_lir->u.m.use_mask).
+ Without(kEncodeMem);
for (check_lir = NEXT_LIR(this_lir); check_lir != tail_lir; check_lir = NEXT_LIR(check_lir)) {
/*
@@ -310,16 +312,17 @@
ResourceMask stop_use_all_mask = *this_lir->u.m.use_mask;
- if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) {
- /*
- * Branches for null/range checks are marked with the true resource
- * bits, and loads to Dalvik registers, constant pools, and non-alias
- * locations are safe to be hoisted. So only mark the heap references
- * conservatively here.
- */
- if (stop_use_all_mask.HasBit(ResourceMask::kHeapRef)) {
- stop_use_all_mask.SetBits(GetPCUseDefEncoding());
- }
+ /*
+ * Branches for null/range checks are marked with the true resource
+ * bits, and loads to Dalvik registers, constant pools, and non-alias
+ * locations are safe to be hoisted. So only mark the heap references
+ * conservatively here.
+ *
+ * Note: on x86(-64) and Arm64 this will add kEncodeNone.
+ * TODO: Sanity check. LoadStoreElimination uses kBranchBit to fake a PC.
+ */
+ if (stop_use_all_mask.HasBit(ResourceMask::kHeapRef)) {
+ stop_use_all_mask.SetBits(GetPCUseDefEncoding());
}
/* Similar as above, but just check for pure register dependency */
diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc
index 129a696..75d3c5d 100644
--- a/compiler/dex/quick/mips/utility_mips.cc
+++ b/compiler/dex/quick/mips/utility_mips.cc
@@ -563,10 +563,7 @@
load = LoadBaseDispBody(r_base, displacement, r_dest, size);
if (UNLIKELY(is_volatile == kVolatile)) {
- // Without context sensitive analysis, we must issue the most conservative barriers.
- // In this case, either a load or store may follow so we issue both barriers.
- GenMemBarrier(kLoadLoad);
- GenMemBarrier(kLoadStore);
+ GenMemBarrier(kLoadAny);
}
return load;
@@ -658,8 +655,8 @@
OpSize size, VolatileKind is_volatile) {
if (is_volatile == kVolatile) {
DCHECK(size != k64 && size != kDouble);
- // There might have been a store before this volatile one so insert StoreStore barrier.
- GenMemBarrier(kStoreStore);
+ // Ensure that prior accesses become visible to other threads first.
+ GenMemBarrier(kAnyStore);
}
// TODO: base this on target.
@@ -670,8 +667,9 @@
store = StoreBaseDispBody(r_base, displacement, r_src, size);
if (UNLIKELY(is_volatile == kVolatile)) {
- // A load might follow the volatile store so insert a StoreLoad barrier.
- GenMemBarrier(kStoreLoad);
+ // Preserve order with respect to any subsequent volatile loads.
+ // We need StoreLoad, but that generally requires the most expensive barrier.
+ GenMemBarrier(kAnyAny);
}
return store;
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 87509b6..d1e83c2 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -1263,6 +1263,9 @@
virtual const char* GetTargetInstFmt(int opcode) = 0;
virtual const char* GetTargetInstName(int opcode) = 0;
virtual std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr) = 0;
+
+ // Note: This may return kEncodeNone on architectures that do not expose a PC. The caller must
+ // take care of this.
virtual ResourceMask GetPCUseDefEncoding() const = 0;
virtual uint64_t GetTargetInstFlags(int opcode) = 0;
virtual size_t GetInsnSize(LIR* lir) = 0;
diff --git a/compiler/dex/quick/resource_mask.h b/compiler/dex/quick/resource_mask.h
index 12ce98a..436cdb5 100644
--- a/compiler/dex/quick/resource_mask.h
+++ b/compiler/dex/quick/resource_mask.h
@@ -63,6 +63,11 @@
ResourceMask(const ResourceMask& other) = default;
ResourceMask& operator=(const ResourceMask& other) = default;
+ // Comparable by content.
+ bool operator==(const ResourceMask& other) {
+ return masks_[0] == other.masks_[0] && masks_[1] == other.masks_[1];
+ }
+
static constexpr ResourceMask RawMask(uint64_t mask1, uint64_t mask2) {
return ResourceMask(mask1, mask2);
}
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index f1166f6..1c63da4 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -19,6 +19,7 @@
#include "codegen_x86.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
+#include "mirror/art_method.h"
#include "mirror/array.h"
#include "x86_lir.h"
@@ -861,7 +862,7 @@
// After a store we need to insert barrier in case of potential load. Since the
// locked cmpxchg has full barrier semantics, only a scheduling barrier will be generated.
- GenMemBarrier(kStoreLoad);
+ GenMemBarrier(kAnyAny);
FreeTemp(rs_r0q);
} else if (is_long) {
@@ -913,10 +914,11 @@
}
NewLIR4(kX86LockCmpxchg64A, rs_obj.GetReg(), rs_off.GetReg(), 0, 0);
- // After a store we need to insert barrier in case of potential load. Since the
- // locked cmpxchg has full barrier semantics, only a scheduling barrier will be generated.
- GenMemBarrier(kStoreLoad);
-
+ // After a store we need to insert barrier to prevent reordering with either
+ // earlier or later memory accesses. Since
+ // locked cmpxchg has full barrier semantics, only a scheduling barrier will be generated,
+ // and it will be associated with the cmpxchg instruction, preventing both.
+ GenMemBarrier(kAnyAny);
if (push_si) {
FreeTemp(rs_rSI);
@@ -954,9 +956,11 @@
LoadValueDirect(rl_src_expected, rs_r0);
NewLIR5(kX86LockCmpxchgAR, rl_object.reg.GetReg(), rl_offset.reg.GetReg(), 0, 0, rl_new_value.reg.GetReg());
- // After a store we need to insert barrier in case of potential load. Since the
- // locked cmpxchg has full barrier semantics, only a scheduling barrier will be generated.
- GenMemBarrier(kStoreLoad);
+ // After a store we need to insert barrier to prevent reordering with either
+ // earlier or later memory accesses. Since
+ // locked cmpxchg has full barrier semantics, only a scheduling barrier will be generated,
+ // and it will be associated with the cmpxchg instruction, preventing both.
+ GenMemBarrier(kAnyAny);
FreeTemp(rs_r0);
}
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 1ebbbbd..0083128 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -246,11 +246,6 @@
}
ResourceMask X86Mir2Lir::GetPCUseDefEncoding() const {
- /*
- * FIXME: might make sense to use a virtual resource encoding bit for pc. Might be
- * able to clean up some of the x86/Arm_Mips differences
- */
- LOG(FATAL) << "Unexpected call to GetPCUseDefEncoding for x86";
return kEncodeNone;
}
@@ -582,11 +577,11 @@
bool ret = false;
/*
- * According to the JSR-133 Cookbook, for x86 only StoreLoad barriers need memory fence. All other barriers
- * (LoadLoad, LoadStore, StoreStore) are nops due to the x86 memory model. For those cases, all we need
- * to ensure is that there is a scheduling barrier in place.
+ * According to the JSR-133 Cookbook, for x86 only StoreLoad/AnyAny barriers need memory fence.
+ * All other barriers (LoadAny, AnyStore, StoreStore) are nops due to the x86 memory model.
+ * For those cases, all we need to ensure is that there is a scheduling barrier in place.
*/
- if (barrier_kind == kStoreLoad) {
+ if (barrier_kind == kAnyAny) {
// If no LIR exists already that can be used a barrier, then generate an mfence.
if (mem_barrier == nullptr) {
mem_barrier = NewLIR0(kX86Mfence);
diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc
index 5c7c91b..045e58e 100644
--- a/compiler/dex/quick/x86/utility_x86.cc
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -762,10 +762,7 @@
size);
if (UNLIKELY(is_volatile == kVolatile)) {
- // Without context sensitive analysis, we must issue the most conservative barriers.
- // In this case, either a load or store may follow so we issue both barriers.
- GenMemBarrier(kLoadLoad);
- GenMemBarrier(kLoadStore);
+ GenMemBarrier(kLoadAny); // Only a scheduling barrier.
}
return load;
@@ -863,8 +860,7 @@
LIR* X86Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size,
VolatileKind is_volatile) {
if (UNLIKELY(is_volatile == kVolatile)) {
- // There might have been a store before this volatile one so insert StoreStore barrier.
- GenMemBarrier(kStoreStore);
+ GenMemBarrier(kAnyStore); // Only a scheduling barrier.
}
// StoreBaseDisp() will emit correct insn for atomic store on x86
@@ -873,8 +869,9 @@
LIR* store = StoreBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_src, size);
if (UNLIKELY(is_volatile == kVolatile)) {
- // A load might follow the volatile store so insert a StoreLoad barrier.
- GenMemBarrier(kStoreLoad);
+ // A volatile load might follow the volatile store so insert a StoreLoad barrier.
+ // This does require a fence, even on x86.
+ GenMemBarrier(kAnyAny);
}
return store;
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 324f717..99fcc26 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_DRIVER_COMPILER_DRIVER_INL_H_
#include "compiler_driver.h"
+
#include "dex/compiler_ir.h"
#include "mirror/art_field.h"
#include "mirror/art_field-inl.h"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 9bf5135..4b4d0d0 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -25,6 +25,7 @@
#include "base/stl_util.h"
#include "base/timing_logger.h"
#include "class_linker.h"
+#include "compiled_class.h"
#include "compiler.h"
#include "compiler_driver-inl.h"
#include "dex_compilation_unit.h"
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 9903421..ae709f8 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -24,7 +24,6 @@
#include "base/mutex.h"
#include "base/timing_logger.h"
#include "class_reference.h"
-#include "compiled_class.h"
#include "compiled_method.h"
#include "compiler.h"
#include "dex_file.h"
@@ -32,6 +31,7 @@
#include "instruction_set.h"
#include "invoke_type.h"
#include "method_reference.h"
+#include "mirror/class.h" // For mirror::Class::Status.
#include "os.h"
#include "profiler.h"
#include "runtime.h"
@@ -46,6 +46,7 @@
class MethodVerifier;
} // namespace verifier
+class CompiledClass;
class CompilerOptions;
class DexCompilationUnit;
class DexFileToMethodInlinerMap;
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index acfa607..38b4100 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -630,11 +630,33 @@
mirror::Reference::ReferentOffset(), image_writer_->GetImageAddress(ref->GetReferent()));
}
- private:
+ protected:
ImageWriter* const image_writer_;
mirror::Object* const copy_;
};
+class FixupClassVisitor FINAL : public FixupVisitor {
+ public:
+ FixupClassVisitor(ImageWriter* image_writer, Object* copy) : FixupVisitor(image_writer, copy) {
+ }
+
+ void operator()(Object* obj, MemberOffset offset, bool /*is_static*/) const
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+ DCHECK(obj->IsClass());
+ FixupVisitor::operator()(obj, offset, false);
+
+ if (offset.Uint32Value() < mirror::Class::EmbeddedVTableOffset().Uint32Value()) {
+ return;
+ }
+ }
+
+ void operator()(mirror::Class* /*klass*/, mirror::Reference* ref) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+ LOG(FATAL) << "Reference not expected here.";
+ }
+};
+
void ImageWriter::FixupObject(Object* orig, Object* copy) {
DCHECK(orig != nullptr);
DCHECK(copy != nullptr);
@@ -646,13 +668,68 @@
DCHECK_EQ(copy->GetReadBarrierPointer(), GetImageAddress(orig));
}
}
- FixupVisitor visitor(this, copy);
- orig->VisitReferences<true /*visit class*/>(visitor, visitor);
+ if (orig->IsClass() && orig->AsClass()->ShouldHaveEmbeddedImtAndVTable()) {
+ FixupClassVisitor visitor(this, copy);
+ orig->VisitReferences<true /*visit class*/>(visitor, visitor);
+ } else {
+ FixupVisitor visitor(this, copy);
+ orig->VisitReferences<true /*visit class*/>(visitor, visitor);
+ }
if (orig->IsArtMethod<kVerifyNone>()) {
FixupMethod(orig->AsArtMethod<kVerifyNone>(), down_cast<ArtMethod*>(copy));
}
}
+const byte* ImageWriter::GetQuickCode(mirror::ArtMethod* method, bool* quick_is_interpreted) {
+ DCHECK(!method->IsResolutionMethod() && !method->IsImtConflictMethod() &&
+ !method->IsAbstract()) << PrettyMethod(method);
+
+ // Use original code if it exists. Otherwise, set the code pointer to the resolution
+ // trampoline.
+
+ // Quick entrypoint:
+ const byte* quick_code = GetOatAddress(method->GetQuickOatCodeOffset());
+ *quick_is_interpreted = false;
+ if (quick_code != nullptr &&
+ (!method->IsStatic() || method->IsConstructor() || method->GetDeclaringClass()->IsInitialized())) {
+ // We have code for a non-static or initialized method, just use the code.
+ } else if (quick_code == nullptr && method->IsNative() &&
+ (!method->IsStatic() || method->GetDeclaringClass()->IsInitialized())) {
+ // Non-static or initialized native method missing compiled code, use generic JNI version.
+ quick_code = GetOatAddress(quick_generic_jni_trampoline_offset_);
+ } else if (quick_code == nullptr && !method->IsNative()) {
+ // We don't have code at all for a non-native method, use the interpreter.
+ quick_code = GetOatAddress(quick_to_interpreter_bridge_offset_);
+ *quick_is_interpreted = true;
+ } else {
+ CHECK(!method->GetDeclaringClass()->IsInitialized());
+ // We have code for a static method, but need to go through the resolution stub for class
+ // initialization.
+ quick_code = GetOatAddress(quick_resolution_trampoline_offset_);
+ }
+ return quick_code;
+}
+
+const byte* ImageWriter::GetQuickEntryPoint(mirror::ArtMethod* method) {
+ // Calculate the quick entry point following the same logic as FixupMethod() below.
+ // The resolution method has a special trampoline to call.
+ if (UNLIKELY(method == Runtime::Current()->GetResolutionMethod())) {
+ return GetOatAddress(quick_resolution_trampoline_offset_);
+ } else if (UNLIKELY(method == Runtime::Current()->GetImtConflictMethod())) {
+ return GetOatAddress(quick_imt_conflict_trampoline_offset_);
+ } else {
+ // We assume all methods have code. If they don't currently then we set them to the use the
+ // resolution trampoline. Abstract methods never have code and so we need to make sure their
+ // use results in an AbstractMethodError. We use the interpreter to achieve this.
+ if (UNLIKELY(method->IsAbstract())) {
+ return GetOatAddress(quick_to_interpreter_bridge_offset_);
+ } else {
+ bool quick_is_interpreted;
+ return GetQuickCode(method, &quick_is_interpreted);
+ }
+ }
+}
+
void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) {
// OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
// oat_begin_
@@ -674,29 +751,8 @@
copy->SetEntryPointFromInterpreter<kVerifyNone>(reinterpret_cast<EntryPointFromInterpreter*>
(const_cast<byte*>(GetOatAddress(interpreter_to_interpreter_bridge_offset_))));
} else {
- // Use original code if it exists. Otherwise, set the code pointer to the resolution
- // trampoline.
-
- // Quick entrypoint:
- const byte* quick_code = GetOatAddress(orig->GetQuickOatCodeOffset());
- bool quick_is_interpreted = false;
- if (quick_code != nullptr &&
- (!orig->IsStatic() || orig->IsConstructor() || orig->GetDeclaringClass()->IsInitialized())) {
- // We have code for a non-static or initialized method, just use the code.
- } else if (quick_code == nullptr && orig->IsNative() &&
- (!orig->IsStatic() || orig->GetDeclaringClass()->IsInitialized())) {
- // Non-static or initialized native method missing compiled code, use generic JNI version.
- quick_code = GetOatAddress(quick_generic_jni_trampoline_offset_);
- } else if (quick_code == nullptr && !orig->IsNative()) {
- // We don't have code at all for a non-native method, use the interpreter.
- quick_code = GetOatAddress(quick_to_interpreter_bridge_offset_);
- quick_is_interpreted = true;
- } else {
- CHECK(!orig->GetDeclaringClass()->IsInitialized());
- // We have code for a static method, but need to go through the resolution stub for class
- // initialization.
- quick_code = GetOatAddress(quick_resolution_trampoline_offset_);
- }
+ bool quick_is_interpreted;
+ const byte* quick_code = GetQuickCode(orig, &quick_is_interpreted);
copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(quick_code);
// Portable entrypoint:
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 2bcb41e..cf5bc93 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -149,6 +149,13 @@
void FixupObject(mirror::Object* orig, mirror::Object* copy)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Get quick code for non-resolution/imt_conflict/abstract method.
+ const byte* GetQuickCode(mirror::ArtMethod* method, bool* quick_is_interpreted)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ const byte* GetQuickEntryPoint(mirror::ArtMethod* method)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Patches references in OatFile to expect runtime addresses.
void PatchOatCodeAndMethods(File* elf_file)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -192,6 +199,7 @@
uint32_t quick_to_interpreter_bridge_offset_;
friend class FixupVisitor;
+ friend class FixupClassVisitor;
DISALLOW_COPY_AND_ASSIGN(ImageWriter);
};
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 3bbb723..dec84f1 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -28,6 +28,7 @@
#include "driver/compiler_driver.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "jni_internal.h"
+#include "mirror/art_method.h"
#include "utils/assembler.h"
#include "utils/managed_register.h"
#include "utils/arm/managed_register_arm.h"
diff --git a/compiler/llvm/gbc_expander.cc b/compiler/llvm/gbc_expander.cc
index f8dca66..902f8dd 100644
--- a/compiler/llvm/gbc_expander.cc
+++ b/compiler/llvm/gbc_expander.cc
@@ -1648,7 +1648,7 @@
field_value = SignOrZeroExtendCat1Types(field_value, field_jty);
if (is_volatile) {
- irb_.CreateMemoryBarrier(art::kLoadLoad);
+ irb_.CreateMemoryBarrier(art::kLoadAny);
}
}
@@ -1702,7 +1702,7 @@
DCHECK_GE(field_offset.Int32Value(), 0);
if (is_volatile) {
- irb_.CreateMemoryBarrier(art::kStoreStore);
+ irb_.CreateMemoryBarrier(art::kAnyStore);
}
llvm::PointerType* field_type =
@@ -1717,7 +1717,7 @@
irb_.CreateStore(new_value, field_addr, kTBAAHeapInstance, field_jty);
if (is_volatile) {
- irb_.CreateMemoryBarrier(art::kLoadLoad);
+ irb_.CreateMemoryBarrier(art::kAnyAny);
}
if (field_jty == kObject) { // If put an object, mark the GC card table.
@@ -1870,7 +1870,7 @@
phi->addIncoming(loaded_storage_object_addr, block_after_load_static);
// Ensure load of status and load of value don't re-order.
- irb_.CreateMemoryBarrier(art::kLoadLoad);
+ irb_.CreateMemoryBarrier(art::kLoadAny);
return phi;
}
@@ -1948,7 +1948,7 @@
static_field_value = SignOrZeroExtendCat1Types(static_field_value, field_jty);
if (is_volatile) {
- irb_.CreateMemoryBarrier(art::kLoadLoad);
+ irb_.CreateMemoryBarrier(art::kLoadAny);
}
}
@@ -2025,7 +2025,7 @@
}
if (is_volatile) {
- irb_.CreateMemoryBarrier(art::kStoreStore);
+ irb_.CreateMemoryBarrier(art::kAnyStore);
}
llvm::Value* static_field_offset_value = irb_.getPtrEquivInt(field_offset.Int32Value());
@@ -2038,7 +2038,7 @@
irb_.CreateStore(new_value, static_field_addr, kTBAAHeapStatic, field_jty);
if (is_volatile) {
- irb_.CreateMemoryBarrier(art::kStoreLoad);
+ irb_.CreateMemoryBarrier(art::kAnyAny);
}
if (field_jty == kObject) { // If put an object, mark the GC card table.
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 73f4ba1..63a3c8c 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -22,6 +22,7 @@
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
+#include "compiled_class.h"
#include "dex_file-inl.h"
#include "dex/verification_results.h"
#include "gc/space/space.h"
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 631d538..0fa1a96 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -984,7 +984,7 @@
const void* GetQuickOatCodeBegin(mirror::ArtMethod* m)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const void* quick_code = m->GetEntryPointFromQuickCompiledCode();
- if (quick_code == GetQuickResolutionTrampoline(Runtime::Current()->GetClassLinker())) {
+ if (quick_code == Runtime::Current()->GetClassLinker()->GetQuickResolutionTrampoline()) {
quick_code = oat_dumper_->GetQuickOatCode(m);
}
if (oat_dumper_->GetInstructionSet() == kThumb2) {
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 7f5cf0c..b0c1a9c 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -309,10 +309,10 @@
LIBART_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
endif
-ifeq ($(MALLOC_IMPL),jemalloc)
- LIBART_CFLAGS += -DUSE_JEMALLOC
-else
+ifeq ($(MALLOC_IMPL),dlmalloc)
LIBART_CFLAGS += -DUSE_DLMALLOC
+else
+ LIBART_CFLAGS += -DUSE_JEMALLOC
endif
# $(1): target or host
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 3fa09cb..cf68c65 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -15,6 +15,7 @@
*/
#include "entrypoints/interpreter/interpreter_entrypoints.h"
+#include "entrypoints/jni/jni_entrypoints.h"
#include "entrypoints/portable/portable_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index c19b79e..0c33d9c 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -15,6 +15,7 @@
*/
#include "entrypoints/interpreter/interpreter_entrypoints.h"
+#include "entrypoints/jni/jni_entrypoints.h"
#include "entrypoints/portable/portable_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/entrypoint_utils.h"
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 70a9619..7a2e961 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "entrypoints/interpreter/interpreter_entrypoints.h"
+#include "entrypoints/jni/jni_entrypoints.h"
#include "entrypoints/portable/portable_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index eb490eb..ce8faea 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -1739,8 +1739,8 @@
// Sanity check: check that there is a conflict for List.contains in ArrayList.
mirror::Class* arraylist_class = soa.Decode<mirror::Class*>(arraylist_jclass);
- mirror::ArtMethod* m = arraylist_class->GetImTable()->Get(
- inf_contains->GetDexMethodIndex() % ClassLinker::kImtSize);
+ mirror::ArtMethod* m = arraylist_class->GetEmbeddedImTableEntry(
+ inf_contains->GetDexMethodIndex() % mirror::Class::kImtSize);
if (!m->IsImtConflictMethod()) {
LOG(WARNING) << "Test is meaningless, no IMT conflict in setup: " <<
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index b217cd6..a072996 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "entrypoints/interpreter/interpreter_entrypoints.h"
+#include "entrypoints/jni/jni_entrypoints.h"
#include "entrypoints/portable/portable_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index 204d52c..35a0cf4 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "entrypoints/interpreter/interpreter_entrypoints.h"
+#include "entrypoints/jni/jni_entrypoints.h"
#include "entrypoints/portable/portable_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -27,8 +29,8 @@
const DexFile::CodeItem* code_item,
ShadowFrame* shadow_frame, JValue* result);
extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh,
- const DexFile::CodeItem* code_item,
- ShadowFrame* shadow_frame, JValue* result);
+ const DexFile::CodeItem* code_item,
+ ShadowFrame* shadow_frame, JValue* result);
// Portable entrypoints.
extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*);
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 7f7226c..885fbfd 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -18,7 +18,7 @@
MACRO0(SETUP_FP_CALLEE_SAVE_FRAME)
// Create space for ART FP callee-saved registers
- subq LITERAL(4 * 8), %rsp
+ subq MACRO_LITERAL(4 * 8), %rsp
CFI_ADJUST_CFA_OFFSET(4 * 8)
movq %xmm12, 0(%rsp)
movq %xmm13, 8(%rsp)
@@ -32,7 +32,7 @@
movq 8(%rsp), %xmm13
movq 16(%rsp), %xmm14
movq 24(%rsp), %xmm15
- addq LITERAL(4 * 8), %rsp
+ addq MACRO_LITERAL(4 * 8), %rsp
CFI_ADJUST_CFA_OFFSET(- 4 * 8)
END_MACRO
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 860cbd2..1436810 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -55,6 +55,7 @@
#include "mirror/object_array-inl.h"
#include "mirror/proxy.h"
#include "mirror/stack_trace_element.h"
+#include "mirror/string-inl.h"
#include "object_utils.h"
#include "os.h"
#include "runtime.h"
@@ -207,7 +208,8 @@
heap->IncrementDisableMovingGC(self);
StackHandleScope<64> hs(self); // 64 is picked arbitrarily.
Handle<mirror::Class> java_lang_Class(hs.NewHandle(down_cast<mirror::Class*>(
- heap->AllocNonMovableObject<true>(self, nullptr, sizeof(mirror::ClassClass),
+ heap->AllocNonMovableObject<true>(self, nullptr,
+ mirror::Class::ClassClassSize(),
VoidFunctor()))));
CHECK(java_lang_Class.Get() != NULL);
mirror::Class::SetClassClass(java_lang_Class.Get());
@@ -215,43 +217,44 @@
if (kUseBakerOrBrooksReadBarrier) {
java_lang_Class->AssertReadBarrierPointer();
}
- java_lang_Class->SetClassSize(sizeof(mirror::ClassClass));
+ java_lang_Class->SetClassSize(mirror::Class::ClassClassSize());
heap->DecrementDisableMovingGC(self);
// AllocClass(mirror::Class*) can now be used
// Class[] is used for reflection support.
- Handle<mirror::Class> class_array_class(
- hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class))));
+ Handle<mirror::Class> class_array_class(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(), mirror::ObjectArray<mirror::Class>::ClassSize())));
class_array_class->SetComponentType(java_lang_Class.Get());
// java_lang_Object comes next so that object_array_class can be created.
- Handle<mirror::Class> java_lang_Object(
- hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class))));
+ Handle<mirror::Class> java_lang_Object(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(), mirror::Object::ClassSize())));
CHECK(java_lang_Object.Get() != NULL);
// backfill Object as the super class of Class.
java_lang_Class->SetSuperClass(java_lang_Object.Get());
java_lang_Object->SetStatus(mirror::Class::kStatusLoaded, self);
// Object[] next to hold class roots.
- Handle<mirror::Class> object_array_class(
- hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class))));
+ Handle<mirror::Class> object_array_class(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(), mirror::ObjectArray<mirror::Object>::ClassSize())));
object_array_class->SetComponentType(java_lang_Object.Get());
- // Setup the char class to be used for char[].
- Handle<mirror::Class> char_class(hs.NewHandle(AllocClass(self, java_lang_Class.Get(),
- sizeof(mirror::Class))));
+ // Setup the char (primitive) class to be used for char[].
+ Handle<mirror::Class> char_class(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(), mirror::Class::PrimitiveClassSize())));
// Setup the char[] class to be used for String.
- Handle<mirror::Class> char_array_class(hs.NewHandle(AllocClass(self, java_lang_Class.Get(),
- sizeof(mirror::Class))));
+ Handle<mirror::Class> char_array_class(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(),
+ mirror::Array::ClassSize())));
char_array_class->SetComponentType(char_class.Get());
mirror::CharArray::SetArrayClass(char_array_class.Get());
// Setup String.
- Handle<mirror::Class> java_lang_String(hs.NewHandle(AllocClass(self, java_lang_Class.Get(),
- sizeof(mirror::StringClass))));
+ Handle<mirror::Class> java_lang_String(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize())));
mirror::String::SetClass(java_lang_String.Get());
- java_lang_String->SetObjectSize(sizeof(mirror::String));
+ java_lang_String->SetObjectSize(mirror::String::InstanceSize());
java_lang_String->SetStatus(mirror::Class::kStatusResolved, self);
// Create storage for root classes, save away our work so far (requires descriptors).
@@ -279,8 +282,8 @@
array_iftable_ = AllocIfTable(self, 2);
// Create int array type for AllocDexCache (done in AppendToBootClassPath).
- Handle<mirror::Class> int_array_class(
- hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class))));
+ Handle<mirror::Class> int_array_class(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize())));
int_array_class->SetComponentType(GetClassRoot(kPrimitiveInt));
mirror::IntArray::SetArrayClass(int_array_class.Get());
SetClassRoot(kIntArrayClass, int_array_class.Get());
@@ -288,44 +291,47 @@
// now that these are registered, we can use AllocClass() and AllocObjectArray
// Set up DexCache. This cannot be done later since AppendToBootClassPath calls AllocDexCache.
- Handle<mirror::Class> java_lang_DexCache(
- hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::DexCacheClass))));
+ Handle<mirror::Class> java_lang_DexCache(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(), mirror::DexCache::ClassSize())));
SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get());
- java_lang_DexCache->SetObjectSize(sizeof(mirror::DexCache));
+ java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize());
java_lang_DexCache->SetStatus(mirror::Class::kStatusResolved, self);
// Constructor, Field, Method, and AbstractMethod are necessary so
// that FindClass can link members.
- Handle<mirror::Class> java_lang_reflect_ArtField(
- hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::ArtFieldClass))));
+ Handle<mirror::Class> java_lang_reflect_ArtField(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(), mirror::ArtField::ClassSize())));
CHECK(java_lang_reflect_ArtField.Get() != NULL);
- java_lang_reflect_ArtField->SetObjectSize(sizeof(mirror::ArtField));
+ java_lang_reflect_ArtField->SetObjectSize(mirror::ArtField::InstanceSize());
SetClassRoot(kJavaLangReflectArtField, java_lang_reflect_ArtField.Get());
java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusResolved, self);
mirror::ArtField::SetClass(java_lang_reflect_ArtField.Get());
- Handle<mirror::Class> java_lang_reflect_ArtMethod(
- hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::ArtMethodClass))));
+ Handle<mirror::Class> java_lang_reflect_ArtMethod(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(), mirror::ArtMethod::ClassSize())));
CHECK(java_lang_reflect_ArtMethod.Get() != NULL);
- java_lang_reflect_ArtMethod->SetObjectSize(sizeof(mirror::ArtMethod));
+ java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize());
SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.Get());
java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved, self);
mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.Get());
// Set up array classes for string, field, method
- Handle<mirror::Class> object_array_string(
- hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class))));
+ Handle<mirror::Class> object_array_string(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(),
+ mirror::ObjectArray<mirror::String>::ClassSize())));
object_array_string->SetComponentType(java_lang_String.Get());
SetClassRoot(kJavaLangStringArrayClass, object_array_string.Get());
- Handle<mirror::Class> object_array_art_method(
- hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class))));
+ Handle<mirror::Class> object_array_art_method(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(),
+ mirror::ObjectArray<mirror::ArtMethod>::ClassSize())));
object_array_art_method->SetComponentType(java_lang_reflect_ArtMethod.Get());
SetClassRoot(kJavaLangReflectArtMethodArrayClass, object_array_art_method.Get());
- Handle<mirror::Class> object_array_art_field(
- hs.NewHandle(AllocClass(self, java_lang_Class.Get(), sizeof(mirror::Class))));
+ Handle<mirror::Class> object_array_art_field(hs.NewHandle(
+ AllocClass(self, java_lang_Class.Get(),
+ mirror::ObjectArray<mirror::ArtField>::ClassSize())));
object_array_art_field->SetComponentType(java_lang_reflect_ArtField.Get());
SetClassRoot(kJavaLangReflectArtFieldArrayClass, object_array_art_field.Get());
@@ -359,16 +365,19 @@
java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* Object_class = FindSystemClass(self, "Ljava/lang/Object;");
CHECK_EQ(java_lang_Object.Get(), Object_class);
- CHECK_EQ(java_lang_Object->GetObjectSize(), sizeof(mirror::Object));
+ CHECK_EQ(java_lang_Object->GetObjectSize(), mirror::Object::InstanceSize());
java_lang_String->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* String_class = FindSystemClass(self, "Ljava/lang/String;");
- CHECK_EQ(java_lang_String.Get(), String_class);
- CHECK_EQ(java_lang_String->GetObjectSize(), sizeof(mirror::String));
+ std::ostringstream os1, os2;
+ java_lang_String->DumpClass(os1, mirror::Class::kDumpClassFullDetail);
+ String_class->DumpClass(os2, mirror::Class::kDumpClassFullDetail);
+ CHECK_EQ(java_lang_String.Get(), String_class) << os1.str() << "\n\n" << os2.str();
+ CHECK_EQ(java_lang_String->GetObjectSize(), mirror::String::InstanceSize());
java_lang_DexCache->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* DexCache_class = FindSystemClass(self, "Ljava/lang/DexCache;");
CHECK_EQ(java_lang_String.Get(), String_class);
CHECK_EQ(java_lang_DexCache.Get(), DexCache_class);
- CHECK_EQ(java_lang_DexCache->GetObjectSize(), sizeof(mirror::DexCache));
+ CHECK_EQ(java_lang_DexCache->GetObjectSize(), mirror::DexCache::InstanceSize());
// Setup the primitive array type classes - can't be done until Object has a vtable.
SetClassRoot(kBooleanArrayClass, FindSystemClass(self, "[Z"));
@@ -476,7 +485,7 @@
// Setup the ClassLoader, verifying the object_size_.
mirror::Class* java_lang_ClassLoader = FindSystemClass(self, "Ljava/lang/ClassLoader;");
- CHECK_EQ(java_lang_ClassLoader->GetObjectSize(), sizeof(mirror::ClassLoader));
+ CHECK_EQ(java_lang_ClassLoader->GetObjectSize(), mirror::ClassLoader::InstanceSize());
SetClassRoot(kJavaLangClassLoader, java_lang_ClassLoader);
// Set up java.lang.Throwable, java.lang.ClassNotFoundException, and
@@ -1402,36 +1411,11 @@
return dex_cache.Get();
}
-// Used to initialize a class in the allocation code path to ensure it is guarded by a StoreStore
-// fence.
-class InitializeClassVisitor {
- public:
- explicit InitializeClassVisitor(uint32_t class_size) : class_size_(class_size) {
- }
-
- void operator()(mirror::Object* obj, size_t usable_size) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK_LE(class_size_, usable_size);
- // Avoid AsClass as object is not yet in live bitmap or allocation stack.
- mirror::Class* klass = down_cast<mirror::Class*>(obj);
- // DCHECK(klass->IsClass());
- klass->SetClassSize(class_size_);
- klass->SetPrimitiveType(Primitive::kPrimNot); // Default to not being primitive.
- klass->SetDexClassDefIndex(DexFile::kDexNoIndex16); // Default to no valid class def index.
- klass->SetDexTypeIndex(DexFile::kDexNoIndex16); // Default to no valid type index.
- }
-
- private:
- const uint32_t class_size_;
-
- DISALLOW_COPY_AND_ASSIGN(InitializeClassVisitor);
-};
-
mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Class,
uint32_t class_size) {
DCHECK_GE(class_size, sizeof(mirror::Class));
gc::Heap* heap = Runtime::Current()->GetHeap();
- InitializeClassVisitor visitor(class_size);
+ mirror::Class::InitializeClassVisitor visitor(class_size);
mirror::Object* k = kMovingClasses ?
heap->AllocObject<true>(self, java_lang_Class, class_size, visitor) :
heap->AllocNonMovableObject<true>(self, java_lang_Class, class_size, visitor);
@@ -1462,9 +1446,33 @@
self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length);
}
-static mirror::Class* EnsureResolved(Thread* self, mirror::Class* klass)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+mirror::Class* ClassLinker::EnsureResolved(Thread* self, const char* descriptor,
+ mirror::Class* klass) {
DCHECK(klass != NULL);
+
+ // For temporary classes we must wait for them to be retired.
+ if (init_done_ && klass->IsTemp()) {
+ CHECK(!klass->IsResolved());
+ if (klass->IsErroneous()) {
+ ThrowEarlierClassFailure(klass);
+ return nullptr;
+ }
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_class(hs.NewHandle(klass));
+ ObjectLock<mirror::Class> lock(self, h_class);
+ // Loop and wait for the resolving thread to retire this class.
+ while (!h_class->IsRetired() && !h_class->IsErroneous()) {
+ lock.WaitIgnoringInterrupts();
+ }
+ if (h_class->IsErroneous()) {
+ ThrowEarlierClassFailure(h_class.Get());
+ return nullptr;
+ }
+ CHECK(h_class->IsRetired());
+ // Get the updated class from class table.
+ klass = LookupClass(descriptor, h_class.Get()->GetClassLoader());
+ }
+
// Wait for the class if it has not already been linked.
if (!klass->IsResolved() && !klass->IsErroneous()) {
StackHandleScope<1> hs(self);
@@ -1481,6 +1489,7 @@
lock.WaitIgnoringInterrupts();
}
}
+
if (klass->IsErroneous()) {
ThrowEarlierClassFailure(klass);
return nullptr;
@@ -1504,7 +1513,7 @@
// Find the class in the loaded classes table.
mirror::Class* klass = LookupClass(descriptor, class_loader.Get());
if (klass != NULL) {
- return EnsureResolved(self, klass);
+ return EnsureResolved(self, descriptor, klass);
}
// Class is not yet loaded.
if (descriptor[0] == '[') {
@@ -1578,8 +1587,10 @@
const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def) {
Thread* self = Thread::Current();
- StackHandleScope<2> hs(self);
+ StackHandleScope<3> hs(self);
auto klass = hs.NewHandle<mirror::Class>(nullptr);
+ bool should_allocate = false;
+
// Load the class from the dex file.
if (UNLIKELY(!init_done_)) {
// finish up init of hand crafted class_roots_
@@ -1596,10 +1607,18 @@
} else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) {
klass.Assign(GetClassRoot(kJavaLangReflectArtMethod));
} else {
- klass.Assign(AllocClass(self, SizeOfClass(dex_file, dex_class_def)));
+ should_allocate = true;
}
} else {
- klass.Assign(AllocClass(self, SizeOfClass(dex_file, dex_class_def)));
+ should_allocate = true;
+ }
+
+ if (should_allocate) {
+ // Allocate a class with the status of not ready.
+ // Interface object should get the right size here. Regular class will
+ // figure out the right size later and be replaced with one of the right
+ // size when the class becomes resolved.
+ klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
}
if (UNLIKELY(klass.Get() == NULL)) {
CHECK(self->IsExceptionPending()); // Expect an OOME.
@@ -1614,13 +1633,15 @@
}
ObjectLock<mirror::Class> lock(self, klass);
klass->SetClinitThreadId(self->GetTid());
+
// Add the newly loaded class to the loaded classes table.
mirror::Class* existing = InsertClass(descriptor, klass.Get(), Hash(descriptor));
if (existing != NULL) {
// We failed to insert because we raced with another thread. Calling EnsureResolved may cause
// this thread to block.
- return EnsureResolved(self, existing);
+ return EnsureResolved(self, descriptor, existing);
}
+
// Finish loading (if necessary) by finding parents
CHECK(!klass->IsLoaded());
if (!LoadSuperAndInterfaces(klass, dex_file)) {
@@ -1633,12 +1654,17 @@
CHECK(!klass->IsResolved());
// TODO: Use fast jobjects?
auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
- if (!LinkClass(self, klass, interfaces)) {
+
+ mirror::Class* new_class = nullptr;
+ if (!LinkClass(self, descriptor, klass, interfaces, &new_class)) {
// Linking failed.
klass->SetStatus(mirror::Class::kStatusError, self);
return NULL;
}
- CHECK(klass->IsResolved());
+ CHECK(new_class != nullptr) << descriptor;
+ CHECK(new_class->IsResolved()) << descriptor;
+
+ Handle<mirror::Class> new_class_h(hs.NewHandle(new_class));
/*
* We send CLASS_PREPARE events to the debugger from here. The
@@ -1651,14 +1677,13 @@
* The class has been prepared and resolved but possibly not yet verified
* at this point.
*/
- Dbg::PostClassPrepare(klass.Get());
+ Dbg::PostClassPrepare(new_class_h.Get());
- return klass.Get();
+ return new_class_h.Get();
}
-// Precomputes size that will be needed for Class, matching LinkStaticFields
-uint32_t ClassLinker::SizeOfClass(const DexFile& dex_file,
- const DexFile::ClassDef& dex_class_def) {
+uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file,
+ const DexFile::ClassDef& dex_class_def) {
const byte* class_data = dex_file.GetClassData(dex_class_def);
size_t num_ref = 0;
size_t num_32 = 0;
@@ -1677,24 +1702,7 @@
}
}
}
- // start with generic class data
- uint32_t size = sizeof(mirror::Class);
- // follow with reference fields which must be contiguous at start
- size += (num_ref * sizeof(uint32_t));
- // if there are 64-bit fields to add, make sure they are aligned
- if (num_64 != 0 && size != RoundUp(size, 8)) { // for 64-bit alignment
- if (num_32 != 0) {
- // use an available 32-bit field for padding
- num_32--;
- }
- size += sizeof(uint32_t); // either way, we are adding a word
- DCHECK_EQ(size, RoundUp(size, 8));
- }
- // tack on any 64-bit fields now that alignment is assured
- size += (num_64 * sizeof(uint64_t));
- // tack on any remaining 32-bit fields
- size += (num_32 * sizeof(uint32_t));
- return size;
+ return mirror::Class::ComputeClassSize(false, 0, num_32, num_64, num_ref);
}
OatFile::OatClass ClassLinker::GetOatClass(const DexFile& dex_file, uint16_t class_def_idx) {
@@ -2308,7 +2316,7 @@
}
mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) {
- mirror::Class* klass = AllocClass(self, sizeof(mirror::Class));
+ mirror::Class* klass = AllocClass(self, mirror::Class::PrimitiveClassSize());
if (UNLIKELY(klass == NULL)) {
return NULL;
}
@@ -2413,7 +2421,7 @@
}
}
if (new_class.Get() == nullptr) {
- new_class.Assign(AllocClass(self, sizeof(mirror::Class)));
+ new_class.Assign(AllocClass(self, mirror::Array::ClassSize()));
if (new_class.Get() == nullptr) {
return nullptr;
}
@@ -2426,6 +2434,8 @@
new_class->SetVTable(java_lang_Object->GetVTable());
new_class->SetPrimitiveType(Primitive::kPrimNot);
new_class->SetClassLoader(component_type->GetClassLoader());
+ new_class->SetStatus(mirror::Class::kStatusLoaded, self);
+ new_class->PopulateEmbeddedImtAndVTable();
new_class->SetStatus(mirror::Class::kStatusInitialized, self);
// don't need to set new_class->SetObjectSize(..)
// because Object::SizeOf delegates to Array::SizeOf
@@ -2519,7 +2529,8 @@
if (existing != NULL) {
return existing;
}
- if (kIsDebugBuild && klass->GetClassLoader() == NULL && dex_cache_image_class_lookup_required_) {
+ if (kIsDebugBuild && !klass->IsTemp() && klass->GetClassLoader() == NULL &&
+ dex_cache_image_class_lookup_required_) {
// Check a class loaded with the system class loader matches one in the image if the class
// is in the image.
existing = LookupClassFromImage(descriptor);
@@ -2535,6 +2546,50 @@
return NULL;
}
+mirror::Class* ClassLinker::UpdateClass(const char* descriptor, mirror::Class* klass,
+ size_t hash) {
+ WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ mirror::Class* existing =
+ LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash);
+
+ if (existing == nullptr) {
+ CHECK(klass->IsProxyClass());
+ return nullptr;
+ }
+
+ CHECK_NE(existing, klass) << descriptor;
+ CHECK(!existing->IsResolved()) << descriptor;
+ CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusResolving) << descriptor;
+
+ for (auto it = class_table_.lower_bound(hash), end = class_table_.end(); it != end && it->first == hash;
+ ++it) {
+ mirror::Class* entry = it->second;
+ if (entry == existing) {
+ class_table_.erase(it);
+ break;
+ }
+ }
+
+ CHECK(!klass->IsTemp()) << descriptor;
+ if (kIsDebugBuild && klass->GetClassLoader() == nullptr &&
+ dex_cache_image_class_lookup_required_) {
+ // Check a class loaded with the system class loader matches one in the image if the class
+ // is in the image.
+ existing = LookupClassFromImage(descriptor);
+ if (existing != nullptr) {
+ CHECK(klass == existing) << descriptor;
+ }
+ }
+ VerifyObject(klass);
+
+ class_table_.insert(std::make_pair(hash, klass));
+ if (log_new_class_table_roots_) {
+ new_class_roots_.push_back(std::make_pair(hash, klass));
+ }
+
+ return existing;
+}
+
bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* class_loader) {
size_t hash = Hash(descriptor);
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
@@ -2954,8 +3009,8 @@
jobjectArray methods, jobjectArray throws) {
Thread* self = soa.Self();
StackHandleScope<8> hs(self);
- Handle<mirror::Class> klass(hs.NewHandle(AllocClass(self, GetClassRoot(kJavaLangClass),
- sizeof(mirror::SynthesizedProxyClass))));
+ Handle<mirror::Class> klass(hs.NewHandle(
+ AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class))));
if (klass.Get() == NULL) {
CHECK(self->IsExceptionPending()); // OOME.
return NULL;
@@ -3046,20 +3101,31 @@
klass->SetStatus(mirror::Class::kStatusLoaded, self); // Now effectively in the loaded state.
self->AssertNoPendingException();
+ std::string descriptor(GetDescriptorForProxy(klass.Get()));
+ mirror::Class* new_class = nullptr;
{
- ObjectLock<mirror::Class> lock(self, klass); // Must hold lock on object when resolved.
+ ObjectLock<mirror::Class> resolution_lock(self, klass); // Must hold lock on object when resolved.
// Link the fields and virtual methods, creating vtable and iftables
- Handle<mirror::ObjectArray<mirror::Class>> h_interfaces(
+ Handle<mirror::ObjectArray<mirror::Class> > h_interfaces(
hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)));
- if (!LinkClass(self, klass, h_interfaces)) {
+ if (!LinkClass(self, descriptor.c_str(), klass, h_interfaces, &new_class)) {
klass->SetStatus(mirror::Class::kStatusError, self);
return nullptr;
}
+ }
- interfaces_sfield->SetObject<false>(
- klass.Get(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
- throws_sfield->SetObject<false>(
- klass.Get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws));
+ CHECK(klass->IsRetired());
+ CHECK_NE(klass.Get(), new_class);
+ klass.Assign(new_class);
+
+ CHECK_EQ(interfaces_sfield->GetDeclaringClass(), new_class);
+ interfaces_sfield->SetObject<false>(klass.Get(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
+ CHECK_EQ(throws_sfield->GetDeclaringClass(), new_class);
+ throws_sfield->SetObject<false>(klass.Get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws));
+
+ {
+ // Lock on klass is released. Lock new class object.
+ ObjectLock<mirror::Class> initialization_lock(self, klass);
klass->SetStatus(mirror::Class::kStatusInitialized, self);
}
@@ -3085,14 +3151,11 @@
decoded_name->ToModifiedUtf8().c_str()));
CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name);
- mirror::SynthesizedProxyClass* synth_proxy_class =
- down_cast<mirror::SynthesizedProxyClass*>(klass.Get());
- CHECK_EQ(synth_proxy_class->GetInterfaces(),
+ CHECK_EQ(klass.Get()->GetInterfaces(),
soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces));
- CHECK_EQ(synth_proxy_class->GetThrows(),
+ CHECK_EQ(klass.Get()->GetThrows(),
soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>*>(throws));
}
- std::string descriptor(GetDescriptorForProxy(klass.Get()));
mirror::Class* existing = InsertClass(descriptor.c_str(), klass.Get(), Hash(descriptor.c_str()));
CHECK(existing == nullptr);
return klass.Get();
@@ -3526,9 +3589,49 @@
}
}
-bool ClassLinker::LinkClass(Thread* self, Handle<mirror::Class> klass,
- Handle<mirror::ObjectArray<mirror::Class>> interfaces) {
+void ClassLinker::FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class) {
+ mirror::ObjectArray<mirror::ArtField>* fields = new_class->GetIFields();
+ if (fields != nullptr) {
+ for (int index = 0; index < fields->GetLength(); index ++) {
+ if (fields->Get(index)->GetDeclaringClass() == temp_class) {
+ fields->Get(index)->SetDeclaringClass(new_class);
+ }
+ }
+ }
+
+ fields = new_class->GetSFields();
+ if (fields != nullptr) {
+ for (int index = 0; index < fields->GetLength(); index ++) {
+ if (fields->Get(index)->GetDeclaringClass() == temp_class) {
+ fields->Get(index)->SetDeclaringClass(new_class);
+ }
+ }
+ }
+
+ mirror::ObjectArray<mirror::ArtMethod>* methods = new_class->GetDirectMethods();
+ if (methods != nullptr) {
+ for (int index = 0; index < methods->GetLength(); index ++) {
+ if (methods->Get(index)->GetDeclaringClass() == temp_class) {
+ methods->Get(index)->SetDeclaringClass(new_class);
+ }
+ }
+ }
+
+ methods = new_class->GetVirtualMethods();
+ if (methods != nullptr) {
+ for (int index = 0; index < methods->GetLength(); index ++) {
+ if (methods->Get(index)->GetDeclaringClass() == temp_class) {
+ methods->Get(index)->SetDeclaringClass(new_class);
+ }
+ }
+ }
+}
+
+bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass,
+ Handle<mirror::ObjectArray<mirror::Class>> interfaces,
+ mirror::Class** new_class) {
CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus());
+
if (!LinkSuperClass(klass)) {
return false;
}
@@ -3538,13 +3641,60 @@
if (!LinkInstanceFields(klass)) {
return false;
}
- if (!LinkStaticFields(klass)) {
+ size_t class_size;
+ if (!LinkStaticFields(klass, &class_size)) {
return false;
}
CreateReferenceInstanceOffsets(klass);
CreateReferenceStaticOffsets(klass);
CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus());
- klass->SetStatus(mirror::Class::kStatusResolved, self);
+
+ if (!klass->IsTemp() || (!init_done_ && klass->GetClassSize() == class_size)) {
+ // We don't need to retire this class as it has no embedded tables or it was created the
+ // correct size during class linker initialization.
+ CHECK_EQ(klass->GetClassSize(), class_size) << PrettyDescriptor(klass.Get());
+
+ if (klass->ShouldHaveEmbeddedImtAndVTable()) {
+ klass->PopulateEmbeddedImtAndVTable();
+ }
+
+ // This will notify waiters on klass that saw the not yet resolved
+ // class in the class_table_ during EnsureResolved.
+ klass->SetStatus(mirror::Class::kStatusResolved, self);
+ *new_class = klass.Get();
+ } else {
+ CHECK(!klass->IsResolved());
+ // Retire the temporary class and create the correctly sized resolved class.
+ *new_class = klass->CopyOf(self, class_size);
+ if (UNLIKELY(*new_class == NULL)) {
+ CHECK(self->IsExceptionPending()); // Expect an OOME.
+ klass->SetStatus(mirror::Class::kStatusError, self);
+ return false;
+ }
+
+ CHECK_EQ((*new_class)->GetClassSize(), class_size);
+ StackHandleScope<1> hs(self);
+ auto new_class_h = hs.NewHandleWrapper<mirror::Class>(new_class);
+ ObjectLock<mirror::Class> lock(self, new_class_h);
+
+ FixupTemporaryDeclaringClass(klass.Get(), new_class_h.Get());
+
+ mirror::Class* existing = UpdateClass(descriptor, new_class_h.Get(), Hash(descriptor));
+ CHECK(existing == NULL || existing == klass.Get());
+
+ // This will notify waiters on temp class that saw the not yet resolved class in the
+ // class_table_ during EnsureResolved.
+ klass->SetStatus(mirror::Class::kStatusRetired, self);
+
+ CHECK_EQ(new_class_h->GetStatus(), mirror::Class::kStatusResolving);
+ // This will notify waiters on new_class that saw the not yet resolved
+ // class in the class_table_ during EnsureResolved.
+ new_class_h->SetStatus(mirror::Class::kStatusResolved, self);
+
+ // Only embedded imt should be used from this point.
+ new_class_h->SetImTable(NULL);
+ // TODO: remove vtable and only use embedded vtable.
+ }
return true;
}
@@ -3565,6 +3715,7 @@
PrettyDescriptor(klass.Get()).c_str());
return false;
}
+ CHECK(super_class->IsResolved());
klass->SetSuperClass(super_class);
}
const DexFile::TypeList* interfaces = dex_file.GetInterfacesList(class_def);
@@ -3878,7 +4029,7 @@
// Allocate imtable
bool imtable_changed = false;
Handle<mirror::ObjectArray<mirror::ArtMethod>> imtable(
- hs.NewHandle(AllocArtMethodArray(self, kImtSize)));
+ hs.NewHandle(AllocArtMethodArray(self, mirror::Class::kImtSize)));
if (UNLIKELY(imtable.Get() == NULL)) {
CHECK(self->IsExceptionPending()); // OOME.
return false;
@@ -3925,7 +4076,7 @@
}
method_array->Set<false>(j, vtable_method);
// Place method in imt if entry is empty, place conflict otherwise.
- uint32_t imt_index = interface_method->GetDexMethodIndex() % kImtSize;
+ uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
if (imtable->Get(imt_index) == NULL) {
imtable->Set<false>(imt_index, vtable_method);
imtable_changed = true;
@@ -3963,7 +4114,7 @@
if (imtable_changed) {
// Fill in empty entries in interface method table with conflict.
mirror::ArtMethod* imt_conflict_method = runtime->GetImtConflictMethod();
- for (size_t i = 0; i < kImtSize; i++) {
+ for (size_t i = 0; i < mirror::Class::kImtSize; i++) {
if (imtable->Get(i) == NULL) {
imtable->Set<false>(i, imt_conflict_method);
}
@@ -4020,15 +4171,12 @@
bool ClassLinker::LinkInstanceFields(Handle<mirror::Class> klass) {
CHECK(klass.Get() != NULL);
- return LinkFields(klass, false);
+ return LinkFields(klass, false, nullptr);
}
-bool ClassLinker::LinkStaticFields(Handle<mirror::Class> klass) {
+bool ClassLinker::LinkStaticFields(Handle<mirror::Class> klass, size_t* class_size) {
CHECK(klass.Get() != NULL);
- size_t allocated_class_size = klass->GetClassSize();
- bool success = LinkFields(klass, true);
- CHECK_EQ(allocated_class_size, klass->GetClassSize());
- return success;
+ return LinkFields(klass, true, class_size);
}
struct LinkFieldsComparator {
@@ -4058,19 +4206,23 @@
}
};
-bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static) {
+bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static, size_t* class_size) {
size_t num_fields =
is_static ? klass->NumStaticFields() : klass->NumInstanceFields();
mirror::ObjectArray<mirror::ArtField>* fields =
is_static ? klass->GetSFields() : klass->GetIFields();
- // Initialize size and field_offset
- size_t size;
+ // Initialize field_offset
MemberOffset field_offset(0);
if (is_static) {
- size = klass->GetClassSize();
- field_offset = mirror::Class::FieldsOffset();
+ uint32_t base = sizeof(mirror::Class); // Static fields come after the class.
+ if (klass->ShouldHaveEmbeddedImtAndVTable()) {
+ // Static fields come after the embedded tables.
+ base = mirror::Class::ComputeClassSize(true, klass->GetVTableDuringLinking()->GetLength(),
+ 0, 0, 0);
+ }
+ field_offset = MemberOffset(base);
} else {
mirror::Class* super_class = klass->GetSuperClass();
if (super_class != NULL) {
@@ -4078,7 +4230,6 @@
<< PrettyClass(klass.Get()) << " " << PrettyClass(super_class);
field_offset = MemberOffset(super_class->GetObjectSize());
}
- size = field_offset.Uint32Value();
}
CHECK_EQ(num_fields == 0, fields == NULL) << PrettyClass(klass.Get());
@@ -4191,11 +4342,12 @@
DCHECK_EQ(num_fields, num_reference_fields) << PrettyClass(klass.Get());
}
}
- size = field_offset.Uint32Value();
+
+ size_t size = field_offset.Uint32Value();
// Update klass
if (is_static) {
klass->SetNumReferenceStaticFields(num_reference_fields);
- klass->SetClassSize(size);
+ *class_size = size;
} else {
klass->SetNumReferenceInstanceFields(num_reference_fields);
if (!klass->IsVariableSize()) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 60dad7b..d9b3d25 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -31,6 +31,7 @@
#include "read_barrier.h"
namespace art {
+
namespace gc {
namespace space {
class ImageSpace;
@@ -56,11 +57,6 @@
class ClassLinker {
public:
- // Interface method table size. Increasing this value reduces the chance of two interface methods
- // colliding in the interface method table but increases the size of classes that implement
- // (non-marker) interfaces.
- static constexpr size_t kImtSize = 64;
-
explicit ClassLinker(InternTable* intern_table);
~ClassLinker();
@@ -385,6 +381,14 @@
// Special code to allocate an art method, use this instead of class->AllocObject.
mirror::ArtMethod* AllocArtMethod(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::ObjectArray<mirror::Class>* GetClassRoots() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ObjectArray<mirror::Class>* class_roots =
+ ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::Class>, kWithReadBarrier>(
+ &class_roots_);
+ DCHECK(class_roots != NULL);
+ return class_roots;
+ }
+
private:
const OatFile::OatMethod GetOatMethodFor(mirror::ArtMethod* method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -427,8 +431,10 @@
mirror::Class* c, SafeMap<uint32_t, mirror::ArtField*>& field_map)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- uint32_t SizeOfClass(const DexFile& dex_file,
- const DexFile::ClassDef& dex_class_def);
+ // Precomputes size needed for Class, in the case of a non-temporary class this size must be
+ // sufficient to hold all static fields.
+ uint32_t SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file,
+ const DexFile::ClassDef& dex_class_def);
void LoadClass(const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def,
@@ -481,8 +487,9 @@
mirror::Class* klass2)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool LinkClass(Thread* self, Handle<mirror::Class> klass,
- Handle<mirror::ObjectArray<mirror::Class>> interfaces)
+ bool LinkClass(Thread* self, const char* descriptor, Handle<mirror::Class> klass,
+ Handle<mirror::ObjectArray<mirror::Class>> interfaces,
+ mirror::Class** new_class)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool LinkSuperClass(Handle<mirror::Class> klass)
@@ -502,17 +509,16 @@
Handle<mirror::ObjectArray<mirror::Class>> interfaces)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool LinkStaticFields(Handle<mirror::Class> klass)
+ bool LinkStaticFields(Handle<mirror::Class> klass, size_t* class_size)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool LinkInstanceFields(Handle<mirror::Class> klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool LinkFields(Handle<mirror::Class> klass, bool is_static)
+ bool LinkFields(Handle<mirror::Class> klass, bool is_static, size_t* class_size)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class,
const DexFile& dex_file, uint32_t dex_method_index, uint32_t method_index)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
void CreateReferenceInstanceOffsets(Handle<mirror::Class> klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void CreateReferenceStaticOffsets(Handle<mirror::Class> klass)
@@ -612,11 +618,27 @@
size_t hash)
SHARED_LOCKS_REQUIRED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+ mirror::Class* UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash)
+ LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
void MoveImageClassesToClassTable() LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::Class* LookupClassFromImage(const char* descriptor)
SHARED_LOCKS_REQUIRED(Locks::mutator_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
+ // in the table to make it resolved. The thread doing resolution must notify on the class' lock
+ // when resolution has occurred. This happens in mirror::Class::SetStatus. As resolution may
+ // 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)
+ __attribute__((warn_unused_result)) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// indexes into class_roots_.
// needs to be kept in sync with class_roots_descriptors_.
enum ClassRoot {
@@ -664,14 +686,6 @@
void SetClassRoot(ClassRoot class_root, mirror::Class* klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::ObjectArray<mirror::Class>* GetClassRoots() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ObjectArray<mirror::Class>* class_roots =
- ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::Class>, kWithReadBarrier>(
- &class_roots_);
- DCHECK(class_roots != NULL);
- return class_roots;
- }
-
static const char* class_roots_descriptors_[];
const char* GetClassRootDescriptor(ClassRoot class_root) {
@@ -695,6 +709,8 @@
InternTable* intern_table_;
+ // Trampolines within the image the bounce to runtime entrypoints. Done so that there is a single
+ // patch point within the image. TODO: make these proper relocations.
const void* portable_resolution_trampoline_;
const void* quick_resolution_trampoline_;
const void* portable_imt_conflict_trampoline_;
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 04f6946..7b5a502 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -22,7 +22,7 @@
#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "dex_file.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "gc/heap.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method.h"
@@ -34,6 +34,7 @@
#include "mirror/proxy.h"
#include "mirror/reference.h"
#include "mirror/stack_trace_element.h"
+#include "mirror/string-inl.h"
#include "handle_scope-inl.h"
namespace art {
@@ -572,37 +573,6 @@
};
};
-struct ClassClassOffsets : public CheckOffsets<mirror::ClassClass> {
- ClassClassOffsets() : CheckOffsets<mirror::ClassClass>(true, "Ljava/lang/Class;") {
- // alphabetical 64-bit
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ClassClass, serialVersionUID_), "serialVersionUID"));
- };
-};
-
-struct StringClassOffsets : public CheckOffsets<mirror::StringClass> {
- StringClassOffsets() : CheckOffsets<mirror::StringClass>(true, "Ljava/lang/String;") {
- // alphabetical references
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, ASCII_), "ASCII"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, CASE_INSENSITIVE_ORDER_), "CASE_INSENSITIVE_ORDER"));
-
- // alphabetical 32-bit
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, REPLACEMENT_CHAR_), "REPLACEMENT_CHAR"));
-
- // alphabetical 64-bit
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, serialVersionUID_), "serialVersionUID"));
- };
-};
-
-struct ArtFieldClassOffsets : public CheckOffsets<mirror::ArtFieldClass> {
- ArtFieldClassOffsets() : CheckOffsets<mirror::ArtFieldClass>(true, "Ljava/lang/reflect/ArtField;") {
- };
-};
-
-struct ArtMethodClassOffsets : public CheckOffsets<mirror::ArtMethodClass> {
- ArtMethodClassOffsets() : CheckOffsets<mirror::ArtMethodClass>(true, "Ljava/lang/reflect/ArtMethod;") {
- };
-};
-
struct DexCacheOffsets : public CheckOffsets<mirror::DexCache> {
DexCacheOffsets() : CheckOffsets<mirror::DexCache>(false, "Ljava/lang/DexCache;") {
// alphabetical references
@@ -652,11 +622,6 @@
EXPECT_TRUE(DexCacheOffsets().Check());
EXPECT_TRUE(ReferenceOffsets().Check());
EXPECT_TRUE(FinalizerReferenceOffsets().Check());
-
- EXPECT_TRUE(ClassClassOffsets().Check());
- EXPECT_TRUE(StringClassOffsets().Check());
- EXPECT_TRUE(ArtFieldClassOffsets().Check());
- EXPECT_TRUE(ArtMethodClassOffsets().Check());
}
TEST_F(ClassLinkerTest, FindClassNonexistent) {
@@ -1091,4 +1056,28 @@
}
}
+TEST_F(ClassLinkerTest, ValidatePredefinedClassSizes) {
+ ScopedObjectAccess soa(Thread::Current());
+ NullHandle<mirror::ClassLoader> class_loader;
+ mirror::Class* c;
+
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Class;", class_loader);
+ EXPECT_EQ(c->GetClassSize(), mirror::Class::ClassClassSize());
+
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/Object;", class_loader);
+ EXPECT_EQ(c->GetClassSize(), mirror::Object::ClassSize());
+
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/String;", class_loader);
+ EXPECT_EQ(c->GetClassSize(), mirror::String::ClassSize());
+
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/DexCache;", class_loader);
+ EXPECT_EQ(c->GetClassSize(), mirror::DexCache::ClassSize());
+
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/reflect/ArtField;", class_loader);
+ EXPECT_EQ(c->GetClassSize(), mirror::ArtField::ClassSize());
+
+ c = class_linker_->FindClass(soa.Self(), "Ljava/lang/reflect/ArtMethod;", class_loader);
+ EXPECT_EQ(c->GetClassSize(), mirror::ArtMethod::ClassSize());
+}
+
} // namespace art
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
new file mode 100644
index 0000000..482ad47
--- /dev/null
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -0,0 +1,696 @@
+/*
+ * Copyright (C) 2012 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_ENTRYPOINTS_ENTRYPOINT_UTILS_INL_H_
+#define ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_INL_H_
+
+#include "entrypoint_utils.h"
+
+#include "class_linker-inl.h"
+#include "common_throws.h"
+#include "dex_file.h"
+#include "indirect_reference_table.h"
+#include "invoke_type.h"
+#include "jni_internal.h"
+#include "mirror/art_method.h"
+#include "mirror/array.h"
+#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
+#include "mirror/throwable.h"
+#include "object_utils.h"
+#include "handle_scope-inl.h"
+#include "thread.h"
+
+namespace art {
+
+// TODO: Fix no thread safety analysis when GCC can handle template specialization.
+template <const bool kAccessCheck>
+ALWAYS_INLINE static inline mirror::Class* CheckObjectAlloc(uint32_t type_idx,
+ mirror::ArtMethod* method,
+ Thread* self, bool* slow_path)
+ NO_THREAD_SAFETY_ANALYSIS {
+ mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx);
+ if (UNLIKELY(klass == NULL)) {
+ klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
+ *slow_path = true;
+ if (klass == NULL) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr; // Failure
+ }
+ }
+ if (kAccessCheck) {
+ if (UNLIKELY(!klass->IsInstantiable())) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;",
+ PrettyDescriptor(klass).c_str());
+ *slow_path = true;
+ return nullptr; // Failure
+ }
+ mirror::Class* referrer = method->GetDeclaringClass();
+ if (UNLIKELY(!referrer->CanAccess(klass))) {
+ ThrowIllegalAccessErrorClass(referrer, klass);
+ *slow_path = true;
+ return nullptr; // Failure
+ }
+ }
+ if (UNLIKELY(!klass->IsInitialized())) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_klass(hs.NewHandle(klass));
+ // EnsureInitialized (the class initializer) might cause a GC.
+ // may cause us to suspend meaning that another thread may try to
+ // change the allocator while we are stuck in the entrypoints of
+ // an old allocator. Also, the class initialization may fail. To
+ // handle these cases we mark the slow path boolean as true so
+ // that the caller knows to check the allocator type to see if it
+ // has changed and to null-check the return value in case the
+ // initialization fails.
+ *slow_path = true;
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_klass, true, true)) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr; // Failure
+ }
+ return h_klass.Get();
+ }
+ return klass;
+}
+
+// TODO: Fix no thread safety analysis when annotalysis is smarter.
+ALWAYS_INLINE static inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror::Class* klass,
+ Thread* self, bool* slow_path)
+ NO_THREAD_SAFETY_ANALYSIS {
+ if (UNLIKELY(!klass->IsInitialized())) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_class(hs.NewHandle(klass));
+ // EnsureInitialized (the class initializer) might cause a GC.
+ // may cause us to suspend meaning that another thread may try to
+ // change the allocator while we are stuck in the entrypoints of
+ // an old allocator. Also, the class initialization may fail. To
+ // handle these cases we mark the slow path boolean as true so
+ // that the caller knows to check the allocator type to see if it
+ // has changed and to null-check the return value in case the
+ // initialization fails.
+ *slow_path = true;
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true)) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr; // Failure
+ }
+ return h_class.Get();
+ }
+ return klass;
+}
+
+// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it
+// cannot be resolved, throw an error. If it can, use it to create an instance.
+// When verification/compiler hasn't been able to verify access, optionally perform an access
+// check.
+// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
+template <bool kAccessCheck, bool kInstrumented>
+ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx,
+ mirror::ArtMethod* method,
+ Thread* self,
+ gc::AllocatorType allocator_type)
+ NO_THREAD_SAFETY_ANALYSIS {
+ bool slow_path = false;
+ mirror::Class* klass = CheckObjectAlloc<kAccessCheck>(type_idx, method, self, &slow_path);
+ if (UNLIKELY(slow_path)) {
+ if (klass == nullptr) {
+ return nullptr;
+ }
+ return klass->Alloc<kInstrumented>(self, Runtime::Current()->GetHeap()->GetCurrentAllocator());
+ }
+ DCHECK(klass != nullptr);
+ return klass->Alloc<kInstrumented>(self, allocator_type);
+}
+
+// Given the context of a calling Method and a resolved class, create an instance.
+// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
+template <bool kInstrumented>
+ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCodeResolved(mirror::Class* klass,
+ mirror::ArtMethod* method,
+ Thread* self,
+ gc::AllocatorType allocator_type)
+ NO_THREAD_SAFETY_ANALYSIS {
+ DCHECK(klass != nullptr);
+ bool slow_path = false;
+ klass = CheckClassInitializedForObjectAlloc(klass, self, &slow_path);
+ if (UNLIKELY(slow_path)) {
+ if (klass == nullptr) {
+ return nullptr;
+ }
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ // Pass in false since the object can not be finalizable.
+ return klass->Alloc<kInstrumented, false>(self, heap->GetCurrentAllocator());
+ }
+ // Pass in false since the object can not be finalizable.
+ return klass->Alloc<kInstrumented, false>(self, allocator_type);
+}
+
+// Given the context of a calling Method and an initialized class, create an instance.
+// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
+template <bool kInstrumented>
+ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCodeInitialized(mirror::Class* klass,
+ mirror::ArtMethod* method,
+ Thread* self,
+ gc::AllocatorType allocator_type)
+ NO_THREAD_SAFETY_ANALYSIS {
+ DCHECK(klass != nullptr);
+ // Pass in false since the object can not be finalizable.
+ return klass->Alloc<kInstrumented, false>(self, allocator_type);
+}
+
+
+// TODO: Fix no thread safety analysis when GCC can handle template specialization.
+template <bool kAccessCheck>
+ALWAYS_INLINE static inline mirror::Class* CheckArrayAlloc(uint32_t type_idx,
+ mirror::ArtMethod* method,
+ int32_t component_count,
+ bool* slow_path)
+ NO_THREAD_SAFETY_ANALYSIS {
+ if (UNLIKELY(component_count < 0)) {
+ ThrowNegativeArraySizeException(component_count);
+ *slow_path = true;
+ return nullptr; // Failure
+ }
+ mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx);
+ if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve
+ klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
+ *slow_path = true;
+ if (klass == nullptr) { // Error
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return nullptr; // Failure
+ }
+ CHECK(klass->IsArrayClass()) << PrettyClass(klass);
+ }
+ if (kAccessCheck) {
+ mirror::Class* referrer = method->GetDeclaringClass();
+ if (UNLIKELY(!referrer->CanAccess(klass))) {
+ ThrowIllegalAccessErrorClass(referrer, klass);
+ *slow_path = true;
+ return nullptr; // Failure
+ }
+ }
+ return klass;
+}
+
+// Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If
+// it cannot be resolved, throw an error. If it can, use it to create an array.
+// When verification/compiler hasn't been able to verify access, optionally perform an access
+// check.
+// TODO: Fix no thread safety analysis when GCC can handle template specialization.
+template <bool kAccessCheck, bool kInstrumented>
+ALWAYS_INLINE static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx,
+ mirror::ArtMethod* method,
+ int32_t component_count,
+ Thread* self,
+ gc::AllocatorType allocator_type)
+ NO_THREAD_SAFETY_ANALYSIS {
+ bool slow_path = false;
+ mirror::Class* klass = CheckArrayAlloc<kAccessCheck>(type_idx, method, component_count,
+ &slow_path);
+ if (UNLIKELY(slow_path)) {
+ if (klass == nullptr) {
+ return nullptr;
+ }
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
+ klass->GetComponentSize(),
+ heap->GetCurrentAllocator());
+ }
+ return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
+ klass->GetComponentSize(), allocator_type);
+}
+
+template <bool kAccessCheck, bool kInstrumented>
+ALWAYS_INLINE static inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass,
+ mirror::ArtMethod* method,
+ int32_t component_count,
+ Thread* self,
+ gc::AllocatorType allocator_type)
+ NO_THREAD_SAFETY_ANALYSIS {
+ DCHECK(klass != nullptr);
+ if (UNLIKELY(component_count < 0)) {
+ ThrowNegativeArraySizeException(component_count);
+ return nullptr; // Failure
+ }
+ if (kAccessCheck) {
+ mirror::Class* referrer = method->GetDeclaringClass();
+ if (UNLIKELY(!referrer->CanAccess(klass))) {
+ ThrowIllegalAccessErrorClass(referrer, klass);
+ return nullptr; // Failure
+ }
+ }
+ // No need to retry a slow-path allocation as the above code won't cause a GC or thread
+ // suspension.
+ return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
+ klass->GetComponentSize(), allocator_type);
+}
+
+template<FindFieldType type, bool access_check>
+static inline mirror::ArtField* FindFieldFromCode(uint32_t field_idx, mirror::ArtMethod* referrer,
+ Thread* self, size_t expected_size) {
+ bool is_primitive;
+ bool is_set;
+ bool is_static;
+ switch (type) {
+ case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break;
+ case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break;
+ case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break;
+ case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break;
+ case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break;
+ case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break;
+ case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break;
+ case StaticPrimitiveWrite: // Keep GCC happy by having a default handler, fall-through.
+ default: is_primitive = true; is_set = true; is_static = true; break;
+ }
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ mirror::ArtField* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
+ if (UNLIKELY(resolved_field == nullptr)) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
+ return nullptr; // Failure.
+ }
+ mirror::Class* fields_class = resolved_field->GetDeclaringClass();
+ if (access_check) {
+ if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
+ ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer);
+ return nullptr;
+ }
+ mirror::Class* referring_class = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class, resolved_field,
+ field_idx))) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
+ return nullptr; // Failure.
+ }
+ if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
+ ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
+ return nullptr; // Failure.
+ } else {
+ if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive ||
+ resolved_field->FieldSize() != expected_size)) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ DCHECK(throw_location.GetMethod() == referrer);
+ self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
+ "Attempted read of %zd-bit %s on field '%s'",
+ expected_size * (32 / sizeof(int32_t)),
+ is_primitive ? "primitive" : "non-primitive",
+ PrettyField(resolved_field, true).c_str());
+ return nullptr; // Failure.
+ }
+ }
+ }
+ if (!is_static) {
+ // instance fields must be being accessed on an initialized class
+ return resolved_field;
+ } else {
+ // If the class is initialized we're done.
+ if (LIKELY(fields_class->IsInitialized())) {
+ return resolved_field;
+ } else {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_class(hs.NewHandle(fields_class));
+ if (LIKELY(class_linker->EnsureInitialized(h_class, true, true))) {
+ // Otherwise let's ensure the class is initialized before resolving the field.
+ return resolved_field;
+ }
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind
+ return nullptr; // Failure.
+ }
+ }
+}
+
+// Explicit template declarations of FindFieldFromCode for all field access types.
+#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+mirror::ArtField* FindFieldFromCode<_type, _access_check>(uint32_t field_idx, \
+ mirror::ArtMethod* referrer, \
+ Thread* self, size_t expected_size) \
+
+#define EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
+ EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, false); \
+ EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, true)
+
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite);
+
+#undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL
+#undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL
+
+template<InvokeType type, bool access_check>
+static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx,
+ mirror::Object** this_object,
+ mirror::ArtMethod** referrer, Thread* self) {
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ mirror::ArtMethod* resolved_method = class_linker->GetResolvedMethod(method_idx, *referrer, type);
+ if (resolved_method == nullptr) {
+ StackHandleScope<1> hs(self);
+ mirror::Object* null_this = nullptr;
+ HandleWrapper<mirror::Object> h_this(
+ hs.NewHandleWrapper(type == kStatic ? &null_this : this_object));
+ resolved_method = class_linker->ResolveMethod(self, method_idx, referrer, type);
+ }
+ if (UNLIKELY(resolved_method == nullptr)) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
+ return nullptr; // Failure.
+ } else if (UNLIKELY(*this_object == nullptr && type != kStatic)) {
+ // Maintain interpreter-like semantics where NullPointerException is thrown
+ // after potential NoSuchMethodError from class linker.
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ DCHECK_EQ(*referrer, throw_location.GetMethod());
+ ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type);
+ return nullptr; // Failure.
+ } else if (access_check) {
+ // Incompatible class change should have been handled in resolve method.
+ if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
+ ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method,
+ *referrer);
+ return nullptr; // Failure.
+ }
+ mirror::Class* methods_class = resolved_method->GetDeclaringClass();
+ mirror::Class* referring_class = (*referrer)->GetDeclaringClass();
+ bool can_access_resolved_method =
+ referring_class->CheckResolvedMethodAccess<type>(methods_class, resolved_method,
+ method_idx);
+ if (UNLIKELY(!can_access_resolved_method)) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
+ return nullptr; // Failure.
+ }
+ }
+ switch (type) {
+ case kStatic:
+ case kDirect:
+ return resolved_method;
+ case kVirtual: {
+ mirror::ObjectArray<mirror::ArtMethod>* vtable = (*this_object)->GetClass()->GetVTable();
+ uint16_t vtable_index = resolved_method->GetMethodIndex();
+ if (access_check &&
+ (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength()))) {
+ // Behavior to agree with that of the verifier.
+ ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(),
+ resolved_method->GetName(), resolved_method->GetSignature());
+ return nullptr; // Failure.
+ }
+ DCHECK(vtable != nullptr);
+ return vtable->GetWithoutChecks(vtable_index);
+ }
+ case kSuper: {
+ mirror::Class* super_class = (*referrer)->GetDeclaringClass()->GetSuperClass();
+ uint16_t vtable_index = resolved_method->GetMethodIndex();
+ mirror::ObjectArray<mirror::ArtMethod>* vtable;
+ if (access_check) {
+ // Check existence of super class.
+ vtable = (super_class != nullptr) ? super_class->GetVTable() : nullptr;
+ if (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength())) {
+ // Behavior to agree with that of the verifier.
+ ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(),
+ resolved_method->GetName(), resolved_method->GetSignature());
+ return nullptr; // Failure.
+ }
+ } else {
+ // Super class must exist.
+ DCHECK(super_class != nullptr);
+ vtable = super_class->GetVTable();
+ }
+ DCHECK(vtable != nullptr);
+ return vtable->GetWithoutChecks(vtable_index);
+ }
+ case kInterface: {
+ uint32_t imt_index = resolved_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+ mirror::ArtMethod* imt_method = (*this_object)->GetClass()->GetEmbeddedImTableEntry(imt_index);
+ if (!imt_method->IsImtConflictMethod()) {
+ return imt_method;
+ } else {
+ mirror::ArtMethod* interface_method =
+ (*this_object)->GetClass()->FindVirtualMethodForInterface(resolved_method);
+ if (UNLIKELY(interface_method == nullptr)) {
+ ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method,
+ *this_object, *referrer);
+ return nullptr; // Failure.
+ }
+ return interface_method;
+ }
+ }
+ default:
+ LOG(FATAL) << "Unknown invoke type " << type;
+ return nullptr; // Failure.
+ }
+}
+
+// Explicit template declarations of FindMethodFromCode for all invoke types.
+#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ mirror::ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \
+ mirror::Object** this_object, \
+ mirror::ArtMethod** referrer, \
+ Thread* self)
+#define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
+ EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, false); \
+ EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, true)
+
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kStatic);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kDirect);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kVirtual);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kSuper);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kInterface);
+
+#undef EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL
+#undef EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL
+
+// Fast path field resolution that can't initialize classes or throw exceptions.
+static inline mirror::ArtField* FindFieldFast(uint32_t field_idx,
+ mirror::ArtMethod* referrer,
+ FindFieldType type, size_t expected_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* resolved_field =
+ referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx);
+ if (UNLIKELY(resolved_field == nullptr)) {
+ return nullptr;
+ }
+ // Check for incompatible class change.
+ bool is_primitive;
+ bool is_set;
+ bool is_static;
+ switch (type) {
+ case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break;
+ case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break;
+ case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break;
+ case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break;
+ case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break;
+ case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break;
+ case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break;
+ case StaticPrimitiveWrite: is_primitive = true; is_set = true; is_static = true; break;
+ default:
+ LOG(FATAL) << "UNREACHABLE"; // Assignment below to avoid GCC warnings.
+ is_primitive = true;
+ is_set = true;
+ is_static = true;
+ break;
+ }
+ if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
+ // Incompatible class change.
+ return nullptr;
+ }
+ mirror::Class* fields_class = resolved_field->GetDeclaringClass();
+ if (is_static) {
+ // Check class is initialized else fail so that we can contend to initialize the class with
+ // other threads that may be racing to do this.
+ if (UNLIKELY(!fields_class->IsInitialized())) {
+ return nullptr;
+ }
+ }
+ mirror::Class* referring_class = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
+ !referring_class->CanAccessMember(fields_class,
+ resolved_field->GetAccessFlags()) ||
+ (is_set && resolved_field->IsFinal() && (fields_class != referring_class)))) {
+ // Illegal access.
+ return nullptr;
+ }
+ if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive ||
+ resolved_field->FieldSize() != expected_size)) {
+ return nullptr;
+ }
+ return resolved_field;
+}
+
+// Fast path method resolution that can't throw exceptions.
+static inline mirror::ArtMethod* FindMethodFast(uint32_t method_idx,
+ mirror::Object* this_object,
+ mirror::ArtMethod* referrer,
+ bool access_check, InvokeType type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool is_direct = type == kStatic || type == kDirect;
+ if (UNLIKELY(this_object == NULL && !is_direct)) {
+ return NULL;
+ }
+ mirror::ArtMethod* resolved_method =
+ referrer->GetDeclaringClass()->GetDexCache()->GetResolvedMethod(method_idx);
+ if (UNLIKELY(resolved_method == NULL)) {
+ return NULL;
+ }
+ if (access_check) {
+ // Check for incompatible class change errors and access.
+ bool icce = resolved_method->CheckIncompatibleClassChange(type);
+ if (UNLIKELY(icce)) {
+ return NULL;
+ }
+ mirror::Class* methods_class = resolved_method->GetDeclaringClass();
+ mirror::Class* referring_class = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
+ !referring_class->CanAccessMember(methods_class,
+ resolved_method->GetAccessFlags()))) {
+ // Potential illegal access, may need to refine the method's class.
+ return NULL;
+ }
+ }
+ if (type == kInterface) { // Most common form of slow path dispatch.
+ return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
+ } else if (is_direct) {
+ return resolved_method;
+ } else if (type == kSuper) {
+ return referrer->GetDeclaringClass()->GetSuperClass()->GetVTable()->
+ Get(resolved_method->GetMethodIndex());
+ } else {
+ DCHECK(type == kVirtual);
+ return this_object->GetClass()->GetVTable()->Get(resolved_method->GetMethodIndex());
+ }
+}
+
+static inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx,
+ mirror::ArtMethod* referrer,
+ Thread* self, bool can_run_clinit,
+ bool verify_access)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ mirror::Class* klass = class_linker->ResolveType(type_idx, referrer);
+ if (UNLIKELY(klass == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ return nullptr; // Failure - Indicate to caller to deliver exception
+ }
+ // Perform access check if necessary.
+ mirror::Class* referring_class = referrer->GetDeclaringClass();
+ if (verify_access && UNLIKELY(!referring_class->CanAccess(klass))) {
+ ThrowIllegalAccessErrorClass(referring_class, klass);
+ return nullptr; // Failure - Indicate to caller to deliver exception
+ }
+ // If we're just implementing const-class, we shouldn't call <clinit>.
+ if (!can_run_clinit) {
+ return klass;
+ }
+ // If we are the <clinit> of this class, just return our storage.
+ //
+ // Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
+ // running.
+ if (klass == referring_class && referrer->IsConstructor() && referrer->IsStatic()) {
+ return klass;
+ }
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_class(hs.NewHandle(klass));
+ if (!class_linker->EnsureInitialized(h_class, true, true)) {
+ CHECK(self->IsExceptionPending());
+ return nullptr; // Failure - Indicate to caller to deliver exception
+ }
+ return h_class.Get();
+}
+
+static inline mirror::String* ResolveStringFromCode(mirror::ArtMethod* referrer,
+ uint32_t string_idx)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ return class_linker->ResolveString(string_idx, referrer);
+}
+
+static inline void UnlockJniSynchronizedMethod(jobject locked, Thread* self)
+ NO_THREAD_SAFETY_ANALYSIS /* SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) */ {
+ // Save any pending exception over monitor exit call.
+ mirror::Throwable* saved_exception = NULL;
+ ThrowLocation saved_throw_location;
+ bool is_exception_reported = self->IsExceptionReportedToInstrumentation();
+ if (UNLIKELY(self->IsExceptionPending())) {
+ saved_exception = self->GetException(&saved_throw_location);
+ self->ClearException();
+ }
+ // Decode locked object and unlock, before popping local references.
+ self->DecodeJObject(locked)->MonitorExit(self);
+ if (UNLIKELY(self->IsExceptionPending())) {
+ LOG(FATAL) << "Synchronized JNI code returning with an exception:\n"
+ << saved_exception->Dump()
+ << "\nEncountered second exception during implicit MonitorExit:\n"
+ << self->GetException(NULL)->Dump();
+ }
+ // Restore pending exception.
+ if (saved_exception != NULL) {
+ self->SetException(saved_throw_location, saved_exception);
+ self->SetExceptionReportedToInstrumentation(is_exception_reported);
+ }
+}
+
+static inline void CheckReferenceResult(mirror::Object* o, Thread* self)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (o == NULL) {
+ return;
+ }
+ mirror::ArtMethod* m = self->GetCurrentMethod(NULL);
+ if (o == kInvalidIndirectRefObject) {
+ JniAbortF(NULL, "invalid reference returned from %s", PrettyMethod(m).c_str());
+ }
+ // Make sure that the result is an instance of the type this method was expected to return.
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ArtMethod> h_m(hs.NewHandle(m));
+ mirror::Class* return_type = MethodHelper(h_m).GetReturnType();
+
+ if (!o->InstanceOf(return_type)) {
+ JniAbortF(NULL, "attempt to return an instance of %s from %s", PrettyTypeOf(o).c_str(),
+ PrettyMethod(h_m.Get()).c_str());
+ }
+}
+
+static inline void CheckSuspend(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ for (;;) {
+ if (thread->ReadFlag(kCheckpointRequest)) {
+ thread->RunCheckpointFunction();
+ } else if (thread->ReadFlag(kSuspendRequest)) {
+ thread->FullSuspendCheck();
+ } else {
+ break;
+ }
+ }
+}
+
+template <typename INT_TYPE, typename FLOAT_TYPE>
+static inline INT_TYPE art_float_to_integral(FLOAT_TYPE f) {
+ const INT_TYPE kMaxInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::max());
+ const INT_TYPE kMinInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::min());
+ const FLOAT_TYPE kMaxIntAsFloat = static_cast<FLOAT_TYPE>(kMaxInt);
+ const FLOAT_TYPE kMinIntAsFloat = static_cast<FLOAT_TYPE>(kMinInt);
+ if (LIKELY(f > kMinIntAsFloat)) {
+ if (LIKELY(f < kMaxIntAsFloat)) {
+ return static_cast<INT_TYPE>(f);
+ } else {
+ return kMaxInt;
+ }
+ } else {
+ return (f != f) ? 0 : kMinInt; // f != f implies NaN
+ }
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_INL_H_
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index a0e32f5..d029df2 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -16,6 +16,7 @@
#include "entrypoints/entrypoint_utils.h"
+#include "base/mutex.h"
#include "class_linker-inl.h"
#include "dex_file-inl.h"
#include "gc/accounting/card_table-inl.h"
@@ -25,7 +26,6 @@
#include "mirror/object-inl.h"
#include "object_utils.h"
#include "mirror/object_array-inl.h"
-#include "mirror/proxy.h"
#include "reflection.h"
#include "scoped_thread_state_change.h"
#include "ScopedLocalRef.h"
@@ -219,8 +219,7 @@
mirror::Throwable* exception = soa.Self()->GetException(NULL);
if (exception->IsCheckedException()) {
mirror::Object* rcvr = soa.Decode<mirror::Object*>(rcvr_jobj);
- mirror::SynthesizedProxyClass* proxy_class =
- down_cast<mirror::SynthesizedProxyClass*>(rcvr->GetClass());
+ mirror::Class* proxy_class = rcvr->GetClass();
mirror::ArtMethod* interface_method =
soa.Decode<mirror::ArtMethod*>(interface_method_jobj);
mirror::ArtMethod* proxy_method =
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index ff836a4..11a67ac 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -17,105 +17,40 @@
#ifndef ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_
#define ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_
+#include <jni.h>
+#include <stdint.h>
+
#include "base/macros.h"
-#include "class_linker-inl.h"
-#include "common_throws.h"
-#include "dex_file.h"
-#include "indirect_reference_table.h"
+#include "base/mutex.h"
+#include "gc/allocator_type.h"
#include "invoke_type.h"
-#include "jni_internal.h"
-#include "mirror/art_method.h"
-#include "mirror/array.h"
-#include "mirror/class-inl.h"
-#include "mirror/object-inl.h"
-#include "mirror/throwable.h"
-#include "object_utils.h"
-#include "handle_scope-inl.h"
-#include "thread.h"
+#include "jvalue.h"
namespace art {
namespace mirror {
class Class;
+ class Array;
class ArtField;
+ class ArtMethod;
class Object;
+ class String;
} // namespace mirror
+class ScopedObjectAccessAlreadyRunnable;
+class Thread;
+
// TODO: Fix no thread safety analysis when GCC can handle template specialization.
template <const bool kAccessCheck>
ALWAYS_INLINE static inline mirror::Class* CheckObjectAlloc(uint32_t type_idx,
mirror::ArtMethod* method,
Thread* self, bool* slow_path)
- NO_THREAD_SAFETY_ANALYSIS {
- mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx);
- if (UNLIKELY(klass == NULL)) {
- klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
- *slow_path = true;
- if (klass == NULL) {
- DCHECK(self->IsExceptionPending());
- return nullptr; // Failure
- }
- }
- if (kAccessCheck) {
- if (UNLIKELY(!klass->IsInstantiable())) {
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;",
- PrettyDescriptor(klass).c_str());
- *slow_path = true;
- return nullptr; // Failure
- }
- mirror::Class* referrer = method->GetDeclaringClass();
- if (UNLIKELY(!referrer->CanAccess(klass))) {
- ThrowIllegalAccessErrorClass(referrer, klass);
- *slow_path = true;
- return nullptr; // Failure
- }
- }
- if (UNLIKELY(!klass->IsInitialized())) {
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_klass(hs.NewHandle(klass));
- // EnsureInitialized (the class initializer) might cause a GC.
- // may cause us to suspend meaning that another thread may try to
- // change the allocator while we are stuck in the entrypoints of
- // an old allocator. Also, the class initialization may fail. To
- // handle these cases we mark the slow path boolean as true so
- // that the caller knows to check the allocator type to see if it
- // has changed and to null-check the return value in case the
- // initialization fails.
- *slow_path = true;
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_klass, true, true)) {
- DCHECK(self->IsExceptionPending());
- return nullptr; // Failure
- }
- return h_klass.Get();
- }
- return klass;
-}
+ NO_THREAD_SAFETY_ANALYSIS;
// TODO: Fix no thread safety analysis when annotalysis is smarter.
ALWAYS_INLINE static inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror::Class* klass,
Thread* self, bool* slow_path)
- NO_THREAD_SAFETY_ANALYSIS {
- if (UNLIKELY(!klass->IsInitialized())) {
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(klass));
- // EnsureInitialized (the class initializer) might cause a GC.
- // may cause us to suspend meaning that another thread may try to
- // change the allocator while we are stuck in the entrypoints of
- // an old allocator. Also, the class initialization may fail. To
- // handle these cases we mark the slow path boolean as true so
- // that the caller knows to check the allocator type to see if it
- // has changed and to null-check the return value in case the
- // initialization fails.
- *slow_path = true;
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true)) {
- DCHECK(self->IsExceptionPending());
- return nullptr; // Failure
- }
- return h_class.Get();
- }
- return klass;
-}
+ NO_THREAD_SAFETY_ANALYSIS;
// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it
// cannot be resolved, throw an error. If it can, use it to create an instance.
@@ -126,19 +61,7 @@
ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx,
mirror::ArtMethod* method,
Thread* self,
- gc::AllocatorType allocator_type)
- NO_THREAD_SAFETY_ANALYSIS {
- bool slow_path = false;
- mirror::Class* klass = CheckObjectAlloc<kAccessCheck>(type_idx, method, self, &slow_path);
- if (UNLIKELY(slow_path)) {
- if (klass == nullptr) {
- return nullptr;
- }
- return klass->Alloc<kInstrumented>(self, Runtime::Current()->GetHeap()->GetCurrentAllocator());
- }
- DCHECK(klass != nullptr);
- return klass->Alloc<kInstrumented>(self, allocator_type);
-}
+ gc::AllocatorType allocator_type);
// Given the context of a calling Method and a resolved class, create an instance.
// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
@@ -147,21 +70,7 @@
mirror::ArtMethod* method,
Thread* self,
gc::AllocatorType allocator_type)
- NO_THREAD_SAFETY_ANALYSIS {
- DCHECK(klass != nullptr);
- bool slow_path = false;
- klass = CheckClassInitializedForObjectAlloc(klass, self, &slow_path);
- if (UNLIKELY(slow_path)) {
- if (klass == nullptr) {
- return nullptr;
- }
- gc::Heap* heap = Runtime::Current()->GetHeap();
- // Pass in false since the object can not be finalizable.
- return klass->Alloc<kInstrumented, false>(self, heap->GetCurrentAllocator());
- }
- // Pass in false since the object can not be finalizable.
- return klass->Alloc<kInstrumented, false>(self, allocator_type);
-}
+ NO_THREAD_SAFETY_ANALYSIS;
// Given the context of a calling Method and an initialized class, create an instance.
// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
@@ -169,12 +78,7 @@
ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCodeInitialized(mirror::Class* klass,
mirror::ArtMethod* method,
Thread* self,
- gc::AllocatorType allocator_type)
- NO_THREAD_SAFETY_ANALYSIS {
- DCHECK(klass != nullptr);
- // Pass in false since the object can not be finalizable.
- return klass->Alloc<kInstrumented, false>(self, allocator_type);
-}
+ gc::AllocatorType allocator_type);
// TODO: Fix no thread safety analysis when GCC can handle template specialization.
@@ -183,32 +87,7 @@
mirror::ArtMethod* method,
int32_t component_count,
bool* slow_path)
- NO_THREAD_SAFETY_ANALYSIS {
- if (UNLIKELY(component_count < 0)) {
- ThrowNegativeArraySizeException(component_count);
- *slow_path = true;
- return nullptr; // Failure
- }
- mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx);
- if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve
- klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
- *slow_path = true;
- if (klass == nullptr) { // Error
- DCHECK(Thread::Current()->IsExceptionPending());
- return nullptr; // Failure
- }
- CHECK(klass->IsArrayClass()) << PrettyClass(klass);
- }
- if (kAccessCheck) {
- mirror::Class* referrer = method->GetDeclaringClass();
- if (UNLIKELY(!referrer->CanAccess(klass))) {
- ThrowIllegalAccessErrorClass(referrer, klass);
- *slow_path = true;
- return nullptr; // Failure
- }
- }
- return klass;
-}
+ NO_THREAD_SAFETY_ANALYSIS;
// Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If
// it cannot be resolved, throw an error. If it can, use it to create an array.
@@ -221,22 +100,7 @@
int32_t component_count,
Thread* self,
gc::AllocatorType allocator_type)
- NO_THREAD_SAFETY_ANALYSIS {
- bool slow_path = false;
- mirror::Class* klass = CheckArrayAlloc<kAccessCheck>(type_idx, method, component_count,
- &slow_path);
- if (UNLIKELY(slow_path)) {
- if (klass == nullptr) {
- return nullptr;
- }
- gc::Heap* heap = Runtime::Current()->GetHeap();
- return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
- klass->GetComponentSize(),
- heap->GetCurrentAllocator());
- }
- return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
- klass->GetComponentSize(), allocator_type);
-}
+ NO_THREAD_SAFETY_ANALYSIS;
template <bool kAccessCheck, bool kInstrumented>
ALWAYS_INLINE static inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass,
@@ -244,24 +108,7 @@
int32_t component_count,
Thread* self,
gc::AllocatorType allocator_type)
- NO_THREAD_SAFETY_ANALYSIS {
- DCHECK(klass != nullptr);
- if (UNLIKELY(component_count < 0)) {
- ThrowNegativeArraySizeException(component_count);
- return nullptr; // Failure
- }
- if (kAccessCheck) {
- mirror::Class* referrer = method->GetDeclaringClass();
- if (UNLIKELY(!referrer->CanAccess(klass))) {
- ThrowIllegalAccessErrorClass(referrer, klass);
- return nullptr; // Failure
- }
- }
- // No need to retry a slow-path allocation as the above code won't cause a GC or thread
- // suspension.
- return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
- klass->GetComponentSize(), allocator_type);
-}
+ NO_THREAD_SAFETY_ANALYSIS;
extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method,
int32_t component_count, Thread* self,
@@ -290,422 +137,47 @@
template<FindFieldType type, bool access_check>
static inline mirror::ArtField* FindFieldFromCode(uint32_t field_idx, mirror::ArtMethod* referrer,
- Thread* self, size_t expected_size) {
- bool is_primitive;
- bool is_set;
- bool is_static;
- switch (type) {
- case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break;
- case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break;
- case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break;
- case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break;
- case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break;
- case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break;
- case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break;
- case StaticPrimitiveWrite: // Keep GCC happy by having a default handler, fall-through.
- default: is_primitive = true; is_set = true; is_static = true; break;
- }
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- mirror::ArtField* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
- if (UNLIKELY(resolved_field == nullptr)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return nullptr; // Failure.
- }
- mirror::Class* fields_class = resolved_field->GetDeclaringClass();
- if (access_check) {
- if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
- ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer);
- return nullptr;
- }
- mirror::Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class, resolved_field,
- field_idx))) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return nullptr; // Failure.
- }
- if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
- ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
- return nullptr; // Failure.
- } else {
- if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive ||
- resolved_field->FieldSize() != expected_size)) {
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- DCHECK(throw_location.GetMethod() == referrer);
- self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
- "Attempted read of %zd-bit %s on field '%s'",
- expected_size * (32 / sizeof(int32_t)),
- is_primitive ? "primitive" : "non-primitive",
- PrettyField(resolved_field, true).c_str());
- return nullptr; // Failure.
- }
- }
- }
- if (!is_static) {
- // instance fields must be being accessed on an initialized class
- return resolved_field;
- } else {
- // If the class is initialized we're done.
- if (LIKELY(fields_class->IsInitialized())) {
- return resolved_field;
- } else {
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(fields_class));
- if (LIKELY(class_linker->EnsureInitialized(h_class, true, true))) {
- // Otherwise let's ensure the class is initialized before resolving the field.
- return resolved_field;
- }
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind
- return nullptr; // Failure.
- }
- }
-}
-
-// Explicit template declarations of FindFieldFromCode for all field access types.
-#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
-template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
-mirror::ArtField* FindFieldFromCode<_type, _access_check>(uint32_t field_idx, \
- mirror::ArtMethod* referrer, \
- Thread* self, size_t expected_size) \
-
-#define EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
- EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, false); \
- EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, true)
-
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite);
-
-#undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL
-#undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL
+ Thread* self, size_t expected_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<InvokeType type, bool access_check>
static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx,
mirror::Object** this_object,
- mirror::ArtMethod** referrer, Thread* self) {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- mirror::ArtMethod* resolved_method = class_linker->GetResolvedMethod(method_idx, *referrer, type);
- if (resolved_method == nullptr) {
- StackHandleScope<1> hs(self);
- mirror::Object* null_this = nullptr;
- HandleWrapper<mirror::Object> h_this(
- hs.NewHandleWrapper(type == kStatic ? &null_this : this_object));
- resolved_method = class_linker->ResolveMethod(self, method_idx, referrer, type);
- }
- if (UNLIKELY(resolved_method == nullptr)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return nullptr; // Failure.
- } else if (UNLIKELY(*this_object == nullptr && type != kStatic)) {
- // Maintain interpreter-like semantics where NullPointerException is thrown
- // after potential NoSuchMethodError from class linker.
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- DCHECK_EQ(*referrer, throw_location.GetMethod());
- ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type);
- return nullptr; // Failure.
- } else if (access_check) {
- // Incompatible class change should have been handled in resolve method.
- if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
- ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method,
- *referrer);
- return nullptr; // Failure.
- }
- mirror::Class* methods_class = resolved_method->GetDeclaringClass();
- mirror::Class* referring_class = (*referrer)->GetDeclaringClass();
- bool can_access_resolved_method =
- referring_class->CheckResolvedMethodAccess<type>(methods_class, resolved_method,
- method_idx);
- if (UNLIKELY(!can_access_resolved_method)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return nullptr; // Failure.
- }
- }
- switch (type) {
- case kStatic:
- case kDirect:
- return resolved_method;
- case kVirtual: {
- mirror::ObjectArray<mirror::ArtMethod>* vtable = (*this_object)->GetClass()->GetVTable();
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- if (access_check &&
- (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength()))) {
- // Behavior to agree with that of the verifier.
- ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(),
- resolved_method->GetName(), resolved_method->GetSignature());
- return nullptr; // Failure.
- }
- DCHECK(vtable != nullptr);
- return vtable->GetWithoutChecks(vtable_index);
- }
- case kSuper: {
- mirror::Class* super_class = (*referrer)->GetDeclaringClass()->GetSuperClass();
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- mirror::ObjectArray<mirror::ArtMethod>* vtable;
- if (access_check) {
- // Check existence of super class.
- vtable = (super_class != nullptr) ? super_class->GetVTable() : nullptr;
- if (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength())) {
- // Behavior to agree with that of the verifier.
- ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(),
- resolved_method->GetName(), resolved_method->GetSignature());
- return nullptr; // Failure.
- }
- } else {
- // Super class must exist.
- DCHECK(super_class != nullptr);
- vtable = super_class->GetVTable();
- }
- DCHECK(vtable != nullptr);
- return vtable->GetWithoutChecks(vtable_index);
- }
- case kInterface: {
- uint32_t imt_index = resolved_method->GetDexMethodIndex() % ClassLinker::kImtSize;
- mirror::ObjectArray<mirror::ArtMethod>* imt_table = (*this_object)->GetClass()->GetImTable();
- mirror::ArtMethod* imt_method = imt_table->Get(imt_index);
- if (!imt_method->IsImtConflictMethod()) {
- return imt_method;
- } else {
- mirror::ArtMethod* interface_method =
- (*this_object)->GetClass()->FindVirtualMethodForInterface(resolved_method);
- if (UNLIKELY(interface_method == nullptr)) {
- ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method,
- *this_object, *referrer);
- return nullptr; // Failure.
- }
- return interface_method;
- }
- }
- default:
- LOG(FATAL) << "Unknown invoke type " << type;
- return nullptr; // Failure.
- }
-}
-
-// Explicit template declarations of FindMethodFromCode for all invoke types.
-#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
- template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
- mirror::ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \
- mirror::Object** this_object, \
- mirror::ArtMethod** referrer, \
- Thread* self)
-#define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
- EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, false); \
- EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, true)
-
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kStatic);
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kDirect);
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kVirtual);
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kSuper);
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kInterface);
-
-#undef EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL
-#undef EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL
+ mirror::ArtMethod** referrer, Thread* self)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Fast path field resolution that can't initialize classes or throw exceptions.
static inline mirror::ArtField* FindFieldFast(uint32_t field_idx,
mirror::ArtMethod* referrer,
FindFieldType type, size_t expected_size)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtField* resolved_field =
- referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx);
- if (UNLIKELY(resolved_field == nullptr)) {
- return nullptr;
- }
- // Check for incompatible class change.
- bool is_primitive;
- bool is_set;
- bool is_static;
- switch (type) {
- case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break;
- case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break;
- case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break;
- case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break;
- case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break;
- case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break;
- case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break;
- case StaticPrimitiveWrite: is_primitive = true; is_set = true; is_static = true; break;
- default:
- LOG(FATAL) << "UNREACHABLE"; // Assignment below to avoid GCC warnings.
- is_primitive = true;
- is_set = true;
- is_static = true;
- break;
- }
- if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
- // Incompatible class change.
- return nullptr;
- }
- mirror::Class* fields_class = resolved_field->GetDeclaringClass();
- if (is_static) {
- // Check class is initialized else fail so that we can contend to initialize the class with
- // other threads that may be racing to do this.
- if (UNLIKELY(!fields_class->IsInitialized())) {
- return nullptr;
- }
- }
- mirror::Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
- !referring_class->CanAccessMember(fields_class,
- resolved_field->GetAccessFlags()) ||
- (is_set && resolved_field->IsFinal() && (fields_class != referring_class)))) {
- // Illegal access.
- return nullptr;
- }
- if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive ||
- resolved_field->FieldSize() != expected_size)) {
- return nullptr;
- }
- return resolved_field;
-}
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Fast path method resolution that can't throw exceptions.
static inline mirror::ArtMethod* FindMethodFast(uint32_t method_idx,
mirror::Object* this_object,
mirror::ArtMethod* referrer,
bool access_check, InvokeType type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- bool is_direct = type == kStatic || type == kDirect;
- if (UNLIKELY(this_object == NULL && !is_direct)) {
- return NULL;
- }
- mirror::ArtMethod* resolved_method =
- referrer->GetDeclaringClass()->GetDexCache()->GetResolvedMethod(method_idx);
- if (UNLIKELY(resolved_method == NULL)) {
- return NULL;
- }
- if (access_check) {
- // Check for incompatible class change errors and access.
- bool icce = resolved_method->CheckIncompatibleClassChange(type);
- if (UNLIKELY(icce)) {
- return NULL;
- }
- mirror::Class* methods_class = resolved_method->GetDeclaringClass();
- mirror::Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
- !referring_class->CanAccessMember(methods_class,
- resolved_method->GetAccessFlags()))) {
- // Potential illegal access, may need to refine the method's class.
- return NULL;
- }
- }
- if (type == kInterface) { // Most common form of slow path dispatch.
- return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
- } else if (is_direct) {
- return resolved_method;
- } else if (type == kSuper) {
- return referrer->GetDeclaringClass()->GetSuperClass()->GetVTable()->
- Get(resolved_method->GetMethodIndex());
- } else {
- DCHECK(type == kVirtual);
- return this_object->GetClass()->GetVTable()->Get(resolved_method->GetMethodIndex());
- }
-}
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx,
mirror::ArtMethod* referrer,
Thread* self, bool can_run_clinit,
bool verify_access)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- mirror::Class* klass = class_linker->ResolveType(type_idx, referrer);
- if (UNLIKELY(klass == nullptr)) {
- CHECK(self->IsExceptionPending());
- return nullptr; // Failure - Indicate to caller to deliver exception
- }
- // Perform access check if necessary.
- mirror::Class* referring_class = referrer->GetDeclaringClass();
- if (verify_access && UNLIKELY(!referring_class->CanAccess(klass))) {
- ThrowIllegalAccessErrorClass(referring_class, klass);
- return nullptr; // Failure - Indicate to caller to deliver exception
- }
- // If we're just implementing const-class, we shouldn't call <clinit>.
- if (!can_run_clinit) {
- return klass;
- }
- // If we are the <clinit> of this class, just return our storage.
- //
- // Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
- // running.
- if (klass == referring_class && referrer->IsConstructor() && referrer->IsStatic()) {
- return klass;
- }
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(klass));
- if (!class_linker->EnsureInitialized(h_class, true, true)) {
- CHECK(self->IsExceptionPending());
- return nullptr; // Failure - Indicate to caller to deliver exception
- }
- return h_class.Get();
-}
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
extern void ThrowStackOverflowError(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static inline mirror::String* ResolveStringFromCode(mirror::ArtMethod* referrer,
uint32_t string_idx)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- return class_linker->ResolveString(string_idx, referrer);
-}
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static inline void UnlockJniSynchronizedMethod(jobject locked, Thread* self)
- NO_THREAD_SAFETY_ANALYSIS /* SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) */ {
- // Save any pending exception over monitor exit call.
- mirror::Throwable* saved_exception = NULL;
- ThrowLocation saved_throw_location;
- bool is_exception_reported = self->IsExceptionReportedToInstrumentation();
- if (UNLIKELY(self->IsExceptionPending())) {
- saved_exception = self->GetException(&saved_throw_location);
- self->ClearException();
- }
- // Decode locked object and unlock, before popping local references.
- self->DecodeJObject(locked)->MonitorExit(self);
- if (UNLIKELY(self->IsExceptionPending())) {
- LOG(FATAL) << "Synchronized JNI code returning with an exception:\n"
- << saved_exception->Dump()
- << "\nEncountered second exception during implicit MonitorExit:\n"
- << self->GetException(NULL)->Dump();
- }
- // Restore pending exception.
- if (saved_exception != NULL) {
- self->SetException(saved_throw_location, saved_exception);
- self->SetExceptionReportedToInstrumentation(is_exception_reported);
- }
-}
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static inline void CheckReferenceResult(mirror::Object* o, Thread* self)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (o == NULL) {
- return;
- }
- mirror::ArtMethod* m = self->GetCurrentMethod(NULL);
- if (o == kInvalidIndirectRefObject) {
- JniAbortF(NULL, "invalid reference returned from %s", PrettyMethod(m).c_str());
- }
- // Make sure that the result is an instance of the type this method was expected to return.
- StackHandleScope<1> hs(self);
- Handle<mirror::ArtMethod> h_m(hs.NewHandle(m));
- mirror::Class* return_type = MethodHelper(h_m).GetReturnType();
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- if (!o->InstanceOf(return_type)) {
- JniAbortF(NULL, "attempt to return an instance of %s from %s", PrettyTypeOf(o).c_str(),
- PrettyMethod(h_m.Get()).c_str());
- }
-}
-
-static inline void CheckSuspend(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- for (;;) {
- if (thread->ReadFlag(kCheckpointRequest)) {
- thread->RunCheckpointFunction();
- } else if (thread->ReadFlag(kSuspendRequest)) {
- thread->FullSuspendCheck();
- } else {
- break;
- }
- }
-}
+static inline void CheckSuspend(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty,
jobject rcvr_jobj, jobject interface_art_method_jobj,
@@ -750,26 +222,6 @@
return GetQuickToInterpreterBridge();
}
-static inline const void* GetPortableResolutionTrampoline(ClassLinker* class_linker) {
- return class_linker->GetPortableResolutionTrampoline();
-}
-
-static inline const void* GetQuickResolutionTrampoline(ClassLinker* class_linker) {
- return class_linker->GetQuickResolutionTrampoline();
-}
-
-static inline const void* GetPortableImtConflictTrampoline(ClassLinker* class_linker) {
- return class_linker->GetPortableImtConflictTrampoline();
-}
-
-static inline const void* GetQuickImtConflictTrampoline(ClassLinker* class_linker) {
- return class_linker->GetQuickImtConflictTrampoline();
-}
-
-static inline const void* GetQuickToInterpreterBridgeTrampoline(ClassLinker* class_linker) {
- return class_linker->GetQuickToInterpreterBridgeTrampoline();
-}
-
extern "C" void art_portable_proxy_invoke_handler();
static inline const void* GetPortableProxyInvokeHandler() {
return reinterpret_cast<void*>(art_portable_proxy_invoke_handler);
@@ -786,21 +238,7 @@
}
template <typename INT_TYPE, typename FLOAT_TYPE>
-static inline INT_TYPE art_float_to_integral(FLOAT_TYPE f) {
- const INT_TYPE kMaxInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::max());
- const INT_TYPE kMinInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::min());
- const FLOAT_TYPE kMaxIntAsFloat = static_cast<FLOAT_TYPE>(kMaxInt);
- const FLOAT_TYPE kMinIntAsFloat = static_cast<FLOAT_TYPE>(kMinInt);
- if (LIKELY(f > kMinIntAsFloat)) {
- if (LIKELY(f < kMaxIntAsFloat)) {
- return static_cast<INT_TYPE>(f);
- } else {
- return kMaxInt;
- }
- } else {
- return (f != f) ? 0 : kMinInt; // f != f implies NaN
- }
-}
+static inline INT_TYPE art_float_to_integral(FLOAT_TYPE f);
} // namespace art
diff --git a/runtime/entrypoints/math_entrypoints.cc b/runtime/entrypoints/math_entrypoints.cc
index b839b63..b0eaf1e 100644
--- a/runtime/entrypoints/math_entrypoints.cc
+++ b/runtime/entrypoints/math_entrypoints.cc
@@ -16,7 +16,7 @@
#include "math_entrypoints.h"
-#include "entrypoint_utils.h"
+#include "entrypoint_utils-inl.h"
namespace art {
diff --git a/runtime/entrypoints/portable/portable_alloc_entrypoints.cc b/runtime/entrypoints/portable/portable_alloc_entrypoints.cc
index 4c05e75..de95f7d 100644
--- a/runtime/entrypoints/portable/portable_alloc_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_alloc_entrypoints.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/object-inl.h"
diff --git a/runtime/entrypoints/portable/portable_cast_entrypoints.cc b/runtime/entrypoints/portable/portable_cast_entrypoints.cc
index a553a22..151b178 100644
--- a/runtime/entrypoints/portable/portable_cast_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_cast_entrypoints.cc
@@ -15,7 +15,7 @@
*/
#include "common_throws.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/object-inl.h"
namespace art {
diff --git a/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc b/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc
index b37ebcf..9364c46 100644
--- a/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_dexcache_entrypoints.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/object-inl.h"
diff --git a/runtime/entrypoints/portable/portable_field_entrypoints.cc b/runtime/entrypoints/portable/portable_field_entrypoints.cc
index f48f1a9..371aca4 100644
--- a/runtime/entrypoints/portable/portable_field_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_field_entrypoints.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/object-inl.h"
diff --git a/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc b/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc
index 335a617..686954b 100644
--- a/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_fillarray_entrypoints.cc
@@ -15,7 +15,7 @@
*/
#include "dex_instruction.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/object-inl.h"
diff --git a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
index eb50ec3..6f9c083 100644
--- a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/object-inl.h"
diff --git a/runtime/entrypoints/portable/portable_jni_entrypoints.cc b/runtime/entrypoints/portable/portable_jni_entrypoints.cc
index 3e7b30a..0d0f21b 100644
--- a/runtime/entrypoints/portable/portable_jni_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_jni_entrypoints.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/object-inl.h"
#include "thread-inl.h"
diff --git a/runtime/entrypoints/portable/portable_lock_entrypoints.cc b/runtime/entrypoints/portable/portable_lock_entrypoints.cc
index 358ac23..fcd3e9d 100644
--- a/runtime/entrypoints/portable/portable_lock_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_lock_entrypoints.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/object-inl.h"
namespace art {
diff --git a/runtime/entrypoints/portable/portable_thread_entrypoints.cc b/runtime/entrypoints/portable/portable_thread_entrypoints.cc
index 9e62e0e..23e1c36 100644
--- a/runtime/entrypoints/portable/portable_thread_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_thread_entrypoints.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/art_method.h"
#include "mirror/object-inl.h"
#include "verifier/dex_gc_map.h"
diff --git a/runtime/entrypoints/portable/portable_throw_entrypoints.cc b/runtime/entrypoints/portable/portable_throw_entrypoints.cc
index 189e6b5..9e36a05 100644
--- a/runtime/entrypoints/portable/portable_throw_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_throw_entrypoints.cc
@@ -15,7 +15,7 @@
*/
#include "dex_instruction.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/object-inl.h"
diff --git a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
index 2da016f..7ee869b 100644
--- a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
@@ -18,7 +18,7 @@
#define ART_RUNTIME_ENTRYPOINTS_PORTABLE_PORTABLE_ARGUMENT_VISITOR_H_
#include "dex_instruction-inl.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "interpreter/interpreter.h"
#include "mirror/art_method-inl.h"
#include "mirror/object-inl.h"
@@ -431,7 +431,7 @@
// Expect class to at least be initializing.
DCHECK(called->GetDeclaringClass()->IsInitializing());
// Don't want infinite recursion.
- DCHECK(code != GetPortableResolutionTrampoline(linker));
+ DCHECK(code != linker->GetPortableResolutionTrampoline());
// Set up entry into main method
*called_addr = called;
}
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index dde74de..1f2713a 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -17,7 +17,7 @@
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "callee_save_frame.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
#include "mirror/object_array-inl.h"
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 53c9b97..704db05 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -15,7 +15,7 @@
*/
#include "callee_save_frame.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "class_linker-inl.h"
#include "dex_file-inl.h"
#include "gc/accounting/card_table-inl.h"
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index 5cb0f36..cd1e247 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -16,7 +16,7 @@
#include "callee_save_frame.h"
#include "dex_file-inl.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
diff --git a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
index 2edcb78..9a22c15 100644
--- a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
@@ -38,7 +38,7 @@
} else {
result = instrumentation->GetQuickCodeFor(method);
}
- DCHECK(result != GetQuickToInterpreterBridgeTrampoline(Runtime::Current()->GetClassLinker()));
+ DCHECK(result != Runtime::Current()->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline());
bool interpreter_entry = (result == GetQuickToInterpreterBridge());
instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? nullptr : this_object,
method, lr, interpreter_entry);
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index 140b075..30e8609 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -15,7 +15,7 @@
*/
#include "dex_file-inl.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
#include "mirror/object.h"
diff --git a/runtime/entrypoints/quick/quick_thread_entrypoints.cc b/runtime/entrypoints/quick/quick_thread_entrypoints.cc
index 5c48fc7..118cd7f 100644
--- a/runtime/entrypoints/quick/quick_thread_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_thread_entrypoints.cc
@@ -15,7 +15,7 @@
*/
#include "callee_save_frame.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "thread.h"
#include "thread_list.h"
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index e6f294a..4dcb1c8 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -16,7 +16,7 @@
#include "callee_save_frame.h"
#include "common_throws.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "mirror/object-inl.h"
#include "object_utils.h"
#include "thread.h"
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 2a66f2f..f7cb126 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -18,7 +18,7 @@
#include "common_throws.h"
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "instruction_set.h"
#include "interpreter/interpreter.h"
diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h
index ad0a4f43..46b9363 100644
--- a/runtime/gc/accounting/card_table-inl.h
+++ b/runtime/gc/accounting/card_table-inl.h
@@ -50,8 +50,9 @@
template <typename Visitor>
inline size_t CardTable::Scan(ContinuousSpaceBitmap* bitmap, byte* scan_begin, byte* scan_end,
const Visitor& visitor, const byte minimum_age) const {
- DCHECK(bitmap->HasAddress(scan_begin));
- DCHECK(bitmap->HasAddress(scan_end - 1)); // scan_end is the byte after the last byte we scan.
+ DCHECK_GE(scan_begin, reinterpret_cast<byte*>(bitmap->HeapBegin()));
+ // scan_end is the byte after the last byte we scan.
+ DCHECK_LE(scan_end, reinterpret_cast<byte*>(bitmap->HeapLimit()));
byte* card_cur = CardFromAddr(scan_begin);
byte* card_end = CardFromAddr(scan_end);
CheckCardValid(card_cur);
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index c66e80d..ad22a2e 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -68,9 +68,9 @@
<< ", capacity=" << std::dec << capacity_
<< ", max_capacity=" << std::dec << max_capacity_;
for (size_t i = 0; i < kNumOfSizeBrackets; i++) {
- size_bracket_lock_names[i] =
+ size_bracket_lock_names_[i] =
StringPrintf("an rosalloc size bracket %d lock", static_cast<int>(i));
- size_bracket_locks_[i] = new Mutex(size_bracket_lock_names[i].c_str(), kRosAllocBracketLock);
+ size_bracket_locks_[i] = new Mutex(size_bracket_lock_names_[i].c_str(), kRosAllocBracketLock);
current_runs_[i] = dedicated_full_run_;
}
DCHECK_EQ(footprint_, capacity_);
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index 85a8225..c0ab151 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -440,7 +440,7 @@
// The mutexes, one per size bracket.
Mutex* size_bracket_locks_[kNumOfSizeBrackets];
// Bracket lock names (since locks only have char* names).
- std::string size_bracket_lock_names[kNumOfSizeBrackets];
+ std::string size_bracket_lock_names_[kNumOfSizeBrackets];
// The types of page map entries.
enum {
kPageMapReleased = 0, // Zero and released back to the OS.
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index 530a3c9..ef5d56e 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -40,6 +40,9 @@
kCollectorTypeHeapTrim,
// A (mostly) concurrent copying collector.
kCollectorTypeCC,
+ // A homogeneous space compaction collector used in background transition
+ // when both foreground and background collector are CMS.
+ kCollectorTypeHomogeneousSpaceCompact,
};
std::ostream& operator<<(std::ostream& os, const CollectorType& collector_type);
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index 9e73f14..f0e1512 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -31,6 +31,7 @@
case kGcCauseForNativeAlloc: return "NativeAlloc";
case kGcCauseCollectorTransition: return "CollectorTransition";
case kGcCauseDisableMovingGc: return "DisableMovingGc";
+ case kGcCauseHomogeneousSpaceCompact: return "HomogeneousSpaceCompact";
case kGcCauseTrim: return "HeapTrim";
default:
LOG(FATAL) << "Unreachable";
diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h
index 10e6667..1f2643a 100644
--- a/runtime/gc/gc_cause.h
+++ b/runtime/gc/gc_cause.h
@@ -39,6 +39,8 @@
kGcCauseDisableMovingGc,
// Not a real GC cause, used when we trim the heap.
kGcCauseTrim,
+ // GC triggered for background transition when both foreground and background collector are CMS.
+ kGcCauseHomogeneousSpaceCompact,
};
const char* PrettyCause(GcCause cause);
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 58ba61b..419af30 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -41,11 +41,11 @@
const PreFenceVisitor& pre_fence_visitor) {
if (kIsDebugBuild) {
CheckPreconditionsForAllocObject(klass, byte_count);
+ // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are
+ // done in the runnable state where suspension is expected.
+ CHECK_EQ(self->GetState(), kRunnable);
+ self->AssertThreadSuspensionIsAllowable();
}
- // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are
- // done in the runnable state where suspension is expected.
- DCHECK_EQ(self->GetState(), kRunnable);
- self->AssertThreadSuspensionIsAllowable();
// Need to check that we arent the large object allocator since the large object allocation code
// path this function. If we didn't check we would have an infinite loop.
if (kCheckLargeObject && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) {
@@ -54,49 +54,70 @@
}
mirror::Object* obj;
AllocationTimer alloc_timer(this, &obj);
- size_t bytes_allocated, usable_size;
- obj = TryToAllocate<kInstrumented, false>(self, allocator, byte_count, &bytes_allocated,
- &usable_size);
- if (UNLIKELY(obj == nullptr)) {
- bool is_current_allocator = allocator == GetCurrentAllocator();
- obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated, &usable_size,
- &klass);
- if (obj == nullptr) {
- bool after_is_current_allocator = allocator == GetCurrentAllocator();
- if (is_current_allocator && !after_is_current_allocator) {
- // If the allocator changed, we need to restart the allocation.
- return AllocObject<kInstrumented>(self, klass, byte_count, pre_fence_visitor);
+ size_t bytes_allocated;
+ size_t usable_size;
+ size_t new_num_bytes_allocated = 0;
+ if (allocator == kAllocatorTypeTLAB) {
+ byte_count = RoundUp(byte_count, space::BumpPointerSpace::kAlignment);
+ }
+ // If we have a thread local allocation we don't need to update bytes allocated.
+ if (allocator == kAllocatorTypeTLAB && byte_count <= self->TlabSize()) {
+ obj = self->AllocTlab(byte_count);
+ obj->SetClass(klass);
+ if (kUseBakerOrBrooksReadBarrier) {
+ if (kUseBrooksReadBarrier) {
+ obj->SetReadBarrierPointer(obj);
}
- return nullptr;
+ obj->AssertReadBarrierPointer();
}
- }
- DCHECK_GT(bytes_allocated, 0u);
- DCHECK_GT(usable_size, 0u);
- obj->SetClass(klass);
- if (kUseBakerOrBrooksReadBarrier) {
- if (kUseBrooksReadBarrier) {
- obj->SetReadBarrierPointer(obj);
+ bytes_allocated = byte_count;
+ pre_fence_visitor(obj, bytes_allocated);
+ QuasiAtomic::ThreadFenceForConstructor();
+ } else {
+ obj = TryToAllocate<kInstrumented, false>(self, allocator, byte_count, &bytes_allocated,
+ &usable_size);
+ if (UNLIKELY(obj == nullptr)) {
+ bool is_current_allocator = allocator == GetCurrentAllocator();
+ obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated, &usable_size,
+ &klass);
+ if (obj == nullptr) {
+ bool after_is_current_allocator = allocator == GetCurrentAllocator();
+ if (is_current_allocator && !after_is_current_allocator) {
+ // If the allocator changed, we need to restart the allocation.
+ return AllocObject<kInstrumented>(self, klass, byte_count, pre_fence_visitor);
+ }
+ return nullptr;
+ }
}
- obj->AssertReadBarrierPointer();
+ DCHECK_GT(bytes_allocated, 0u);
+ DCHECK_GT(usable_size, 0u);
+ obj->SetClass(klass);
+ if (kUseBakerOrBrooksReadBarrier) {
+ if (kUseBrooksReadBarrier) {
+ obj->SetReadBarrierPointer(obj);
+ }
+ obj->AssertReadBarrierPointer();
+ }
+ if (collector::SemiSpace::kUseRememberedSet && UNLIKELY(allocator == kAllocatorTypeNonMoving)) {
+ // (Note this if statement will be constant folded away for the
+ // fast-path quick entry points.) Because SetClass() has no write
+ // barrier, if a non-moving space allocation, we need a write
+ // barrier as the class pointer may point to the bump pointer
+ // space (where the class pointer is an "old-to-young" reference,
+ // though rare) under the GSS collector with the remembered set
+ // enabled. We don't need this for kAllocatorTypeRosAlloc/DlMalloc
+ // cases because we don't directly allocate into the main alloc
+ // space (besides promotions) under the SS/GSS collector.
+ WriteBarrierField(obj, mirror::Object::ClassOffset(), klass);
+ }
+ pre_fence_visitor(obj, usable_size);
+ if (kIsDebugBuild && Runtime::Current()->IsStarted()) {
+ CHECK_LE(obj->SizeOf(), usable_size);
+ }
+ new_num_bytes_allocated =
+ static_cast<size_t>(num_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes_allocated))
+ + bytes_allocated;
}
- if (collector::SemiSpace::kUseRememberedSet && UNLIKELY(allocator == kAllocatorTypeNonMoving)) {
- // (Note this if statement will be constant folded away for the
- // fast-path quick entry points.) Because SetClass() has no write
- // barrier, if a non-moving space allocation, we need a write
- // barrier as the class pointer may point to the bump pointer
- // space (where the class pointer is an "old-to-young" reference,
- // though rare) under the GSS collector with the remembered set
- // enabled. We don't need this for kAllocatorTypeRosAlloc/DlMalloc
- // cases because we don't directly allocate into the main alloc
- // space (besides promotions) under the SS/GSS collector.
- WriteBarrierField(obj, mirror::Object::ClassOffset(), klass);
- }
- pre_fence_visitor(obj, usable_size);
- if (kIsDebugBuild && Runtime::Current()->IsStarted()) {
- CHECK_LE(obj->SizeOf(), usable_size);
- }
- const size_t new_num_bytes_allocated =
- static_cast<size_t>(num_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes_allocated)) + bytes_allocated;
// TODO: Deprecate.
if (kInstrumented) {
if (Runtime::Current()->HasStatsEnabled()) {
@@ -158,7 +179,8 @@
inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator_type,
size_t alloc_size, size_t* bytes_allocated,
size_t* usable_size) {
- if (UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) {
+ if (allocator_type != kAllocatorTypeTLAB &&
+ UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) {
return nullptr;
}
mirror::Object* ret;
@@ -206,18 +228,24 @@
break;
}
case kAllocatorTypeTLAB: {
- alloc_size = RoundUp(alloc_size, space::BumpPointerSpace::kAlignment);
+ DCHECK_ALIGNED(alloc_size, space::BumpPointerSpace::kAlignment);
if (UNLIKELY(self->TlabSize() < alloc_size)) {
- // Try allocating a new thread local buffer, if the allocaiton fails the space must be
- // full so return nullptr.
- if (!bump_pointer_space_->AllocNewTlab(self, alloc_size + kDefaultTLABSize)) {
+ const size_t new_tlab_size = alloc_size + kDefaultTLABSize;
+ if (UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, new_tlab_size))) {
return nullptr;
}
+ // Try allocating a new thread local buffer, if the allocaiton fails the space must be
+ // full so return nullptr.
+ if (!bump_pointer_space_->AllocNewTlab(self, new_tlab_size)) {
+ return nullptr;
+ }
+ *bytes_allocated = new_tlab_size;
+ } else {
+ *bytes_allocated = 0;
}
// The allocation can't fail.
ret = self->AllocTlab(alloc_size);
DCHECK(ret != nullptr);
- *bytes_allocated = alloc_size;
*usable_size = alloc_size;
break;
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 19715e9..4ec9bc2 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -93,6 +93,10 @@
static constexpr size_t kAllocationStackReserveSize = 1024;
// Default mark stack size in bytes.
static const size_t kDefaultMarkStackSize = 64 * KB;
+// Define space name.
+static const char* kDlMallocSpaceName[2] = {"main dlmalloc space", "main dlmalloc space 1"};
+static const char* kRosAllocSpaceName[2] = {"main rosalloc space", "main rosalloc space 1"};
+static const char* kMemMapSpaceName[2] = {"main space", "main space 1"};
Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free,
double target_utilization, double foreground_heap_growth_multiplier, size_t capacity,
@@ -103,7 +107,8 @@
bool ignore_max_footprint, bool use_tlab,
bool verify_pre_gc_heap, bool verify_pre_sweeping_heap, bool verify_post_gc_heap,
bool verify_pre_gc_rosalloc, bool verify_pre_sweeping_rosalloc,
- bool verify_post_gc_rosalloc)
+ bool verify_post_gc_rosalloc, bool use_homogeneous_space_compaction_for_oom,
+ uint64_t min_interval_homogeneous_space_compaction_by_oom)
: non_moving_space_(nullptr),
rosalloc_space_(nullptr),
dlmalloc_space_(nullptr),
@@ -173,7 +178,11 @@
verify_object_mode_(kVerifyObjectModeDisabled),
disable_moving_gc_count_(0),
running_on_valgrind_(Runtime::Current()->RunningOnValgrind()),
- use_tlab_(use_tlab) {
+ use_tlab_(use_tlab),
+ main_space_backup_(nullptr),
+ min_interval_homogeneous_space_compaction_by_oom_(min_interval_homogeneous_space_compaction_by_oom),
+ last_time_homogeneous_space_compaction_by_oom_(NanoTime()),
+ use_homogeneous_space_compaction_for_oom_(use_homogeneous_space_compaction_for_oom) {
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "Heap() entering";
}
@@ -205,30 +214,90 @@
CHECK_GT(oat_file_end_addr, image_space->End());
requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
}
+
+ /*
+ requested_alloc_space_begin -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ +- nonmoving space (kNonMovingSpaceCapacity) +-
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ +- main alloc space (capacity_) +-
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ +- main alloc space 1 (capacity_) +-
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ */
+ bool create_backup_main_space =
+ background_collector_type == gc::kCollectorTypeHomogeneousSpaceCompact ||
+ use_homogeneous_space_compaction_for_oom;
if (is_zygote) {
// Reserve the address range before we create the non moving space to make sure bitmaps don't
// take it.
std::string error_str;
- MemMap* mem_map = MemMap::MapAnonymous(
- "main space", requested_alloc_space_begin + kNonMovingSpaceCapacity, capacity,
+ MemMap* main_space_map = MemMap::MapAnonymous(
+ kMemMapSpaceName[0], requested_alloc_space_begin + kNonMovingSpaceCapacity, capacity_,
PROT_READ | PROT_WRITE, true, &error_str);
- CHECK(mem_map != nullptr) << error_str;
+ CHECK(main_space_map != nullptr) << error_str;
+ MemMap* main_space_1_map = nullptr;
+ // Attempt to reserve an extra mem_map for homogeneous space compaction right after the main space map.
+ if (create_backup_main_space) {
+ main_space_1_map = MemMap::MapAnonymous(kMemMapSpaceName[1], main_space_map->End(), capacity_,
+ PROT_READ | PROT_WRITE, true, &error_str);
+ if (main_space_1_map == nullptr) {
+ LOG(WARNING) << "Failed to create map " << kMemMapSpaceName[1] << " with error "
+ << error_str;
+ }
+ }
// Non moving space is always dlmalloc since we currently don't have support for multiple
- // rosalloc spaces.
+ // active rosalloc spaces.
non_moving_space_ = space::DlMallocSpace::Create(
- "zygote / non moving space", initial_size, kNonMovingSpaceCapacity, kNonMovingSpaceCapacity,
- requested_alloc_space_begin, false);
+ "zygote / non moving space", initial_size, kNonMovingSpaceCapacity,
+ kNonMovingSpaceCapacity, requested_alloc_space_begin, false);
non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity());
- CreateMainMallocSpace(mem_map, initial_size, growth_limit, capacity);
+ CreateMainMallocSpace(main_space_map, initial_size, growth_limit_, capacity_);
+ if (main_space_1_map != nullptr) {
+ const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
+ main_space_backup_ = CreateMallocSpaceFromMemMap(main_space_1_map, initial_size,
+ growth_limit_, capacity_, name, true);
+ }
} else {
std::string error_str;
- MemMap* mem_map = MemMap::MapAnonymous("main/non-moving space", requested_alloc_space_begin,
- capacity, PROT_READ | PROT_WRITE, true, &error_str);
- CHECK(mem_map != nullptr) << error_str;
+ byte* request_begin = requested_alloc_space_begin;
+ if (request_begin == nullptr) {
+ // Disable homogeneous space compaction since we don't have an image.
+ create_backup_main_space = false;
+ }
+ MemMap* main_space_1_map = nullptr;
+ if (create_backup_main_space) {
+ request_begin += kNonMovingSpaceCapacity;
+ // Attempt to reserve an extra mem_map for homogeneous space compaction right after the main space map.
+ main_space_1_map = MemMap::MapAnonymous(kMemMapSpaceName[1], request_begin + capacity_,
+ capacity_, PROT_READ | PROT_WRITE, true, &error_str);
+ if (main_space_1_map == nullptr) {
+ LOG(WARNING) << "Failed to create map " << kMemMapSpaceName[1] << " with error "
+ << error_str;
+ request_begin = requested_alloc_space_begin;
+ }
+ }
+ MemMap* main_space_map = MemMap::MapAnonymous(kMemMapSpaceName[0], request_begin, capacity_,
+ PROT_READ | PROT_WRITE, true, &error_str);
+ CHECK(main_space_map != nullptr) << error_str;
+ // Introduce a seperate non moving space.
+ if (main_space_1_map != nullptr) {
+ // Do this before creating the main malloc space to prevent bitmaps from being placed here.
+ non_moving_space_ = space::DlMallocSpace::Create(
+ "non moving space", kDefaultInitialSize, kNonMovingSpaceCapacity, kNonMovingSpaceCapacity,
+ requested_alloc_space_begin, false);
+ non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity());
+ }
// Create the main free list space, which doubles as the non moving space. We can do this since
// non zygote means that we won't have any background compaction.
- CreateMainMallocSpace(mem_map, initial_size, growth_limit, capacity);
- non_moving_space_ = main_space_;
+ CreateMainMallocSpace(main_space_map, initial_size, growth_limit_, capacity_);
+ if (main_space_1_map != nullptr) {
+ const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
+ main_space_backup_ = CreateMallocSpaceFromMemMap(main_space_1_map, initial_size,
+ growth_limit_, capacity_, name, true);
+ CHECK(main_space_backup_ != nullptr);
+ } else {
+ non_moving_space_ = main_space_;
+ }
}
CHECK(non_moving_space_ != nullptr);
@@ -240,7 +309,7 @@
(IsMovingGc(foreground_collector_type_) || IsMovingGc(background_collector_type_))) {
// TODO: Place bump-pointer spaces somewhere to minimize size of card table.
// Divide by 2 for a temporary fix for reducing virtual memory usage.
- const size_t bump_pointer_space_capacity = capacity / 2;
+ const size_t bump_pointer_space_capacity = capacity_ / 2;
bump_pointer_space_ = space::BumpPointerSpace::Create("Bump pointer space",
bump_pointer_space_capacity, nullptr);
CHECK(bump_pointer_space_ != nullptr) << "Failed to create bump pointer space";
@@ -253,13 +322,25 @@
if (non_moving_space_ != main_space_) {
AddSpace(non_moving_space_);
}
+ if (main_space_backup_ != nullptr) {
+ AddSpace(main_space_backup_);
+ } else {
+ const char* disable_msg = "Disabling homogenous space compact due to no backup main space";
+ if (background_collector_type_ == gc::kCollectorTypeHomogeneousSpaceCompact) {
+ background_collector_type_ = collector_type_;
+ LOG(WARNING) << disable_msg;
+ } else if (use_homogeneous_space_compaction_for_oom_) {
+ LOG(WARNING) << disable_msg;
+ }
+ use_homogeneous_space_compaction_for_oom_ = false;
+ }
if (main_space_ != nullptr) {
AddSpace(main_space_);
}
// Allocate the large object space.
if (kUseFreeListSpaceForLOS) {
- large_object_space_ = space::FreeListSpace::Create("large object space", nullptr, capacity);
+ large_object_space_ = space::FreeListSpace::Create("large object space", nullptr, capacity_);
} else {
large_object_space_ = space::LargeObjectMapSpace::Create("large object space");
}
@@ -328,7 +409,7 @@
}
if (kMovingCollector) {
// TODO: Clean this up.
- bool generational = foreground_collector_type_ == kCollectorTypeGSS;
+ const bool generational = foreground_collector_type_ == kCollectorTypeGSS;
semi_space_collector_ = new collector::SemiSpace(this, generational,
generational ? "generational" : "");
garbage_collectors_.push_back(semi_space_collector_);
@@ -339,9 +420,8 @@
}
if (GetImageSpace() != nullptr && main_space_ != nullptr) {
- // Check that there's no gap between the image space and the main
- // space so that the immune region won't break (eg. due to a large
- // object allocated in the gap).
+ // Check that there's no gap between the image space and the main space so that the immune
+ // region won't break (eg. due to a large object allocated in the gap).
bool no_gap = MemMap::CheckNoGaps(GetImageSpace()->GetMemMap(), main_space_->GetMemMap());
if (!no_gap) {
MemMap::DumpMaps(LOG(ERROR));
@@ -358,11 +438,36 @@
}
}
+space::MallocSpace* Heap::CreateMallocSpaceFromMemMap(MemMap* mem_map, size_t initial_size,
+ size_t growth_limit, size_t capacity,
+ const char* name, bool can_move_objects) {
+ space::MallocSpace* malloc_space = nullptr;
+ if (kUseRosAlloc) {
+ // Create rosalloc space.
+ malloc_space = space::RosAllocSpace::CreateFromMemMap(mem_map, name, kDefaultStartingSize,
+ initial_size, growth_limit, capacity,
+ low_memory_mode_, can_move_objects);
+ } else {
+ malloc_space = space::DlMallocSpace::CreateFromMemMap(mem_map, name, kDefaultStartingSize,
+ initial_size, growth_limit, capacity,
+ can_move_objects);
+ }
+ if (collector::SemiSpace::kUseRememberedSet) {
+ accounting::RememberedSet* rem_set =
+ new accounting::RememberedSet(std::string(name) + " remembered set", this, malloc_space);
+ CHECK(rem_set != nullptr) << "Failed to create main space remembered set";
+ AddRememberedSet(rem_set);
+ }
+ CHECK(malloc_space != nullptr) << "Failed to create " << name;
+ malloc_space->SetFootprintLimit(malloc_space->Capacity());
+ return malloc_space;
+}
+
void Heap::CreateMainMallocSpace(MemMap* mem_map, size_t initial_size, size_t growth_limit,
size_t capacity) {
// Is background compaction is enabled?
bool can_move_objects = IsMovingGc(background_collector_type_) !=
- IsMovingGc(foreground_collector_type_);
+ IsMovingGc(foreground_collector_type_) || use_homogeneous_space_compaction_for_oom_;
// If we are the zygote and don't yet have a zygote space, it means that the zygote fork will
// happen in the future. If this happens and we have kCompactZygote enabled we wish to compact
// from the main space to the zygote space. If background compaction is enabled, always pass in
@@ -375,26 +480,10 @@
if (collector::SemiSpace::kUseRememberedSet && main_space_ != nullptr) {
RemoveRememberedSet(main_space_);
}
- if (kUseRosAlloc) {
- rosalloc_space_ = space::RosAllocSpace::CreateFromMemMap(
- mem_map, "main rosalloc space", kDefaultStartingSize, initial_size, growth_limit, capacity,
- low_memory_mode_, can_move_objects);
- main_space_ = rosalloc_space_;
- CHECK(main_space_ != nullptr) << "Failed to create rosalloc space";
- } else {
- dlmalloc_space_ = space::DlMallocSpace::CreateFromMemMap(
- mem_map, "main dlmalloc space", kDefaultStartingSize, initial_size, growth_limit, capacity,
- can_move_objects);
- main_space_ = dlmalloc_space_;
- CHECK(main_space_ != nullptr) << "Failed to create dlmalloc space";
- }
- main_space_->SetFootprintLimit(main_space_->Capacity());
- if (collector::SemiSpace::kUseRememberedSet) {
- accounting::RememberedSet* main_space_rem_set =
- new accounting::RememberedSet("Main space remembered set", this, main_space_);
- CHECK(main_space_rem_set != nullptr) << "Failed to create main space remembered set";
- AddRememberedSet(main_space_rem_set);
- }
+ const char* name = kUseRosAlloc ? kRosAllocSpaceName[0] : kDlMallocSpaceName[0];
+ main_space_ = CreateMallocSpaceFromMemMap(mem_map, initial_size, growth_limit, capacity, name,
+ can_move_objects);
+ SetSpaceAsDefault(main_space_);
VLOG(heap) << "Created main space " << main_space_;
}
@@ -547,8 +636,11 @@
RequestCollectorTransition(foreground_collector_type_, 0);
} else {
// Don't delay for debug builds since we may want to stress test the GC.
- RequestCollectorTransition(background_collector_type_, kIsDebugBuild ? 0 :
- kCollectorTransitionWait);
+ // If background_collector_type_ is kCollectorTypeHomogeneousSpaceCompact then we have
+ // special handling which does a homogenous space compaction once but then doesn't transition
+ // the collector.
+ RequestCollectorTransition(background_collector_type_,
+ kIsDebugBuild ? 0 : kCollectorTransitionWait);
}
}
}
@@ -605,7 +697,7 @@
}
void Heap::AddSpace(space::Space* space) {
- DCHECK(space != nullptr);
+ CHECK(space != nullptr);
WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
if (space->IsContinuousSpace()) {
DCHECK(!space->IsDiscontinuousSpace());
@@ -811,7 +903,7 @@
oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free
<< " free bytes";
// If the allocation failed due to fragmentation, print out the largest continuous allocation.
- if (allocator_type != kAllocatorTypeLOS && total_bytes_free >= byte_count) {
+ if (total_bytes_free >= byte_count) {
space::MallocSpace* space = nullptr;
if (allocator_type == kAllocatorTypeNonMoving) {
space = non_moving_space_;
@@ -844,6 +936,15 @@
ScopedThreadStateChange tsc(self, kSleeping);
usleep(wait_time / 1000); // Usleep takes microseconds.
}
+ // Launch homogeneous space compaction if it is desired.
+ if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) {
+ if (!CareAboutPauseTimes()) {
+ PerformHomogeneousSpaceCompact();
+ }
+ // No need to Trim(). Homogeneous space compaction may free more virtual and physical memory.
+ desired_collector_type = collector_type_;
+ return;
+ }
// Transition the collector if the desired collector type is not the same as the current
// collector type.
TransitionCollector(desired_collector_type);
@@ -1092,6 +1193,17 @@
}
}
+space::RosAllocSpace* Heap::GetRosAllocSpace(gc::allocator::RosAlloc* rosalloc) const {
+ for (const auto& space : continuous_spaces_) {
+ if (space->AsContinuousSpace()->IsRosAllocSpace()) {
+ if (space->AsContinuousSpace()->AsRosAllocSpace()->GetRosAlloc() == rosalloc) {
+ return space->AsContinuousSpace()->AsRosAllocSpace();
+ }
+ }
+ }
+ return nullptr;
+}
+
mirror::Object* Heap::AllocateInternalWithGc(Thread* self, AllocatorType allocator,
size_t alloc_size, size_t* bytes_allocated,
size_t* usable_size,
@@ -1173,6 +1285,44 @@
return nullptr;
}
ptr = TryToAllocate<true, true>(self, allocator, alloc_size, bytes_allocated, usable_size);
+ if (ptr == nullptr && use_homogeneous_space_compaction_for_oom_) {
+ const uint64_t current_time = NanoTime();
+ if ((allocator == kAllocatorTypeRosAlloc || allocator == kAllocatorTypeDlMalloc) &&
+ current_time - last_time_homogeneous_space_compaction_by_oom_ >
+ min_interval_homogeneous_space_compaction_by_oom_) {
+ last_time_homogeneous_space_compaction_by_oom_ = current_time;
+ HomogeneousSpaceCompactResult result = PerformHomogeneousSpaceCompact();
+ switch (result) {
+ case HomogeneousSpaceCompactResult::kSuccess:
+ // If the allocation succeeded, we delayed an oom.
+ ptr = TryToAllocate<true, true>(self, allocator, alloc_size, bytes_allocated, usable_size);
+ if (ptr != nullptr) {
+ count_delayed_oom_++;
+ }
+ break;
+ case HomogeneousSpaceCompactResult::kErrorReject:
+ // Reject due to disabled moving GC.
+ break;
+ case HomogeneousSpaceCompactResult::kErrorVMShuttingDown:
+ // Throw OOM by default.
+ break;
+ default: {
+ LOG(FATAL) << "Unimplemented homogeneous space compaction result " << static_cast<size_t>(result);
+ }
+ }
+ // Always print that we ran homogeneous space compation since this can cause jank.
+ VLOG(heap) << "Ran heap homogeneous space compaction, "
+ << " requested defragmentation "
+ << count_requested_homogeneous_space_compaction_.LoadSequentiallyConsistent()
+ << " performed defragmentation "
+ << count_performed_homogeneous_space_compaction_.LoadSequentiallyConsistent()
+ << " ignored homogeneous space compaction "
+ << count_ignored_homogeneous_space_compaction_.LoadSequentiallyConsistent()
+ << " delayed count = "
+ << count_delayed_oom_.LoadSequentiallyConsistent();
+ }
+ }
+ // If the allocation hasn't succeeded by this point, throw an OOM error.
if (ptr == nullptr) {
ThrowOutOfMemoryError(self, alloc_size, allocator);
}
@@ -1331,6 +1481,66 @@
CollectGarbageInternal(gc_plan_.back(), kGcCauseExplicit, clear_soft_references);
}
+HomogeneousSpaceCompactResult Heap::PerformHomogeneousSpaceCompact() {
+ Thread* self = Thread::Current();
+ // 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);
+ {
+ ScopedThreadStateChange tsc(self, kWaitingForGcToComplete);
+ MutexLock mu(self, *gc_complete_lock_);
+ // Ensure there is only one GC at a time.
+ WaitForGcToCompleteLocked(kGcCauseHomogeneousSpaceCompact, self);
+ // Homogeneous space compaction is a copying transition, can't run it if the moving GC disable count
+ // is non zero.
+ // If the collecotr type changed to something which doesn't benefit from homogeneous space compaction,
+ // exit.
+ if (disable_moving_gc_count_ != 0 || IsMovingGc(collector_type_)) {
+ return HomogeneousSpaceCompactResult::kErrorReject;
+ }
+ collector_type_running_ = kCollectorTypeHomogeneousSpaceCompact;
+ }
+ if (Runtime::Current()->IsShuttingDown(self)) {
+ // Don't allow heap transitions to happen if the runtime is shutting down since these can
+ // cause objects to get finalized.
+ FinishGC(self, collector::kGcTypeNone);
+ return HomogeneousSpaceCompactResult::kErrorVMShuttingDown;
+ }
+ // Suspend all threads.
+ tl->SuspendAll();
+ uint64_t start_time = NanoTime();
+ // Launch compaction.
+ space::MallocSpace* to_space = main_space_backup_;
+ space::MallocSpace* from_space = main_space_;
+ to_space->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
+ const uint64_t space_size_before_compaction = from_space->Size();
+ Compact(to_space, from_space, kGcCauseHomogeneousSpaceCompact);
+ // Leave as prot read so that we can still run ROSAlloc verification on this space.
+ from_space->GetMemMap()->Protect(PROT_READ);
+ const uint64_t space_size_after_compaction = to_space->Size();
+ std::swap(main_space_, main_space_backup_);
+ 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;
+ LOG(INFO) << "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();
+ // Finish GC.
+ reference_processor_.EnqueueClearedReferences(self);
+ GrowForUtilization(semi_space_collector_);
+ FinishGC(self, collector::kGcTypeFull);
+ return HomogeneousSpaceCompactResult::kSuccess;
+}
+
+
void Heap::TransitionCollector(CollectorType collector_type) {
if (collector_type == collector_type_) {
return;
@@ -1385,7 +1595,7 @@
// We are transitioning from non moving GC -> moving GC, since we copied from the bump
// pointer space last transition it will be protected.
bump_pointer_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
- Compact(bump_pointer_space_, main_space_);
+ Compact(bump_pointer_space_, main_space_, kGcCauseCollectorTransition);
// 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_);
@@ -1399,7 +1609,7 @@
// Compact to the main space from the bump pointer space, don't need to swap semispaces.
AddSpace(main_space_);
main_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
- Compact(main_space_, bump_pointer_space_);
+ Compact(main_space_, bump_pointer_space_, kGcCauseCollectorTransition);
}
break;
}
@@ -1725,14 +1935,15 @@
}
void Heap::Compact(space::ContinuousMemMapAllocSpace* target_space,
- space::ContinuousMemMapAllocSpace* source_space) {
+ space::ContinuousMemMapAllocSpace* source_space,
+ GcCause gc_cause) {
CHECK(kMovingCollector);
if (target_space != source_space) {
// Don't swap spaces since this isn't a typical semi space collection.
semi_space_collector_->SetSwapSemiSpaces(false);
semi_space_collector_->SetFromSpace(source_space);
semi_space_collector_->SetToSpace(target_space);
- semi_space_collector_->Run(kGcCauseCollectorTransition, false);
+ semi_space_collector_->Run(gc_cause, false);
} else {
CHECK(target_space->IsBumpPointerSpace())
<< "In-place compaction is only supported for bump pointer spaces";
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 86dab21..b207953 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -72,6 +72,10 @@
class SemiSpace;
} // namespace collector
+namespace allocator {
+ class RosAlloc;
+} // namespace allocator
+
namespace space {
class AllocSpace;
class BumpPointerSpace;
@@ -97,6 +101,15 @@
}
};
+enum HomogeneousSpaceCompactResult {
+ // Success.
+ kSuccess,
+ // Reject due to disabled moving GC.
+ kErrorReject,
+ // System is shutting down.
+ kErrorVMShuttingDown,
+};
+
// If true, use rosalloc/RosAllocSpace instead of dlmalloc/DlMallocSpace
static constexpr bool kUseRosAlloc = true;
@@ -151,7 +164,8 @@
bool ignore_max_footprint, bool use_tlab,
bool verify_pre_gc_heap, bool verify_pre_sweeping_heap, bool verify_post_gc_heap,
bool verify_pre_gc_rosalloc, bool verify_pre_sweeping_rosalloc,
- bool verify_post_gc_rosalloc);
+ bool verify_post_gc_rosalloc, bool use_homogeneous_space_compaction,
+ uint64_t min_interval_homogeneous_space_compaction_by_oom);
~Heap();
@@ -499,6 +513,9 @@
return rosalloc_space_;
}
+ // Return the corresponding rosalloc space.
+ space::RosAllocSpace* GetRosAllocSpace(gc::allocator::RosAlloc* rosalloc) const;
+
space::MallocSpace* GetNonMovingSpace() const {
return non_moving_space_;
}
@@ -568,12 +585,19 @@
}
private:
+ // Compact source space to target space.
void Compact(space::ContinuousMemMapAllocSpace* target_space,
- space::ContinuousMemMapAllocSpace* source_space)
+ space::ContinuousMemMapAllocSpace* source_space,
+ GcCause gc_cause)
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
void FinishGC(Thread* self, collector::GcType gc_type) LOCKS_EXCLUDED(gc_complete_lock_);
+ bool SupportHSpaceCompaction() const {
+ // Returns true if we can do hspace compaction.
+ return main_space_backup_ != nullptr;
+ }
+
static ALWAYS_INLINE bool AllocatorHasAllocationStack(AllocatorType allocator_type) {
return
allocator_type != kAllocatorTypeBumpPointer &&
@@ -584,7 +608,8 @@
}
static bool IsMovingGc(CollectorType collector_type) {
return collector_type == kCollectorTypeSS || collector_type == kCollectorTypeGSS ||
- collector_type == kCollectorTypeCC || collector_type == kCollectorTypeMC;
+ collector_type == kCollectorTypeCC || collector_type == kCollectorTypeMC ||
+ collector_type == kCollectorTypeHomogeneousSpaceCompact;
}
bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -682,10 +707,18 @@
// Find a collector based on GC type.
collector::GarbageCollector* FindCollectorByGcType(collector::GcType gc_type);
- // Create the main free list space, typically either a RosAlloc space or DlMalloc space.
+ // Create a new alloc space and compact default alloc space to it.
+ HomogeneousSpaceCompactResult PerformHomogeneousSpaceCompact();
+
+ // Create the main free list malloc space, either a RosAlloc space or DlMalloc space.
void CreateMainMallocSpace(MemMap* mem_map, size_t initial_size, size_t growth_limit,
size_t capacity);
+ // Create a malloc space based on a mem map. Does not set the space as default.
+ space::MallocSpace* CreateMallocSpaceFromMemMap(MemMap* mem_map, size_t initial_size,
+ size_t growth_limit, size_t capacity,
+ const char* name, bool can_move_objects);
+
// Given the current contents of the alloc space, increase the allowed heap footprint to match
// the target utilization ratio. This should only be called immediately after a full garbage
// collection.
@@ -972,6 +1005,30 @@
const bool running_on_valgrind_;
const bool use_tlab_;
+ // Pointer to the space which becomes the new main space when we do homogeneous space compaction.
+ space::MallocSpace* main_space_backup_;
+
+ // Minimal interval allowed between two homogeneous space compactions caused by OOM.
+ uint64_t min_interval_homogeneous_space_compaction_by_oom_;
+
+ // Times of the last homogeneous space compaction caused by OOM.
+ uint64_t last_time_homogeneous_space_compaction_by_oom_;
+
+ // Saved OOMs by homogeneous space compaction.
+ Atomic<size_t> count_delayed_oom_;
+
+ // Count for requested homogeneous space compaction.
+ Atomic<size_t> count_requested_homogeneous_space_compaction_;
+
+ // Count for ignored homogeneous space compaction.
+ Atomic<size_t> count_ignored_homogeneous_space_compaction_;
+
+ // Count for performed homogeneous space compaction.
+ Atomic<size_t> count_performed_homogeneous_space_compaction_;
+
+ // Whether or not we use homogeneous space compaction to avoid OOM errors.
+ bool use_homogeneous_space_compaction_for_oom_;
+
friend class collector::GarbageCollector;
friend class collector::MarkCompact;
friend class collector::MarkSweep;
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 5738d47..92c6f53 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -227,7 +227,7 @@
// Callback from rosalloc when it needs to increase the footprint
extern "C" void* art_heap_rosalloc_morecore(allocator::RosAlloc* rosalloc, intptr_t increment) {
Heap* heap = Runtime::Current()->GetHeap();
- RosAllocSpace* rosalloc_space = heap->GetRosAllocSpace();
+ RosAllocSpace* rosalloc_space = heap->GetRosAllocSpace(rosalloc);
DCHECK(rosalloc_space != nullptr);
DCHECK_EQ(rosalloc_space->GetRosAlloc(), rosalloc);
return rosalloc_space->MoreCore(increment);
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 0e05b62..05320ce 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -93,16 +93,17 @@
method->ClearIsPortableCompiled();
}
if (!method->IsResolutionMethod()) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
if (quick_code == GetQuickToInterpreterBridge() ||
- quick_code == GetQuickToInterpreterBridgeTrampoline(Runtime::Current()->GetClassLinker()) ||
- (quick_code == GetQuickResolutionTrampoline(Runtime::Current()->GetClassLinker()) &&
+ quick_code == class_linker->GetQuickToInterpreterBridgeTrampoline() ||
+ (quick_code == class_linker->GetQuickResolutionTrampoline() &&
Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly()
&& !method->IsNative() && !method->IsProxyMethod())) {
if (kIsDebugBuild) {
if (quick_code == GetQuickToInterpreterBridge()) {
DCHECK(portable_code == GetPortableToInterpreterBridge());
- } else if (quick_code == GetQuickResolutionTrampoline(Runtime::Current()->GetClassLinker())) {
- DCHECK(portable_code == GetPortableResolutionTrampoline(Runtime::Current()->GetClassLinker()));
+ } else if (quick_code == class_linker->GetQuickResolutionTrampoline()) {
+ DCHECK(portable_code == class_linker->GetPortableResolutionTrampoline());
}
}
DCHECK(!method->IsNative()) << PrettyMethod(method);
@@ -133,8 +134,8 @@
new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
new_quick_code = class_linker->GetQuickOatCodeFor(method);
} else {
- new_portable_code = GetPortableResolutionTrampoline(class_linker);
- new_quick_code = GetQuickResolutionTrampoline(class_linker);
+ new_portable_code = class_linker->GetPortableResolutionTrampoline();
+ new_quick_code = class_linker->GetQuickResolutionTrampoline();
}
} else { // !uninstall
if ((interpreter_stubs_installed_ || forced_interpret_only_ || IsDeoptimized(method)) &&
@@ -152,11 +153,11 @@
} else {
new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
new_quick_code = class_linker->GetQuickOatCodeFor(method);
- DCHECK(new_quick_code != GetQuickToInterpreterBridgeTrampoline(class_linker));
+ DCHECK(new_quick_code != class_linker->GetQuickToInterpreterBridgeTrampoline());
}
} else {
- new_portable_code = GetPortableResolutionTrampoline(class_linker);
- new_quick_code = GetQuickResolutionTrampoline(class_linker);
+ new_portable_code = class_linker->GetPortableResolutionTrampoline();
+ new_quick_code = class_linker->GetQuickResolutionTrampoline();
}
}
}
@@ -592,22 +593,25 @@
new_portable_code = GetPortableToInterpreterBridge();
new_quick_code = GetQuickToInterpreterBridge();
new_have_portable_code = false;
- } else if (quick_code == GetQuickResolutionTrampoline(Runtime::Current()->GetClassLinker()) ||
- quick_code == GetQuickToInterpreterBridgeTrampoline(Runtime::Current()->GetClassLinker()) ||
- quick_code == GetQuickToInterpreterBridge()) {
- DCHECK((portable_code == GetPortableResolutionTrampoline(Runtime::Current()->GetClassLinker())) ||
- (portable_code == GetPortableToInterpreterBridge()));
- new_portable_code = portable_code;
- new_quick_code = quick_code;
- new_have_portable_code = have_portable_code;
- } else if (entry_exit_stubs_installed_) {
- new_quick_code = GetQuickInstrumentationEntryPoint();
- new_portable_code = GetPortableToInterpreterBridge();
- new_have_portable_code = false;
} else {
- new_portable_code = portable_code;
- new_quick_code = quick_code;
- new_have_portable_code = have_portable_code;
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (quick_code == class_linker->GetQuickResolutionTrampoline() ||
+ quick_code == class_linker->GetQuickToInterpreterBridgeTrampoline() ||
+ quick_code == GetQuickToInterpreterBridge()) {
+ DCHECK((portable_code == class_linker->GetPortableResolutionTrampoline()) ||
+ (portable_code == GetPortableToInterpreterBridge()));
+ new_portable_code = portable_code;
+ new_quick_code = quick_code;
+ new_have_portable_code = have_portable_code;
+ } else if (entry_exit_stubs_installed_) {
+ new_quick_code = GetQuickInstrumentationEntryPoint();
+ new_portable_code = GetPortableToInterpreterBridge();
+ new_have_portable_code = false;
+ } else {
+ new_portable_code = portable_code;
+ new_quick_code = quick_code;
+ new_have_portable_code = have_portable_code;
+ }
}
}
UpdateEntrypoints(method, new_quick_code, new_portable_code, new_have_portable_code);
@@ -661,8 +665,9 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
if (method->IsStatic() && !method->IsConstructor() &&
!method->GetDeclaringClass()->IsInitialized()) {
- UpdateEntrypoints(method, GetQuickResolutionTrampoline(class_linker),
- GetPortableResolutionTrampoline(class_linker), false);
+ // TODO: we're updating to entrypoints in the image here, we can avoid the trampoline.
+ UpdateEntrypoints(method, class_linker->GetQuickResolutionTrampoline(),
+ class_linker->GetPortableResolutionTrampoline(), false);
} else {
bool have_portable_code = false;
const void* quick_code = class_linker->GetQuickOatCodeFor(method);
@@ -742,8 +747,9 @@
if (LIKELY(!instrumentation_stubs_installed_)) {
const void* code = method->GetEntryPointFromQuickCompiledCode();
DCHECK(code != nullptr);
- if (LIKELY(code != GetQuickResolutionTrampoline(runtime->GetClassLinker())) &&
- LIKELY(code != GetQuickToInterpreterBridgeTrampoline(runtime->GetClassLinker())) &&
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ if (LIKELY(code != class_linker->GetQuickResolutionTrampoline()) &&
+ LIKELY(code != class_linker->GetQuickToInterpreterBridgeTrampoline()) &&
LIKELY(code != GetQuickToInterpreterBridge())) {
return code;
}
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 2db62f8..e3068b3 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -95,11 +95,11 @@
jint newValue = args[4];
bool success;
if (Runtime::Current()->IsActiveTransaction()) {
- success = obj->CasFieldWeakSequentiallyConsistent32<true>(MemberOffset(offset),
- expectedValue, newValue);
+ success = obj->CasFieldStrongSequentiallyConsistent32<true>(MemberOffset(offset),
+ expectedValue, newValue);
} else {
- success = obj->CasFieldWeakSequentiallyConsistent32<false>(MemberOffset(offset),
- expectedValue, newValue);
+ success = obj->CasFieldStrongSequentiallyConsistent32<false>(MemberOffset(offset),
+ expectedValue, newValue);
}
result->SetZ(success ? JNI_TRUE : JNI_FALSE);
} else if (name == "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)") {
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 5277330..db42eb0 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -27,7 +27,7 @@
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
#include "dex_instruction.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "handle_scope-inl.h"
#include "nth_caller_visitor.h"
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 43bdf49..f3c8250 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -27,6 +27,11 @@
namespace art {
namespace mirror {
+inline uint32_t Array::ClassSize() {
+ uint32_t vtable_entries = Object::kVTableLength;
+ return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+}
+
template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
inline size_t Array::SizeOf() {
// This is safe from overflow because the array was already allocated, so we know it's sane.
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 25a4535..6588b57 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -30,6 +30,9 @@
class MANAGED Array : public Object {
public:
+ // The size of a java.lang.Class representing an array.
+ static uint32_t ClassSize();
+
// Allocates an array with the given properties, if fill_usable is true the array will be of at
// least component_count size, however, if there's usable space at the end of the allocation the
// array will fill it.
diff --git a/runtime/mirror/art_field-inl.h b/runtime/mirror/art_field-inl.h
index 686fded..90247ed 100644
--- a/runtime/mirror/art_field-inl.h
+++ b/runtime/mirror/art_field-inl.h
@@ -29,6 +29,11 @@
namespace art {
namespace mirror {
+inline uint32_t ArtField::ClassSize() {
+ uint32_t vtable_entries = Object::kVTableLength + 6;
+ return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+}
+
inline Class* ArtField::GetDeclaringClass() {
Class* result = GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(ArtField, declaring_class_));
DCHECK(result != NULL);
diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h
index 502cec7..741c6eb 100644
--- a/runtime/mirror/art_field.h
+++ b/runtime/mirror/art_field.h
@@ -19,22 +19,33 @@
#include <jni.h>
-#include "class.h"
#include "modifiers.h"
#include "object.h"
#include "object_callbacks.h"
+#include "primitive.h"
#include "read_barrier.h"
namespace art {
struct ArtFieldOffsets;
+class DexFile;
class ScopedObjectAccessAlreadyRunnable;
namespace mirror {
+class DexCache;
+
// C++ mirror of java.lang.reflect.ArtField
-class MANAGED ArtField : public Object {
+class MANAGED ArtField FINAL : public Object {
public:
+ // Size of java.lang.reflect.ArtField.class.
+ static uint32_t ClassSize();
+
+ // Size of an instance of java.lang.reflect.ArtField not including its value array.
+ static constexpr uint32_t InstanceSize() {
+ return sizeof(ArtField);
+ }
+
static ArtField* FromReflectedField(const ScopedObjectAccessAlreadyRunnable& soa,
jobject jlr_field)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -143,11 +154,17 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const char* GetTypeDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
Primitive::Type GetTypeAsPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
bool IsPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
size_t FieldSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const DexFile* GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
@@ -169,11 +186,6 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(ArtField);
};
-class MANAGED ArtFieldClass : public Class {
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(ArtFieldClass);
-};
-
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index 8fcacc2..a5b5df6 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -21,14 +21,29 @@
#include "dex_file.h"
#include "entrypoints/entrypoint_utils.h"
+#include "object-inl.h"
#include "object_array.h"
+#include "object_utils.h"
#include "oat.h"
#include "quick/quick_method_frame_info.h"
+#include "read_barrier-inl.h"
#include "runtime-inl.h"
namespace art {
namespace mirror {
+inline uint32_t ArtMethod::ClassSize() {
+ uint32_t vtable_entries = Object::kVTableLength + 8;
+ return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+}
+
+template<ReadBarrierOption kReadBarrierOption>
+inline Class* ArtMethod::GetJavaLangReflectArtMethod() {
+ DCHECK(java_lang_reflect_ArtMethod_ != nullptr);
+ return ReadBarrier::BarrierForRoot<mirror::Class, kReadBarrierOption>(
+ &java_lang_reflect_ArtMethod_);
+}
+
inline Class* ArtMethod::GetDeclaringClass() {
Class* result = GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, declaring_class_));
DCHECK(result != NULL) << this;
@@ -122,8 +137,8 @@
return;
}
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (code == GetQuickResolutionTrampoline(class_linker) ||
- code == GetQuickToInterpreterBridgeTrampoline(class_linker)) {
+ if (code == class_linker->GetQuickResolutionTrampoline() ||
+ code == class_linker->GetQuickToInterpreterBridgeTrampoline()) {
return;
}
DCHECK(IsWithinQuickCode(pc))
@@ -162,7 +177,7 @@
// On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method
// indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline)
// for non-native methods.
- DCHECK(entry_point != GetQuickToInterpreterBridgeTrampoline(runtime->GetClassLinker()));
+ DCHECK(entry_point != runtime->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline());
if (UNLIKELY(entry_point == GetQuickToInterpreterBridge()) ||
UNLIKELY(entry_point == runtime->GetClassLinker()->GetQuickGenericJniTrampoline())) {
return nullptr;
@@ -289,7 +304,7 @@
// On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method
// indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline)
// for non-native methods. And we really shouldn't see a failure for non-native methods here.
- DCHECK(entry_point != GetQuickToInterpreterBridgeTrampoline(runtime->GetClassLinker()));
+ DCHECK(entry_point != runtime->GetClassLinker()->GetQuickToInterpreterBridgeTrampoline());
CHECK(entry_point != GetQuickToInterpreterBridge());
if (UNLIKELY(entry_point == runtime->GetClassLinker()->GetQuickGenericJniTrampoline())) {
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 4821e29..1fa680d 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -355,14 +355,6 @@
self->PopManagedStackFragment(fragment);
}
-bool ArtMethod::IsRegistered() {
- void* native_method =
- GetFieldPtr<void*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_));
- CHECK(native_method != nullptr);
- void* jni_stub = GetJniDlsymLookupStub();
- return native_method != jni_stub;
-}
-
void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_fast) {
DCHECK(Thread::Current() == self);
CHECK(IsNative()) << PrettyMethod(this);
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index a55c48b..081bee1 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -17,21 +17,19 @@
#ifndef ART_RUNTIME_MIRROR_ART_METHOD_H_
#define ART_RUNTIME_MIRROR_ART_METHOD_H_
-#include "class.h"
#include "dex_file.h"
#include "invoke_type.h"
#include "modifiers.h"
#include "object.h"
#include "object_callbacks.h"
#include "quick/quick_method_frame_info.h"
-#include "read_barrier.h"
+#include "read_barrier_option.h"
namespace art {
struct ArtMethodOffsets;
struct ConstructorMethodOffsets;
union JValue;
-struct MethodClassOffsets;
class MethodHelper;
class ScopedObjectAccessAlreadyRunnable;
class StringPiece;
@@ -39,14 +37,20 @@
namespace mirror {
-class StaticStorageBase;
-
typedef void (EntryPointFromInterpreter)(Thread* self, MethodHelper& mh,
const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result);
-// C++ mirror of java.lang.reflect.Method and java.lang.reflect.Constructor
-class MANAGED ArtMethod : public Object {
+// C++ mirror of java.lang.reflect.ArtMethod.
+class MANAGED ArtMethod FINAL : public Object {
public:
+ // Size of java.lang.reflect.ArtMethod.class.
+ static uint32_t ClassSize();
+
+ // Size of an instance of java.lang.reflect.ArtMethod not including its value array.
+ static constexpr uint32_t InstanceSize() {
+ return sizeof(ArtMethod);
+ }
+
static ArtMethod* FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa,
jobject jlr_method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -357,8 +361,6 @@
return kPointerSize;
}
- bool IsRegistered() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
void RegisterNative(Thread* self, const void* native_method, bool is_fast)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -411,11 +413,7 @@
static void SetClass(Class* java_lang_reflect_ArtMethod);
template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
- static Class* GetJavaLangReflectArtMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(java_lang_reflect_ArtMethod_ != nullptr);
- return ReadBarrier::BarrierForRoot<mirror::Class, kReadBarrierOption>(
- &java_lang_reflect_ArtMethod_);
- }
+ static Class* GetJavaLangReflectArtMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void ResetClass();
@@ -423,27 +421,45 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const DexFile* GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const char* GetDeclaringClassDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const char* GetShorty() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t unused_length;
return GetShorty(&unused_length);
}
+
const char* GetShorty(uint32_t* out_length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const Signature GetSignature() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const DexFile::CodeItem* GetCodeItem() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
bool IsResolvedTypeIdx(uint16_t type_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
int32_t GetLineNumFromDexPC(uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const DexFile::ProtoId& GetPrototype() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const DexFile::TypeList* GetParameterTypeList() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const char* GetDeclaringClassSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
uint16_t GetClassDefIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const DexFile::ClassDef& GetClassDef() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const char* GetReturnTypeDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const char* GetTypeDescriptorFromTypeIdx(uint16_t type_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
mirror::ClassLoader* GetClassLoader() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
ArtMethod* GetInterfaceMethodIfProxy() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
protected:
@@ -505,11 +521,6 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(ArtMethod);
};
-class MANAGED ArtMethodClass : public Class {
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(ArtMethodClass);
-};
-
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 2daa6e4..349d4a3 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -19,8 +19,8 @@
#include "class.h"
-#include "art_field.h"
-#include "art_method.h"
+#include "art_field-inl.h"
+#include "art_method-inl.h"
#include "class_linker-inl.h"
#include "class_loader.h"
#include "common_throws.h"
@@ -29,6 +29,7 @@
#include "gc/heap-inl.h"
#include "iftable.h"
#include "object_array-inl.h"
+#include "read_barrier-inl.h"
#include "runtime.h"
#include "string.h"
@@ -148,6 +149,23 @@
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable);
}
+inline ArtMethod* Class::GetEmbeddedImTableEntry(uint32_t i) {
+ uint32_t offset = EmbeddedImTableOffset().Uint32Value() + i * sizeof(ImTableEntry);
+ return GetFieldObject<mirror::ArtMethod>(MemberOffset(offset));
+}
+
+inline void Class::SetEmbeddedImTableEntry(uint32_t i, ArtMethod* method) {
+ uint32_t offset = EmbeddedImTableOffset().Uint32Value() + i * sizeof(ImTableEntry);
+ SetFieldObject<false>(MemberOffset(offset), method);
+ CHECK(method == GetImTable()->Get(i));
+}
+
+inline void Class::SetEmbeddedVTableEntry(uint32_t i, ArtMethod* method) {
+ uint32_t offset = EmbeddedVTableOffset().Uint32Value() + i * sizeof(VTableEntry);
+ SetFieldObject<false>(MemberOffset(offset), method);
+ CHECK(method == GetVTableDuringLinking()->Get(i));
+}
+
inline bool Class::Implements(Class* klass) {
DCHECK(klass != NULL);
DCHECK(klass->IsInterface()) << PrettyClass(this);
@@ -373,7 +391,8 @@
inline void Class::SetSFields(ObjectArray<ArtField>* new_sfields)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(NULL == GetFieldObject<ObjectArray<ArtField>>(OFFSET_OF_OBJECT_MEMBER(Class, sfields_)));
+ DCHECK((IsRetired() && new_sfields == nullptr) ||
+ (NULL == GetFieldObject<ObjectArray<ArtField>>(OFFSET_OF_OBJECT_MEMBER(Class, sfields_))));
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), new_sfields);
}
@@ -435,9 +454,9 @@
template<VerifyObjectFlags kVerifyFlags>
inline uint32_t Class::GetAccessFlags() {
- // Check class is loaded or this is java.lang.String that has a
+ // Check class is loaded/retired or this is java.lang.String that has a
// circularity issue during loading the names of its members
- DCHECK(IsLoaded<kVerifyFlags>() ||
+ DCHECK(IsIdxLoaded<kVerifyFlags>() || IsRetired<kVerifyFlags>() ||
IsErroneous<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>() ||
this == String::GetJavaLangString() ||
this == ArtField::GetJavaLangReflectArtField() ||
@@ -503,12 +522,63 @@
return Alloc<true>(self, Runtime::Current()->GetHeap()->GetCurrentNonMovingAllocator());
}
+inline uint32_t Class::ComputeClassSize(bool has_embedded_tables,
+ uint32_t num_vtable_entries,
+ uint32_t num_32bit_static_fields,
+ uint32_t num_64bit_static_fields,
+ uint32_t num_ref_static_fields) {
+ // Space used by java.lang.Class and its instance fields.
+ uint32_t size = sizeof(Class);
+ // Space used by embedded tables.
+ if (has_embedded_tables) {
+ uint32_t embedded_imt_size = kImtSize * sizeof(ImTableEntry);
+ uint32_t embedded_vtable_size = num_vtable_entries * sizeof(VTableEntry);
+ size += embedded_imt_size + embedded_vtable_size;
+ }
+ // Space used by reference statics.
+ size += num_ref_static_fields * sizeof(HeapReference<Object>);
+ // Possible pad for alignment.
+ if (((size & 7) != 0) && (num_64bit_static_fields > 0) && (num_32bit_static_fields == 0)) {
+ size += sizeof(uint32_t);
+ }
+ // Space used for primitive static fields.
+ size += (num_32bit_static_fields * sizeof(uint32_t)) +
+ (num_64bit_static_fields * sizeof(uint64_t));
+ return size;
+}
+
template <bool kVisitClass, typename Visitor>
inline void Class::VisitReferences(mirror::Class* klass, const Visitor& visitor) {
// Visit the static fields first so that we don't overwrite the SFields / IFields instance
// fields.
- VisitStaticFieldsReferences<kVisitClass>(this, visitor);
VisitInstanceFieldsReferences<kVisitClass>(klass, visitor);
+ if (!IsTemp()) {
+ // Temp classes don't ever populate imt/vtable or static fields and they are not even
+ // allocated with the right size for those.
+ VisitStaticFieldsReferences<kVisitClass>(this, visitor);
+ if (ShouldHaveEmbeddedImtAndVTable()) {
+ VisitEmbeddedImtAndVTable(visitor);
+ }
+ }
+}
+
+template<typename Visitor>
+inline void Class::VisitEmbeddedImtAndVTable(const Visitor& visitor) {
+ uint32_t pos = sizeof(mirror::Class);
+
+ size_t count = kImtSize;
+ for (size_t i = 0; i < count; ++i) {
+ MemberOffset offset = MemberOffset(pos);
+ visitor(this, offset, true);
+ pos += sizeof(ImTableEntry);
+ }
+
+ count = ((GetVTable() != NULL) ? GetVTable()->GetLength() : 0);
+ for (size_t i = 0; i < count; ++i) {
+ MemberOffset offset = MemberOffset(pos);
+ visitor(this, offset, true);
+ pos += sizeof(VTableEntry);
+ }
}
template<ReadBarrierOption kReadBarrierOption>
@@ -554,6 +624,36 @@
}
}
+inline ObjectArray<Class>* Class::GetInterfaces() {
+ CHECK(IsProxyClass());
+ // First static field.
+ DCHECK(GetSFields()->Get(0)->IsArtField());
+ DCHECK_STREQ(GetSFields()->Get(0)->GetName(), "interfaces");
+ MemberOffset field_offset = GetSFields()->Get(0)->GetOffset();
+ return GetFieldObject<ObjectArray<Class>>(field_offset);
+}
+
+inline ObjectArray<ObjectArray<Class>>* Class::GetThrows() {
+ CHECK(IsProxyClass());
+ // Second static field.
+ DCHECK(GetSFields()->Get(1)->IsArtField());
+ DCHECK_STREQ(GetSFields()->Get(1)->GetName(), "throws");
+ MemberOffset field_offset = GetSFields()->Get(1)->GetOffset();
+ return GetFieldObject<ObjectArray<ObjectArray<Class>>>(field_offset);
+}
+
+inline void Class::InitializeClassVisitor::operator()(
+ mirror::Object* obj, size_t usable_size) const {
+ DCHECK_LE(class_size_, usable_size);
+ // Avoid AsClass as object is not yet in live bitmap or allocation stack.
+ mirror::Class* klass = down_cast<mirror::Class*>(obj);
+ // DCHECK(klass->IsClass());
+ klass->SetClassSize(class_size_);
+ klass->SetPrimitiveType(Primitive::kPrimNot); // Default to not being primitive.
+ klass->SetDexClassDefIndex(DexFile::kDexNoIndex16); // Default to no valid class def index.
+ klass->SetDexTypeIndex(DexFile::kDexNoIndex16); // Default to no valid type index.
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index c6472c6..371e984 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -63,7 +63,8 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
bool class_linker_initialized = class_linker != nullptr && class_linker->IsInitialized();
if (LIKELY(class_linker_initialized)) {
- if (UNLIKELY(new_status <= old_status && new_status != kStatusError)) {
+ if (UNLIKELY(new_status <= old_status && new_status != kStatusError &&
+ new_status != kStatusRetired)) {
LOG(FATAL) << "Unexpected change back of class status for " << PrettyClass(this) << " "
<< old_status << " -> " << new_status;
}
@@ -113,11 +114,27 @@
} else {
SetField32Volatile<false>(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status);
}
- // Classes that are being resolved or initialized need to notify waiters that the class status
- // changed. See ClassLinker::EnsureResolved and ClassLinker::WaitForInitializeClass.
- if ((old_status >= kStatusResolved || new_status >= kStatusResolved) &&
- class_linker_initialized) {
- NotifyAll(self);
+
+ if (!class_linker_initialized) {
+ // When the class linker is being initialized its single threaded and by definition there can be
+ // no waiters. During initialization classes may appear temporary but won't be retired as their
+ // size was statically computed.
+ } else {
+ // Classes that are being resolved or initialized need to notify waiters that the class status
+ // changed. See ClassLinker::EnsureResolved and ClassLinker::WaitForInitializeClass.
+ if (IsTemp()) {
+ // Class is a temporary one, ensure that waiters for resolution get notified of retirement
+ // so that they can grab the new version of the class from the class linker's table.
+ CHECK_LT(new_status, kStatusResolved) << PrettyDescriptor(this);
+ if (new_status == kStatusRetired || new_status == kStatusError) {
+ NotifyAll(self);
+ }
+ } else {
+ CHECK_NE(new_status, kStatusRetired);
+ if (old_status >= kStatusResolved || new_status >= kStatusResolved) {
+ NotifyAll(self);
+ }
+ }
}
}
@@ -217,35 +234,39 @@
os << StringPrintf(" %2zd: %s (cl=%p)\n", i, PrettyClass(interface).c_str(), cl);
}
}
- // After this point, this may have moved due to GetDirectInterface.
- os << " vtable (" << h_this->NumVirtualMethods() << " entries, "
- << (h_super.Get() != nullptr ? h_super->NumVirtualMethods() : 0) << " in super):\n";
- for (size_t i = 0; i < NumVirtualMethods(); ++i) {
- os << StringPrintf(" %2zd: %s\n", i,
- PrettyMethod(h_this->GetVirtualMethodDuringLinking(i)).c_str());
- }
- os << " direct methods (" << h_this->NumDirectMethods() << " entries):\n";
- for (size_t i = 0; i < h_this->NumDirectMethods(); ++i) {
- os << StringPrintf(" %2zd: %s\n", i, PrettyMethod(h_this->GetDirectMethod(i)).c_str());
- }
- if (h_this->NumStaticFields() > 0) {
- os << " static fields (" << h_this->NumStaticFields() << " entries):\n";
- if (h_this->IsResolved() || h_this->IsErroneous()) {
- for (size_t i = 0; i < h_this->NumStaticFields(); ++i) {
- os << StringPrintf(" %2zd: %s\n", i, PrettyField(h_this->GetStaticField(i)).c_str());
- }
- } else {
- os << " <not yet available>";
+ if (!IsLoaded()) {
+ os << " class not yet loaded";
+ } else {
+ // After this point, this may have moved due to GetDirectInterface.
+ os << " vtable (" << h_this->NumVirtualMethods() << " entries, "
+ << (h_super.Get() != nullptr ? h_super->NumVirtualMethods() : 0) << " in super):\n";
+ for (size_t i = 0; i < NumVirtualMethods(); ++i) {
+ os << StringPrintf(" %2zd: %s\n", i,
+ PrettyMethod(h_this->GetVirtualMethodDuringLinking(i)).c_str());
}
- }
- if (h_this->NumInstanceFields() > 0) {
- os << " instance fields (" << h_this->NumInstanceFields() << " entries):\n";
- if (h_this->IsResolved() || h_this->IsErroneous()) {
- for (size_t i = 0; i < h_this->NumInstanceFields(); ++i) {
- os << StringPrintf(" %2zd: %s\n", i, PrettyField(h_this->GetInstanceField(i)).c_str());
+ os << " direct methods (" << h_this->NumDirectMethods() << " entries):\n";
+ for (size_t i = 0; i < h_this->NumDirectMethods(); ++i) {
+ os << StringPrintf(" %2zd: %s\n", i, PrettyMethod(h_this->GetDirectMethod(i)).c_str());
+ }
+ if (h_this->NumStaticFields() > 0) {
+ os << " static fields (" << h_this->NumStaticFields() << " entries):\n";
+ if (h_this->IsResolved() || h_this->IsErroneous()) {
+ for (size_t i = 0; i < h_this->NumStaticFields(); ++i) {
+ os << StringPrintf(" %2zd: %s\n", i, PrettyField(h_this->GetStaticField(i)).c_str());
+ }
+ } else {
+ os << " <not yet available>";
}
- } else {
- os << " <not yet available>";
+ }
+ if (h_this->NumInstanceFields() > 0) {
+ os << " instance fields (" << h_this->NumInstanceFields() << " entries):\n";
+ if (h_this->IsResolved() || h_this->IsErroneous()) {
+ for (size_t i = 0; i < h_this->NumInstanceFields(); ++i) {
+ os << StringPrintf(" %2zd: %s\n", i, PrettyField(h_this->GetInstanceField(i)).c_str());
+ }
+ } else {
+ os << " <not yet available>";
+ }
}
}
}
@@ -721,9 +742,7 @@
} else if (IsArrayClass()) {
return 2;
} else if (IsProxyClass()) {
- mirror::SynthesizedProxyClass* proxy_class=
- reinterpret_cast<mirror::SynthesizedProxyClass*>(this);
- mirror::ObjectArray<mirror::Class>* interfaces = proxy_class->GetInterfaces();
+ mirror::ObjectArray<mirror::Class>* interfaces = GetInterfaces();
return interfaces != nullptr ? interfaces->GetLength() : 0;
} else {
const DexFile::TypeList* interfaces = GetInterfaceTypeList();
@@ -753,9 +772,7 @@
return class_linker->FindSystemClass(self, "Ljava/io/Serializable;");
}
} else if (klass->IsProxyClass()) {
- mirror::SynthesizedProxyClass* proxy_class =
- reinterpret_cast<mirror::SynthesizedProxyClass*>(klass.Get());
- mirror::ObjectArray<mirror::Class>* interfaces = proxy_class->GetInterfaces();
+ mirror::ObjectArray<mirror::Class>* interfaces = klass.Get()->GetInterfaces();
DCHECK(interfaces != nullptr);
return interfaces->Get(idx);
} else {
@@ -798,5 +815,49 @@
return GetDexFile().GetInterfacesList(*class_def);
}
+void Class::PopulateEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ObjectArray<ArtMethod>* table = GetImTable();
+ if (table != nullptr) {
+ for (uint32_t i = 0; i < kImtSize; i++) {
+ SetEmbeddedImTableEntry(i, table->Get(i));
+ }
+ }
+
+ table = GetVTableDuringLinking();
+ CHECK(table != nullptr);
+ for (int32_t i = 0; i < table->GetLength(); i++) {
+ SetEmbeddedVTableEntry(i, table->Get(i));
+ }
+}
+
+Class* Class::CopyOf(Thread* self, int32_t new_length) {
+ DCHECK_GE(new_length, static_cast<int32_t>(sizeof(Class)));
+ // We may get copied by a compacting GC.
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_this(hs.NewHandle(this));
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ InitializeClassVisitor visitor(new_length);
+
+ mirror::Object* new_class =
+ kMovingClasses ? heap->AllocObject<true>(self, java_lang_Class_, new_length, visitor)
+ : heap->AllocNonMovableObject<true>(self, java_lang_Class_, new_length, visitor);
+ if (UNLIKELY(new_class == nullptr)) {
+ CHECK(self->IsExceptionPending()); // Expect an OOME.
+ return NULL;
+ }
+
+ mirror::Class* new_class_obj = new_class->AsClass();
+ memcpy(new_class_obj, h_this.Get(), sizeof(Class));
+
+ new_class_obj->SetStatus(kStatusResolving, self);
+ new_class_obj->PopulateEmbeddedImtAndVTable();
+ // Correct some fields.
+ new_class_obj->SetLockWord(LockWord(), false);
+ new_class_obj->SetClassSize(new_length);
+
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(new_class_obj);
+ return new_class_obj;
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 7ac53ea..0f42044 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -22,6 +22,7 @@
#include "invoke_type.h"
#include "modifiers.h"
#include "object.h"
+#include "object_array.h"
#include "object_callbacks.h"
#include "primitive.h"
#include "read_barrier.h"
@@ -62,7 +63,6 @@
namespace art {
-struct ClassClassOffsets;
struct ClassOffsets;
class Signature;
class StringPiece;
@@ -70,13 +70,29 @@
namespace mirror {
class ArtField;
+class ArtMethod;
class ClassLoader;
class DexCache;
class IfTable;
// C++ mirror of java.lang.Class
-class MANAGED Class : public Object {
+class MANAGED Class FINAL : public Object {
public:
+ // Interface method table size. Increasing this value reduces the chance of two interface methods
+ // colliding in the interface method table but increases the size of classes that implement
+ // (non-marker) interfaces.
+ static constexpr size_t kImtSize = 64;
+
+ // imtable entry embedded in class object.
+ struct MANAGED ImTableEntry {
+ HeapReference<ArtMethod> method;
+ };
+
+ // vtable entry embedded in class object.
+ struct MANAGED VTableEntry {
+ HeapReference<ArtMethod> method;
+ };
+
// Class Status
//
// kStatusNotReady: If a Class cannot be found in the class table by
@@ -95,6 +111,11 @@
// using ResolveClass to initialize the super_class_ and ensuring the
// interfaces are resolved.
//
+ // kStatusResolving: Class is just cloned with the right size from
+ // temporary class that's acting as a placeholder for linking. The old
+ // class will be retired. New class is set to this status first before
+ // moving on to being resolved.
+ //
// kStatusResolved: Still holding the lock on Class, the ClassLinker
// shows linking is complete and fields of the Class populated by making
// it kStatusResolved. Java allows circularities of the form where a super
@@ -109,18 +130,20 @@
//
// TODO: Explain the other states
enum Status {
+ kStatusRetired = -2,
kStatusError = -1,
kStatusNotReady = 0,
kStatusIdx = 1, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.
kStatusLoaded = 2, // DEX idx values resolved.
- kStatusResolved = 3, // Part of linking.
- kStatusVerifying = 4, // In the process of being verified.
- kStatusRetryVerificationAtRuntime = 5, // Compile time verification failed, retry at runtime.
- kStatusVerifyingAtRuntime = 6, // Retrying verification at runtime.
- kStatusVerified = 7, // Logically part of linking; done pre-init.
- kStatusInitializing = 8, // Class init in progress.
- kStatusInitialized = 9, // Ready to go.
- kStatusMax = 10,
+ kStatusResolving = 3, // Just cloned from temporary class object.
+ kStatusResolved = 4, // Part of linking.
+ kStatusVerifying = 5, // In the process of being verified.
+ kStatusRetryVerificationAtRuntime = 6, // Compile time verification failed, retry at runtime.
+ kStatusVerifyingAtRuntime = 7, // Retrying verification at runtime.
+ kStatusVerified = 8, // Logically part of linking; done pre-init.
+ kStatusInitializing = 9, // Class init in progress.
+ kStatusInitialized = 10, // Ready to go.
+ kStatusMax = 11,
};
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -136,6 +159,12 @@
return OFFSET_OF_OBJECT_MEMBER(Class, status_);
}
+ // Returns true if the class has been retired.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsRetired() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetStatus<kVerifyFlags>() == kStatusRetired;
+ }
+
// Returns true if the class has failed to link.
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsErroneous() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -274,6 +303,13 @@
}
}
+ // Returns true if this class is the placeholder and should retire and
+ // be replaced with a class with the right size for embedded imt/vtable.
+ bool IsTemp() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Status s = GetStatus();
+ return s < Status::kStatusResolving && ShouldHaveEmbeddedImtAndVTable();
+ }
+
String* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Returns the cached name.
void SetName(String* name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Sets the cached name.
// Computes the name, then sets the cached value.
@@ -451,6 +487,25 @@
void SetClassSize(uint32_t new_class_size)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Compute how many bytes would be used a class with the given elements.
+ static uint32_t ComputeClassSize(bool has_embedded_tables,
+ uint32_t num_vtable_entries,
+ uint32_t num_32bit_static_fields,
+ uint32_t num_64bit_static_fields,
+ uint32_t num_ref_static_fields);
+
+ // The size of java.lang.Class.class.
+ static uint32_t ClassClassSize() {
+ // The number of vtable entries in java.lang.Class.
+ uint32_t vtable_entries = Object::kVTableLength + 64;
+ return ComputeClassSize(true, vtable_entries, 0, 1, 0);
+ }
+
+ // The size of a java.lang.Class representing a primitive such as int.class.
+ static uint32_t PrimitiveClassSize() {
+ return ComputeClassSize(false, 0, 0, 0, 0);
+ }
+
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
uint32_t GetObjectSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -623,8 +678,6 @@
return OFFSET_OF_OBJECT_MEMBER(Class, vtable_);
}
- ObjectArray<ArtMethod>* GetImTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
void SetImTable(ObjectArray<ArtMethod>* new_imtable)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -632,6 +685,26 @@
return OFFSET_OF_OBJECT_MEMBER(Class, imtable_);
}
+ static MemberOffset EmbeddedImTableOffset() {
+ return MemberOffset(sizeof(Class));
+ }
+
+ static MemberOffset EmbeddedVTableOffset() {
+ return MemberOffset(sizeof(Class) + kImtSize * sizeof(mirror::Class::ImTableEntry));
+ }
+
+ bool ShouldHaveEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return IsInstantiable();
+ }
+
+ ArtMethod* GetEmbeddedImTableEntry(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void SetEmbeddedImTableEntry(uint32_t i, ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void SetEmbeddedVTableEntry(uint32_t i, ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void PopulateEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Given a method implemented by this class but potentially from a super class, return the
// specific implementation method for this class.
ArtMethod* FindVirtualMethodForVirtual(ArtMethod* method)
@@ -739,11 +812,6 @@
void SetReferenceInstanceOffsets(uint32_t new_reference_offsets)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Beginning of static field data
- static MemberOffset FieldsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(Class, fields_);
- }
-
// Returns the number of static fields containing reference types.
uint32_t NumReferenceStaticFields() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(IsResolved() || IsErroneous());
@@ -751,7 +819,7 @@
}
uint32_t NumReferenceStaticFieldsDuringLinking() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(IsLoaded() || IsErroneous());
+ DCHECK(IsLoaded() || IsErroneous() || IsRetired());
return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_));
}
@@ -865,25 +933,65 @@
template <bool kVisitClass, typename Visitor>
void VisitReferences(mirror::Class* klass, const Visitor& visitor)
- NO_THREAD_SAFETY_ANALYSIS;
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Visit references within the embedded tables of the class.
+ // TODO: remove NO_THREAD_SAFETY_ANALYSIS when annotalysis handles visitors better.
+ template<typename Visitor>
+ void VisitEmbeddedImtAndVTable(const Visitor& visitor) NO_THREAD_SAFETY_ANALYSIS;
std::string GetDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
bool DescriptorEquals(const char* match) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
std::string GetArrayDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const DexFile::ClassDef* GetClassDef() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
uint32_t NumDirectInterfaces() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
uint16_t GetDirectInterfaceTypeIdx(uint32_t idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
static mirror::Class* GetDirectInterface(Thread* self, Handle<mirror::Class> klass, uint32_t idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const char* GetSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
std::string GetLocation() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const DexFile& GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const DexFile::TypeList* GetInterfaceTypeList() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Asserts we are initialized or initializing in the given thread.
void AssertInitializedOrInitializingInThread(Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ Class* CopyOf(Thread* self, int32_t new_length)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // For proxy class only.
+ ObjectArray<Class>* GetInterfaces() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // For proxy class only.
+ ObjectArray<ObjectArray<Class>>* GetThrows() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Used to initialize a class in the allocation code path to ensure it is guarded by a StoreStore
+ // fence.
+ class InitializeClassVisitor {
+ public:
+ explicit InitializeClassVisitor(uint32_t class_size) : class_size_(class_size) {
+ }
+
+ void operator()(mirror::Object* obj, size_t usable_size) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+ const uint32_t class_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(InitializeClassVisitor);
+ };
+
private:
void SetVerifyErrorClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -902,6 +1010,8 @@
void CheckObjectAlloc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ObjectArray<ArtMethod>* GetImTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// defining class loader, or NULL for the "bootstrap" system loader
HeapReference<ClassLoader> class_loader_;
@@ -1012,7 +1122,12 @@
// values are kept in a table in gDvm.
// InitiatingLoaderList initiating_loader_list_;
- // Location of first static field.
+ // The following data exist in real class objects.
+ // Embedded Imtable, for class object that's not an interface, fixed size.
+ ImTableEntry embedded_imtable_[0];
+ // Embedded Vtable, for class object that's not an interface, variable size.
+ VTableEntry embedded_vtable_[0];
+ // Static fields, variable size.
uint32_t fields_[0];
// java.lang.Class
@@ -1024,14 +1139,6 @@
std::ostream& operator<<(std::ostream& os, const Class::Status& rhs);
-class MANAGED ClassClass : public Class {
- private:
- int32_t pad_;
- int64_t serialVersionUID_;
- friend struct art::ClassClassOffsets; // for verifying offset information
- DISALLOW_IMPLICIT_CONSTRUCTORS(ClassClass);
-};
-
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h
index 74dae38..f3594e4 100644
--- a/runtime/mirror/class_loader.h
+++ b/runtime/mirror/class_loader.h
@@ -27,6 +27,12 @@
// C++ mirror of java.lang.ClassLoader
class MANAGED ClassLoader : public Object {
+ public:
+ // Size of an instance of java.lang.ClassLoader.
+ static constexpr uint32_t InstanceSize() {
+ return sizeof(ClassLoader);
+ }
+
private:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
HeapReference<Object> packages_;
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 7e40f64..08cff99 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -24,6 +24,11 @@
namespace art {
namespace mirror {
+inline uint32_t DexCache::ClassSize() {
+ uint32_t vtable_entries = Object::kVTableLength + 1;
+ return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+}
+
inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ArtMethod* method = GetResolvedMethods()->Get(method_idx);
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 65a5026..bfd603a 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -18,10 +18,8 @@
#define ART_RUNTIME_MIRROR_DEX_CACHE_H_
#include "art_method.h"
-#include "class.h"
#include "object.h"
#include "object_array.h"
-#include "string.h"
namespace art {
@@ -33,15 +31,21 @@
namespace mirror {
class ArtField;
+class ArtMethod;
class Class;
+class String;
-class MANAGED DexCacheClass : public Class {
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(DexCacheClass);
-};
-
-class MANAGED DexCache : public Object {
+// C++ mirror of java.lang.DexCache.
+class MANAGED DexCache FINAL : public Object {
public:
+ // Size of java.lang.DexCache.class.
+ static uint32_t ClassSize();
+
+ // Size of an instance of java.lang.DexCache not including referenced values.
+ static constexpr uint32_t InstanceSize() {
+ return sizeof(DexCache);
+ }
+
void Init(const DexFile* dex_file,
String* location,
ObjectArray<String>* strings,
diff --git a/runtime/mirror/iftable.h b/runtime/mirror/iftable.h
index ad312ed..5feb602 100644
--- a/runtime/mirror/iftable.h
+++ b/runtime/mirror/iftable.h
@@ -23,7 +23,7 @@
namespace art {
namespace mirror {
-class MANAGED IfTable : public ObjectArray<Object> {
+class MANAGED IfTable FINAL : public ObjectArray<Object> {
public:
Class* GetInterface(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Class* interface = Get((i * kMax) + kInterface)->AsClass();
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 089ef57..3d45683 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -35,6 +35,11 @@
namespace art {
namespace mirror {
+inline uint32_t Object::ClassSize() {
+ uint32_t vtable_entries = kVTableLength;
+ return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+}
+
template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
inline Class* Object::GetClass() {
return GetFieldObject<Class, kVerifyFlags, kReadBarrierOption>(
@@ -75,6 +80,12 @@
OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), new_val.GetValue());
}
+inline bool Object::CasLockWordWeakRelaxed(LockWord old_val, LockWord new_val) {
+ // Force use of non-transactional mode and do not check.
+ return CasFieldWeakRelaxed32<false, false>(
+ OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), new_val.GetValue());
+}
+
inline uint32_t Object::GetLockOwnerThreadId() {
return Monitor::GetLockOwnerThreadId(this);
}
@@ -443,6 +454,8 @@
SetField32<kTransactionActive, kCheckTransaction, kVerifyFlags, true>(field_offset, new_value);
}
+// TODO: Pass memory_order_ and strong/weak as arguments to avoid code duplication?
+
template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
inline bool Object::CasFieldWeakSequentiallyConsistent32(MemberOffset field_offset,
int32_t old_value, int32_t new_value) {
@@ -461,6 +474,42 @@
return atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_value, new_value);
}
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldWeakRelaxed32(MemberOffset field_offset,
+ int32_t old_value, int32_t new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+ AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr);
+
+ return atomic_addr->CompareExchangeWeakRelaxed(old_value, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldStrongSequentiallyConsistent32(MemberOffset field_offset,
+ int32_t old_value, int32_t new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+ AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr);
+
+ return atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_value, new_value);
+}
+
template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
inline int64_t Object::GetField64(MemberOffset field_offset) {
if (kVerifyFlags & kVerifyThis) {
@@ -526,6 +575,23 @@
return atomic_addr->CompareExchangeWeakSequentiallyConsistent(old_value, new_value);
}
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldStrongSequentiallyConsistent64(MemberOffset field_offset,
+ int64_t old_value, int64_t new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteField64(this, field_offset, old_value, true);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+ Atomic<int64_t>* atomic_addr = reinterpret_cast<Atomic<int64_t>*>(raw_addr);
+ return atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_value, new_value);
+}
+
template<class T, VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption,
bool kIsVolatile>
inline T* Object::GetFieldObject(MemberOffset field_offset) {
@@ -644,6 +710,38 @@
return success;
}
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset,
+ Object* old_value, Object* new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ if (kVerifyFlags & kVerifyWrites) {
+ VerifyObject(new_value);
+ }
+ if (kVerifyFlags & kVerifyReads) {
+ VerifyObject(old_value);
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
+ }
+ HeapReference<Object> old_ref(HeapReference<Object>::FromMirrorPtr(old_value));
+ HeapReference<Object> new_ref(HeapReference<Object>::FromMirrorPtr(new_value));
+ byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+ Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
+
+ bool success = atomic_addr->CompareExchangeStrongSequentiallyConsistent(old_ref.reference_,
+ new_ref.reference_);
+
+ if (success) {
+ Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value);
+ }
+ return success;
+}
+
template<bool kVisitClass, bool kIsStatic, typename Visitor>
inline void Object::VisitFieldsReferences(uint32_t ref_offsets, const Visitor& visitor) {
if (LIKELY(ref_offsets != CLASS_WALK_SUPER)) {
@@ -687,6 +785,7 @@
template<bool kVisitClass, typename Visitor>
inline void Object::VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor) {
+ DCHECK(!klass->IsTemp());
klass->VisitFieldsReferences<kVisitClass, true>(
klass->GetReferenceStaticOffsets<kVerifyNone>(), visitor);
}
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index e58091f..5f88d54 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -156,7 +156,7 @@
// loop iteration.
LockWord hash_word(LockWord::FromHashCode(GenerateIdentityHashCode()));
DCHECK_EQ(hash_word.GetState(), LockWord::kHashCode);
- if (const_cast<Object*>(this)->CasLockWordWeakSequentiallyConsistent(lw, hash_word)) {
+ if (const_cast<Object*>(this)->CasLockWordWeakRelaxed(lw, hash_word)) {
return hash_word.GetHashCode();
}
break;
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index d29011a..4fae470 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -63,13 +63,24 @@
// C++ mirror of java.lang.Object
class MANAGED LOCKABLE Object {
public:
+ // The number of vtable entries in java.lang.Object.
+ static constexpr size_t kVTableLength = 11;
+
+ // The size of the java.lang.Class representing a java.lang.Object.
+ static uint32_t ClassSize();
+
+ // Size of an instance of java.lang.Object.
+ static constexpr uint32_t InstanceSize() {
+ return sizeof(Object);
+ }
+
static MemberOffset ClassOffset() {
return OFFSET_OF_OBJECT_MEMBER(Object, klass_);
}
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
- Class* GetClass() ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE Class* GetClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetClass(Class* new_klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -99,19 +110,14 @@
return OFFSET_OF_OBJECT_MEMBER(Object, monitor_);
}
- // As volatile can be false if the mutators are suspended. This is an optimization since it
+ // As_volatile can be false if the mutators are suspended. This is an optimization since it
// avoids the barriers.
LockWord GetLockWord(bool as_volatile) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetLockWord(LockWord new_val, bool as_volatile) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // All Cas operations defined here have C++11 memory_order_seq_cst ordering
- // semantics: Preceding memory operations become visible to other threads
- // before the CAS, and subsequent operations become visible after the CAS.
- // The Cas operations defined here do not fail spuriously, i.e. they
- // have C++11 "strong" semantics.
- // TODO: In most, possibly all, cases, these assumptions are too strong.
- // Confirm and weaken the implementation.
bool CasLockWordWeakSequentiallyConsistent(LockWord old_val, LockWord new_val)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool CasLockWordWeakRelaxed(LockWord old_val, LockWord new_val)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
uint32_t GetLockOwnerThreadId();
mirror::Object* MonitorEnter(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
@@ -202,27 +208,27 @@
// Accessor for Java type fields.
template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
ReadBarrierOption kReadBarrierOption = kWithReadBarrier, bool kIsVolatile = false>
- T* GetFieldObject(MemberOffset field_offset) ALWAYS_INLINE
+ ALWAYS_INLINE T* GetFieldObject(MemberOffset field_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
- T* GetFieldObjectVolatile(MemberOffset field_offset) ALWAYS_INLINE
+ ALWAYS_INLINE T* GetFieldObjectVolatile(MemberOffset field_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
- void SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value)
- ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ALWAYS_INLINE void SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
- void SetFieldObject(MemberOffset field_offset, Object* new_value) ALWAYS_INLINE
+ ALWAYS_INLINE void SetFieldObject(MemberOffset field_offset, Object* new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void SetFieldObjectVolatile(MemberOffset field_offset, Object* new_value) ALWAYS_INLINE
+ ALWAYS_INLINE void SetFieldObjectVolatile(MemberOffset field_offset, Object* new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive, bool kCheckTransaction = true,
@@ -231,49 +237,67 @@
Object* new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset, Object* old_value,
+ Object* new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
HeapReference<Object>* GetFieldObjectReferenceAddr(MemberOffset field_offset);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
- int32_t GetField32(MemberOffset field_offset) ALWAYS_INLINE
+ ALWAYS_INLINE int32_t GetField32(MemberOffset field_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- int32_t GetField32Volatile(MemberOffset field_offset) ALWAYS_INLINE
+ ALWAYS_INLINE int32_t GetField32Volatile(MemberOffset field_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
- void SetField32(MemberOffset field_offset, int32_t new_value) ALWAYS_INLINE
+ ALWAYS_INLINE void SetField32(MemberOffset field_offset, int32_t new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void SetField32Volatile(MemberOffset field_offset, int32_t new_value) ALWAYS_INLINE
+ ALWAYS_INLINE void SetField32Volatile(MemberOffset field_offset, int32_t new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- bool CasFieldWeakSequentiallyConsistent32(MemberOffset field_offset, int32_t old_value,
- int32_t new_value) ALWAYS_INLINE
+ ALWAYS_INLINE bool CasFieldWeakSequentiallyConsistent32(MemberOffset field_offset,
+ int32_t old_value, int32_t new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool CasFieldWeakRelaxed32(MemberOffset field_offset, int32_t old_value,
+ int32_t new_value) ALWAYS_INLINE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool CasFieldStrongSequentiallyConsistent32(MemberOffset field_offset, int32_t old_value,
+ int32_t new_value) ALWAYS_INLINE
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
- int64_t GetField64(MemberOffset field_offset) ALWAYS_INLINE
+ ALWAYS_INLINE int64_t GetField64(MemberOffset field_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- int64_t GetField64Volatile(MemberOffset field_offset) ALWAYS_INLINE
+ ALWAYS_INLINE int64_t GetField64Volatile(MemberOffset field_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
- void SetField64(MemberOffset field_offset, int64_t new_value) ALWAYS_INLINE
+ ALWAYS_INLINE void SetField64(MemberOffset field_offset, int64_t new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- void SetField64Volatile(MemberOffset field_offset, int64_t new_value) ALWAYS_INLINE
+ ALWAYS_INLINE void SetField64Volatile(MemberOffset field_offset, int64_t new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive, bool kCheckTransaction = true,
@@ -283,6 +307,12 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool CasFieldStrongSequentiallyConsistent64(MemberOffset field_offset, int64_t old_value,
+ int64_t new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename T>
void SetFieldPtr(MemberOffset field_offset, T new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h
index 54d1240..7012b19 100644
--- a/runtime/mirror/object_array.h
+++ b/runtime/mirror/object_array.h
@@ -23,8 +23,13 @@
namespace mirror {
template<class T>
-class MANAGED ObjectArray : public Array {
+class MANAGED ObjectArray: public Array {
public:
+ // The size of Object[].class.
+ static uint32_t ClassSize() {
+ return Array::ClassSize();
+ }
+
static ObjectArray<T>* Alloc(Thread* self, Class* object_array_class, int32_t length,
gc::AllocatorType allocator_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index f85fb27..7e1de5d 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -28,7 +28,7 @@
#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "dex_file.h"
-#include "entrypoints/entrypoint_utils.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/heap.h"
#include "iftable-inl.h"
diff --git a/runtime/mirror/proxy.h b/runtime/mirror/proxy.h
index 6e4947e..db511d6 100644
--- a/runtime/mirror/proxy.h
+++ b/runtime/mirror/proxy.h
@@ -25,28 +25,8 @@
namespace mirror {
-// All proxy objects have a class which is a synthesized proxy class. The synthesized proxy class
-// has the static fields used to implement reflection on proxy objects.
-class MANAGED SynthesizedProxyClass : public Class {
- public:
- ObjectArray<Class>* GetInterfaces() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetFieldObject<ObjectArray<Class>>(OFFSET_OF_OBJECT_MEMBER(SynthesizedProxyClass,
- interfaces_));
- }
-
- ObjectArray<ObjectArray<Class>>* GetThrows() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetFieldObject<ObjectArray<ObjectArray<Class>>>(OFFSET_OF_OBJECT_MEMBER(SynthesizedProxyClass,
- throws_));
- }
-
- private:
- HeapReference<ObjectArray<Class>> interfaces_;
- HeapReference<ObjectArray<ObjectArray<Class>>> throws_;
- DISALLOW_IMPLICIT_CONSTRUCTORS(SynthesizedProxyClass);
-};
-
// C++ mirror of java.lang.reflect.Proxy.
-class MANAGED Proxy : public Object {
+class MANAGED Proxy FINAL : public Object {
private:
HeapReference<Object> h_;
diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h
index abecbc5..52b0927 100644
--- a/runtime/mirror/stack_trace_element.h
+++ b/runtime/mirror/stack_trace_element.h
@@ -29,7 +29,7 @@
namespace mirror {
// C++ mirror of java.lang.StackTraceElement
-class MANAGED StackTraceElement : public Object {
+class MANAGED StackTraceElement FINAL : public Object {
public:
String* GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, declaring_class_));
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 315f7b1..6736497 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_MIRROR_STRING_INL_H_
#include "array.h"
+#include "class.h"
#include "intern_table.h"
#include "runtime.h"
#include "string.h"
@@ -26,6 +27,11 @@
namespace art {
namespace mirror {
+inline uint32_t String::ClassSize() {
+ uint32_t vtable_entries = Object::kVTableLength + 51;
+ return Class::ComputeClassSize(true, vtable_entries, 1, 1, 2);
+}
+
inline CharArray* String::GetCharArray() {
return GetFieldObject<CharArray>(ValueOffset());
}
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index b8acede..8ab4db9 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -19,22 +19,28 @@
#include <gtest/gtest.h>
-#include "class.h"
#include "object_callbacks.h"
#include "read_barrier.h"
namespace art {
template<class T> class Handle;
-struct StringClassOffsets;
struct StringOffsets;
class StringPiece;
namespace mirror {
// C++ mirror of java.lang.String
-class MANAGED String : public Object {
+class MANAGED String FINAL : public Object {
public:
+ // Size of java.lang.String.class.
+ static uint32_t ClassSize();
+
+ // Size of an instance of java.lang.String not including its value array.
+ static constexpr uint32_t InstanceSize() {
+ return sizeof(String);
+ }
+
static MemberOffset CountOffset() {
return OFFSET_OF_OBJECT_MEMBER(String, count_);
}
@@ -160,16 +166,6 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(String);
};
-class MANAGED StringClass : public Class {
- private:
- HeapReference<CharArray> ASCII_;
- HeapReference<Object> CASE_INSENSITIVE_ORDER_;
- uint32_t REPLACEMENT_CHAR_;
- int64_t serialVersionUID_;
- friend struct art::StringClassOffsets; // for verifying offset information
- DISALLOW_IMPLICIT_CONSTRUCTORS(StringClass);
-};
-
} // namespace mirror
} // namespace art
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index e619dda..cede1a0 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -21,7 +21,6 @@
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
-#include "mirror/proxy.h"
#include "object_utils.h"
#include "scoped_thread_state_change.h"
#include "scoped_fast_native_object_access.h"
@@ -91,8 +90,7 @@
static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) {
ScopedFastNativeObjectAccess soa(env);
- mirror::SynthesizedProxyClass* c =
- down_cast<mirror::SynthesizedProxyClass*>(DecodeClass(soa, javaThis));
+ mirror::Class* c = DecodeClass(soa, javaThis);
return soa.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone(soa.Self()));
}
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index 22e81e4..ac602ac 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -21,7 +21,6 @@
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
-#include "mirror/proxy.h"
#include "object_utils.h"
#include "reflection.h"
#include "scoped_fast_native_object_access.h"
@@ -39,8 +38,7 @@
ScopedFastNativeObjectAccess soa(env);
mirror::ArtMethod* proxy_method = mirror::ArtMethod::FromReflectedMethod(soa, javaMethod);
CHECK(proxy_method->GetDeclaringClass()->IsProxyClass());
- mirror::SynthesizedProxyClass* proxy_class =
- down_cast<mirror::SynthesizedProxyClass*>(proxy_method->GetDeclaringClass());
+ mirror::Class* proxy_class = proxy_method->GetDeclaringClass();
int throws_index = -1;
size_t num_virt_methods = proxy_class->NumVirtualMethods();
for (size_t i = 0; i < num_virt_methods; i++) {
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 7cc4cac..65dece0 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -28,8 +28,8 @@
ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
// JNI must use non transactional mode.
- bool success = obj->CasFieldWeakSequentiallyConsistent32<false>(MemberOffset(offset),
- expectedValue, newValue);
+ bool success = obj->CasFieldStrongSequentiallyConsistent32<false>(MemberOffset(offset),
+ expectedValue, newValue);
return success ? JNI_TRUE : JNI_FALSE;
}
@@ -38,8 +38,8 @@
ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
// JNI must use non transactional mode.
- bool success = obj->CasFieldWeakSequentiallyConsistent64<false>(MemberOffset(offset),
- expectedValue, newValue);
+ bool success = obj->CasFieldStrongSequentiallyConsistent64<false>(MemberOffset(offset),
+ expectedValue, newValue);
return success ? JNI_TRUE : JNI_FALSE;
}
@@ -50,8 +50,8 @@
mirror::Object* expectedValue = soa.Decode<mirror::Object*>(javaExpectedValue);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
// JNI must use non transactional mode.
- bool success = obj->CasFieldWeakSequentiallyConsistentObject<false>(MemberOffset(offset),
- expectedValue, newValue);
+ bool success = obj->CasFieldStrongSequentiallyConsistentObject<false>(MemberOffset(offset),
+ expectedValue, newValue);
return success ? JNI_TRUE : JNI_FALSE;
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 70253af..44f4466 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -24,7 +24,7 @@
#include "dex_file.h"
#include "invoke_type.h"
#include "mem_map.h"
-#include "mirror/art_method.h"
+#include "mirror/class.h"
#include "oat.h"
#include "os.h"
diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h
index d8c1c40..0e6f4d8 100644
--- a/runtime/object_callbacks.h
+++ b/runtime/object_callbacks.h
@@ -26,10 +26,10 @@
namespace art {
namespace mirror {
-class Class;
-class Object;
-template<class MirrorType> class HeapReference;
-class Reference;
+ class Class;
+ class Object;
+ template<class MirrorType> class HeapReference;
+ class Reference;
} // namespace mirror
class StackVisitor;
diff --git a/runtime/object_utils.h b/runtime/object_utils.h
index 28ce8f3..4379b4a 100644
--- a/runtime/object_utils.h
+++ b/runtime/object_utils.h
@@ -25,7 +25,6 @@
#include "mirror/class.h"
#include "mirror/dex_cache.h"
#include "mirror/iftable.h"
-#include "mirror/proxy.h"
#include "mirror/string.h"
#include "runtime.h"
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index e1e133f..a016cc5 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -197,13 +197,18 @@
#else
#error "ART default GC type must be set"
#endif
+ // If we are using homogeneous space compaction then default background compaction to off since
+ // homogeneous space compactions when we transition to not jank perceptible.
+ use_homogeneous_space_compaction_for_oom_ = false;
// If background_collector_type_ is kCollectorTypeNone, it defaults to the collector_type_ after
- // parsing options.
+ // parsing options. If you set this to kCollectorTypeHSpaceCompact then we will do an hspace
+ // compaction when we transition to background instead of a normal collector transition.
background_collector_type_ = gc::kCollectorTypeSS;
stack_size_ = 0; // 0 means default.
max_spins_before_thin_lock_inflation_ = Monitor::kDefaultMaxSpinsBeforeThinLockInflation;
low_memory_mode_ = false;
use_tlab_ = false;
+ min_interval_homogeneous_space_compaction_by_oom_ = MsToNs(100 * 1000); // 100s.
verify_pre_gc_heap_ = false;
// Pre sweeping is the one that usually fails if the GC corrupted the heap.
verify_pre_sweeping_heap_ = kIsDebugBuild;
@@ -416,6 +421,10 @@
low_memory_mode_ = true;
} else if (option == "-XX:UseTLAB") {
use_tlab_ = true;
+ } else if (option == "-XX:EnableHSpaceCompactForOOM") {
+ use_homogeneous_space_compaction_for_oom_ = true;
+ } else if (option == "-XX:DisableHSpaceCompactForOOM") {
+ use_homogeneous_space_compaction_for_oom_ = false;
} else if (StartsWith(option, "-D")) {
properties_.push_back(option.substr(strlen("-D")));
} else if (StartsWith(option, "-Xjnitrace:")) {
@@ -439,12 +448,17 @@
if (!ParseStringAfterChar(option, '=', &substring)) {
return false;
}
- gc::CollectorType collector_type = ParseCollectorType(substring);
- if (collector_type != gc::kCollectorTypeNone) {
- background_collector_type_ = collector_type;
+ // Special handling for HSpaceCompact since this is only valid as a background GC type.
+ if (substring == "HSpaceCompact") {
+ background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
} else {
- Usage("Unknown -XX:BackgroundGC option %s\n", substring.c_str());
- return false;
+ gc::CollectorType collector_type = ParseCollectorType(substring);
+ if (collector_type != gc::kCollectorTypeNone) {
+ background_collector_type_ = collector_type;
+ } else {
+ Usage("Unknown -XX:BackgroundGC option %s\n", substring.c_str());
+ return false;
+ }
}
} else if (option == "-XX:+DisableExplicitGC") {
is_explicit_gc_disabled_ = true;
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index d0f3c12..4c74be6 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -88,6 +88,12 @@
static constexpr uint32_t kExplicitSuspendCheck = 2;
static constexpr uint32_t kExplicitStackOverflowCheck = 4;
uint32_t explicit_checks_;
+ // Whether or not we use homogeneous space compaction to avoid OOM errors. If enabled,
+ // the heap will attempt to create an extra space which enables compacting from a malloc space to
+ // another malloc space when we are about to throw OOM.
+ bool use_homogeneous_space_compaction_for_oom_;
+ // Minimal interval allowed between two homogeneous space compactions caused by OOM.
+ uint64_t min_interval_homogeneous_space_compaction_by_oom_;
private:
ParsedOptions() {}
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 1034923..49f6fe0 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -20,6 +20,9 @@
#include "entrypoints/entrypoint_utils.h"
#include "handle_scope-inl.h"
#include "mirror/art_method-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/throwable.h"
#include "verifier/method_verifier.h"
namespace art {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index efa205e..94d6cdf 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -638,7 +638,9 @@
options->verify_post_gc_heap_,
options->verify_pre_gc_rosalloc_,
options->verify_pre_sweeping_rosalloc_,
- options->verify_post_gc_rosalloc_);
+ options->verify_post_gc_rosalloc_,
+ options->use_homogeneous_space_compaction_for_oom_,
+ options->min_interval_homogeneous_space_compaction_by_oom_);
dump_gc_performance_on_shutdown_ = options->dump_gc_performance_on_shutdown_;
@@ -1013,8 +1015,8 @@
method->SetEntryPointFromPortableCompiledCode(nullptr);
method->SetEntryPointFromQuickCompiledCode(nullptr);
} else {
- method->SetEntryPointFromPortableCompiledCode(GetPortableImtConflictTrampoline(class_linker));
- method->SetEntryPointFromQuickCompiledCode(GetQuickImtConflictTrampoline(class_linker));
+ method->SetEntryPointFromPortableCompiledCode(class_linker->GetPortableImtConflictTrampoline());
+ method->SetEntryPointFromQuickCompiledCode(class_linker->GetQuickImtConflictTrampoline());
}
return method.Get();
}
@@ -1033,8 +1035,8 @@
method->SetEntryPointFromPortableCompiledCode(nullptr);
method->SetEntryPointFromQuickCompiledCode(nullptr);
} else {
- method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionTrampoline(class_linker));
- method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionTrampoline(class_linker));
+ method->SetEntryPointFromPortableCompiledCode(class_linker->GetPortableResolutionTrampoline());
+ method->SetEntryPointFromQuickCompiledCode(class_linker->GetQuickResolutionTrampoline());
}
return method.Get();
}
diff --git a/runtime/thread.h b/runtime/thread.h
index 4312741..bf125f9 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -47,7 +47,7 @@
namespace gc {
namespace collector {
-class SemiSpace;
+ class SemiSpace;
} // namespace collector
} // namespace gc
@@ -61,7 +61,6 @@
template<class T> class PrimitiveArray;
typedef PrimitiveArray<int32_t> IntArray;
class StackTraceElement;
- class StaticStorageBase;
class Throwable;
} // namespace mirror
class BaseMutex;
@@ -781,7 +780,7 @@
void RevokeThreadLocalAllocationStack();
size_t GetThreadLocalBytesAllocated() const {
- return tlsPtr_.thread_local_pos - tlsPtr_.thread_local_start;
+ return tlsPtr_.thread_local_end - tlsPtr_.thread_local_start;
}
size_t GetThreadLocalObjectsAllocated() const {
@@ -900,7 +899,7 @@
// first if possible.
/***********************************************************************************************/
- struct PACKED(4) tls_32bit_sized_values {
+ struct PACKED(4) tls_32bit_sized_values {
// We have no control over the size of 'bool', but want our boolean fields
// to be 4-byte quantities.
typedef uint32_t bool32_t;
diff --git a/runtime/utils.h b/runtime/utils.h
index 448c591..b47de81 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -167,6 +167,10 @@
// For rounding integers.
template<typename T>
+static constexpr T RoundDown(T x, typename TypeIdentity<T>::type n)
+ __attribute__((warn_unused_result));
+
+template<typename T>
static constexpr T RoundDown(T x, typename TypeIdentity<T>::type n) {
return
DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))
@@ -174,17 +178,27 @@
}
template<typename T>
+static constexpr T RoundUp(T x, typename TypeIdentity<T>::type n)
+ __attribute__((warn_unused_result));
+
+template<typename T>
static constexpr T RoundUp(T x, typename TypeIdentity<T>::type n) {
return RoundDown(x + n - 1, n);
}
// For aligning pointers.
template<typename T>
+static inline T* AlignDown(T* x, uintptr_t n) __attribute__((warn_unused_result));
+
+template<typename T>
static inline T* AlignDown(T* x, uintptr_t n) {
return reinterpret_cast<T*>(RoundDown(reinterpret_cast<uintptr_t>(x), n));
}
template<typename T>
+static inline T* AlignUp(T* x, uintptr_t n) __attribute__((warn_unused_result));
+
+template<typename T>
static inline T* AlignUp(T* x, uintptr_t n) {
return reinterpret_cast<T*>(RoundUp(reinterpret_cast<uintptr_t>(x), n));
}
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index 9ac04d7..f70faf5 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -19,7 +19,7 @@
#include <stdio.h>
#include <memory>
-#include "class_linker.h"
+#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "dex_file.h"
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index f412034..739dbf8 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -61,9 +61,6 @@
test_Memory_pokeShort();
test_Memory_pokeInt();
test_Memory_pokeLong();
- test_AtomicBoolean_compareAndSet();
- test_AtomicInteger_compareAndSet();
- test_AtomicLong_compareAndSet();
}
/*
@@ -96,60 +93,6 @@
Assert.assertNotNull(Thread.currentThread());
}
- /**
- * Will test inlining CAS, by inclusion of AtomicBoolean in core.oat.
- */
- public static void test_AtomicBoolean_compareAndSet() {
- java.util.concurrent.atomic.AtomicBoolean ab = new java.util.concurrent.atomic.AtomicBoolean();
- Assert.assertEquals(ab.compareAndSet(false, false), true);
- Assert.assertEquals(ab.compareAndSet(true, false), false);
- Assert.assertEquals(ab.compareAndSet(true, true), false);
- Assert.assertEquals(ab.compareAndSet(false, true), true);
- Assert.assertEquals(ab.compareAndSet(false, true), false);
- Assert.assertEquals(ab.compareAndSet(false, false), false);
- Assert.assertEquals(ab.compareAndSet(true, true), true);
- Assert.assertEquals(ab.compareAndSet(true, false), true);
- Assert.assertEquals(ab.compareAndSet(true, false), false);
- Assert.assertEquals(ab.compareAndSet(true, true), false);
- Assert.assertEquals(ab.compareAndSet(false, false), true);
- }
-
- /**
- * Will test inlining CAS, by inclusion of AtomicInteger in core.oat.
- */
- public static void test_AtomicInteger_compareAndSet() {
- java.util.concurrent.atomic.AtomicInteger ab = new java.util.concurrent.atomic.AtomicInteger();
- Assert.assertEquals(ab.compareAndSet(0, 0), true);
- Assert.assertEquals(ab.compareAndSet(0x12345678, 0), false);
- Assert.assertEquals(ab.compareAndSet(0x12345678, 0x12345678), false);
- Assert.assertEquals(ab.compareAndSet(0, 0x12345678), true);
- Assert.assertEquals(ab.compareAndSet(0, 0x12345678), false);
- Assert.assertEquals(ab.compareAndSet(0, 0), false);
- Assert.assertEquals(ab.compareAndSet(0x12345678, 0x12345678), true);
- Assert.assertEquals(ab.compareAndSet(0x12345678, 0), true);
- Assert.assertEquals(ab.compareAndSet(0x12345678, 0), false);
- Assert.assertEquals(ab.compareAndSet(0x12345678, 0x12345678), false);
- Assert.assertEquals(ab.compareAndSet(0, 0), true);
- }
-
- /**
- * Will test inlining CAS, by inclusion of AtomicLong in core.oat.
- */
- public static void test_AtomicLong_compareAndSet() {
- java.util.concurrent.atomic.AtomicLong ab = new java.util.concurrent.atomic.AtomicLong();
- Assert.assertEquals(ab.compareAndSet(0l, 0l), true);
- Assert.assertEquals(ab.compareAndSet(0x1234567890l, 0l), false);
- Assert.assertEquals(ab.compareAndSet(0x1234567890l, 0x1234567890l), false);
- Assert.assertEquals(ab.compareAndSet(0l, 0x1234567890l), true);
- Assert.assertEquals(ab.compareAndSet(0l, 0x1234567890l), false);
- Assert.assertEquals(ab.compareAndSet(0l, 0l), false);
- Assert.assertEquals(ab.compareAndSet(0x1234567890l, 0x1234567890l), true);
- Assert.assertEquals(ab.compareAndSet(0x1234567890l, 0l), true);
- Assert.assertEquals(ab.compareAndSet(0x1234567890l, 0l), false);
- Assert.assertEquals(ab.compareAndSet(0x1234567890l, 0x1234567890l), false);
- Assert.assertEquals(ab.compareAndSet(0l, 0l), true);
- }
-
public static void test_String_length() {
String str0 = "";
String str1 = "x";
@@ -580,6 +523,7 @@
static Object runtime;
static Method address_of;
+ static Method new_non_movable_array;
static Method peek_byte;
static Method peek_short;
static Method peek_int;
@@ -594,6 +538,7 @@
Method get_runtime = vm_runtime.getDeclaredMethod("getRuntime");
runtime = get_runtime.invoke(null);
address_of = vm_runtime.getDeclaredMethod("addressOf", Object.class);
+ new_non_movable_array = vm_runtime.getDeclaredMethod("newNonMovableArray", Class.class, Integer.TYPE);
Class<?> io_memory = Class.forName("libcore.io.Memory");
peek_byte = io_memory.getDeclaredMethod("peekByte", Long.TYPE);
@@ -607,7 +552,7 @@
}
public static void test_Memory_peekByte() throws Exception {
- byte[] b = new byte [2];
+ byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 2);
b[0] = 0x12;
b[1] = 0x11;
long address = (long)address_of.invoke(runtime, b);
@@ -616,7 +561,7 @@
}
public static void test_Memory_peekShort() throws Exception {
- byte[] b = new byte [3];
+ byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 3);
b[0] = 0x13;
b[1] = 0x12;
b[2] = 0x11;
@@ -626,7 +571,7 @@
}
public static void test_Memory_peekInt() throws Exception {
- byte[] b = new byte [5];
+ byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 5);
b[0] = 0x15;
b[1] = 0x14;
b[2] = 0x13;
@@ -638,7 +583,7 @@
}
public static void test_Memory_peekLong() throws Exception {
- byte[] b = new byte [9];
+ byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 9);
b[0] = 0x19;
b[1] = 0x18;
b[2] = 0x17;
@@ -655,7 +600,7 @@
public static void test_Memory_pokeByte() throws Exception {
byte[] r = {0x11, 0x12};
- byte[] b = new byte [2];
+ byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 2);
long address = (long)address_of.invoke(runtime, b);
poke_byte.invoke(null, address, (byte)0x11);
poke_byte.invoke(null, address + 1, (byte)0x12);
@@ -665,7 +610,7 @@
public static void test_Memory_pokeShort() throws Exception {
byte[] ra = {0x12, 0x11, 0x13};
byte[] ru = {0x12, 0x22, 0x21};
- byte[] b = new byte [3];
+ byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 3);
long address = (long)address_of.invoke(runtime, b);
// Aligned write
@@ -681,7 +626,7 @@
public static void test_Memory_pokeInt() throws Exception {
byte[] ra = {0x14, 0x13, 0x12, 0x11, 0x15};
byte[] ru = {0x14, 0x24, 0x23, 0x22, 0x21};
- byte[] b = new byte [5];
+ byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 5);
long address = (long)address_of.invoke(runtime, b);
b[4] = 0x15;
@@ -695,7 +640,7 @@
public static void test_Memory_pokeLong() throws Exception {
byte[] ra = {0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x19};
byte[] ru = {0x18, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21};
- byte[] b = new byte [9];
+ byte[] b = (byte[])new_non_movable_array.invoke(runtime, Byte.TYPE, 9);
long address = (long)address_of.invoke(runtime, b);
b[8] = 0x19;