Merge "Fix longstanding bug around implicit NPEs and GC, version 2."
diff --git a/build/art.go b/build/art.go
index 3dabce3..61b1a4e 100644
--- a/build/art.go
+++ b/build/art.go
@@ -66,8 +66,12 @@
"-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1")
}
- cdexLevel := envDefault(ctx, "ART_DEFAULT_COMPACT_DEX_LEVEL", "fast")
- cflags = append(cflags, "-DART_DEFAULT_COMPACT_DEX_LEVEL="+cdexLevel)
+ if envTrue(ctx, "ART_USE_GENERATIONAL_CC") {
+ cflags = append(cflags, "-DART_USE_GENERATIONAL_CC=1")
+ }
+
+ cdexLevel := envDefault(ctx, "ART_DEFAULT_COMPACT_DEX_LEVEL", "fast")
+ cflags = append(cflags, "-DART_DEFAULT_COMPACT_DEX_LEVEL="+cdexLevel)
// We need larger stack overflow guards for ASAN, as the compiled code will have
// larger frame sizes. For simplicity, just use global not-target-specific cflags.
@@ -312,19 +316,19 @@
codegen(ctx, c, true)
type props struct {
- Target struct {
- Android struct {
- Shared_libs []string
- }
- }
+ Target struct {
+ Android struct {
+ Shared_libs []string
+ }
+ }
}
p := &props{}
// TODO: express this in .bp instead b/79671158
if !envTrue(ctx, "ART_TARGET_LINUX") {
- p.Target.Android.Shared_libs = []string {
- "libmetricslogger",
- }
+ p.Target.Android.Shared_libs = []string{
+ "libmetricslogger",
+ }
}
ctx.AppendProperties(p)
})
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index a52e163..a33d537 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -59,7 +59,7 @@
template <typename T>
bool UsuallyEquals(const T& expected, const T& actual,
typename std::enable_if<
- detail::SupportsEqualityOperator<T>::value>::type* = 0) {
+ detail::SupportsEqualityOperator<T>::value>::type* = nullptr) {
return expected == actual;
}
@@ -73,8 +73,8 @@
template <typename T, typename ... Ignore>
bool UsuallyEquals(const T& expected, const T& actual,
const Ignore& ... more ATTRIBUTE_UNUSED,
- typename std::enable_if<std::is_pod<T>::value>::type* = 0,
- typename std::enable_if<!detail::SupportsEqualityOperator<T>::value>::type* = 0
+ typename std::enable_if<std::is_pod<T>::value>::type* = nullptr,
+ typename std::enable_if<!detail::SupportsEqualityOperator<T>::value>::type* = nullptr
) {
return memcmp(std::addressof(expected), std::addressof(actual), sizeof(T)) == 0;
}
diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h
index 65c1114..d011e7f 100644
--- a/cmdline/detail/cmdline_parse_argument_detail.h
+++ b/cmdline/detail/cmdline_parse_argument_detail.h
@@ -90,7 +90,7 @@
struct CmdlineParserArgumentInfo {
// This version will only be used if TArg is arithmetic and thus has the <= operators.
template <typename T = TArg> // Necessary to get SFINAE to kick in.
- bool CheckRange(const TArg& value, typename EnableIfNumeric<T>::type* = 0) {
+ bool CheckRange(const TArg& value, typename EnableIfNumeric<T>::type* = nullptr) {
if (has_range_) {
return min_ <= value && value <= max_;
}
@@ -99,7 +99,7 @@
// This version will be used at other times when TArg is not arithmetic.
template <typename T = TArg>
- bool CheckRange(const TArg&, typename DisableIfNumeric<T>::type* = 0) {
+ bool CheckRange(const TArg&, typename DisableIfNumeric<T>::type* = nullptr) {
assert(!has_range_);
return true;
}
diff --git a/cmdline/detail/cmdline_parser_detail.h b/cmdline/detail/cmdline_parser_detail.h
index 4c26ba3..2078d7a 100644
--- a/cmdline/detail/cmdline_parser_detail.h
+++ b/cmdline/detail/cmdline_parser_detail.h
@@ -90,7 +90,7 @@
template <typename T>
std::string ToStringAny(const T& value,
typename std::enable_if<
- SupportsInsertionOperator<T>::value>::type* = 0) {
+ SupportsInsertionOperator<T>::value>::type* = nullptr) {
std::stringstream stream;
stream << value;
return stream.str();
@@ -99,7 +99,7 @@
template <typename T>
std::string ToStringAny(const std::vector<T> value,
typename std::enable_if<
- SupportsInsertionOperator<T>::value>::type* = 0) {
+ SupportsInsertionOperator<T>::value>::type* = nullptr) {
std::stringstream stream;
stream << "vector{";
@@ -118,7 +118,7 @@
template <typename T>
std::string ToStringAny(const T&,
typename std::enable_if<
- !SupportsInsertionOperator<T>::value>::type* = 0
+ !SupportsInsertionOperator<T>::value>::type* = nullptr
) {
return std::string("(unknown type [no operator<< implemented] for )");
}
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 87197be..d603d96 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -221,7 +221,7 @@
callbacks_.reset();
verification_results_.reset();
compiler_options_.reset();
- image_reservation_.reset();
+ image_reservation_.Reset();
CommonRuntimeTest::TearDown();
}
@@ -323,18 +323,17 @@
// accidentally end up colliding with the fixed memory address when we need to load the image.
std::string error_msg;
MemMap::Init();
- image_reservation_.reset(MemMap::MapAnonymous("image reservation",
- reinterpret_cast<uint8_t*>(ART_BASE_ADDRESS),
- (size_t)120 * 1024 * 1024, // 120MB
- PROT_NONE,
- false /* no need for 4gb flag with fixed mmap*/,
- false /* not reusing existing reservation */,
- &error_msg));
- CHECK(image_reservation_.get() != nullptr) << error_msg;
+ image_reservation_ = MemMap::MapAnonymous("image reservation",
+ reinterpret_cast<uint8_t*>(ART_BASE_ADDRESS),
+ (size_t)120 * 1024 * 1024, // 120MB
+ PROT_NONE,
+ false /* no need for 4gb flag with fixed mmap */,
+ &error_msg);
+ CHECK(image_reservation_.IsValid()) << error_msg;
}
void CommonCompilerTest::UnreserveImageSpace() {
- image_reservation_.reset();
+ image_reservation_.Reset();
}
void CommonCompilerTest::SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files) {
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index db38110..366489c 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -115,7 +115,7 @@
std::unique_ptr<CompilerDriver> compiler_driver_;
private:
- std::unique_ptr<MemMap> image_reservation_;
+ MemMap image_reservation_;
// Chunks must not move their storage after being created - use the node-based std::list.
std::list<std::vector<uint8_t>> header_code_and_maps_chunks_;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index a881c5e..3fc559e 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -88,7 +88,7 @@
JitCompiler::JitCompiler() {
compiler_options_.reset(new CompilerOptions());
// Special case max code units for inlining, whose default is "unset" (implictly
- // meaning no limit). Do this before parsing the actuall passed options.
+ // meaning no limit). Do this before parsing the actual passed options.
compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
{
std::string error_msg;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 4ebe12e..a460f77 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -89,25 +89,10 @@
// Reference load (except object array loads) is using LDR Wt, [Xn, #offset] which can handle
// offset < 16KiB. For offsets >= 16KiB, the load shall be emitted as two or more instructions.
-// For the Baker read barrier implementation using link-generated thunks we need to split
+// For the Baker read barrier implementation using link-time generated thunks we need to split
// the offset explicitly.
constexpr uint32_t kReferenceLoadMinFarOffset = 16 * KB;
-// Flags controlling the use of link-time generated thunks for Baker read barriers.
-constexpr bool kBakerReadBarrierLinkTimeThunksEnableForFields = true;
-constexpr bool kBakerReadBarrierLinkTimeThunksEnableForArrays = true;
-constexpr bool kBakerReadBarrierLinkTimeThunksEnableForGcRoots = true;
-
-// Some instructions have special requirements for a temporary, for example
-// LoadClass/kBssEntry and LoadString/kBssEntry for Baker read barrier require
-// temp that's not an R0 (to avoid an extra move) and Baker read barrier field
-// loads with large offsets need a fixed register to limit the number of link-time
-// thunks we generate. For these and similar cases, we want to reserve a specific
-// register that's neither callee-save nor an argument register. We choose x15.
-inline Location FixedTempLocation() {
- return Location::RegisterLocation(x15.GetCode());
-}
-
inline Condition ARM64Condition(IfCondition cond) {
switch (cond) {
case kCondEQ: return eq;
@@ -614,503 +599,6 @@
}
}
-// Abstract base class for read barrier slow paths marking a reference
-// `ref`.
-//
-// Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked or an empty
-// location; in the latter case, the read barrier marking runtime
-// entry point will be loaded by the slow path code itself.
-class ReadBarrierMarkSlowPathBaseARM64 : public SlowPathCodeARM64 {
- protected:
- ReadBarrierMarkSlowPathBaseARM64(HInstruction* instruction, Location ref, Location entrypoint)
- : SlowPathCodeARM64(instruction), ref_(ref), entrypoint_(entrypoint) {
- DCHECK(kEmitCompilerReadBarrier);
- }
-
- const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathBaseARM64"; }
-
- // Generate assembly code calling the read barrier marking runtime
- // entry point (ReadBarrierMarkRegX).
- void GenerateReadBarrierMarkRuntimeCall(CodeGenerator* codegen) {
- // No need to save live registers; it's taken care of by the
- // entrypoint. Also, there is no need to update the stack mask,
- // as this runtime call will not trigger a garbage collection.
- CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
- DCHECK_NE(ref_.reg(), LR);
- DCHECK_NE(ref_.reg(), WSP);
- DCHECK_NE(ref_.reg(), WZR);
- // IP0 is used internally by the ReadBarrierMarkRegX entry point
- // as a temporary, it cannot be the entry point's input/output.
- DCHECK_NE(ref_.reg(), IP0);
- DCHECK(0 <= ref_.reg() && ref_.reg() < kNumberOfWRegisters) << ref_.reg();
- // "Compact" slow path, saving two moves.
- //
- // Instead of using the standard runtime calling convention (input
- // and output in W0):
- //
- // W0 <- ref
- // W0 <- ReadBarrierMark(W0)
- // ref <- W0
- //
- // we just use rX (the register containing `ref`) as input and output
- // of a dedicated entrypoint:
- //
- // rX <- ReadBarrierMarkRegX(rX)
- //
- if (entrypoint_.IsValid()) {
- arm64_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
- __ Blr(XRegisterFrom(entrypoint_));
- } else {
- // Entrypoint is not already loaded, load from the thread.
- int32_t entry_point_offset =
- Thread::ReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ref_.reg());
- // This runtime call does not require a stack map.
- arm64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
- }
- }
-
- // The location (register) of the marked object reference.
- const Location ref_;
-
- // The location of the entrypoint if it is already loaded.
- const Location entrypoint_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathBaseARM64);
-};
-
-// Slow path marking an object reference `ref` during a read
-// barrier. The field `obj.field` in the object `obj` holding this
-// reference does not get updated by this slow path after marking.
-//
-// This means that after the execution of this slow path, `ref` will
-// always be up-to-date, but `obj.field` may not; i.e., after the
-// flip, `ref` will be a to-space reference, but `obj.field` will
-// probably still be a from-space reference (unless it gets updated by
-// another thread, or if another thread installed another object
-// reference (different from `ref`) in `obj.field`).
-//
-// Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked or an empty
-// location; in the latter case, the read barrier marking runtime
-// entry point will be loaded by the slow path code itself.
-class ReadBarrierMarkSlowPathARM64 : public ReadBarrierMarkSlowPathBaseARM64 {
- public:
- ReadBarrierMarkSlowPathARM64(HInstruction* instruction,
- Location ref,
- Location entrypoint = Location::NoLocation())
- : ReadBarrierMarkSlowPathBaseARM64(instruction, ref, entrypoint) {
- DCHECK(kEmitCompilerReadBarrier);
- }
-
- const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARM64"; }
-
- void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
- LocationSummary* locations = instruction_->GetLocations();
- DCHECK(locations->CanCall());
- DCHECK(ref_.IsRegister()) << ref_;
- DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_.reg())) << ref_.reg();
- DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
- << "Unexpected instruction in read barrier marking slow path: "
- << instruction_->DebugName();
-
- __ Bind(GetEntryLabel());
- GenerateReadBarrierMarkRuntimeCall(codegen);
- __ B(GetExitLabel());
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARM64);
-};
-
-// Slow path loading `obj`'s lock word, loading a reference from
-// object `*(obj + offset + (index << scale_factor))` into `ref`, and
-// marking `ref` if `obj` is gray according to the lock word (Baker
-// read barrier). The field `obj.field` in the object `obj` holding
-// this reference does not get updated by this slow path after marking
-// (see LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64
-// below for that).
-//
-// This means that after the execution of this slow path, `ref` will
-// always be up-to-date, but `obj.field` may not; i.e., after the
-// flip, `ref` will be a to-space reference, but `obj.field` will
-// probably still be a from-space reference (unless it gets updated by
-// another thread, or if another thread installed another object
-// reference (different from `ref`) in `obj.field`).
-//
-// Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked or an empty
-// location; in the latter case, the read barrier marking runtime
-// entry point will be loaded by the slow path code itself.
-class LoadReferenceWithBakerReadBarrierSlowPathARM64 : public ReadBarrierMarkSlowPathBaseARM64 {
- public:
- LoadReferenceWithBakerReadBarrierSlowPathARM64(HInstruction* instruction,
- Location ref,
- Register obj,
- uint32_t offset,
- Location index,
- size_t scale_factor,
- bool needs_null_check,
- bool use_load_acquire,
- Register temp,
- Location entrypoint = Location::NoLocation())
- : ReadBarrierMarkSlowPathBaseARM64(instruction, ref, entrypoint),
- obj_(obj),
- offset_(offset),
- index_(index),
- scale_factor_(scale_factor),
- needs_null_check_(needs_null_check),
- use_load_acquire_(use_load_acquire),
- temp_(temp) {
- DCHECK(kEmitCompilerReadBarrier);
- DCHECK(kUseBakerReadBarrier);
- }
-
- const char* GetDescription() const OVERRIDE {
- return "LoadReferenceWithBakerReadBarrierSlowPathARM64";
- }
-
- void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
- LocationSummary* locations = instruction_->GetLocations();
- DCHECK(locations->CanCall());
- DCHECK(ref_.IsRegister()) << ref_;
- DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_.reg())) << ref_.reg();
- DCHECK(obj_.IsW());
- DCHECK_NE(ref_.reg(), LocationFrom(temp_).reg());
- DCHECK(instruction_->IsInstanceFieldGet() ||
- instruction_->IsStaticFieldGet() ||
- instruction_->IsArrayGet() ||
- instruction_->IsArraySet() ||
- instruction_->IsInstanceOf() ||
- instruction_->IsCheckCast() ||
- (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
- (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
- << "Unexpected instruction in read barrier marking slow path: "
- << instruction_->DebugName();
- // The read barrier instrumentation of object ArrayGet
- // instructions does not support the HIntermediateAddress
- // instruction.
- DCHECK(!(instruction_->IsArrayGet() &&
- instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
-
- // Temporary register `temp_`, used to store the lock word, must
- // not be IP0 nor IP1, as we may use them to emit the reference
- // load (in the call to GenerateRawReferenceLoad below), and we
- // need the lock word to still be in `temp_` after the reference
- // load.
- DCHECK_NE(LocationFrom(temp_).reg(), IP0);
- DCHECK_NE(LocationFrom(temp_).reg(), IP1);
-
- __ Bind(GetEntryLabel());
-
- // When using MaybeGenerateReadBarrierSlow, the read barrier call is
- // inserted after the original load. However, in fast path based
- // Baker's read barriers, we need to perform the load of
- // mirror::Object::monitor_ *before* the original reference load.
- // This load-load ordering is required by the read barrier.
- // The slow path (for Baker's algorithm) should look like:
- //
- // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
- // lfence; // Load fence or artificial data dependency to prevent load-load reordering
- // HeapReference<mirror::Object> ref = *src; // Original reference load.
- // bool is_gray = (rb_state == ReadBarrier::GrayState());
- // if (is_gray) {
- // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
- // }
- //
- // Note: the original implementation in ReadBarrier::Barrier is
- // slightly more complex as it performs additional checks that we do
- // not do here for performance reasons.
-
- // /* int32_t */ monitor = obj->monitor_
- uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
- __ Ldr(temp_, HeapOperand(obj_, monitor_offset));
- if (needs_null_check_) {
- codegen->MaybeRecordImplicitNullCheck(instruction_);
- }
- // /* LockWord */ lock_word = LockWord(monitor)
- static_assert(sizeof(LockWord) == sizeof(int32_t),
- "art::LockWord and int32_t have different sizes.");
-
- // Introduce a dependency on the lock_word including rb_state,
- // to prevent load-load reordering, and without using
- // a memory barrier (which would be more expensive).
- // `obj` is unchanged by this operation, but its value now depends
- // on `temp`.
- __ Add(obj_.X(), obj_.X(), Operand(temp_.X(), LSR, 32));
-
- // The actual reference load.
- // A possible implicit null check has already been handled above.
- CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
- arm64_codegen->GenerateRawReferenceLoad(instruction_,
- ref_,
- obj_,
- offset_,
- index_,
- scale_factor_,
- /* needs_null_check */ false,
- use_load_acquire_);
-
- // Mark the object `ref` when `obj` is gray.
- //
- // if (rb_state == ReadBarrier::GrayState())
- // ref = ReadBarrier::Mark(ref);
- //
- // Given the numeric representation, it's enough to check the low bit of the rb_state.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
- static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
- __ Tbz(temp_, LockWord::kReadBarrierStateShift, GetExitLabel());
- GenerateReadBarrierMarkRuntimeCall(codegen);
-
- __ B(GetExitLabel());
- }
-
- private:
- // The register containing the object holding the marked object reference field.
- Register obj_;
- // The offset, index and scale factor to access the reference in `obj_`.
- uint32_t offset_;
- Location index_;
- size_t scale_factor_;
- // Is a null check required?
- bool needs_null_check_;
- // Should this reference load use Load-Acquire semantics?
- bool use_load_acquire_;
- // A temporary register used to hold the lock word of `obj_`.
- Register temp_;
-
- DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierSlowPathARM64);
-};
-
-// Slow path loading `obj`'s lock word, loading a reference from
-// object `*(obj + offset + (index << scale_factor))` into `ref`, and
-// marking `ref` if `obj` is gray according to the lock word (Baker
-// read barrier). If needed, this slow path also atomically updates
-// the field `obj.field` in the object `obj` holding this reference
-// after marking (contrary to
-// LoadReferenceWithBakerReadBarrierSlowPathARM64 above, which never
-// tries to update `obj.field`).
-//
-// This means that after the execution of this slow path, both `ref`
-// and `obj.field` will be up-to-date; i.e., after the flip, both will
-// hold the same to-space reference (unless another thread installed
-// another object reference (different from `ref`) in `obj.field`).
-//
-// Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked or an empty
-// location; in the latter case, the read barrier marking runtime
-// entry point will be loaded by the slow path code itself.
-class LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64
- : public ReadBarrierMarkSlowPathBaseARM64 {
- public:
- LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64(
- HInstruction* instruction,
- Location ref,
- Register obj,
- uint32_t offset,
- Location index,
- size_t scale_factor,
- bool needs_null_check,
- bool use_load_acquire,
- Register temp,
- Location entrypoint = Location::NoLocation())
- : ReadBarrierMarkSlowPathBaseARM64(instruction, ref, entrypoint),
- obj_(obj),
- offset_(offset),
- index_(index),
- scale_factor_(scale_factor),
- needs_null_check_(needs_null_check),
- use_load_acquire_(use_load_acquire),
- temp_(temp) {
- DCHECK(kEmitCompilerReadBarrier);
- DCHECK(kUseBakerReadBarrier);
- }
-
- const char* GetDescription() const OVERRIDE {
- return "LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64";
- }
-
- void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
- LocationSummary* locations = instruction_->GetLocations();
- Register ref_reg = WRegisterFrom(ref_);
- DCHECK(locations->CanCall());
- DCHECK(ref_.IsRegister()) << ref_;
- DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_.reg())) << ref_.reg();
- DCHECK(obj_.IsW());
- DCHECK_NE(ref_.reg(), LocationFrom(temp_).reg());
-
- // This slow path is only used by the UnsafeCASObject intrinsic at the moment.
- DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
- << "Unexpected instruction in read barrier marking and field updating slow path: "
- << instruction_->DebugName();
- DCHECK(instruction_->GetLocations()->Intrinsified());
- DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
- DCHECK_EQ(offset_, 0u);
- DCHECK_EQ(scale_factor_, 0u);
- DCHECK_EQ(use_load_acquire_, false);
- // The location of the offset of the marked reference field within `obj_`.
- Location field_offset = index_;
- DCHECK(field_offset.IsRegister()) << field_offset;
-
- // Temporary register `temp_`, used to store the lock word, must
- // not be IP0 nor IP1, as we may use them to emit the reference
- // load (in the call to GenerateRawReferenceLoad below), and we
- // need the lock word to still be in `temp_` after the reference
- // load.
- DCHECK_NE(LocationFrom(temp_).reg(), IP0);
- DCHECK_NE(LocationFrom(temp_).reg(), IP1);
-
- __ Bind(GetEntryLabel());
-
- // The implementation is similar to LoadReferenceWithBakerReadBarrierSlowPathARM64's:
- //
- // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
- // lfence; // Load fence or artificial data dependency to prevent load-load reordering
- // HeapReference<mirror::Object> ref = *src; // Original reference load.
- // bool is_gray = (rb_state == ReadBarrier::GrayState());
- // if (is_gray) {
- // old_ref = ref;
- // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
- // compareAndSwapObject(obj, field_offset, old_ref, ref);
- // }
-
- // /* int32_t */ monitor = obj->monitor_
- uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
- __ Ldr(temp_, HeapOperand(obj_, monitor_offset));
- if (needs_null_check_) {
- codegen->MaybeRecordImplicitNullCheck(instruction_);
- }
- // /* LockWord */ lock_word = LockWord(monitor)
- static_assert(sizeof(LockWord) == sizeof(int32_t),
- "art::LockWord and int32_t have different sizes.");
-
- // Introduce a dependency on the lock_word including rb_state,
- // to prevent load-load reordering, and without using
- // a memory barrier (which would be more expensive).
- // `obj` is unchanged by this operation, but its value now depends
- // on `temp`.
- __ Add(obj_.X(), obj_.X(), Operand(temp_.X(), LSR, 32));
-
- // The actual reference load.
- // A possible implicit null check has already been handled above.
- CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
- arm64_codegen->GenerateRawReferenceLoad(instruction_,
- ref_,
- obj_,
- offset_,
- index_,
- scale_factor_,
- /* needs_null_check */ false,
- use_load_acquire_);
-
- // Mark the object `ref` when `obj` is gray.
- //
- // if (rb_state == ReadBarrier::GrayState())
- // ref = ReadBarrier::Mark(ref);
- //
- // Given the numeric representation, it's enough to check the low bit of the rb_state.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
- static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
- __ Tbz(temp_, LockWord::kReadBarrierStateShift, GetExitLabel());
-
- // Save the old value of the reference before marking it.
- // Note that we cannot use IP to save the old reference, as IP is
- // used internally by the ReadBarrierMarkRegX entry point, and we
- // need the old reference after the call to that entry point.
- DCHECK_NE(LocationFrom(temp_).reg(), IP0);
- __ Mov(temp_.W(), ref_reg);
-
- GenerateReadBarrierMarkRuntimeCall(codegen);
-
- // If the new reference is different from the old reference,
- // update the field in the holder (`*(obj_ + field_offset)`).
- //
- // Note that this field could also hold a different object, if
- // another thread had concurrently changed it. In that case, the
- // LDXR/CMP/BNE sequence of instructions in the compare-and-set
- // (CAS) operation below would abort the CAS, leaving the field
- // as-is.
- __ Cmp(temp_.W(), ref_reg);
- __ B(eq, GetExitLabel());
-
- // Update the the holder's field atomically. This may fail if
- // mutator updates before us, but it's OK. This is achieved
- // using a strong compare-and-set (CAS) operation with relaxed
- // memory synchronization ordering, where the expected value is
- // the old reference and the desired value is the new reference.
-
- MacroAssembler* masm = arm64_codegen->GetVIXLAssembler();
- UseScratchRegisterScope temps(masm);
-
- // Convenience aliases.
- Register base = obj_.W();
- Register offset = XRegisterFrom(field_offset);
- Register expected = temp_.W();
- Register value = ref_reg;
- Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
- Register tmp_value = temps.AcquireW(); // Value in memory.
-
- __ Add(tmp_ptr, base.X(), Operand(offset));
-
- if (kPoisonHeapReferences) {
- arm64_codegen->GetAssembler()->PoisonHeapReference(expected);
- if (value.Is(expected)) {
- // Do not poison `value`, as it is the same register as
- // `expected`, which has just been poisoned.
- } else {
- arm64_codegen->GetAssembler()->PoisonHeapReference(value);
- }
- }
-
- // do {
- // tmp_value = [tmp_ptr] - expected;
- // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
-
- vixl::aarch64::Label loop_head, comparison_failed, exit_loop;
- __ Bind(&loop_head);
- __ Ldxr(tmp_value, MemOperand(tmp_ptr));
- __ Cmp(tmp_value, expected);
- __ B(&comparison_failed, ne);
- __ Stxr(tmp_value, value, MemOperand(tmp_ptr));
- __ Cbnz(tmp_value, &loop_head);
- __ B(&exit_loop);
- __ Bind(&comparison_failed);
- __ Clrex();
- __ Bind(&exit_loop);
-
- if (kPoisonHeapReferences) {
- arm64_codegen->GetAssembler()->UnpoisonHeapReference(expected);
- if (value.Is(expected)) {
- // Do not unpoison `value`, as it is the same register as
- // `expected`, which has just been unpoisoned.
- } else {
- arm64_codegen->GetAssembler()->UnpoisonHeapReference(value);
- }
- }
-
- __ B(GetExitLabel());
- }
-
- private:
- // The register containing the object holding the marked object reference field.
- const Register obj_;
- // The offset, index and scale factor to access the reference in `obj_`.
- uint32_t offset_;
- Location index_;
- size_t scale_factor_;
- // Is a null check required?
- bool needs_null_check_;
- // Should this reference load use Load-Acquire semantics?
- bool use_load_acquire_;
- // A temporary register used to hold the lock word of `obj_`; and
- // also to hold the original reference value, when the reference is
- // marked.
- const Register temp_;
-
- DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64);
-};
-
// Slow path generating a read barrier for a heap reference.
class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 {
public:
@@ -1466,13 +954,20 @@
BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
// Check that the next instruction matches the expected LDR.
switch (kind) {
- case BakerReadBarrierKind::kField: {
+ case BakerReadBarrierKind::kField:
+ case BakerReadBarrierKind::kAcquire: {
DCHECK_GE(code.size() - literal_offset, 8u);
uint32_t next_insn = GetInsn(literal_offset + 4u);
- // LDR (immediate) with correct base_reg.
CheckValidReg(next_insn & 0x1fu); // Check destination register.
const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
- CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (base_reg << 5));
+ if (kind == BakerReadBarrierKind::kField) {
+ // LDR (immediate) with correct base_reg.
+ CHECK_EQ(next_insn & 0xffc003e0u, 0xb9400000u | (base_reg << 5));
+ } else {
+ DCHECK(kind == BakerReadBarrierKind::kAcquire);
+ // LDAR with correct base_reg.
+ CHECK_EQ(next_insn & 0xffffffe0u, 0x88dffc00u | (base_reg << 5));
+ }
break;
}
case BakerReadBarrierKind::kArray: {
@@ -1489,9 +984,12 @@
case BakerReadBarrierKind::kGcRoot: {
DCHECK_GE(literal_offset, 4u);
uint32_t prev_insn = GetInsn(literal_offset - 4u);
- // LDR (immediate) with correct root_reg.
const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
- CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | root_reg);
+ // Usually LDR (immediate) with correct root_reg but
+ // we may have a "MOV marked, old_value" for UnsafeCASObject.
+ if ((prev_insn & 0xffe0ffff) != (0x2a0003e0 | root_reg)) { // MOV?
+ CHECK_EQ(prev_insn & 0xffc0001fu, 0xb9400000u | root_reg); // LDR?
+ }
break;
}
default:
@@ -1676,8 +1174,24 @@
if (value_can_be_null) {
__ Cbz(value, &done);
}
+ // Load the address of the card table into `card`.
__ Ldr(card, MemOperand(tr, Thread::CardTableOffset<kArm64PointerSize>().Int32Value()));
+ // Calculate the offset (in the card table) of the card corresponding to
+ // `object`.
__ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
+ // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
+ // `object`'s card.
+ //
+ // Register `card` contains the address of the card table. Note that the card
+ // table's base is biased during its creation so that it always starts at an
+ // address whose least-significant byte is equal to `kCardDirty` (see
+ // art::gc::accounting::CardTable::Create). Therefore the STRB instruction
+ // below writes the `kCardDirty` (byte) value into the `object`'s card
+ // (located at `card + object >> kCardShift`).
+ //
+ // This dual use of the value in register `card` (1. to calculate the location
+ // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
+ // (no need to explicitly load `kCardDirty` as an immediate value).
__ Strb(card, MemOperand(card, temp.X()));
if (value_can_be_null) {
__ Bind(&done);
@@ -2324,18 +1838,12 @@
: LocationSummary::kNoCall);
if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
- // We need a temporary register for the read barrier marking slow
- // path in CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier.
- if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
- !Runtime::Current()->UseJitCompilation() &&
- !field_info.IsVolatile()) {
- // If link-time thunks for the Baker read barrier are enabled, for AOT
- // non-volatile loads we need a temporary only if the offset is too big.
- if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
- locations->AddTemp(FixedTempLocation());
- }
- } else {
- locations->AddTemp(Location::RequiresRegister());
+ // We need a temporary register for the read barrier load in
+ // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier()
+ // only if the field is volatile or the offset is too big.
+ if (field_info.IsVolatile() ||
+ field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
+ locations->AddTemp(FixedTempLocation());
}
}
locations->SetInAt(0, Location::RequiresRegister());
@@ -2798,14 +2306,11 @@
: LocationSummary::kNoCall);
if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
- // We need a temporary register for the read barrier marking slow
- // path in CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier.
- if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
- !Runtime::Current()->UseJitCompilation() &&
- instruction->GetIndex()->IsConstant()) {
+ if (instruction->GetIndex()->IsConstant()) {
// Array loads with constant index are treated as field loads.
- // If link-time thunks for the Baker read barrier are enabled, for AOT
- // constant index loads we need a temporary only if the offset is too big.
+ // We need a temporary register for the read barrier load in
+ // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier()
+ // only if the offset is too big.
uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
uint32_t index = instruction->GetIndex()->AsIntConstant()->GetValue();
offset += index << DataType::SizeShift(DataType::Type::kReference);
@@ -2813,6 +2318,8 @@
locations->AddTemp(FixedTempLocation());
}
} else {
+ // We need a non-scratch temporary for the array data pointer in
+ // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier().
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -2868,7 +2375,7 @@
} else {
Register temp = WRegisterFrom(locations->GetTemp(0));
codegen_->GenerateArrayLoadWithBakerReadBarrier(
- instruction, out, obj.W(), offset, index, temp, /* needs_null_check */ false);
+ out, obj.W(), offset, index, temp, /* needs_null_check */ false);
}
} else {
// General case.
@@ -4759,7 +4266,7 @@
}
void CodeGeneratorARM64::EmitBakerReadBarrierCbnz(uint32_t custom_data) {
- ExactAssemblyScope guard(GetVIXLAssembler(), 1 * vixl::aarch64::kInstructionSize);
+ DCHECK(!__ AllowMacroInstructions()); // In ExactAssemblyScope.
if (Runtime::Current()->UseJitCompilation()) {
auto it = jit_baker_read_barrier_slow_paths_.FindOrAdd(custom_data);
vixl::aarch64::Label* slow_path_entry = &it->second.label;
@@ -6276,74 +5783,39 @@
if (kUseBakerReadBarrier) {
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used.
- if (kBakerReadBarrierLinkTimeThunksEnableForGcRoots) {
- // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
- // the Marking Register) to decide whether we need to enter
- // the slow path to mark the GC root.
- //
- // We use shared thunks for the slow path; shared within the method
- // for JIT, across methods for AOT. That thunk checks the reference
- // and jumps to the entrypoint if needed.
- //
- // lr = &return_address;
- // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
- // if (mr) { // Thread::Current()->GetIsGcMarking()
- // goto gc_root_thunk<root_reg>(lr)
- // }
- // return_address:
- UseScratchRegisterScope temps(GetVIXLAssembler());
- DCHECK(temps.IsAvailable(ip0));
- DCHECK(temps.IsAvailable(ip1));
- temps.Exclude(ip0, ip1);
- uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode());
+ // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
+ // the Marking Register) to decide whether we need to enter
+ // the slow path to mark the GC root.
+ //
+ // We use shared thunks for the slow path; shared within the method
+ // for JIT, across methods for AOT. That thunk checks the reference
+ // and jumps to the entrypoint if needed.
+ //
+ // lr = &return_address;
+ // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
+ // if (mr) { // Thread::Current()->GetIsGcMarking()
+ // goto gc_root_thunk<root_reg>(lr)
+ // }
+ // return_address:
- ExactAssemblyScope guard(GetVIXLAssembler(), 3 * vixl::aarch64::kInstructionSize);
- vixl::aarch64::Label return_address;
- __ adr(lr, &return_address);
- if (fixup_label != nullptr) {
- __ bind(fixup_label);
- }
- static_assert(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_OFFSET == -8,
- "GC root LDR must be 2 instruction (8B) before the return address label.");
- __ ldr(root_reg, MemOperand(obj.X(), offset));
- EmitBakerReadBarrierCbnz(custom_data);
- __ bind(&return_address);
- } else {
- // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
- // the Marking Register) to decide whether we need to enter
- // the slow path to mark the GC root.
- //
- // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
- // if (mr) { // Thread::Current()->GetIsGcMarking()
- // // Slow path.
- // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
- // root = entrypoint(root); // root = ReadBarrier::Mark(root); // Entry point call.
- // }
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ DCHECK(temps.IsAvailable(ip0));
+ DCHECK(temps.IsAvailable(ip1));
+ temps.Exclude(ip0, ip1);
+ uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode());
- // Slow path marking the GC root `root`. The entrypoint will
- // be loaded by the slow path code.
- SlowPathCodeARM64* slow_path =
- new (GetScopedAllocator()) ReadBarrierMarkSlowPathARM64(instruction, root);
- AddSlowPath(slow_path);
-
- // /* GcRoot<mirror::Object> */ root = *(obj + offset)
- if (fixup_label == nullptr) {
- __ Ldr(root_reg, MemOperand(obj, offset));
- } else {
- EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj);
- }
- static_assert(
- sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
- "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
- "have different sizes.");
- static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
- "art::mirror::CompressedReference<mirror::Object> and int32_t "
- "have different sizes.");
-
- __ Cbnz(mr, slow_path->GetEntryLabel());
- __ Bind(slow_path->GetExitLabel());
+ ExactAssemblyScope guard(GetVIXLAssembler(), 3 * vixl::aarch64::kInstructionSize);
+ vixl::aarch64::Label return_address;
+ __ adr(lr, &return_address);
+ if (fixup_label != nullptr) {
+ __ bind(fixup_label);
}
+ static_assert(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_OFFSET == -8,
+ "GC root LDR must be 2 instructions (8B) before the return address label.");
+ __ ldr(root_reg, MemOperand(obj.X(), offset));
+ EmitBakerReadBarrierCbnz(custom_data);
+ __ bind(&return_address);
} else {
// GC root loaded through a slow path for read barriers other
// than Baker's.
@@ -6370,6 +5842,95 @@
MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
}
+void CodeGeneratorARM64::GenerateUnsafeCasOldValueMovWithBakerReadBarrier(
+ vixl::aarch64::Register marked,
+ vixl::aarch64::Register old_value) {
+ DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(kUseBakerReadBarrier);
+
+ // Similar to the Baker RB path in GenerateGcRootFieldLoad(), with a MOV instead of LDR.
+ uint32_t custom_data = EncodeBakerReadBarrierGcRootData(marked.GetCode());
+
+ ExactAssemblyScope guard(GetVIXLAssembler(), 3 * vixl::aarch64::kInstructionSize);
+ vixl::aarch64::Label return_address;
+ __ adr(lr, &return_address);
+ static_assert(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_OFFSET == -8,
+ "GC root LDR must be 2 instructions (8B) before the return address label.");
+ __ mov(marked, old_value);
+ EmitBakerReadBarrierCbnz(custom_data);
+ __ bind(&return_address);
+}
+
+void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ vixl::aarch64::Register obj,
+ const vixl::aarch64::MemOperand& src,
+ bool needs_null_check,
+ bool use_load_acquire) {
+ DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(kUseBakerReadBarrier);
+
+ // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
+ // Marking Register) to decide whether we need to enter the slow
+ // path to mark the reference. Then, in the slow path, check the
+ // gray bit in the lock word of the reference's holder (`obj`) to
+ // decide whether to mark `ref` or not.
+ //
+ // We use shared thunks for the slow path; shared within the method
+ // for JIT, across methods for AOT. That thunk checks the holder
+ // and jumps to the entrypoint if needed. If the holder is not gray,
+ // it creates a fake dependency and returns to the LDR instruction.
+ //
+ // lr = &gray_return_address;
+ // if (mr) { // Thread::Current()->GetIsGcMarking()
+ // goto field_thunk<holder_reg, base_reg, use_load_acquire>(lr)
+ // }
+ // not_gray_return_address:
+ // // Original reference load. If the offset is too large to fit
+ // // into LDR, we use an adjusted base register here.
+ // HeapReference<mirror::Object> reference = *(obj+offset);
+ // gray_return_address:
+
+ DCHECK(src.GetAddrMode() == vixl::aarch64::Offset);
+ DCHECK_ALIGNED(src.GetOffset(), sizeof(mirror::HeapReference<mirror::Object>));
+
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ DCHECK(temps.IsAvailable(ip0));
+ DCHECK(temps.IsAvailable(ip1));
+ temps.Exclude(ip0, ip1);
+ uint32_t custom_data = use_load_acquire
+ ? EncodeBakerReadBarrierAcquireData(src.GetBaseRegister().GetCode(), obj.GetCode())
+ : EncodeBakerReadBarrierFieldData(src.GetBaseRegister().GetCode(), obj.GetCode());
+
+ {
+ ExactAssemblyScope guard(GetVIXLAssembler(),
+ (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
+ vixl::aarch64::Label return_address;
+ __ adr(lr, &return_address);
+ EmitBakerReadBarrierCbnz(custom_data);
+ static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
+ "Field LDR must be 1 instruction (4B) before the return address label; "
+ " 2 instructions (8B) for heap poisoning.");
+ Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
+ if (use_load_acquire) {
+ DCHECK_EQ(src.GetOffset(), 0);
+ __ ldar(ref_reg, src);
+ } else {
+ __ ldr(ref_reg, src);
+ }
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ // Unpoison the reference explicitly if needed. MaybeUnpoisonHeapReference() uses
+ // macro instructions disallowed in ExactAssemblyScope.
+ if (kPoisonHeapReferences) {
+ __ neg(ref_reg, Operand(ref_reg));
+ }
+ __ bind(&return_address);
+ }
+ MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__, /* temp_loc */ LocationFrom(ip1));
+}
+
void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
Location ref,
Register obj,
@@ -6377,88 +5938,26 @@
Location maybe_temp,
bool needs_null_check,
bool use_load_acquire) {
- DCHECK(kEmitCompilerReadBarrier);
- DCHECK(kUseBakerReadBarrier);
-
- if (kBakerReadBarrierLinkTimeThunksEnableForFields && !use_load_acquire) {
- // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
- // Marking Register) to decide whether we need to enter the slow
- // path to mark the reference. Then, in the slow path, check the
- // gray bit in the lock word of the reference's holder (`obj`) to
- // decide whether to mark `ref` or not.
- //
- // We use shared thunks for the slow path; shared within the method
- // for JIT, across methods for AOT. That thunk checks the holder
- // and jumps to the entrypoint if needed. If the holder is not gray,
- // it creates a fake dependency and returns to the LDR instruction.
- //
- // lr = &gray_return_address;
- // if (mr) { // Thread::Current()->GetIsGcMarking()
- // goto field_thunk<holder_reg, base_reg>(lr)
- // }
- // not_gray_return_address:
- // // Original reference load. If the offset is too large to fit
- // // into LDR, we use an adjusted base register here.
- // HeapReference<mirror::Object> reference = *(obj+offset);
- // gray_return_address:
-
- DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
- Register base = obj;
- if (offset >= kReferenceLoadMinFarOffset) {
- DCHECK(maybe_temp.IsRegister());
- base = WRegisterFrom(maybe_temp);
- static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
- __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
- offset &= (kReferenceLoadMinFarOffset - 1u);
- }
- UseScratchRegisterScope temps(GetVIXLAssembler());
- DCHECK(temps.IsAvailable(ip0));
- DCHECK(temps.IsAvailable(ip1));
- temps.Exclude(ip0, ip1);
- uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode());
-
- {
- ExactAssemblyScope guard(GetVIXLAssembler(),
- (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
- vixl::aarch64::Label return_address;
- __ adr(lr, &return_address);
- EmitBakerReadBarrierCbnz(custom_data);
- static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
- "Field LDR must be 1 instruction (4B) before the return address label; "
- " 2 instructions (8B) for heap poisoning.");
- Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
- __ ldr(ref_reg, MemOperand(base.X(), offset));
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- // Unpoison the reference explicitly if needed. MaybeUnpoisonHeapReference() uses
- // macro instructions disallowed in ExactAssemblyScope.
- if (kPoisonHeapReferences) {
- __ neg(ref_reg, Operand(ref_reg));
- }
- __ bind(&return_address);
- }
- MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__, /* temp_loc */ LocationFrom(ip1));
- return;
+ DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
+ Register base = obj;
+ if (use_load_acquire) {
+ DCHECK(maybe_temp.IsRegister());
+ base = WRegisterFrom(maybe_temp);
+ __ Add(base, obj, offset);
+ offset = 0u;
+ } else if (offset >= kReferenceLoadMinFarOffset) {
+ DCHECK(maybe_temp.IsRegister());
+ base = WRegisterFrom(maybe_temp);
+ static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
+ __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
+ offset &= (kReferenceLoadMinFarOffset - 1u);
}
-
- // /* HeapReference<Object> */ ref = *(obj + offset)
- Register temp = WRegisterFrom(maybe_temp);
- Location no_index = Location::NoLocation();
- size_t no_scale_factor = 0u;
- GenerateReferenceLoadWithBakerReadBarrier(instruction,
- ref,
- obj,
- offset,
- no_index,
- no_scale_factor,
- temp,
- needs_null_check,
- use_load_acquire);
+ MemOperand src(base.X(), offset);
+ GenerateFieldLoadWithBakerReadBarrier(
+ instruction, ref, obj, src, needs_null_check, use_load_acquire);
}
-void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
+void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(Location ref,
Register obj,
uint32_t data_offset,
Location index,
@@ -6472,268 +5971,57 @@
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
size_t scale_factor = DataType::SizeShift(DataType::Type::kReference);
- if (kBakerReadBarrierLinkTimeThunksEnableForArrays) {
- // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
- // Marking Register) to decide whether we need to enter the slow
- // path to mark the reference. Then, in the slow path, check the
- // gray bit in the lock word of the reference's holder (`obj`) to
- // decide whether to mark `ref` or not.
- //
- // We use shared thunks for the slow path; shared within the method
- // for JIT, across methods for AOT. That thunk checks the holder
- // and jumps to the entrypoint if needed. If the holder is not gray,
- // it creates a fake dependency and returns to the LDR instruction.
- //
- // lr = &gray_return_address;
- // if (mr) { // Thread::Current()->GetIsGcMarking()
- // goto array_thunk<base_reg>(lr)
- // }
- // not_gray_return_address:
- // // Original reference load. If the offset is too large to fit
- // // into LDR, we use an adjusted base register here.
- // HeapReference<mirror::Object> reference = data[index];
- // gray_return_address:
-
- DCHECK(index.IsValid());
- Register index_reg = RegisterFrom(index, DataType::Type::kInt32);
- Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
-
- UseScratchRegisterScope temps(GetVIXLAssembler());
- DCHECK(temps.IsAvailable(ip0));
- DCHECK(temps.IsAvailable(ip1));
- temps.Exclude(ip0, ip1);
- uint32_t custom_data = EncodeBakerReadBarrierArrayData(temp.GetCode());
-
- __ Add(temp.X(), obj.X(), Operand(data_offset));
- {
- ExactAssemblyScope guard(GetVIXLAssembler(),
- (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
- vixl::aarch64::Label return_address;
- __ adr(lr, &return_address);
- EmitBakerReadBarrierCbnz(custom_data);
- static_assert(BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
- "Array LDR must be 1 instruction (4B) before the return address label; "
- " 2 instructions (8B) for heap poisoning.");
- __ ldr(ref_reg, MemOperand(temp.X(), index_reg.X(), LSL, scale_factor));
- DCHECK(!needs_null_check); // The thunk cannot handle the null check.
- // Unpoison the reference explicitly if needed. MaybeUnpoisonHeapReference() uses
- // macro instructions disallowed in ExactAssemblyScope.
- if (kPoisonHeapReferences) {
- __ neg(ref_reg, Operand(ref_reg));
- }
- __ bind(&return_address);
- }
- MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__, /* temp_loc */ LocationFrom(ip1));
- return;
- }
-
- // Array cells are never volatile variables, therefore array loads
- // never use Load-Acquire instructions on ARM64.
- const bool use_load_acquire = false;
-
- // /* HeapReference<Object> */ ref =
- // *(obj + data_offset + index * sizeof(HeapReference<Object>))
- GenerateReferenceLoadWithBakerReadBarrier(instruction,
- ref,
- obj,
- data_offset,
- index,
- scale_factor,
- temp,
- needs_null_check,
- use_load_acquire);
-}
-
-void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- Register obj,
- uint32_t offset,
- Location index,
- size_t scale_factor,
- Register temp,
- bool needs_null_check,
- bool use_load_acquire) {
- DCHECK(kEmitCompilerReadBarrier);
- DCHECK(kUseBakerReadBarrier);
- // If we are emitting an array load, we should not be using a
- // Load Acquire instruction. In other words:
- // `instruction->IsArrayGet()` => `!use_load_acquire`.
- DCHECK(!instruction->IsArrayGet() || !use_load_acquire);
-
// Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
// Marking Register) to decide whether we need to enter the slow
// path to mark the reference. Then, in the slow path, check the
// gray bit in the lock word of the reference's holder (`obj`) to
// decide whether to mark `ref` or not.
//
- // if (mr) { // Thread::Current()->GetIsGcMarking()
- // // Slow path.
- // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
- // lfence; // Load fence or artificial data dependency to prevent load-load reordering
- // HeapReference<mirror::Object> ref = *src; // Original reference load.
- // bool is_gray = (rb_state == ReadBarrier::GrayState());
- // if (is_gray) {
- // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
- // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
- // }
- // } else {
- // HeapReference<mirror::Object> ref = *src; // Original reference load.
- // }
-
- // Slow path marking the object `ref` when the GC is marking. The
- // entrypoint will be loaded by the slow path code.
- SlowPathCodeARM64* slow_path =
- new (GetScopedAllocator()) LoadReferenceWithBakerReadBarrierSlowPathARM64(
- instruction,
- ref,
- obj,
- offset,
- index,
- scale_factor,
- needs_null_check,
- use_load_acquire,
- temp);
- AddSlowPath(slow_path);
-
- __ Cbnz(mr, slow_path->GetEntryLabel());
- // Fast path: the GC is not marking: just load the reference.
- GenerateRawReferenceLoad(
- instruction, ref, obj, offset, index, scale_factor, needs_null_check, use_load_acquire);
- __ Bind(slow_path->GetExitLabel());
- MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
-}
-
-void CodeGeneratorARM64::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- Register obj,
- Location field_offset,
- Register temp,
- bool needs_null_check,
- bool use_load_acquire) {
- DCHECK(kEmitCompilerReadBarrier);
- DCHECK(kUseBakerReadBarrier);
- // If we are emitting an array load, we should not be using a
- // Load Acquire instruction. In other words:
- // `instruction->IsArrayGet()` => `!use_load_acquire`.
- DCHECK(!instruction->IsArrayGet() || !use_load_acquire);
-
- // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
- // Marking Register) to decide whether we need to enter the slow
- // path to update the reference field within `obj`. Then, in the
- // slow path, check the gray bit in the lock word of the reference's
- // holder (`obj`) to decide whether to mark `ref` and update the
- // field or not.
+ // We use shared thunks for the slow path; shared within the method
+ // for JIT, across methods for AOT. That thunk checks the holder
+ // and jumps to the entrypoint if needed. If the holder is not gray,
+ // it creates a fake dependency and returns to the LDR instruction.
//
- // if (mr) { // Thread::Current()->GetIsGcMarking()
- // // Slow path.
- // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
- // lfence; // Load fence or artificial data dependency to prevent load-load reordering
- // HeapReference<mirror::Object> ref = *(obj + field_offset); // Reference load.
- // bool is_gray = (rb_state == ReadBarrier::GrayState());
- // if (is_gray) {
- // old_ref = ref;
- // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
- // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
- // compareAndSwapObject(obj, field_offset, old_ref, ref);
+ // lr = &gray_return_address;
+ // if (mr) { // Thread::Current()->GetIsGcMarking()
+ // goto array_thunk<base_reg>(lr)
// }
- // }
+ // not_gray_return_address:
+ // // Original reference load. If the offset is too large to fit
+ // // into LDR, we use an adjusted base register here.
+ // HeapReference<mirror::Object> reference = data[index];
+ // gray_return_address:
- // Slow path updating the object reference at address `obj + field_offset`
- // when the GC is marking. The entrypoint will be loaded by the slow path code.
- SlowPathCodeARM64* slow_path =
- new (GetScopedAllocator()) LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64(
- instruction,
- ref,
- obj,
- /* offset */ 0u,
- /* index */ field_offset,
- /* scale_factor */ 0u /* "times 1" */,
- needs_null_check,
- use_load_acquire,
- temp);
- AddSlowPath(slow_path);
+ DCHECK(index.IsValid());
+ Register index_reg = RegisterFrom(index, DataType::Type::kInt32);
+ Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
- __ Cbnz(mr, slow_path->GetEntryLabel());
- // Fast path: the GC is not marking: nothing to do (the field is
- // up-to-date, and we don't need to load the reference).
- __ Bind(slow_path->GetExitLabel());
- MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
-}
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ DCHECK(temps.IsAvailable(ip0));
+ DCHECK(temps.IsAvailable(ip1));
+ temps.Exclude(ip0, ip1);
+ uint32_t custom_data = EncodeBakerReadBarrierArrayData(temp.GetCode());
-void CodeGeneratorARM64::GenerateRawReferenceLoad(HInstruction* instruction,
- Location ref,
- Register obj,
- uint32_t offset,
- Location index,
- size_t scale_factor,
- bool needs_null_check,
- bool use_load_acquire) {
- DCHECK(obj.IsW());
- DataType::Type type = DataType::Type::kReference;
- Register ref_reg = RegisterFrom(ref, type);
-
- // If needed, vixl::EmissionCheckScope guards are used to ensure
- // that no pools are emitted between the load (macro) instruction
- // and MaybeRecordImplicitNullCheck.
-
- if (index.IsValid()) {
- // Load types involving an "index": ArrayGet,
- // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
- // intrinsics.
- if (use_load_acquire) {
- // UnsafeGetObjectVolatile intrinsic case.
- // Register `index` is not an index in an object array, but an
- // offset to an object reference field within object `obj`.
- DCHECK(instruction->IsInvoke()) << instruction->DebugName();
- DCHECK(instruction->GetLocations()->Intrinsified());
- DCHECK(instruction->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)
- << instruction->AsInvoke()->GetIntrinsic();
- DCHECK_EQ(offset, 0u);
- DCHECK_EQ(scale_factor, 0u);
- DCHECK_EQ(needs_null_check, false);
- // /* HeapReference<mirror::Object> */ ref = *(obj + index)
- MemOperand field = HeapOperand(obj, XRegisterFrom(index));
- LoadAcquire(instruction, ref_reg, field, /* needs_null_check */ false);
- } else {
- // ArrayGet and UnsafeGetObject and UnsafeCASObject intrinsics cases.
- // /* HeapReference<mirror::Object> */ ref = *(obj + offset + (index << scale_factor))
- if (index.IsConstant()) {
- uint32_t computed_offset = offset + (Int64FromLocation(index) << scale_factor);
- EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
- Load(type, ref_reg, HeapOperand(obj, computed_offset));
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- } else {
- UseScratchRegisterScope temps(GetVIXLAssembler());
- Register temp = temps.AcquireW();
- __ Add(temp, obj, offset);
- {
- EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
- Load(type, ref_reg, HeapOperand(temp, XRegisterFrom(index), LSL, scale_factor));
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- }
- }
+ __ Add(temp.X(), obj.X(), Operand(data_offset));
+ {
+ ExactAssemblyScope guard(GetVIXLAssembler(),
+ (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
+ vixl::aarch64::Label return_address;
+ __ adr(lr, &return_address);
+ EmitBakerReadBarrierCbnz(custom_data);
+ static_assert(BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
+ "Array LDR must be 1 instruction (4B) before the return address label; "
+ " 2 instructions (8B) for heap poisoning.");
+ __ ldr(ref_reg, MemOperand(temp.X(), index_reg.X(), LSL, scale_factor));
+ DCHECK(!needs_null_check); // The thunk cannot handle the null check.
+ // Unpoison the reference explicitly if needed. MaybeUnpoisonHeapReference() uses
+ // macro instructions disallowed in ExactAssemblyScope.
+ if (kPoisonHeapReferences) {
+ __ neg(ref_reg, Operand(ref_reg));
}
- } else {
- // /* HeapReference<mirror::Object> */ ref = *(obj + offset)
- MemOperand field = HeapOperand(obj, offset);
- if (use_load_acquire) {
- // Implicit null checks are handled by CodeGeneratorARM64::LoadAcquire.
- LoadAcquire(instruction, ref_reg, field, needs_null_check);
- } else {
- EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
- Load(type, ref_reg, field);
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- }
+ __ bind(&return_address);
}
-
- // Object* ref = ref_addr->AsMirrorPtr()
- GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
+ MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__, /* temp_loc */ LocationFrom(ip1));
}
void CodeGeneratorARM64::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
@@ -6874,7 +6162,7 @@
// Load the lock word containing the rb_state.
__ Ldr(ip0.W(), lock_word);
// Given the numeric representation, it's enough to check the low bit of the rb_state.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
+ static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
__ Tbnz(ip0.W(), LockWord::kReadBarrierStateShift, slow_path);
static_assert(
@@ -6912,7 +6200,8 @@
/*out*/ std::string* debug_name) {
BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
switch (kind) {
- case BakerReadBarrierKind::kField: {
+ case BakerReadBarrierKind::kField:
+ case BakerReadBarrierKind::kAcquire: {
auto base_reg =
Register::GetXRegFromCode(BakerReadBarrierFirstRegField::Decode(encoded_data));
CheckValidReg(base_reg.GetCode());
@@ -6938,11 +6227,18 @@
MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value());
EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, throw_npe);
__ Bind(&slow_path);
- MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET);
- __ Ldr(ip0.W(), ldr_address); // Load the LDR (immediate) unsigned offset.
- LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1);
- __ Ubfx(ip0.W(), ip0.W(), 10, 12); // Extract the offset.
- __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2)); // Load the reference.
+ if (kind == BakerReadBarrierKind::kField) {
+ MemOperand ldr_address(lr, BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET);
+ __ Ldr(ip0.W(), ldr_address); // Load the LDR (immediate) unsigned offset.
+ LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1);
+ __ Ubfx(ip0.W(), ip0.W(), 10, 12); // Extract the offset.
+ __ Ldr(ip0.W(), MemOperand(base_reg, ip0, LSL, 2)); // Load the reference.
+ } else {
+ DCHECK(kind == BakerReadBarrierKind::kAcquire);
+ DCHECK(!base_reg.Is(holder_reg));
+ LoadReadBarrierMarkIntrospectionEntrypoint(assembler, ip1);
+ __ Ldar(ip0.W(), MemOperand(base_reg));
+ }
// Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference.
__ Br(ip1); // Jump to the entrypoint.
break;
@@ -7023,6 +6319,10 @@
oss << "Field_r" << BakerReadBarrierFirstRegField::Decode(encoded_data)
<< "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data);
break;
+ case BakerReadBarrierKind::kAcquire:
+ oss << "Acquire_r" << BakerReadBarrierFirstRegField::Decode(encoded_data)
+ << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data);
+ break;
case BakerReadBarrierKind::kArray:
oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index c07d1ea..4f6a44f 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -92,6 +92,16 @@
((kEmitCompilerReadBarrier && kUseBakerReadBarrier) ? mr : vixl::aarch64::NoCPUReg),
vixl::aarch64::lr);
+// Some instructions have special requirements for a temporary, for example
+// LoadClass/kBssEntry and LoadString/kBssEntry for Baker read barrier require
+// temp that's not an R0 (to avoid an extra move) and Baker read barrier field
+// loads with large offsets need a fixed register to limit the number of link-time
+// thunks we generate. For these and similar cases, we want to reserve a specific
+// register that's neither callee-save nor an argument register. We choose x15.
+inline Location FixedTempLocation() {
+ return Location::RegisterLocation(vixl::aarch64::x15.GetCode());
+}
+
// Callee-save registers AAPCS64, without x19 (Thread Register) (nor
// x20 (Marking Register) when emitting Baker read barriers).
const vixl::aarch64::CPURegList callee_saved_core_registers(
@@ -661,6 +671,18 @@
uint32_t offset,
vixl::aarch64::Label* fixup_label,
ReadBarrierOption read_barrier_option);
+ // Generate MOV for the `old_value` in UnsafeCASObject and mark it with Baker read barrier.
+ void GenerateUnsafeCasOldValueMovWithBakerReadBarrier(vixl::aarch64::Register marked,
+ vixl::aarch64::Register old_value);
+ // Fast path implementation of ReadBarrier::Barrier for a heap
+ // reference field load when Baker's read barriers are used.
+ // Overload suitable for Unsafe.getObject/-Volatile() intrinsic.
+ void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ vixl::aarch64::Register obj,
+ const vixl::aarch64::MemOperand& src,
+ bool needs_null_check,
+ bool use_load_acquire);
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference field load when Baker's read barriers are used.
void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -672,58 +694,12 @@
bool use_load_acquire);
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference array load when Baker's read barriers are used.
- void GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
+ void GenerateArrayLoadWithBakerReadBarrier(Location ref,
vixl::aarch64::Register obj,
uint32_t data_offset,
Location index,
vixl::aarch64::Register temp,
bool needs_null_check);
- // Factored implementation, used by GenerateFieldLoadWithBakerReadBarrier,
- // GenerateArrayLoadWithBakerReadBarrier and some intrinsics.
- //
- // Load the object reference located at the address
- // `obj + offset + (index << scale_factor)`, held by object `obj`, into
- // `ref`, and mark it if needed.
- void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- vixl::aarch64::Register obj,
- uint32_t offset,
- Location index,
- size_t scale_factor,
- vixl::aarch64::Register temp,
- bool needs_null_check,
- bool use_load_acquire);
-
- // Generate code checking whether the the reference field at the
- // address `obj + field_offset`, held by object `obj`, needs to be
- // marked, and if so, marking it and updating the field within `obj`
- // with the marked value.
- //
- // This routine is used for the implementation of the
- // UnsafeCASObject intrinsic with Baker read barriers.
- //
- // This method has a structure similar to
- // GenerateReferenceLoadWithBakerReadBarrier, but note that argument
- // `ref` is only as a temporary here, and thus its value should not
- // be used afterwards.
- void UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- vixl::aarch64::Register obj,
- Location field_offset,
- vixl::aarch64::Register temp,
- bool needs_null_check,
- bool use_load_acquire);
-
- // Generate a heap reference load (with no read barrier).
- void GenerateRawReferenceLoad(HInstruction* instruction,
- Location ref,
- vixl::aarch64::Register obj,
- uint32_t offset,
- Location index,
- size_t scale_factor,
- bool needs_null_check,
- bool use_load_acquire);
// Emit code checking the status of the Marking Register, and
// aborting the program if MR does not match the value stored in the
@@ -798,9 +774,10 @@
// Encoding of thunk type and data for link-time generated thunks for Baker read barriers.
enum class BakerReadBarrierKind : uint8_t {
- kField, // Field get or array get with constant offset (i.e. constant index).
- kArray, // Array get with index in register.
- kGcRoot, // GC root load.
+ kField, // Field get or array get with constant offset (i.e. constant index).
+ kAcquire, // Volatile field get.
+ kArray, // Array get with index in register.
+ kGcRoot, // GC root load.
kLast = kGcRoot
};
@@ -833,6 +810,15 @@
BakerReadBarrierSecondRegField::Encode(holder_reg);
}
+ static inline uint32_t EncodeBakerReadBarrierAcquireData(uint32_t base_reg, uint32_t holder_reg) {
+ CheckValidReg(base_reg);
+ CheckValidReg(holder_reg);
+ DCHECK_NE(base_reg, holder_reg);
+ return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kAcquire) |
+ BakerReadBarrierFirstRegField::Encode(base_reg) |
+ BakerReadBarrierSecondRegField::Encode(holder_reg);
+ }
+
static inline uint32_t EncodeBakerReadBarrierArrayData(uint32_t base_reg) {
CheckValidReg(base_reg);
return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kArray) |
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 47d7360..8c5eafd 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -85,15 +85,10 @@
// Reference load (except object array loads) is using LDR Rt, [Rn, #offset] which can handle
// offset < 4KiB. For offsets >= 4KiB, the load shall be emitted as two or more instructions.
-// For the Baker read barrier implementation using link-generated thunks we need to split
+// For the Baker read barrier implementation using link-time generated thunks we need to split
// the offset explicitly.
constexpr uint32_t kReferenceLoadMinFarOffset = 4 * KB;
-// Flags controlling the use of link-time generated thunks for Baker read barriers.
-constexpr bool kBakerReadBarrierLinkTimeThunksEnableForFields = true;
-constexpr bool kBakerReadBarrierLinkTimeThunksEnableForArrays = true;
-constexpr bool kBakerReadBarrierLinkTimeThunksEnableForGcRoots = true;
-
// Using a base helps identify when we hit Marking Register check breakpoints.
constexpr int kMarkingRegisterCheckBreakCodeBaseCode = 0x10;
@@ -116,7 +111,7 @@
public:
EmitAdrCode(ArmVIXLMacroAssembler* assembler, vixl32::Register rd, vixl32::Label* label)
: assembler_(assembler), rd_(rd), label_(label) {
- ExactAssemblyScope aas(assembler, kMaxInstructionSizeInBytes);
+ DCHECK(!assembler->AllowMacroInstructions()); // In ExactAssemblyScope.
adr_location_ = assembler->GetCursorOffset();
assembler->adr(EncodingSize(Wide), rd, label);
}
@@ -720,489 +715,6 @@
DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
};
-// Abstract base class for read barrier slow paths marking a reference
-// `ref`.
-//
-// Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked or an empty
-// location; in the latter case, the read barrier marking runtime
-// entry point will be loaded by the slow path code itself.
-class ReadBarrierMarkSlowPathBaseARMVIXL : public SlowPathCodeARMVIXL {
- protected:
- ReadBarrierMarkSlowPathBaseARMVIXL(HInstruction* instruction, Location ref, Location entrypoint)
- : SlowPathCodeARMVIXL(instruction), ref_(ref), entrypoint_(entrypoint) {
- DCHECK(kEmitCompilerReadBarrier);
- }
-
- const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathBaseARMVIXL"; }
-
- // Generate assembly code calling the read barrier marking runtime
- // entry point (ReadBarrierMarkRegX).
- void GenerateReadBarrierMarkRuntimeCall(CodeGenerator* codegen) {
- vixl32::Register ref_reg = RegisterFrom(ref_);
-
- // No need to save live registers; it's taken care of by the
- // entrypoint. Also, there is no need to update the stack mask,
- // as this runtime call will not trigger a garbage collection.
- CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
- DCHECK(!ref_reg.Is(sp));
- DCHECK(!ref_reg.Is(lr));
- DCHECK(!ref_reg.Is(pc));
- // IP is used internally by the ReadBarrierMarkRegX entry point
- // as a temporary, it cannot be the entry point's input/output.
- DCHECK(!ref_reg.Is(ip));
- DCHECK(ref_reg.IsRegister()) << ref_reg;
- // "Compact" slow path, saving two moves.
- //
- // Instead of using the standard runtime calling convention (input
- // and output in R0):
- //
- // R0 <- ref
- // R0 <- ReadBarrierMark(R0)
- // ref <- R0
- //
- // we just use rX (the register containing `ref`) as input and output
- // of a dedicated entrypoint:
- //
- // rX <- ReadBarrierMarkRegX(rX)
- //
- if (entrypoint_.IsValid()) {
- arm_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
- __ Blx(RegisterFrom(entrypoint_));
- } else {
- // Entrypoint is not already loaded, load from the thread.
- int32_t entry_point_offset =
- Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg.GetCode());
- // This runtime call does not require a stack map.
- arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
- }
- }
-
- // The location (register) of the marked object reference.
- const Location ref_;
-
- // The location of the entrypoint if already loaded.
- const Location entrypoint_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathBaseARMVIXL);
-};
-
-// Slow path marking an object reference `ref` during a read
-// barrier. The field `obj.field` in the object `obj` holding this
-// reference does not get updated by this slow path after marking.
-//
-// This means that after the execution of this slow path, `ref` will
-// always be up-to-date, but `obj.field` may not; i.e., after the
-// flip, `ref` will be a to-space reference, but `obj.field` will
-// probably still be a from-space reference (unless it gets updated by
-// another thread, or if another thread installed another object
-// reference (different from `ref`) in `obj.field`).
-//
-// Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked or an empty
-// location; in the latter case, the read barrier marking runtime
-// entry point will be loaded by the slow path code itself.
-class ReadBarrierMarkSlowPathARMVIXL : public ReadBarrierMarkSlowPathBaseARMVIXL {
- public:
- ReadBarrierMarkSlowPathARMVIXL(HInstruction* instruction,
- Location ref,
- Location entrypoint = Location::NoLocation())
- : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint) {
- DCHECK(kEmitCompilerReadBarrier);
- }
-
- const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARMVIXL"; }
-
- void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
- LocationSummary* locations = instruction_->GetLocations();
- DCHECK(locations->CanCall());
- DCHECK(ref_.IsRegister()) << ref_;
- DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_.reg())) << ref_.reg();
- DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
- << "Unexpected instruction in read barrier marking slow path: "
- << instruction_->DebugName();
-
- __ Bind(GetEntryLabel());
- GenerateReadBarrierMarkRuntimeCall(codegen);
- __ B(GetExitLabel());
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARMVIXL);
-};
-
-// Slow path loading `obj`'s lock word, loading a reference from
-// object `*(obj + offset + (index << scale_factor))` into `ref`, and
-// marking `ref` if `obj` is gray according to the lock word (Baker
-// read barrier). The field `obj.field` in the object `obj` holding
-// this reference does not get updated by this slow path after marking
-// (see LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL
-// below for that).
-//
-// This means that after the execution of this slow path, `ref` will
-// always be up-to-date, but `obj.field` may not; i.e., after the
-// flip, `ref` will be a to-space reference, but `obj.field` will
-// probably still be a from-space reference (unless it gets updated by
-// another thread, or if another thread installed another object
-// reference (different from `ref`) in `obj.field`).
-//
-// Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked or an empty
-// location; in the latter case, the read barrier marking runtime
-// entry point will be loaded by the slow path code itself.
-class LoadReferenceWithBakerReadBarrierSlowPathARMVIXL : public ReadBarrierMarkSlowPathBaseARMVIXL {
- public:
- LoadReferenceWithBakerReadBarrierSlowPathARMVIXL(HInstruction* instruction,
- Location ref,
- vixl32::Register obj,
- uint32_t offset,
- Location index,
- ScaleFactor scale_factor,
- bool needs_null_check,
- vixl32::Register temp,
- Location entrypoint = Location::NoLocation())
- : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint),
- obj_(obj),
- offset_(offset),
- index_(index),
- scale_factor_(scale_factor),
- needs_null_check_(needs_null_check),
- temp_(temp) {
- DCHECK(kEmitCompilerReadBarrier);
- DCHECK(kUseBakerReadBarrier);
- }
-
- const char* GetDescription() const OVERRIDE {
- return "LoadReferenceWithBakerReadBarrierSlowPathARMVIXL";
- }
-
- void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
- LocationSummary* locations = instruction_->GetLocations();
- vixl32::Register ref_reg = RegisterFrom(ref_);
- DCHECK(locations->CanCall());
- DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg;
- DCHECK(instruction_->IsInstanceFieldGet() ||
- instruction_->IsStaticFieldGet() ||
- instruction_->IsArrayGet() ||
- instruction_->IsArraySet() ||
- instruction_->IsInstanceOf() ||
- instruction_->IsCheckCast() ||
- (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
- (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
- << "Unexpected instruction in read barrier marking slow path: "
- << instruction_->DebugName();
- // The read barrier instrumentation of object ArrayGet
- // instructions does not support the HIntermediateAddress
- // instruction.
- DCHECK(!(instruction_->IsArrayGet() &&
- instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
-
- // Temporary register `temp_`, used to store the lock word, must
- // not be IP, as we may use it to emit the reference load (in the
- // call to GenerateRawReferenceLoad below), and we need the lock
- // word to still be in `temp_` after the reference load.
- DCHECK(!temp_.Is(ip));
-
- __ Bind(GetEntryLabel());
-
- // When using MaybeGenerateReadBarrierSlow, the read barrier call is
- // inserted after the original load. However, in fast path based
- // Baker's read barriers, we need to perform the load of
- // mirror::Object::monitor_ *before* the original reference load.
- // This load-load ordering is required by the read barrier.
- // The slow path (for Baker's algorithm) should look like:
- //
- // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
- // lfence; // Load fence or artificial data dependency to prevent load-load reordering
- // HeapReference<mirror::Object> ref = *src; // Original reference load.
- // bool is_gray = (rb_state == ReadBarrier::GrayState());
- // if (is_gray) {
- // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
- // }
- //
- // Note: the original implementation in ReadBarrier::Barrier is
- // slightly more complex as it performs additional checks that we do
- // not do here for performance reasons.
-
- CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
-
- // /* int32_t */ monitor = obj->monitor_
- uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
- arm_codegen->GetAssembler()->LoadFromOffset(kLoadWord, temp_, obj_, monitor_offset);
- if (needs_null_check_) {
- codegen->MaybeRecordImplicitNullCheck(instruction_);
- }
- // /* LockWord */ lock_word = LockWord(monitor)
- static_assert(sizeof(LockWord) == sizeof(int32_t),
- "art::LockWord and int32_t have different sizes.");
-
- // Introduce a dependency on the lock_word including the rb_state,
- // which shall prevent load-load reordering without using
- // a memory barrier (which would be more expensive).
- // `obj` is unchanged by this operation, but its value now depends
- // on `temp`.
- __ Add(obj_, obj_, Operand(temp_, ShiftType::LSR, 32));
-
- // The actual reference load.
- // A possible implicit null check has already been handled above.
- arm_codegen->GenerateRawReferenceLoad(
- instruction_, ref_, obj_, offset_, index_, scale_factor_, /* needs_null_check */ false);
-
- // Mark the object `ref` when `obj` is gray.
- //
- // if (rb_state == ReadBarrier::GrayState())
- // ref = ReadBarrier::Mark(ref);
- //
- // Given the numeric representation, it's enough to check the low bit of the
- // rb_state. We do that by shifting the bit out of the lock word with LSRS
- // which can be a 16-bit instruction unlike the TST immediate.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
- static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
- __ Lsrs(temp_, temp_, LockWord::kReadBarrierStateShift + 1);
- __ B(cc, GetExitLabel()); // Carry flag is the last bit shifted out by LSRS.
- GenerateReadBarrierMarkRuntimeCall(codegen);
-
- __ B(GetExitLabel());
- }
-
- private:
- // The register containing the object holding the marked object reference field.
- vixl32::Register obj_;
- // The offset, index and scale factor to access the reference in `obj_`.
- uint32_t offset_;
- Location index_;
- ScaleFactor scale_factor_;
- // Is a null check required?
- bool needs_null_check_;
- // A temporary register used to hold the lock word of `obj_`.
- vixl32::Register temp_;
-
- DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierSlowPathARMVIXL);
-};
-
-// Slow path loading `obj`'s lock word, loading a reference from
-// object `*(obj + offset + (index << scale_factor))` into `ref`, and
-// marking `ref` if `obj` is gray according to the lock word (Baker
-// read barrier). If needed, this slow path also atomically updates
-// the field `obj.field` in the object `obj` holding this reference
-// after marking (contrary to
-// LoadReferenceWithBakerReadBarrierSlowPathARMVIXL above, which never
-// tries to update `obj.field`).
-//
-// This means that after the execution of this slow path, both `ref`
-// and `obj.field` will be up-to-date; i.e., after the flip, both will
-// hold the same to-space reference (unless another thread installed
-// another object reference (different from `ref`) in `obj.field`).
-//
-// Argument `entrypoint` must be a register location holding the read
-// barrier marking runtime entry point to be invoked or an empty
-// location; in the latter case, the read barrier marking runtime
-// entry point will be loaded by the slow path code itself.
-class LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL
- : public ReadBarrierMarkSlowPathBaseARMVIXL {
- public:
- LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL(
- HInstruction* instruction,
- Location ref,
- vixl32::Register obj,
- uint32_t offset,
- Location index,
- ScaleFactor scale_factor,
- bool needs_null_check,
- vixl32::Register temp1,
- vixl32::Register temp2,
- Location entrypoint = Location::NoLocation())
- : ReadBarrierMarkSlowPathBaseARMVIXL(instruction, ref, entrypoint),
- obj_(obj),
- offset_(offset),
- index_(index),
- scale_factor_(scale_factor),
- needs_null_check_(needs_null_check),
- temp1_(temp1),
- temp2_(temp2) {
- DCHECK(kEmitCompilerReadBarrier);
- DCHECK(kUseBakerReadBarrier);
- }
-
- const char* GetDescription() const OVERRIDE {
- return "LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL";
- }
-
- void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
- LocationSummary* locations = instruction_->GetLocations();
- vixl32::Register ref_reg = RegisterFrom(ref_);
- DCHECK(locations->CanCall());
- DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg;
- DCHECK_NE(ref_.reg(), LocationFrom(temp1_).reg());
-
- // This slow path is only used by the UnsafeCASObject intrinsic at the moment.
- DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
- << "Unexpected instruction in read barrier marking and field updating slow path: "
- << instruction_->DebugName();
- DCHECK(instruction_->GetLocations()->Intrinsified());
- DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
- DCHECK_EQ(offset_, 0u);
- DCHECK_EQ(scale_factor_, ScaleFactor::TIMES_1);
- Location field_offset = index_;
- DCHECK(field_offset.IsRegisterPair()) << field_offset;
-
- // Temporary register `temp1_`, used to store the lock word, must
- // not be IP, as we may use it to emit the reference load (in the
- // call to GenerateRawReferenceLoad below), and we need the lock
- // word to still be in `temp1_` after the reference load.
- DCHECK(!temp1_.Is(ip));
-
- __ Bind(GetEntryLabel());
-
- // The implementation is similar to LoadReferenceWithBakerReadBarrierSlowPathARMVIXL's:
- //
- // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
- // lfence; // Load fence or artificial data dependency to prevent load-load reordering
- // HeapReference<mirror::Object> ref = *src; // Original reference load.
- // bool is_gray = (rb_state == ReadBarrier::GrayState());
- // if (is_gray) {
- // old_ref = ref;
- // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
- // compareAndSwapObject(obj, field_offset, old_ref, ref);
- // }
-
- CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
-
- // /* int32_t */ monitor = obj->monitor_
- uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
- arm_codegen->GetAssembler()->LoadFromOffset(kLoadWord, temp1_, obj_, monitor_offset);
- if (needs_null_check_) {
- codegen->MaybeRecordImplicitNullCheck(instruction_);
- }
- // /* LockWord */ lock_word = LockWord(monitor)
- static_assert(sizeof(LockWord) == sizeof(int32_t),
- "art::LockWord and int32_t have different sizes.");
-
- // Introduce a dependency on the lock_word including the rb_state,
- // which shall prevent load-load reordering without using
- // a memory barrier (which would be more expensive).
- // `obj` is unchanged by this operation, but its value now depends
- // on `temp`.
- __ Add(obj_, obj_, Operand(temp1_, ShiftType::LSR, 32));
-
- // The actual reference load.
- // A possible implicit null check has already been handled above.
- arm_codegen->GenerateRawReferenceLoad(
- instruction_, ref_, obj_, offset_, index_, scale_factor_, /* needs_null_check */ false);
-
- // Mark the object `ref` when `obj` is gray.
- //
- // if (rb_state == ReadBarrier::GrayState())
- // ref = ReadBarrier::Mark(ref);
- //
- // Given the numeric representation, it's enough to check the low bit of the
- // rb_state. We do that by shifting the bit out of the lock word with LSRS
- // which can be a 16-bit instruction unlike the TST immediate.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
- static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
- __ Lsrs(temp1_, temp1_, LockWord::kReadBarrierStateShift + 1);
- __ B(cc, GetExitLabel()); // Carry flag is the last bit shifted out by LSRS.
-
- // Save the old value of the reference before marking it.
- // Note that we cannot use IP to save the old reference, as IP is
- // used internally by the ReadBarrierMarkRegX entry point, and we
- // need the old reference after the call to that entry point.
- DCHECK(!temp1_.Is(ip));
- __ Mov(temp1_, ref_reg);
-
- GenerateReadBarrierMarkRuntimeCall(codegen);
-
- // If the new reference is different from the old reference,
- // update the field in the holder (`*(obj_ + field_offset)`).
- //
- // Note that this field could also hold a different object, if
- // another thread had concurrently changed it. In that case, the
- // LDREX/CMP/BNE sequence of instructions in the compare-and-set
- // (CAS) operation below would abort the CAS, leaving the field
- // as-is.
- __ Cmp(temp1_, ref_reg);
- __ B(eq, GetExitLabel());
-
- // Update the the holder's field atomically. This may fail if
- // mutator updates before us, but it's OK. This is achieved
- // using a strong compare-and-set (CAS) operation with relaxed
- // memory synchronization ordering, where the expected value is
- // the old reference and the desired value is the new reference.
-
- UseScratchRegisterScope temps(arm_codegen->GetVIXLAssembler());
- // Convenience aliases.
- vixl32::Register base = obj_;
- // The UnsafeCASObject intrinsic uses a register pair as field
- // offset ("long offset"), of which only the low part contains
- // data.
- vixl32::Register offset = LowRegisterFrom(field_offset);
- vixl32::Register expected = temp1_;
- vixl32::Register value = ref_reg;
- vixl32::Register tmp_ptr = temps.Acquire(); // Pointer to actual memory.
- vixl32::Register tmp = temp2_; // Value in memory.
-
- __ Add(tmp_ptr, base, offset);
-
- if (kPoisonHeapReferences) {
- arm_codegen->GetAssembler()->PoisonHeapReference(expected);
- if (value.Is(expected)) {
- // Do not poison `value`, as it is the same register as
- // `expected`, which has just been poisoned.
- } else {
- arm_codegen->GetAssembler()->PoisonHeapReference(value);
- }
- }
-
- // do {
- // tmp = [r_ptr] - expected;
- // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
-
- vixl32::Label loop_head, comparison_failed, exit_loop;
- __ Bind(&loop_head);
- __ Ldrex(tmp, MemOperand(tmp_ptr));
- __ Cmp(tmp, expected);
- __ B(ne, &comparison_failed, /* far_target */ false);
- __ Strex(tmp, value, MemOperand(tmp_ptr));
- __ CompareAndBranchIfZero(tmp, &exit_loop, /* far_target */ false);
- __ B(&loop_head);
- __ Bind(&comparison_failed);
- __ Clrex();
- __ Bind(&exit_loop);
-
- if (kPoisonHeapReferences) {
- arm_codegen->GetAssembler()->UnpoisonHeapReference(expected);
- if (value.Is(expected)) {
- // Do not unpoison `value`, as it is the same register as
- // `expected`, which has just been unpoisoned.
- } else {
- arm_codegen->GetAssembler()->UnpoisonHeapReference(value);
- }
- }
-
- __ B(GetExitLabel());
- }
-
- private:
- // The register containing the object holding the marked object reference field.
- const vixl32::Register obj_;
- // The offset, index and scale factor to access the reference in `obj_`.
- uint32_t offset_;
- Location index_;
- ScaleFactor scale_factor_;
- // Is a null check required?
- bool needs_null_check_;
- // A temporary register used to hold the lock word of `obj_`; and
- // also to hold the original reference value, when the reference is
- // marked.
- const vixl32::Register temp1_;
- // A temporary register used in the implementation of the CAS, to
- // update the object's reference field.
- const vixl32::Register temp2_;
-
- DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL);
-};
-
// Slow path generating a read barrier for a heap reference.
class ReadBarrierForHeapReferenceSlowPathARMVIXL : public SlowPathCodeARMVIXL {
public:
@@ -2495,6 +2007,14 @@
}
break;
}
+ case BakerReadBarrierKind::kUnsafeCas: {
+ DCHECK_GE(literal_offset, 4u);
+ uint32_t prev_insn = GetInsn32(literal_offset - 4u);
+ // ADD (register), encoding T3, with correct root_reg.
+ const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
+ CHECK_EQ(prev_insn & 0xfff0fff0u, 0xeb000000u | (root_reg << 8));
+ break;
+ }
default:
LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
UNREACHABLE();
@@ -5964,16 +5484,10 @@
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
} else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
- // We need a temporary register for the read barrier marking slow
- // path in CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier.
- if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
- !Runtime::Current()->UseJitCompilation()) {
- // If link-time thunks for the Baker read barrier are enabled, for AOT
- // loads we need a temporary only if the offset is too big.
- if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
- locations->AddTemp(Location::RequiresRegister());
- }
- } else {
+ // We need a temporary register for the read barrier load in
+ // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier()
+ // only if the offset is too big.
+ if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -6388,12 +5902,11 @@
object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
}
if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
- if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
- !Runtime::Current()->UseJitCompilation() &&
- instruction->GetIndex()->IsConstant()) {
+ if (instruction->GetIndex()->IsConstant()) {
// Array loads with constant index are treated as field loads.
- // If link-time thunks for the Baker read barrier are enabled, for AOT
- // constant index loads we need a temporary only if the offset is too big.
+ // We need a temporary register for the read barrier load in
+ // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier()
+ // only if the offset is too big.
uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
uint32_t index = instruction->GetIndex()->AsIntConstant()->GetValue();
offset += index << DataType::SizeShift(DataType::Type::kReference);
@@ -6401,9 +5914,8 @@
locations->AddTemp(Location::RequiresRegister());
}
} else {
- // If using introspection, we need a non-scratch temporary for the array data pointer.
- // Otherwise, we need a temporary register for the read barrier marking slow
- // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier.
+ // We need a non-scratch temporary for the array data pointer in
+ // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier().
locations->AddTemp(Location::RequiresRegister());
}
} else if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
@@ -6533,7 +6045,7 @@
} else {
Location temp = locations->GetTemp(0);
codegen_->GenerateArrayLoadWithBakerReadBarrier(
- instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ false);
+ out_loc, obj, data_offset, index, temp, /* needs_null_check */ false);
}
} else {
vixl32::Register out = OutputRegister(instruction);
@@ -7052,9 +6564,25 @@
if (can_be_null) {
__ CompareAndBranchIfZero(value, &is_null);
}
+ // Load the address of the card table into `card`.
GetAssembler()->LoadFromOffset(
kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
+ // Calculate the offset (in the card table) of the card corresponding to
+ // `object`.
__ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
+ // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
+ // `object`'s card.
+ //
+ // Register `card` contains the address of the card table. Note that the card
+ // table's base is biased during its creation so that it always starts at an
+ // address whose least-significant byte is equal to `kCardDirty` (see
+ // art::gc::accounting::CardTable::Create). Therefore the STRB instruction
+ // below writes the `kCardDirty` (byte) value into the `object`'s card
+ // (located at `card + object >> kCardShift`).
+ //
+ // This dual use of the value in register `card` (1. to calculate the location
+ // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
+ // (no need to explicitly load `kCardDirty` as an immediate value).
__ Strb(card, MemOperand(card, temp));
if (can_be_null) {
__ Bind(&is_null);
@@ -8797,72 +8325,45 @@
if (kUseBakerReadBarrier) {
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used.
- if (kBakerReadBarrierLinkTimeThunksEnableForGcRoots) {
- // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
- // the Marking Register) to decide whether we need to enter
- // the slow path to mark the GC root.
- //
- // We use shared thunks for the slow path; shared within the method
- // for JIT, across methods for AOT. That thunk checks the reference
- // and jumps to the entrypoint if needed.
- //
- // lr = &return_address;
- // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
- // if (mr) { // Thread::Current()->GetIsGcMarking()
- // goto gc_root_thunk<root_reg>(lr)
- // }
- // return_address:
- UseScratchRegisterScope temps(GetVIXLAssembler());
- temps.Exclude(ip);
- bool narrow = CanEmitNarrowLdr(root_reg, obj, offset);
- uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow);
+ // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
+ // the Marking Register) to decide whether we need to enter
+ // the slow path to mark the GC root.
+ //
+ // We use shared thunks for the slow path; shared within the method
+ // for JIT, across methods for AOT. That thunk checks the reference
+ // and jumps to the entrypoint if needed.
+ //
+ // lr = &return_address;
+ // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
+ // if (mr) { // Thread::Current()->GetIsGcMarking()
+ // goto gc_root_thunk<root_reg>(lr)
+ // }
+ // return_address:
- vixl::EmissionCheckScope guard(GetVIXLAssembler(), 4 * vixl32::kMaxInstructionSizeInBytes);
- vixl32::Label return_address;
- EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
- __ cmp(mr, Operand(0));
- // Currently the offset is always within range. If that changes,
- // we shall have to split the load the same way as for fields.
- DCHECK_LT(offset, kReferenceLoadMinFarOffset);
- ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
- __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset));
- EmitBakerReadBarrierBne(custom_data);
- __ Bind(&return_address);
- DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
- narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET
- : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET);
- } else {
- // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
- // the Marking Register) to decide whether we need to enter
- // the slow path to mark the GC root.
- //
- // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
- // if (mr) { // Thread::Current()->GetIsGcMarking()
- // // Slow path.
- // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
- // root = entrypoint(root); // root = ReadBarrier::Mark(root); // Entry point call.
- // }
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ temps.Exclude(ip);
+ bool narrow = CanEmitNarrowLdr(root_reg, obj, offset);
+ uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow);
- // Slow path marking the GC root `root`. The entrypoint will
- // be loaded by the slow path code.
- SlowPathCodeARMVIXL* slow_path =
- new (GetScopedAllocator()) ReadBarrierMarkSlowPathARMVIXL(instruction, root);
- AddSlowPath(slow_path);
-
- // /* GcRoot<mirror::Object> */ root = *(obj + offset)
- GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
- static_assert(
- sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
- "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
- "have different sizes.");
- static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
- "art::mirror::CompressedReference<mirror::Object> and int32_t "
- "have different sizes.");
-
- __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
- __ Bind(slow_path->GetExitLabel());
- }
+ size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u) + /* LDR */ (narrow ? 1u : 0u);
+ size_t wide_instructions = /* ADR+CMP+LDR+BNE */ 4u - narrow_instructions;
+ size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
+ narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
+ ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
+ vixl32::Label return_address;
+ EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
+ __ cmp(mr, Operand(0));
+ // Currently the offset is always within range. If that changes,
+ // we shall have to split the load the same way as for fields.
+ DCHECK_LT(offset, kReferenceLoadMinFarOffset);
+ ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
+ __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset));
+ EmitBakerReadBarrierBne(custom_data);
+ __ bind(&return_address);
+ DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
+ narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET
+ : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET);
} else {
// GC root loaded through a slow path for read barriers other
// than Baker's.
@@ -8881,95 +8382,125 @@
MaybeGenerateMarkingRegisterCheck(/* code */ 19);
}
+void CodeGeneratorARMVIXL::GenerateUnsafeCasOldValueAddWithBakerReadBarrier(
+ vixl::aarch32::Register old_value,
+ vixl::aarch32::Register adjusted_old_value,
+ vixl::aarch32::Register expected) {
+ DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(kUseBakerReadBarrier);
+
+ // Similar to the Baker RB path in GenerateGcRootFieldLoad(), with an ADD instead of LDR.
+ uint32_t custom_data = EncodeBakerReadBarrierUnsafeCasData(old_value.GetCode());
+
+ size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u);
+ size_t wide_instructions = /* ADR+CMP+ADD+BNE */ 4u - narrow_instructions;
+ size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
+ narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
+ ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
+ vixl32::Label return_address;
+ EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
+ __ cmp(mr, Operand(0));
+ ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
+ __ add(EncodingSize(Wide), old_value, adjusted_old_value, Operand(expected)); // Preserves flags.
+ EmitBakerReadBarrierBne(custom_data);
+ __ bind(&return_address);
+ DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
+ BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ADD_OFFSET);
+}
+
+void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ vixl32::Register obj,
+ const vixl32::MemOperand& src,
+ bool needs_null_check) {
+ DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(kUseBakerReadBarrier);
+
+ // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
+ // Marking Register) to decide whether we need to enter the slow
+ // path to mark the reference. Then, in the slow path, check the
+ // gray bit in the lock word of the reference's holder (`obj`) to
+ // decide whether to mark `ref` or not.
+ //
+ // We use shared thunks for the slow path; shared within the method
+ // for JIT, across methods for AOT. That thunk checks the holder
+ // and jumps to the entrypoint if needed. If the holder is not gray,
+ // it creates a fake dependency and returns to the LDR instruction.
+ //
+ // lr = &gray_return_address;
+ // if (mr) { // Thread::Current()->GetIsGcMarking()
+ // goto field_thunk<holder_reg, base_reg>(lr)
+ // }
+ // not_gray_return_address:
+ // // Original reference load. If the offset is too large to fit
+ // // into LDR, we use an adjusted base register here.
+ // HeapReference<mirror::Object> reference = *(obj+offset);
+ // gray_return_address:
+
+ DCHECK(src.GetAddrMode() == vixl32::Offset);
+ DCHECK_ALIGNED(src.GetOffsetImmediate(), sizeof(mirror::HeapReference<mirror::Object>));
+ vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
+ bool narrow = CanEmitNarrowLdr(ref_reg, src.GetBaseRegister(), src.GetOffsetImmediate());
+
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ temps.Exclude(ip);
+ uint32_t custom_data =
+ EncodeBakerReadBarrierFieldData(src.GetBaseRegister().GetCode(), obj.GetCode(), narrow);
+
+ {
+ size_t narrow_instructions =
+ /* CMP */ (mr.IsLow() ? 1u : 0u) +
+ /* LDR+unpoison? */ (narrow ? (kPoisonHeapReferences ? 2u : 1u) : 0u);
+ size_t wide_instructions =
+ /* ADR+CMP+LDR+BNE+unpoison? */ (kPoisonHeapReferences ? 5u : 4u) - narrow_instructions;
+ size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
+ narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
+ ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
+ vixl32::Label return_address;
+ EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
+ __ cmp(mr, Operand(0));
+ EmitBakerReadBarrierBne(custom_data);
+ ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
+ __ ldr(EncodingSize(narrow ? Narrow : Wide), ref_reg, src);
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ // Note: We need a specific width for the unpoisoning NEG.
+ if (kPoisonHeapReferences) {
+ if (narrow) {
+ // The only 16-bit encoding is T1 which sets flags outside IT block (i.e. RSBS, not RSB).
+ __ rsbs(EncodingSize(Narrow), ref_reg, ref_reg, Operand(0));
+ } else {
+ __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
+ }
+ }
+ __ bind(&return_address);
+ DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
+ narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
+ : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
+ }
+ MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip));
+}
+
void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
Location ref,
vixl32::Register obj,
uint32_t offset,
Location temp,
bool needs_null_check) {
- DCHECK(kEmitCompilerReadBarrier);
- DCHECK(kUseBakerReadBarrier);
-
- if (kBakerReadBarrierLinkTimeThunksEnableForFields) {
- // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
- // Marking Register) to decide whether we need to enter the slow
- // path to mark the reference. Then, in the slow path, check the
- // gray bit in the lock word of the reference's holder (`obj`) to
- // decide whether to mark `ref` or not.
- //
- // We use shared thunks for the slow path; shared within the method
- // for JIT, across methods for AOT. That thunk checks the holder
- // and jumps to the entrypoint if needed. If the holder is not gray,
- // it creates a fake dependency and returns to the LDR instruction.
- //
- // lr = &gray_return_address;
- // if (mr) { // Thread::Current()->GetIsGcMarking()
- // goto field_thunk<holder_reg, base_reg>(lr)
- // }
- // not_gray_return_address:
- // // Original reference load. If the offset is too large to fit
- // // into LDR, we use an adjusted base register here.
- // HeapReference<mirror::Object> reference = *(obj+offset);
- // gray_return_address:
-
- DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
- vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
- bool narrow = CanEmitNarrowLdr(ref_reg, obj, offset);
- vixl32::Register base = obj;
- if (offset >= kReferenceLoadMinFarOffset) {
- base = RegisterFrom(temp);
- static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
- __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
- offset &= (kReferenceLoadMinFarOffset - 1u);
- // Use narrow LDR only for small offsets. Generating narrow encoding LDR for the large
- // offsets with `(offset & (kReferenceLoadMinFarOffset - 1u)) < 32u` would most likely
- // increase the overall code size when taking the generated thunks into account.
- DCHECK(!narrow);
- }
- UseScratchRegisterScope temps(GetVIXLAssembler());
- temps.Exclude(ip);
- uint32_t custom_data = EncodeBakerReadBarrierFieldData(base.GetCode(), obj.GetCode(), narrow);
-
- {
- vixl::EmissionCheckScope guard(
- GetVIXLAssembler(),
- (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
- vixl32::Label return_address;
- EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
- __ cmp(mr, Operand(0));
- EmitBakerReadBarrierBne(custom_data);
- ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
- __ ldr(EncodingSize(narrow ? Narrow : Wide), ref_reg, MemOperand(base, offset));
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- // Note: We need a specific width for the unpoisoning NEG.
- if (kPoisonHeapReferences) {
- if (narrow) {
- // The only 16-bit encoding is T1 which sets flags outside IT block (i.e. RSBS, not RSB).
- __ rsbs(EncodingSize(Narrow), ref_reg, ref_reg, Operand(0));
- } else {
- __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
- }
- }
- __ Bind(&return_address);
- DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
- narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
- : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
- }
- MaybeGenerateMarkingRegisterCheck(/* code */ 20, /* temp_loc */ LocationFrom(ip));
- return;
+ DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
+ vixl32::Register base = obj;
+ if (offset >= kReferenceLoadMinFarOffset) {
+ base = RegisterFrom(temp);
+ static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
+ __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
+ offset &= (kReferenceLoadMinFarOffset - 1u);
}
-
- // /* HeapReference<Object> */ ref = *(obj + offset)
- Location no_index = Location::NoLocation();
- ScaleFactor no_scale_factor = TIMES_1;
- GenerateReferenceLoadWithBakerReadBarrier(
- instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check);
+ GenerateFieldLoadWithBakerReadBarrier(
+ instruction, ref, obj, MemOperand(base, offset), needs_null_check);
}
-void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
+void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(Location ref,
vixl32::Register obj,
uint32_t data_offset,
Location index,
@@ -8983,226 +8514,60 @@
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
ScaleFactor scale_factor = TIMES_4;
- if (kBakerReadBarrierLinkTimeThunksEnableForArrays) {
- // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
- // Marking Register) to decide whether we need to enter the slow
- // path to mark the reference. Then, in the slow path, check the
- // gray bit in the lock word of the reference's holder (`obj`) to
- // decide whether to mark `ref` or not.
- //
- // We use shared thunks for the slow path; shared within the method
- // for JIT, across methods for AOT. That thunk checks the holder
- // and jumps to the entrypoint if needed. If the holder is not gray,
- // it creates a fake dependency and returns to the LDR instruction.
- //
- // lr = &gray_return_address;
- // if (mr) { // Thread::Current()->GetIsGcMarking()
- // goto array_thunk<base_reg>(lr)
- // }
- // not_gray_return_address:
- // // Original reference load. If the offset is too large to fit
- // // into LDR, we use an adjusted base register here.
- // HeapReference<mirror::Object> reference = data[index];
- // gray_return_address:
-
- DCHECK(index.IsValid());
- vixl32::Register index_reg = RegisterFrom(index, DataType::Type::kInt32);
- vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
- vixl32::Register data_reg = RegisterFrom(temp, DataType::Type::kInt32); // Raw pointer.
-
- UseScratchRegisterScope temps(GetVIXLAssembler());
- temps.Exclude(ip);
- uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode());
-
- __ Add(data_reg, obj, Operand(data_offset));
- {
- vixl::EmissionCheckScope guard(
- GetVIXLAssembler(),
- (kPoisonHeapReferences ? 5u : 4u) * vixl32::kMaxInstructionSizeInBytes);
- vixl32::Label return_address;
- EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
- __ cmp(mr, Operand(0));
- EmitBakerReadBarrierBne(custom_data);
- ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
- __ ldr(ref_reg, MemOperand(data_reg, index_reg, vixl32::LSL, scale_factor));
- DCHECK(!needs_null_check); // The thunk cannot handle the null check.
- // Note: We need a Wide NEG for the unpoisoning.
- if (kPoisonHeapReferences) {
- __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
- }
- __ Bind(&return_address);
- DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
- BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
- }
- MaybeGenerateMarkingRegisterCheck(/* code */ 21, /* temp_loc */ LocationFrom(ip));
- return;
- }
-
- // /* HeapReference<Object> */ ref =
- // *(obj + data_offset + index * sizeof(HeapReference<Object>))
- GenerateReferenceLoadWithBakerReadBarrier(
- instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check);
-}
-
-void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- vixl32::Register obj,
- uint32_t offset,
- Location index,
- ScaleFactor scale_factor,
- Location temp,
- bool needs_null_check) {
- DCHECK(kEmitCompilerReadBarrier);
- DCHECK(kUseBakerReadBarrier);
-
// Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
// Marking Register) to decide whether we need to enter the slow
// path to mark the reference. Then, in the slow path, check the
// gray bit in the lock word of the reference's holder (`obj`) to
// decide whether to mark `ref` or not.
//
- // if (mr) { // Thread::Current()->GetIsGcMarking()
- // // Slow path.
- // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
- // lfence; // Load fence or artificial data dependency to prevent load-load reordering
- // HeapReference<mirror::Object> ref = *src; // Original reference load.
- // bool is_gray = (rb_state == ReadBarrier::GrayState());
- // if (is_gray) {
- // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
- // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
- // }
- // } else {
- // HeapReference<mirror::Object> ref = *src; // Original reference load.
- // }
-
- vixl32::Register temp_reg = RegisterFrom(temp);
-
- // Slow path marking the object `ref` when the GC is marking. The
- // entrypoint will be loaded by the slow path code.
- SlowPathCodeARMVIXL* slow_path =
- new (GetScopedAllocator()) LoadReferenceWithBakerReadBarrierSlowPathARMVIXL(
- instruction, ref, obj, offset, index, scale_factor, needs_null_check, temp_reg);
- AddSlowPath(slow_path);
-
- __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
- // Fast path: the GC is not marking: just load the reference.
- GenerateRawReferenceLoad(instruction, ref, obj, offset, index, scale_factor, needs_null_check);
- __ Bind(slow_path->GetExitLabel());
- MaybeGenerateMarkingRegisterCheck(/* code */ 22);
-}
-
-void CodeGeneratorARMVIXL::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- vixl32::Register obj,
- Location field_offset,
- Location temp,
- bool needs_null_check,
- vixl32::Register temp2) {
- DCHECK(kEmitCompilerReadBarrier);
- DCHECK(kUseBakerReadBarrier);
-
- // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
- // Marking Register) to decide whether we need to enter the slow
- // path to update the reference field within `obj`. Then, in the
- // slow path, check the gray bit in the lock word of the reference's
- // holder (`obj`) to decide whether to mark `ref` and update the
- // field or not.
+ // We use shared thunks for the slow path; shared within the method
+ // for JIT, across methods for AOT. That thunk checks the holder
+ // and jumps to the entrypoint if needed. If the holder is not gray,
+ // it creates a fake dependency and returns to the LDR instruction.
//
- // if (mr) { // Thread::Current()->GetIsGcMarking()
- // // Slow path.
- // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
- // lfence; // Load fence or artificial data dependency to prevent load-load reordering
- // HeapReference<mirror::Object> ref = *(obj + field_offset); // Reference load.
- // bool is_gray = (rb_state == ReadBarrier::GrayState());
- // if (is_gray) {
- // old_ref = ref;
- // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
- // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
- // compareAndSwapObject(obj, field_offset, old_ref, ref);
+ // lr = &gray_return_address;
+ // if (mr) { // Thread::Current()->GetIsGcMarking()
+ // goto array_thunk<base_reg>(lr)
// }
- // }
+ // not_gray_return_address:
+ // // Original reference load. If the offset is too large to fit
+ // // into LDR, we use an adjusted base register here.
+ // HeapReference<mirror::Object> reference = data[index];
+ // gray_return_address:
- vixl32::Register temp_reg = RegisterFrom(temp);
+ DCHECK(index.IsValid());
+ vixl32::Register index_reg = RegisterFrom(index, DataType::Type::kInt32);
+ vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
+ vixl32::Register data_reg = RegisterFrom(temp, DataType::Type::kInt32); // Raw pointer.
- // Slow path updating the object reference at address `obj + field_offset`
- // when the GC is marking. The entrypoint will be loaded by the slow path code.
- SlowPathCodeARMVIXL* slow_path =
- new (GetScopedAllocator()) LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARMVIXL(
- instruction,
- ref,
- obj,
- /* offset */ 0u,
- /* index */ field_offset,
- /* scale_factor */ ScaleFactor::TIMES_1,
- needs_null_check,
- temp_reg,
- temp2);
- AddSlowPath(slow_path);
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ temps.Exclude(ip);
+ uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode());
- __ CompareAndBranchIfNonZero(mr, slow_path->GetEntryLabel());
- // Fast path: the GC is not marking: nothing to do (the field is
- // up-to-date, and we don't need to load the reference).
- __ Bind(slow_path->GetExitLabel());
- MaybeGenerateMarkingRegisterCheck(/* code */ 23);
-}
-
-void CodeGeneratorARMVIXL::GenerateRawReferenceLoad(HInstruction* instruction,
- Location ref,
- vixl32::Register obj,
- uint32_t offset,
- Location index,
- ScaleFactor scale_factor,
- bool needs_null_check) {
- DataType::Type type = DataType::Type::kReference;
- vixl32::Register ref_reg = RegisterFrom(ref, type);
-
- // If needed, vixl::EmissionCheckScope guards are used to ensure
- // that no pools are emitted between the load (macro) instruction
- // and MaybeRecordImplicitNullCheck.
-
- if (index.IsValid()) {
- // Load types involving an "index": ArrayGet,
- // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
- // intrinsics.
- // /* HeapReference<mirror::Object> */ ref = *(obj + offset + (index << scale_factor))
- if (index.IsConstant()) {
- size_t computed_offset =
- (Int32ConstantFrom(index) << scale_factor) + offset;
- vixl::EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
- GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- } else {
- // Handle the special case of the
- // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
- // intrinsics, which use a register pair as index ("long
- // offset"), of which only the low part contains data.
- vixl32::Register index_reg = index.IsRegisterPair()
- ? LowRegisterFrom(index)
- : RegisterFrom(index);
- UseScratchRegisterScope temps(GetVIXLAssembler());
- vixl32::Register temp = temps.Acquire();
- __ Add(temp, obj, Operand(index_reg, ShiftType::LSL, scale_factor));
- {
- vixl::EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
- GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, temp, offset);
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
- }
+ __ Add(data_reg, obj, Operand(data_offset));
+ {
+ size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u);
+ size_t wide_instructions =
+ /* ADR+CMP+BNE+LDR+unpoison? */ (kPoisonHeapReferences ? 5u : 4u) - narrow_instructions;
+ size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
+ narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
+ ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
+ vixl32::Label return_address;
+ EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
+ __ cmp(mr, Operand(0));
+ EmitBakerReadBarrierBne(custom_data);
+ ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
+ __ ldr(ref_reg, MemOperand(data_reg, index_reg, vixl32::LSL, scale_factor));
+ DCHECK(!needs_null_check); // The thunk cannot handle the null check.
+ // Note: We need a Wide NEG for the unpoisoning.
+ if (kPoisonHeapReferences) {
+ __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
}
- } else {
- // /* HeapReference<mirror::Object> */ ref = *(obj + offset)
- vixl::EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
- GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, offset);
- if (needs_null_check) {
- MaybeRecordImplicitNullCheck(instruction);
- }
+ __ bind(&return_address);
+ DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
+ BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
}
-
- // Object* ref = ref_addr->AsMirrorPtr()
- GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
+ MaybeGenerateMarkingRegisterCheck(/* code */ 21, /* temp_loc */ LocationFrom(ip));
}
void CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
@@ -9494,7 +8859,7 @@
}
void CodeGeneratorARMVIXL::EmitBakerReadBarrierBne(uint32_t custom_data) {
- ExactAssemblyScope eas(GetVIXLAssembler(), 1 * k32BitT32InstructionSizeInBytes);
+ DCHECK(!__ AllowMacroInstructions()); // In ExactAssemblyScope.
if (Runtime::Current()->UseJitCompilation()) {
auto it = jit_baker_read_barrier_slow_paths_.FindOrAdd(custom_data);
vixl::aarch32::Label* slow_path_entry = &it->second.label;
@@ -9940,7 +9305,7 @@
// Load the lock word containing the rb_state.
__ Ldr(ip, lock_word);
// Given the numeric representation, it's enough to check the low bit of the rb_state.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
+ static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
__ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted));
__ B(ne, slow_path, /* is_far_target */ false);
@@ -10053,7 +9418,8 @@
__ Bx(ep_reg); // Jump to the entrypoint's array switch case.
break;
}
- case BakerReadBarrierKind::kGcRoot: {
+ case BakerReadBarrierKind::kGcRoot:
+ case BakerReadBarrierKind::kUnsafeCas: {
// Check if the reference needs to be marked and if so (i.e. not null, not marked yet
// and it does not have a forwarding address), call the correct introspection entrypoint;
// otherwise return the reference (or the extracted forwarding address).
@@ -10081,10 +9447,14 @@
__ B(hs, &forwarding_address);
vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
// Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister
- // to art_quick_read_barrier_mark_introspection_gc_roots.
- int32_t entrypoint_offset = (width == BakerReadBarrierWidth::kWide)
- ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET
- : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET;
+ // to one of art_quick_read_barrier_mark_introspection_{gc_roots_{wide,narrow},unsafe_cas}.
+ DCHECK(kind != BakerReadBarrierKind::kUnsafeCas || width == BakerReadBarrierWidth::kWide);
+ int32_t entrypoint_offset =
+ (kind == BakerReadBarrierKind::kGcRoot)
+ ? (width == BakerReadBarrierWidth::kWide)
+ ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET
+ : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET
+ : BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ENTRYPOINT_OFFSET;
__ Add(ep_reg, ep_reg, Operand(entrypoint_offset));
__ Mov(ip, root_reg);
__ Bx(ep_reg);
@@ -10130,6 +9500,12 @@
DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
BakerReadBarrierSecondRegField::Decode(encoded_data));
break;
+ case BakerReadBarrierKind::kUnsafeCas:
+ oss << "UnsafeCas_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
+ DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
+ BakerReadBarrierSecondRegField::Decode(encoded_data));
+ DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
+ break;
}
*debug_name = oss.str();
}
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index ef82f2e..cb131a7 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -622,6 +622,19 @@
vixl::aarch32::Register obj,
uint32_t offset,
ReadBarrierOption read_barrier_option);
+ // Generate ADD for UnsafeCASObject to reconstruct the old value from
+ // `old_value - expected` and mark it with Baker read barrier.
+ void GenerateUnsafeCasOldValueAddWithBakerReadBarrier(vixl::aarch32::Register old_value,
+ vixl::aarch32::Register adjusted_old_value,
+ vixl::aarch32::Register expected);
+ // Fast path implementation of ReadBarrier::Barrier for a heap
+ // reference field load when Baker's read barriers are used.
+ // Overload suitable for Unsafe.getObject/-Volatile() intrinsic.
+ void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ vixl::aarch32::Register obj,
+ const vixl::aarch32::MemOperand& src,
+ bool needs_null_check);
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference field load when Baker's read barriers are used.
void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -632,56 +645,12 @@
bool needs_null_check);
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference array load when Baker's read barriers are used.
- void GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
+ void GenerateArrayLoadWithBakerReadBarrier(Location ref,
vixl::aarch32::Register obj,
uint32_t data_offset,
Location index,
Location temp,
bool needs_null_check);
- // Factored implementation, used by GenerateFieldLoadWithBakerReadBarrier,
- // GenerateArrayLoadWithBakerReadBarrier and some intrinsics.
- //
- // Load the object reference located at the address
- // `obj + offset + (index << scale_factor)`, held by object `obj`, into
- // `ref`, and mark it if needed.
- void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- vixl::aarch32::Register obj,
- uint32_t offset,
- Location index,
- ScaleFactor scale_factor,
- Location temp,
- bool needs_null_check);
-
- // Generate code checking whether the the reference field at the
- // address `obj + field_offset`, held by object `obj`, needs to be
- // marked, and if so, marking it and updating the field within `obj`
- // with the marked value.
- //
- // This routine is used for the implementation of the
- // UnsafeCASObject intrinsic with Baker read barriers.
- //
- // This method has a structure similar to
- // GenerateReferenceLoadWithBakerReadBarrier, but note that argument
- // `ref` is only as a temporary here, and thus its value should not
- // be used afterwards.
- void UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- vixl::aarch32::Register obj,
- Location field_offset,
- Location temp,
- bool needs_null_check,
- vixl::aarch32::Register temp2);
-
- // Generate a heap reference load (with no read barrier).
- void GenerateRawReferenceLoad(HInstruction* instruction,
- Location ref,
- vixl::aarch32::Register obj,
- uint32_t offset,
- Location index,
- ScaleFactor scale_factor,
- bool needs_null_check);
// Emit code checking the status of the Marking Register, and
// aborting the program if MR does not match the value stored in the
@@ -779,10 +748,11 @@
// Encoding of thunk type and data for link-time generated thunks for Baker read barriers.
enum class BakerReadBarrierKind : uint8_t {
- kField, // Field get or array get with constant offset (i.e. constant index).
- kArray, // Array get with index in register.
- kGcRoot, // GC root load.
- kLast = kGcRoot
+ kField, // Field get or array get with constant offset (i.e. constant index).
+ kArray, // Array get with index in register.
+ kGcRoot, // GC root load.
+ kUnsafeCas, // UnsafeCASObject intrinsic.
+ kLast = kUnsafeCas
};
enum class BakerReadBarrierWidth : uint8_t {
@@ -849,6 +819,14 @@
BakerReadBarrierWidthField::Encode(width);
}
+ static uint32_t EncodeBakerReadBarrierUnsafeCasData(uint32_t root_reg) {
+ CheckValidReg(root_reg);
+ return BakerReadBarrierKindField::Encode(BakerReadBarrierKind::kUnsafeCas) |
+ BakerReadBarrierFirstRegField::Encode(root_reg) |
+ BakerReadBarrierSecondRegField::Encode(kBakerReadBarrierInvalidEncodedReg) |
+ BakerReadBarrierWidthField::Encode(BakerReadBarrierWidth::kWide);
+ }
+
void CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler,
uint32_t encoded_data,
/*out*/ std::string* debug_name);
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 0ed5756..aed334b 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1868,12 +1868,27 @@
if (value_can_be_null) {
__ Beqz(value, &done);
}
+ // Load the address of the card table into `card`.
__ LoadFromOffset(kLoadWord,
card,
TR,
Thread::CardTableOffset<kMipsPointerSize>().Int32Value());
+ // Calculate the address of the card corresponding to `object`.
__ Srl(temp, object, gc::accounting::CardTable::kCardShift);
__ Addu(temp, card, temp);
+ // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
+ // `object`'s card.
+ //
+ // Register `card` contains the address of the card table. Note that the card
+ // table's base is biased during its creation so that it always starts at an
+ // address whose least-significant byte is equal to `kCardDirty` (see
+ // art::gc::accounting::CardTable::Create). Therefore the SB instruction
+ // below writes the `kCardDirty` (byte) value into the `object`'s card
+ // (located at `card + object >> kCardShift`).
+ //
+ // This dual use of the value in register `card` (1. to calculate the location
+ // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
+ // (no need to explicitly load `kCardDirty` as an immediate value).
__ Sb(card, temp, 0);
if (value_can_be_null) {
__ Bind(&done);
@@ -7439,7 +7454,7 @@
// Given the numeric representation, it's enough to check the low bit of the
// rb_state. We do that by shifting the bit into the sign bit (31) and
// performing a branch on less than zero.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
+ static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
static_assert(LockWord::kReadBarrierStateSize == 1, "Expecting 1-bit read barrier state size");
__ Sll(temp_reg, temp_reg, 31 - LockWord::kReadBarrierStateShift);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 2b6928e..72318e9 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1490,12 +1490,27 @@
if (value_can_be_null) {
__ Beqzc(value, &done);
}
+ // Load the address of the card table into `card`.
__ LoadFromOffset(kLoadDoubleword,
card,
TR,
Thread::CardTableOffset<kMips64PointerSize>().Int32Value());
+ // Calculate the address of the card corresponding to `object`.
__ Dsrl(temp, object, gc::accounting::CardTable::kCardShift);
__ Daddu(temp, card, temp);
+ // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
+ // `object`'s card.
+ //
+ // Register `card` contains the address of the card table. Note that the card
+ // table's base is biased during its creation so that it always starts at an
+ // address whose least-significant byte is equal to `kCardDirty` (see
+ // art::gc::accounting::CardTable::Create). Therefore the SB instruction
+ // below writes the `kCardDirty` (byte) value into the `object`'s card
+ // (located at `card + object >> kCardShift`).
+ //
+ // This dual use of the value in register `card` (1. to calculate the location
+ // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
+ // (no need to explicitly load `kCardDirty` as an immediate value).
__ Sb(card, temp, 0);
if (value_can_be_null) {
__ Bind(&done);
@@ -5555,7 +5570,7 @@
// Given the numeric representation, it's enough to check the low bit of the
// rb_state. We do that by shifting the bit into the sign bit (31) and
// performing a branch on less than zero.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
+ static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
static_assert(LockWord::kReadBarrierStateSize == 1, "Expecting 1-bit read barrier state size");
__ Sll(temp_reg, temp_reg, 31 - LockWord::kReadBarrierStateShift);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index a835aed..df00ec7 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -5104,9 +5104,25 @@
__ testl(value, value);
__ j(kEqual, &is_null);
}
+ // Load the address of the card table into `card`.
__ fs()->movl(card, Address::Absolute(Thread::CardTableOffset<kX86PointerSize>().Int32Value()));
+ // Calculate the offset (in the card table) of the card corresponding to
+ // `object`.
__ movl(temp, object);
__ shrl(temp, Immediate(gc::accounting::CardTable::kCardShift));
+ // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
+ // `object`'s card.
+ //
+ // Register `card` contains the address of the card table. Note that the card
+ // table's base is biased during its creation so that it always starts at an
+ // address whose least-significant byte is equal to `kCardDirty` (see
+ // art::gc::accounting::CardTable::Create). Therefore the MOVB instruction
+ // below writes the `kCardDirty` (byte) value into the `object`'s card
+ // (located at `card + object >> kCardShift`).
+ //
+ // This dual use of the value in register `card` (1. to calculate the location
+ // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
+ // (no need to explicitly load `kCardDirty` as an immediate value).
__ movb(Address(temp, card, TIMES_1, 0),
X86ManagedRegister::FromCpuRegister(card).AsByteRegister());
if (value_can_be_null) {
@@ -7741,7 +7757,7 @@
uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
// Given the numeric representation, it's enough to check the low bit of the rb_state.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
+ static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
constexpr uint32_t gray_byte_position = LockWord::kReadBarrierStateShift / kBitsPerByte;
constexpr uint32_t gray_bit_position = LockWord::kReadBarrierStateShift % kBitsPerByte;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index dee891b..ae2a000 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -5436,10 +5436,26 @@
__ testl(value, value);
__ j(kEqual, &is_null);
}
+ // Load the address of the card table into `card`.
__ gs()->movq(card, Address::Absolute(Thread::CardTableOffset<kX86_64PointerSize>().Int32Value(),
/* no_rip */ true));
+ // Calculate the offset (in the card table) of the card corresponding to
+ // `object`.
__ movq(temp, object);
__ shrq(temp, Immediate(gc::accounting::CardTable::kCardShift));
+ // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
+ // `object`'s card.
+ //
+ // Register `card` contains the address of the card table. Note that the card
+ // table's base is biased during its creation so that it always starts at an
+ // address whose least-significant byte is equal to `kCardDirty` (see
+ // art::gc::accounting::CardTable::Create). Therefore the MOVB instruction
+ // below writes the `kCardDirty` (byte) value into the `object`'s card
+ // (located at `card + object >> kCardShift`).
+ //
+ // This dual use of the value in register `card` (1. to calculate the location
+ // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
+ // (no need to explicitly load `kCardDirty` as an immediate value).
__ movb(Address(temp, card, TIMES_1, 0), card);
if (value_can_be_null) {
__ Bind(&is_null);
@@ -7034,7 +7050,7 @@
uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
// Given the numeric representation, it's enough to check the low bit of the rb_state.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
+ static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
constexpr uint32_t gray_byte_position = LockWord::kReadBarrierStateShift / kBitsPerByte;
constexpr uint32_t gray_bit_position = LockWord::kReadBarrierStateShift % kBitsPerByte;
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 4b2bcc8..a657b58 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -745,15 +745,15 @@
if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
// UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
Register temp = WRegisterFrom(locations->GetTemp(0));
- codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
- trg_loc,
- base,
- /* offset */ 0u,
- /* index */ offset_loc,
- /* scale_factor */ 0u,
- temp,
- /* needs_null_check */ false,
- is_volatile);
+ MacroAssembler* masm = codegen->GetVIXLAssembler();
+ // Piggy-back on the field load path using introspection for the Baker read barrier.
+ __ Add(temp, base, offset.W()); // Offset should not exceed 32 bits.
+ codegen->GenerateFieldLoadWithBakerReadBarrier(invoke,
+ trg_loc,
+ base,
+ MemOperand(temp.X()),
+ /* needs_null_check */ false,
+ is_volatile);
} else {
// Other cases.
MemOperand mem_op(base.X(), offset);
@@ -782,9 +782,9 @@
kIntrinsified);
if (can_call && kUseBakerReadBarrier) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
- // We need a temporary register for the read barrier marking slow
- // path in CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier.
- locations->AddTemp(Location::RequiresRegister());
+ // We need a temporary register for the read barrier load in order to use
+ // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier().
+ locations->AddTemp(FixedTempLocation());
}
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
@@ -984,106 +984,155 @@
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall,
kIntrinsified);
+ if (can_call) {
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ }
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
locations->SetInAt(3, Location::RequiresRegister());
locations->SetInAt(4, Location::RequiresRegister());
- // If heap poisoning is enabled, we don't want the unpoisoning
- // operations to potentially clobber the output. Likewise when
- // emitting a (Baker) read barrier, which may call.
- Location::OutputOverlap overlaps =
- ((kPoisonHeapReferences && type == DataType::Type::kReference) || can_call)
- ? Location::kOutputOverlap
- : Location::kNoOutputOverlap;
- locations->SetOut(Location::RequiresRegister(), overlaps);
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
- // Temporary register for (Baker) read barrier.
+ // We need two non-scratch temporary registers for (Baker) read barrier.
+ locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
}
}
+class BakerReadBarrierCasSlowPathARM64 : public SlowPathCodeARM64 {
+ public:
+ explicit BakerReadBarrierCasSlowPathARM64(HInvoke* invoke)
+ : SlowPathCodeARM64(invoke) {}
+
+ const char* GetDescription() const OVERRIDE { return "BakerReadBarrierCasSlowPathARM64"; }
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
+ Arm64Assembler* assembler = arm64_codegen->GetAssembler();
+ MacroAssembler* masm = assembler->GetVIXLAssembler();
+ __ Bind(GetEntryLabel());
+
+ // Get the locations.
+ LocationSummary* locations = instruction_->GetLocations();
+ Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
+ Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
+ Register expected = WRegisterFrom(locations->InAt(3)); // Expected.
+ Register value = WRegisterFrom(locations->InAt(4)); // Value.
+
+ Register old_value = WRegisterFrom(locations->GetTemp(0)); // The old value from main path.
+ Register marked = WRegisterFrom(locations->GetTemp(1)); // The marked old value.
+
+ // Mark the `old_value` from the main path and compare with `expected`. This clobbers the
+ // `tmp_ptr` scratch register but we do not want to allocate another non-scratch temporary.
+ arm64_codegen->GenerateUnsafeCasOldValueMovWithBakerReadBarrier(marked, old_value);
+ __ Cmp(marked, expected);
+ __ B(GetExitLabel(), ne); // If taken, Z=false indicates failure.
+
+ // The `old_value` we have read did not match `expected` (which is always a to-space reference)
+ // but after the read barrier in GenerateUnsafeCasOldValueMovWithBakerReadBarrier() the marked
+ // to-space value matched, so the `old_value` must be a from-space reference to the same
+ // object. Do the same CAS loop as the main path but check for both `expected` and the unmarked
+ // old value representing the to-space and from-space references for the same object.
+
+ UseScratchRegisterScope temps(masm);
+ Register tmp_ptr = temps.AcquireX();
+ Register tmp = temps.AcquireSameSizeAs(value);
+
+ // Recalculate the `tmp_ptr` clobbered above.
+ __ Add(tmp_ptr, base.X(), Operand(offset));
+
+ // do {
+ // tmp_value = [tmp_ptr];
+ // } while ((tmp_value == expected || tmp == old_value) && failure([tmp_ptr] <- r_new_value));
+ // result = (tmp_value == expected || tmp == old_value);
+
+ vixl::aarch64::Label loop_head;
+ __ Bind(&loop_head);
+ __ Ldaxr(tmp, MemOperand(tmp_ptr));
+ assembler->MaybeUnpoisonHeapReference(tmp);
+ __ Cmp(tmp, expected);
+ __ Ccmp(tmp, old_value, ZFlag, ne);
+ __ B(GetExitLabel(), ne); // If taken, Z=false indicates failure.
+ assembler->MaybePoisonHeapReference(value);
+ __ Stlxr(tmp.W(), value, MemOperand(tmp_ptr));
+ assembler->MaybeUnpoisonHeapReference(value);
+ __ Cbnz(tmp.W(), &loop_head);
+
+ // Z=true from the above CMP+CCMP indicates success.
+ __ B(GetExitLabel());
+ }
+};
+
static void GenCas(HInvoke* invoke, DataType::Type type, CodeGeneratorARM64* codegen) {
- MacroAssembler* masm = codegen->GetVIXLAssembler();
+ Arm64Assembler* assembler = codegen->GetAssembler();
+ MacroAssembler* masm = assembler->GetVIXLAssembler();
LocationSummary* locations = invoke->GetLocations();
- Location out_loc = locations->Out();
- Register out = WRegisterFrom(out_loc); // Boolean result.
-
- Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
- Location offset_loc = locations->InAt(2);
- Register offset = XRegisterFrom(offset_loc); // Long offset.
- Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
- Register value = RegisterFrom(locations->InAt(4), type); // Value.
+ Register out = WRegisterFrom(locations->Out()); // Boolean result.
+ Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
+ Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
+ Register expected = RegisterFrom(locations->InAt(3), type); // Expected.
+ Register value = RegisterFrom(locations->InAt(4), type); // Value.
// This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
if (type == DataType::Type::kReference) {
// Mark card for object assuming new value is stored.
bool value_can_be_null = true; // TODO: Worth finding out this information?
codegen->MarkGCCard(base, value, value_can_be_null);
-
- // The only read barrier implementation supporting the
- // UnsafeCASObject intrinsic is the Baker-style read barriers.
- DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
-
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
- Register temp = WRegisterFrom(locations->GetTemp(0));
- // Need to make sure the reference stored in the field is a to-space
- // one before attempting the CAS or the CAS could fail incorrectly.
- codegen->UpdateReferenceFieldWithBakerReadBarrier(
- invoke,
- out_loc, // Unused, used only as a "temporary" within the read barrier.
- base,
- /* field_offset */ offset_loc,
- temp,
- /* needs_null_check */ false,
- /* use_load_acquire */ false);
- }
}
UseScratchRegisterScope temps(masm);
Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
- Register tmp_value = temps.AcquireSameSizeAs(value); // Value in memory.
+ Register old_value; // Value in memory.
- Register tmp_32 = tmp_value.W();
+ vixl::aarch64::Label exit_loop_label;
+ vixl::aarch64::Label* exit_loop = &exit_loop_label;
+ vixl::aarch64::Label* failure = &exit_loop_label;
+
+ if (kEmitCompilerReadBarrier && type == DataType::Type::kReference) {
+ // The only read barrier implementation supporting the
+ // UnsafeCASObject intrinsic is the Baker-style read barriers.
+ DCHECK(kUseBakerReadBarrier);
+
+ BakerReadBarrierCasSlowPathARM64* slow_path =
+ new (codegen->GetScopedAllocator()) BakerReadBarrierCasSlowPathARM64(invoke);
+ codegen->AddSlowPath(slow_path);
+ exit_loop = slow_path->GetExitLabel();
+ failure = slow_path->GetEntryLabel();
+ // We need to store the `old_value` in a non-scratch register to make sure
+ // the Baker read barrier in the slow path does not clobber it.
+ old_value = WRegisterFrom(locations->GetTemp(0));
+ } else {
+ old_value = temps.AcquireSameSizeAs(value);
+ }
__ Add(tmp_ptr, base.X(), Operand(offset));
- if (kPoisonHeapReferences && type == DataType::Type::kReference) {
- codegen->GetAssembler()->PoisonHeapReference(expected);
- if (value.Is(expected)) {
- // Do not poison `value`, as it is the same register as
- // `expected`, which has just been poisoned.
- } else {
- codegen->GetAssembler()->PoisonHeapReference(value);
- }
- }
-
// do {
- // tmp_value = [tmp_ptr] - expected;
- // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
- // result = tmp_value != 0;
+ // tmp_value = [tmp_ptr];
+ // } while (tmp_value == expected && failure([tmp_ptr] <- r_new_value));
+ // result = tmp_value == expected;
- vixl::aarch64::Label loop_head, exit_loop;
+ vixl::aarch64::Label loop_head;
__ Bind(&loop_head);
- __ Ldaxr(tmp_value, MemOperand(tmp_ptr));
- __ Cmp(tmp_value, expected);
- __ B(&exit_loop, ne);
- __ Stlxr(tmp_32, value, MemOperand(tmp_ptr));
- __ Cbnz(tmp_32, &loop_head);
- __ Bind(&exit_loop);
- __ Cset(out, eq);
-
- if (kPoisonHeapReferences && type == DataType::Type::kReference) {
- codegen->GetAssembler()->UnpoisonHeapReference(expected);
- if (value.Is(expected)) {
- // Do not unpoison `value`, as it is the same register as
- // `expected`, which has just been unpoisoned.
- } else {
- codegen->GetAssembler()->UnpoisonHeapReference(value);
- }
+ __ Ldaxr(old_value, MemOperand(tmp_ptr));
+ if (type == DataType::Type::kReference) {
+ assembler->MaybeUnpoisonHeapReference(old_value);
}
+ __ Cmp(old_value, expected);
+ __ B(failure, ne);
+ if (type == DataType::Type::kReference) {
+ assembler->MaybePoisonHeapReference(value);
+ }
+ __ Stlxr(old_value.W(), value, MemOperand(tmp_ptr)); // Reuse `old_value` for STLXR result.
+ if (type == DataType::Type::kReference) {
+ assembler->MaybeUnpoisonHeapReference(value);
+ }
+ __ Cbnz(old_value.W(), &loop_head);
+ __ Bind(exit_loop);
+ __ Cset(out, eq);
}
void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
@@ -2690,7 +2739,7 @@
codegen_->AddSlowPath(read_barrier_slow_path);
// Given the numeric representation, it's enough to check the low bit of the rb_state.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
+ static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
__ Tbnz(tmp, LockWord::kReadBarrierStateShift, read_barrier_slow_path->GetEntryLabel());
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 2963308..74a779d 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -638,8 +638,11 @@
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
Location temp = locations->GetTemp(0);
- codegen->GenerateReferenceLoadWithBakerReadBarrier(
- invoke, trg_loc, base, 0U, offset_loc, TIMES_1, temp, /* needs_null_check */ false);
+ // Piggy-back on the field load path using introspection for the Baker read barrier.
+ __ Add(RegisterFrom(temp), base, Operand(offset));
+ MemOperand src(RegisterFrom(temp), 0);
+ codegen->GenerateFieldLoadWithBakerReadBarrier(
+ invoke, trg_loc, base, src, /* needs_null_check */ false);
if (is_volatile) {
__ Dmb(vixl32::ISH);
}
@@ -933,9 +936,7 @@
codegen_);
}
-static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* allocator,
- HInvoke* invoke,
- DataType::Type type) {
+static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* allocator, HInvoke* invoke) {
bool can_call = kEmitCompilerReadBarrier &&
kUseBakerReadBarrier &&
(invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
@@ -945,20 +946,16 @@
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall,
kIntrinsified);
+ if (can_call) {
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ }
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
locations->SetInAt(3, Location::RequiresRegister());
locations->SetInAt(4, Location::RequiresRegister());
- // If heap poisoning is enabled, we don't want the unpoisoning
- // operations to potentially clobber the output. Likewise when
- // emitting a (Baker) read barrier, which may call.
- Location::OutputOverlap overlaps =
- ((kPoisonHeapReferences && type == DataType::Type::kReference) || can_call)
- ? Location::kOutputOverlap
- : Location::kNoOutputOverlap;
- locations->SetOut(Location::RequiresRegister(), overlaps);
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
// Temporary registers used in CAS. In the object case
// (UnsafeCASObject intrinsic), these are also used for
@@ -967,24 +964,92 @@
locations->AddTemp(Location::RequiresRegister()); // Temp 1.
}
+class BakerReadBarrierCasSlowPathARMVIXL : public SlowPathCodeARMVIXL {
+ public:
+ explicit BakerReadBarrierCasSlowPathARMVIXL(HInvoke* invoke)
+ : SlowPathCodeARMVIXL(invoke) {}
+
+ const char* GetDescription() const OVERRIDE { return "BakerReadBarrierCasSlowPathARMVIXL"; }
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
+ ArmVIXLAssembler* assembler = arm_codegen->GetAssembler();
+ __ Bind(GetEntryLabel());
+
+ LocationSummary* locations = instruction_->GetLocations();
+ vixl32::Register base = InputRegisterAt(instruction_, 1); // Object pointer.
+ vixl32::Register offset = LowRegisterFrom(locations->InAt(2)); // Offset (discard high 4B).
+ vixl32::Register expected = InputRegisterAt(instruction_, 3); // Expected.
+ vixl32::Register value = InputRegisterAt(instruction_, 4); // Value.
+
+ vixl32::Register tmp_ptr = RegisterFrom(locations->GetTemp(0)); // Pointer to actual memory.
+ vixl32::Register tmp = RegisterFrom(locations->GetTemp(1)); // Temporary.
+
+ // The `tmp` is initialized to `[tmp_ptr] - expected` in the main path. Reconstruct
+ // and mark the old value and compare with `expected`. We clobber `tmp_ptr` in the
+ // process due to lack of other temps suitable for the read barrier.
+ arm_codegen->GenerateUnsafeCasOldValueAddWithBakerReadBarrier(tmp_ptr, tmp, expected);
+ __ Cmp(tmp_ptr, expected);
+ __ B(ne, GetExitLabel());
+
+ // The old value we have read did not match `expected` (which is always a to-space reference)
+ // but after the read barrier in GenerateUnsafeCasOldValueAddWithBakerReadBarrier() the marked
+ // to-space value matched, so the old value must be a from-space reference to the same object.
+ // Do the same CAS loop as the main path but check for both `expected` and the unmarked
+ // old value representing the to-space and from-space references for the same object.
+
+ UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
+ vixl32::Register adjusted_old_value = temps.Acquire(); // For saved `tmp` from main path.
+
+ // Recalculate the `tmp_ptr` clobbered above and store the `adjusted_old_value`, i.e. IP.
+ __ Add(tmp_ptr, base, offset);
+ __ Mov(adjusted_old_value, tmp);
+
+ // do {
+ // tmp = [r_ptr] - expected;
+ // } while ((tmp == 0 || tmp == adjusted_old_value) && failure([r_ptr] <- r_new_value));
+ // result = (tmp == 0 || tmp == adjusted_old_value);
+
+ vixl32::Label loop_head;
+ __ Bind(&loop_head);
+ __ Ldrex(tmp, MemOperand(tmp_ptr)); // This can now load null stored by another thread.
+ assembler->MaybeUnpoisonHeapReference(tmp);
+ __ Subs(tmp, tmp, expected); // Use SUBS to get non-zero value if both compares fail.
+ {
+ // If the newly loaded value did not match `expected`, compare with `adjusted_old_value`.
+ ExactAssemblyScope aas(assembler->GetVIXLAssembler(), 2 * k16BitT32InstructionSizeInBytes);
+ __ it(ne);
+ __ cmp(ne, tmp, adjusted_old_value);
+ }
+ __ B(ne, GetExitLabel());
+ assembler->MaybePoisonHeapReference(value);
+ __ Strex(tmp, value, MemOperand(tmp_ptr));
+ assembler->MaybeUnpoisonHeapReference(value);
+ __ Cmp(tmp, 0);
+ __ B(ne, &loop_head, /* far_target */ false);
+ __ B(GetExitLabel());
+ }
+};
+
static void GenCas(HInvoke* invoke, DataType::Type type, CodeGeneratorARMVIXL* codegen) {
DCHECK_NE(type, DataType::Type::kInt64);
ArmVIXLAssembler* assembler = codegen->GetAssembler();
LocationSummary* locations = invoke->GetLocations();
- Location out_loc = locations->Out();
vixl32::Register out = OutputRegister(invoke); // Boolean result.
vixl32::Register base = InputRegisterAt(invoke, 1); // Object pointer.
- Location offset_loc = locations->InAt(2);
- vixl32::Register offset = LowRegisterFrom(offset_loc); // Offset (discard high 4B).
+ vixl32::Register offset = LowRegisterFrom(locations->InAt(2)); // Offset (discard high 4B).
vixl32::Register expected = InputRegisterAt(invoke, 3); // Expected.
vixl32::Register value = InputRegisterAt(invoke, 4); // Value.
- Location tmp_ptr_loc = locations->GetTemp(0);
- vixl32::Register tmp_ptr = RegisterFrom(tmp_ptr_loc); // Pointer to actual memory.
- vixl32::Register tmp = RegisterFrom(locations->GetTemp(1)); // Value in memory.
+ vixl32::Register tmp_ptr = RegisterFrom(locations->GetTemp(0)); // Pointer to actual memory.
+ vixl32::Register tmp = RegisterFrom(locations->GetTemp(1)); // Temporary.
+
+ vixl32::Label loop_exit_label;
+ vixl32::Label* loop_exit = &loop_exit_label;
+ vixl32::Label* failure = &loop_exit_label;
if (type == DataType::Type::kReference) {
// The only read barrier implementation supporting the
@@ -997,87 +1062,62 @@
codegen->MarkGCCard(tmp_ptr, tmp, base, value, value_can_be_null);
if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
- // Need to make sure the reference stored in the field is a to-space
- // one before attempting the CAS or the CAS could fail incorrectly.
- codegen->UpdateReferenceFieldWithBakerReadBarrier(
- invoke,
- out_loc, // Unused, used only as a "temporary" within the read barrier.
- base,
- /* field_offset */ offset_loc,
- tmp_ptr_loc,
- /* needs_null_check */ false,
- tmp);
+ // If marking, check if the stored reference is a from-space reference to the same
+ // object as the to-space reference `expected`. If so, perform a custom CAS loop.
+ BakerReadBarrierCasSlowPathARMVIXL* slow_path =
+ new (codegen->GetScopedAllocator()) BakerReadBarrierCasSlowPathARMVIXL(invoke);
+ codegen->AddSlowPath(slow_path);
+ failure = slow_path->GetEntryLabel();
+ loop_exit = slow_path->GetExitLabel();
}
}
// Prevent reordering with prior memory operations.
// Emit a DMB ISH instruction instead of an DMB ISHST one, as the
- // latter allows a preceding load to be delayed past the STXR
+ // latter allows a preceding load to be delayed past the STREX
// instruction below.
__ Dmb(vixl32::ISH);
__ Add(tmp_ptr, base, offset);
- if (kPoisonHeapReferences && type == DataType::Type::kReference) {
- codegen->GetAssembler()->PoisonHeapReference(expected);
- if (value.Is(expected)) {
- // Do not poison `value`, as it is the same register as
- // `expected`, which has just been poisoned.
- } else {
- codegen->GetAssembler()->PoisonHeapReference(value);
- }
- }
-
// do {
// tmp = [r_ptr] - expected;
// } while (tmp == 0 && failure([r_ptr] <- r_new_value));
- // result = tmp != 0;
+ // result = tmp == 0;
vixl32::Label loop_head;
__ Bind(&loop_head);
-
__ Ldrex(tmp, MemOperand(tmp_ptr));
-
- __ Subs(tmp, tmp, expected);
-
- {
- ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
- 3 * kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
-
- __ itt(eq);
- __ strex(eq, tmp, value, MemOperand(tmp_ptr));
- __ cmp(eq, tmp, 1);
+ if (type == DataType::Type::kReference) {
+ assembler->MaybeUnpoisonHeapReference(tmp);
}
+ __ Subs(tmp, tmp, expected);
+ __ B(ne, failure, (failure == loop_exit) ? kNear : kBranchWithoutHint);
+ if (type == DataType::Type::kReference) {
+ assembler->MaybePoisonHeapReference(value);
+ }
+ __ Strex(tmp, value, MemOperand(tmp_ptr));
+ if (type == DataType::Type::kReference) {
+ assembler->MaybeUnpoisonHeapReference(value);
+ }
+ __ Cmp(tmp, 0);
+ __ B(ne, &loop_head, /* far_target */ false);
- __ B(eq, &loop_head, /* far_target */ false);
+ __ Bind(loop_exit);
__ Dmb(vixl32::ISH);
- __ Rsbs(out, tmp, 1);
+ // out = tmp == 0.
+ __ Clz(out, tmp);
+ __ Lsr(out, out, WhichPowerOf2(out.GetSizeInBits()));
- {
- ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
- 2 * kMaxInstructionSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
-
- __ it(cc);
- __ mov(cc, out, 0);
- }
-
- if (kPoisonHeapReferences && type == DataType::Type::kReference) {
- codegen->GetAssembler()->UnpoisonHeapReference(expected);
- if (value.Is(expected)) {
- // Do not unpoison `value`, as it is the same register as
- // `expected`, which has just been unpoisoned.
- } else {
- codegen->GetAssembler()->UnpoisonHeapReference(value);
- }
+ if (type == DataType::Type::kReference) {
+ codegen->MaybeGenerateMarkingRegisterCheck(/* code */ 128);
}
}
void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeCASInt(HInvoke* invoke) {
- CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke, DataType::Type::kInt32);
+ CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke);
}
void IntrinsicLocationsBuilderARMVIXL::VisitUnsafeCASObject(HInvoke* invoke) {
// The only read barrier implementation supporting the
@@ -1086,7 +1126,7 @@
return;
}
- CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke, DataType::Type::kReference);
+ CreateIntIntIntIntIntToIntPlusTemps(allocator_, invoke);
}
void IntrinsicCodeGeneratorARMVIXL::VisitUnsafeCASInt(HInvoke* invoke) {
GenCas(invoke, DataType::Type::kInt32, codegen_);
@@ -2202,7 +2242,7 @@
// Given the numeric representation, it's enough to check the low bit of the
// rb_state. We do that by shifting the bit out of the lock word with LSRS
// which can be a 16-bit instruction unlike the TST immediate.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
+ static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
__ Lsrs(temp2, temp2, LockWord::kReadBarrierStateShift + 1);
// Carry flag is the last bit shifted out by LSRS.
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 98cea35..5c7be54 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2781,7 +2781,7 @@
__ j(kEqual, &done);
// Given the numeric representation, it's enough to check the low bit of the rb_state.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
+ static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
constexpr uint32_t gray_byte_position = LockWord::kReadBarrierStateShift / kBitsPerByte;
constexpr uint32_t gray_bit_position = LockWord::kReadBarrierStateShift % kBitsPerByte;
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index ac6eab0..b5afe93 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1143,7 +1143,7 @@
__ j(kEqual, &done);
// Given the numeric representation, it's enough to check the low bit of the rb_state.
- static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
+ static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
constexpr uint32_t gray_byte_position = LockWord::kReadBarrierStateShift / kBitsPerByte;
constexpr uint32_t gray_bit_position = LockWord::kReadBarrierStateShift % kBitsPerByte;
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 8f822cc..79a7e2c 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1301,6 +1301,15 @@
++it;
if (dominator->StrictlyDominates(user)) {
user->ReplaceInput(replacement, index);
+ } else if (user->IsPhi() && !user->AsPhi()->IsCatchPhi()) {
+ // If the input flows from a block dominated by `dominator`, we can replace it.
+ // We do not perform this for catch phis as we don't have control flow support
+ // for their inputs.
+ const ArenaVector<HBasicBlock*>& predecessors = user->GetBlock()->GetPredecessors();
+ HBasicBlock* predecessor = predecessors[index];
+ if (dominator->GetBlock()->Dominates(predecessor)) {
+ user->ReplaceInput(replacement, index);
+ }
}
}
}
diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc
index 1aa16f4..df897a4 100644
--- a/compiler/optimizing/scheduler.cc
+++ b/compiler/optimizing/scheduler.cc
@@ -280,6 +280,23 @@
return false;
}
+// Check if the specified instruction is a better candidate which more likely will
+// have other instructions depending on it.
+static bool IsBetterCandidateWithMoreLikelyDependencies(HInstruction* new_candidate,
+ HInstruction* old_candidate) {
+ if (!new_candidate->GetSideEffects().Includes(old_candidate->GetSideEffects())) {
+ // Weaker side effects.
+ return false;
+ }
+ if (old_candidate->GetSideEffects().Includes(new_candidate->GetSideEffects())) {
+ // Same side effects, check if `new_candidate` has stronger `CanThrow()`.
+ return new_candidate->CanThrow() && !old_candidate->CanThrow();
+ } else {
+ // Stronger side effects, check if `new_candidate` has at least as strong `CanThrow()`.
+ return new_candidate->CanThrow() || !old_candidate->CanThrow();
+ }
+}
+
void SchedulingGraph::AddDependencies(HInstruction* instruction, bool is_scheduling_barrier) {
SchedulingNode* instruction_node = GetNode(instruction);
@@ -331,6 +348,7 @@
// Side effect dependencies.
if (!instruction->GetSideEffects().DoesNothing() || instruction->CanThrow()) {
+ HInstruction* dep_chain_candidate = nullptr;
for (HInstruction* other = instruction->GetNext(); other != nullptr; other = other->GetNext()) {
SchedulingNode* other_node = GetNode(other);
if (other_node->IsSchedulingBarrier()) {
@@ -340,7 +358,18 @@
break;
}
if (HasSideEffectDependency(other, instruction)) {
- AddOtherDependency(other_node, instruction_node);
+ if (dep_chain_candidate != nullptr &&
+ HasSideEffectDependency(other, dep_chain_candidate)) {
+ // Skip an explicit dependency to reduce memory usage, rely on the transitive dependency.
+ } else {
+ AddOtherDependency(other_node, instruction_node);
+ }
+ // Check if `other` is a better candidate which more likely will have other instructions
+ // depending on it.
+ if (dep_chain_candidate == nullptr ||
+ IsBetterCandidateWithMoreLikelyDependencies(other, dep_chain_candidate)) {
+ dep_chain_candidate = other;
+ }
}
}
}
diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc
index fe23fb4..981fcc4 100644
--- a/compiler/optimizing/scheduler_test.cc
+++ b/compiler/optimizing/scheduler_test.cc
@@ -171,7 +171,9 @@
ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(array_set1, array_get1));
ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(array_set2, array_get2));
ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(array_get2, array_set1));
- ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(array_set2, array_set1));
+ // Unnecessary dependency is not stored, we rely on transitive dependencies.
+ // The array_set2 -> array_get2 -> array_set1 dependencies are tested above.
+ ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(array_set2, array_set1));
// Env dependency.
ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(div_check, mul));
@@ -308,7 +310,9 @@
loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i);
loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_j);
ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
- ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_i));
+ // Unnecessary dependency is not stored, we rely on transitive dependencies.
+ // The arr_set_j -> arr_set_sub0 -> arr_set_add0 -> arr_set_i dependencies are tested below.
+ ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_i));
// Test side effect dependency based on LSA analysis: array[i] and array[i+0]
loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i);
@@ -320,7 +324,10 @@
loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i);
loc2 = heap_location_collector.GetArrayHeapLocation(arr_set_sub0);
ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
- ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub0, arr_set_i));
+ // Unnecessary dependency is not stored, we rely on transitive dependencies.
+ ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub0, arr_set_i));
+ // Instead, we rely on arr_set_sub0 -> arr_set_add0 -> arr_set_i, the latter is tested above.
+ ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub0, arr_set_add0));
// Test side effect dependency based on LSA analysis: array[i] and array[i+1]
loc1 = heap_location_collector.GetArrayHeapLocation(arr_set_i);
@@ -335,11 +342,12 @@
ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub1, arr_set_add1));
// Test side effect dependency based on LSA analysis: array[j] and all others array accesses
- ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_i));
- ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_add0));
ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_sub0));
ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_add1));
ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_sub1));
+ // Unnecessary dependencies are not stored, we rely on transitive dependencies.
+ ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_i));
+ ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_add0));
// Test that ArraySet and FieldSet should not have side effect dependency
ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_i, set_field10));
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 3918b65..60ca61c 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -296,10 +296,10 @@
ScopedArenaVector<uint8_t> buffer(allocator_->Adapter(kArenaAllocStackMapStream));
BitMemoryWriter<ScopedArenaVector<uint8_t>> out(&buffer);
- EncodeVarintBits(out, packed_frame_size_);
- EncodeVarintBits(out, core_spill_mask_);
- EncodeVarintBits(out, fp_spill_mask_);
- EncodeVarintBits(out, num_dex_registers_);
+ out.WriteVarint(packed_frame_size_);
+ out.WriteVarint(core_spill_mask_);
+ out.WriteVarint(fp_spill_mask_);
+ out.WriteVarint(num_dex_registers_);
EncodeTable(out, stack_maps_);
EncodeTable(out, register_masks_);
EncodeTable(out, stack_masks_);
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index df11709..01c6bf9 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -109,7 +109,7 @@
BitTableBuilder<RegisterMask> register_masks_;
BitmapTableBuilder stack_masks_;
BitmapTableBuilder dex_register_masks_;
- BitTableBuilder<MaskInfo> dex_register_maps_;
+ BitTableBuilder<DexRegisterMapInfo> dex_register_maps_;
BitTableBuilder<DexRegisterInfo> dex_register_catalog_;
ScopedArenaVector<BitVector*> lazy_stack_masks_;
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index a281bb3..d28f09f 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -750,9 +750,9 @@
ScopedArenaVector<uint8_t> memory = stream.Encode();
std::vector<uint8_t> out;
- CodeInfo::DedupeMap dedupe_map;
- size_t deduped1 = CodeInfo::Dedupe(&out, memory.data(), &dedupe_map);
- size_t deduped2 = CodeInfo::Dedupe(&out, memory.data(), &dedupe_map);
+ CodeInfo::Deduper deduper(&out);
+ size_t deduped1 = deduper.Dedupe(memory.data());
+ size_t deduped2 = deduper.Dedupe(memory.data());
for (size_t deduped : { deduped1, deduped2 }) {
CodeInfo code_info(out.data() + deduped);
diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc
index 1f9ad42..dee83d1 100644
--- a/compiler/utils/swap_space.cc
+++ b/compiler/utils/swap_space.cc
@@ -141,6 +141,7 @@
it->size -= size;
} else {
// Changing in place would break the std::set<> ordering, we need to remove and insert.
+ // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert.
free_by_size_.erase(it);
free_by_size_.insert(new_value);
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 0b68620..29df067 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -669,9 +669,7 @@
for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files_) {
dex_file.release();
}
- for (std::unique_ptr<MemMap>& map : opened_dex_files_maps_) {
- map.release();
- }
+ new std::vector<MemMap>(std::move(opened_dex_files_maps_)); // Leak MemMaps.
for (std::unique_ptr<File>& vdex_file : vdex_files_) {
vdex_file.release();
}
@@ -1449,14 +1447,14 @@
LOG(INFO) << "No " << VdexFile::kVdexNameInDmFile << " file in DexMetadata archive. "
<< "Not doing fast verification.";
} else {
- std::unique_ptr<MemMap> input_file(zip_entry->MapDirectlyOrExtract(
+ MemMap input_file = zip_entry->MapDirectlyOrExtract(
VdexFile::kVdexNameInDmFile,
kDexMetadata,
- &error_msg));
- if (input_file == nullptr) {
+ &error_msg);
+ if (!input_file.IsValid()) {
LOG(WARNING) << "Could not open vdex file in DexMetadata archive: " << error_msg;
} else {
- input_vdex_file_ = std::make_unique<VdexFile>(input_file.release());
+ input_vdex_file_ = std::make_unique<VdexFile>(std::move(input_file));
}
}
}
@@ -1631,7 +1629,7 @@
for (size_t i = 0, size = oat_writers_.size(); i != size; ++i) {
rodata_.push_back(elf_writers_[i]->StartRoData());
// Unzip or copy dex files straight to the oat file.
- std::vector<std::unique_ptr<MemMap>> opened_dex_files_map;
+ std::vector<MemMap> opened_dex_files_map;
std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
// No need to verify the dex file when we have a vdex file, which means it was already
// verified.
@@ -1651,7 +1649,7 @@
if (opened_dex_files_map.empty()) {
DCHECK(opened_dex_files.empty());
} else {
- for (std::unique_ptr<MemMap>& map : opened_dex_files_map) {
+ for (MemMap& map : opened_dex_files_map) {
opened_dex_files_maps_.push_back(std::move(map));
}
for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files) {
@@ -1732,8 +1730,8 @@
}
// Ensure opened dex files are writable for dex-to-dex transformations.
- for (const std::unique_ptr<MemMap>& map : opened_dex_files_maps_) {
- if (!map->Protect(PROT_READ | PROT_WRITE)) {
+ for (MemMap& map : opened_dex_files_maps_) {
+ if (!map.Protect(PROT_READ | PROT_WRITE)) {
PLOG(ERROR) << "Failed to make .dex files writeable.";
return dex2oat::ReturnCode::kOther;
}
@@ -2002,9 +2000,9 @@
TimingLogger::ScopedTiming t("dex2oat Oat", timings_);
// Sync the data to the file, in case we did dex2dex transformations.
- for (const std::unique_ptr<MemMap>& map : opened_dex_files_maps_) {
- if (!map->Sync()) {
- PLOG(ERROR) << "Failed to Sync() dex2dex output. Map: " << map->GetName();
+ for (MemMap& map : opened_dex_files_maps_) {
+ if (!map.Sync()) {
+ PLOG(ERROR) << "Failed to Sync() dex2dex output. Map: " << map.GetName();
return false;
}
}
@@ -2737,16 +2735,13 @@
zip_filename, error_msg->c_str());
return nullptr;
}
- std::unique_ptr<MemMap> input_file(zip_entry->ExtractToMemMap(zip_filename,
- input_filename,
- error_msg));
- if (input_file.get() == nullptr) {
+ MemMap input_file = zip_entry->ExtractToMemMap(zip_filename, input_filename, error_msg);
+ if (!input_file.IsValid()) {
*error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", input_filename,
zip_filename, error_msg->c_str());
return nullptr;
}
- const std::string input_string(reinterpret_cast<char*>(input_file->Begin()),
- input_file->Size());
+ const std::string input_string(reinterpret_cast<char*>(input_file.Begin()), input_file.Size());
std::istringstream input_stream(input_string);
return ReadCommentedInputStream<T>(input_stream, process);
}
@@ -2873,7 +2868,7 @@
std::unique_ptr<linker::ImageWriter> image_writer_;
std::unique_ptr<CompilerDriver> driver_;
- std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_;
+ std::vector<MemMap> opened_dex_files_maps_;
std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
bool avoid_storing_invocation_;
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index fa8c778..440b3a4 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -252,7 +252,7 @@
}
std::vector<OutputStream*> rodata;
- std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps;
+ std::vector<MemMap> opened_dex_files_maps;
std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
// Now that we have finalized key_value_store_, start writing the oat file.
for (size_t i = 0, size = oat_writers.size(); i != size; ++i) {
@@ -265,7 +265,7 @@
dex_file->GetLocation().c_str(),
dex_file->GetLocationChecksum());
- std::vector<std::unique_ptr<MemMap>> cur_opened_dex_files_maps;
+ std::vector<MemMap> cur_opened_dex_files_maps;
std::vector<std::unique_ptr<const DexFile>> cur_opened_dex_files;
bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles(
out_helper.vdex_files[i].GetFile(),
@@ -279,7 +279,7 @@
ASSERT_TRUE(dex_files_ok);
if (!cur_opened_dex_files_maps.empty()) {
- for (std::unique_ptr<MemMap>& cur_map : cur_opened_dex_files_maps) {
+ for (MemMap& cur_map : cur_opened_dex_files_maps) {
opened_dex_files_maps.push_back(std::move(cur_map));
}
for (std::unique_ptr<const DexFile>& cur_dex_file : cur_opened_dex_files) {
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index e10f9b3..67ded32 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -303,8 +303,8 @@
}
// Image data size excludes the bitmap and the header.
- ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin());
- ArrayRef<const uint8_t> raw_image_data(image_info.image_->Begin() + sizeof(ImageHeader),
+ ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_info.image_.Begin());
+ ArrayRef<const uint8_t> raw_image_data(image_info.image_.Begin() + sizeof(ImageHeader),
image_header->GetImageSize() - sizeof(ImageHeader));
CHECK_EQ(image_header->storage_mode_, image_storage_mode_);
@@ -362,7 +362,7 @@
// We do not want to have a corrupted image with a valid header.
// The header is uncompressed since it contains whether the image is compressed or not.
image_header->data_size_ = image_data.size();
- if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_->Begin()),
+ if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_.Begin()),
sizeof(ImageHeader),
0)) {
PLOG(ERROR) << "Failed to write image file header " << image_filename;
@@ -730,14 +730,13 @@
image_info.CreateImageSections(unused_sections), kPageSize);
std::string error_msg;
- image_info.image_.reset(MemMap::MapAnonymous("image writer image",
- nullptr,
- length,
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- if (UNLIKELY(image_info.image_.get() == nullptr)) {
+ image_info.image_ = MemMap::MapAnonymous("image writer image",
+ /* addr */ nullptr,
+ length,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ if (UNLIKELY(!image_info.image_.IsValid())) {
LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg;
return false;
}
@@ -745,7 +744,7 @@
// Create the image bitmap, only needs to cover mirror object section which is up to image_end_.
CHECK_LE(image_info.image_end_, length);
image_info.image_bitmap_.reset(gc::accounting::ContinuousSpaceBitmap::Create(
- "image bitmap", image_info.image_->Begin(), RoundUp(image_info.image_end_, kPageSize)));
+ "image bitmap", image_info.image_.Begin(), RoundUp(image_info.image_end_, kPageSize)));
if (image_info.image_bitmap_.get() == nullptr) {
LOG(ERROR) << "Failed to allocate memory for image bitmap";
return false;
@@ -2025,7 +2024,7 @@
// Create the header, leave 0 for data size since we will fill this in as we are writing the
// image.
- ImageHeader* header = new (image_info.image_->Begin()) ImageHeader(
+ ImageHeader* header = new (image_info.image_.Begin()) ImageHeader(
PointerToLowMemUInt32(image_info.image_begin_),
image_end,
sections,
@@ -2163,8 +2162,8 @@
if (relocation.oat_index != oat_index) {
continue;
}
- auto* dest = image_info.image_->Begin() + relocation.offset;
- DCHECK_GE(dest, image_info.image_->Begin() + image_info.image_end_);
+ auto* dest = image_info.image_.Begin() + relocation.offset;
+ DCHECK_GE(dest, image_info.image_.Begin() + image_info.image_end_);
DCHECK(!IsInBootImage(pair.first));
switch (relocation.type) {
case NativeObjectRelocationType::kArtField: {
@@ -2219,7 +2218,7 @@
}
}
// Fixup the image method roots.
- auto* image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin());
+ auto* image_header = reinterpret_cast<ImageHeader*>(image_info.image_.Begin());
for (size_t i = 0; i < ImageHeader::kImageMethodsCount; ++i) {
ArtMethod* method = image_methods_[i];
CHECK(method != nullptr);
@@ -2235,7 +2234,7 @@
const ImageSection& intern_table_section = image_header->GetInternedStringsSection();
InternTable* const intern_table = image_info.intern_table_.get();
uint8_t* const intern_table_memory_ptr =
- image_info.image_->Begin() + intern_table_section.Offset();
+ image_info.image_.Begin() + intern_table_section.Offset();
const size_t intern_table_bytes = intern_table->WriteToMemory(intern_table_memory_ptr);
CHECK_EQ(intern_table_bytes, image_info.intern_table_bytes_);
// Fixup the pointers in the newly written intern table to contain image addresses.
@@ -2260,7 +2259,7 @@
if (image_info.class_table_bytes_ > 0u) {
const ImageSection& class_table_section = image_header->GetClassTableSection();
uint8_t* const class_table_memory_ptr =
- image_info.image_->Begin() + class_table_section.Offset();
+ image_info.image_.Begin() + class_table_section.Offset();
Thread* self = Thread::Current();
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
@@ -2342,14 +2341,14 @@
size_t offset = GetImageOffset(obj);
size_t oat_index = GetOatIndex(obj);
ImageInfo& image_info = GetImageInfo(oat_index);
- auto* dst = reinterpret_cast<Object*>(image_info.image_->Begin() + offset);
+ auto* dst = reinterpret_cast<Object*>(image_info.image_.Begin() + offset);
DCHECK_LT(offset, image_info.image_end_);
const auto* src = reinterpret_cast<const uint8_t*>(obj);
image_info.image_bitmap_->Set(dst); // Mark the obj as live.
const size_t n = obj->SizeOf();
- DCHECK_LE(offset + n, image_info.image_->Size());
+ DCHECK_LE(offset + n, image_info.image_.Size());
memcpy(dst, src, n);
// Write in a hash code of objects which have inflated monitors or a hash code in their monitor
@@ -2456,7 +2455,7 @@
T* ImageWriter::NativeCopyLocation(T* obj) {
const NativeObjectRelocation relocation = GetNativeRelocation(obj);
const ImageInfo& image_info = GetImageInfo(relocation.oat_index);
- return reinterpret_cast<T*>(image_info.image_->Begin() + relocation.offset);
+ return reinterpret_cast<T*>(image_info.image_.Begin() + relocation.offset);
}
class ImageWriter::NativeLocationVisitor {
@@ -3011,12 +3010,12 @@
}
// Calculate the offset within the image.
ImageInfo* image_info = &image_infos_[oat_index];
- DCHECK(image_info->image_->HasAddress(dest))
- << "MemMap range " << static_cast<const void*>(image_info->image_->Begin())
- << "-" << static_cast<const void*>(image_info->image_->End())
+ DCHECK(image_info->image_.HasAddress(dest))
+ << "MemMap range " << static_cast<const void*>(image_info->image_.Begin())
+ << "-" << static_cast<const void*>(image_info->image_.End())
<< " does not contain " << dest;
- size_t offset = reinterpret_cast<const uint8_t*>(dest) - image_info->image_->Begin();
- ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_info->image_->Begin());
+ size_t offset = reinterpret_cast<const uint8_t*>(dest) - image_info->image_.Begin();
+ ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_info->image_.Begin());
size_t image_end = image_header->GetClassTableSection().End();
DCHECK_LT(offset, image_end);
// Calculate the location index.
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index 9ab9c3e..7cf555b 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -307,7 +307,7 @@
// Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins.
size_t GetBinSizeSum(Bin up_to) const;
- std::unique_ptr<MemMap> image_; // Memory mapped for generating the image.
+ MemMap image_; // Memory mapped for generating the image.
// Target begin of this image. Notes: It is not valid to write here, this is the address
// of the target image, not necessarily where image_ is mapped. The address is only valid
@@ -408,7 +408,7 @@
size_t offset = GetImageOffset(object);
size_t oat_index = GetOatIndex(object);
const ImageInfo& image_info = GetImageInfo(oat_index);
- uint8_t* dst = image_info.image_->Begin() + offset;
+ uint8_t* dst = image_info.image_.Begin() + offset;
return reinterpret_cast<mirror::Object*>(dst);
}
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 484554e..9045c43 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -654,7 +654,7 @@
bool verify,
bool update_input_vdex,
CopyOption copy_dex_files,
- /*out*/ std::vector<std::unique_ptr<MemMap>>* opened_dex_files_map,
+ /*out*/ std::vector<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
CHECK(write_state_ == WriteState::kAddingDexFileSources);
@@ -663,7 +663,7 @@
return false;
}
- std::vector<std::unique_ptr<MemMap>> dex_files_map;
+ std::vector<MemMap> dex_files_map;
std::vector<std::unique_ptr<const DexFile>> dex_files;
// Initialize VDEX and OAT headers.
@@ -1438,10 +1438,10 @@
class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor {
public:
- static constexpr bool kDebugVerifyDedupedCodeInfo = false;
-
InitMapMethodVisitor(OatWriter* writer, size_t offset)
- : OatDexMethodVisitor(writer, offset) {}
+ : OatDexMethodVisitor(writer, offset),
+ dedupe_bit_table_(&writer_->code_info_data_) {
+ }
bool VisitMethod(size_t class_def_method_index,
const ClassAccessor::Method& method ATTRIBUTE_UNUSED)
@@ -1455,21 +1455,9 @@
ArrayRef<const uint8_t> map = compiled_method->GetVmapTable();
if (map.size() != 0u) {
- // Deduplicate the inner bittables within the CodeInfo.
- std::vector<uint8_t>* data = &writer_->code_info_data_;
size_t offset = dedupe_code_info_.GetOrCreate(map.data(), [=]() {
- size_t deduped_offset = CodeInfo::Dedupe(data, map.data(), &dedupe_bit_table_);
- if (kDebugVerifyDedupedCodeInfo) {
- InstructionSet isa = writer_->GetCompilerOptions().GetInstructionSet();
- std::stringstream old_code_info;
- VariableIndentationOutputStream old_vios(&old_code_info);
- std::stringstream new_code_info;
- VariableIndentationOutputStream new_vios(&new_code_info);
- CodeInfo(map.data()).Dump(&old_vios, 0, true, isa);
- CodeInfo(data->data() + deduped_offset).Dump(&new_vios, 0, true, isa);
- DCHECK_EQ(old_code_info.str(), new_code_info.str());
- }
- return offset_ + deduped_offset;
+ // Deduplicate the inner BitTable<>s within the CodeInfo.
+ return offset_ + dedupe_bit_table_.Dedupe(map.data());
});
// Code offset is not initialized yet, so set the map offset to 0u-offset.
DCHECK_EQ(oat_class->method_offsets_[method_offsets_index_].code_offset_, 0u);
@@ -1487,8 +1475,8 @@
// The compiler already deduplicated the pointers but it did not dedupe the tables.
SafeMap<const uint8_t*, size_t> dedupe_code_info_;
- // Deduplicate at BitTable level. The value is bit offset within code_info_data_.
- std::map<BitMemoryRegion, uint32_t, BitMemoryRegion::Less> dedupe_bit_table_;
+ // Deduplicate at BitTable level.
+ CodeInfo::Deduper dedupe_bit_table_;
};
class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
@@ -1908,7 +1896,7 @@
DCHECK(target != nullptr);
const void* oat_code_offset =
target->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_);
- if (oat_code_offset != 0) {
+ if (oat_code_offset != nullptr) {
DCHECK(!writer_->GetCompilerOptions().IsBootImage());
DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(oat_code_offset));
DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(oat_code_offset));
@@ -2082,6 +2070,7 @@
InitMapMethodVisitor visitor(this, offset);
bool success = VisitDexMethods(&visitor);
DCHECK(success);
+ code_info_data_.shrink_to_fit();
offset += code_info_data_.size();
}
return offset;
@@ -3435,12 +3424,12 @@
const ArtDexFileLoader dex_file_loader;
if (oat_dex_file->source_.IsZipEntry()) {
ZipEntry* zip_entry = oat_dex_file->source_.GetZipEntry();
- std::unique_ptr<MemMap> mem_map;
+ MemMap mem_map;
{
TimingLogger::ScopedTiming extract("Unzip", timings_);
- mem_map.reset(zip_entry->ExtractToMemMap(location.c_str(), "classes.dex", &error_msg));
+ mem_map = zip_entry->ExtractToMemMap(location.c_str(), "classes.dex", &error_msg);
}
- if (mem_map == nullptr) {
+ if (!mem_map.IsValid()) {
LOG(ERROR) << "Failed to extract dex file to mem map for layout: " << error_msg;
return false;
}
@@ -3695,7 +3684,7 @@
bool OatWriter::OpenDexFiles(
File* file,
bool verify,
- /*out*/ std::vector<std::unique_ptr<MemMap>>* opened_dex_files_map,
+ /*out*/ std::vector<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
TimingLogger::ScopedTiming split("OpenDexFiles", timings_);
@@ -3706,16 +3695,16 @@
if (!extract_dex_files_into_vdex_) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
- std::vector<std::unique_ptr<MemMap>> maps;
+ std::vector<MemMap> maps;
for (OatDexFile& oat_dex_file : oat_dex_files_) {
std::string error_msg;
- MemMap* map = oat_dex_file.source_.GetZipEntry()->MapDirectlyOrExtract(
- oat_dex_file.dex_file_location_data_, "zipped dex", &error_msg);
- if (map == nullptr) {
+ maps.emplace_back(oat_dex_file.source_.GetZipEntry()->MapDirectlyOrExtract(
+ oat_dex_file.dex_file_location_data_, "zipped dex", &error_msg));
+ MemMap* map = &maps.back();
+ if (!map->IsValid()) {
LOG(ERROR) << error_msg;
return false;
}
- maps.emplace_back(map);
// Now, open the dex file.
const ArtDexFileLoader dex_file_loader;
dex_files.emplace_back(dex_file_loader.Open(map->Begin(),
@@ -3746,7 +3735,7 @@
size_t length = vdex_size_ - map_offset;
std::string error_msg;
- std::unique_ptr<MemMap> dex_files_map(MemMap::MapFile(
+ MemMap dex_files_map = MemMap::MapFile(
length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
@@ -3754,8 +3743,8 @@
map_offset,
/* low_4gb */ false,
file->GetPath().c_str(),
- &error_msg));
- if (dex_files_map == nullptr) {
+ &error_msg);
+ if (!dex_files_map.IsValid()) {
LOG(ERROR) << "Failed to mmap() dex files from oat file. File: " << file->GetPath()
<< " error: " << error_msg;
return false;
@@ -3764,7 +3753,7 @@
std::vector<std::unique_ptr<const DexFile>> dex_files;
for (OatDexFile& oat_dex_file : oat_dex_files_) {
const uint8_t* raw_dex_file =
- dex_files_map->Begin() + oat_dex_file.dex_file_offset_ - map_offset;
+ dex_files_map.Begin() + oat_dex_file.dex_file_offset_ - map_offset;
if (kIsDebugBuild) {
// Sanity check our input files.
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index 9470f8c..5202d39 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -177,7 +177,7 @@
bool verify,
bool update_input_vdex,
CopyOption copy_dex_files,
- /*out*/ std::vector<std::unique_ptr<MemMap>>* opened_dex_files_map,
+ /*out*/ std::vector<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
// Initialize the writer with the given parameters.
void Initialize(const CompilerDriver* compiler_driver,
@@ -315,7 +315,7 @@
bool update_input_vdex);
bool OpenDexFiles(File* file,
bool verify,
- /*out*/ std::vector<std::unique_ptr<MemMap>>* opened_dex_files_map,
+ /*out*/ std::vector<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
size_t InitOatHeader(uint32_t num_dex_files, SafeMap<std::string, std::string>* key_value_store);
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index d73f10a..0264b09 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -169,7 +169,7 @@
oat_file);
elf_writer->Start();
OutputStream* oat_rodata = elf_writer->StartRoData();
- std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps;
+ std::vector<MemMap> opened_dex_files_maps;
std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
if (!oat_writer.WriteAndOpenDexFiles(
vdex_file,
@@ -246,7 +246,7 @@
return false;
}
- for (std::unique_ptr<MemMap>& map : opened_dex_files_maps) {
+ for (MemMap& map : opened_dex_files_maps) {
opened_dex_files_maps_.emplace_back(std::move(map));
}
for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files) {
@@ -261,7 +261,7 @@
std::unique_ptr<QuickCompilerCallbacks> callbacks_;
- std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_;
+ std::vector<MemMap> opened_dex_files_maps_;
std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
};
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 9f355ba..8f853ea 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -926,8 +926,6 @@
ClassData* GetClassData() { return class_data_; }
EncodedArrayItem* StaticValues() { return static_values_; }
- MethodItem* GenerateMethodItem(Header& header, ClassDataItemIterator& cdii);
-
void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
private:
diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h
index 7d8de39..5668b6c 100644
--- a/libartbase/base/bit_memory_region.h
+++ b/libartbase/base/bit_memory_region.h
@@ -29,47 +29,43 @@
class BitMemoryRegion FINAL : public ValueObject {
public:
struct Less {
- constexpr bool operator()(const BitMemoryRegion& lhs, const BitMemoryRegion& rhs) const {
- if (lhs.size_in_bits() != rhs.size_in_bits()) {
- return lhs.size_in_bits() < rhs.size_in_bits();
- }
- size_t bit = 0;
- constexpr size_t kNumBits = BitSizeOf<uint32_t>();
- for (; bit + kNumBits <= lhs.size_in_bits(); bit += kNumBits) {
- uint32_t lhs_bits = lhs.LoadBits(bit, kNumBits);
- uint32_t rhs_bits = rhs.LoadBits(bit, kNumBits);
- if (lhs_bits != rhs_bits) {
- return lhs_bits < rhs_bits;
- }
- }
- size_t num_bits = lhs.size_in_bits() - bit;
- return lhs.LoadBits(bit, num_bits) < rhs.LoadBits(bit, num_bits);
+ bool operator()(const BitMemoryRegion& lhs, const BitMemoryRegion& rhs) const {
+ return Compare(lhs, rhs) < 0;
}
};
BitMemoryRegion() = default;
- ALWAYS_INLINE BitMemoryRegion(void* data, size_t bit_start, size_t bit_size)
- : data_(reinterpret_cast<uintptr_t*>(AlignDown(data, sizeof(uintptr_t)))),
- bit_start_(bit_start + 8 * (reinterpret_cast<uintptr_t>(data) % sizeof(uintptr_t))),
- bit_size_(bit_size) {
+ ALWAYS_INLINE BitMemoryRegion(uint8_t* data, ssize_t bit_start, size_t bit_size) {
+ // Normalize the data pointer. Note that bit_start may be negative.
+ uint8_t* aligned_data = AlignDown(data + (bit_start >> kBitsPerByteLog2), sizeof(uintptr_t));
+ data_ = reinterpret_cast<uintptr_t*>(aligned_data);
+ bit_start_ = bit_start + kBitsPerByte * (data - aligned_data);
+ bit_size_ = bit_size;
+ DCHECK_LT(bit_start_, static_cast<size_t>(kBitsPerIntPtrT));
}
ALWAYS_INLINE explicit BitMemoryRegion(MemoryRegion region)
: BitMemoryRegion(region.begin(), /* bit_start */ 0, region.size_in_bits()) {
}
ALWAYS_INLINE BitMemoryRegion(MemoryRegion region, size_t bit_offset, size_t bit_length)
: BitMemoryRegion(region) {
- DCHECK_LE(bit_offset, bit_size_);
- DCHECK_LE(bit_length, bit_size_ - bit_offset);
- bit_start_ += bit_offset;
- bit_size_ = bit_length;
+ *this = Subregion(bit_offset, bit_length);
}
ALWAYS_INLINE bool IsValid() const { return data_ != nullptr; }
+ const uint8_t* data() const {
+ DCHECK_ALIGNED(bit_start_, kBitsPerByte);
+ return reinterpret_cast<const uint8_t*>(data_) + bit_start_ / kBitsPerByte;
+ }
+
size_t size_in_bits() const {
return bit_size_;
}
+ void Resize(size_t bit_size) {
+ bit_size_ = bit_size;
+ }
+
ALWAYS_INLINE BitMemoryRegion Subregion(size_t bit_offset, size_t bit_length) const {
DCHECK_LE(bit_offset, bit_size_);
DCHECK_LE(bit_length, bit_size_ - bit_offset);
@@ -79,12 +75,11 @@
return result;
}
- // Increase the size of the region and return the newly added range (starting at the old end).
- ALWAYS_INLINE BitMemoryRegion Extend(size_t bit_length) {
+ ALWAYS_INLINE BitMemoryRegion Subregion(size_t bit_offset) const {
+ DCHECK_LE(bit_offset, bit_size_);
BitMemoryRegion result = *this;
- result.bit_start_ += result.bit_size_;
- result.bit_size_ = bit_length;
- bit_size_ += bit_length;
+ result.bit_start_ += bit_offset;
+ result.bit_size_ -= bit_offset;
return result;
}
@@ -183,10 +178,26 @@
return count;
}
- ALWAYS_INLINE bool Equals(const BitMemoryRegion& other) const {
- return data_ == other.data_ &&
- bit_start_ == other.bit_start_ &&
- bit_size_ == other.bit_size_;
+ static int Compare(const BitMemoryRegion& lhs, const BitMemoryRegion& rhs) {
+ if (lhs.size_in_bits() != rhs.size_in_bits()) {
+ return (lhs.size_in_bits() < rhs.size_in_bits()) ? -1 : 1;
+ }
+ size_t bit = 0;
+ constexpr size_t kNumBits = BitSizeOf<uint32_t>();
+ for (; bit + kNumBits <= lhs.size_in_bits(); bit += kNumBits) {
+ uint32_t lhs_bits = lhs.LoadBits(bit, kNumBits);
+ uint32_t rhs_bits = rhs.LoadBits(bit, kNumBits);
+ if (lhs_bits != rhs_bits) {
+ return (lhs_bits < rhs_bits) ? -1 : 1;
+ }
+ }
+ size_t num_bits = lhs.size_in_bits() - bit;
+ uint32_t lhs_bits = lhs.LoadBits(bit, num_bits);
+ uint32_t rhs_bits = rhs.LoadBits(bit, num_bits);
+ if (lhs_bits != rhs_bits) {
+ return (lhs_bits < rhs_bits) ? -1 : 1;
+ }
+ return 0;
}
private:
@@ -196,30 +207,49 @@
size_t bit_size_ = 0;
};
+constexpr uint32_t kVarintHeaderBits = 4;
+constexpr uint32_t kVarintSmallValue = 11; // Maximum value which is stored as-is.
+
class BitMemoryReader {
public:
- explicit BitMemoryReader(const uint8_t* data, size_t bit_offset = 0)
- : finished_region_(const_cast<uint8_t*>(data), /* bit_start */ 0, bit_offset) {
- DCHECK_EQ(GetBitOffset(), bit_offset);
+ BitMemoryReader(BitMemoryReader&&) = default;
+ explicit BitMemoryReader(BitMemoryRegion data)
+ : finished_region_(data.Subregion(0, 0) /* set the length to zero */ ) {
+ }
+ explicit BitMemoryReader(const uint8_t* data, ssize_t bit_offset = 0)
+ : finished_region_(const_cast<uint8_t*>(data), bit_offset, /* bit_length */ 0) {
}
- size_t GetBitOffset() const { return finished_region_.size_in_bits(); }
+ const uint8_t* data() const { return finished_region_.data(); }
- ALWAYS_INLINE BitMemoryRegion Skip(size_t bit_length) {
- return finished_region_.Extend(bit_length);
- }
+ BitMemoryRegion GetReadRegion() const { return finished_region_; }
- // Get the most recently read bits.
- ALWAYS_INLINE BitMemoryRegion Tail(size_t bit_length) {
- return finished_region_.Subregion(finished_region_.size_in_bits() - bit_length, bit_length);
+ size_t NumberOfReadBits() const { return finished_region_.size_in_bits(); }
+
+ ALWAYS_INLINE BitMemoryRegion ReadRegion(size_t bit_length) {
+ size_t bit_offset = finished_region_.size_in_bits();
+ finished_region_.Resize(bit_offset + bit_length);
+ return finished_region_.Subregion(bit_offset, bit_length);
}
ALWAYS_INLINE uint32_t ReadBits(size_t bit_length) {
- return finished_region_.Extend(bit_length).LoadBits(0, bit_length);
+ return ReadRegion(bit_length).LoadBits(/* bit_offset */ 0, bit_length);
}
ALWAYS_INLINE bool ReadBit() {
- return finished_region_.Extend(1).LoadBit(0);
+ return ReadRegion(/* bit_length */ 1).LoadBit(/* bit_offset */ 0);
+ }
+
+ // Read variable-length bit-packed integer.
+ // The first four bits determine the variable length of the encoded integer:
+ // Values 0..11 represent the result as-is, with no further following bits.
+ // Values 12..15 mean the result is in the next 8/16/24/32-bits respectively.
+ ALWAYS_INLINE uint32_t ReadVarint() {
+ uint32_t x = ReadBits(kVarintHeaderBits);
+ if (x > kVarintSmallValue) {
+ x = ReadBits((x - kVarintSmallValue) * kBitsPerByte);
+ }
+ return x;
}
private:
@@ -234,36 +264,58 @@
class BitMemoryWriter {
public:
explicit BitMemoryWriter(Vector* out, size_t bit_offset = 0)
- : out_(out), bit_offset_(bit_offset) {
- DCHECK_EQ(GetBitOffset(), bit_offset);
+ : out_(out), bit_start_(bit_offset), bit_offset_(bit_offset) {
+ DCHECK_EQ(NumberOfWrittenBits(), 0u);
+ }
+
+ BitMemoryRegion GetWrittenRegion() const {
+ return BitMemoryRegion(out_->data(), bit_start_, bit_offset_ - bit_start_);
}
const uint8_t* data() const { return out_->data(); }
- size_t GetBitOffset() const { return bit_offset_; }
+ size_t NumberOfWrittenBits() const { return bit_offset_ - bit_start_; }
ALWAYS_INLINE BitMemoryRegion Allocate(size_t bit_length) {
out_->resize(BitsToBytesRoundUp(bit_offset_ + bit_length));
- BitMemoryRegion region(MemoryRegion(out_->data(), out_->size()), bit_offset_, bit_length);
+ BitMemoryRegion region(out_->data(), bit_offset_, bit_length);
DCHECK_LE(bit_length, std::numeric_limits<size_t>::max() - bit_offset_) << "Overflow";
bit_offset_ += bit_length;
return region;
}
+ ALWAYS_INLINE void WriteRegion(const BitMemoryRegion& region) {
+ Allocate(region.size_in_bits()).StoreBits(/* bit_offset */ 0, region, region.size_in_bits());
+ }
+
ALWAYS_INLINE void WriteBits(uint32_t value, size_t bit_length) {
- Allocate(bit_length).StoreBits(0, value, bit_length);
+ Allocate(bit_length).StoreBits(/* bit_offset */ 0, value, bit_length);
}
ALWAYS_INLINE void WriteBit(bool value) {
- Allocate(1).StoreBit(0, value);
+ Allocate(1).StoreBit(/* bit_offset */ 0, value);
}
- ALWAYS_INLINE void WriteRegion(const BitMemoryRegion& region) {
- Allocate(region.size_in_bits()).StoreBits(0, region, region.size_in_bits());
+ // Write variable-length bit-packed integer.
+ ALWAYS_INLINE void WriteVarint(uint32_t value) {
+ if (value <= kVarintSmallValue) {
+ WriteBits(value, kVarintHeaderBits);
+ } else {
+ uint32_t num_bits = RoundUp(MinimumBitsToStore(value), kBitsPerByte);
+ uint32_t header = kVarintSmallValue + num_bits / kBitsPerByte;
+ WriteBits(header, kVarintHeaderBits);
+ WriteBits(value, num_bits);
+ }
+ }
+
+ ALWAYS_INLINE void ByteAlign() {
+ size_t end = bit_start_ + bit_offset_;
+ bit_offset_ += RoundUp(end, kBitsPerByte) - end;
}
private:
Vector* out_;
+ size_t bit_start_;
size_t bit_offset_;
DISALLOW_COPY_AND_ASSIGN(BitMemoryWriter);
diff --git a/libartbase/base/bit_memory_region_test.cc b/libartbase/base/bit_memory_region_test.cc
index b754698..02623bf 100644
--- a/libartbase/base/bit_memory_region_test.cc
+++ b/libartbase/base/bit_memory_region_test.cc
@@ -33,6 +33,24 @@
}
}
+TEST(BitMemoryRegion, TestVarint) {
+ for (size_t start_bit_offset = 0; start_bit_offset <= 32; start_bit_offset++) {
+ uint32_t values[] = { 0, 1, 11, 12, 15, 16, 255, 256, 1u << 16, 1u << 24, ~1u, ~0u };
+ for (uint32_t value : values) {
+ std::vector<uint8_t> buffer;
+ BitMemoryWriter<std::vector<uint8_t>> writer(&buffer, start_bit_offset);
+ writer.WriteVarint(value);
+
+ BitMemoryReader reader(buffer.data(), start_bit_offset);
+ uint32_t result = reader.ReadVarint();
+ uint32_t upper_bound = RoundUp(MinimumBitsToStore(value), kBitsPerByte) + kVarintHeaderBits;
+ EXPECT_EQ(writer.NumberOfWrittenBits(), reader.NumberOfReadBits());
+ EXPECT_EQ(value, result);
+ EXPECT_GE(upper_bound, writer.NumberOfWrittenBits());
+ }
+ }
+}
+
TEST(BitMemoryRegion, TestBit) {
uint8_t data[sizeof(uint32_t) * 2];
for (size_t bit_offset = 0; bit_offset < 2 * sizeof(uint32_t) * kBitsPerByte; ++bit_offset) {
diff --git a/libartbase/base/bit_struct_detail.h b/libartbase/base/bit_struct_detail.h
index 68c2e44..60de1b6 100644
--- a/libartbase/base/bit_struct_detail.h
+++ b/libartbase/base/bit_struct_detail.h
@@ -85,7 +85,7 @@
static constexpr FalseT Test(...);
public:
- static constexpr bool value = decltype(Test<T>(0))::value;
+ static constexpr bool value = decltype(Test<T>(nullptr))::value;
};
// Infer the type of the member of &T::M.
diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h
index 1c7614b..54e8861 100644
--- a/libartbase/base/bit_table.h
+++ b/libartbase/base/bit_table.h
@@ -33,34 +33,6 @@
namespace art {
-constexpr uint32_t kVarintHeaderBits = 4;
-constexpr uint32_t kVarintSmallValue = 11; // Maximum value which is stored as-is.
-
-// Load variable-length bit-packed integer from `data` starting at `bit_offset`.
-// The first four bits determine the variable length of the encoded integer:
-// Values 0..11 represent the result as-is, with no further following bits.
-// Values 12..15 mean the result is in the next 8/16/24/32-bits respectively.
-ALWAYS_INLINE static inline uint32_t DecodeVarintBits(BitMemoryReader& reader) {
- uint32_t x = reader.ReadBits(kVarintHeaderBits);
- if (x > kVarintSmallValue) {
- x = reader.ReadBits((x - kVarintSmallValue) * kBitsPerByte);
- }
- return x;
-}
-
-// Store variable-length bit-packed integer from `data` starting at `bit_offset`.
-template<typename Vector>
-ALWAYS_INLINE static inline void EncodeVarintBits(BitMemoryWriter<Vector>& out, uint32_t value) {
- if (value <= kVarintSmallValue) {
- out.WriteBits(value, kVarintHeaderBits);
- } else {
- uint32_t num_bits = RoundUp(MinimumBitsToStore(value), kBitsPerByte);
- uint32_t header = kVarintSmallValue + num_bits / kBitsPerByte;
- out.WriteBits(header, kVarintHeaderBits);
- out.WriteBits(value, num_bits);
- }
-}
-
// Generic purpose table of uint32_t values, which are tightly packed at bit level.
// It has its own header with the number of rows and the bit-widths of all columns.
// The values are accessible by (row, column). The value -1 is stored efficiently.
@@ -77,23 +49,21 @@
ALWAYS_INLINE void Decode(BitMemoryReader& reader) {
// Decode row count and column sizes from the table header.
- size_t initial_bit_offset = reader.GetBitOffset();
- num_rows_ = DecodeVarintBits(reader);
+ num_rows_ = reader.ReadVarint();
if (num_rows_ != 0) {
column_offset_[0] = 0;
for (uint32_t i = 0; i < kNumColumns; i++) {
- size_t column_end = column_offset_[i] + DecodeVarintBits(reader);
+ size_t column_end = column_offset_[i] + reader.ReadVarint();
column_offset_[i + 1] = dchecked_integral_cast<uint16_t>(column_end);
}
}
- header_bit_size_ = reader.GetBitOffset() - initial_bit_offset;
// Record the region which contains the table data and skip past it.
- table_data_ = reader.Skip(num_rows_ * NumRowBits());
+ table_data_ = reader.ReadRegion(num_rows_ * NumRowBits());
}
ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column = 0) const {
- DCHECK_NE(header_bit_size_, 0u) << "Table has not been loaded";
+ DCHECK(table_data_.IsValid()) << "Table has not been loaded";
DCHECK_LT(row, num_rows_);
DCHECK_LT(column, kNumColumns);
size_t offset = row * NumRowBits() + column_offset_[column];
@@ -101,7 +71,7 @@
}
ALWAYS_INLINE BitMemoryRegion GetBitMemoryRegion(uint32_t row, uint32_t column = 0) const {
- DCHECK_NE(header_bit_size_, 0u) << "Table has not been loaded";
+ DCHECK(table_data_.IsValid()) << "Table has not been loaded";
DCHECK_LT(row, num_rows_);
DCHECK_LT(column, kNumColumns);
size_t offset = row * NumRowBits() + column_offset_[column];
@@ -118,16 +88,20 @@
return column_offset_[column + 1] - column_offset_[column];
}
- size_t HeaderBitSize() const { return header_bit_size_; }
+ size_t DataBitSize() const { return table_data_.size_in_bits(); }
- size_t BitSize() const { return header_bit_size_ + table_data_.size_in_bits(); }
+ bool Equals(const BitTableBase& other) const {
+ return num_rows_ == other.num_rows_ &&
+ std::equal(column_offset_, column_offset_ + kNumColumns, other.column_offset_) &&
+ BitMemoryRegion::Compare(table_data_, other.table_data_) == 0;
+ }
protected:
BitMemoryRegion table_data_;
size_t num_rows_ = 0;
-
uint16_t column_offset_[kNumColumns + 1] = {};
- uint16_t header_bit_size_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(BitTableBase);
};
// Helper class which can be used to create BitTable accessors with named getters.
@@ -151,9 +125,10 @@
}
// Helper macro to create constructors and per-table utilities in derived class.
-#define BIT_TABLE_HEADER() \
+#define BIT_TABLE_HEADER(NAME) \
using BitTableAccessor<kNumColumns>::BitTableAccessor; /* inherit constructors */ \
template<int COLUMN, int UNUSED /*needed to compile*/> struct ColumnName; \
+ static constexpr const char* kTableName = #NAME; \
// Helper macro to create named column accessors in derived class.
#define BIT_TABLE_COLUMN(COLUMN, NAME) \
@@ -176,12 +151,6 @@
return names;
}
-// Returns the names of all columns in the given accessor.
-template<typename Accessor>
-static const char* const* GetBitTableColumnNames() {
- return GetBitTableColumnNamesImpl<Accessor>(std::make_index_sequence<Accessor::kNumColumns>());
-}
-
// Wrapper which makes it easier to use named accessors for the individual rows.
template<typename Accessor>
class BitTable : public BitTableBase<Accessor::kNumColumns> {
@@ -239,6 +208,14 @@
ALWAYS_INLINE Accessor GetInvalidRow() const {
return Accessor(this, static_cast<uint32_t>(-1));
}
+
+ const char* GetName() const {
+ return Accessor::kTableName;
+ }
+
+ const char* const* GetColumnNames() const {
+ return GetBitTableColumnNamesImpl<Accessor>(std::make_index_sequence<Accessor::kNumColumns>());
+ }
};
template<typename Accessor>
@@ -376,15 +353,15 @@
// Encode the stored data into a BitTable.
template<typename Vector>
void Encode(BitMemoryWriter<Vector>& out) const {
- size_t initial_bit_offset = out.GetBitOffset();
+ size_t initial_bit_offset = out.NumberOfWrittenBits();
std::array<uint32_t, kNumColumns> column_bits;
Measure(&column_bits);
- EncodeVarintBits(out, size());
+ out.WriteVarint(size());
if (size() != 0) {
// Write table header.
for (uint32_t c = 0; c < kNumColumns; c++) {
- EncodeVarintBits(out, column_bits[c]);
+ out.WriteVarint(column_bits[c]);
}
// Write table data.
@@ -398,7 +375,7 @@
// Verify the written data.
if (kIsDebugBuild) {
BitTableBase<kNumColumns> table;
- BitMemoryReader reader(out.data(), initial_bit_offset);
+ BitMemoryReader reader(out.GetWrittenRegion().Subregion(initial_bit_offset));
table.Decode(reader);
DCHECK_EQ(size(), table.NumRows());
for (uint32_t c = 0; c < kNumColumns; c++) {
@@ -467,11 +444,11 @@
// Encode the stored data into a BitTable.
template<typename Vector>
void Encode(BitMemoryWriter<Vector>& out) const {
- size_t initial_bit_offset = out.GetBitOffset();
+ size_t initial_bit_offset = out.NumberOfWrittenBits();
- EncodeVarintBits(out, size());
+ out.WriteVarint(size());
if (size() != 0) {
- EncodeVarintBits(out, max_num_bits_);
+ out.WriteVarint(max_num_bits_);
// Write table data.
for (MemoryRegion row : rows_) {
@@ -484,7 +461,7 @@
// Verify the written data.
if (kIsDebugBuild) {
BitTableBase<1> table;
- BitMemoryReader reader(out.data(), initial_bit_offset);
+ BitMemoryReader reader(out.GetWrittenRegion().Subregion(initial_bit_offset));
table.Decode(reader);
DCHECK_EQ(size(), table.NumRows());
DCHECK_EQ(max_num_bits_, table.NumColumnBits(0));
diff --git a/libartbase/base/bit_table_test.cc b/libartbase/base/bit_table_test.cc
index 2fd9052..bf32dc6 100644
--- a/libartbase/base/bit_table_test.cc
+++ b/libartbase/base/bit_table_test.cc
@@ -26,22 +26,6 @@
namespace art {
-TEST(BitTableTest, TestVarint) {
- for (size_t start_bit_offset = 0; start_bit_offset <= 32; start_bit_offset++) {
- uint32_t values[] = { 0, 1, 11, 12, 15, 16, 255, 256, ~1u, ~0u };
- for (uint32_t value : values) {
- std::vector<uint8_t> buffer;
- BitMemoryWriter<std::vector<uint8_t>> writer(&buffer, start_bit_offset);
- EncodeVarintBits(writer, value);
-
- BitMemoryReader reader(buffer.data(), start_bit_offset);
- uint32_t result = DecodeVarintBits(reader);
- EXPECT_EQ(writer.GetBitOffset(), reader.GetBitOffset());
- EXPECT_EQ(value, result);
- }
- }
-}
-
TEST(BitTableTest, TestEmptyTable) {
MallocArenaPool pool;
ArenaStack arena_stack(&pool);
@@ -54,7 +38,7 @@
BitMemoryReader reader(buffer.data());
BitTableBase<1> table(reader);
- EXPECT_EQ(writer.GetBitOffset(), reader.GetBitOffset());
+ EXPECT_EQ(writer.NumberOfWrittenBits(), reader.NumberOfReadBits());
EXPECT_EQ(0u, table.NumRows());
}
@@ -75,7 +59,7 @@
BitMemoryReader reader(buffer.data());
BitTableBase<1> table(reader);
- EXPECT_EQ(writer.GetBitOffset(), reader.GetBitOffset());
+ EXPECT_EQ(writer.NumberOfWrittenBits(), reader.NumberOfReadBits());
EXPECT_EQ(4u, table.NumRows());
EXPECT_EQ(42u, table.Get(0));
EXPECT_EQ(kNoValue, table.Get(1));
@@ -98,7 +82,7 @@
BitMemoryReader reader(buffer.data(), start_bit_offset);
BitTableBase<1> table(reader);
- EXPECT_EQ(writer.GetBitOffset(), reader.GetBitOffset());
+ EXPECT_EQ(writer.NumberOfWrittenBits(), reader.NumberOfReadBits());
EXPECT_EQ(1u, table.NumRows());
EXPECT_EQ(42u, table.Get(0));
}
@@ -119,7 +103,7 @@
BitMemoryReader reader(buffer.data());
BitTableBase<4> table(reader);
- EXPECT_EQ(writer.GetBitOffset(), reader.GetBitOffset());
+ EXPECT_EQ(writer.NumberOfWrittenBits(), reader.NumberOfReadBits());
EXPECT_EQ(2u, table.NumRows());
EXPECT_EQ(42u, table.Get(0, 0));
EXPECT_EQ(kNoValue, table.Get(0, 1));
@@ -169,7 +153,7 @@
BitMemoryReader reader(buffer.data());
BitTableBase<1> table(reader);
- EXPECT_EQ(writer.GetBitOffset(), reader.GetBitOffset());
+ EXPECT_EQ(writer.NumberOfWrittenBits(), reader.NumberOfReadBits());
for (auto it : indicies) {
uint64_t expected = it.first;
BitMemoryRegion actual = table.GetBitMemoryRegion(it.second);
diff --git a/libartbase/base/globals.h b/libartbase/base/globals.h
index cd0bf8f..bc79ff2 100644
--- a/libartbase/base/globals.h
+++ b/libartbase/base/globals.h
@@ -122,6 +122,14 @@
static constexpr bool kMarkCompactSupport = false && kMovingCollector;
// True if we allow moving classes.
static constexpr bool kMovingClasses = !kMarkCompactSupport;
+// If true, enable generational collection when using the Concurrent Copying
+// collector, i.e. use sticky-bit CC for minor collections and (full) CC for
+// major collections.
+#ifdef ART_USE_GENERATIONAL_CC
+static constexpr bool kEnableGenerationalConcurrentCopyingCollection = true;
+#else
+static constexpr bool kEnableGenerationalConcurrentCopyingCollection = false;
+#endif
// If true, enable the tlab allocator by default.
#ifdef ART_USE_TLAB
diff --git a/libartbase/base/hash_set.h b/libartbase/base/hash_set.h
index 2b1a5eb..42aa46f 100644
--- a/libartbase/base/hash_set.h
+++ b/libartbase/base/hash_set.h
@@ -359,6 +359,9 @@
// and set the empty slot to be the location we just moved from.
// Relies on maintaining the invariant that there's no empty slots from the 'ideal' index of an
// element to its actual location/index.
+ // Note that since erase shuffles back elements, it may result in the same element being visited
+ // twice during HashSet iteration. This happens when an element already visited during iteration
+ // gets shuffled to the end of the bucket array.
iterator erase(iterator it) {
// empty_index is the index that will become empty.
size_t empty_index = it.index_;
diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc
index 5cea869..d9760bd 100644
--- a/libartbase/base/mem_map.cc
+++ b/libartbase/base/mem_map.cc
@@ -61,6 +61,21 @@
// All the non-empty MemMaps. Use a multimap as we do a reserve-and-divide (eg ElfMap::Load()).
static Maps* gMaps GUARDED_BY(MemMap::GetMemMapsLock()) = nullptr;
+// Retrieve iterator to a `gMaps` entry that is known to exist.
+Maps::iterator GetGMapsEntry(const MemMap& map) REQUIRES(MemMap::GetMemMapsLock()) {
+ DCHECK(map.IsValid());
+ DCHECK(gMaps != nullptr);
+ for (auto it = gMaps->lower_bound(map.BaseBegin()), end = gMaps->end();
+ it != end && it->first == map.BaseBegin();
+ ++it) {
+ if (it->second == &map) {
+ return it;
+ }
+ }
+ LOG(FATAL) << "MemMap not found";
+ UNREACHABLE();
+}
+
std::ostream& operator<<(std::ostream& os, const Maps& mem_maps) {
os << "MemMap:" << std::endl;
for (auto it = mem_maps.begin(); it != mem_maps.end(); ++it) {
@@ -231,20 +246,21 @@
}
#endif
-MemMap* MemMap::MapAnonymous(const char* name,
- uint8_t* expected_ptr,
- size_t byte_count,
- int prot,
- bool low_4gb,
- bool reuse,
- std::string* error_msg,
- bool use_ashmem) {
+MemMap MemMap::MapAnonymous(const char* name,
+ uint8_t* addr,
+ size_t byte_count,
+ int prot,
+ bool low_4gb,
+ bool reuse,
+ std::string* error_msg,
+ bool use_ashmem) {
#ifndef __LP64__
UNUSED(low_4gb);
#endif
use_ashmem = use_ashmem && !kIsTargetLinux && !kIsTargetFuchsia;
if (byte_count == 0) {
- return new MemMap(name, nullptr, 0, nullptr, 0, prot, false);
+ *error_msg = "Empty MemMap requested.";
+ return Invalid();
}
size_t page_aligned_byte_count = RoundUp(byte_count, kPageSize);
@@ -252,9 +268,9 @@
if (reuse) {
// reuse means it is okay that it overlaps an existing page mapping.
// Only use this if you actually made the page reservation yourself.
- CHECK(expected_ptr != nullptr);
+ CHECK(addr != nullptr);
- DCHECK(ContainedWithinExistingMap(expected_ptr, byte_count, error_msg)) << *error_msg;
+ DCHECK(ContainedWithinExistingMap(addr, byte_count, error_msg)) << *error_msg;
flags |= MAP_FIXED;
}
@@ -296,7 +312,7 @@
// We need to store and potentially set an error number for pretty printing of errors
int saved_errno = 0;
- void* actual = MapInternal(expected_ptr,
+ void* actual = MapInternal(addr,
page_aligned_byte_count,
prot,
flags,
@@ -313,28 +329,33 @@
*error_msg = StringPrintf("Failed anonymous mmap(%p, %zd, 0x%x, 0x%x, %d, 0): %s. "
"See process maps in the log.",
- expected_ptr,
+ addr,
page_aligned_byte_count,
prot,
flags,
fd.get(),
strerror(saved_errno));
}
- return nullptr;
+ return Invalid();
}
- if (!CheckMapRequest(expected_ptr, actual, page_aligned_byte_count, error_msg)) {
- return nullptr;
+ if (!CheckMapRequest(addr, actual, page_aligned_byte_count, error_msg)) {
+ return Invalid();
}
- return new MemMap(name, reinterpret_cast<uint8_t*>(actual), byte_count, actual,
- page_aligned_byte_count, prot, reuse);
+ return MemMap(name,
+ reinterpret_cast<uint8_t*>(actual),
+ byte_count,
+ actual,
+ page_aligned_byte_count,
+ prot,
+ reuse);
}
-MemMap* MemMap::MapDummy(const char* name, uint8_t* addr, size_t byte_count) {
+MemMap MemMap::MapDummy(const char* name, uint8_t* addr, size_t byte_count) {
if (byte_count == 0) {
- return new MemMap(name, nullptr, 0, nullptr, 0, 0, false);
+ return Invalid();
}
const size_t page_aligned_byte_count = RoundUp(byte_count, kPageSize);
- return new MemMap(name, addr, byte_count, addr, page_aligned_byte_count, 0, true /* reuse */);
+ return MemMap(name, addr, byte_count, addr, page_aligned_byte_count, 0, true /* reuse */);
}
template<typename A, typename B>
@@ -342,19 +363,18 @@
return static_cast<ptrdiff_t>(reinterpret_cast<intptr_t>(a) - reinterpret_cast<intptr_t>(b));
}
-bool MemMap::ReplaceWith(MemMap** source_ptr, /*out*/std::string* error) {
+bool MemMap::ReplaceWith(MemMap* source, /*out*/std::string* error) {
#if !HAVE_MREMAP_SYSCALL
- UNUSED(source_ptr);
+ UNUSED(source);
*error = "Cannot perform atomic replace because we are missing the required mremap syscall";
return false;
#else // !HAVE_MREMAP_SYSCALL
- CHECK(source_ptr != nullptr);
- CHECK(*source_ptr != nullptr);
+ CHECK(source != nullptr);
+ CHECK(source->IsValid());
if (!MemMap::kCanReplaceMapping) {
*error = "Unable to perform atomic replace due to runtime environment!";
return false;
}
- MemMap* source = *source_ptr;
// neither can be reuse.
if (source->reuse_ || reuse_) {
*error = "One or both mappings is not a real mmap!";
@@ -406,12 +426,9 @@
// them later.
size_t new_base_size = std::max(source->base_size_, base_size_);
- // Delete the old source, don't unmap it though (set reuse) since it is already gone.
- *source_ptr = nullptr;
+ // Invalidate *source, don't unmap it though since it is already gone.
size_t source_size = source->size_;
- source->already_unmapped_ = true;
- delete source;
- source = nullptr;
+ source->Invalidate();
size_ = source_size;
base_size_ = new_base_size;
@@ -422,16 +439,16 @@
#endif // !HAVE_MREMAP_SYSCALL
}
-MemMap* MemMap::MapFileAtAddress(uint8_t* expected_ptr,
- size_t byte_count,
- int prot,
- int flags,
- int fd,
- off_t start,
- bool low_4gb,
- bool reuse,
- const char* filename,
- std::string* error_msg) {
+MemMap MemMap::MapFileAtAddress(uint8_t* expected_ptr,
+ size_t byte_count,
+ int prot,
+ int flags,
+ int fd,
+ off_t start,
+ bool low_4gb,
+ bool reuse,
+ const char* filename,
+ std::string* error_msg) {
CHECK_NE(0, prot);
CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE));
@@ -452,7 +469,7 @@
}
if (byte_count == 0) {
- return new MemMap(filename, nullptr, 0, nullptr, 0, prot, false);
+ return Invalid();
}
// Adjust 'offset' to be page-aligned as required by mmap.
int page_offset = start % kPageSize;
@@ -491,10 +508,10 @@
static_cast<int64_t>(page_aligned_offset), filename,
strerror(saved_errno));
}
- return nullptr;
+ return Invalid();
}
if (!CheckMapRequest(expected_ptr, actual, page_aligned_byte_count, error_msg)) {
- return nullptr;
+ return Invalid();
}
if (redzone_size != 0) {
const uint8_t *real_start = actual + page_offset;
@@ -506,14 +523,27 @@
page_aligned_byte_count -= redzone_size;
}
- return new MemMap(filename, actual + page_offset, byte_count, actual, page_aligned_byte_count,
- prot, reuse, redzone_size);
+ return MemMap(filename,
+ actual + page_offset,
+ byte_count,
+ actual,
+ page_aligned_byte_count,
+ prot,
+ reuse,
+ redzone_size);
+}
+
+MemMap::MemMap(MemMap&& other)
+ : MemMap() {
+ swap(other);
}
MemMap::~MemMap() {
- if (base_begin_ == nullptr && base_size_ == 0) {
- return;
- }
+ Reset();
+}
+
+void MemMap::DoReset() {
+ DCHECK(IsValid());
// Unlike Valgrind, AddressSanitizer requires that all manually poisoned memory is unpoisoned
// before it is returned to the system.
@@ -533,19 +563,56 @@
}
}
+ Invalidate();
+}
+
+void MemMap::Invalidate() {
+ DCHECK(IsValid());
+
// Remove it from gMaps.
std::lock_guard<std::mutex> mu(*mem_maps_lock_);
- bool found = false;
- DCHECK(gMaps != nullptr);
- for (auto it = gMaps->lower_bound(base_begin_), end = gMaps->end();
- it != end && it->first == base_begin_; ++it) {
- if (it->second == this) {
- found = true;
- gMaps->erase(it);
- break;
+ auto it = GetGMapsEntry(*this);
+ gMaps->erase(it);
+
+ // Mark it as invalid.
+ base_size_ = 0u;
+ DCHECK(!IsValid());
+}
+
+void MemMap::swap(MemMap& other) {
+ if (IsValid() || other.IsValid()) {
+ std::lock_guard<std::mutex> mu(*mem_maps_lock_);
+ DCHECK(gMaps != nullptr);
+ auto this_it = IsValid() ? GetGMapsEntry(*this) : gMaps->end();
+ auto other_it = other.IsValid() ? GetGMapsEntry(other) : gMaps->end();
+ if (IsValid()) {
+ DCHECK(this_it != gMaps->end());
+ DCHECK_EQ(this_it->second, this);
+ this_it->second = &other;
}
+ if (other.IsValid()) {
+ DCHECK(other_it != gMaps->end());
+ DCHECK_EQ(other_it->second, &other);
+ other_it->second = this;
+ }
+ // Swap members with the `mem_maps_lock_` held so that `base_begin_` matches
+ // with the `gMaps` key when other threads try to use `gMaps`.
+ SwapMembers(other);
+ } else {
+ SwapMembers(other);
}
- CHECK(found) << "MemMap not found";
+}
+
+void MemMap::SwapMembers(MemMap& other) {
+ name_.swap(other.name_);
+ std::swap(begin_, other.begin_);
+ std::swap(size_, other.size_);
+ std::swap(base_begin_, other.base_begin_);
+ std::swap(base_size_, other.base_size_);
+ std::swap(prot_, other.prot_);
+ std::swap(reuse_, other.reuse_);
+ std::swap(already_unmapped_, other.already_unmapped_);
+ std::swap(redzone_size_, other.redzone_size_);
}
MemMap::MemMap(const std::string& name, uint8_t* begin, size_t size, void* base_begin,
@@ -568,8 +635,11 @@
}
}
-MemMap* MemMap::RemapAtEnd(uint8_t* new_end, const char* tail_name, int tail_prot,
- std::string* error_msg, bool use_ashmem) {
+MemMap MemMap::RemapAtEnd(uint8_t* new_end,
+ const char* tail_name,
+ int tail_prot,
+ std::string* error_msg,
+ bool use_ashmem) {
use_ashmem = use_ashmem && !kIsTargetLinux && !kIsTargetFuchsia;
DCHECK_GE(new_end, Begin());
DCHECK_LE(new_end, End());
@@ -583,11 +653,11 @@
uint8_t* new_base_end = new_end;
DCHECK_LE(new_base_end, old_base_end);
if (new_base_end == old_base_end) {
- return new MemMap(tail_name, nullptr, 0, nullptr, 0, tail_prot, false);
+ return Invalid();
}
- size_ = new_end - reinterpret_cast<uint8_t*>(begin_);
- base_size_ = new_base_end - reinterpret_cast<uint8_t*>(base_begin_);
- DCHECK_LE(begin_ + size_, reinterpret_cast<uint8_t*>(base_begin_) + base_size_);
+ size_t new_size = new_end - reinterpret_cast<uint8_t*>(begin_);
+ size_t new_base_size = new_base_end - reinterpret_cast<uint8_t*>(base_begin_);
+ DCHECK_LE(begin_ + new_size, reinterpret_cast<uint8_t*>(base_begin_) + new_base_size);
size_t tail_size = old_end - new_end;
uint8_t* tail_base_begin = new_base_end;
size_t tail_base_size = old_base_end - new_base_end;
@@ -595,7 +665,7 @@
DCHECK_ALIGNED(tail_base_size, kPageSize);
unique_fd fd;
- int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+ int flags = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS;
if (use_ashmem) {
// android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are
// prefixed "dalvik-".
@@ -606,23 +676,14 @@
if (fd.get() == -1) {
*error_msg = StringPrintf("ashmem_create_region failed for '%s': %s",
tail_name, strerror(errno));
- return nullptr;
+ return Invalid();
}
}
MEMORY_TOOL_MAKE_UNDEFINED(tail_base_begin, tail_base_size);
- // Unmap/map the tail region.
- int result = TargetMUnmap(tail_base_begin, tail_base_size);
- if (result == -1) {
- PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
- *error_msg = StringPrintf("munmap(%p, %zd) failed for '%s'. See process maps in the log.",
- tail_base_begin, tail_base_size, name_.c_str());
- return nullptr;
- }
- // Don't cause memory allocation between the munmap and the mmap
- // calls. Otherwise, libc (or something else) might take this memory
- // region. Note this isn't perfect as there's no way to prevent
- // other threads to try to take this memory region here.
+ // Note: Do not explicitly unmap the tail region, mmap() with MAP_FIXED automatically
+ // removes old mappings for the overlapping region. This makes the operation atomic
+ // and prevents other threads from racing to allocate memory in the requested region.
uint8_t* actual = reinterpret_cast<uint8_t*>(TargetMMap(tail_base_begin,
tail_base_size,
tail_prot,
@@ -634,9 +695,18 @@
*error_msg = StringPrintf("anonymous mmap(%p, %zd, 0x%x, 0x%x, %d, 0) failed. See process "
"maps in the log.", tail_base_begin, tail_base_size, tail_prot, flags,
fd.get());
- return nullptr;
+ return Invalid();
}
- return new MemMap(tail_name, actual, tail_size, actual, tail_base_size, tail_prot, false);
+ // Update *this.
+ if (new_base_size == 0u) {
+ std::lock_guard<std::mutex> mu(*mem_maps_lock_);
+ auto it = GetGMapsEntry(*this);
+ gMaps->erase(it);
+ }
+ size_ = new_size;
+ base_size_ = new_base_size;
+ // Return the new mapping.
+ return MemMap(tail_name, actual, tail_size, actual, tail_base_size, tail_prot, false);
}
void MemMap::MadviseDontNeedAndZero() {
@@ -675,15 +745,15 @@
return false;
}
-bool MemMap::CheckNoGaps(MemMap* begin_map, MemMap* end_map) {
+bool MemMap::CheckNoGaps(MemMap& begin_map, MemMap& end_map) {
std::lock_guard<std::mutex> mu(*mem_maps_lock_);
- CHECK(begin_map != nullptr);
- CHECK(end_map != nullptr);
+ CHECK(begin_map.IsValid());
+ CHECK(end_map.IsValid());
CHECK(HasMemMap(begin_map));
CHECK(HasMemMap(end_map));
- CHECK_LE(begin_map->BaseBegin(), end_map->BaseBegin());
- MemMap* map = begin_map;
- while (map->BaseBegin() != end_map->BaseBegin()) {
+ CHECK_LE(begin_map.BaseBegin(), end_map.BaseBegin());
+ MemMap* map = &begin_map;
+ while (map->BaseBegin() != end_map.BaseBegin()) {
MemMap* next_map = GetLargestMemMapAt(map->BaseEnd());
if (next_map == nullptr) {
// Found a gap.
@@ -758,11 +828,11 @@
}
}
-bool MemMap::HasMemMap(MemMap* map) {
- void* base_begin = map->BaseBegin();
+bool MemMap::HasMemMap(MemMap& map) {
+ void* base_begin = map.BaseBegin();
for (auto it = gMaps->lower_bound(base_begin), end = gMaps->end();
it != end && it->first == base_begin; ++it) {
- if (it->second == map) {
+ if (it->second == &map) {
return true;
}
}
@@ -1049,6 +1119,7 @@
CHECK_EQ(size_, base_size_) << "Unsupported";
CHECK_GT(size, static_cast<size_t>(kPageSize));
CHECK_ALIGNED(size, kPageSize);
+ CHECK(!reuse_);
if (IsAlignedParam(reinterpret_cast<uintptr_t>(base_begin_), size) &&
IsAlignedParam(base_size_, size)) {
// Already aligned.
@@ -1079,17 +1150,17 @@
<< " aligned_base_end=" << reinterpret_cast<void*>(aligned_base_end);
}
std::lock_guard<std::mutex> mu(*mem_maps_lock_);
+ if (base_begin < aligned_base_begin) {
+ auto it = GetGMapsEntry(*this);
+ // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert.
+ gMaps->erase(it);
+ gMaps->insert(std::make_pair(aligned_base_begin, this));
+ }
base_begin_ = aligned_base_begin;
base_size_ = aligned_base_size;
begin_ = aligned_base_begin;
size_ = aligned_base_size;
DCHECK(gMaps != nullptr);
- if (base_begin < aligned_base_begin) {
- auto it = gMaps->find(base_begin);
- CHECK(it != gMaps->end()) << "MemMap not found";
- gMaps->erase(it);
- gMaps->insert(std::make_pair(base_begin_, this));
- }
}
} // namespace art
diff --git a/libartbase/base/mem_map.h b/libartbase/base/mem_map.h
index 1979357..cd7d502 100644
--- a/libartbase/base/mem_map.h
+++ b/libartbase/base/mem_map.h
@@ -60,6 +60,37 @@
public:
static constexpr bool kCanReplaceMapping = HAVE_MREMAP_SYSCALL;
+ // Creates an invalid mapping.
+ MemMap() {}
+
+ // Creates an invalid mapping. Used when we want to be more explicit than MemMap().
+ static MemMap Invalid() {
+ return MemMap();
+ }
+
+ MemMap(MemMap&& other) REQUIRES(!MemMap::mem_maps_lock_);
+ MemMap& operator=(MemMap&& other) REQUIRES(!MemMap::mem_maps_lock_) {
+ Reset();
+ swap(other);
+ return *this;
+ }
+
+ // Releases the memory mapping.
+ ~MemMap() REQUIRES(!MemMap::mem_maps_lock_);
+
+ // Swap two MemMaps.
+ void swap(MemMap& other);
+
+ void Reset() {
+ if (IsValid()) {
+ DoReset();
+ }
+ }
+
+ bool IsValid() const {
+ return base_size_ != 0u;
+ }
+
// Replace the data in this memmmap with the data in the memmap pointed to by source. The caller
// relinquishes ownership of the source mmap.
//
@@ -74,15 +105,14 @@
// * mremap must succeed when called on the mappings.
//
// If this call succeeds it will return true and:
- // * Deallocate *source
- // * Sets *source to nullptr
+ // * Invalidate *source
// * The protection of this will remain the same.
// * The size of this will be the size of the source
// * The data in this will be the data from source.
//
// If this call fails it will return false and make no changes to *source or this. The ownership
// of the source mmap is returned to the caller.
- bool ReplaceWith(/*in-out*/MemMap** source, /*out*/std::string* error);
+ bool ReplaceWith(/*in-out*/MemMap* source, /*out*/std::string* error);
// Request an anonymous region of length 'byte_count' and a requested base address.
// Use null as the requested base address if you don't care.
@@ -92,34 +122,42 @@
// 'name' will be used -- on systems that support it -- to give the mapping
// a name.
//
- // On success, returns returns a MemMap instance. On failure, returns null.
- static MemMap* MapAnonymous(const char* name,
- uint8_t* addr,
- size_t byte_count,
- int prot,
- bool low_4gb,
- bool reuse,
- std::string* error_msg,
- bool use_ashmem = true);
+ // On success, returns returns a valid MemMap. On failure, returns an invalid MemMap.
+ static MemMap MapAnonymous(const char* name,
+ uint8_t* addr,
+ size_t byte_count,
+ int prot,
+ bool low_4gb,
+ bool reuse,
+ std::string* error_msg,
+ bool use_ashmem = true);
+ static MemMap MapAnonymous(const char* name,
+ uint8_t* addr,
+ size_t byte_count,
+ int prot,
+ bool low_4gb,
+ std::string* error_msg) {
+ return MapAnonymous(name, addr, byte_count, prot, low_4gb, /* reuse */ false, error_msg);
+ }
// Create placeholder for a region allocated by direct call to mmap.
// This is useful when we do not have control over the code calling mmap,
// but when we still want to keep track of it in the list.
// The region is not considered to be owned and will not be unmmaped.
- static MemMap* MapDummy(const char* name, uint8_t* addr, size_t byte_count);
+ static MemMap MapDummy(const char* name, uint8_t* addr, size_t byte_count);
// Map part of a file, taking care of non-page aligned offsets. The
// "start" offset is absolute, not relative.
//
- // On success, returns returns a MemMap instance. On failure, returns null.
- static MemMap* MapFile(size_t byte_count,
- int prot,
- int flags,
- int fd,
- off_t start,
- bool low_4gb,
- const char* filename,
- std::string* error_msg) {
+ // On success, returns returns a valid MemMap. On failure, returns an invalid MemMap.
+ static MemMap MapFile(size_t byte_count,
+ int prot,
+ int flags,
+ int fd,
+ off_t start,
+ bool low_4gb,
+ const char* filename,
+ std::string* error_msg) {
return MapFileAtAddress(nullptr,
byte_count,
prot,
@@ -139,20 +177,17 @@
// MapFileAtAddress fails. This helps improve performance of the fail case since reading and
// printing /proc/maps takes several milliseconds in the worst case.
//
- // On success, returns returns a MemMap instance. On failure, returns null.
- static MemMap* MapFileAtAddress(uint8_t* addr,
- size_t byte_count,
- int prot,
- int flags,
- int fd,
- off_t start,
- bool low_4gb,
- bool reuse,
- const char* filename,
- std::string* error_msg);
-
- // Releases the memory mapping.
- ~MemMap() REQUIRES(!MemMap::mem_maps_lock_);
+ // On success, returns returns a valid MemMap. On failure, returns an invalid MemMap.
+ static MemMap MapFileAtAddress(uint8_t* addr,
+ size_t byte_count,
+ int prot,
+ int flags,
+ int fd,
+ off_t start,
+ bool low_4gb,
+ bool reuse,
+ const char* filename,
+ std::string* error_msg);
const std::string& GetName() const {
return name_;
@@ -200,13 +235,13 @@
}
// Unmap the pages at end and remap them to create another memory map.
- MemMap* RemapAtEnd(uint8_t* new_end,
- const char* tail_name,
- int tail_prot,
- std::string* error_msg,
- bool use_ashmem = true);
+ MemMap RemapAtEnd(uint8_t* new_end,
+ const char* tail_name,
+ int tail_prot,
+ std::string* error_msg,
+ bool use_ashmem = true);
- static bool CheckNoGaps(MemMap* begin_map, MemMap* end_map)
+ static bool CheckNoGaps(MemMap& begin_map, MemMap& end_map)
REQUIRES(!MemMap::mem_maps_lock_);
static void DumpMaps(std::ostream& os, bool terse = false)
REQUIRES(!MemMap::mem_maps_lock_);
@@ -240,9 +275,13 @@
bool reuse,
size_t redzone_size = 0) REQUIRES(!MemMap::mem_maps_lock_);
+ void DoReset();
+ void Invalidate();
+ void SwapMembers(MemMap& other);
+
static void DumpMapsLocked(std::ostream& os, bool terse)
REQUIRES(MemMap::mem_maps_lock_);
- static bool HasMemMap(MemMap* map)
+ static bool HasMemMap(MemMap& map)
REQUIRES(MemMap::mem_maps_lock_);
static MemMap* GetLargestMemMapAt(void* address)
REQUIRES(MemMap::mem_maps_lock_);
@@ -271,23 +310,23 @@
size_t byte_count,
std::string* error_msg);
- const std::string name_;
- uint8_t* begin_; // Start of data. May be changed by AlignBy.
- size_t size_; // Length of data.
+ std::string name_;
+ uint8_t* begin_ = nullptr; // Start of data. May be changed by AlignBy.
+ size_t size_ = 0u; // Length of data.
- void* base_begin_; // Page-aligned base address. May be changed by AlignBy.
- size_t base_size_; // Length of mapping. May be changed by RemapAtEnd (ie Zygote).
- int prot_; // Protection of the map.
+ void* base_begin_ = nullptr; // Page-aligned base address. May be changed by AlignBy.
+ size_t base_size_ = 0u; // Length of mapping. May be changed by RemapAtEnd (ie Zygote).
+ int prot_ = 0; // Protection of the map.
// When reuse_ is true, this is just a view of an existing mapping
// and we do not take ownership and are not responsible for
// unmapping.
- const bool reuse_;
+ bool reuse_ = false;
// When already_unmapped_ is true the destructor will not call munmap.
- bool already_unmapped_;
+ bool already_unmapped_ = false;
- const size_t redzone_size_;
+ size_t redzone_size_ = 0u;
#if USE_ART_LOW_4G_ALLOCATOR
static uintptr_t next_mem_pos_; // Next memory location to check for low_4g extent.
@@ -309,6 +348,10 @@
friend class MemMapTest; // To allow access to base_begin_ and base_size_.
};
+inline void swap(MemMap& lhs, MemMap& rhs) {
+ lhs.swap(rhs);
+}
+
std::ostream& operator<<(std::ostream& os, const MemMap& mem_map);
// Zero and release pages if possible, no requirements on alignments.
diff --git a/libartbase/base/mem_map_fuchsia.cc b/libartbase/base/mem_map_fuchsia.cc
index db31efb..d1c92ce 100644
--- a/libartbase/base/mem_map_fuchsia.cc
+++ b/libartbase/base/mem_map_fuchsia.cc
@@ -41,8 +41,8 @@
ZX_INFO_VMAR,
&vmarinfo,
sizeof(vmarinfo),
- NULL,
- NULL), ZX_OK) << "could not find info from root vmar";
+ nullptr,
+ nullptr), ZX_OK) << "could not find info from root vmar";
uintptr_t lower_mem_start = FUCHSIA_LOWER_MEM_START - vmarinfo.base;
fuchsia_lowmem_size = FUCHSIA_LOWER_MEM_SIZE;
@@ -97,8 +97,8 @@
ZX_INFO_VMAR,
&vmarinfo,
sizeof(vmarinfo),
- NULL,
- NULL);
+ nullptr,
+ nullptr);
if (status < 0 || reinterpret_cast<uintptr_t>(start) < vmarinfo.base) {
errno = EINVAL;
return MAP_FAILED;
diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc
index c575c7a..396f12b 100644
--- a/libartbase/base/mem_map_test.cc
+++ b/libartbase/base/mem_map_test.cc
@@ -30,14 +30,6 @@
class MemMapTest : public CommonArtTest {
public:
- static uint8_t* BaseBegin(MemMap* mem_map) {
- return reinterpret_cast<uint8_t*>(mem_map->base_begin_);
- }
-
- static size_t BaseSize(MemMap* mem_map) {
- return mem_map->base_size_;
- }
-
static bool IsAddressMapped(void* addr) {
bool res = msync(addr, 1, MS_SYNC) == 0;
if (!res && errno != ENOMEM) {
@@ -60,15 +52,14 @@
static uint8_t* GetValidMapAddress(size_t size, bool low_4gb) {
// Find a valid map address and unmap it before returning.
std::string error_msg;
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous("temp",
- nullptr,
- size,
- PROT_READ,
- low_4gb,
- false,
- &error_msg));
- CHECK(map != nullptr);
- return map->Begin();
+ MemMap map = MemMap::MapAnonymous("temp",
+ /* addr */ nullptr,
+ size,
+ PROT_READ,
+ low_4gb,
+ &error_msg);
+ CHECK(map.IsValid());
+ return map.Begin();
}
static void RemapAtEndTest(bool low_4gb) {
@@ -76,37 +67,37 @@
// Cast the page size to size_t.
const size_t page_size = static_cast<size_t>(kPageSize);
// Map a two-page memory region.
- MemMap* m0 = MemMap::MapAnonymous("MemMapTest_RemapAtEndTest_map0",
- nullptr,
- 2 * page_size,
- PROT_READ | PROT_WRITE,
- low_4gb,
- false,
- &error_msg);
+ MemMap m0 = MemMap::MapAnonymous("MemMapTest_RemapAtEndTest_map0",
+ /* addr */ nullptr,
+ 2 * page_size,
+ PROT_READ | PROT_WRITE,
+ low_4gb,
+ &error_msg);
// Check its state and write to it.
- uint8_t* base0 = m0->Begin();
+ ASSERT_TRUE(m0.IsValid());
+ uint8_t* base0 = m0.Begin();
ASSERT_TRUE(base0 != nullptr) << error_msg;
- size_t size0 = m0->Size();
- EXPECT_EQ(m0->Size(), 2 * page_size);
- EXPECT_EQ(BaseBegin(m0), base0);
- EXPECT_EQ(BaseSize(m0), size0);
+ size_t size0 = m0.Size();
+ EXPECT_EQ(m0.Size(), 2 * page_size);
+ EXPECT_EQ(m0.BaseBegin(), base0);
+ EXPECT_EQ(m0.BaseSize(), size0);
memset(base0, 42, 2 * page_size);
// Remap the latter half into a second MemMap.
- MemMap* m1 = m0->RemapAtEnd(base0 + page_size,
- "MemMapTest_RemapAtEndTest_map1",
- PROT_READ | PROT_WRITE,
- &error_msg);
+ MemMap m1 = m0.RemapAtEnd(base0 + page_size,
+ "MemMapTest_RemapAtEndTest_map1",
+ PROT_READ | PROT_WRITE,
+ &error_msg);
// Check the states of the two maps.
- EXPECT_EQ(m0->Begin(), base0) << error_msg;
- EXPECT_EQ(m0->Size(), page_size);
- EXPECT_EQ(BaseBegin(m0), base0);
- EXPECT_EQ(BaseSize(m0), page_size);
- uint8_t* base1 = m1->Begin();
- size_t size1 = m1->Size();
+ EXPECT_EQ(m0.Begin(), base0) << error_msg;
+ EXPECT_EQ(m0.Size(), page_size);
+ EXPECT_EQ(m0.BaseBegin(), base0);
+ EXPECT_EQ(m0.BaseSize(), page_size);
+ uint8_t* base1 = m1.Begin();
+ size_t size1 = m1.Size();
EXPECT_EQ(base1, base0 + page_size);
EXPECT_EQ(size1, page_size);
- EXPECT_EQ(BaseBegin(m1), base1);
- EXPECT_EQ(BaseSize(m1), size1);
+ EXPECT_EQ(m1.BaseBegin(), base1);
+ EXPECT_EQ(m1.BaseSize(), size1);
// Write to the second region.
memset(base1, 43, page_size);
// Check the contents of the two regions.
@@ -117,13 +108,18 @@
EXPECT_EQ(base1[i], 43);
}
// Unmap the first region.
- delete m0;
+ m0.Reset();
// Make sure the second region is still accessible after the first
// region is unmapped.
for (size_t i = 0; i < page_size; ++i) {
EXPECT_EQ(base1[i], 43);
}
- delete m1;
+ MemMap m2 = m1.RemapAtEnd(m1.Begin(),
+ "MemMapTest_RemapAtEndTest_map1",
+ PROT_READ | PROT_WRITE,
+ &error_msg);
+ ASSERT_TRUE(m2.IsValid()) << error_msg;
+ ASSERT_FALSE(m1.IsValid());
}
void CommonInit() {
@@ -168,232 +164,228 @@
#if HAVE_MREMAP_SYSCALL
TEST_F(MemMapTest, ReplaceMapping_SameSize) {
std::string error_msg;
- std::unique_ptr<MemMap> dest(MemMap::MapAnonymous("MapAnonymousEmpty-atomic-replace-dest",
- nullptr,
- kPageSize,
- PROT_READ,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(dest != nullptr);
- MemMap* source = MemMap::MapAnonymous("MapAnonymous-atomic-replace-source",
- nullptr,
- kPageSize,
- PROT_WRITE | PROT_READ,
- false,
- false,
- &error_msg);
- ASSERT_TRUE(source != nullptr);
- void* source_addr = source->Begin();
- void* dest_addr = dest->Begin();
+ MemMap dest = MemMap::MapAnonymous("MapAnonymousEmpty-atomic-replace-dest",
+ /* addr */ nullptr,
+ kPageSize,
+ PROT_READ,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(dest.IsValid());
+ MemMap source = MemMap::MapAnonymous("MapAnonymous-atomic-replace-source",
+ /* addr */ nullptr,
+ kPageSize,
+ PROT_WRITE | PROT_READ,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(source.IsValid());
+ void* source_addr = source.Begin();
+ void* dest_addr = dest.Begin();
ASSERT_TRUE(IsAddressMapped(source_addr));
ASSERT_TRUE(IsAddressMapped(dest_addr));
std::vector<uint8_t> data = RandomData(kPageSize);
- memcpy(source->Begin(), data.data(), data.size());
+ memcpy(source.Begin(), data.data(), data.size());
- ASSERT_TRUE(dest->ReplaceWith(&source, &error_msg)) << error_msg;
+ ASSERT_TRUE(dest.ReplaceWith(&source, &error_msg)) << error_msg;
ASSERT_FALSE(IsAddressMapped(source_addr));
ASSERT_TRUE(IsAddressMapped(dest_addr));
- ASSERT_TRUE(source == nullptr);
+ ASSERT_FALSE(source.IsValid());
- ASSERT_EQ(dest->Size(), static_cast<size_t>(kPageSize));
+ ASSERT_EQ(dest.Size(), static_cast<size_t>(kPageSize));
- ASSERT_EQ(memcmp(dest->Begin(), data.data(), dest->Size()), 0);
+ ASSERT_EQ(memcmp(dest.Begin(), data.data(), dest.Size()), 0);
}
TEST_F(MemMapTest, ReplaceMapping_MakeLarger) {
std::string error_msg;
- std::unique_ptr<MemMap> dest(MemMap::MapAnonymous("MapAnonymousEmpty-atomic-replace-dest",
- nullptr,
- 5 * kPageSize, // Need to make it larger
- // initially so we know
- // there won't be mappings
- // in the way we we move
- // source.
- PROT_READ,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(dest != nullptr);
- MemMap* source = MemMap::MapAnonymous("MapAnonymous-atomic-replace-source",
- nullptr,
- 3 * kPageSize,
- PROT_WRITE | PROT_READ,
- false,
- false,
- &error_msg);
- ASSERT_TRUE(source != nullptr);
- uint8_t* source_addr = source->Begin();
- uint8_t* dest_addr = dest->Begin();
+ MemMap dest = MemMap::MapAnonymous("MapAnonymousEmpty-atomic-replace-dest",
+ /* addr */ nullptr,
+ 5 * kPageSize, // Need to make it larger
+ // initially so we know
+ // there won't be mappings
+ // in the way we we move
+ // source.
+ PROT_READ,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(dest.IsValid());
+ MemMap source = MemMap::MapAnonymous("MapAnonymous-atomic-replace-source",
+ /* addr */ nullptr,
+ 3 * kPageSize,
+ PROT_WRITE | PROT_READ,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(source.IsValid());
+ uint8_t* source_addr = source.Begin();
+ uint8_t* dest_addr = dest.Begin();
ASSERT_TRUE(IsAddressMapped(source_addr));
// Fill the source with random data.
std::vector<uint8_t> data = RandomData(3 * kPageSize);
- memcpy(source->Begin(), data.data(), data.size());
+ memcpy(source.Begin(), data.data(), data.size());
// Make the dest smaller so that we know we'll have space.
- dest->SetSize(kPageSize);
+ dest.SetSize(kPageSize);
ASSERT_TRUE(IsAddressMapped(dest_addr));
ASSERT_FALSE(IsAddressMapped(dest_addr + 2 * kPageSize));
- ASSERT_EQ(dest->Size(), static_cast<size_t>(kPageSize));
+ ASSERT_EQ(dest.Size(), static_cast<size_t>(kPageSize));
- ASSERT_TRUE(dest->ReplaceWith(&source, &error_msg)) << error_msg;
+ ASSERT_TRUE(dest.ReplaceWith(&source, &error_msg)) << error_msg;
ASSERT_FALSE(IsAddressMapped(source_addr));
- ASSERT_EQ(dest->Size(), static_cast<size_t>(3 * kPageSize));
+ ASSERT_EQ(dest.Size(), static_cast<size_t>(3 * kPageSize));
ASSERT_TRUE(IsAddressMapped(dest_addr));
ASSERT_TRUE(IsAddressMapped(dest_addr + 2 * kPageSize));
- ASSERT_TRUE(source == nullptr);
+ ASSERT_FALSE(source.IsValid());
- ASSERT_EQ(memcmp(dest->Begin(), data.data(), dest->Size()), 0);
+ ASSERT_EQ(memcmp(dest.Begin(), data.data(), dest.Size()), 0);
}
TEST_F(MemMapTest, ReplaceMapping_MakeSmaller) {
std::string error_msg;
- std::unique_ptr<MemMap> dest(MemMap::MapAnonymous("MapAnonymousEmpty-atomic-replace-dest",
- nullptr,
- 3 * kPageSize,
- PROT_READ,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(dest != nullptr);
- MemMap* source = MemMap::MapAnonymous("MapAnonymous-atomic-replace-source",
- nullptr,
- kPageSize,
- PROT_WRITE | PROT_READ,
- false,
- false,
- &error_msg);
- ASSERT_TRUE(source != nullptr);
- uint8_t* source_addr = source->Begin();
- uint8_t* dest_addr = dest->Begin();
+ MemMap dest = MemMap::MapAnonymous("MapAnonymousEmpty-atomic-replace-dest",
+ /* addr */ nullptr,
+ 3 * kPageSize,
+ PROT_READ,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(dest.IsValid());
+ MemMap source = MemMap::MapAnonymous("MapAnonymous-atomic-replace-source",
+ /* addr */ nullptr,
+ kPageSize,
+ PROT_WRITE | PROT_READ,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(source.IsValid());
+ uint8_t* source_addr = source.Begin();
+ uint8_t* dest_addr = dest.Begin();
ASSERT_TRUE(IsAddressMapped(source_addr));
ASSERT_TRUE(IsAddressMapped(dest_addr));
ASSERT_TRUE(IsAddressMapped(dest_addr + 2 * kPageSize));
- ASSERT_EQ(dest->Size(), static_cast<size_t>(3 * kPageSize));
+ ASSERT_EQ(dest.Size(), static_cast<size_t>(3 * kPageSize));
std::vector<uint8_t> data = RandomData(kPageSize);
- memcpy(source->Begin(), data.data(), kPageSize);
+ memcpy(source.Begin(), data.data(), kPageSize);
- ASSERT_TRUE(dest->ReplaceWith(&source, &error_msg)) << error_msg;
+ ASSERT_TRUE(dest.ReplaceWith(&source, &error_msg)) << error_msg;
ASSERT_FALSE(IsAddressMapped(source_addr));
- ASSERT_EQ(dest->Size(), static_cast<size_t>(kPageSize));
+ ASSERT_EQ(dest.Size(), static_cast<size_t>(kPageSize));
ASSERT_TRUE(IsAddressMapped(dest_addr));
ASSERT_FALSE(IsAddressMapped(dest_addr + 2 * kPageSize));
- ASSERT_TRUE(source == nullptr);
+ ASSERT_FALSE(source.IsValid());
- ASSERT_EQ(memcmp(dest->Begin(), data.data(), dest->Size()), 0);
+ ASSERT_EQ(memcmp(dest.Begin(), data.data(), dest.Size()), 0);
}
TEST_F(MemMapTest, ReplaceMapping_FailureOverlap) {
std::string error_msg;
- std::unique_ptr<MemMap> dest(
+ MemMap dest =
MemMap::MapAnonymous(
"MapAnonymousEmpty-atomic-replace-dest",
- nullptr,
+ /* addr */ nullptr,
3 * kPageSize, // Need to make it larger initially so we know there won't be mappings in
// the way we we move source.
PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(dest != nullptr);
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(dest.IsValid());
// Resize down to 1 page so we can remap the rest.
- dest->SetSize(kPageSize);
+ dest.SetSize(kPageSize);
// Create source from the last 2 pages
- MemMap* source = MemMap::MapAnonymous("MapAnonymous-atomic-replace-source",
- dest->Begin() + kPageSize,
- 2 * kPageSize,
- PROT_WRITE | PROT_READ,
- false,
- false,
- &error_msg);
- ASSERT_TRUE(source != nullptr);
- MemMap* orig_source = source;
- ASSERT_EQ(dest->Begin() + kPageSize, source->Begin());
- uint8_t* source_addr = source->Begin();
- uint8_t* dest_addr = dest->Begin();
+ MemMap source = MemMap::MapAnonymous("MapAnonymous-atomic-replace-source",
+ dest.Begin() + kPageSize,
+ 2 * kPageSize,
+ PROT_WRITE | PROT_READ,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(source.IsValid());
+ ASSERT_EQ(dest.Begin() + kPageSize, source.Begin());
+ uint8_t* source_addr = source.Begin();
+ uint8_t* dest_addr = dest.Begin();
ASSERT_TRUE(IsAddressMapped(source_addr));
// Fill the source and dest with random data.
std::vector<uint8_t> data = RandomData(2 * kPageSize);
- memcpy(source->Begin(), data.data(), data.size());
+ memcpy(source.Begin(), data.data(), data.size());
std::vector<uint8_t> dest_data = RandomData(kPageSize);
- memcpy(dest->Begin(), dest_data.data(), dest_data.size());
+ memcpy(dest.Begin(), dest_data.data(), dest_data.size());
ASSERT_TRUE(IsAddressMapped(dest_addr));
- ASSERT_EQ(dest->Size(), static_cast<size_t>(kPageSize));
+ ASSERT_EQ(dest.Size(), static_cast<size_t>(kPageSize));
- ASSERT_FALSE(dest->ReplaceWith(&source, &error_msg)) << error_msg;
+ ASSERT_FALSE(dest.ReplaceWith(&source, &error_msg)) << error_msg;
- ASSERT_TRUE(source == orig_source);
ASSERT_TRUE(IsAddressMapped(source_addr));
ASSERT_TRUE(IsAddressMapped(dest_addr));
- ASSERT_EQ(source->Size(), data.size());
- ASSERT_EQ(dest->Size(), dest_data.size());
+ ASSERT_EQ(source.Size(), data.size());
+ ASSERT_EQ(dest.Size(), dest_data.size());
- ASSERT_EQ(memcmp(source->Begin(), data.data(), data.size()), 0);
- ASSERT_EQ(memcmp(dest->Begin(), dest_data.data(), dest_data.size()), 0);
-
- delete source;
+ ASSERT_EQ(memcmp(source.Begin(), data.data(), data.size()), 0);
+ ASSERT_EQ(memcmp(dest.Begin(), dest_data.data(), dest_data.size()), 0);
}
#endif // HAVE_MREMAP_SYSCALL
TEST_F(MemMapTest, MapAnonymousEmpty) {
CommonInit();
std::string error_msg;
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousEmpty",
- nullptr,
- 0,
- PROT_READ,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(map.get() != nullptr) << error_msg;
- ASSERT_TRUE(error_msg.empty());
- map.reset(MemMap::MapAnonymous("MapAnonymousEmpty",
- nullptr,
- kPageSize,
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(map.get() != nullptr) << error_msg;
+ MemMap map = MemMap::MapAnonymous("MapAnonymousEmpty",
+ /* addr */ nullptr,
+ 0,
+ PROT_READ,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_FALSE(map.IsValid()) << error_msg;
+ ASSERT_FALSE(error_msg.empty());
+
+ error_msg.clear();
+ map = MemMap::MapAnonymous("MapAnonymousNonEmpty",
+ /* addr */ nullptr,
+ kPageSize,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(map.IsValid()) << error_msg;
ASSERT_TRUE(error_msg.empty());
}
TEST_F(MemMapTest, MapAnonymousFailNullError) {
CommonInit();
// Test that we don't crash with a null error_str when mapping at an invalid location.
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousInvalid",
- reinterpret_cast<uint8_t*>(kPageSize),
- 0x20000,
- PROT_READ | PROT_WRITE,
- false,
- false,
- nullptr));
- ASSERT_EQ(nullptr, map.get());
+ MemMap map = MemMap::MapAnonymous("MapAnonymousInvalid",
+ reinterpret_cast<uint8_t*>(kPageSize),
+ 0x20000,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ nullptr);
+ ASSERT_FALSE(map.IsValid());
}
#ifdef __LP64__
TEST_F(MemMapTest, MapAnonymousEmpty32bit) {
CommonInit();
std::string error_msg;
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousEmpty",
- nullptr,
- kPageSize,
- PROT_READ | PROT_WRITE,
- true,
- false,
- &error_msg));
- ASSERT_TRUE(map.get() != nullptr) << error_msg;
+ MemMap map = MemMap::MapAnonymous("MapAnonymousEmpty",
+ /* addr */ nullptr,
+ 0,
+ PROT_READ,
+ /* low_4gb */ true,
+ &error_msg);
+ ASSERT_FALSE(map.IsValid()) << error_msg;
+ ASSERT_FALSE(error_msg.empty());
+
+ error_msg.clear();
+ map = MemMap::MapAnonymous("MapAnonymousNonEmpty",
+ /* addr */ nullptr,
+ kPageSize,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ true,
+ &error_msg);
+ ASSERT_TRUE(map.IsValid()) << error_msg;
ASSERT_TRUE(error_msg.empty());
- ASSERT_LT(reinterpret_cast<uintptr_t>(BaseBegin(map.get())), 1ULL << 32);
+ ASSERT_LT(reinterpret_cast<uintptr_t>(map.BaseBegin()), 1ULL << 32);
}
TEST_F(MemMapTest, MapFile32Bit) {
CommonInit();
@@ -402,18 +394,18 @@
constexpr size_t kMapSize = kPageSize;
std::unique_ptr<uint8_t[]> data(new uint8_t[kMapSize]());
ASSERT_TRUE(scratch_file.GetFile()->WriteFully(&data[0], kMapSize));
- std::unique_ptr<MemMap> map(MemMap::MapFile(/*byte_count*/kMapSize,
- PROT_READ,
- MAP_PRIVATE,
- scratch_file.GetFd(),
- /*start*/0,
- /*low_4gb*/true,
- scratch_file.GetFilename().c_str(),
- &error_msg));
- ASSERT_TRUE(map != nullptr) << error_msg;
+ MemMap map = MemMap::MapFile(/*byte_count*/kMapSize,
+ PROT_READ,
+ MAP_PRIVATE,
+ scratch_file.GetFd(),
+ /*start*/0,
+ /*low_4gb*/true,
+ scratch_file.GetFilename().c_str(),
+ &error_msg);
+ ASSERT_TRUE(map.IsValid()) << error_msg;
ASSERT_TRUE(error_msg.empty());
- ASSERT_EQ(map->Size(), kMapSize);
- ASSERT_LT(reinterpret_cast<uintptr_t>(BaseBegin(map.get())), 1ULL << 32);
+ ASSERT_EQ(map.Size(), kMapSize);
+ ASSERT_LT(reinterpret_cast<uintptr_t>(map.BaseBegin()), 1ULL << 32);
}
#endif
@@ -423,36 +415,33 @@
// Find a valid address.
uint8_t* valid_address = GetValidMapAddress(kPageSize, /*low_4gb*/false);
// Map at an address that should work, which should succeed.
- std::unique_ptr<MemMap> map0(MemMap::MapAnonymous("MapAnonymous0",
- valid_address,
- kPageSize,
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(map0.get() != nullptr) << error_msg;
+ MemMap map0 = MemMap::MapAnonymous("MapAnonymous0",
+ valid_address,
+ kPageSize,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(map0.IsValid()) << error_msg;
ASSERT_TRUE(error_msg.empty());
- ASSERT_TRUE(map0->BaseBegin() == valid_address);
+ ASSERT_TRUE(map0.BaseBegin() == valid_address);
// Map at an unspecified address, which should succeed.
- std::unique_ptr<MemMap> map1(MemMap::MapAnonymous("MapAnonymous1",
- nullptr,
- kPageSize,
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(map1.get() != nullptr) << error_msg;
+ MemMap map1 = MemMap::MapAnonymous("MapAnonymous1",
+ /* addr */ nullptr,
+ kPageSize,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(map1.IsValid()) << error_msg;
ASSERT_TRUE(error_msg.empty());
- ASSERT_TRUE(map1->BaseBegin() != nullptr);
+ ASSERT_TRUE(map1.BaseBegin() != nullptr);
// Attempt to map at the same address, which should fail.
- std::unique_ptr<MemMap> map2(MemMap::MapAnonymous("MapAnonymous2",
- reinterpret_cast<uint8_t*>(map1->BaseBegin()),
- kPageSize,
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(map2.get() == nullptr) << error_msg;
+ MemMap map2 = MemMap::MapAnonymous("MapAnonymous2",
+ reinterpret_cast<uint8_t*>(map1.BaseBegin()),
+ kPageSize,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_FALSE(map2.IsValid()) << error_msg;
ASSERT_TRUE(!error_msg.empty());
}
@@ -480,23 +469,22 @@
// Try all addresses starting from 2GB to 4GB.
size_t start_addr = 2 * GB;
std::string error_msg;
- std::unique_ptr<MemMap> map;
+ MemMap map;
for (; start_addr <= std::numeric_limits<uint32_t>::max() - size; start_addr += size) {
- map.reset(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr",
- reinterpret_cast<uint8_t*>(start_addr),
- size,
- PROT_READ | PROT_WRITE,
- /*low_4gb*/true,
- false,
- &error_msg));
- if (map != nullptr) {
+ map = MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr",
+ reinterpret_cast<uint8_t*>(start_addr),
+ size,
+ PROT_READ | PROT_WRITE,
+ /*low_4gb*/ true,
+ &error_msg);
+ if (map.IsValid()) {
break;
}
}
- ASSERT_TRUE(map.get() != nullptr) << error_msg;
- ASSERT_GE(reinterpret_cast<uintptr_t>(map->End()), 2u * GB);
+ ASSERT_TRUE(map.IsValid()) << error_msg;
+ ASSERT_GE(reinterpret_cast<uintptr_t>(map.End()), 2u * GB);
ASSERT_TRUE(error_msg.empty());
- ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast<void*>(start_addr));
+ ASSERT_EQ(map.BaseBegin(), reinterpret_cast<void*>(start_addr));
}
TEST_F(MemMapTest, MapAnonymousOverflow) {
@@ -504,14 +492,13 @@
std::string error_msg;
uintptr_t ptr = 0;
ptr -= kPageSize; // Now it's close to the top.
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousOverflow",
- reinterpret_cast<uint8_t*>(ptr),
- 2 * kPageSize, // brings it over the top.
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- ASSERT_EQ(nullptr, map.get());
+ MemMap map = MemMap::MapAnonymous("MapAnonymousOverflow",
+ reinterpret_cast<uint8_t*>(ptr),
+ 2 * kPageSize, // brings it over the top.
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_FALSE(map.IsValid());
ASSERT_FALSE(error_msg.empty());
}
@@ -519,29 +506,27 @@
TEST_F(MemMapTest, MapAnonymousLow4GBExpectedTooHigh) {
CommonInit();
std::string error_msg;
- std::unique_ptr<MemMap> map(
+ MemMap map =
MemMap::MapAnonymous("MapAnonymousLow4GBExpectedTooHigh",
reinterpret_cast<uint8_t*>(UINT64_C(0x100000000)),
kPageSize,
PROT_READ | PROT_WRITE,
- true,
- false,
- &error_msg));
- ASSERT_EQ(nullptr, map.get());
+ /* low_4gb */ true,
+ &error_msg);
+ ASSERT_FALSE(map.IsValid());
ASSERT_FALSE(error_msg.empty());
}
TEST_F(MemMapTest, MapAnonymousLow4GBRangeTooHigh) {
CommonInit();
std::string error_msg;
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousLow4GBRangeTooHigh",
- reinterpret_cast<uint8_t*>(0xF0000000),
- 0x20000000,
- PROT_READ | PROT_WRITE,
- true,
- false,
- &error_msg));
- ASSERT_EQ(nullptr, map.get());
+ MemMap map = MemMap::MapAnonymous("MapAnonymousLow4GBRangeTooHigh",
+ reinterpret_cast<uint8_t*>(0xF0000000),
+ 0x20000000,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ true,
+ &error_msg);
+ ASSERT_FALSE(map.IsValid());
ASSERT_FALSE(error_msg.empty());
}
#endif
@@ -549,23 +534,23 @@
TEST_F(MemMapTest, MapAnonymousReuse) {
CommonInit();
std::string error_msg;
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousReserve",
- nullptr,
- 0x20000,
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- ASSERT_NE(nullptr, map.get());
+ MemMap map = MemMap::MapAnonymous("MapAnonymousReserve",
+ nullptr,
+ 0x20000,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ /* reuse */ false,
+ &error_msg);
+ ASSERT_TRUE(map.IsValid());
ASSERT_TRUE(error_msg.empty());
- std::unique_ptr<MemMap> map2(MemMap::MapAnonymous("MapAnonymousReused",
- reinterpret_cast<uint8_t*>(map->BaseBegin()),
- 0x10000,
- PROT_READ | PROT_WRITE,
- false,
- true,
- &error_msg));
- ASSERT_NE(nullptr, map2.get());
+ MemMap map2 = MemMap::MapAnonymous("MapAnonymousReused",
+ reinterpret_cast<uint8_t*>(map.BaseBegin()),
+ 0x10000,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ /* reuse */ true,
+ &error_msg);
+ ASSERT_TRUE(map2.IsValid());
ASSERT_TRUE(error_msg.empty());
}
@@ -574,65 +559,61 @@
std::string error_msg;
constexpr size_t kNumPages = 3;
// Map a 3-page mem map.
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymous0",
- nullptr,
- kPageSize * kNumPages,
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(map.get() != nullptr) << error_msg;
+ MemMap map = MemMap::MapAnonymous("MapAnonymous0",
+ /* addr */ nullptr,
+ kPageSize * kNumPages,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(map.IsValid()) << error_msg;
ASSERT_TRUE(error_msg.empty());
// Record the base address.
- uint8_t* map_base = reinterpret_cast<uint8_t*>(map->BaseBegin());
+ uint8_t* map_base = reinterpret_cast<uint8_t*>(map.BaseBegin());
// Unmap it.
- map.reset();
+ map.Reset();
// Map at the same address, but in page-sized separate mem maps,
// assuming the space at the address is still available.
- std::unique_ptr<MemMap> map0(MemMap::MapAnonymous("MapAnonymous0",
- map_base,
- kPageSize,
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(map0.get() != nullptr) << error_msg;
+ MemMap map0 = MemMap::MapAnonymous("MapAnonymous0",
+ map_base,
+ kPageSize,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(map0.IsValid()) << error_msg;
ASSERT_TRUE(error_msg.empty());
- std::unique_ptr<MemMap> map1(MemMap::MapAnonymous("MapAnonymous1",
- map_base + kPageSize,
- kPageSize,
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(map1.get() != nullptr) << error_msg;
+ MemMap map1 = MemMap::MapAnonymous("MapAnonymous1",
+ map_base + kPageSize,
+ kPageSize,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(map1.IsValid()) << error_msg;
ASSERT_TRUE(error_msg.empty());
- std::unique_ptr<MemMap> map2(MemMap::MapAnonymous("MapAnonymous2",
- map_base + kPageSize * 2,
- kPageSize,
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- ASSERT_TRUE(map2.get() != nullptr) << error_msg;
+ MemMap map2 = MemMap::MapAnonymous("MapAnonymous2",
+ map_base + kPageSize * 2,
+ kPageSize,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(map2.IsValid()) << error_msg;
ASSERT_TRUE(error_msg.empty());
// One-map cases.
- ASSERT_TRUE(MemMap::CheckNoGaps(map0.get(), map0.get()));
- ASSERT_TRUE(MemMap::CheckNoGaps(map1.get(), map1.get()));
- ASSERT_TRUE(MemMap::CheckNoGaps(map2.get(), map2.get()));
+ ASSERT_TRUE(MemMap::CheckNoGaps(map0, map0));
+ ASSERT_TRUE(MemMap::CheckNoGaps(map1, map1));
+ ASSERT_TRUE(MemMap::CheckNoGaps(map2, map2));
// Two or three-map cases.
- ASSERT_TRUE(MemMap::CheckNoGaps(map0.get(), map1.get()));
- ASSERT_TRUE(MemMap::CheckNoGaps(map1.get(), map2.get()));
- ASSERT_TRUE(MemMap::CheckNoGaps(map0.get(), map2.get()));
+ ASSERT_TRUE(MemMap::CheckNoGaps(map0, map1));
+ ASSERT_TRUE(MemMap::CheckNoGaps(map1, map2));
+ ASSERT_TRUE(MemMap::CheckNoGaps(map0, map2));
// Unmap the middle one.
- map1.reset();
+ map1.Reset();
// Should return false now that there's a gap in the middle.
- ASSERT_FALSE(MemMap::CheckNoGaps(map0.get(), map2.get()));
+ ASSERT_FALSE(MemMap::CheckNoGaps(map0, map2));
}
TEST_F(MemMapTest, AlignBy) {
@@ -641,52 +622,52 @@
// Cast the page size to size_t.
const size_t page_size = static_cast<size_t>(kPageSize);
// Map a region.
- std::unique_ptr<MemMap> m0(MemMap::MapAnonymous("MemMapTest_AlignByTest_map0",
- nullptr,
- 14 * page_size,
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- uint8_t* base0 = m0->Begin();
+ MemMap m0 = MemMap::MapAnonymous("MemMapTest_AlignByTest_map0",
+ /* addr */ nullptr,
+ 14 * page_size,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(m0.IsValid());
+ uint8_t* base0 = m0.Begin();
ASSERT_TRUE(base0 != nullptr) << error_msg;
- ASSERT_EQ(m0->Size(), 14 * page_size);
- ASSERT_EQ(BaseBegin(m0.get()), base0);
- ASSERT_EQ(BaseSize(m0.get()), m0->Size());
+ ASSERT_EQ(m0.Size(), 14 * page_size);
+ ASSERT_EQ(m0.BaseBegin(), base0);
+ ASSERT_EQ(m0.BaseSize(), m0.Size());
// Break it into several regions by using RemapAtEnd.
- std::unique_ptr<MemMap> m1(m0->RemapAtEnd(base0 + 3 * page_size,
- "MemMapTest_AlignByTest_map1",
- PROT_READ | PROT_WRITE,
- &error_msg));
- uint8_t* base1 = m1->Begin();
+ MemMap m1 = m0.RemapAtEnd(base0 + 3 * page_size,
+ "MemMapTest_AlignByTest_map1",
+ PROT_READ | PROT_WRITE,
+ &error_msg);
+ uint8_t* base1 = m1.Begin();
ASSERT_TRUE(base1 != nullptr) << error_msg;
ASSERT_EQ(base1, base0 + 3 * page_size);
- ASSERT_EQ(m0->Size(), 3 * page_size);
+ ASSERT_EQ(m0.Size(), 3 * page_size);
- std::unique_ptr<MemMap> m2(m1->RemapAtEnd(base1 + 4 * page_size,
- "MemMapTest_AlignByTest_map2",
- PROT_READ | PROT_WRITE,
- &error_msg));
- uint8_t* base2 = m2->Begin();
+ MemMap m2 = m1.RemapAtEnd(base1 + 4 * page_size,
+ "MemMapTest_AlignByTest_map2",
+ PROT_READ | PROT_WRITE,
+ &error_msg);
+ uint8_t* base2 = m2.Begin();
ASSERT_TRUE(base2 != nullptr) << error_msg;
ASSERT_EQ(base2, base1 + 4 * page_size);
- ASSERT_EQ(m1->Size(), 4 * page_size);
+ ASSERT_EQ(m1.Size(), 4 * page_size);
- std::unique_ptr<MemMap> m3(m2->RemapAtEnd(base2 + 3 * page_size,
- "MemMapTest_AlignByTest_map1",
- PROT_READ | PROT_WRITE,
- &error_msg));
- uint8_t* base3 = m3->Begin();
+ MemMap m3 = m2.RemapAtEnd(base2 + 3 * page_size,
+ "MemMapTest_AlignByTest_map1",
+ PROT_READ | PROT_WRITE,
+ &error_msg);
+ uint8_t* base3 = m3.Begin();
ASSERT_TRUE(base3 != nullptr) << error_msg;
ASSERT_EQ(base3, base2 + 3 * page_size);
- ASSERT_EQ(m2->Size(), 3 * page_size);
- ASSERT_EQ(m3->Size(), 4 * page_size);
+ ASSERT_EQ(m2.Size(), 3 * page_size);
+ ASSERT_EQ(m3.Size(), 4 * page_size);
- uint8_t* end0 = base0 + m0->Size();
- uint8_t* end1 = base1 + m1->Size();
- uint8_t* end2 = base2 + m2->Size();
- uint8_t* end3 = base3 + m3->Size();
+ uint8_t* end0 = base0 + m0.Size();
+ uint8_t* end1 = base1 + m1.Size();
+ uint8_t* end2 = base2 + m2.Size();
+ uint8_t* end3 = base3 + m3.Size();
ASSERT_EQ(static_cast<size_t>(end3 - base0), 14 * page_size);
@@ -703,39 +684,39 @@
}
// Align by 2 * page_size;
- m0->AlignBy(2 * page_size);
- m1->AlignBy(2 * page_size);
- m2->AlignBy(2 * page_size);
- m3->AlignBy(2 * page_size);
+ m0.AlignBy(2 * page_size);
+ m1.AlignBy(2 * page_size);
+ m2.AlignBy(2 * page_size);
+ m3.AlignBy(2 * page_size);
- EXPECT_TRUE(IsAlignedParam(m0->Begin(), 2 * page_size));
- EXPECT_TRUE(IsAlignedParam(m1->Begin(), 2 * page_size));
- EXPECT_TRUE(IsAlignedParam(m2->Begin(), 2 * page_size));
- EXPECT_TRUE(IsAlignedParam(m3->Begin(), 2 * page_size));
+ EXPECT_TRUE(IsAlignedParam(m0.Begin(), 2 * page_size));
+ EXPECT_TRUE(IsAlignedParam(m1.Begin(), 2 * page_size));
+ EXPECT_TRUE(IsAlignedParam(m2.Begin(), 2 * page_size));
+ EXPECT_TRUE(IsAlignedParam(m3.Begin(), 2 * page_size));
- EXPECT_TRUE(IsAlignedParam(m0->Begin() + m0->Size(), 2 * page_size));
- EXPECT_TRUE(IsAlignedParam(m1->Begin() + m1->Size(), 2 * page_size));
- EXPECT_TRUE(IsAlignedParam(m2->Begin() + m2->Size(), 2 * page_size));
- EXPECT_TRUE(IsAlignedParam(m3->Begin() + m3->Size(), 2 * page_size));
+ EXPECT_TRUE(IsAlignedParam(m0.Begin() + m0.Size(), 2 * page_size));
+ EXPECT_TRUE(IsAlignedParam(m1.Begin() + m1.Size(), 2 * page_size));
+ EXPECT_TRUE(IsAlignedParam(m2.Begin() + m2.Size(), 2 * page_size));
+ EXPECT_TRUE(IsAlignedParam(m3.Begin() + m3.Size(), 2 * page_size));
if (IsAlignedParam(base0, 2 * page_size)) {
- EXPECT_EQ(m0->Begin(), base0);
- EXPECT_EQ(m0->Begin() + m0->Size(), end0 - page_size);
- EXPECT_EQ(m1->Begin(), base1 + page_size);
- EXPECT_EQ(m1->Begin() + m1->Size(), end1 - page_size);
- EXPECT_EQ(m2->Begin(), base2 + page_size);
- EXPECT_EQ(m2->Begin() + m2->Size(), end2);
- EXPECT_EQ(m3->Begin(), base3);
- EXPECT_EQ(m3->Begin() + m3->Size(), end3);
+ EXPECT_EQ(m0.Begin(), base0);
+ EXPECT_EQ(m0.Begin() + m0.Size(), end0 - page_size);
+ EXPECT_EQ(m1.Begin(), base1 + page_size);
+ EXPECT_EQ(m1.Begin() + m1.Size(), end1 - page_size);
+ EXPECT_EQ(m2.Begin(), base2 + page_size);
+ EXPECT_EQ(m2.Begin() + m2.Size(), end2);
+ EXPECT_EQ(m3.Begin(), base3);
+ EXPECT_EQ(m3.Begin() + m3.Size(), end3);
} else {
- EXPECT_EQ(m0->Begin(), base0 + page_size);
- EXPECT_EQ(m0->Begin() + m0->Size(), end0);
- EXPECT_EQ(m1->Begin(), base1);
- EXPECT_EQ(m1->Begin() + m1->Size(), end1);
- EXPECT_EQ(m2->Begin(), base2);
- EXPECT_EQ(m2->Begin() + m2->Size(), end2 - page_size);
- EXPECT_EQ(m3->Begin(), base3 + page_size);
- EXPECT_EQ(m3->Begin() + m3->Size(), end3 - page_size);
+ EXPECT_EQ(m0.Begin(), base0 + page_size);
+ EXPECT_EQ(m0.Begin() + m0.Size(), end0);
+ EXPECT_EQ(m1.Begin(), base1);
+ EXPECT_EQ(m1.Begin() + m1.Size(), end1);
+ EXPECT_EQ(m2.Begin(), base2);
+ EXPECT_EQ(m2.Begin() + m2.Size(), end2 - page_size);
+ EXPECT_EQ(m3.Begin(), base3 + page_size);
+ EXPECT_EQ(m3.Begin() + m3.Size(), end3 - page_size);
}
}
diff --git a/libartbase/base/zip_archive.cc b/libartbase/base/zip_archive.cc
index b5f946e..a841bae 100644
--- a/libartbase/base/zip_archive.cc
+++ b/libartbase/base/zip_archive.cc
@@ -68,31 +68,33 @@
return true;
}
-MemMap* ZipEntry::ExtractToMemMap(const char* zip_filename, const char* entry_filename,
- std::string* error_msg) {
+MemMap ZipEntry::ExtractToMemMap(const char* zip_filename,
+ const char* entry_filename,
+ std::string* error_msg) {
std::string name(entry_filename);
name += " extracted in memory from ";
name += zip_filename;
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous(name.c_str(),
- nullptr, GetUncompressedLength(),
- PROT_READ | PROT_WRITE, false, false,
- error_msg));
- if (map.get() == nullptr) {
+ MemMap map = MemMap::MapAnonymous(name.c_str(),
+ /* addr */ nullptr,
+ GetUncompressedLength(),
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ error_msg);
+ if (!map.IsValid()) {
DCHECK(!error_msg->empty());
- return nullptr;
+ return MemMap::Invalid();
}
- const int32_t error = ExtractToMemory(handle_, zip_entry_,
- map->Begin(), map->Size());
+ const int32_t error = ExtractToMemory(handle_, zip_entry_, map.Begin(), map.Size());
if (error) {
*error_msg = std::string(ErrorCodeString(error));
- return nullptr;
+ return MemMap::Invalid();
}
- return map.release();
+ return map;
}
-MemMap* ZipEntry::MapDirectlyFromFile(const char* zip_filename, std::string* error_msg) {
+MemMap ZipEntry::MapDirectlyFromFile(const char* zip_filename, std::string* error_msg) {
const int zip_fd = GetFileDescriptor(handle_);
const char* entry_filename = entry_name_.c_str();
@@ -109,7 +111,7 @@
*error_msg = StringPrintf("Cannot map '%s' (in zip '%s') directly because it is compressed.",
entry_filename,
zip_filename);
- return nullptr;
+ return MemMap::Invalid();
} else if (zip_entry_->uncompressed_length != zip_entry_->compressed_length) {
*error_msg = StringPrintf("Cannot map '%s' (in zip '%s') directly because "
"entry has bad size (%u != %u).",
@@ -117,7 +119,7 @@
zip_filename,
zip_entry_->uncompressed_length,
zip_entry_->compressed_length);
- return nullptr;
+ return MemMap::Invalid();
}
std::string name(entry_filename);
@@ -130,7 +132,7 @@
LOG(INFO) << "zip_archive: " << "make mmap of " << name << " @ offset = " << offset;
}
- std::unique_ptr<MemMap> map(
+ MemMap map =
MemMap::MapFileAtAddress(nullptr, // Expected pointer address
GetUncompressedLength(), // Byte count
PROT_READ | PROT_WRITE,
@@ -140,9 +142,9 @@
false, // Don't restrict allocation to lower4GB
false, // Doesn't overlap existing map (reuse=false)
name.c_str(),
- /*out*/error_msg));
+ /*out*/error_msg);
- if (map == nullptr) {
+ if (!map.IsValid()) {
DCHECK(!error_msg->empty());
}
@@ -169,12 +171,12 @@
LOG(INFO) << "---------------------------";
// Dump map contents.
- if (map != nullptr) {
+ if (map.IsValid()) {
tmp = "";
count = kMaxDumpChars;
- uint8_t* begin = map->Begin();
+ uint8_t* begin = map.Begin();
for (i = 0; i < count; ++i) {
tmp += StringPrintf("%3d ", (unsigned int)begin[i]);
}
@@ -185,19 +187,20 @@
}
}
- return map.release();
+ return map;
}
-MemMap* ZipEntry::MapDirectlyOrExtract(const char* zip_filename,
- const char* entry_filename,
- std::string* error_msg) {
+MemMap ZipEntry::MapDirectlyOrExtract(const char* zip_filename,
+ const char* entry_filename,
+ std::string* error_msg) {
if (IsUncompressed() && GetFileDescriptor(handle_) >= 0) {
- MemMap* ret = MapDirectlyFromFile(zip_filename, error_msg);
- if (ret != nullptr) {
+ std::string local_error_msg;
+ MemMap ret = MapDirectlyFromFile(zip_filename, &local_error_msg);
+ if (ret.IsValid()) {
return ret;
}
+ // Fall back to extraction for the failure case.
}
- // Fall back to extraction for the failure case.
return ExtractToMemMap(zip_filename, entry_filename, error_msg);
}
diff --git a/libartbase/base/zip_archive.h b/libartbase/base/zip_archive.h
index 73495da..8fc8b54 100644
--- a/libartbase/base/zip_archive.h
+++ b/libartbase/base/zip_archive.h
@@ -43,21 +43,22 @@
bool ExtractToFile(File& file, std::string* error_msg);
// Extract this entry to anonymous memory (R/W).
// Returns null on failure and sets error_msg.
- MemMap* ExtractToMemMap(const char* zip_filename, const char* entry_filename,
- std::string* error_msg);
+ MemMap ExtractToMemMap(const char* zip_filename,
+ const char* entry_filename,
+ std::string* error_msg);
// Create a file-backed private (clean, R/W) memory mapping to this entry.
// 'zip_filename' is used for diagnostics only,
// the original file that the ZipArchive was open with is used
// for the mapping.
//
// Will only succeed if the entry is stored uncompressed.
- // Returns null on failure and sets error_msg.
- MemMap* MapDirectlyFromFile(const char* zip_filename, /*out*/std::string* error_msg);
+ // Returns invalid MemMap on failure and sets error_msg.
+ MemMap MapDirectlyFromFile(const char* zip_filename, /*out*/std::string* error_msg);
virtual ~ZipEntry();
- MemMap* MapDirectlyOrExtract(const char* zip_filename,
- const char* entry_filename,
- std::string* error_msg);
+ MemMap MapDirectlyOrExtract(const char* zip_filename,
+ const char* entry_filename,
+ std::string* error_msg);
uint32_t GetUncompressedLength();
uint32_t GetCrc32();
diff --git a/libdexfile/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc
index cc7d7aa..1846a13 100644
--- a/libdexfile/dex/art_dex_file_loader.cc
+++ b/libdexfile/dex/art_dex_file_loader.cc
@@ -23,6 +23,7 @@
#include "base/file_magic.h"
#include "base/file_utils.h"
+#include "base/mem_map.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/unix_file/fd_file.h"
@@ -38,14 +39,14 @@
class MemMapContainer : public DexFileContainer {
public:
- explicit MemMapContainer(std::unique_ptr<MemMap>&& mem_map) : mem_map_(std::move(mem_map)) { }
+ explicit MemMapContainer(MemMap&& mem_map) : mem_map_(std::move(mem_map)) { }
virtual ~MemMapContainer() OVERRIDE { }
int GetPermissions() OVERRIDE {
- if (mem_map_.get() == nullptr) {
+ if (!mem_map_.IsValid()) {
return 0;
} else {
- return mem_map_->GetProtect();
+ return mem_map_.GetProtect();
}
}
@@ -55,24 +56,24 @@
bool EnableWrite() OVERRIDE {
CHECK(IsReadOnly());
- if (mem_map_.get() == nullptr) {
+ if (!mem_map_.IsValid()) {
return false;
} else {
- return mem_map_->Protect(PROT_READ | PROT_WRITE);
+ return mem_map_.Protect(PROT_READ | PROT_WRITE);
}
}
bool DisableWrite() OVERRIDE {
CHECK(!IsReadOnly());
- if (mem_map_.get() == nullptr) {
+ if (!mem_map_.IsValid()) {
return false;
} else {
- return mem_map_->Protect(PROT_READ);
+ return mem_map_.Protect(PROT_READ);
}
}
private:
- std::unique_ptr<MemMap> mem_map_;
+ MemMap mem_map_;
DISALLOW_COPY_AND_ASSIGN(MemMapContainer);
};
@@ -180,22 +181,24 @@
std::unique_ptr<const DexFile> ArtDexFileLoader::Open(const std::string& location,
uint32_t location_checksum,
- std::unique_ptr<MemMap> map,
+ MemMap&& map,
bool verify,
bool verify_checksum,
std::string* error_msg) const {
ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location);
- CHECK(map.get() != nullptr);
+ CHECK(map.IsValid());
- if (map->Size() < sizeof(DexFile::Header)) {
+ size_t size = map.Size();
+ if (size < sizeof(DexFile::Header)) {
*error_msg = StringPrintf(
"DexFile: failed to open dex file '%s' that is too short to have a header",
location.c_str());
return nullptr;
}
- std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
- map->Size(),
+ uint8_t* begin = map.Begin();
+ std::unique_ptr<DexFile> dex_file = OpenCommon(begin,
+ size,
/*data_base*/ nullptr,
/*data_size*/ 0u,
location,
@@ -285,7 +288,7 @@
std::string* error_msg) const {
ScopedTrace trace(std::string("Open dex file ") + std::string(location));
CHECK(!location.empty());
- std::unique_ptr<MemMap> map;
+ MemMap map;
{
File delayed_close(fd, /* check_usage */ false);
struct stat sbuf;
@@ -300,31 +303,33 @@
return nullptr;
}
size_t length = sbuf.st_size;
- map.reset(MemMap::MapFile(length,
- PROT_READ,
- mmap_shared ? MAP_SHARED : MAP_PRIVATE,
- fd,
- 0,
- /*low_4gb*/false,
- location.c_str(),
- error_msg));
- if (map == nullptr) {
+ map = MemMap::MapFile(length,
+ PROT_READ,
+ mmap_shared ? MAP_SHARED : MAP_PRIVATE,
+ fd,
+ 0,
+ /*low_4gb*/false,
+ location.c_str(),
+ error_msg);
+ if (!map.IsValid()) {
DCHECK(!error_msg->empty());
return nullptr;
}
}
- if (map->Size() < sizeof(DexFile::Header)) {
+ const uint8_t* begin = map.Begin();
+ size_t size = map.Size();
+ if (size < sizeof(DexFile::Header)) {
*error_msg = StringPrintf(
"DexFile: failed to open dex file '%s' that is too short to have a header",
location.c_str());
return nullptr;
}
- const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(map->Begin());
+ const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(begin);
- std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
- map->Size(),
+ std::unique_ptr<DexFile> dex_file = OpenCommon(begin,
+ size,
/*data_base*/ nullptr,
/*data_size*/ 0u,
location,
@@ -366,7 +371,7 @@
return nullptr;
}
- std::unique_ptr<MemMap> map;
+ MemMap map;
if (zip_entry->IsUncompressed()) {
if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) {
// Do not mmap unaligned ZIP entries because
@@ -376,8 +381,8 @@
<< "Falling back to extracting file.";
} else {
// Map uncompressed files within zip as file-backed to avoid a dirty copy.
- map.reset(zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg));
- if (map == nullptr) {
+ map = zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg);
+ if (!map.IsValid()) {
LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; "
<< "is your ZIP file corrupted? Falling back to extraction.";
// Try again with Extraction which still has a chance of recovery.
@@ -385,21 +390,23 @@
}
}
- if (map == nullptr) {
+ if (!map.IsValid()) {
// Default path for compressed ZIP entries,
// and fallback for stored ZIP entries.
- map.reset(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
+ map = zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg);
}
- if (map == nullptr) {
+ if (!map.IsValid()) {
*error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
error_msg->c_str());
*error_code = DexFileLoaderErrorCode::kExtractToMemoryError;
return nullptr;
}
VerifyResult verify_result;
- std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
- map->Size(),
+ uint8_t* begin = map.Begin();
+ size_t size = map.Size();
+ std::unique_ptr<DexFile> dex_file = OpenCommon(begin,
+ size,
/*data_base*/ nullptr,
/*data_size*/ 0u,
location,
diff --git a/libdexfile/dex/art_dex_file_loader.h b/libdexfile/dex/art_dex_file_loader.h
index da2620f..420b347 100644
--- a/libdexfile/dex/art_dex_file_loader.h
+++ b/libdexfile/dex/art_dex_file_loader.h
@@ -66,7 +66,7 @@
// Opens .dex file that has been memory-mapped by the caller.
std::unique_ptr<const DexFile> Open(const std::string& location,
uint32_t location_checkum,
- std::unique_ptr<MemMap> mem_map,
+ MemMap&& mem_map,
bool verify,
bool verify_checksum,
std::string* error_msg) const;
diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h
index 9b9ac87..d40577f 100644
--- a/libdexfile/dex/class_accessor.h
+++ b/libdexfile/dex/class_accessor.h
@@ -31,7 +31,7 @@
// Classes to access Dex data.
class ClassAccessor {
- private:
+ public:
class BaseItem {
public:
explicit BaseItem(const DexFile& dex_file,
@@ -65,6 +65,14 @@
return ptr_pos_;
}
+ bool MemberIsNative() const {
+ return GetRawAccessFlags() & kAccNative;
+ }
+
+ bool MemberIsFinal() const {
+ return GetRawAccessFlags() & kAccFinal;
+ }
+
protected:
// Internal data pointer for reading.
const DexFile& dex_file_;
@@ -73,7 +81,6 @@
uint32_t access_flags_ = 0u;
};
- public:
// A decoded version of the method of a class_data_item.
class Method : public BaseItem {
public:
@@ -350,6 +357,10 @@
return class_def_index_;
}
+ const DexFile::ClassDef& GetClassDef() const {
+ return dex_file_.GetClassDef(GetClassDefIndex());
+ }
+
protected:
// Template visitor to reduce copy paste for visiting elements.
// No thread safety analysis since the visitor may require capabilities.
diff --git a/libdexfile/dex/code_item_accessors.h b/libdexfile/dex/code_item_accessors.h
index 5786d3f..695cc7b 100644
--- a/libdexfile/dex/code_item_accessors.h
+++ b/libdexfile/dex/code_item_accessors.h
@@ -80,7 +80,7 @@
uint32_t insns_size_in_code_units_ = 0;
// Pointer to the instructions, null if there is no code item.
- const uint16_t* insns_ = 0;
+ const uint16_t* insns_ = nullptr;
};
// Abstracts accesses to code item fields other than debug info for CompactDexFile and
diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h
index 0966859..c512361 100644
--- a/libdexfile/dex/dex_file-inl.h
+++ b/libdexfile/dex/dex_file-inl.h
@@ -204,26 +204,6 @@
return true;
}
-inline
-InvokeType ClassDataItemIterator::GetMethodInvokeType(const DexFile::ClassDef& class_def) const {
- if (HasNextDirectMethod()) {
- if ((GetRawMemberAccessFlags() & kAccStatic) != 0) {
- return kStatic;
- } else {
- return kDirect;
- }
- } else {
- DCHECK_EQ(GetRawMemberAccessFlags() & kAccStatic, 0U);
- if ((class_def.access_flags_ & kAccInterface) != 0) {
- return kInterface;
- } else if ((GetRawMemberAccessFlags() & kAccConstructor) != 0) {
- return kSuper;
- } else {
- return kVirtual;
- }
- }
-}
-
template<typename NewLocalCallback, typename IndexToStringData, typename TypeIndexToStringData>
bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream,
const std::string& location,
@@ -518,18 +498,6 @@
return handler_data + offset;
}
-template <typename Visitor>
-inline void DexFile::ClassDef::VisitMethods(const DexFile* dex_file, const Visitor& visitor) const {
- const uint8_t* class_data = dex_file->GetClassData(*this);
- if (class_data != nullptr) {
- ClassDataItemIterator it(*dex_file, class_data);
- it.SkipAllFields();
- for (; it.HasNext(); it.Next()) {
- visitor(it);
- }
- }
-}
-
inline IterationRange<ClassIterator> DexFile::GetClasses() const {
return { ClassIterator(*this, 0u), ClassIterator(*this, NumClassDefs()) };
}
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index f1f8960..a2198b7 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -31,6 +31,7 @@
#include "base/enums.h"
#include "base/leb128.h"
#include "base/stl_util.h"
+#include "class_accessor-inl.h"
#include "descriptors_names.h"
#include "dex_file-inl.h"
#include "standard_dex_file.h"
@@ -219,21 +220,12 @@
uint32_t DexFile::FindCodeItemOffset(const DexFile::ClassDef& class_def,
uint32_t method_idx) const {
- const uint8_t* class_data = GetClassData(class_def);
- CHECK(class_data != nullptr);
- ClassDataItemIterator it(*this, class_data);
- it.SkipAllFields();
- while (it.HasNextDirectMethod()) {
- if (it.GetMemberIndex() == method_idx) {
- return it.GetMethodCodeItemOffset();
+ ClassAccessor accessor(*this, class_def);
+ CHECK(accessor.HasClassData());
+ for (const ClassAccessor::Method& method : accessor.GetMethods()) {
+ if (method.GetIndex() == method_idx) {
+ return method.GetCodeItemOffset();
}
- it.Next();
- }
- while (it.HasNextVirtualMethod()) {
- if (it.GetMemberIndex() == method_idx) {
- return it.GetMethodCodeItemOffset();
- }
- it.Next();
}
LOG(FATAL) << "Unable to find method " << method_idx;
UNREACHABLE();
@@ -684,31 +676,6 @@
return os << sig.ToString();
}
-// Decodes the header section from the class data bytes.
-void ClassDataItemIterator::ReadClassDataHeader() {
- CHECK(ptr_pos_ != nullptr);
- header_.static_fields_size_ = DecodeUnsignedLeb128(&ptr_pos_);
- header_.instance_fields_size_ = DecodeUnsignedLeb128(&ptr_pos_);
- header_.direct_methods_size_ = DecodeUnsignedLeb128(&ptr_pos_);
- header_.virtual_methods_size_ = DecodeUnsignedLeb128(&ptr_pos_);
-}
-
-void ClassDataItemIterator::ReadClassDataField() {
- field_.field_idx_delta_ = DecodeUnsignedLeb128(&ptr_pos_);
- field_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_);
- // The user of the iterator is responsible for checking if there
- // are unordered or duplicate indexes.
-}
-
-void ClassDataItemIterator::ReadClassDataMethod() {
- method_.method_idx_delta_ = DecodeUnsignedLeb128(&ptr_pos_);
- method_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_);
- method_.code_off_ = DecodeUnsignedLeb128(&ptr_pos_);
- if (last_idx_ != 0 && method_.method_idx_delta_ == 0) {
- LOG(WARNING) << "Duplicate method in " << dex_file_.GetLocation();
- }
-}
-
EncodedArrayValueIterator::EncodedArrayValueIterator(const DexFile& dex_file,
const uint8_t* array_data)
: dex_file_(dex_file),
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index 25cd2f4..98787d1 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -238,9 +238,6 @@
}
}
- template <typename Visitor>
- void VisitMethods(const DexFile* dex_file, const Visitor& visitor) const;
-
private:
DISALLOW_COPY_AND_ASSIGN(ClassDef);
};
@@ -1174,211 +1171,6 @@
};
std::ostream& operator<<(std::ostream& os, const Signature& sig);
-// Iterate and decode class_data_item
-class ClassDataItemIterator {
- public:
- ClassDataItemIterator(const DexFile& dex_file, const uint8_t* raw_class_data_item)
- : dex_file_(dex_file), pos_(0), ptr_pos_(raw_class_data_item), last_idx_(0) {
- ReadClassDataHeader();
- if (EndOfInstanceFieldsPos() > 0) {
- ReadClassDataField();
- } else if (EndOfVirtualMethodsPos() > 0) {
- ReadClassDataMethod();
- }
- }
- uint32_t NumStaticFields() const {
- return header_.static_fields_size_;
- }
- uint32_t NumInstanceFields() const {
- return header_.instance_fields_size_;
- }
- uint32_t NumDirectMethods() const {
- return header_.direct_methods_size_;
- }
- uint32_t NumVirtualMethods() const {
- return header_.virtual_methods_size_;
- }
- bool IsAtMethod() const {
- return pos_ >= EndOfInstanceFieldsPos();
- }
- bool IsAtVirtualMethod() const {
- return pos_ >= EndOfDirectMethodsPos();
- }
- bool HasNextStaticField() const {
- return pos_ < EndOfStaticFieldsPos();
- }
- bool HasNextInstanceField() const {
- return pos_ >= EndOfStaticFieldsPos() && pos_ < EndOfInstanceFieldsPos();
- }
- bool HasNextDirectMethod() const {
- return pos_ >= EndOfInstanceFieldsPos() && pos_ < EndOfDirectMethodsPos();
- }
- bool HasNextVirtualMethod() const {
- return pos_ >= EndOfDirectMethodsPos() && pos_ < EndOfVirtualMethodsPos();
- }
- bool HasNextMethod() const {
- const bool result = pos_ >= EndOfInstanceFieldsPos() && pos_ < EndOfVirtualMethodsPos();
- DCHECK_EQ(result, HasNextDirectMethod() || HasNextVirtualMethod());
- return result;
- }
- void SkipStaticFields() {
- while (HasNextStaticField()) {
- Next();
- }
- }
- void SkipInstanceFields() {
- while (HasNextInstanceField()) {
- Next();
- }
- }
- void SkipAllFields() {
- SkipStaticFields();
- SkipInstanceFields();
- }
- void SkipDirectMethods() {
- while (HasNextDirectMethod()) {
- Next();
- }
- }
- void SkipVirtualMethods() {
- while (HasNextVirtualMethod()) {
- Next();
- }
- }
- bool HasNext() const {
- return pos_ < EndOfVirtualMethodsPos();
- }
- inline void Next() {
- pos_++;
- if (pos_ < EndOfStaticFieldsPos()) {
- last_idx_ = GetMemberIndex();
- ReadClassDataField();
- } else if (pos_ == EndOfStaticFieldsPos() && NumInstanceFields() > 0) {
- last_idx_ = 0; // transition to next array, reset last index
- ReadClassDataField();
- } else if (pos_ < EndOfInstanceFieldsPos()) {
- last_idx_ = GetMemberIndex();
- ReadClassDataField();
- } else if (pos_ == EndOfInstanceFieldsPos() && NumDirectMethods() > 0) {
- last_idx_ = 0; // transition to next array, reset last index
- ReadClassDataMethod();
- } else if (pos_ < EndOfDirectMethodsPos()) {
- last_idx_ = GetMemberIndex();
- ReadClassDataMethod();
- } else if (pos_ == EndOfDirectMethodsPos() && NumVirtualMethods() > 0) {
- last_idx_ = 0; // transition to next array, reset last index
- ReadClassDataMethod();
- } else if (pos_ < EndOfVirtualMethodsPos()) {
- last_idx_ = GetMemberIndex();
- ReadClassDataMethod();
- } else {
- DCHECK(!HasNext());
- }
- }
- uint32_t GetMemberIndex() const {
- if (pos_ < EndOfInstanceFieldsPos()) {
- return last_idx_ + field_.field_idx_delta_;
- } else {
- DCHECK_LT(pos_, EndOfVirtualMethodsPos());
- return last_idx_ + method_.method_idx_delta_;
- }
- }
- uint32_t GetRawMemberAccessFlags() const {
- if (pos_ < EndOfInstanceFieldsPos()) {
- return field_.access_flags_;
- } else {
- DCHECK_LT(pos_, EndOfVirtualMethodsPos());
- return method_.access_flags_;
- }
- }
- uint32_t GetFieldAccessFlags() const {
- return GetMemberAccessFlags() & kAccValidFieldFlags;
- }
- uint32_t GetMethodAccessFlags() const {
- return GetMemberAccessFlags() & kAccValidMethodFlags;
- }
- uint32_t GetMemberAccessFlags() const {
- return HiddenApiAccessFlags::RemoveFromDex(GetRawMemberAccessFlags());
- }
- HiddenApiAccessFlags::ApiList DecodeHiddenAccessFlags() const {
- return HiddenApiAccessFlags::DecodeFromDex(GetRawMemberAccessFlags());
- }
- bool MemberIsNative() const {
- return GetRawMemberAccessFlags() & kAccNative;
- }
- bool MemberIsFinal() const {
- return GetRawMemberAccessFlags() & kAccFinal;
- }
- ALWAYS_INLINE InvokeType GetMethodInvokeType(const DexFile::ClassDef& class_def) const;
- const DexFile::CodeItem* GetMethodCodeItem() const {
- return dex_file_.GetCodeItem(method_.code_off_);
- }
- uint32_t GetMethodCodeItemOffset() const {
- return method_.code_off_;
- }
- const uint8_t* DataPointer() const {
- return ptr_pos_;
- }
- const uint8_t* EndDataPointer() const {
- CHECK(!HasNext());
- return ptr_pos_;
- }
-
- private:
- // A dex file's class_data_item is leb128 encoded, this structure holds a decoded form of the
- // header for a class_data_item
- struct ClassDataHeader {
- uint32_t static_fields_size_; // the number of static fields
- uint32_t instance_fields_size_; // the number of instance fields
- uint32_t direct_methods_size_; // the number of direct methods
- uint32_t virtual_methods_size_; // the number of virtual methods
- } header_;
-
- // Read and decode header from a class_data_item stream into header
- void ReadClassDataHeader();
-
- uint32_t EndOfStaticFieldsPos() const {
- return header_.static_fields_size_;
- }
- uint32_t EndOfInstanceFieldsPos() const {
- return EndOfStaticFieldsPos() + header_.instance_fields_size_;
- }
- uint32_t EndOfDirectMethodsPos() const {
- return EndOfInstanceFieldsPos() + header_.direct_methods_size_;
- }
- uint32_t EndOfVirtualMethodsPos() const {
- return EndOfDirectMethodsPos() + header_.virtual_methods_size_;
- }
-
- // A decoded version of the field of a class_data_item
- struct ClassDataField {
- uint32_t field_idx_delta_; // delta of index into the field_ids array for FieldId
- uint32_t access_flags_; // access flags for the field
- ClassDataField() : field_idx_delta_(0), access_flags_(0) {}
- };
- ClassDataField field_;
-
- // Read and decode a field from a class_data_item stream into field
- void ReadClassDataField();
-
- // A decoded version of the method of a class_data_item
- struct ClassDataMethod {
- uint32_t method_idx_delta_; // delta of index into the method_ids array for MethodId
- uint32_t access_flags_;
- uint32_t code_off_;
- ClassDataMethod() : method_idx_delta_(0), access_flags_(0), code_off_(0) {}
- };
- ClassDataMethod method_;
-
- // Read and decode a method from a class_data_item stream into method
- void ReadClassDataMethod();
-
- const DexFile& dex_file_;
- size_t pos_; // integral number of items passed
- const uint8_t* ptr_pos_; // pointer into stream of class_data_item
- uint32_t last_idx_; // last read field or method index to apply delta to
-};
-
class EncodedArrayValueIterator {
public:
EncodedArrayValueIterator(const DexFile& dex_file, const uint8_t* array_data);
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
index be82fff..38f8455 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -42,9 +42,8 @@
static constexpr uint32_t kAccJavaFlagsMask = 0xffff; // bits set from Java sources (low 16)
-// The following flags are used to insert hidden API access flags into boot
-// class path dex files. They are decoded by DexFile::ClassDataItemIterator and
-// removed from the access flags before used by the runtime.
+// The following flags are used to insert hidden API access flags into boot class path dex files.
+// They are decoded by ClassAccessor and removed from the access flags before used by the runtime.
static constexpr uint32_t kAccDexHiddenBit = 0x00000020; // field, method (not native)
static constexpr uint32_t kAccDexHiddenBitNative = 0x00000200; // method (native)
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
index 6f49adf..c765345 100644
--- a/libprofile/profile/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -1183,7 +1183,7 @@
// (e.g. dex metadata files)
LOG(WARNING) << "Could not find entry " << kDexMetadataProfileEntry
<< " in the zip archive. Creating an empty profile.";
- source->reset(ProfileSource::Create(nullptr));
+ source->reset(ProfileSource::Create(MemMap::Invalid()));
return kProfileLoadSuccess;
}
if (zip_entry->GetUncompressedLength() == 0) {
@@ -1192,11 +1192,9 @@
}
// TODO(calin) pass along file names to assist with debugging.
- std::unique_ptr<MemMap> map(zip_entry->MapDirectlyOrExtract(kDexMetadataProfileEntry,
- "profile file",
- error));
+ MemMap map = zip_entry->MapDirectlyOrExtract(kDexMetadataProfileEntry, "profile file", error);
- if (map != nullptr) {
+ if (map.IsValid()) {
source->reset(ProfileSource::Create(std::move(map)));
return kProfileLoadSuccess;
} else {
@@ -1211,11 +1209,11 @@
const std::string& debug_stage,
std::string* error) {
if (IsMemMap()) {
- if (mem_map_cur_ + byte_count > mem_map_->Size()) {
+ if (mem_map_cur_ + byte_count > mem_map_.Size()) {
return kProfileLoadBadData;
}
for (size_t i = 0; i < byte_count; i++) {
- buffer[i] = *(mem_map_->Begin() + mem_map_cur_);
+ buffer[i] = *(mem_map_.Begin() + mem_map_cur_);
mem_map_cur_++;
}
} else {
@@ -1237,13 +1235,13 @@
bool ProfileCompilationInfo::ProfileSource::HasConsumedAllData() const {
return IsMemMap()
- ? (mem_map_ == nullptr || mem_map_cur_ == mem_map_->Size())
+ ? (!mem_map_.IsValid() || mem_map_cur_ == mem_map_.Size())
: (testEOF(fd_) == 0);
}
bool ProfileCompilationInfo::ProfileSource::HasEmptyContent() const {
if (IsMemMap()) {
- return mem_map_ == nullptr || mem_map_->Size() == 0;
+ return !mem_map_.IsValid() || mem_map_.Size() == 0;
} else {
struct stat stat_buffer;
if (fstat(fd_, &stat_buffer) != 0) {
diff --git a/libprofile/profile/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h
index 3596f3e..0dbf490 100644
--- a/libprofile/profile/profile_compilation_info.h
+++ b/libprofile/profile/profile_compilation_info.h
@@ -637,14 +637,14 @@
*/
static ProfileSource* Create(int32_t fd) {
DCHECK_GT(fd, -1);
- return new ProfileSource(fd, /*map*/ nullptr);
+ return new ProfileSource(fd, MemMap::Invalid());
}
/**
* Create a profile source backed by a memory map. The map can be null in
* which case it will the treated as an empty source.
*/
- static ProfileSource* Create(std::unique_ptr<MemMap>&& mem_map) {
+ static ProfileSource* Create(MemMap&& mem_map) {
return new ProfileSource(/*fd*/ -1, std::move(mem_map));
}
@@ -664,13 +664,13 @@
bool HasConsumedAllData() const;
private:
- ProfileSource(int32_t fd, std::unique_ptr<MemMap>&& mem_map)
+ ProfileSource(int32_t fd, MemMap&& mem_map)
: fd_(fd), mem_map_(std::move(mem_map)), mem_map_cur_(0) {}
bool IsMemMap() const { return fd_ == -1; }
int32_t fd_; // The fd is not owned by this class.
- std::unique_ptr<MemMap> mem_map_;
+ MemMap mem_map_;
size_t mem_map_cur_; // Current position in the map to read from.
};
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 9d73879..a5cc38b 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -708,7 +708,7 @@
return nullptr;
}
- std::unique_ptr<MemMap> mmap(MemMap::MapFile(
+ MemMap mmap = MemMap::MapFile(
file->GetLength(),
PROT_READ | PROT_WRITE,
MAP_PRIVATE,
@@ -716,13 +716,13 @@
/* start offset */ 0,
/* low_4gb */ false,
vdex_filename.c_str(),
- error_msg));
- if (mmap == nullptr) {
+ error_msg);
+ if (!mmap.IsValid()) {
*error_msg = "Failed to mmap file " + vdex_filename + ": " + *error_msg;
return nullptr;
}
- std::unique_ptr<VdexFile> vdex_file(new VdexFile(mmap.release()));
+ std::unique_ptr<VdexFile> vdex_file(new VdexFile(std::move(mmap)));
if (!vdex_file->IsValid()) {
*error_msg = "Vdex file is not valid";
return nullptr;
@@ -1578,7 +1578,7 @@
// The optimizing compiler outputs its CodeInfo data in the vmap table.
StackMapsHelper helper(oat_method.GetVmapTable(), instruction_set_);
if (AddStatsObject(oat_method.GetVmapTable())) {
- helper.GetCodeInfo().AddSizeStats(&stats_);
+ helper.GetCodeInfo().CollectSizeStats(oat_method.GetVmapTable(), &stats_);
}
const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code);
size_t offset = 0;
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index 2c28f06..4ee5101 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -171,7 +171,7 @@
// Code and dex code do not show up if list only.
expected_prefixes.push_back("DEX CODE:");
expected_prefixes.push_back("CODE:");
- expected_prefixes.push_back("InlineInfos");
+ expected_prefixes.push_back("InlineInfo");
}
if (mode == kModeArt) {
exec_argv.push_back("--image=" + core_art_location_);
diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc
index df002b6..8d0200c 100644
--- a/openjdkjvm/OpenjdkJvm.cc
+++ b/openjdkjvm/OpenjdkJvm.cc
@@ -187,7 +187,7 @@
}
JNIEXPORT int jio_vfprintf(FILE* fp, const char* fmt, va_list args) {
- assert(fp != NULL);
+ assert(fp != nullptr);
return vfprintf(fp, fmt, args);
}
@@ -203,7 +203,7 @@
JNIEXPORT jlong JVM_CurrentTimeMillis(JNIEnv* env ATTRIBUTE_UNUSED,
jclass clazz ATTRIBUTE_UNUSED) {
struct timeval tv;
- gettimeofday(&tv, (struct timezone *) NULL);
+ gettimeofday(&tv, (struct timezone *) nullptr);
jlong when = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
return when;
}
@@ -319,8 +319,8 @@
jstring javaFilename,
jobject javaLoader) {
ScopedUtfChars filename(env, javaFilename);
- if (filename.c_str() == NULL) {
- return NULL;
+ if (filename.c_str() == nullptr) {
+ return nullptr;
}
std::string error_msg;
@@ -348,7 +348,7 @@
art::ScopedObjectAccess soa(env);
art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
art::Thread* thread = art::Thread::FromManagedThread(soa, jthread);
- if (thread != NULL) {
+ if (thread != nullptr) {
thread->SetNativePriority(prio);
}
}
@@ -421,7 +421,7 @@
art::SuspendReason::kInternal,
&timed_out);
}
- if (thread != NULL) {
+ if (thread != nullptr) {
{
art::ScopedObjectAccess soa(env);
thread->SetThreadName(name.c_str());
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
index 2f24d7e..d20c756 100644
--- a/openjdkjvmti/deopt_manager.cc
+++ b/openjdkjvmti/deopt_manager.cc
@@ -40,6 +40,8 @@
#include "dex/dex_file_annotations.h"
#include "dex/modifiers.h"
#include "events-inl.h"
+#include "gc/heap.h"
+#include "gc/scoped_gc_critical_section.h"
#include "jit/jit.h"
#include "jni/jni_internal.h"
#include "mirror/class-inl.h"
@@ -266,29 +268,35 @@
deoptimization_status_lock_.ExclusiveUnlock(self);
}
+// Users should make sure that only gc-critical-section safe code is used while a
+// ScopedDeoptimizationContext exists.
class ScopedDeoptimizationContext : public art::ValueObject {
public:
ScopedDeoptimizationContext(art::Thread* self, DeoptManager* deopt)
RELEASE(deopt->deoptimization_status_lock_)
ACQUIRE(art::Locks::mutator_lock_)
ACQUIRE(art::Roles::uninterruptible_)
- : self_(self), deopt_(deopt), uninterruptible_cause_(nullptr) {
+ : self_(self),
+ deopt_(deopt),
+ critical_section_(self_, "JVMTI Deoptimizing methods"),
+ uninterruptible_cause_(nullptr) {
deopt_->WaitForDeoptimizationToFinishLocked(self_);
DCHECK(!deopt->performing_deoptimization_)
<< "Already performing deoptimization on another thread!";
// Use performing_deoptimization_ to keep track of the lock.
deopt_->performing_deoptimization_ = true;
deopt_->deoptimization_status_lock_.Unlock(self_);
+ uninterruptible_cause_ = critical_section_.Enter(art::gc::kGcCauseInstrumentation,
+ art::gc::kCollectorTypeCriticalSection);
art::Runtime::Current()->GetThreadList()->SuspendAll("JMVTI Deoptimizing methods",
/*long_suspend*/ false);
- uninterruptible_cause_ = self_->StartAssertNoThreadSuspension("JVMTI deoptimizing methods");
}
~ScopedDeoptimizationContext()
RELEASE(art::Locks::mutator_lock_)
RELEASE(art::Roles::uninterruptible_) {
// Can be suspended again.
- self_->EndAssertNoThreadSuspension(uninterruptible_cause_);
+ critical_section_.Exit(uninterruptible_cause_);
// Release the mutator lock.
art::Runtime::Current()->GetThreadList()->ResumeAll();
// Let other threads know it's fine to proceed.
@@ -300,6 +308,7 @@
private:
art::Thread* self_;
DeoptManager* deopt_;
+ art::gc::GCCriticalSection critical_section_;
const char* uninterruptible_cause_;
};
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index a660fb5..2ca87fd 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -79,11 +79,8 @@
const art::VdexFile* vdex = GetVdex(original_dex_file);
if (vdex != nullptr) {
vdex->UnquickenDexFile(new_dex_file, original_dex_file, /* decompile_return_instruction */true);
- } else {
- // The dex file isn't quickened since it is being used directly. We might still have hiddenapis
- // so we need to get rid of those.
- UnhideApis(new_dex_file);
}
+ UnhideApis(new_dex_file);
}
static void DCheckVerifyDexFile(const art::DexFile& dex) {
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 9bea18a..209add3 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -91,10 +91,8 @@
// Make the mmap
std::string error_msg;
art::ArrayRef<const unsigned char> final_data(final_dex_data, final_len);
- std::unique_ptr<art::MemMap> map(Redefiner::MoveDataToMemMap(orig_location,
- final_data,
- &error_msg));
- if (map.get() == nullptr) {
+ art::MemMap map = Redefiner::MoveDataToMemMap(orig_location, final_data, &error_msg);
+ if (!map.IsValid()) {
LOG(WARNING) << "Unable to allocate mmap for redefined dex file! Error was: " << error_msg;
self->ThrowOutOfMemoryError(StringPrintf(
"Unable to allocate dex file for transformation of %s", descriptor).c_str());
@@ -102,15 +100,15 @@
}
// Make a dex-file
- if (map->Size() < sizeof(art::DexFile::Header)) {
+ if (map.Size() < sizeof(art::DexFile::Header)) {
LOG(WARNING) << "Could not read dex file header because dex_data was too short";
art::ThrowClassFormatError(nullptr,
"Unable to read transformed dex file of %s",
descriptor);
return nullptr;
}
- uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map->Begin())->checksum_;
- std::string map_name = map->GetName();
+ uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map.Begin())->checksum_;
+ std::string map_name = map.GetName();
const art::ArtDexFileLoader dex_file_loader;
std::unique_ptr<const art::DexFile> dex_file(dex_file_loader.Open(map_name,
checksum,
diff --git a/openjdkjvmti/ti_class_definition.cc b/openjdkjvmti/ti_class_definition.cc
index dce2733..895e734 100644
--- a/openjdkjvmti/ti_class_definition.cc
+++ b/openjdkjvmti/ti_class_definition.cc
@@ -49,26 +49,27 @@
void ArtClassDefinition::InitializeMemory() const {
DCHECK(art::MemMap::kCanReplaceMapping);
VLOG(signals) << "Initializing de-quickened memory for dex file of " << name_;
- CHECK(dex_data_mmap_ != nullptr);
- CHECK(temp_mmap_ != nullptr);
- CHECK_EQ(dex_data_mmap_->GetProtect(), PROT_NONE);
- CHECK_EQ(temp_mmap_->GetProtect(), PROT_READ | PROT_WRITE);
+ CHECK(dex_data_mmap_.IsValid());
+ CHECK(temp_mmap_.IsValid());
+ CHECK_EQ(dex_data_mmap_.GetProtect(), PROT_NONE);
+ CHECK_EQ(temp_mmap_.GetProtect(), PROT_READ | PROT_WRITE);
std::string desc = std::string("L") + name_ + ";";
std::unique_ptr<FixedUpDexFile>
fixed_dex_file(FixedUpDexFile::Create(*initial_dex_file_unquickened_, desc.c_str()));
CHECK(fixed_dex_file.get() != nullptr);
- CHECK_LE(fixed_dex_file->Size(), temp_mmap_->Size());
- CHECK_EQ(temp_mmap_->Size(), dex_data_mmap_->Size());
+ CHECK_LE(fixed_dex_file->Size(), temp_mmap_.Size());
+ CHECK_EQ(temp_mmap_.Size(), dex_data_mmap_.Size());
// Copy the data to the temp mmap.
- memcpy(temp_mmap_->Begin(), fixed_dex_file->Begin(), fixed_dex_file->Size());
+ memcpy(temp_mmap_.Begin(), fixed_dex_file->Begin(), fixed_dex_file->Size());
// Move the mmap atomically.
- art::MemMap* source = temp_mmap_.release();
+ art::MemMap source;
+ source.swap(temp_mmap_);
std::string error;
- CHECK(dex_data_mmap_->ReplaceWith(&source, &error)) << "Failed to replace mmap for "
- << name_ << " because " << error;
- CHECK(dex_data_mmap_->Protect(PROT_READ));
+ CHECK(dex_data_mmap_.ReplaceWith(&source, &error)) << "Failed to replace mmap for "
+ << name_ << " because " << error;
+ CHECK(dex_data_mmap_.Protect(PROT_READ));
}
bool ArtClassDefinition::IsModified() const {
@@ -85,13 +86,13 @@
}
// The dex_data_ was never touched by the agents.
- if (dex_data_mmap_ != nullptr && dex_data_mmap_->GetProtect() == PROT_NONE) {
- if (current_dex_file_.data() == dex_data_mmap_->Begin()) {
+ if (dex_data_mmap_.IsValid() && dex_data_mmap_.GetProtect() == PROT_NONE) {
+ if (current_dex_file_.data() == dex_data_mmap_.Begin()) {
// the dex_data_ looks like it changed (not equal to current_dex_file_) but we never
// initialized the dex_data_mmap_. This means the new_dex_data was filled in without looking
// at the initial dex_data_.
return true;
- } else if (dex_data_.data() == dex_data_mmap_->Begin()) {
+ } else if (dex_data_.data() == dex_data_mmap_.Begin()) {
// The dex file used to have modifications but they were not added again.
return true;
} else {
@@ -244,26 +245,24 @@
std::string mmap_name("anon-mmap-for-redefine: ");
mmap_name += name_;
std::string error;
- dex_data_mmap_.reset(art::MemMap::MapAnonymous(mmap_name.c_str(),
- nullptr,
- dequick_size,
- PROT_NONE,
- /*low_4gb*/ false,
- /*reuse*/ false,
- &error));
- mmap_name += "-TEMP";
- temp_mmap_.reset(art::MemMap::MapAnonymous(mmap_name.c_str(),
- nullptr,
+ dex_data_mmap_ = art::MemMap::MapAnonymous(mmap_name.c_str(),
+ /* addr */ nullptr,
dequick_size,
- PROT_READ | PROT_WRITE,
+ PROT_NONE,
/*low_4gb*/ false,
- /*reuse*/ false,
- &error));
- if (UNLIKELY(dex_data_mmap_ != nullptr && temp_mmap_ != nullptr)) {
+ &error);
+ mmap_name += "-TEMP";
+ temp_mmap_ = art::MemMap::MapAnonymous(mmap_name.c_str(),
+ /* addr */ nullptr,
+ dequick_size,
+ PROT_READ | PROT_WRITE,
+ /*low_4gb*/ false,
+ &error);
+ if (UNLIKELY(dex_data_mmap_.IsValid() && temp_mmap_.IsValid())) {
// Need to save the initial dexfile so we don't need to search for it in the fault-handler.
initial_dex_file_unquickened_ = quick_dex;
- dex_data_ = art::ArrayRef<const unsigned char>(dex_data_mmap_->Begin(),
- dex_data_mmap_->Size());
+ dex_data_ = art::ArrayRef<const unsigned char>(dex_data_mmap_.Begin(),
+ dex_data_mmap_.Size());
if (from_class_ext_) {
// We got initial from class_ext so the current one must have undergone redefinition so no
// cdex or quickening stuff.
@@ -275,14 +274,14 @@
// This class hasn't been redefined before. The dequickened current data is the same as the
// dex_data_mmap_ when it's filled it. We don't need to copy anything because the mmap will
// not be cleared until after everything is done.
- current_dex_file_ = art::ArrayRef<const unsigned char>(dex_data_mmap_->Begin(),
+ current_dex_file_ = art::ArrayRef<const unsigned char>(dex_data_mmap_.Begin(),
dequick_size);
}
return;
}
}
- dex_data_mmap_.reset(nullptr);
- temp_mmap_.reset(nullptr);
+ dex_data_mmap_.Reset();
+ temp_mmap_.Reset();
// Failed to mmap a large enough area (or on-demand dequickening was disabled). This is
// unfortunate. Since currently the size is just a guess though we might as well try to do it
// manually.
diff --git a/openjdkjvmti/ti_class_definition.h b/openjdkjvmti/ti_class_definition.h
index f888a74..224e664 100644
--- a/openjdkjvmti/ti_class_definition.h
+++ b/openjdkjvmti/ti_class_definition.h
@@ -56,8 +56,8 @@
loader_(nullptr),
name_(),
protection_domain_(nullptr),
- dex_data_mmap_(nullptr),
- temp_mmap_(nullptr),
+ dex_data_mmap_(),
+ temp_mmap_(),
dex_data_memory_(),
initial_dex_file_unquickened_(nullptr),
dex_data_(),
@@ -100,9 +100,9 @@
}
bool ContainsAddress(uintptr_t ptr) const {
- return dex_data_mmap_ != nullptr &&
- reinterpret_cast<uintptr_t>(dex_data_mmap_->Begin()) <= ptr &&
- reinterpret_cast<uintptr_t>(dex_data_mmap_->End()) > ptr;
+ return dex_data_mmap_.IsValid() &&
+ reinterpret_cast<uintptr_t>(dex_data_mmap_.Begin()) <= ptr &&
+ reinterpret_cast<uintptr_t>(dex_data_mmap_.End()) > ptr;
}
bool IsModified() const REQUIRES_SHARED(art::Locks::mutator_lock_);
@@ -128,9 +128,9 @@
bool IsLazyDefinition() const {
DCHECK(IsInitialized());
- return dex_data_mmap_ != nullptr &&
- dex_data_.data() == dex_data_mmap_->Begin() &&
- dex_data_mmap_->GetProtect() == PROT_NONE;
+ return dex_data_mmap_.IsValid() &&
+ dex_data_.data() == dex_data_mmap_.Begin() &&
+ dex_data_mmap_.GetProtect() == PROT_NONE;
}
jobject GetProtectionDomain() const {
@@ -159,9 +159,9 @@
// Mmap that will be filled with the original-dex-file lazily if it needs to be de-quickened or
// de-compact-dex'd
- mutable std::unique_ptr<art::MemMap> dex_data_mmap_;
+ mutable art::MemMap dex_data_mmap_;
// This is a temporary mmap we will use to be able to fill the dex file data atomically.
- mutable std::unique_ptr<art::MemMap> temp_mmap_;
+ mutable art::MemMap temp_mmap_;
// A unique_ptr to the current dex_data if it needs to be cleaned up.
std::vector<unsigned char> dex_data_memory_;
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 1476880..8707e27 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -300,24 +300,22 @@
}
// Moves dex data to an anonymous, read-only mmap'd region.
-std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& original_location,
- art::ArrayRef<const unsigned char> data,
- std::string* error_msg) {
- std::unique_ptr<art::MemMap> map(art::MemMap::MapAnonymous(
+art::MemMap Redefiner::MoveDataToMemMap(const std::string& original_location,
+ art::ArrayRef<const unsigned char> data,
+ std::string* error_msg) {
+ art::MemMap map = art::MemMap::MapAnonymous(
StringPrintf("%s-transformed", original_location.c_str()).c_str(),
- nullptr,
+ /* addr */ nullptr,
data.size(),
PROT_READ|PROT_WRITE,
- /*low_4gb*/false,
- /*reuse*/false,
- error_msg));
- if (map == nullptr) {
- return map;
+ /*low_4gb*/ false,
+ error_msg);
+ if (LIKELY(map.IsValid())) {
+ memcpy(map.Begin(), data.data(), data.size());
+ // Make the dex files mmap read only. This matches how other DexFiles are mmaped and prevents
+ // programs from corrupting it.
+ map.Protect(PROT_READ);
}
- memcpy(map->Begin(), data.data(), data.size());
- // Make the dex files mmap read only. This matches how other DexFiles are mmaped and prevents
- // programs from corrupting it.
- map->Protect(PROT_READ);
return map;
}
@@ -368,17 +366,6 @@
if (res != OK) {
return res;
}
- // We make a copy of the class_bytes to pass into the retransformation.
- // This makes cleanup easier (since we unambiguously own the bytes) and also is useful since we
- // will need to keep the original bytes around unaltered for subsequent RetransformClasses calls
- // to get the passed in bytes.
- unsigned char* class_bytes_copy = nullptr;
- res = env->Allocate(definitions[i].class_byte_count, &class_bytes_copy);
- if (res != OK) {
- return res;
- }
- memcpy(class_bytes_copy, definitions[i].class_bytes, definitions[i].class_byte_count);
-
ArtClassDefinition def;
res = def.Init(self, definitions[i]);
if (res != OK) {
@@ -440,23 +427,22 @@
}
JvmtiUniquePtr<char> generic_unique_ptr(MakeJvmtiUniquePtr(env, generic_ptr_unused));
JvmtiUniquePtr<char> signature_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr));
- std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location,
- def.GetDexData(),
- error_msg_));
+ art::MemMap map = MoveDataToMemMap(original_dex_location, def.GetDexData(), error_msg_);
std::ostringstream os;
- if (map.get() == nullptr) {
+ if (!map.IsValid()) {
os << "Failed to create anonymous mmap for modified dex file of class " << def.GetName()
<< "in dex file " << original_dex_location << " because: " << *error_msg_;
*error_msg_ = os.str();
return ERR(OUT_OF_MEMORY);
}
- if (map->Size() < sizeof(art::DexFile::Header)) {
+ if (map.Size() < sizeof(art::DexFile::Header)) {
*error_msg_ = "Could not read dex file header because dex_data was too short";
return ERR(INVALID_CLASS_FORMAT);
}
- uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map->Begin())->checksum_;
+ std::string name = map.GetName();
+ uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map.Begin())->checksum_;
const art::ArtDexFileLoader dex_file_loader;
- std::unique_ptr<const art::DexFile> dex_file(dex_file_loader.Open(map->GetName(),
+ std::unique_ptr<const art::DexFile> dex_file(dex_file_loader.Open(name,
checksum,
std::move(map),
/*verify*/true,
diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h
index 6d8f6bf..f4a4280 100644
--- a/openjdkjvmti/ti_redefine.h
+++ b/openjdkjvmti/ti_redefine.h
@@ -78,9 +78,9 @@
static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
- static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location,
- art::ArrayRef<const unsigned char> data,
- std::string* error_msg);
+ static art::MemMap MoveDataToMemMap(const std::string& original_location,
+ art::ArrayRef<const unsigned char> data,
+ std::string* error_msg);
// Helper for checking if redefinition/retransformation is allowed.
static jvmtiError GetClassRedefinitionError(jclass klass, /*out*/std::string* error_msg)
diff --git a/openjdkjvmti/ti_timers.cc b/openjdkjvmti/ti_timers.cc
index 24fb041..11b58c4 100644
--- a/openjdkjvmti/ti_timers.cc
+++ b/openjdkjvmti/ti_timers.cc
@@ -83,7 +83,7 @@
// No CLOCK_MONOTONIC support on older Mac OS.
struct timeval t;
t.tv_sec = t.tv_usec = 0;
- gettimeofday(&t, NULL);
+ gettimeofday(&t, nullptr);
*nanos_ptr = static_cast<jlong>(t.tv_sec)*1000000000LL + static_cast<jlong>(t.tv_usec)*1000LL;
#endif
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index a15f7b8..8169979 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -538,7 +538,7 @@
ScopedObjectAccess soa(Thread::Current());
std::vector<gc::space::ImageSpace*> spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
- std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>> space_to_memmap_map;
+ std::map<gc::space::ImageSpace*, MemMap> space_to_memmap_map;
for (size_t i = 0; i < spaces.size(); ++i) {
t.NewTiming("Image Patching setup");
@@ -567,15 +567,15 @@
// Create the map where we will write the image patches to.
std::string error_msg;
- std::unique_ptr<MemMap> image(MemMap::MapFile(image_len,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE,
- input_image->Fd(),
- 0,
- /*low_4gb*/false,
- input_image->GetPath().c_str(),
- &error_msg));
- if (image.get() == nullptr) {
+ MemMap image = MemMap::MapFile(image_len,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ input_image->Fd(),
+ 0,
+ /*low_4gb*/false,
+ input_image->GetPath().c_str(),
+ &error_msg);
+ if (!image.IsValid()) {
LOG(ERROR) << "Unable to map image file " << input_image->GetPath() << " : " << error_msg;
return false;
}
@@ -583,7 +583,7 @@
space_to_memmap_map.emplace(space, std::move(image));
PatchOat p = PatchOat(isa,
- space_to_memmap_map[space].get(),
+ &space_to_memmap_map[space],
space->GetLiveBitmap(),
space->GetMemMap(),
delta,
@@ -636,22 +636,22 @@
LOG(ERROR) << "Error while getting input image size";
return false;
}
- std::unique_ptr<MemMap> original(MemMap::MapFile(input_image_size,
- PROT_READ,
- MAP_PRIVATE,
- input_image->Fd(),
- 0,
- /*low_4gb*/false,
- input_image->GetPath().c_str(),
- &error_msg));
- if (original.get() == nullptr) {
+ MemMap original = MemMap::MapFile(input_image_size,
+ PROT_READ,
+ MAP_PRIVATE,
+ input_image->Fd(),
+ 0,
+ /*low_4gb*/false,
+ input_image->GetPath().c_str(),
+ &error_msg);
+ if (!original.IsValid()) {
LOG(ERROR) << "Unable to map image file " << input_image->GetPath() << " : " << error_msg;
return false;
}
const MemMap* relocated = p.image_;
- if (!WriteRelFile(*original, *relocated, image_relocation_filename, &error_msg)) {
+ if (!WriteRelFile(original, *relocated, image_relocation_filename, &error_msg)) {
LOG(ERROR) << "Failed to create image relocation file " << image_relocation_filename
<< ": " << error_msg;
return false;
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 2b1210b..ac2fdf5 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -74,7 +74,7 @@
// All pointers are only borrowed.
PatchOat(InstructionSet isa, MemMap* image,
gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta,
- std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>>* map, TimingLogger* timings)
+ std::map<gc::space::ImageSpace*, MemMap>* map, TimingLogger* timings)
: image_(image), bitmap_(bitmap), heap_(heap),
delta_(delta), isa_(isa), space_map_(map), timings_(timings) {}
@@ -139,7 +139,7 @@
if (image_space->Contains(obj)) {
uintptr_t heap_off = reinterpret_cast<uintptr_t>(obj) -
reinterpret_cast<uintptr_t>(image_space->GetMemMap()->Begin());
- return reinterpret_cast<T*>(space_map_->find(image_space)->second->Begin() + heap_off);
+ return reinterpret_cast<T*>(space_map_->find(image_space)->second.Begin() + heap_off);
}
}
LOG(FATAL) << "Did not find object in boot image space " << obj;
@@ -195,7 +195,7 @@
// Active instruction set, used to know the entrypoint size.
const InstructionSet isa_;
- const std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>>* space_map_;
+ const std::map<gc::space::ImageSpace*, MemMap>* space_map_;
TimingLogger* timings_;
diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h
index 7123ae7..9a01656 100644
--- a/runtime/arch/arm/asm_support_arm.h
+++ b/runtime/arch/arm/asm_support_arm.h
@@ -37,6 +37,9 @@
// The offset from art_quick_read_barrier_mark_introspection to the array switch cases,
// i.e. art_quick_read_barrier_mark_introspection_arrays.
#define BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET 0x100
+// The offset from art_quick_read_barrier_mark_introspection to the entrypoint for the
+// UnsafeCASObject intrinsic, i.e. art_quick_read_barrier_mark_introspection_unsafe_cas.
+#define BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ENTRYPOINT_OFFSET 0x180
// The offset of the reference load LDR from the return address in LR for field loads.
#ifdef USE_HEAP_POISONING
@@ -55,5 +58,7 @@
// The offset of the reference load LDR from the return address in LR for GC root loads.
#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET -8
#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET -6
+// The offset of the ADD from the return address in LR for UnsafeCASObject intrinsic.
+#define BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ADD_OFFSET -8
#endif // ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index b4e9036..2c5465e 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -59,6 +59,7 @@
extern "C" mirror::Object* art_quick_read_barrier_mark_introspection_gc_roots_wide(mirror::Object*);
extern "C" mirror::Object* art_quick_read_barrier_mark_introspection_gc_roots_narrow(
mirror::Object*);
+extern "C" mirror::Object* art_quick_read_barrier_mark_introspection_unsafe_cas(mirror::Object*);
// Used by soft float.
// Single-precision FP arithmetics.
@@ -113,6 +114,10 @@
reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection_gc_roots_narrow) -
reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection);
DCHECK_EQ(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET, gc_roots_narrow_diff);
+ intptr_t unsafe_cas_diff =
+ reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection_unsafe_cas) -
+ reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection);
+ DCHECK_EQ(BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ENTRYPOINT_OFFSET, unsafe_cas_diff);
// The register 12, i.e. IP, is reserved, so there is no art_quick_read_barrier_mark_reg12.
// We're using the entry to hold a pointer to the introspection entrypoint instead.
qpoints->pReadBarrierMarkReg12 =
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index c86baa1..1153a77 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -656,18 +656,21 @@
END art_quick_osr_stub
/*
- * On entry r0 is uint32_t* gprs_ and r1 is uint32_t* fprs_
+ * On entry r0 is uint32_t* gprs_ and r1 is uint32_t* fprs_.
+ * Both must reside on the stack, between current SP and target SP.
+ * The r12 (IP) shall be clobbered rather than retrieved from gprs_.
*/
ARM_ENTRY art_quick_do_long_jump
- vldm r1, {s0-s31} @ load all fprs from argument fprs_
- ldr r2, [r0, #60] @ r2 = r15 (PC from gprs_ 60=4*15)
- ldr r14, [r0, #56] @ (LR from gprs_ 56=4*14)
- add r0, r0, #12 @ increment r0 to skip gprs_[0..2] 12=4*3
- ldm r0, {r3-r13} @ load remaining gprs from argument gprs_
+ vldm r1, {s0-s31} @ Load all fprs from argument fprs_.
+ mov sp, r0 @ Make SP point to gprs_.
+ @ Do not access fprs_ from now, they may be below SP.
+ ldm sp, {r0-r11} @ load r0-r11 from gprs_.
+ ldr r12, [sp, #60] @ Load the value of PC (r15) from gprs_ (60 = 4 * 15) into IP (r12).
+ ldr lr, [sp, #56] @ Load LR from gprs_, 56 = 4 * 14.
+ ldr sp, [sp, #52] @ Load SP from gprs_ 52 = 4 * 13.
+ @ Do not access gprs_ from now, they are below SP.
REFRESH_MARKING_REGISTER
- ldr r0, [r0, #-12] @ load r0 value
- mov r1, #0 @ clear result register r1
- bx r2 @ do long jump
+ bx r12 @ Do long jump.
END art_quick_do_long_jump
/*
@@ -2575,6 +2578,12 @@
* The thunk also performs all the fast-path checks, so we need just the
* slow path.
*
+ * The UnsafeCASObject intrinsic is similar to the GC roots wide approach
+ * but using ADD (register, T3) instead of the LDR (immediate, T3), so the
+ * destination register is in bits 8-11 rather than 12-15. Therefore it has
+ * its own entrypoint, art_quick_read_barrier_mark_introspection_unsafe_cas
+ * at the offset BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ENTRYPOINT_OFFSET.
+ *
* The code structure is
* art_quick_read_barrier_mark_introspection: // @0x00
* Up to 32 bytes code for main entrypoint fast-path code for fields
@@ -2610,6 +2619,9 @@
* return switch.
* art_quick_read_barrier_mark_introspection_arrays: // @0x100
* Exactly 128 bytes for array load switch cases (16x2 instructions).
+ * art_quick_read_barrier_mark_introspection_unsafe_cas: // @0x180
+ * UnsafeCASObject intrinsic entrypoint for ADD (register) encoding T3
+ * (6 bytes). Loads the return register and jumps to the runtime call.
*/
#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
.balign 512
@@ -2669,7 +2681,6 @@
BRBMI_RUNTIME_CALL
b .Lmark_introspection_return_switch
-
.balign 256
.thumb_func
.type art_quick_read_barrier_mark_introspection_arrays, #function
@@ -2677,6 +2688,19 @@
.global art_quick_read_barrier_mark_introspection_arrays
art_quick_read_barrier_mark_introspection_arrays:
BRBMI_FOR_REGISTERS BRBMI_ARRAY_LOAD, BRBMI_BKPT_FILL_8B
+
+ .balign 8
+ .thumb_func
+ .type art_quick_read_barrier_mark_introspection_unsafe_cas, #function
+ .hidden art_quick_read_barrier_mark_introspection_unsafe_cas
+ .global art_quick_read_barrier_mark_introspection_unsafe_cas
+art_quick_read_barrier_mark_introspection_unsafe_cas:
+ // Load the byte of the ADD instruction that contains Rd. Adjust for the thumb state in LR.
+ // The ADD (register, T3) is |11101011000|S|Rn|(0)imm3|Rd|imm2|type|Rm| and we're using
+ // no shift (type=0, imm2=0, imm3=0), so the byte we read here, i.e. |(0)imm3|Rd|,
+ // contains only the register number, the top 4 bits are 0.
+ ldrb rMR, [lr, #(-1 + BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ADD_OFFSET + 3)]
+ b .Lmark_introspection_runtime_call
END art_quick_read_barrier_mark_introspection
#else // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
ENTRY art_quick_read_barrier_mark_introspection
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 8b77453..2622d40 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1091,56 +1091,59 @@
END art_quick_osr_stub
/*
- * On entry x0 is uintptr_t* gprs_ and x1 is uint64_t* fprs_
+ * On entry x0 is uintptr_t* gprs_ and x1 is uint64_t* fprs_.
+ * Both must reside on the stack, between current SP and target SP.
+ * IP0 and IP1 shall be clobbered rather than retrieved from gprs_.
*/
ENTRY art_quick_do_long_jump
// Load FPRs
- ldp d0, d1, [x1], #16
- ldp d2, d3, [x1], #16
- ldp d4, d5, [x1], #16
- ldp d6, d7, [x1], #16
- ldp d8, d9, [x1], #16
- ldp d10, d11, [x1], #16
- ldp d12, d13, [x1], #16
- ldp d14, d15, [x1], #16
- ldp d16, d17, [x1], #16
- ldp d18, d19, [x1], #16
- ldp d20, d21, [x1], #16
- ldp d22, d23, [x1], #16
- ldp d24, d25, [x1], #16
- ldp d26, d27, [x1], #16
- ldp d28, d29, [x1], #16
- ldp d30, d31, [x1]
+ ldp d0, d1, [x1, #0]
+ ldp d2, d3, [x1, #16]
+ ldp d4, d5, [x1, #32]
+ ldp d6, d7, [x1, #48]
+ ldp d8, d9, [x1, #64]
+ ldp d10, d11, [x1, #80]
+ ldp d12, d13, [x1, #96]
+ ldp d14, d15, [x1, #112]
+ ldp d16, d17, [x1, #128]
+ ldp d18, d19, [x1, #144]
+ ldp d20, d21, [x1, #160]
+ ldp d22, d23, [x1, #176]
+ ldp d24, d25, [x1, #192]
+ ldp d26, d27, [x1, #208]
+ ldp d28, d29, [x1, #224]
+ ldp d30, d31, [x1, #240]
- // Load GPRs
- // TODO: lots of those are smashed, could optimize.
- add x0, x0, #30*8
- ldp x30, x1, [x0], #-16 // LR & SP
- ldp x28, x29, [x0], #-16
- ldp x26, x27, [x0], #-16
- ldp x24, x25, [x0], #-16
- ldp x22, x23, [x0], #-16
- ldp x20, x21, [x0], #-16
- ldp x18, x19, [x0], #-16 // X18 & xSELF
- ldp x16, x17, [x0], #-16
- ldp x14, x15, [x0], #-16
- ldp x12, x13, [x0], #-16
- ldp x10, x11, [x0], #-16
- ldp x8, x9, [x0], #-16
- ldp x6, x7, [x0], #-16
- ldp x4, x5, [x0], #-16
- ldp x2, x3, [x0], #-16
- mov sp, x1
+ // Load GPRs. Delay loading x0, x1 because x0 is used as gprs_.
+ ldp x2, x3, [x0, #16]
+ ldp x4, x5, [x0, #32]
+ ldp x6, x7, [x0, #48]
+ ldp x8, x9, [x0, #64]
+ ldp x10, x11, [x0, #80]
+ ldp x12, x13, [x0, #96]
+ ldp x14, x15, [x0, #112]
+ // Do not load IP0 (x16) and IP1 (x17), these shall be clobbered below.
+ ldp x18, x19, [x0, #144] // X18 and xSELF.
+ ldp x20, x21, [x0, #160] // For Baker RB, wMR (w20) is reloaded below.
+ ldp x22, x23, [x0, #176]
+ ldp x24, x25, [x0, #192]
+ ldp x26, x27, [x0, #208]
+ ldp x28, x29, [x0, #224]
+ ldp x30, xIP0, [x0, #240] // LR and SP, load SP to IP0.
+
+ // Load PC to IP1, it's at the end (after the space for the unused XZR).
+ ldr xIP1, [x0, #33*8]
+
+ // Load x0, x1.
+ ldp x0, x1, [x0, #0]
+
+ // Set SP. Do not access fprs_ and gprs_ from now, they are below SP.
+ mov sp, xIP0
REFRESH_MARKING_REGISTER
- // Need to load PC, it's at the end (after the space for the unused XZR). Use x1.
- ldr x1, [x0, #33*8]
- // And the value of x0.
- ldr x0, [x0]
-
- br x1
+ br xIP1
END art_quick_do_long_jump
/*
@@ -2785,6 +2788,8 @@
* the root register to IP0 and jumps to the customized entrypoint,
* art_quick_read_barrier_mark_introspection_gc_roots. The thunk also
* performs all the fast-path checks, so we need just the slow path.
+ * The UnsafeCASObject intrinsic is also using the GC root entrypoint with
+ * MOV instead of LDR, the destination register is in the same bits.
*
* The code structure is
* art_quick_read_barrier_mark_introspection:
diff --git a/runtime/base/mem_map_arena_pool.cc b/runtime/base/mem_map_arena_pool.cc
index 702f0e4..a9fbafe 100644
--- a/runtime/base/mem_map_arena_pool.cc
+++ b/runtime/base/mem_map_arena_pool.cc
@@ -38,22 +38,33 @@
void Release() OVERRIDE;
private:
- std::unique_ptr<MemMap> map_;
+ static MemMap Allocate(size_t size, bool low_4gb, const char* name);
+
+ MemMap map_;
};
-MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) {
+MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name)
+ : map_(Allocate(size, low_4gb, name)) {
+ memory_ = map_.Begin();
+ static_assert(ArenaAllocator::kArenaAlignment <= kPageSize,
+ "Arena should not need stronger alignment than kPageSize.");
+ DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
+ size_ = map_.Size();
+}
+
+MemMap MemMapArena::Allocate(size_t size, bool low_4gb, const char* name) {
// Round up to a full page as that's the smallest unit of allocation for mmap()
// and we want to be able to use all memory that we actually allocate.
size = RoundUp(size, kPageSize);
std::string error_msg;
- map_.reset(MemMap::MapAnonymous(
- name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg));
- CHECK(map_.get() != nullptr) << error_msg;
- memory_ = map_->Begin();
- static_assert(ArenaAllocator::kArenaAlignment <= kPageSize,
- "Arena should not need stronger alignment than kPageSize.");
- DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
- size_ = map_->Size();
+ MemMap map = MemMap::MapAnonymous(name,
+ /* addr */ nullptr,
+ size,
+ PROT_READ | PROT_WRITE,
+ low_4gb,
+ &error_msg);
+ CHECK(map.IsValid()) << error_msg;
+ return map;
}
MemMapArena::~MemMapArena() {
@@ -62,7 +73,7 @@
void MemMapArena::Release() {
if (bytes_allocated_ > 0) {
- map_->MadviseDontNeedAndZero();
+ map_.MadviseDontNeedAndZero();
bytes_allocated_ = 0;
}
}
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 7b888b1..044c4c2 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -1142,10 +1142,6 @@
DCHECK(subtype_check_lock_ == nullptr);
subtype_check_lock_ = new Mutex("SubtypeCheck lock", current_lock_level);
- UPDATE_CURRENT_LOCK_LEVEL(kCHALock);
- DCHECK(cha_lock_ == nullptr);
- cha_lock_ = new Mutex("CHA lock", current_lock_level);
-
UPDATE_CURRENT_LOCK_LEVEL(kClassLinkerClassesLock);
DCHECK(classlinker_classes_lock_ == nullptr);
classlinker_classes_lock_ = new ReaderWriterMutex("ClassLinker classes lock",
@@ -1226,6 +1222,10 @@
DCHECK(custom_tls_lock_ == nullptr);
custom_tls_lock_ = new Mutex("Thread::custom_tls_ lock", current_lock_level);
+ UPDATE_CURRENT_LOCK_LEVEL(kCHALock);
+ DCHECK(cha_lock_ == nullptr);
+ cha_lock_ = new Mutex("CHA lock", current_lock_level);
+
UPDATE_CURRENT_LOCK_LEVEL(kNativeDebugInterfaceLock);
DCHECK(native_debug_interface_lock_ == nullptr);
native_debug_interface_lock_ = new Mutex("Native debug interface lock", current_lock_level);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index af2e7b2..fba209a 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -72,6 +72,7 @@
kJdwpSocketLock,
kRegionSpaceRegionLock,
kMarkSweepMarkStackLock,
+ kCHALock,
kJitCodeCacheLock,
kRosAllocGlobalLock,
kRosAllocBracketLock,
@@ -109,7 +110,6 @@
kMonitorPoolLock,
kClassLinkerClassesLock, // TODO rename.
kDexToDexCompilerLock,
- kCHALock,
kSubtypeCheckLock,
kBreakpointLock,
kMonitorLock,
@@ -661,14 +661,11 @@
// TODO: improve name, perhaps instrumentation_update_lock_.
static Mutex* deoptimization_lock_ ACQUIRED_AFTER(alloc_tracker_lock_);
- // Guards Class Hierarchy Analysis (CHA).
- static Mutex* cha_lock_ ACQUIRED_AFTER(deoptimization_lock_);
-
// Guard the update of the SubtypeCheck data stores in each Class::status_ field.
// This lock is used in SubtypeCheck methods which are the interface for
// any SubtypeCheck-mutating methods.
// In Class::IsSubClass, the lock is not required since it does not update the SubtypeCheck data.
- static Mutex* subtype_check_lock_ ACQUIRED_AFTER(cha_lock_);
+ static Mutex* subtype_check_lock_ ACQUIRED_AFTER(deoptimization_lock_);
// The thread_list_lock_ guards ThreadList::list_. It is also commonly held to stop threads
// attaching and detaching.
@@ -745,11 +742,14 @@
// GetThreadLocalStorage.
static Mutex* custom_tls_lock_ ACQUIRED_AFTER(jni_function_table_lock_);
+ // Guards Class Hierarchy Analysis (CHA).
+ static Mutex* cha_lock_ ACQUIRED_AFTER(custom_tls_lock_);
+
// When declaring any Mutex add BOTTOM_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code
// doesn't try to acquire a higher level Mutex. NB Due to the way the annotalysis works this
// actually only encodes the mutex being below jni_function_table_lock_ although having
// kGenericBottomLock level is lower than this.
- #define BOTTOM_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(art::Locks::custom_tls_lock_)
+ #define BOTTOM_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(art::Locks::cha_lock_)
// Have an exclusive aborting thread.
static Mutex* abort_lock_ ACQUIRED_AFTER(custom_tls_lock_);
diff --git a/runtime/cha.cc b/runtime/cha.cc
index ccbe066..ce84e8c 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -636,38 +636,54 @@
// We do this under cha_lock_. Committing code also grabs this lock to
// make sure the code is only committed when all single-implementation
// assumptions are still true.
- MutexLock cha_mu(self, *Locks::cha_lock_);
- // Invalidate compiled methods that assume some virtual calls have only
- // single implementations.
- for (ArtMethod* invalidated : invalidated_single_impl_methods) {
- if (!invalidated->HasSingleImplementation()) {
- // It might have been invalidated already when other class linking is
- // going on.
- continue;
- }
- invalidated->SetHasSingleImplementation(false);
- if (invalidated->IsAbstract()) {
- // Clear the single implementation method.
- invalidated->SetSingleImplementation(nullptr, image_pointer_size);
- }
+ std::vector<std::pair<ArtMethod*, OatQuickMethodHeader*>> headers;
+ {
+ MutexLock cha_mu(self, *Locks::cha_lock_);
+ // Invalidate compiled methods that assume some virtual calls have only
+ // single implementations.
+ for (ArtMethod* invalidated : invalidated_single_impl_methods) {
+ if (!invalidated->HasSingleImplementation()) {
+ // It might have been invalidated already when other class linking is
+ // going on.
+ continue;
+ }
+ invalidated->SetHasSingleImplementation(false);
+ if (invalidated->IsAbstract()) {
+ // Clear the single implementation method.
+ invalidated->SetSingleImplementation(nullptr, image_pointer_size);
+ }
- if (runtime->IsAotCompiler()) {
- // No need to invalidate any compiled code as the AotCompiler doesn't
- // run any code.
- continue;
- }
+ if (runtime->IsAotCompiler()) {
+ // No need to invalidate any compiled code as the AotCompiler doesn't
+ // run any code.
+ continue;
+ }
- // Invalidate all dependents.
- for (const auto& dependent : GetDependents(invalidated)) {
- ArtMethod* method = dependent.first;;
- OatQuickMethodHeader* method_header = dependent.second;
- VLOG(class_linker) << "CHA invalidated compiled code for " << method->PrettyMethod();
- DCHECK(runtime->UseJitCompilation());
- runtime->GetJit()->GetCodeCache()->InvalidateCompiledCodeFor(
- method, method_header);
- dependent_method_headers.insert(method_header);
+ // Invalidate all dependents.
+ for (const auto& dependent : GetDependents(invalidated)) {
+ ArtMethod* method = dependent.first;;
+ OatQuickMethodHeader* method_header = dependent.second;
+ VLOG(class_linker) << "CHA invalidated compiled code for " << method->PrettyMethod();
+ DCHECK(runtime->UseJitCompilation());
+ // We need to call JitCodeCache::InvalidateCompiledCodeFor but we cannot do it here
+ // since it would run into problems with lock-ordering. We don't want to re-order the
+ // locks since that would make code-commit racy.
+ headers.push_back({method, method_header});
+ dependent_method_headers.insert(method_header);
+ }
+ RemoveAllDependenciesFor(invalidated);
}
- RemoveAllDependenciesFor(invalidated);
+ }
+ // Since we are still loading the class that invalidated the code it's fine we have this after
+ // getting rid of the dependency. Any calls would need to be with the old version (since the
+ // new one isn't loaded yet) which still works fine. We will deoptimize just after this to
+ // ensure everything gets the new state.
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ jit::JitCodeCache* code_cache = jit->GetCodeCache();
+ for (const auto& pair : headers) {
+ code_cache->InvalidateCompiledCodeFor(pair.first, pair.second);
+ }
}
}
diff --git a/runtime/class_root.h b/runtime/class_root.h
index 4aa9801..19a78b1 100644
--- a/runtime/class_root.h
+++ b/runtime/class_root.h
@@ -165,18 +165,19 @@
template <class MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
inline ObjPtr<mirror::Class> GetClassRoot(ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetClassRoot<kWithReadBarrier>(detail::ClassRootSelector<MirrorType>::value, class_roots);
+ return GetClassRoot<kReadBarrierOption>(detail::ClassRootSelector<MirrorType>::value,
+ class_roots);
}
template <class MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
inline ObjPtr<mirror::Class> GetClassRoot(ClassLinker* linker)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetClassRoot<kWithReadBarrier>(detail::ClassRootSelector<MirrorType>::value, linker);
+ return GetClassRoot<kReadBarrierOption>(detail::ClassRootSelector<MirrorType>::value, linker);
}
template <class MirrorType, ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
inline ObjPtr<mirror::Class> GetClassRoot() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetClassRoot<kWithReadBarrier>(detail::ClassRootSelector<MirrorType>::value);
+ return GetClassRoot<kReadBarrierOption>(detail::ClassRootSelector<MirrorType>::value);
}
} // namespace art
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index f8388f3..9e3159d 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -249,14 +249,16 @@
void DexoptTest::ReserveImageSpaceChunk(uintptr_t start, uintptr_t end) {
if (start < end) {
std::string error_msg;
- image_reservation_.push_back(std::unique_ptr<MemMap>(
- MemMap::MapAnonymous("image reservation",
- reinterpret_cast<uint8_t*>(start), end - start,
- PROT_NONE, false, false, &error_msg)));
- ASSERT_TRUE(image_reservation_.back().get() != nullptr) << error_msg;
+ image_reservation_.push_back(MemMap::MapAnonymous("image reservation",
+ reinterpret_cast<uint8_t*>(start),
+ end - start,
+ PROT_NONE,
+ /* low_4gb*/ false,
+ &error_msg));
+ ASSERT_TRUE(image_reservation_.back().IsValid()) << error_msg;
LOG(INFO) << "Reserved space for image " <<
- reinterpret_cast<void*>(image_reservation_.back()->Begin()) << "-" <<
- reinterpret_cast<void*>(image_reservation_.back()->End());
+ reinterpret_cast<void*>(image_reservation_.back().Begin()) << "-" <<
+ reinterpret_cast<void*>(image_reservation_.back().End());
}
}
diff --git a/runtime/dexopt_test.h b/runtime/dexopt_test.h
index 6e8dc09..3203ee5 100644
--- a/runtime/dexopt_test.h
+++ b/runtime/dexopt_test.h
@@ -91,7 +91,7 @@
// before the image is loaded.
void UnreserveImageSpace();
- std::vector<std::unique_ptr<MemMap>> image_reservation_;
+ std::vector<MemMap> image_reservation_;
};
} // namespace art
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 026b5da..d45a689 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -283,7 +283,6 @@
template <typename ElfTypes>
ElfFileImpl<ElfTypes>::~ElfFileImpl() {
- STLDeleteElements(&segments_);
delete symtab_symbol_table_;
delete dynsym_symbol_table_;
}
@@ -418,17 +417,17 @@
}
template <typename ElfTypes>
-bool ElfFileImpl<ElfTypes>::SetMap(File* file, MemMap* map, std::string* error_msg) {
- if (map == nullptr) {
+bool ElfFileImpl<ElfTypes>::SetMap(File* file, MemMap&& map, std::string* error_msg) {
+ if (!map.IsValid()) {
// MemMap::Open should have already set an error.
DCHECK(!error_msg->empty());
return false;
}
- map_.reset(map);
- CHECK(map_.get() != nullptr) << file->GetPath();
- CHECK(map_->Begin() != nullptr) << file->GetPath();
+ map_ = std::move(map);
+ CHECK(map_.IsValid()) << file->GetPath();
+ CHECK(map_.Begin() != nullptr) << file->GetPath();
- header_ = reinterpret_cast<Elf_Ehdr*>(map_->Begin());
+ header_ = reinterpret_cast<Elf_Ehdr*>(map_.Begin());
if ((ELFMAG0 != header_->e_ident[EI_MAG0])
|| (ELFMAG1 != header_->e_ident[EI_MAG1])
|| (ELFMAG2 != header_->e_ident[EI_MAG2])
@@ -1164,14 +1163,13 @@
DCHECK(!error_msg->empty());
return false;
}
- std::unique_ptr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
- reserve_base_override,
- loaded_size,
- PROT_NONE,
- low_4gb,
- false,
- error_msg));
- if (reserve.get() == nullptr) {
+ MemMap reserve = MemMap::MapAnonymous(reservation_name.c_str(),
+ reserve_base_override,
+ loaded_size,
+ PROT_NONE,
+ low_4gb,
+ error_msg);
+ if (!reserve.IsValid()) {
*error_msg = StringPrintf("Failed to allocate %s: %s",
reservation_name.c_str(), error_msg->c_str());
return false;
@@ -1179,14 +1177,14 @@
reserved = true;
// Base address is the difference of actual mapped location and the p_vaddr
- base_address_ = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(reserve->Begin())
+ base_address_ = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(reserve.Begin())
- reinterpret_cast<uintptr_t>(reserve_base));
// By adding the p_vaddr of a section/symbol to base_address_ we will always get the
// dynamic memory address of where that object is actually mapped
//
// TODO: base_address_ needs to be calculated in ::Open, otherwise
// FindDynamicSymbolAddress returns the wrong values until Load is called.
- segments_.push_back(reserve.release());
+ segments_.push_back(std::move(reserve));
}
// empty segment, nothing to map
if (program_header->p_memsz == 0) {
@@ -1234,7 +1232,7 @@
return false;
}
if (program_header->p_filesz != 0u) {
- std::unique_ptr<MemMap> segment(
+ MemMap segment =
MemMap::MapFileAtAddress(p_vaddr,
program_header->p_filesz,
prot,
@@ -1244,40 +1242,42 @@
/*low4_gb*/false,
/*reuse*/true, // implies MAP_FIXED
file->GetPath().c_str(),
- error_msg));
- if (segment.get() == nullptr) {
+ error_msg);
+ if (!segment.IsValid()) {
*error_msg = StringPrintf("Failed to map ELF file segment %d from %s: %s",
i, file->GetPath().c_str(), error_msg->c_str());
return false;
}
- if (segment->Begin() != p_vaddr) {
+ if (segment.Begin() != p_vaddr) {
*error_msg = StringPrintf("Failed to map ELF file segment %d from %s at expected address %p, "
"instead mapped to %p",
- i, file->GetPath().c_str(), p_vaddr, segment->Begin());
+ i, file->GetPath().c_str(), p_vaddr, segment.Begin());
return false;
}
- segments_.push_back(segment.release());
+ segments_.push_back(std::move(segment));
}
if (program_header->p_filesz < program_header->p_memsz) {
std::string name = StringPrintf("Zero-initialized segment %" PRIu64 " of ELF file %s",
static_cast<uint64_t>(i), file->GetPath().c_str());
- std::unique_ptr<MemMap> segment(
- MemMap::MapAnonymous(name.c_str(),
- p_vaddr + program_header->p_filesz,
- program_header->p_memsz - program_header->p_filesz,
- prot, false, true /* reuse */, error_msg));
- if (segment == nullptr) {
+ MemMap segment = MemMap::MapAnonymous(name.c_str(),
+ p_vaddr + program_header->p_filesz,
+ program_header->p_memsz - program_header->p_filesz,
+ prot,
+ /* low_4gb */ false,
+ /* reuse */ true,
+ error_msg);
+ if (!segment.IsValid()) {
*error_msg = StringPrintf("Failed to map zero-initialized ELF file segment %d from %s: %s",
i, file->GetPath().c_str(), error_msg->c_str());
return false;
}
- if (segment->Begin() != p_vaddr) {
+ if (segment.Begin() != p_vaddr) {
*error_msg = StringPrintf("Failed to map zero-initialized ELF file segment %d from %s "
"at expected address %p, instead mapped to %p",
- i, file->GetPath().c_str(), p_vaddr, segment->Begin());
+ i, file->GetPath().c_str(), p_vaddr, segment.Begin());
return false;
}
- segments_.push_back(segment.release());
+ segments_.push_back(std::move(segment));
}
}
@@ -1343,9 +1343,8 @@
template <typename ElfTypes>
bool ElfFileImpl<ElfTypes>::ValidPointer(const uint8_t* start) const {
- for (size_t i = 0; i < segments_.size(); ++i) {
- const MemMap* segment = segments_[i];
- if (segment->Begin() <= start && start < segment->End()) {
+ for (const MemMap& segment : segments_) {
+ if (segment.Begin() <= start && start < segment.End()) {
return true;
}
}
@@ -1712,18 +1711,18 @@
file->GetPath().c_str());
return nullptr;
}
- std::unique_ptr<MemMap> map(MemMap::MapFile(EI_NIDENT,
- PROT_READ,
- MAP_PRIVATE,
- file->Fd(),
- 0,
- low_4gb,
- file->GetPath().c_str(),
- error_msg));
- if (map == nullptr || map->Size() != EI_NIDENT) {
+ MemMap map = MemMap::MapFile(EI_NIDENT,
+ PROT_READ,
+ MAP_PRIVATE,
+ file->Fd(),
+ 0,
+ low_4gb,
+ file->GetPath().c_str(),
+ error_msg);
+ if (!map.IsValid() || map.Size() != EI_NIDENT) {
return nullptr;
}
- uint8_t* header = map->Begin();
+ uint8_t* header = map.Begin();
if (header[EI_CLASS] == ELFCLASS64) {
ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file,
writable,
@@ -1763,18 +1762,18 @@
file->GetPath().c_str());
return nullptr;
}
- std::unique_ptr<MemMap> map(MemMap::MapFile(EI_NIDENT,
- PROT_READ,
- MAP_PRIVATE,
- file->Fd(),
- 0,
- low_4gb,
- file->GetPath().c_str(),
- error_msg));
- if (map == nullptr || map->Size() != EI_NIDENT) {
+ MemMap map = MemMap::MapFile(EI_NIDENT,
+ PROT_READ,
+ MAP_PRIVATE,
+ file->Fd(),
+ /* start */ 0,
+ low_4gb,
+ file->GetPath().c_str(),
+ error_msg);
+ if (!map.IsValid() || map.Size() != EI_NIDENT) {
return nullptr;
}
- uint8_t* header = map->Begin();
+ uint8_t* header = map.Begin();
if (header[EI_CLASS] == ELFCLASS64) {
ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file,
mmap_prot,
diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h
index a5808e2..58c38a4 100644
--- a/runtime/elf_file_impl.h
+++ b/runtime/elf_file_impl.h
@@ -62,15 +62,15 @@
}
uint8_t* Begin() const {
- return map_->Begin();
+ return map_.Begin();
}
uint8_t* End() const {
- return map_->End();
+ return map_.End();
}
size_t Size() const {
- return map_->Size();
+ return map_.Size();
}
Elf_Ehdr& GetHeader() const;
@@ -135,7 +135,7 @@
bool Setup(File* file, int prot, int flags, bool low_4gb, std::string* error_msg);
- bool SetMap(File* file, MemMap* map, std::string* error_msg);
+ bool SetMap(File* file, MemMap&& map, std::string* error_msg);
uint8_t* GetProgramHeadersStart() const;
uint8_t* GetSectionHeadersStart() const;
@@ -193,9 +193,9 @@
// ELF header mapping. If program_header_only_ is false, will
// actually point to the entire elf file.
- std::unique_ptr<MemMap> map_;
+ MemMap map_;
Elf_Ehdr* header_;
- std::vector<MemMap*> segments_;
+ std::vector<MemMap> segments_;
// Pointer to start of first PT_LOAD program segment after Load()
// when program_header_only_ is true.
diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h
index e30fef4..10af10d 100644
--- a/runtime/gc/accounting/atomic_stack.h
+++ b/runtime/gc/accounting/atomic_stack.h
@@ -72,12 +72,12 @@
~AtomicStack() {}
void Reset() {
- DCHECK(mem_map_.get() != nullptr);
+ DCHECK(mem_map_.IsValid());
DCHECK(begin_ != nullptr);
front_index_.store(0, std::memory_order_relaxed);
back_index_.store(0, std::memory_order_relaxed);
debug_is_sorted_ = true;
- mem_map_->MadviseDontNeedAndZero();
+ mem_map_.MadviseDontNeedAndZero();
}
// Beware: Mixing atomic pushes and atomic pops will cause ABA problem.
@@ -252,10 +252,14 @@
// Size in number of elements.
void Init() {
std::string error_msg;
- mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), nullptr, capacity_ * sizeof(begin_[0]),
- PROT_READ | PROT_WRITE, false, false, &error_msg));
- CHECK(mem_map_.get() != nullptr) << "couldn't allocate mark stack.\n" << error_msg;
- uint8_t* addr = mem_map_->Begin();
+ mem_map_ = MemMap::MapAnonymous(name_.c_str(),
+ /* addr */ nullptr,
+ capacity_ * sizeof(begin_[0]),
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ CHECK(mem_map_.IsValid()) << "couldn't allocate mark stack.\n" << error_msg;
+ uint8_t* addr = mem_map_.Begin();
CHECK(addr != nullptr);
debug_is_sorted_ = true;
begin_ = reinterpret_cast<StackReference<T>*>(addr);
@@ -265,7 +269,7 @@
// Name of the mark stack.
std::string name_;
// Memory mapping of the atomic stack.
- std::unique_ptr<MemMap> mem_map_;
+ MemMap mem_map_;
// Back index (index after the last element pushed).
AtomicInteger back_index_;
// Front index, used for implementing PopFront.
diff --git a/runtime/gc/accounting/bitmap.cc b/runtime/gc/accounting/bitmap.cc
index d45a0cc..bb2beaa 100644
--- a/runtime/gc/accounting/bitmap.cc
+++ b/runtime/gc/accounting/bitmap.cc
@@ -27,47 +27,50 @@
namespace gc {
namespace accounting {
-Bitmap* Bitmap::CreateFromMemMap(MemMap* mem_map, size_t num_bits) {
- CHECK(mem_map != nullptr);
- return new Bitmap(mem_map, num_bits);
+Bitmap* Bitmap::CreateFromMemMap(MemMap&& mem_map, size_t num_bits) {
+ CHECK(mem_map.IsValid());
+ return new Bitmap(std::move(mem_map), num_bits);
}
-Bitmap::Bitmap(MemMap* mem_map, size_t bitmap_size)
- : mem_map_(mem_map), bitmap_begin_(reinterpret_cast<uintptr_t*>(mem_map->Begin())),
+Bitmap::Bitmap(MemMap&& mem_map, size_t bitmap_size)
+ : mem_map_(std::move(mem_map)),
+ bitmap_begin_(reinterpret_cast<uintptr_t*>(mem_map_.Begin())),
bitmap_size_(bitmap_size) {
CHECK(bitmap_begin_ != nullptr);
CHECK_NE(bitmap_size, 0U);
}
Bitmap::~Bitmap() {
- // Destroys MemMap via std::unique_ptr<>.
+ // Destroys member MemMap.
}
-MemMap* Bitmap::AllocateMemMap(const std::string& name, size_t num_bits) {
+MemMap Bitmap::AllocateMemMap(const std::string& name, size_t num_bits) {
const size_t bitmap_size = RoundUp(
RoundUp(num_bits, kBitsPerBitmapWord) / kBitsPerBitmapWord * sizeof(uintptr_t), kPageSize);
std::string error_msg;
- std::unique_ptr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), nullptr, bitmap_size,
- PROT_READ | PROT_WRITE, false, false,
- &error_msg));
- if (UNLIKELY(mem_map.get() == nullptr)) {
+ MemMap mem_map = MemMap::MapAnonymous(name.c_str(),
+ /* addr */ nullptr,
+ bitmap_size,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ if (UNLIKELY(!mem_map.IsValid())) {
LOG(ERROR) << "Failed to allocate bitmap " << name << ": " << error_msg;
- return nullptr;
}
- return mem_map.release();
+ return mem_map;
}
Bitmap* Bitmap::Create(const std::string& name, size_t num_bits) {
- auto* const mem_map = AllocateMemMap(name, num_bits);
- if (mem_map == nullptr) {
+ MemMap mem_map = AllocateMemMap(name, num_bits);
+ if (UNLIKELY(!mem_map.IsValid())) {
return nullptr;
}
- return CreateFromMemMap(mem_map, num_bits);
+ return CreateFromMemMap(std::move(mem_map), num_bits);
}
void Bitmap::Clear() {
if (bitmap_begin_ != nullptr) {
- mem_map_->MadviseDontNeedAndZero();
+ mem_map_.MadviseDontNeedAndZero();
}
}
@@ -83,14 +86,15 @@
CHECK_ALIGNED(cover_begin, kAlignment);
CHECK_ALIGNED(cover_end, kAlignment);
const size_t num_bits = (cover_end - cover_begin) / kAlignment;
- auto* const mem_map = Bitmap::AllocateMemMap(name, num_bits);
- return CreateFromMemMap(mem_map, cover_begin, num_bits);
+ MemMap mem_map = Bitmap::AllocateMemMap(name, num_bits);
+ CHECK(mem_map.IsValid());
+ return CreateFromMemMap(std::move(mem_map), cover_begin, num_bits);
}
template<size_t kAlignment>
MemoryRangeBitmap<kAlignment>* MemoryRangeBitmap<kAlignment>::CreateFromMemMap(
- MemMap* mem_map, uintptr_t begin, size_t num_bits) {
- return new MemoryRangeBitmap(mem_map, begin, num_bits);
+ MemMap&& mem_map, uintptr_t begin, size_t num_bits) {
+ return new MemoryRangeBitmap(std::move(mem_map), begin, num_bits);
}
template class MemoryRangeBitmap<CardTable::kCardSize>;
diff --git a/runtime/gc/accounting/bitmap.h b/runtime/gc/accounting/bitmap.h
index 2d83a8a..ffef566 100644
--- a/runtime/gc/accounting/bitmap.h
+++ b/runtime/gc/accounting/bitmap.h
@@ -24,12 +24,11 @@
#include <vector>
#include "base/globals.h"
+#include "base/mem_map.h"
#include "base/mutex.h"
namespace art {
-class MemMap;
-
namespace gc {
namespace accounting {
@@ -42,7 +41,7 @@
// Initialize a space bitmap using the provided mem_map as the live bits. Takes ownership of the
// mem map. The address range covered starts at heap_begin and is of size equal to heap_capacity.
// Objects are kAlignement-aligned.
- static Bitmap* CreateFromMemMap(MemMap* mem_map, size_t num_bits);
+ static Bitmap* CreateFromMemMap(MemMap&& mem_map, size_t num_bits);
// offset is the difference from base to a index.
static ALWAYS_INLINE constexpr size_t BitIndexToWordIndex(uintptr_t offset) {
@@ -101,17 +100,17 @@
protected:
static constexpr size_t kBitsPerBitmapWord = sizeof(uintptr_t) * kBitsPerByte;
- Bitmap(MemMap* mem_map, size_t bitmap_size);
+ Bitmap(MemMap&& mem_map, size_t bitmap_size);
~Bitmap();
// Allocate the mem-map for a bitmap based on how many bits are required.
- static MemMap* AllocateMemMap(const std::string& name, size_t num_bits);
+ static MemMap AllocateMemMap(const std::string& name, size_t num_bits);
template<bool kSetBit>
ALWAYS_INLINE bool ModifyBit(uintptr_t bit_index);
// Backing storage for bitmap.
- std::unique_ptr<MemMap> mem_map_;
+ MemMap mem_map_;
// This bitmap itself, word sized for efficiency in scanning.
uintptr_t* const bitmap_begin_;
@@ -127,10 +126,10 @@
template<size_t kAlignment>
class MemoryRangeBitmap : public Bitmap {
public:
- static MemoryRangeBitmap* Create(const std::string& name, uintptr_t cover_begin,
- uintptr_t cover_end);
- static MemoryRangeBitmap* CreateFromMemMap(MemMap* mem_map, uintptr_t cover_begin,
- size_t num_bits);
+ static MemoryRangeBitmap* Create(
+ const std::string& name, uintptr_t cover_begin, uintptr_t cover_end);
+ static MemoryRangeBitmap* CreateFromMemMap(
+ MemMap&& mem_map, uintptr_t cover_begin, size_t num_bits);
// Beginning of the memory range that the bitmap covers.
ALWAYS_INLINE uintptr_t CoverBegin() const {
@@ -177,9 +176,10 @@
}
private:
- MemoryRangeBitmap(MemMap* mem_map, uintptr_t begin, size_t num_bits)
- : Bitmap(mem_map, num_bits), cover_begin_(begin), cover_end_(begin + kAlignment * num_bits) {
- }
+ MemoryRangeBitmap(MemMap&& mem_map, uintptr_t begin, size_t num_bits)
+ : Bitmap(std::move(mem_map), num_bits),
+ cover_begin_(begin),
+ cover_end_(begin + kAlignment * num_bits) {}
uintptr_t const cover_begin_;
uintptr_t const cover_end_;
diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h
index 357a498..1e7d76c 100644
--- a/runtime/gc/accounting/card_table-inl.h
+++ b/runtime/gc/accounting/card_table-inl.h
@@ -213,8 +213,8 @@
inline void* CardTable::AddrFromCard(const uint8_t *card_addr) const {
DCHECK(IsValidCard(card_addr))
<< " card_addr: " << reinterpret_cast<const void*>(card_addr)
- << " begin: " << reinterpret_cast<void*>(mem_map_->Begin() + offset_)
- << " end: " << reinterpret_cast<void*>(mem_map_->End());
+ << " begin: " << reinterpret_cast<void*>(mem_map_.Begin() + offset_)
+ << " end: " << reinterpret_cast<void*>(mem_map_.End());
uintptr_t offset = card_addr - biased_begin_;
return reinterpret_cast<void*>(offset << kCardShift);
}
@@ -228,16 +228,16 @@
}
inline bool CardTable::IsValidCard(const uint8_t* card_addr) const {
- uint8_t* begin = mem_map_->Begin() + offset_;
- uint8_t* end = mem_map_->End();
+ uint8_t* begin = mem_map_.Begin() + offset_;
+ uint8_t* end = mem_map_.End();
return card_addr >= begin && card_addr < end;
}
inline void CardTable::CheckCardValid(uint8_t* card) const {
DCHECK(IsValidCard(card))
<< " card_addr: " << reinterpret_cast<const void*>(card)
- << " begin: " << reinterpret_cast<void*>(mem_map_->Begin() + offset_)
- << " end: " << reinterpret_cast<void*>(mem_map_->End());
+ << " begin: " << reinterpret_cast<void*>(mem_map_.Begin() + offset_)
+ << " end: " << reinterpret_cast<void*>(mem_map_.End());
}
} // namespace accounting
diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc
index c7f936f..7cddec6 100644
--- a/runtime/gc/accounting/card_table.cc
+++ b/runtime/gc/accounting/card_table.cc
@@ -41,10 +41,10 @@
* non-null values to heap addresses should go through an entry in
* WriteBarrier, and from there to here.
*
- * The heap is divided into "cards" of GC_CARD_SIZE bytes, as
- * determined by GC_CARD_SHIFT. The card table contains one byte of
+ * The heap is divided into "cards" of `kCardSize` bytes, as
+ * determined by `kCardShift`. The card table contains one byte of
* data per card, to be used by the GC. The value of the byte will be
- * one of GC_CARD_CLEAN or GC_CARD_DIRTY.
+ * one of `kCardClean` or `kCardDirty`.
*
* After any store of a non-null object pointer into a heap object,
* code is obliged to mark the card dirty. The setters in
@@ -53,9 +53,9 @@
*
* The card table's base [the "biased card table"] gets set to a
* rather strange value. In order to keep the JIT from having to
- * fabricate or load GC_DIRTY_CARD to store into the card table,
+ * fabricate or load `kCardDirty` to store into the card table,
* biased base is within the mmap allocation at a point where its low
- * byte is equal to GC_DIRTY_CARD. See CardTable::Create for details.
+ * byte is equal to `kCardDirty`. See CardTable::Create for details.
*/
CardTable* CardTable::Create(const uint8_t* heap_begin, size_t heap_capacity) {
@@ -64,19 +64,22 @@
size_t capacity = heap_capacity / kCardSize;
/* Allocate an extra 256 bytes to allow fixed low-byte of base */
std::string error_msg;
- std::unique_ptr<MemMap> mem_map(
- MemMap::MapAnonymous("card table", nullptr, capacity + 256, PROT_READ | PROT_WRITE,
- false, false, &error_msg));
- CHECK(mem_map.get() != nullptr) << "couldn't allocate card table: " << error_msg;
+ MemMap mem_map = MemMap::MapAnonymous("card table",
+ /* addr */ nullptr,
+ capacity + 256,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ CHECK(mem_map.IsValid()) << "couldn't allocate card table: " << error_msg;
// All zeros is the correct initial value; all clean. Anonymous mmaps are initialized to zero, we
// don't clear the card table to avoid unnecessary pages being allocated
static_assert(kCardClean == 0, "kCardClean must be 0");
- uint8_t* cardtable_begin = mem_map->Begin();
+ uint8_t* cardtable_begin = mem_map.Begin();
CHECK(cardtable_begin != nullptr);
- // We allocated up to a bytes worth of extra space to allow biased_begin's byte value to equal
- // kCardDirty, compute a offset value to make this the case
+ // We allocated up to a bytes worth of extra space to allow `biased_begin`'s byte value to equal
+ // `kCardDirty`, compute a offset value to make this the case
size_t offset = 0;
uint8_t* biased_begin = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(cardtable_begin) -
(reinterpret_cast<uintptr_t>(heap_begin) >> kCardShift));
@@ -87,11 +90,11 @@
biased_begin += offset;
}
CHECK_EQ(reinterpret_cast<uintptr_t>(biased_begin) & 0xff, kCardDirty);
- return new CardTable(mem_map.release(), biased_begin, offset);
+ return new CardTable(std::move(mem_map), biased_begin, offset);
}
-CardTable::CardTable(MemMap* mem_map, uint8_t* biased_begin, size_t offset)
- : mem_map_(mem_map), biased_begin_(biased_begin), offset_(offset) {
+CardTable::CardTable(MemMap&& mem_map, uint8_t* biased_begin, size_t offset)
+ : mem_map_(std::move(mem_map)), biased_begin_(biased_begin), offset_(offset) {
}
CardTable::~CardTable() {
@@ -100,7 +103,7 @@
void CardTable::ClearCardTable() {
static_assert(kCardClean == 0, "kCardClean must be 0");
- mem_map_->MadviseDontNeedAndZero();
+ mem_map_.MadviseDontNeedAndZero();
}
void CardTable::ClearCardRange(uint8_t* start, uint8_t* end) {
@@ -118,8 +121,8 @@
void CardTable::CheckAddrIsInCardTable(const uint8_t* addr) const {
uint8_t* card_addr = biased_begin_ + ((uintptr_t)addr >> kCardShift);
- uint8_t* begin = mem_map_->Begin() + offset_;
- uint8_t* end = mem_map_->End();
+ uint8_t* begin = mem_map_.Begin() + offset_;
+ uint8_t* end = mem_map_.End();
CHECK(AddrIsInCardTable(addr))
<< "Card table " << this
<< " begin: " << reinterpret_cast<void*>(begin)
diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h
index f3548f7..f163898 100644
--- a/runtime/gc/accounting/card_table.h
+++ b/runtime/gc/accounting/card_table.h
@@ -20,12 +20,11 @@
#include <memory>
#include "base/globals.h"
+#include "base/mem_map.h"
#include "base/mutex.h"
namespace art {
-class MemMap;
-
namespace mirror {
class Object;
} // namespace mirror
@@ -56,7 +55,7 @@
static CardTable* Create(const uint8_t* heap_begin, size_t heap_capacity);
~CardTable();
- // Set the card associated with the given address to GC_CARD_DIRTY.
+ // Set the card associated with the given address to `kCardDirty`.
ALWAYS_INLINE void MarkCard(const void *addr) {
*CardFromAddr(addr) = kCardDirty;
}
@@ -66,6 +65,11 @@
return GetCard(obj) == kCardDirty;
}
+ // Is the object on a clean card?
+ bool IsClean(const mirror::Object* obj) const {
+ return GetCard(obj) == kCardClean;
+ }
+
// Return the state of the card at an address.
uint8_t GetCard(const mirror::Object* obj) const {
return *CardFromAddr(obj);
@@ -84,8 +88,8 @@
}
}
- // Returns a value that when added to a heap address >> GC_CARD_SHIFT will address the appropriate
- // card table byte. For convenience this value is cached in every Thread
+ // Returns a value that when added to a heap address >> `kCardShift` will address the appropriate
+ // card table byte. For convenience this value is cached in every Thread.
uint8_t* GetBiasedBegin() const {
return biased_begin_;
}
@@ -133,7 +137,7 @@
bool AddrIsInCardTable(const void* addr) const;
private:
- CardTable(MemMap* begin, uint8_t* biased_begin, size_t offset);
+ CardTable(MemMap&& mem_map, uint8_t* biased_begin, size_t offset);
// Returns true iff the card table address is within the bounds of the card table.
bool IsValidCard(const uint8_t* card_addr) const ALWAYS_INLINE;
@@ -144,11 +148,11 @@
void VerifyCardTable();
// Mmapped pages for the card table
- std::unique_ptr<MemMap> mem_map_;
+ MemMap mem_map_;
// Value used to compute card table addresses from object addresses, see GetBiasedBegin
uint8_t* const biased_begin_;
// Card table doesn't begin at the beginning of the mem_map_, instead it is displaced by offset
- // to allow the byte value of biased_begin_ to equal GC_CARD_DIRTY
+ // to allow the byte value of `biased_begin_` to equal `kCardDirty`.
const size_t offset_;
DISALLOW_IMPLICIT_CONSTRUCTORS(CardTable);
diff --git a/runtime/gc/accounting/read_barrier_table.h b/runtime/gc/accounting/read_barrier_table.h
index 4b5a8c6..8bdf6da 100644
--- a/runtime/gc/accounting/read_barrier_table.h
+++ b/runtime/gc/accounting/read_barrier_table.h
@@ -39,11 +39,14 @@
DCHECK_EQ(kHeapCapacity / kRegionSize,
static_cast<uint64_t>(static_cast<size_t>(kHeapCapacity / kRegionSize)));
std::string error_msg;
- MemMap* mem_map = MemMap::MapAnonymous("read barrier table", nullptr, capacity,
- PROT_READ | PROT_WRITE, false, false, &error_msg);
- CHECK(mem_map != nullptr && mem_map->Begin() != nullptr)
+ mem_map_ = MemMap::MapAnonymous("read barrier table",
+ /* addr */ nullptr,
+ capacity,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ CHECK(mem_map_.IsValid() && mem_map_.Begin() != nullptr)
<< "couldn't allocate read barrier table: " << error_msg;
- mem_map_.reset(mem_map);
}
void ClearForSpace(space::ContinuousSpace* space) {
uint8_t* entry_start = EntryFromAddr(space->Begin());
@@ -66,14 +69,14 @@
return entry_value == kSetEntryValue;
}
void ClearAll() {
- mem_map_->MadviseDontNeedAndZero();
+ mem_map_.MadviseDontNeedAndZero();
}
void SetAll() {
- memset(mem_map_->Begin(), kSetEntryValue, mem_map_->Size());
+ memset(mem_map_.Begin(), kSetEntryValue, mem_map_.Size());
}
bool IsAllCleared() const {
- for (uint32_t* p = reinterpret_cast<uint32_t*>(mem_map_->Begin());
- p < reinterpret_cast<uint32_t*>(mem_map_->End()); ++p) {
+ for (uint32_t* p = reinterpret_cast<uint32_t*>(mem_map_.Begin());
+ p < reinterpret_cast<uint32_t*>(mem_map_.End()); ++p) {
if (*p != 0) {
return false;
}
@@ -90,7 +93,7 @@
uint8_t* EntryFromAddr(const void* heap_addr) const {
DCHECK(IsValidHeapAddr(heap_addr)) << heap_addr;
- uint8_t* entry_addr = mem_map_->Begin() + reinterpret_cast<uintptr_t>(heap_addr) / kRegionSize;
+ uint8_t* entry_addr = mem_map_.Begin() + reinterpret_cast<uintptr_t>(heap_addr) / kRegionSize;
DCHECK(IsValidEntry(entry_addr)) << "heap_addr: " << heap_addr
<< " entry_addr: " << reinterpret_cast<void*>(entry_addr);
return entry_addr;
@@ -106,12 +109,12 @@
}
bool IsValidEntry(const uint8_t* entry_addr) const {
- uint8_t* begin = mem_map_->Begin();
- uint8_t* end = mem_map_->End();
+ uint8_t* begin = mem_map_.Begin();
+ uint8_t* end = mem_map_.End();
return entry_addr >= begin && entry_addr < end;
}
- std::unique_ptr<MemMap> mem_map_;
+ MemMap mem_map_;
};
} // namespace accounting
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index ced62cd..2946486 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -49,21 +49,22 @@
template<size_t kAlignment>
SpaceBitmap<kAlignment>* SpaceBitmap<kAlignment>::CreateFromMemMap(
- const std::string& name, MemMap* mem_map, uint8_t* heap_begin, size_t heap_capacity) {
- CHECK(mem_map != nullptr);
- uintptr_t* bitmap_begin = reinterpret_cast<uintptr_t*>(mem_map->Begin());
+ const std::string& name, MemMap&& mem_map, uint8_t* heap_begin, size_t heap_capacity) {
+ CHECK(mem_map.IsValid());
+ uintptr_t* bitmap_begin = reinterpret_cast<uintptr_t*>(mem_map.Begin());
const size_t bitmap_size = ComputeBitmapSize(heap_capacity);
- return new SpaceBitmap(name, mem_map, bitmap_begin, bitmap_size, heap_begin, heap_capacity);
+ return new SpaceBitmap(
+ name, std::move(mem_map), bitmap_begin, bitmap_size, heap_begin, heap_capacity);
}
template<size_t kAlignment>
SpaceBitmap<kAlignment>::SpaceBitmap(const std::string& name,
- MemMap* mem_map,
+ MemMap&& mem_map,
uintptr_t* bitmap_begin,
size_t bitmap_size,
const void* heap_begin,
size_t heap_capacity)
- : mem_map_(mem_map),
+ : mem_map_(std::move(mem_map)),
bitmap_begin_(reinterpret_cast<Atomic<uintptr_t>*>(bitmap_begin)),
bitmap_size_(bitmap_size),
heap_begin_(reinterpret_cast<uintptr_t>(heap_begin)),
@@ -83,14 +84,17 @@
// (we represent one word as an `intptr_t`).
const size_t bitmap_size = ComputeBitmapSize(heap_capacity);
std::string error_msg;
- std::unique_ptr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), nullptr, bitmap_size,
- PROT_READ | PROT_WRITE, false, false,
- &error_msg));
- if (UNLIKELY(mem_map.get() == nullptr)) {
+ MemMap mem_map = MemMap::MapAnonymous(name.c_str(),
+ /* addr */ nullptr,
+ bitmap_size,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ if (UNLIKELY(!mem_map.IsValid())) {
LOG(ERROR) << "Failed to allocate bitmap " << name << ": " << error_msg;
return nullptr;
}
- return CreateFromMemMap(name, mem_map.release(), heap_begin, heap_capacity);
+ return CreateFromMemMap(name, std::move(mem_map), heap_begin, heap_capacity);
}
template<size_t kAlignment>
@@ -114,7 +118,7 @@
template<size_t kAlignment>
void SpaceBitmap<kAlignment>::Clear() {
if (bitmap_begin_ != nullptr) {
- mem_map_->MadviseDontNeedAndZero();
+ mem_map_.MadviseDontNeedAndZero();
}
}
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index 1237f6e..6a3faef 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -24,6 +24,7 @@
#include <vector>
#include "base/globals.h"
+#include "base/mem_map.h"
#include "base/mutex.h"
namespace art {
@@ -32,7 +33,6 @@
class Class;
class Object;
} // namespace mirror
-class MemMap;
namespace gc {
namespace accounting {
@@ -50,8 +50,10 @@
// Initialize a space bitmap using the provided mem_map as the live bits. Takes ownership of the
// mem map. The address range covered starts at heap_begin and is of size equal to heap_capacity.
// Objects are kAlignement-aligned.
- static SpaceBitmap* CreateFromMemMap(const std::string& name, MemMap* mem_map,
- uint8_t* heap_begin, size_t heap_capacity);
+ static SpaceBitmap* CreateFromMemMap(const std::string& name,
+ MemMap&& mem_map,
+ uint8_t* heap_begin,
+ size_t heap_capacity);
~SpaceBitmap();
@@ -215,7 +217,7 @@
// TODO: heap_end_ is initialized so that the heap bitmap is empty, this doesn't require the -1,
// however, we document that this is expected on heap_end_
SpaceBitmap(const std::string& name,
- MemMap* mem_map,
+ MemMap&& mem_map,
uintptr_t* bitmap_begin,
size_t bitmap_size,
const void* heap_begin,
@@ -227,7 +229,7 @@
bool Modify(const mirror::Object* obj);
// Backing storage for bitmap.
- std::unique_ptr<MemMap> mem_map_;
+ MemMap mem_map_;
// This bitmap itself, word sized for efficiency in scanning.
Atomic<uintptr_t>* const bitmap_begin_;
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index a4095d8..0dbafde 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -91,11 +91,14 @@
size_t num_of_pages = footprint_ / kPageSize;
size_t max_num_of_pages = max_capacity_ / kPageSize;
std::string error_msg;
- page_map_mem_map_.reset(MemMap::MapAnonymous("rosalloc page map", nullptr,
- RoundUp(max_num_of_pages, kPageSize),
- PROT_READ | PROT_WRITE, false, false, &error_msg));
- CHECK(page_map_mem_map_.get() != nullptr) << "Couldn't allocate the page map : " << error_msg;
- page_map_ = page_map_mem_map_->Begin();
+ page_map_mem_map_ = MemMap::MapAnonymous("rosalloc page map",
+ /* addr */ nullptr,
+ RoundUp(max_num_of_pages, kPageSize),
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ CHECK(page_map_mem_map_.IsValid()) << "Couldn't allocate the page map : " << error_msg;
+ page_map_ = page_map_mem_map_.Begin();
page_map_size_ = num_of_pages;
max_page_map_size_ = max_num_of_pages;
free_page_run_size_map_.resize(num_of_pages);
@@ -1364,8 +1367,8 @@
// Zero out the tail of the page map.
uint8_t* zero_begin = const_cast<uint8_t*>(page_map_) + new_num_of_pages;
uint8_t* madvise_begin = AlignUp(zero_begin, kPageSize);
- DCHECK_LE(madvise_begin, page_map_mem_map_->End());
- size_t madvise_size = page_map_mem_map_->End() - madvise_begin;
+ DCHECK_LE(madvise_begin, page_map_mem_map_.End());
+ size_t madvise_size = page_map_mem_map_.End() - madvise_begin;
if (madvise_size > 0) {
DCHECK_ALIGNED(madvise_begin, kPageSize);
DCHECK_EQ(RoundUp(madvise_size, kPageSize), madvise_size);
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index 30213d5..0562167 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -31,13 +31,12 @@
#include "base/allocator.h"
#include "base/bit_utils.h"
#include "base/globals.h"
+#include "base/mem_map.h"
#include "base/mutex.h"
#include "thread.h"
namespace art {
-class MemMap;
-
namespace gc {
namespace allocator {
@@ -746,7 +745,7 @@
volatile uint8_t* page_map_; // No GUARDED_BY(lock_) for kReadPageMapEntryWithoutLockInBulkFree.
size_t page_map_size_;
size_t max_page_map_size_;
- std::unique_ptr<MemMap> page_map_mem_map_;
+ MemMap page_map_mem_map_;
// The table that indicates the size of free page runs. These sizes
// are stored here to avoid storing in the free page header and
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
index 36fefbd..3095f9f 100644
--- a/runtime/gc/collector/concurrent_copying-inl.h
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -25,6 +25,7 @@
#include "gc/space/region_space.h"
#include "gc/verification.h"
#include "lock_word.h"
+#include "mirror/class.h"
#include "mirror/object-readbarrier-inl.h"
namespace art {
@@ -35,12 +36,32 @@
Thread* const self,
mirror::Object* ref,
accounting::ContinuousSpaceBitmap* bitmap) {
- // For the Baker-style RB, in a rare case, we could incorrectly change the object from white
- // to gray even though the object has already been marked through. This happens if a mutator
- // thread gets preempted before the AtomicSetReadBarrierState below, GC marks through the
- // object (changes it from white to gray and back to white), and the thread runs and
- // incorrectly changes it from white to gray. If this happens, the object will get added to the
- // mark stack again and get changed back to white after it is processed.
+ if (kEnableGenerationalConcurrentCopyingCollection
+ && young_gen_
+ && !done_scanning_.load(std::memory_order_acquire)) {
+ // Everything in the unevac space should be marked for generational CC except for large objects.
+ DCHECK(region_space_bitmap_->Test(ref) || region_space_->IsLargeObject(ref)) << ref << " "
+ << ref->GetClass<kVerifyNone, kWithoutReadBarrier>()->PrettyClass();
+ // Since the mark bitmap is still filled in from last GC, we can not use that or else the
+ // mutator may see references to the from space. Instead, use the baker pointer itself as
+ // the mark bit.
+ if (ref->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(), ReadBarrier::GrayState())) {
+ // TODO: We don't actually need to scan this object later, we just need to clear the gray
+ // bit.
+ // TODO: We could also set the mark bit here for "free" since this case comes from the
+ // read barrier.
+ PushOntoMarkStack(self, ref);
+ }
+ DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState());
+ return ref;
+ }
+ // For the Baker-style RB, in a rare case, we could incorrectly change the object from non-gray
+ // (black) to gray even though the object has already been marked through. This happens if a
+ // mutator thread gets preempted before the AtomicSetReadBarrierState below, GC marks through the
+ // object (changes it from non-gray (white) to gray and back to non-gray (black)), and the thread
+ // runs and incorrectly changes it from non-gray (black) to gray. If this happens, the object
+ // will get added to the mark stack again and get changed back to non-gray (black) after it is
+ // processed.
if (kUseBakerReadBarrier) {
// Test the bitmap first to avoid graying an object that has already been marked through most
// of the time.
@@ -55,7 +76,7 @@
// we can avoid an expensive CAS.
// For the baker case, an object is marked if either the mark bit marked or the bitmap bit is
// set.
- success = ref->AtomicSetReadBarrierState(/* expected_rb_state */ ReadBarrier::WhiteState(),
+ success = ref->AtomicSetReadBarrierState(/* expected_rb_state */ ReadBarrier::NonGrayState(),
/* rb_state */ ReadBarrier::GrayState());
} else {
success = !bitmap->AtomicTestAndSet(ref);
@@ -91,8 +112,9 @@
return ref;
}
// This may or may not succeed, which is ok because the object may already be gray.
- bool success = ref->AtomicSetReadBarrierState(/* expected_rb_state */ ReadBarrier::WhiteState(),
- /* rb_state */ ReadBarrier::GrayState());
+ bool success =
+ ref->AtomicSetReadBarrierState(/* expected_rb_state */ ReadBarrier::NonGrayState(),
+ /* rb_state */ ReadBarrier::GrayState());
if (success) {
MutexLock mu(self, immune_gray_stack_lock_);
immune_gray_stack_.push_back(ref);
@@ -101,11 +123,13 @@
return ref;
}
-template<bool kGrayImmuneObject, bool kFromGCThread>
+template<bool kGrayImmuneObject, bool kNoUnEvac, bool kFromGCThread>
inline mirror::Object* ConcurrentCopying::Mark(Thread* const self,
mirror::Object* from_ref,
mirror::Object* holder,
MemberOffset offset) {
+ // Cannot have `kNoUnEvac` when Generational CC collection is disabled.
+ DCHECK(kEnableGenerationalConcurrentCopyingCollection || !kNoUnEvac);
if (from_ref == nullptr) {
return nullptr;
}
@@ -147,9 +171,19 @@
return to_ref;
}
case space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace:
+ if (kEnableGenerationalConcurrentCopyingCollection
+ && kNoUnEvac
+ && !region_space_->IsLargeObject(from_ref)) {
+ if (!kFromGCThread) {
+ DCHECK(IsMarkedInUnevacFromSpace(from_ref)) << "Returning unmarked object to mutator";
+ }
+ return from_ref;
+ }
return MarkUnevacFromSpaceRegion(self, from_ref, region_space_bitmap_);
default:
- // The reference is in an unused region.
+ // The reference is in an unused region. Remove memory protection from
+ // the region space and log debugging information.
+ region_space_->Unprotect();
LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(holder, offset, from_ref);
region_space_->DumpNonFreeRegions(LOG_STREAM(FATAL_WITHOUT_ABORT));
heap_->GetVerification()->LogHeapCorruption(holder, offset, from_ref, /* fatal */ true);
@@ -175,7 +209,8 @@
if (UNLIKELY(mark_from_read_barrier_measurements_)) {
ret = MarkFromReadBarrierWithMeasurements(self, from_ref);
} else {
- ret = Mark(self, from_ref);
+ ret = Mark</*kGrayImmuneObject*/true, /*kNoUnEvac*/false, /*kFromGCThread*/false>(self,
+ from_ref);
}
// Only set the mark bit for baker barrier.
if (kUseBakerReadBarrier && LIKELY(!rb_mark_bit_stack_full_ && ret->AtomicSetMarkBit(0, 1))) {
@@ -204,9 +239,14 @@
}
inline bool ConcurrentCopying::IsMarkedInUnevacFromSpace(mirror::Object* from_ref) {
- // Use load acquire on the read barrier pointer to ensure that we never see a white read barrier
- // state with an unmarked bit due to reordering.
+ // Use load-acquire on the read barrier pointer to ensure that we never see a black (non-gray)
+ // read barrier state with an unmarked bit due to reordering.
DCHECK(region_space_->IsInUnevacFromSpace(from_ref));
+ if (kEnableGenerationalConcurrentCopyingCollection
+ && young_gen_
+ && !done_scanning_.load(std::memory_order_acquire)) {
+ return from_ref->GetReadBarrierStateAcquire() == ReadBarrier::GrayState();
+ }
if (kUseBakerReadBarrier && from_ref->GetReadBarrierStateAcquire() == ReadBarrier::GrayState()) {
return true;
}
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index edaa043..fdd0b62 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -53,17 +53,20 @@
// If kFilterModUnionCards then we attempt to filter cards that don't need to be dirty in the mod
// union table. Disabled since it does not seem to help the pause much.
static constexpr bool kFilterModUnionCards = kIsDebugBuild;
-// If kDisallowReadBarrierDuringScan is true then the GC aborts if there are any that occur during
-// ConcurrentCopying::Scan. May be used to diagnose possibly unnecessary read barriers.
-// Only enabled for kIsDebugBuild to avoid performance hit.
+// If kDisallowReadBarrierDuringScan is true then the GC aborts if there are any read barrier that
+// occur during ConcurrentCopying::Scan in GC thread. May be used to diagnose possibly unnecessary
+// read barriers. Only enabled for kIsDebugBuild to avoid performance hit.
static constexpr bool kDisallowReadBarrierDuringScan = kIsDebugBuild;
// Slow path mark stack size, increase this if the stack is getting full and it is causing
// performance problems.
static constexpr size_t kReadBarrierMarkStackSize = 512 * KB;
+// Size (in the number of objects) of the sweep array free buffer.
+static constexpr size_t kSweepArrayChunkFreeSize = 1024;
// Verify that there are no missing card marks.
static constexpr bool kVerifyNoMissingCardMarks = kIsDebugBuild;
ConcurrentCopying::ConcurrentCopying(Heap* heap,
+ bool young_gen,
const std::string& name_prefix,
bool measure_read_barrier_slow_path)
: GarbageCollector(heap,
@@ -90,6 +93,7 @@
from_space_num_bytes_at_first_pause_(0),
mark_stack_mode_(kMarkStackModeOff),
weak_ref_access_enabled_(true),
+ young_gen_(young_gen),
skipped_blocks_lock_("concurrent copying bytes blocks lock", kMarkSweepMarkStackLock),
measure_read_barrier_slow_path_(measure_read_barrier_slow_path),
mark_from_read_barrier_measurements_(false),
@@ -107,6 +111,7 @@
kMarkSweepMarkStackLock) {
static_assert(space::RegionSpace::kRegionSize == accounting::ReadBarrierTable::kRegionSize,
"The region space size and the read barrier table region size must match");
+ CHECK(kEnableGenerationalConcurrentCopyingCollection || !young_gen_);
Thread* self = Thread::Current();
{
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
@@ -125,6 +130,19 @@
pooled_mark_stacks_.push_back(mark_stack);
}
}
+ if (kEnableGenerationalConcurrentCopyingCollection) {
+ // Allocate sweep array free buffer.
+ std::string error_msg;
+ sweep_array_free_buffer_mem_map_ = MemMap::MapAnonymous(
+ "concurrent copying sweep array free buffer",
+ /* addr */ nullptr,
+ RoundUp(kSweepArrayChunkFreeSize * sizeof(mirror::Object*), kPageSize),
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ CHECK(sweep_array_free_buffer_mem_map_.IsValid())
+ << "Couldn't allocate sweep array free buffer: " << error_msg;
+ }
}
void ConcurrentCopying::MarkHeapReference(mirror::HeapReference<mirror::Object>* field,
@@ -268,11 +286,36 @@
space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) {
CHECK(space->IsZygoteSpace() || space->IsImageSpace());
immune_spaces_.AddSpace(space);
- } else if (space == region_space_) {
- // It is OK to clear the bitmap with mutators running since the only place it is read is
- // VisitObjects which has exclusion with CC.
- region_space_bitmap_ = region_space_->GetMarkBitmap();
- region_space_bitmap_->Clear();
+ } else {
+ CHECK(!space->IsZygoteSpace());
+ CHECK(!space->IsImageSpace());
+ if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (space == region_space_) {
+ region_space_bitmap_ = region_space_->GetMarkBitmap();
+ } else if (young_gen_ && space->IsContinuousMemMapAllocSpace()) {
+ DCHECK_EQ(space->GetGcRetentionPolicy(), space::kGcRetentionPolicyAlwaysCollect);
+ space->AsContinuousMemMapAllocSpace()->BindLiveToMarkBitmap();
+ }
+ // Age all of the cards for the region space so that we know which evac regions to scan.
+ Runtime::Current()->GetHeap()->GetCardTable()->ModifyCardsAtomic(
+ space->Begin(),
+ space->End(),
+ AgeCardVisitor(),
+ VoidFunctor());
+ } else {
+ if (space == region_space_) {
+ // It is OK to clear the bitmap with mutators running since the only place it is read is
+ // VisitObjects which has exclusion with CC.
+ region_space_bitmap_ = region_space_->GetMarkBitmap();
+ region_space_bitmap_->Clear();
+ }
+ }
+ }
+ }
+ if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ for (const auto& space : GetHeap()->GetDiscontinuousSpaces()) {
+ CHECK(space->IsLargeObjectSpace());
+ space->AsLargeObjectSpace()->CopyLiveToMarked();
}
}
}
@@ -304,12 +347,14 @@
bytes_moved_gc_thread_ = 0;
objects_moved_gc_thread_ = 0;
GcCause gc_cause = GetCurrentIteration()->GetGcCause();
- if (gc_cause == kGcCauseExplicit ||
- gc_cause == kGcCauseCollectorTransition ||
- GetCurrentIteration()->GetClearSoftReferences()) {
- force_evacuate_all_ = true;
- } else {
- force_evacuate_all_ = false;
+
+ force_evacuate_all_ = false;
+ if (!kEnableGenerationalConcurrentCopyingCollection || !young_gen_) {
+ if (gc_cause == kGcCauseExplicit ||
+ gc_cause == kGcCauseCollectorTransition ||
+ GetCurrentIteration()->GetClearSoftReferences()) {
+ force_evacuate_all_ = true;
+ }
}
if (kUseBakerReadBarrier) {
updated_all_immune_objects_.store(false, std::memory_order_relaxed);
@@ -320,9 +365,13 @@
DCHECK(immune_gray_stack_.empty());
}
}
+ if (kEnableGenerationalConcurrentCopyingCollection) {
+ done_scanning_.store(false, std::memory_order_release);
+ }
BindBitmaps();
if (kVerboseMode) {
- LOG(INFO) << "force_evacuate_all=" << force_evacuate_all_;
+ LOG(INFO) << "young_gen=" << std::boolalpha << young_gen_ << std::noboolalpha;
+ LOG(INFO) << "force_evacuate_all=" << std::boolalpha << force_evacuate_all_ << std::noboolalpha;
LOG(INFO) << "Largest immune region: " << immune_spaces_.GetLargestImmuneRegion().Begin()
<< "-" << immune_spaces_.GetLargestImmuneRegion().End();
for (space::ContinuousSpace* space : immune_spaces_.GetSpaces()) {
@@ -330,6 +379,9 @@
}
LOG(INFO) << "GC end of InitializePhase";
}
+ if (kEnableGenerationalConcurrentCopyingCollection && !young_gen_) {
+ region_space_bitmap_->Clear();
+ }
// Mark all of the zygote large objects without graying them.
MarkZygoteLargeObjects();
}
@@ -425,9 +477,18 @@
}
CHECK_EQ(thread, self);
Locks::mutator_lock_->AssertExclusiveHeld(self);
+ space::RegionSpace::EvacMode evac_mode = space::RegionSpace::kEvacModeLivePercentNewlyAllocated;
+ if (cc->young_gen_) {
+ CHECK(!cc->force_evacuate_all_);
+ evac_mode = space::RegionSpace::kEvacModeNewlyAllocated;
+ } else if (cc->force_evacuate_all_) {
+ evac_mode = space::RegionSpace::kEvacModeForceAll;
+ }
{
TimingLogger::ScopedTiming split2("(Paused)SetFromSpace", cc->GetTimings());
- cc->region_space_->SetFromSpace(cc->rb_table_, cc->force_evacuate_all_);
+ // Only change live bytes for full CC.
+ cc->region_space_->SetFromSpace(
+ cc->rb_table_, evac_mode, /*clear_live_bytes*/ !cc->young_gen_);
}
cc->SwapStacks();
if (ConcurrentCopying::kEnableFromSpaceAccountingCheck) {
@@ -438,7 +499,7 @@
cc->is_marking_ = true;
cc->mark_stack_mode_.store(ConcurrentCopying::kMarkStackModeThreadLocal,
std::memory_order_relaxed);
- if (kIsDebugBuild) {
+ if (kIsDebugBuild && !cc->young_gen_) {
cc->region_space_->AssertAllRegionLiveBytesZeroOrCleared();
}
if (UNLIKELY(Runtime::Current()->IsActiveTransaction())) {
@@ -596,7 +657,8 @@
auto visitor = [&](mirror::Object* obj)
REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_) {
- // Objects not on dirty or aged cards should never have references to newly allocated regions.
+ // Objects on clean cards should never have references to newly allocated regions. Note
+ // that aged cards are also not clean.
if (heap_->GetCardTable()->GetCard(obj) == gc::accounting::CardTable::kCardClean) {
VerifyNoMissingCardMarkVisitor internal_visitor(this, /*holder*/ obj);
obj->VisitReferences</*kVisitNativeRoots*/true, kVerifyNone, kWithoutReadBarrier>(
@@ -646,10 +708,10 @@
explicit GrayImmuneObjectVisitor(Thread* self) : self_(self) {}
ALWAYS_INLINE void operator()(mirror::Object* obj) const REQUIRES_SHARED(Locks::mutator_lock_) {
- if (kUseBakerReadBarrier && obj->GetReadBarrierState() == ReadBarrier::WhiteState()) {
+ if (kUseBakerReadBarrier && obj->GetReadBarrierState() == ReadBarrier::NonGrayState()) {
if (kConcurrent) {
Locks::mutator_lock_->AssertSharedHeld(self_);
- obj->AtomicSetReadBarrierState(ReadBarrier::WhiteState(), ReadBarrier::GrayState());
+ obj->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(), ReadBarrier::GrayState());
// Mod union table VisitObjects may visit the same object multiple times so we can't check
// the result of the atomic set.
} else {
@@ -752,7 +814,13 @@
DCHECK(obj != nullptr);
DCHECK(immune_spaces_.ContainsObject(obj));
// Update the fields without graying it or pushing it onto the mark stack.
- Scan(obj);
+ if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ // Young GC does not care about references to unevac space. It is safe to not gray these as
+ // long as scan immune objects happens after scanning the dirty cards.
+ Scan<true>(obj);
+ } else {
+ Scan<false>(obj);
+ }
}
class ConcurrentCopying::ImmuneSpaceScanObjVisitor {
@@ -765,9 +833,9 @@
// Only need to scan gray objects.
if (obj->GetReadBarrierState() == ReadBarrier::GrayState()) {
collector_->ScanImmuneObject(obj);
- // Done scanning the object, go back to white.
+ // Done scanning the object, go back to black (non-gray).
bool success = obj->AtomicSetReadBarrierState(ReadBarrier::GrayState(),
- ReadBarrier::WhiteState());
+ ReadBarrier::NonGrayState());
CHECK(success)
<< Runtime::Current()->GetHeap()->GetVerification()->DumpObjectInfo(obj, "failed CAS");
}
@@ -803,7 +871,55 @@
if (kUseBakerReadBarrier) {
gc_grays_immune_objects_ = false;
}
+ if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ if (kVerboseMode) {
+ LOG(INFO) << "GC ScanCardsForSpace";
+ }
+ TimingLogger::ScopedTiming split2("ScanCardsForSpace", GetTimings());
+ WriterMutexLock rmu(Thread::Current(), *Locks::heap_bitmap_lock_);
+ CHECK(!done_scanning_.load(std::memory_order_relaxed));
+ if (kIsDebugBuild) {
+ // Leave some time for mutators to race ahead to try and find races between the GC card
+ // scanning and mutators reading references.
+ usleep(10 * 1000);
+ }
+ for (space::ContinuousSpace* space : GetHeap()->GetContinuousSpaces()) {
+ if (space->IsImageSpace() || space->IsZygoteSpace()) {
+ // Image and zygote spaces are already handled since we gray the objects in the pause.
+ continue;
+ }
+ // Scan all of the objects on dirty cards in unevac from space, and non moving space. These
+ // are from previous GCs and may reference things in the from space.
+ Runtime::Current()->GetHeap()->GetCardTable()->Scan<false>(
+ space->GetMarkBitmap(),
+ space->Begin(),
+ space->End(),
+ [this, space](mirror::Object* obj)
+ REQUIRES(Locks::heap_bitmap_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Don't push or gray unevac refs.
+ if (kIsDebugBuild && space == region_space_) {
+ // We may get unevac large objects.
+ if (!region_space_->IsInUnevacFromSpace(obj)) {
+ CHECK(region_space_bitmap_->Test(obj));
+ region_space_->DumpRegionForObject(LOG_STREAM(FATAL_WITHOUT_ABORT), obj);
+ LOG(FATAL) << "Scanning " << obj << " not in unevac space";
+ }
+ }
+ Scan<true>(obj);
+ },
+ accounting::CardTable::kCardDirty - 1);
+ }
+ // Done scanning unevac space.
+ done_scanning_.store(true, std::memory_order_release);
+ if (kVerboseMode) {
+ LOG(INFO) << "GC end of ScanCardsForSpace";
+ }
+ }
{
+ // For a sticky-bit collection, this phase needs to be after the card scanning since the
+ // mutator may read an unevac space object out of an image object. If the image object is no
+ // longer gray it will trigger a read barrier for the unevac space object.
TimingLogger::ScopedTiming split2("ScanImmuneSpaces", GetTimings());
for (auto& space : immune_spaces_.GetSpaces()) {
DCHECK(space->IsImageSpace() || space->IsZygoteSpace());
@@ -824,21 +940,23 @@
// This release fence makes the field updates in the above loop visible before allowing mutator
// getting access to immune objects without graying it first.
updated_all_immune_objects_.store(true, std::memory_order_release);
- // Now whiten immune objects concurrently accessed and grayed by mutators. We can't do this in
- // the above loop because we would incorrectly disable the read barrier by whitening an object
- // which may point to an unscanned, white object, breaking the to-space invariant.
+ // Now "un-gray" (conceptually blacken) immune objects concurrently accessed and grayed by
+ // mutators. We can't do this in the above loop because we would incorrectly disable the read
+ // barrier by un-graying (conceptually blackening) an object which may point to an unscanned,
+ // white object, breaking the to-space invariant (a mutator shall never observe a from-space
+ // (white) object).
//
- // Make sure no mutators are in the middle of marking an immune object before whitening immune
- // objects.
+ // Make sure no mutators are in the middle of marking an immune object before un-graying
+ // (blackening) immune objects.
IssueEmptyCheckpoint();
MutexLock mu(Thread::Current(), immune_gray_stack_lock_);
if (kVerboseMode) {
LOG(INFO) << "immune gray stack size=" << immune_gray_stack_.size();
}
for (mirror::Object* obj : immune_gray_stack_) {
- DCHECK(obj->GetReadBarrierState() == ReadBarrier::GrayState());
+ DCHECK_EQ(obj->GetReadBarrierState(), ReadBarrier::GrayState());
bool success = obj->AtomicSetReadBarrierState(ReadBarrier::GrayState(),
- ReadBarrier::WhiteState());
+ ReadBarrier::NonGrayState());
DCHECK(success);
}
immune_gray_stack_.clear();
@@ -1038,16 +1156,17 @@
void ConcurrentCopying::ProcessFalseGrayStack() {
CHECK(kUseBakerReadBarrier);
- // Change the objects on the false gray stack from gray to white.
+ // Change the objects on the false gray stack from gray to non-gray (conceptually black).
MutexLock mu(Thread::Current(), mark_stack_lock_);
for (mirror::Object* obj : false_gray_stack_) {
DCHECK(IsMarked(obj));
- // The object could be white here if a thread got preempted after a success at the
- // AtomicSetReadBarrierState in Mark(), GC started marking through it (but not finished so
- // still gray), and the thread ran to register it onto the false gray stack.
+ // The object could be non-gray (conceptually black) here if a thread got preempted after a
+ // success at the AtomicSetReadBarrierState in MarkNonMoving(), GC started marking through it
+ // (but not finished so still gray), the thread ran to register it onto the false gray stack,
+ // and then GC eventually marked it black (non-gray) after it finished scanning it.
if (obj->GetReadBarrierState() == ReadBarrier::GrayState()) {
bool success = obj->AtomicSetReadBarrierState(ReadBarrier::GrayState(),
- ReadBarrier::WhiteState());
+ ReadBarrier::NonGrayState());
DCHECK(success);
}
}
@@ -1166,9 +1285,8 @@
}
collector_->AssertToSpaceInvariant(holder, offset, ref);
if (kUseBakerReadBarrier) {
- CHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::WhiteState())
- << "Ref " << ref << " " << ref->PrettyTypeOf()
- << " has non-white rb_state ";
+ CHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::NonGrayState())
+ << "Ref " << ref << " " << ref->PrettyTypeOf() << " has gray rb_state";
}
}
@@ -1200,7 +1318,7 @@
ObjPtr<mirror::Reference> ref) const
REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
CHECK(klass->IsTypeOfReferenceClass());
- this->operator()(ref, mirror::Reference::ReferentOffset(), false);
+ this->operator()(ObjPtr<mirror::Object>(ref), mirror::Reference::ReferentOffset(), false);
}
void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
@@ -1243,8 +1361,8 @@
visitor,
visitor);
if (kUseBakerReadBarrier) {
- CHECK_EQ(obj->GetReadBarrierState(), ReadBarrier::WhiteState())
- << "obj=" << obj << " non-white rb_state " << obj->GetReadBarrierState();
+ CHECK_EQ(obj->GetReadBarrierState(), ReadBarrier::NonGrayState())
+ << "obj=" << obj << " has gray rb_state " << obj->GetReadBarrierState();
}
};
// Roots.
@@ -1518,18 +1636,40 @@
<< " " << to_ref << " " << to_ref->GetReadBarrierState()
<< " is_marked=" << IsMarked(to_ref);
}
+ space::RegionSpace::RegionType rtype = region_space_->GetRegionType(to_ref);
bool add_to_live_bytes = false;
- if (region_space_->IsInUnevacFromSpace(to_ref)) {
+ // Invariant: There should be no object from a newly-allocated
+ // region (either large or non-large) on the mark stack.
+ DCHECK(!region_space_->IsInNewlyAllocatedRegion(to_ref)) << to_ref;
+ if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
// Mark the bitmap only in the GC thread here so that we don't need a CAS.
- if (!kUseBakerReadBarrier || !region_space_bitmap_->Set(to_ref)) {
+ if (!kUseBakerReadBarrier ||
+ !region_space_bitmap_->Set(to_ref)) {
// It may be already marked if we accidentally pushed the same object twice due to the racy
// bitmap read in MarkUnevacFromSpaceRegion.
- Scan(to_ref);
- // Only add to the live bytes if the object was not already marked.
+ if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ CHECK(region_space_->IsLargeObject(to_ref));
+ region_space_->ZeroLiveBytesForLargeObject(to_ref);
+ Scan<true>(to_ref);
+ } else {
+ Scan<false>(to_ref);
+ }
+ // Only add to the live bytes if the object was not already marked and we are not the young
+ // GC.
add_to_live_bytes = true;
}
} else {
- Scan(to_ref);
+ if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
+ // Copied to to-space, set the bit so that the next GC can scan objects.
+ region_space_bitmap_->Set(to_ref);
+ }
+ }
+ if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ Scan<true>(to_ref);
+ } else {
+ Scan<false>(to_ref);
+ }
}
if (kUseBakerReadBarrier) {
DCHECK(to_ref->GetReadBarrierState() == ReadBarrier::GrayState())
@@ -1542,18 +1682,18 @@
(referent = to_ref->AsReference()->GetReferent<kWithoutReadBarrier>()) != nullptr &&
!IsInToSpace(referent)))) {
// Leave this reference gray in the queue so that GetReferent() will trigger a read barrier. We
- // will change it to white later in ReferenceQueue::DequeuePendingReference().
+ // will change it to non-gray later in ReferenceQueue::DisableReadBarrierForReference.
DCHECK(to_ref->AsReference()->GetPendingNext() != nullptr)
<< "Left unenqueued ref gray " << to_ref;
} else {
- // We may occasionally leave a reference white in the queue if its referent happens to be
+ // We may occasionally leave a reference non-gray in the queue if its referent happens to be
// concurrently marked after the Scan() call above has enqueued the Reference, in which case the
- // above IsInToSpace() evaluates to true and we change the color from gray to white here in this
- // else block.
+ // above IsInToSpace() evaluates to true and we change the color from gray to non-gray here in
+ // this else block.
if (kUseBakerReadBarrier) {
bool success = to_ref->AtomicSetReadBarrierState<std::memory_order_release>(
ReadBarrier::GrayState(),
- ReadBarrier::WhiteState());
+ ReadBarrier::NonGrayState());
DCHECK(success) << "Must succeed as we won the race.";
}
}
@@ -1672,29 +1812,126 @@
}
void ConcurrentCopying::Sweep(bool swap_bitmaps) {
- {
- TimingLogger::ScopedTiming t("MarkStackAsLive", GetTimings());
- accounting::ObjectStack* live_stack = heap_->GetLiveStack();
- if (kEnableFromSpaceAccountingCheck) {
- CHECK_GE(live_stack_freeze_size_, live_stack->Size());
+ if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ // Only sweep objects on the live stack.
+ SweepArray(heap_->GetLiveStack(), /* swap_bitmaps */ false);
+ } else {
+ {
+ TimingLogger::ScopedTiming t("MarkStackAsLive", GetTimings());
+ accounting::ObjectStack* live_stack = heap_->GetLiveStack();
+ if (kEnableFromSpaceAccountingCheck) {
+ // Ensure that nobody inserted items in the live stack after we swapped the stacks.
+ CHECK_GE(live_stack_freeze_size_, live_stack->Size());
+ }
+ heap_->MarkAllocStackAsLive(live_stack);
+ live_stack->Reset();
}
- heap_->MarkAllocStackAsLive(live_stack);
- live_stack->Reset();
+ CheckEmptyMarkStack();
+ TimingLogger::ScopedTiming split("Sweep", GetTimings());
+ for (const auto& space : GetHeap()->GetContinuousSpaces()) {
+ if (space->IsContinuousMemMapAllocSpace()) {
+ space::ContinuousMemMapAllocSpace* alloc_space = space->AsContinuousMemMapAllocSpace();
+ if (space == region_space_ || immune_spaces_.ContainsSpace(space)) {
+ continue;
+ }
+ TimingLogger::ScopedTiming split2(
+ alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepAllocSpace", GetTimings());
+ RecordFree(alloc_space->Sweep(swap_bitmaps));
+ }
+ }
+ SweepLargeObjects(swap_bitmaps);
}
+}
+
+// Copied and adapted from MarkSweep::SweepArray.
+void ConcurrentCopying::SweepArray(accounting::ObjectStack* allocations, bool swap_bitmaps) {
+ // This method is only used when Generational CC collection is enabled.
+ DCHECK(kEnableGenerationalConcurrentCopyingCollection);
CheckEmptyMarkStack();
- TimingLogger::ScopedTiming split("Sweep", GetTimings());
- for (const auto& space : GetHeap()->GetContinuousSpaces()) {
- if (space->IsContinuousMemMapAllocSpace()) {
- space::ContinuousMemMapAllocSpace* alloc_space = space->AsContinuousMemMapAllocSpace();
- if (space == region_space_ || immune_spaces_.ContainsSpace(space)) {
+ TimingLogger::ScopedTiming t("SweepArray", GetTimings());
+ Thread* self = Thread::Current();
+ mirror::Object** chunk_free_buffer = reinterpret_cast<mirror::Object**>(
+ sweep_array_free_buffer_mem_map_.BaseBegin());
+ size_t chunk_free_pos = 0;
+ ObjectBytePair freed;
+ ObjectBytePair freed_los;
+ // How many objects are left in the array, modified after each space is swept.
+ StackReference<mirror::Object>* objects = allocations->Begin();
+ size_t count = allocations->Size();
+ // Start by sweeping the continuous spaces.
+ for (space::ContinuousSpace* space : heap_->GetContinuousSpaces()) {
+ if (!space->IsAllocSpace() ||
+ space == region_space_ ||
+ immune_spaces_.ContainsSpace(space) ||
+ space->GetLiveBitmap() == nullptr) {
+ continue;
+ }
+ space::AllocSpace* alloc_space = space->AsAllocSpace();
+ accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
+ accounting::ContinuousSpaceBitmap* mark_bitmap = space->GetMarkBitmap();
+ if (swap_bitmaps) {
+ std::swap(live_bitmap, mark_bitmap);
+ }
+ StackReference<mirror::Object>* out = objects;
+ for (size_t i = 0; i < count; ++i) {
+ mirror::Object* const obj = objects[i].AsMirrorPtr();
+ if (kUseThreadLocalAllocationStack && obj == nullptr) {
continue;
}
- TimingLogger::ScopedTiming split2(
- alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepAllocSpace", GetTimings());
- RecordFree(alloc_space->Sweep(swap_bitmaps));
+ if (space->HasAddress(obj)) {
+ // This object is in the space, remove it from the array and add it to the sweep buffer
+ // if needed.
+ if (!mark_bitmap->Test(obj)) {
+ if (chunk_free_pos >= kSweepArrayChunkFreeSize) {
+ TimingLogger::ScopedTiming t2("FreeList", GetTimings());
+ freed.objects += chunk_free_pos;
+ freed.bytes += alloc_space->FreeList(self, chunk_free_pos, chunk_free_buffer);
+ chunk_free_pos = 0;
+ }
+ chunk_free_buffer[chunk_free_pos++] = obj;
+ }
+ } else {
+ (out++)->Assign(obj);
+ }
+ }
+ if (chunk_free_pos > 0) {
+ TimingLogger::ScopedTiming t2("FreeList", GetTimings());
+ freed.objects += chunk_free_pos;
+ freed.bytes += alloc_space->FreeList(self, chunk_free_pos, chunk_free_buffer);
+ chunk_free_pos = 0;
+ }
+ // All of the references which space contained are no longer in the allocation stack, update
+ // the count.
+ count = out - objects;
+ }
+ // Handle the large object space.
+ space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace();
+ if (large_object_space != nullptr) {
+ accounting::LargeObjectBitmap* large_live_objects = large_object_space->GetLiveBitmap();
+ accounting::LargeObjectBitmap* large_mark_objects = large_object_space->GetMarkBitmap();
+ if (swap_bitmaps) {
+ std::swap(large_live_objects, large_mark_objects);
+ }
+ for (size_t i = 0; i < count; ++i) {
+ mirror::Object* const obj = objects[i].AsMirrorPtr();
+ // Handle large objects.
+ if (kUseThreadLocalAllocationStack && obj == nullptr) {
+ continue;
+ }
+ if (!large_mark_objects->Test(obj)) {
+ ++freed_los.objects;
+ freed_los.bytes += large_object_space->Free(self, obj);
+ }
}
}
- SweepLargeObjects(swap_bitmaps);
+ {
+ TimingLogger::ScopedTiming t2("RecordFree", GetTimings());
+ RecordFree(freed);
+ RecordFreeLOS(freed_los);
+ t2.NewTiming("ResetStack");
+ allocations->Reset();
+ }
+ sweep_array_free_buffer_mem_map_.MadviseDontNeedAndZero();
}
void ConcurrentCopying::MarkZygoteLargeObjects() {
@@ -1804,7 +2041,7 @@
{
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
- Sweep(false);
+ Sweep(/* swap_bitmaps */ false);
SwapBitmaps();
heap_->UnBindBitmaps();
@@ -1871,20 +2108,45 @@
} else if (type == RegionType::kRegionTypeUnevacFromSpace) {
if (!IsMarkedInUnevacFromSpace(ref)) {
LOG(FATAL_WITHOUT_ABORT) << "Found unmarked reference in unevac from-space:";
+ // Remove memory protection from the region space and log debugging information.
+ region_space_->Unprotect();
LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(obj, offset, ref);
+ Thread::Current()->DumpJavaStack(LOG_STREAM(FATAL_WITHOUT_ABORT));
}
CHECK(IsMarkedInUnevacFromSpace(ref)) << ref;
} else {
// Not OK: either a from-space ref or a reference in an unused region.
- // Do extra logging.
if (type == RegionType::kRegionTypeFromSpace) {
LOG(FATAL_WITHOUT_ABORT) << "Found from-space reference:";
} else {
LOG(FATAL_WITHOUT_ABORT) << "Found reference in region with type " << type << ":";
}
+ // Remove memory protection from the region space and log debugging information.
+ region_space_->Unprotect();
LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(obj, offset, ref);
if (obj != nullptr) {
LogFromSpaceRefHolder(obj, offset);
+ LOG(FATAL_WITHOUT_ABORT) << "UNEVAC " << region_space_->IsInUnevacFromSpace(obj) << " "
+ << obj << " " << obj->GetMarkBit();
+ if (region_space_->HasAddress(obj)) {
+ region_space_->DumpRegionForObject(LOG_STREAM(FATAL_WITHOUT_ABORT), obj);
+ }
+ LOG(FATAL_WITHOUT_ABORT) << "CARD " << static_cast<size_t>(
+ *Runtime::Current()->GetHeap()->GetCardTable()->CardFromAddr(
+ reinterpret_cast<uint8_t*>(obj)));
+ if (region_space_->HasAddress(obj)) {
+ LOG(FATAL_WITHOUT_ABORT) << "BITMAP " << region_space_bitmap_->Test(obj);
+ } else {
+ accounting::ContinuousSpaceBitmap* mark_bitmap =
+ heap_mark_bitmap_->GetContinuousSpaceBitmap(obj);
+ if (mark_bitmap != nullptr) {
+ LOG(FATAL_WITHOUT_ABORT) << "BITMAP " << mark_bitmap->Test(obj);
+ } else {
+ accounting::LargeObjectBitmap* los_bitmap =
+ heap_mark_bitmap_->GetLargeObjectBitmap(obj);
+ LOG(FATAL_WITHOUT_ABORT) << "BITMAP " << los_bitmap->Test(obj);
+ }
+ }
}
ref->GetLockWord(false).Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
LOG(FATAL_WITHOUT_ABORT) << "Non-free regions:";
@@ -1949,17 +2211,20 @@
} else if (type == RegionType::kRegionTypeUnevacFromSpace) {
if (!IsMarkedInUnevacFromSpace(ref)) {
LOG(FATAL_WITHOUT_ABORT) << "Found unmarked reference in unevac from-space:";
+ // Remove memory protection from the region space and log debugging information.
+ region_space_->Unprotect();
LOG(FATAL_WITHOUT_ABORT) << DumpGcRoot(ref);
}
CHECK(IsMarkedInUnevacFromSpace(ref)) << ref;
} else {
// Not OK: either a from-space ref or a reference in an unused region.
- // Do extra logging.
if (type == RegionType::kRegionTypeFromSpace) {
LOG(FATAL_WITHOUT_ABORT) << "Found from-space reference:";
} else {
LOG(FATAL_WITHOUT_ABORT) << "Found reference in region with type " << type << ":";
}
+ // Remove memory protection from the region space and log debugging information.
+ region_space_->Unprotect();
LOG(FATAL_WITHOUT_ABORT) << DumpGcRoot(ref);
if (gc_root_source == nullptr) {
// No info.
@@ -2077,15 +2342,19 @@
}
// Used to scan ref fields of an object.
+template <bool kNoUnEvac>
class ConcurrentCopying::RefFieldsVisitor {
public:
explicit RefFieldsVisitor(ConcurrentCopying* collector, Thread* const thread)
- : collector_(collector), thread_(thread) {}
+ : collector_(collector), thread_(thread) {
+ // Cannot have `kNoUnEvac` when Generational CC collection is disabled.
+ DCHECK(kEnableGenerationalConcurrentCopyingCollection || !kNoUnEvac);
+ }
void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */)
const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES_SHARED(Locks::heap_bitmap_lock_) {
- collector_->Process(obj, offset);
+ collector_->Process<kNoUnEvac>(obj, offset);
}
void operator()(ObjPtr<mirror::Class> klass, ObjPtr<mirror::Reference> ref) const
@@ -2113,7 +2382,10 @@
Thread* const thread_;
};
+template <bool kNoUnEvac>
inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
+ // Cannot have `kNoUnEvac` when Generational CC collection is disabled.
+ DCHECK(kEnableGenerationalConcurrentCopyingCollection || !kNoUnEvac);
if (kDisallowReadBarrierDuringScan && !Runtime::Current()->IsActiveTransaction()) {
// Avoid all read barriers during visit references to help performance.
// Don't do this in transaction mode because we may read the old value of an field which may
@@ -2122,7 +2394,7 @@
}
DCHECK(!region_space_->IsInFromSpace(to_ref));
DCHECK_EQ(Thread::Current(), thread_running_gc_);
- RefFieldsVisitor visitor(this, thread_running_gc_);
+ RefFieldsVisitor<kNoUnEvac> visitor(this, thread_running_gc_);
// Disable the read barrier for a performance reason.
to_ref->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
visitor, visitor);
@@ -2131,11 +2403,14 @@
}
}
+template <bool kNoUnEvac>
inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) {
+ // Cannot have `kNoUnEvac` when Generational CC collection is disabled.
+ DCHECK(kEnableGenerationalConcurrentCopyingCollection || !kNoUnEvac);
DCHECK_EQ(Thread::Current(), thread_running_gc_);
mirror::Object* ref = obj->GetFieldObject<
mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset);
- mirror::Object* to_ref = Mark</*kGrayImmuneObject*/false, /*kFromGCThread*/true>(
+ mirror::Object* to_ref = Mark</*kGrayImmuneObject*/false, kNoUnEvac, /*kFromGCThread*/true>(
thread_running_gc_,
ref,
/*holder*/ obj,
@@ -2359,6 +2634,8 @@
// from a previous GC that is either inside or outside the allocated region.
mirror::Class* klass = from_ref->GetClass<kVerifyNone, kWithoutReadBarrier>();
if (UNLIKELY(klass == nullptr)) {
+ // Remove memory protection from the region space and log debugging information.
+ region_space_->Unprotect();
heap_->GetVerification()->LogHeapCorruption(holder, offset, from_ref, /* fatal */ true);
}
// There must not be a read barrier to avoid nested RB that might violate the to-space invariant.
@@ -2413,7 +2690,8 @@
accounting::ContinuousSpaceBitmap* mark_bitmap =
heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref);
CHECK(mark_bitmap != nullptr);
- CHECK(!mark_bitmap->AtomicTestAndSet(to_ref));
+ bool previously_marked_in_bitmap = mark_bitmap->AtomicTestAndSet(to_ref);
+ CHECK(!previously_marked_in_bitmap);
}
}
DCHECK(to_ref != nullptr);
@@ -2610,6 +2888,28 @@
accounting::LargeObjectBitmap* los_bitmap =
heap_mark_bitmap_->GetLargeObjectBitmap(ref);
bool is_los = mark_bitmap == nullptr;
+ if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ // The sticky-bit CC collector is only compatible with Baker-style read barriers.
+ DCHECK(kUseBakerReadBarrier);
+ // Not done scanning, use AtomicSetReadBarrierPointer.
+ if (!done_scanning_.load(std::memory_order_acquire)) {
+ // Since the mark bitmap is still filled in from last GC, we can not use that or else the
+ // mutator may see references to the from space. Instead, use the Baker pointer itself as
+ // the mark bit.
+ if (ref->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(), ReadBarrier::GrayState())) {
+ // TODO: We don't actually need to scan this object later, we just need to clear the gray
+ // bit.
+ // Also make sure the object is marked.
+ if (is_los) {
+ los_bitmap->AtomicTestAndSet(ref);
+ } else {
+ mark_bitmap->AtomicTestAndSet(ref);
+ }
+ PushOntoMarkStack(self, ref);
+ }
+ return ref;
+ }
+ }
if (!is_los && mark_bitmap->Test(ref)) {
// Already marked.
} else if (is_los && los_bitmap->Test(ref)) {
@@ -2617,7 +2917,7 @@
} else {
// Not marked.
if (IsOnAllocStack(ref)) {
- // If it's on the allocation stack, it's considered marked. Keep it white.
+ // If it's on the allocation stack, it's considered marked. Keep it white (non-gray).
// Objects on the allocation stack need not be marked.
if (!is_los) {
DCHECK(!mark_bitmap->Test(ref));
@@ -2625,7 +2925,7 @@
DCHECK(!los_bitmap->Test(ref));
}
if (kUseBakerReadBarrier) {
- DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::WhiteState());
+ DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::NonGrayState());
}
} else {
// For the baker-style RB, we need to handle 'false-gray' cases. See the
@@ -2638,26 +2938,31 @@
}
}
if (is_los && !IsAligned<kPageSize>(ref)) {
- // Ref is a large object that is not aligned, it must be heap corruption. Dump data before
- // AtomicSetReadBarrierState since it will fault if the address is not valid.
+ // Ref is a large object that is not aligned, it must be heap
+ // corruption. Remove memory protection and dump data before
+ // AtomicSetReadBarrierState since it will fault if the address is not
+ // valid.
+ region_space_->Unprotect();
heap_->GetVerification()->LogHeapCorruption(holder, offset, ref, /* fatal */ true);
}
- // Not marked or on the allocation stack. Try to mark it.
+ // Not marked nor on the allocation stack. Try to mark it.
// This may or may not succeed, which is ok.
bool cas_success = false;
if (kUseBakerReadBarrier) {
- cas_success = ref->AtomicSetReadBarrierState(ReadBarrier::WhiteState(),
+ cas_success = ref->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(),
ReadBarrier::GrayState());
}
if (!is_los && mark_bitmap->AtomicTestAndSet(ref)) {
// Already marked.
- if (kUseBakerReadBarrier && cas_success &&
+ if (kUseBakerReadBarrier &&
+ cas_success &&
ref->GetReadBarrierState() == ReadBarrier::GrayState()) {
PushOntoFalseGrayStack(self, ref);
}
} else if (is_los && los_bitmap->AtomicTestAndSet(ref)) {
// Already marked in LOS.
- if (kUseBakerReadBarrier && cas_success &&
+ if (kUseBakerReadBarrier &&
+ cas_success &&
ref->GetReadBarrierState() == ReadBarrier::GrayState()) {
PushOntoFalseGrayStack(self, ref);
}
@@ -2681,7 +2986,7 @@
}
// kVerifyNoMissingCardMarks relies on the region space cards not being cleared to avoid false
// positives.
- if (!kVerifyNoMissingCardMarks) {
+ if (!kEnableGenerationalConcurrentCopyingCollection && !kVerifyNoMissingCardMarks) {
TimingLogger::ScopedTiming split("ClearRegionSpaceCards", GetTimings());
// We do not currently use the region space cards at all, madvise them away to save ram.
heap_->GetCardTable()->ClearCardRange(region_space_->Begin(), region_space_->Limit());
@@ -2789,7 +3094,8 @@
}
ScopedTrace tr(__FUNCTION__);
const uint64_t start_time = measure_read_barrier_slow_path_ ? NanoTime() : 0u;
- mirror::Object* ret = Mark(self, from_ref);
+ mirror::Object* ret =
+ Mark</*kGrayImmuneObject*/true, /*kNoUnEvac*/false, /*kFromGCThread*/false>(self, from_ref);
if (measure_read_barrier_slow_path_) {
rb_slow_path_ns_.fetch_add(NanoTime() - start_time, std::memory_order_relaxed);
}
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 448525d..0ebe6f0 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -66,6 +66,7 @@
static constexpr bool kGrayDirtyImmuneObjects = true;
explicit ConcurrentCopying(Heap* heap,
+ bool young_gen,
const std::string& name_prefix = "",
bool measure_read_barrier_slow_path = false);
~ConcurrentCopying();
@@ -87,7 +88,9 @@
void BindBitmaps() REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::heap_bitmap_lock_);
virtual GcType GetGcType() const OVERRIDE {
- return kGcTypePartial;
+ return (kEnableGenerationalConcurrentCopyingCollection && young_gen_)
+ ? kGcTypeSticky
+ : kGcTypePartial;
}
virtual CollectorType GetCollectorType() const OVERRIDE {
return kCollectorTypeCC;
@@ -110,8 +113,8 @@
DCHECK(ref != nullptr);
return IsMarked(ref) == ref;
}
- template<bool kGrayImmuneObject = true, bool kFromGCThread = false>
// Mark object `from_ref`, copying it to the to-space if needed.
+ template<bool kGrayImmuneObject = true, bool kNoUnEvac = false, bool kFromGCThread = false>
ALWAYS_INLINE mirror::Object* Mark(Thread* const self,
mirror::Object* from_ref,
mirror::Object* holder = nullptr,
@@ -155,9 +158,11 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
// Scan the reference fields of object `to_ref`.
+ template <bool kNoUnEvac>
void Scan(mirror::Object* to_ref) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
// Process a field.
+ template <bool kNoUnEvac>
void Process(mirror::Object* obj, MemberOffset offset)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_ , !skipped_blocks_lock_, !immune_gray_stack_lock_);
@@ -217,8 +222,14 @@
REQUIRES_SHARED(Locks::mutator_lock_);
void SweepSystemWeaks(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_);
+ // Sweep unmarked objects to complete the garbage collection. Full GCs sweep
+ // all allocation spaces (except the region space). Sticky-bit GCs just sweep
+ // a subset of the heap.
void Sweep(bool swap_bitmaps)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_, !mark_stack_lock_);
+ // Sweep only pointers within an array.
+ void SweepArray(accounting::ObjectStack* allocation_stack_, bool swap_bitmaps)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_, !mark_stack_lock_);
void SweepLargeObjects(bool swap_bitmaps)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
void MarkZygoteLargeObjects()
@@ -347,6 +358,12 @@
Atomic<uint64_t> cumulative_bytes_moved_;
Atomic<uint64_t> cumulative_objects_moved_;
+ // Generational "sticky", only trace through dirty objects in region space.
+ const bool young_gen_;
+ // If true, the GC thread is done scanning marked objects on dirty and aged
+ // card (see ConcurrentCopying::MarkingPhase).
+ Atomic<bool> done_scanning_;
+
// The skipped blocks are memory blocks/chucks that were copies of
// objects that were unused due to lost races (cas failures) at
// object copy/forward pointer install. They are reused.
@@ -381,6 +398,11 @@
// ObjPtr since the GC may transition to suspended and runnable between phases.
mirror::Class* java_lang_Object_;
+ // Sweep array free buffer, used to sweep the spaces based on an array more
+ // efficiently, by recording dead objects to be freed in batches (see
+ // ConcurrentCopying::SweepArray).
+ MemMap sweep_array_free_buffer_mem_map_;
+
class ActivateReadBarrierEntrypointsCallback;
class ActivateReadBarrierEntrypointsCheckpoint;
class AssertToSpaceInvariantFieldVisitor;
@@ -394,7 +416,7 @@
template <bool kConcurrent> class GrayImmuneObjectVisitor;
class ImmuneSpaceScanObjVisitor;
class LostCopyVisitor;
- class RefFieldsVisitor;
+ template <bool kNoUnEvac> class RefFieldsVisitor;
class RevokeThreadLocalMarkStackCheckpoint;
class ScopedGcGraysImmuneObjects;
class ThreadFlipVisitor;
diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc
index 9767807..145bd02 100644
--- a/runtime/gc/collector/immune_spaces_test.cc
+++ b/runtime/gc/collector/immune_spaces_test.cc
@@ -40,22 +40,22 @@
class DummyImageSpace : public space::ImageSpace {
public:
- DummyImageSpace(MemMap* map,
+ DummyImageSpace(MemMap&& map,
accounting::ContinuousSpaceBitmap* live_bitmap,
std::unique_ptr<DummyOatFile>&& oat_file,
- std::unique_ptr<MemMap>&& oat_map)
+ MemMap&& oat_map)
: ImageSpace("DummyImageSpace",
/*image_location*/"",
- map,
+ std::move(map),
live_bitmap,
- map->End()),
+ map.End()),
oat_map_(std::move(oat_map)) {
oat_file_ = std::move(oat_file);
oat_file_non_owned_ = oat_file_.get();
}
private:
- std::unique_ptr<MemMap> oat_map_;
+ MemMap oat_map_;
};
class ImmuneSpacesTest : public CommonRuntimeTest {
@@ -83,39 +83,37 @@
uint8_t* oat_begin,
size_t oat_size) {
std::string error_str;
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous("DummyImageSpace",
- image_begin,
- image_size,
- PROT_READ | PROT_WRITE,
- /*low_4gb*/true,
- /*reuse*/false,
- &error_str));
- if (map == nullptr) {
+ MemMap map = MemMap::MapAnonymous("DummyImageSpace",
+ image_begin,
+ image_size,
+ PROT_READ | PROT_WRITE,
+ /*low_4gb*/true,
+ &error_str);
+ if (!map.IsValid()) {
LOG(ERROR) << error_str;
return nullptr;
}
CHECK(!live_bitmaps_.empty());
std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap(std::move(live_bitmaps_.back()));
live_bitmaps_.pop_back();
- std::unique_ptr<MemMap> oat_map(MemMap::MapAnonymous("OatMap",
- oat_begin,
- oat_size,
- PROT_READ | PROT_WRITE,
- /*low_4gb*/true,
- /*reuse*/false,
- &error_str));
- if (oat_map == nullptr) {
+ MemMap oat_map = MemMap::MapAnonymous("OatMap",
+ oat_begin,
+ oat_size,
+ PROT_READ | PROT_WRITE,
+ /*low_4gb*/true,
+ &error_str);
+ if (!oat_map.IsValid()) {
LOG(ERROR) << error_str;
return nullptr;
}
- std::unique_ptr<DummyOatFile> oat_file(new DummyOatFile(oat_map->Begin(), oat_map->End()));
+ std::unique_ptr<DummyOatFile> oat_file(new DummyOatFile(oat_map.Begin(), oat_map.End()));
// Create image header.
ImageSection sections[ImageHeader::kSectionCount];
- new (map->Begin()) ImageHeader(
- /*image_begin*/PointerToLowMemUInt32(map->Begin()),
- /*image_size*/map->Size(),
+ new (map.Begin()) ImageHeader(
+ /*image_begin*/PointerToLowMemUInt32(map.Begin()),
+ /*image_size*/map.Size(),
sections,
- /*image_roots*/PointerToLowMemUInt32(map->Begin()) + 1,
+ /*image_roots*/PointerToLowMemUInt32(map.Begin()) + 1,
/*oat_checksum*/0u,
// The oat file data in the header is always right after the image space.
/*oat_file_begin*/PointerToLowMemUInt32(oat_begin),
@@ -131,7 +129,7 @@
/*is_pic*/false,
ImageHeader::kStorageModeUncompressed,
/*storage_size*/0u);
- return new DummyImageSpace(map.release(),
+ return new DummyImageSpace(std::move(map),
live_bitmap.release(),
std::move(oat_file),
std::move(oat_map));
@@ -141,18 +139,17 @@
// returned address.
static uint8_t* GetContinuousMemoryRegion(size_t size) {
std::string error_str;
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous("reserve",
- nullptr,
- size,
- PROT_READ | PROT_WRITE,
- /*low_4gb*/true,
- /*reuse*/false,
- &error_str));
- if (map == nullptr) {
+ MemMap map = MemMap::MapAnonymous("reserve",
+ /* addr */ nullptr,
+ size,
+ PROT_READ | PROT_WRITE,
+ /*low_4gb*/ true,
+ &error_str);
+ if (!map.IsValid()) {
LOG(ERROR) << "Failed to allocate memory region " << error_str;
return nullptr;
}
- return map->Begin();
+ return map.Begin();
}
private:
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 2335964..997d3b6 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -103,12 +103,15 @@
is_concurrent_(is_concurrent),
live_stack_freeze_size_(0) {
std::string error_msg;
- MemMap* mem_map = MemMap::MapAnonymous(
- "mark sweep sweep array free buffer", nullptr,
+ sweep_array_free_buffer_mem_map_ = MemMap::MapAnonymous(
+ "mark sweep sweep array free buffer",
+ /* addr */ nullptr,
RoundUp(kSweepArrayChunkFreeSize * sizeof(mirror::Object*), kPageSize),
- PROT_READ | PROT_WRITE, false, false, &error_msg);
- CHECK(mem_map != nullptr) << "Couldn't allocate sweep array free buffer: " << error_msg;
- sweep_array_free_buffer_mem_map_.reset(mem_map);
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ CHECK(sweep_array_free_buffer_mem_map_.IsValid())
+ << "Couldn't allocate sweep array free buffer: " << error_msg;
}
void MarkSweep::InitializePhase() {
@@ -1207,7 +1210,7 @@
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
Thread* self = Thread::Current();
mirror::Object** chunk_free_buffer = reinterpret_cast<mirror::Object**>(
- sweep_array_free_buffer_mem_map_->BaseBegin());
+ sweep_array_free_buffer_mem_map_.BaseBegin());
size_t chunk_free_pos = 0;
ObjectBytePair freed;
ObjectBytePair freed_los;
@@ -1300,7 +1303,7 @@
t2.NewTiming("ResetStack");
allocations->Reset();
}
- sweep_array_free_buffer_mem_map_->MadviseDontNeedAndZero();
+ sweep_array_free_buffer_mem_map_.MadviseDontNeedAndZero();
}
void MarkSweep::Sweep(bool swap_bitmaps) {
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 5e0fe06..af2bb97 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -351,7 +351,10 @@
// Verification.
size_t live_stack_freeze_size_;
- std::unique_ptr<MemMap> sweep_array_free_buffer_mem_map_;
+ // Sweep array free buffer, used to sweep the spaces based on an array more
+ // efficiently, by recording dead objects to be freed in batches (see
+ // MarkSweep::SweepArray).
+ MemMap sweep_array_free_buffer_mem_map_;
private:
class CardScanTask;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 58becb1..7913354 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -102,7 +102,8 @@
// Sticky GC throughput adjustment, divided by 4. Increasing this causes sticky GC to occur more
// relative to partial/full GC. This may be desirable since sticky GCs interfere less with mutator
// threads (lower pauses, use less memory bandwidth).
-static constexpr double kStickyGcThroughputAdjustment = 1.0;
+static constexpr double kStickyGcThroughputAdjustment =
+ kEnableGenerationalConcurrentCopyingCollection ? 0.5 : 1.0;
// Whether or not we compact the zygote in PreZygoteFork.
static constexpr bool kCompactZygote = kMovingCollector;
// How many reserve entries are at the end of the allocation stack, these are only needed if the
@@ -260,6 +261,8 @@
verify_object_mode_(kVerifyObjectModeDisabled),
disable_moving_gc_count_(0),
semi_space_collector_(nullptr),
+ active_concurrent_copying_collector_(nullptr),
+ young_concurrent_copying_collector_(nullptr),
concurrent_copying_collector_(nullptr),
is_running_on_memory_tool_(Runtime::Current()->IsRunningOnMemoryTool()),
use_tlab_(use_tlab),
@@ -318,12 +321,14 @@
}
// Load image space(s).
+ std::vector<std::unique_ptr<space::ImageSpace>> boot_image_spaces;
if (space::ImageSpace::LoadBootImage(image_file_name,
image_instruction_set,
- &boot_image_spaces_,
+ &boot_image_spaces,
&requested_alloc_space_begin)) {
- for (auto space : boot_image_spaces_) {
- AddSpace(space);
+ for (std::unique_ptr<space::ImageSpace>& space : boot_image_spaces) {
+ boot_image_spaces_.push_back(space.get());
+ AddSpace(space.release());
}
}
@@ -358,8 +363,8 @@
if (foreground_collector_type_ == kCollectorTypeGSS) {
separate_non_moving_space = false;
}
- std::unique_ptr<MemMap> main_mem_map_1;
- std::unique_ptr<MemMap> main_mem_map_2;
+ MemMap main_mem_map_1;
+ MemMap main_mem_map_2;
// Gross hack to make dex2oat deterministic.
if (foreground_collector_type_ == kCollectorTypeMS &&
@@ -374,7 +379,7 @@
request_begin += non_moving_space_capacity;
}
std::string error_str;
- std::unique_ptr<MemMap> non_moving_space_mem_map;
+ MemMap non_moving_space_mem_map;
if (separate_non_moving_space) {
ScopedTrace trace2("Create separate non moving space");
// If we are the zygote, the non moving space becomes the zygote space when we run
@@ -383,11 +388,9 @@
const char* space_name = is_zygote ? kZygoteSpaceName : kNonMovingSpaceName;
// Reserve the non moving mem map before the other two since it needs to be at a specific
// address.
- non_moving_space_mem_map.reset(MapAnonymousPreferredAddress(space_name,
- requested_alloc_space_begin,
- non_moving_space_capacity,
- &error_str));
- CHECK(non_moving_space_mem_map != nullptr) << error_str;
+ non_moving_space_mem_map = MapAnonymousPreferredAddress(
+ space_name, requested_alloc_space_begin, non_moving_space_capacity, &error_str);
+ CHECK(non_moving_space_mem_map.IsValid()) << error_str;
// Try to reserve virtual memory at a lower address if we have a separate non moving space.
request_begin = kPreferredAllocSpaceBegin + non_moving_space_capacity;
}
@@ -395,27 +398,28 @@
if (foreground_collector_type_ != kCollectorTypeCC) {
ScopedTrace trace2("Create main mem map");
if (separate_non_moving_space || !is_zygote) {
- main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0],
- request_begin,
- capacity_,
- &error_str));
+ main_mem_map_1 = MapAnonymousPreferredAddress(
+ kMemMapSpaceName[0], request_begin, capacity_, &error_str);
} else {
// If no separate non-moving space and we are the zygote, the main space must come right
// after the image space to avoid a gap. This is required since we want the zygote space to
// be adjacent to the image space.
- main_mem_map_1.reset(MemMap::MapAnonymous(kMemMapSpaceName[0], request_begin, capacity_,
- PROT_READ | PROT_WRITE, true, false,
- &error_str));
+ main_mem_map_1 = MemMap::MapAnonymous(kMemMapSpaceName[0],
+ request_begin,
+ capacity_,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ true,
+ &error_str);
}
- CHECK(main_mem_map_1.get() != nullptr) << error_str;
+ CHECK(main_mem_map_1.IsValid()) << error_str;
}
if (support_homogeneous_space_compaction ||
background_collector_type_ == kCollectorTypeSS ||
foreground_collector_type_ == kCollectorTypeSS) {
ScopedTrace trace2("Create main mem map 2");
- main_mem_map_2.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[1], main_mem_map_1->End(),
- capacity_, &error_str));
- CHECK(main_mem_map_2.get() != nullptr) << error_str;
+ main_mem_map_2 = MapAnonymousPreferredAddress(
+ kMemMapSpaceName[1], main_mem_map_1.End(), capacity_, &error_str);
+ CHECK(main_mem_map_2.IsValid()) << error_str;
}
// Create the non moving space first so that bitmaps don't take up the address range.
@@ -423,10 +427,14 @@
ScopedTrace trace2("Add non moving space");
// Non moving space is always dlmalloc since we currently don't have support for multiple
// active rosalloc spaces.
- const size_t size = non_moving_space_mem_map->Size();
- non_moving_space_ = space::DlMallocSpace::CreateFromMemMap(
- non_moving_space_mem_map.release(), "zygote / non moving space", kDefaultStartingSize,
- initial_size, size, size, false);
+ const size_t size = non_moving_space_mem_map.Size();
+ non_moving_space_ = space::DlMallocSpace::CreateFromMemMap(std::move(non_moving_space_mem_map),
+ "zygote / non moving space",
+ kDefaultStartingSize,
+ initial_size,
+ size,
+ size,
+ /* can_move_objects */ false);
non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity());
CHECK(non_moving_space_ != nullptr) << "Failed creating non moving space "
<< requested_alloc_space_begin;
@@ -436,11 +444,10 @@
if (foreground_collector_type_ == kCollectorTypeCC) {
CHECK(separate_non_moving_space);
// Reserve twice the capacity, to allow evacuating every region for explicit GCs.
- MemMap* region_space_mem_map = space::RegionSpace::CreateMemMap(kRegionSpaceName,
- capacity_ * 2,
- request_begin);
- CHECK(region_space_mem_map != nullptr) << "No region space mem map";
- region_space_ = space::RegionSpace::Create(kRegionSpaceName, region_space_mem_map);
+ MemMap region_space_mem_map =
+ space::RegionSpace::CreateMemMap(kRegionSpaceName, capacity_ * 2, request_begin);
+ CHECK(region_space_mem_map.IsValid()) << "No region space mem map";
+ region_space_ = space::RegionSpace::Create(kRegionSpaceName, std::move(region_space_mem_map));
AddSpace(region_space_);
} else if (IsMovingGc(foreground_collector_type_) &&
foreground_collector_type_ != kCollectorTypeGSS) {
@@ -448,16 +455,16 @@
// We only to create the bump pointer if the foreground collector is a compacting GC.
// TODO: Place bump-pointer spaces somewhere to minimize size of card table.
bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 1",
- main_mem_map_1.release());
+ std::move(main_mem_map_1));
CHECK(bump_pointer_space_ != nullptr) << "Failed to create bump pointer space";
AddSpace(bump_pointer_space_);
temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2",
- main_mem_map_2.release());
+ std::move(main_mem_map_2));
CHECK(temp_space_ != nullptr) << "Failed to create bump pointer space";
AddSpace(temp_space_);
CHECK(separate_non_moving_space);
} else {
- CreateMainMallocSpace(main_mem_map_1.release(), initial_size, growth_limit_, capacity_);
+ CreateMainMallocSpace(std::move(main_mem_map_1), initial_size, growth_limit_, capacity_);
CHECK(main_space_ != nullptr);
AddSpace(main_space_);
if (!separate_non_moving_space) {
@@ -467,19 +474,23 @@
if (foreground_collector_type_ == kCollectorTypeGSS) {
CHECK_EQ(foreground_collector_type_, background_collector_type_);
// Create bump pointer spaces instead of a backup space.
- main_mem_map_2.release();
- bump_pointer_space_ = space::BumpPointerSpace::Create("Bump pointer space 1",
- kGSSBumpPointerSpaceCapacity, nullptr);
+ main_mem_map_2.Reset();
+ bump_pointer_space_ = space::BumpPointerSpace::Create(
+ "Bump pointer space 1", kGSSBumpPointerSpaceCapacity, /* requested_begin */ nullptr);
CHECK(bump_pointer_space_ != nullptr);
AddSpace(bump_pointer_space_);
- temp_space_ = space::BumpPointerSpace::Create("Bump pointer space 2",
- kGSSBumpPointerSpaceCapacity, nullptr);
+ temp_space_ = space::BumpPointerSpace::Create(
+ "Bump pointer space 2", kGSSBumpPointerSpaceCapacity, /* requested_begin */ nullptr);
CHECK(temp_space_ != nullptr);
AddSpace(temp_space_);
- } else if (main_mem_map_2.get() != nullptr) {
+ } else if (main_mem_map_2.IsValid()) {
const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
- main_space_backup_.reset(CreateMallocSpaceFromMemMap(main_mem_map_2.release(), initial_size,
- growth_limit_, capacity_, name, true));
+ main_space_backup_.reset(CreateMallocSpaceFromMemMap(std::move(main_mem_map_2),
+ initial_size,
+ growth_limit_,
+ capacity_,
+ name,
+ /* can_move_objects */ true));
CHECK(main_space_backup_.get() != nullptr);
// Add the space so its accounted for in the heap_begin and heap_end.
AddSpace(main_space_backup_.get());
@@ -594,11 +605,26 @@
}
if (MayUseCollector(kCollectorTypeCC)) {
concurrent_copying_collector_ = new collector::ConcurrentCopying(this,
+ /*young_gen*/false,
"",
measure_gc_performance);
+ if (kEnableGenerationalConcurrentCopyingCollection) {
+ young_concurrent_copying_collector_ = new collector::ConcurrentCopying(
+ this,
+ /*young_gen*/true,
+ "young",
+ measure_gc_performance);
+ }
+ active_concurrent_copying_collector_ = concurrent_copying_collector_;
DCHECK(region_space_ != nullptr);
concurrent_copying_collector_->SetRegionSpace(region_space_);
+ if (kEnableGenerationalConcurrentCopyingCollection) {
+ young_concurrent_copying_collector_->SetRegionSpace(region_space_);
+ }
garbage_collectors_.push_back(concurrent_copying_collector_);
+ if (kEnableGenerationalConcurrentCopyingCollection) {
+ garbage_collectors_.push_back(young_concurrent_copying_collector_);
+ }
}
}
if (!GetBootImageSpaces().empty() && non_moving_space_ != nullptr &&
@@ -613,7 +639,7 @@
first_space = space;
}
}
- bool no_gap = MemMap::CheckNoGaps(first_space->GetMemMap(), non_moving_space_->GetMemMap());
+ bool no_gap = MemMap::CheckNoGaps(*first_space->GetMemMap(), *non_moving_space_->GetMemMap());
if (!no_gap) {
PrintFileToLog("/proc/self/maps", LogSeverity::ERROR);
MemMap::DumpMaps(LOG_STREAM(ERROR), true);
@@ -632,14 +658,18 @@
}
}
-MemMap* Heap::MapAnonymousPreferredAddress(const char* name,
- uint8_t* request_begin,
- size_t capacity,
- std::string* out_error_str) {
+MemMap Heap::MapAnonymousPreferredAddress(const char* name,
+ uint8_t* request_begin,
+ size_t capacity,
+ std::string* out_error_str) {
while (true) {
- MemMap* map = MemMap::MapAnonymous(name, request_begin, capacity,
- PROT_READ | PROT_WRITE, true, false, out_error_str);
- if (map != nullptr || request_begin == nullptr) {
+ MemMap map = MemMap::MapAnonymous(name,
+ request_begin,
+ capacity,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb*/ true,
+ out_error_str);
+ if (map.IsValid() || request_begin == nullptr) {
return map;
}
// Retry a second time with no specified request begin.
@@ -651,7 +681,7 @@
return foreground_collector_type_ == type || background_collector_type_ == type;
}
-space::MallocSpace* Heap::CreateMallocSpaceFromMemMap(MemMap* mem_map,
+space::MallocSpace* Heap::CreateMallocSpaceFromMemMap(MemMap&& mem_map,
size_t initial_size,
size_t growth_limit,
size_t capacity,
@@ -660,12 +690,21 @@
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);
+ malloc_space = space::RosAllocSpace::CreateFromMemMap(std::move(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,
+ malloc_space = space::DlMallocSpace::CreateFromMemMap(std::move(mem_map),
+ name,
+ kDefaultStartingSize,
+ initial_size,
+ growth_limit,
+ capacity,
can_move_objects);
}
if (collector::SemiSpace::kUseRememberedSet) {
@@ -679,7 +718,9 @@
return malloc_space;
}
-void Heap::CreateMainMallocSpace(MemMap* mem_map, size_t initial_size, size_t growth_limit,
+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_) !=
@@ -698,7 +739,10 @@
RemoveRememberedSet(main_space_);
}
const char* name = kUseRosAlloc ? kRosAllocSpaceName[0] : kDlMallocSpaceName[0];
- main_space_ = CreateMallocSpaceFromMemMap(mem_map, initial_size, growth_limit, capacity, name,
+ main_space_ = CreateMallocSpaceFromMemMap(std::move(mem_map),
+ initial_size,
+ growth_limit,
+ capacity, name,
can_move_objects);
SetSpaceAsDefault(main_space_);
VLOG(heap) << "Created main space " << main_space_;
@@ -2012,17 +2056,17 @@
if (!IsMovingGc(collector_type_)) {
// Create the bump pointer space from the backup space.
CHECK(main_space_backup_ != nullptr);
- std::unique_ptr<MemMap> mem_map(main_space_backup_->ReleaseMemMap());
+ MemMap mem_map = main_space_backup_->ReleaseMemMap();
// We are transitioning from non moving GC -> moving GC, since we copied from the bump
// pointer space last transition it will be protected.
- CHECK(mem_map != nullptr);
- mem_map->Protect(PROT_READ | PROT_WRITE);
+ CHECK(mem_map.IsValid());
+ mem_map.Protect(PROT_READ | PROT_WRITE);
bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space",
- mem_map.release());
+ std::move(mem_map));
AddSpace(bump_pointer_space_);
collector = Compact(bump_pointer_space_, main_space_, kGcCauseCollectorTransition);
// Use the now empty main space mem map for the bump pointer temp space.
- mem_map.reset(main_space_->ReleaseMemMap());
+ mem_map = main_space_->ReleaseMemMap();
// Unset the pointers just in case.
if (dlmalloc_space_ == main_space_) {
dlmalloc_space_ = nullptr;
@@ -2038,7 +2082,7 @@
RemoveRememberedSet(main_space_backup_.get());
main_space_backup_.reset(nullptr); // Deletes the space.
temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2",
- mem_map.release());
+ std::move(mem_map));
AddSpace(temp_space_);
}
break;
@@ -2048,37 +2092,35 @@
case kCollectorTypeCMS: {
if (IsMovingGc(collector_type_)) {
CHECK(temp_space_ != nullptr);
- std::unique_ptr<MemMap> mem_map(temp_space_->ReleaseMemMap());
+ MemMap mem_map = temp_space_->ReleaseMemMap();
RemoveSpace(temp_space_);
temp_space_ = nullptr;
- mem_map->Protect(PROT_READ | PROT_WRITE);
- CreateMainMallocSpace(mem_map.get(),
+ mem_map.Protect(PROT_READ | PROT_WRITE);
+ CreateMainMallocSpace(std::move(mem_map),
kDefaultInitialSize,
- std::min(mem_map->Size(), growth_limit_),
- mem_map->Size());
- mem_map.release();
+ std::min(mem_map.Size(), growth_limit_),
+ mem_map.Size());
// Compact to the main space from the bump pointer space, don't need to swap semispaces.
AddSpace(main_space_);
collector = Compact(main_space_, bump_pointer_space_, kGcCauseCollectorTransition);
- mem_map.reset(bump_pointer_space_->ReleaseMemMap());
+ mem_map = bump_pointer_space_->ReleaseMemMap();
RemoveSpace(bump_pointer_space_);
bump_pointer_space_ = nullptr;
const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
// Temporarily unprotect the backup mem map so rosalloc can write the debug magic number.
if (kIsDebugBuild && kUseRosAlloc) {
- mem_map->Protect(PROT_READ | PROT_WRITE);
+ mem_map.Protect(PROT_READ | PROT_WRITE);
}
main_space_backup_.reset(CreateMallocSpaceFromMemMap(
- mem_map.get(),
+ std::move(mem_map),
kDefaultInitialSize,
- std::min(mem_map->Size(), growth_limit_),
- mem_map->Size(),
+ std::min(mem_map.Size(), growth_limit_),
+ mem_map.Size(),
name,
true));
if (kIsDebugBuild && kUseRosAlloc) {
- mem_map->Protect(PROT_NONE);
+ main_space_backup_->GetMemMap()->Protect(PROT_NONE);
}
- mem_map.release();
}
break;
}
@@ -2120,6 +2162,9 @@
gc_plan_.clear();
switch (collector_type_) {
case kCollectorTypeCC: {
+ if (kEnableGenerationalConcurrentCopyingCollection) {
+ gc_plan_.push_back(collector::kGcTypeSticky);
+ }
gc_plan_.push_back(collector::kGcTypeFull);
if (use_tlab_) {
ChangeAllocator(kAllocatorTypeRegionTLAB);
@@ -2159,7 +2204,8 @@
}
if (IsGcConcurrent()) {
concurrent_start_bytes_ =
- std::max(max_allowed_footprint_, kMinConcurrentRemainingBytes) - kMinConcurrentRemainingBytes;
+ std::max(max_allowed_footprint_, kMinConcurrentRemainingBytes) -
+ kMinConcurrentRemainingBytes;
} else {
concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
}
@@ -2323,11 +2369,13 @@
if (reset_main_space) {
main_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
madvise(main_space_->Begin(), main_space_->Capacity(), MADV_DONTNEED);
- MemMap* mem_map = main_space_->ReleaseMemMap();
+ MemMap mem_map = main_space_->ReleaseMemMap();
RemoveSpace(main_space_);
space::Space* old_main_space = main_space_;
- CreateMainMallocSpace(mem_map, kDefaultInitialSize, std::min(mem_map->Size(), growth_limit_),
- mem_map->Size());
+ CreateMainMallocSpace(std::move(mem_map),
+ kDefaultInitialSize,
+ std::min(mem_map.Size(), growth_limit_),
+ mem_map.Size());
delete old_main_space;
AddSpace(main_space_);
} else {
@@ -2567,12 +2615,19 @@
collector = semi_space_collector_;
break;
case kCollectorTypeCC:
- collector = concurrent_copying_collector_;
+ if (kEnableGenerationalConcurrentCopyingCollection) {
+ // TODO: Other threads must do the flip checkpoint before they start poking at
+ // active_concurrent_copying_collector_. So we should not concurrency here.
+ active_concurrent_copying_collector_ = (gc_type == collector::kGcTypeSticky) ?
+ young_concurrent_copying_collector_ : concurrent_copying_collector_;
+ active_concurrent_copying_collector_->SetRegionSpace(region_space_);
+ }
+ collector = active_concurrent_copying_collector_;
break;
default:
LOG(FATAL) << "Invalid collector type " << static_cast<size_t>(collector_type_);
}
- if (collector != concurrent_copying_collector_) {
+ if (collector != active_concurrent_copying_collector_) {
temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
if (kIsDebugBuild) {
// Try to read each page of the memory map in case mprotect didn't work properly b/19894268.
@@ -3435,7 +3490,8 @@
uint64_t target_size;
collector::GcType gc_type = collector_ran->GetGcType();
// Use the multiplier to grow more for foreground.
- const double multiplier = HeapGrowthMultiplier();
+ const double multiplier = HeapGrowthMultiplier(); // Use the multiplier to grow more for
+ // foreground.
const uint64_t adjusted_min_free = static_cast<uint64_t>(min_free_ * multiplier);
const uint64_t adjusted_max_free = static_cast<uint64_t>(max_free_ * multiplier);
if (gc_type != collector::kGcTypeSticky) {
@@ -3451,6 +3507,12 @@
collector::GcType non_sticky_gc_type = NonStickyGcType();
// Find what the next non sticky collector will be.
collector::GarbageCollector* non_sticky_collector = FindCollectorByGcType(non_sticky_gc_type);
+ if (kEnableGenerationalConcurrentCopyingCollection) {
+ if (non_sticky_collector == nullptr) {
+ non_sticky_collector = FindCollectorByGcType(collector::kGcTypePartial);
+ }
+ CHECK(non_sticky_collector != nullptr);
+ }
// If the throughput of the current sticky GC >= throughput of the non sticky collector, then
// do another sticky collection next.
// We also check that the bytes allocated aren't over the footprint limit in order to prevent a
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index d014372..90bac20 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -709,8 +709,15 @@
return zygote_space_ != nullptr;
}
+ // Returns the active concurrent copying collector.
collector::ConcurrentCopying* ConcurrentCopyingCollector() {
- return concurrent_copying_collector_;
+ if (kEnableGenerationalConcurrentCopyingCollection) {
+ DCHECK((active_concurrent_copying_collector_ == concurrent_copying_collector_) ||
+ (active_concurrent_copying_collector_ == young_concurrent_copying_collector_));
+ } else {
+ DCHECK_EQ(active_concurrent_copying_collector_, concurrent_copying_collector_);
+ }
+ return active_concurrent_copying_collector_;
}
CollectorType CurrentCollectorType() {
@@ -835,8 +842,10 @@
void FinishGC(Thread* self, collector::GcType gc_type) REQUIRES(!*gc_complete_lock_);
// Create a mem map with a preferred base address.
- static MemMap* MapAnonymousPreferredAddress(const char* name, uint8_t* request_begin,
- size_t capacity, std::string* out_error_str);
+ static MemMap MapAnonymousPreferredAddress(const char* name,
+ uint8_t* request_begin,
+ size_t capacity,
+ std::string* out_error_str);
bool SupportHSpaceCompaction() const {
// Returns true if we can do hspace compaction
@@ -979,13 +988,13 @@
collector::GarbageCollector* FindCollectorByGcType(collector::GcType gc_type);
// Create the main free list malloc space, either a RosAlloc space or DlMalloc space.
- void CreateMainMallocSpace(MemMap* mem_map,
+ 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,
+ space::MallocSpace* CreateMallocSpaceFromMemMap(MemMap&& mem_map,
size_t initial_size,
size_t growth_limit,
size_t capacity,
@@ -1335,6 +1344,8 @@
std::vector<collector::GarbageCollector*> garbage_collectors_;
collector::SemiSpace* semi_space_collector_;
+ collector::ConcurrentCopying* active_concurrent_copying_collector_;
+ collector::ConcurrentCopying* young_concurrent_copying_collector_;
collector::ConcurrentCopying* concurrent_copying_collector_;
const bool is_running_on_memory_tool_;
@@ -1425,6 +1436,7 @@
friend class collector::ConcurrentCopying;
friend class collector::MarkSweep;
friend class collector::SemiSpace;
+ friend class GCCriticalSection;
friend class ReferenceQueue;
friend class ScopedGCCriticalSection;
friend class VerifyReferenceCardVisitor;
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index c6b2120..8720a3e 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -33,19 +33,18 @@
MemMap::Init();
std::string error_msg;
// Reserve the preferred address to force the heap to use another one for testing.
- reserved_.reset(MemMap::MapAnonymous("ReserveMap",
- gc::Heap::kPreferredAllocSpaceBegin,
- 16 * KB,
- PROT_READ,
- /*low_4gb*/ true,
- /*reuse*/ false,
- &error_msg));
- ASSERT_TRUE(reserved_ != nullptr) << error_msg;
+ reserved_ = MemMap::MapAnonymous("ReserveMap",
+ gc::Heap::kPreferredAllocSpaceBegin,
+ 16 * KB,
+ PROT_READ,
+ /*low_4gb*/ true,
+ &error_msg);
+ ASSERT_TRUE(reserved_.IsValid()) << error_msg;
CommonRuntimeTest::SetUp();
}
private:
- std::unique_ptr<MemMap> reserved_;
+ MemMap reserved_;
};
TEST_F(HeapTest, ClearGrowthLimit) {
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc
index 321d22a..e25e279 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -76,19 +76,19 @@
Heap* heap = Runtime::Current()->GetHeap();
if (kUseBakerOrBrooksReadBarrier && heap->CurrentCollectorType() == kCollectorTypeCC &&
heap->ConcurrentCopyingCollector()->IsActive()) {
- // Change the gray ptr we left in ConcurrentCopying::ProcessMarkStackRef() to white.
+ // Change the gray ptr we left in ConcurrentCopying::ProcessMarkStackRef() to non-gray.
// We check IsActive() above because we don't want to do this when the zygote compaction
// collector (SemiSpace) is running.
CHECK(ref != nullptr);
collector::ConcurrentCopying* concurrent_copying = heap->ConcurrentCopyingCollector();
uint32_t rb_state = ref->GetReadBarrierState();
if (rb_state == ReadBarrier::GrayState()) {
- ref->AtomicSetReadBarrierState(ReadBarrier::GrayState(), ReadBarrier::WhiteState());
- CHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::WhiteState());
+ ref->AtomicSetReadBarrierState(ReadBarrier::GrayState(), ReadBarrier::NonGrayState());
+ CHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::NonGrayState());
} else {
- // In ConcurrentCopying::ProcessMarkStackRef() we may leave a white reference in the queue and
- // find it here, which is OK.
- CHECK_EQ(rb_state, ReadBarrier::WhiteState()) << "ref=" << ref << " rb_state=" << rb_state;
+ // In ConcurrentCopying::ProcessMarkStackRef() we may leave a non-gray reference in the queue
+ // and find it here, which is OK.
+ CHECK_EQ(rb_state, ReadBarrier::NonGrayState()) << "ref=" << ref << " rb_state=" << rb_state;
ObjPtr<mirror::Object> referent = ref->GetReferent<kWithoutReadBarrier>();
// The referent could be null if it's cleared by a mutator (Reference.clear()).
if (referent != nullptr) {
diff --git a/runtime/gc/scoped_gc_critical_section.cc b/runtime/gc/scoped_gc_critical_section.cc
index 2976dd0..7a0a6e8 100644
--- a/runtime/gc/scoped_gc_critical_section.cc
+++ b/runtime/gc/scoped_gc_critical_section.cc
@@ -24,20 +24,38 @@
namespace art {
namespace gc {
+const char* GCCriticalSection::Enter(GcCause cause, CollectorType type) {
+ Runtime::Current()->GetHeap()->StartGC(self_, cause, type);
+ if (self_ != nullptr) {
+ return self_->StartAssertNoThreadSuspension(section_name_);
+ } else {
+ // Workaround to avoid having to mark the whole function as NO_THREAD_SAFETY_ANALYSIS.
+ auto kludge = []() ACQUIRE(Roles::uninterruptible_) NO_THREAD_SAFETY_ANALYSIS {};
+ kludge();
+ return nullptr;
+ }
+}
+
+void GCCriticalSection::Exit(const char* old_cause) {
+ if (self_ != nullptr) {
+ self_->EndAssertNoThreadSuspension(old_cause);
+ } else {
+ // Workaround to avoid having to mark the whole function as NO_THREAD_SAFETY_ANALYSIS.
+ auto kludge = []() RELEASE(Roles::uninterruptible_) NO_THREAD_SAFETY_ANALYSIS {};
+ kludge();
+ }
+ Runtime::Current()->GetHeap()->FinishGC(self_, collector::kGcTypeNone);
+}
+
ScopedGCCriticalSection::ScopedGCCriticalSection(Thread* self,
GcCause cause,
CollectorType collector_type)
- : self_(self) {
- Runtime::Current()->GetHeap()->StartGC(self, cause, collector_type);
- if (self != nullptr) {
- old_cause_ = self->StartAssertNoThreadSuspension("ScopedGCCriticalSection");
- }
+ : critical_section_(self, "ScopedGCCriticalSection") {
+ old_no_suspend_reason_ = critical_section_.Enter(cause, collector_type);
}
+
ScopedGCCriticalSection::~ScopedGCCriticalSection() {
- if (self_ != nullptr) {
- self_->EndAssertNoThreadSuspension(old_cause_);
- }
- Runtime::Current()->GetHeap()->FinishGC(self_, collector::kGcTypeNone);
+ critical_section_.Exit(old_no_suspend_reason_);
}
} // namespace gc
diff --git a/runtime/gc/scoped_gc_critical_section.h b/runtime/gc/scoped_gc_critical_section.h
index 1271ff7..864bf87 100644
--- a/runtime/gc/scoped_gc_critical_section.h
+++ b/runtime/gc/scoped_gc_critical_section.h
@@ -27,6 +27,24 @@
namespace gc {
+// The use of ScopedGCCriticalSection should be preferred whenever possible.
+class GCCriticalSection {
+ public:
+ GCCriticalSection(Thread* self, const char* name)
+ : self_(self), section_name_(name) {}
+ ~GCCriticalSection() {}
+
+ // Starts a GCCriticalSection. Returns the previous no-suspension reason.
+ const char* Enter(GcCause cause, CollectorType type) ACQUIRE(Roles::uninterruptible_);
+
+ // Ends a GCCriticalSection. Takes the old no-suspension reason.
+ void Exit(const char* old_reason) RELEASE(Roles::uninterruptible_);
+
+ private:
+ Thread* const self_;
+ const char* section_name_;
+};
+
// Wait until the GC is finished and then prevent the GC from starting until the destructor. Used
// to prevent deadlocks in places where we call ClassLinker::VisitClass with all the threads
// suspended.
@@ -37,8 +55,8 @@
~ScopedGCCriticalSection() RELEASE(Roles::uninterruptible_);
private:
- Thread* const self_;
- const char* old_cause_;
+ GCCriticalSection critical_section_;
+ const char* old_no_suspend_reason_;
};
} // namespace gc
diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc
index e95da01..42453f5 100644
--- a/runtime/gc/space/bump_pointer_space.cc
+++ b/runtime/gc/space/bump_pointer_space.cc
@@ -28,23 +28,30 @@
uint8_t* requested_begin) {
capacity = RoundUp(capacity, kPageSize);
std::string error_msg;
- std::unique_ptr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), requested_begin, capacity,
- PROT_READ | PROT_WRITE, true, false,
- &error_msg));
- if (mem_map.get() == nullptr) {
+ MemMap mem_map = MemMap::MapAnonymous(name.c_str(),
+ requested_begin,
+ capacity,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ true,
+ &error_msg);
+ if (!mem_map.IsValid()) {
LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size "
<< PrettySize(capacity) << " with message " << error_msg;
return nullptr;
}
- return new BumpPointerSpace(name, mem_map.release());
+ return new BumpPointerSpace(name, std::move(mem_map));
}
-BumpPointerSpace* BumpPointerSpace::CreateFromMemMap(const std::string& name, MemMap* mem_map) {
- return new BumpPointerSpace(name, mem_map);
+BumpPointerSpace* BumpPointerSpace::CreateFromMemMap(const std::string& name, MemMap&& mem_map) {
+ return new BumpPointerSpace(name, std::move(mem_map));
}
BumpPointerSpace::BumpPointerSpace(const std::string& name, uint8_t* begin, uint8_t* limit)
- : ContinuousMemMapAllocSpace(name, nullptr, begin, begin, limit,
+ : ContinuousMemMapAllocSpace(name,
+ MemMap::Invalid(),
+ begin,
+ begin,
+ limit,
kGcRetentionPolicyAlwaysCollect),
growth_end_(limit),
objects_allocated_(0), bytes_allocated_(0),
@@ -53,10 +60,14 @@
num_blocks_(0) {
}
-BumpPointerSpace::BumpPointerSpace(const std::string& name, MemMap* mem_map)
- : ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->Begin(), mem_map->End(),
+BumpPointerSpace::BumpPointerSpace(const std::string& name, MemMap&& mem_map)
+ : ContinuousMemMapAllocSpace(name,
+ std::move(mem_map),
+ mem_map.Begin(),
+ mem_map.Begin(),
+ mem_map.End(),
kGcRetentionPolicyAlwaysCollect),
- growth_end_(mem_map->End()),
+ growth_end_(mem_map_.End()),
objects_allocated_(0), bytes_allocated_(0),
block_lock_("Block lock", kBumpPointerSpaceBlockLock),
main_block_size_(0),
diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h
index 5ba13ca..9b31558 100644
--- a/runtime/gc/space/bump_pointer_space.h
+++ b/runtime/gc/space/bump_pointer_space.h
@@ -47,7 +47,7 @@
// guaranteed to be granted, if it is required, the caller should call Begin on the returned
// space to confirm the request was granted.
static BumpPointerSpace* Create(const std::string& name, size_t capacity, uint8_t* requested_begin);
- static BumpPointerSpace* CreateFromMemMap(const std::string& name, MemMap* mem_map);
+ static BumpPointerSpace* CreateFromMemMap(const std::string& name, MemMap&& mem_map);
// Allocate num_bytes, returns null if the space is full.
mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
@@ -166,7 +166,7 @@
static constexpr size_t kAlignment = 8;
protected:
- BumpPointerSpace(const std::string& name, MemMap* mem_map);
+ BumpPointerSpace(const std::string& name, MemMap&& mem_map);
// Allocate a raw block of bytes.
uint8_t* AllocBlock(size_t bytes) REQUIRES(block_lock_);
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index 025c3f0..36d2161 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -38,41 +38,73 @@
static constexpr bool kPrefetchDuringDlMallocFreeList = true;
-DlMallocSpace::DlMallocSpace(MemMap* mem_map, size_t initial_size, const std::string& name,
- void* mspace, uint8_t* begin, uint8_t* end, uint8_t* limit,
- size_t growth_limit, bool can_move_objects, size_t starting_size)
- : MallocSpace(name, mem_map, begin, end, limit, growth_limit, true, can_move_objects,
+DlMallocSpace::DlMallocSpace(MemMap&& mem_map,
+ size_t initial_size,
+ const std::string& name,
+ void* mspace,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
+ size_t growth_limit,
+ bool can_move_objects,
+ size_t starting_size)
+ : MallocSpace(name,
+ std::move(mem_map),
+ begin,
+ end,
+ limit,
+ growth_limit,
+ /* create_bitmaps */ true,
+ can_move_objects,
starting_size, initial_size),
mspace_(mspace) {
CHECK(mspace != nullptr);
}
-DlMallocSpace* DlMallocSpace::CreateFromMemMap(MemMap* mem_map, const std::string& name,
- size_t starting_size, size_t initial_size,
- size_t growth_limit, size_t capacity,
+DlMallocSpace* DlMallocSpace::CreateFromMemMap(MemMap&& mem_map,
+ const std::string& name,
+ size_t starting_size,
+ size_t initial_size,
+ size_t growth_limit,
+ size_t capacity,
bool can_move_objects) {
- DCHECK(mem_map != nullptr);
- void* mspace = CreateMspace(mem_map->Begin(), starting_size, initial_size);
+ DCHECK(mem_map.IsValid());
+ void* mspace = CreateMspace(mem_map.Begin(), starting_size, initial_size);
if (mspace == nullptr) {
LOG(ERROR) << "Failed to initialize mspace for alloc space (" << name << ")";
return nullptr;
}
// Protect memory beyond the starting size. morecore will add r/w permissions when necessory
- uint8_t* end = mem_map->Begin() + starting_size;
+ uint8_t* end = mem_map.Begin() + starting_size;
if (capacity - starting_size > 0) {
CheckedCall(mprotect, name.c_str(), end, capacity - starting_size, PROT_NONE);
}
// Everything is set so record in immutable structure and leave
- uint8_t* begin = mem_map->Begin();
+ uint8_t* begin = mem_map.Begin();
if (Runtime::Current()->IsRunningOnMemoryTool()) {
return new MemoryToolMallocSpace<DlMallocSpace, kDefaultMemoryToolRedZoneBytes, true, false>(
- mem_map, initial_size, name, mspace, begin, end, begin + capacity, growth_limit,
- can_move_objects, starting_size);
+ std::move(mem_map),
+ initial_size,
+ name,
+ mspace,
+ begin,
+ end,
+ begin + capacity, growth_limit,
+ can_move_objects,
+ starting_size);
} else {
- return new DlMallocSpace(mem_map, initial_size, name, mspace, begin, end, begin + capacity,
- growth_limit, can_move_objects, starting_size);
+ return new DlMallocSpace(std::move(mem_map),
+ initial_size,
+ name,
+ mspace,
+ begin,
+ end,
+ begin + capacity,
+ growth_limit,
+ can_move_objects,
+ starting_size);
}
}
@@ -94,15 +126,20 @@
// will ask for this memory from sys_alloc which will fail as the footprint (this value plus the
// size of the large allocation) will be greater than the footprint limit.
size_t starting_size = kPageSize;
- MemMap* mem_map = CreateMemMap(name, starting_size, &initial_size, &growth_limit, &capacity,
- requested_begin);
- if (mem_map == nullptr) {
+ MemMap mem_map =
+ CreateMemMap(name, starting_size, &initial_size, &growth_limit, &capacity, requested_begin);
+ if (!mem_map.IsValid()) {
LOG(ERROR) << "Failed to create mem map for alloc space (" << name << ") of size "
<< PrettySize(capacity);
return nullptr;
}
- DlMallocSpace* space = CreateFromMemMap(mem_map, name, starting_size, initial_size,
- growth_limit, capacity, can_move_objects);
+ DlMallocSpace* space = CreateFromMemMap(std::move(mem_map),
+ name,
+ starting_size,
+ initial_size,
+ growth_limit,
+ capacity,
+ can_move_objects);
// We start out with only the initial size possibly containing objects.
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "DlMallocSpace::Create exiting (" << PrettyDuration(NanoTime() - start_time)
@@ -152,17 +189,37 @@
return result;
}
-MallocSpace* DlMallocSpace::CreateInstance(MemMap* mem_map, const std::string& name,
- void* allocator, uint8_t* begin, uint8_t* end,
- uint8_t* limit, size_t growth_limit,
+MallocSpace* DlMallocSpace::CreateInstance(MemMap&& mem_map,
+ const std::string& name,
+ void* allocator,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
+ size_t growth_limit,
bool can_move_objects) {
if (Runtime::Current()->IsRunningOnMemoryTool()) {
return new MemoryToolMallocSpace<DlMallocSpace, kDefaultMemoryToolRedZoneBytes, true, false>(
- mem_map, initial_size_, name, allocator, begin, end, limit, growth_limit,
- can_move_objects, starting_size_);
+ std::move(mem_map),
+ initial_size_,
+ name,
+ allocator,
+ begin,
+ end,
+ limit,
+ growth_limit,
+ can_move_objects,
+ starting_size_);
} else {
- return new DlMallocSpace(mem_map, initial_size_, name, allocator, begin, end, limit,
- growth_limit, can_move_objects, starting_size_);
+ return new DlMallocSpace(std::move(mem_map),
+ initial_size_,
+ name,
+ allocator,
+ begin,
+ end,
+ limit,
+ growth_limit,
+ can_move_objects,
+ starting_size_);
}
}
@@ -283,7 +340,7 @@
live_bitmap_->Clear();
mark_bitmap_->Clear();
SetEnd(Begin() + starting_size_);
- mspace_ = CreateMspace(mem_map_->Begin(), starting_size_, initial_size_);
+ mspace_ = CreateMspace(mem_map_.Begin(), starting_size_, initial_size_);
SetFootprintLimit(footprint_limit);
}
diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h
index 4c7fcfd..66537d5 100644
--- a/runtime/gc/space/dlmalloc_space.h
+++ b/runtime/gc/space/dlmalloc_space.h
@@ -34,9 +34,12 @@
class DlMallocSpace : public MallocSpace {
public:
// Create a DlMallocSpace from an existing mem_map.
- static DlMallocSpace* CreateFromMemMap(MemMap* mem_map, const std::string& name,
- size_t starting_size, size_t initial_size,
- size_t growth_limit, size_t capacity,
+ static DlMallocSpace* CreateFromMemMap(MemMap&& mem_map,
+ const std::string& name,
+ size_t starting_size,
+ size_t initial_size,
+ size_t growth_limit,
+ size_t capacity,
bool can_move_objects);
// Create a DlMallocSpace with the requested sizes. The requested
@@ -118,9 +121,14 @@
// allocations fail we GC before increasing the footprint limit and allowing the mspace to grow.
void SetFootprintLimit(size_t limit) OVERRIDE;
- MallocSpace* CreateInstance(MemMap* mem_map, const std::string& name, void* allocator,
- uint8_t* begin, uint8_t* end, uint8_t* limit, size_t growth_limit,
- bool can_move_objects);
+ MallocSpace* CreateInstance(MemMap&& mem_map,
+ const std::string& name,
+ void* allocator,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
+ size_t growth_limit,
+ bool can_move_objects) OVERRIDE;
uint64_t GetBytesAllocated() OVERRIDE;
uint64_t GetObjectsAllocated() OVERRIDE;
@@ -139,9 +147,16 @@
REQUIRES_SHARED(Locks::mutator_lock_);
protected:
- DlMallocSpace(MemMap* mem_map, size_t initial_size, const std::string& name, void* mspace,
- uint8_t* begin, uint8_t* end, uint8_t* limit, size_t growth_limit,
- bool can_move_objects, size_t starting_size);
+ DlMallocSpace(MemMap&& mem_map,
+ size_t initial_size,
+ const std::string& name,
+ void* mspace,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
+ size_t growth_limit,
+ bool can_move_objects,
+ size_t starting_size);
private:
mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated,
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 826f382..7178627 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -62,12 +62,12 @@
ImageSpace::ImageSpace(const std::string& image_filename,
const char* image_location,
- MemMap* mem_map,
+ MemMap&& mem_map,
accounting::ContinuousSpaceBitmap* live_bitmap,
uint8_t* end)
: MemMapSpace(image_filename,
- mem_map,
- mem_map->Begin(),
+ std::move(mem_map),
+ mem_map.Begin(),
end,
end,
kGcRetentionPolicyNeverCollect),
@@ -181,18 +181,19 @@
bool have_android_data = false;
*dalvik_cache_exists = false;
GetDalvikCache(GetInstructionSetString(image_isa),
- true,
+ /* create_if_absent */ true,
dalvik_cache,
&have_android_data,
dalvik_cache_exists,
is_global_cache);
- if (have_android_data && *dalvik_cache_exists) {
+ if (*dalvik_cache_exists) {
+ DCHECK(have_android_data);
// Always set output location even if it does not exist,
// so that the caller knows where to create the image.
//
// image_location = /system/framework/boot.art
- // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
+ // *image_filename = /data/dalvik-cache/<image_isa>/system@framework@boot.art
std::string error_msg;
if (!GetDalvikCacheFilename(image_location,
dalvik_cache->c_str(),
@@ -381,33 +382,6 @@
return nullptr;
}
-static bool ChecksumsMatch(const char* image_a, const char* image_b, std::string* error_msg) {
- DCHECK(error_msg != nullptr);
-
- ImageHeader hdr_a;
- ImageHeader hdr_b;
-
- if (!ReadSpecificImageHeader(image_a, &hdr_a)) {
- *error_msg = StringPrintf("Cannot read header of %s", image_a);
- return false;
- }
- if (!ReadSpecificImageHeader(image_b, &hdr_b)) {
- *error_msg = StringPrintf("Cannot read header of %s", image_b);
- return false;
- }
-
- if (hdr_a.GetOatChecksum() != hdr_b.GetOatChecksum()) {
- *error_msg = StringPrintf("Checksum mismatch: %u(%s) vs %u(%s)",
- hdr_a.GetOatChecksum(),
- image_a,
- hdr_b.GetOatChecksum(),
- image_b);
- return false;
- }
-
- return true;
-}
-
static bool CanWriteToDalvikCache(const InstructionSet isa) {
const std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(isa));
if (access(dalvik_cache.c_str(), O_RDWR) == 0) {
@@ -507,9 +481,9 @@
// Helper class encapsulating loading, so we can access private ImageSpace members (this is a
// friend class), but not declare functions in the header.
-class ImageSpaceLoader {
+class ImageSpace::Loader {
public:
- static std::unique_ptr<ImageSpace> Load(const char* image_location,
+ static std::unique_ptr<ImageSpace> Load(const std::string& image_location,
const std::string& image_filename,
bool is_zygote,
bool is_global_cache,
@@ -541,7 +515,7 @@
// Since we are the boot image, pass null since we load the oat file from the boot image oat
// file name.
return Init(image_filename.c_str(),
- image_location,
+ image_location.c_str(),
validate_oat_file,
/* oat_file */nullptr,
error_msg);
@@ -636,53 +610,53 @@
return nullptr;
}
- std::unique_ptr<MemMap> map;
+ MemMap map;
// GetImageBegin is the preferred address to map the image. If we manage to map the
// image at the image begin, the amount of fixup work required is minimized.
// If it is pic we will retry with error_msg for the failure case. Pass a null error_msg to
// avoid reading proc maps for a mapping failure and slowing everything down.
- map.reset(LoadImageFile(image_filename,
- image_location,
- *image_header,
- image_header->GetImageBegin(),
- file->Fd(),
- logger,
- image_header->IsPic() ? nullptr : error_msg));
+ map = LoadImageFile(image_filename,
+ image_location,
+ *image_header,
+ image_header->GetImageBegin(),
+ file->Fd(),
+ logger,
+ image_header->IsPic() ? nullptr : error_msg);
// If the header specifies PIC mode, we can also map at a random low_4gb address since we can
// relocate in-place.
- if (map == nullptr && image_header->IsPic()) {
- map.reset(LoadImageFile(image_filename,
- image_location,
- *image_header,
- /* address */ nullptr,
- file->Fd(),
- logger,
- error_msg));
+ if (!map.IsValid() && image_header->IsPic()) {
+ map = LoadImageFile(image_filename,
+ image_location,
+ *image_header,
+ /* address */ nullptr,
+ file->Fd(),
+ logger,
+ error_msg);
}
// Were we able to load something and continue?
- if (map == nullptr) {
+ if (!map.IsValid()) {
DCHECK(!error_msg->empty());
return nullptr;
}
- DCHECK_EQ(0, memcmp(image_header, map->Begin(), sizeof(ImageHeader)));
+ DCHECK_EQ(0, memcmp(image_header, map.Begin(), sizeof(ImageHeader)));
- std::unique_ptr<MemMap> image_bitmap_map(MemMap::MapFileAtAddress(nullptr,
- bitmap_section.Size(),
- PROT_READ, MAP_PRIVATE,
- file->Fd(),
- image_bitmap_offset,
- /*low_4gb*/false,
- /*reuse*/false,
- image_filename,
- error_msg));
- if (image_bitmap_map == nullptr) {
+ MemMap image_bitmap_map = MemMap::MapFileAtAddress(nullptr,
+ bitmap_section.Size(),
+ PROT_READ, MAP_PRIVATE,
+ file->Fd(),
+ image_bitmap_offset,
+ /*low_4gb*/false,
+ /*reuse*/false,
+ image_filename,
+ error_msg);
+ if (!image_bitmap_map.IsValid()) {
*error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str());
return nullptr;
}
// Loaded the map, use the image header from the file now in case we patch it with
// RelocateInPlace.
- image_header = reinterpret_cast<ImageHeader*>(map->Begin());
+ image_header = reinterpret_cast<ImageHeader*>(map.Begin());
const uint32_t bitmap_index = ImageSpace::bitmap_index_.fetch_add(1, std::memory_order_seq_cst);
std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u",
image_filename,
@@ -690,15 +664,15 @@
// Bitmap only needs to cover until the end of the mirror objects section.
const ImageSection& image_objects = image_header->GetObjectsSection();
// We only want the mirror object, not the ArtFields and ArtMethods.
- uint8_t* const image_end = map->Begin() + image_objects.End();
+ uint8_t* const image_end = map.Begin() + image_objects.End();
std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap;
{
TimingLogger::ScopedTiming timing("CreateImageBitmap", &logger);
bitmap.reset(
accounting::ContinuousSpaceBitmap::CreateFromMemMap(
bitmap_name,
- image_bitmap_map.release(),
- reinterpret_cast<uint8_t*>(map->Begin()),
+ std::move(image_bitmap_map),
+ reinterpret_cast<uint8_t*>(map.Begin()),
// Make sure the bitmap is aligned to card size instead of just bitmap word size.
RoundUp(image_objects.End(), gc::accounting::CardTable::kCardSize)));
if (bitmap == nullptr) {
@@ -709,7 +683,7 @@
{
TimingLogger::ScopedTiming timing("RelocateImage", &logger);
if (!RelocateInPlace(*image_header,
- map->Begin(),
+ map.Begin(),
bitmap.get(),
oat_file,
error_msg)) {
@@ -719,7 +693,7 @@
// We only want the mirror object, not the ArtFields and ArtMethods.
std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename,
image_location,
- map.release(),
+ std::move(map),
bitmap.release(),
image_end));
@@ -807,13 +781,13 @@
}
private:
- static MemMap* LoadImageFile(const char* image_filename,
- const char* image_location,
- const ImageHeader& image_header,
- uint8_t* address,
- int fd,
- TimingLogger& logger,
- std::string* error_msg) {
+ static MemMap LoadImageFile(const char* image_filename,
+ const char* image_location,
+ const ImageHeader& image_header,
+ uint8_t* address,
+ int fd,
+ TimingLogger& logger,
+ std::string* error_msg) {
TimingLogger::ScopedTiming timing("MapImageFile", &logger);
const ImageHeader::StorageMode storage_mode = image_header.GetStorageMode();
if (storage_mode == ImageHeader::kStorageModeUncompressed) {
@@ -835,45 +809,44 @@
*error_msg = StringPrintf("Invalid storage mode in image header %d",
static_cast<int>(storage_mode));
}
- return nullptr;
+ return MemMap::Invalid();
}
// Reserve output and decompress into it.
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous(image_location,
- address,
- image_header.GetImageSize(),
- PROT_READ | PROT_WRITE,
- /*low_4gb*/true,
- /*reuse*/false,
- error_msg));
- if (map != nullptr) {
+ MemMap map = MemMap::MapAnonymous(image_location,
+ address,
+ image_header.GetImageSize(),
+ PROT_READ | PROT_WRITE,
+ /*low_4gb*/ true,
+ error_msg);
+ if (map.IsValid()) {
const size_t stored_size = image_header.GetDataSize();
const size_t decompress_offset = sizeof(ImageHeader); // Skip the header.
- std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size,
- PROT_READ,
- MAP_PRIVATE,
- fd,
- /*offset*/0,
- /*low_4gb*/false,
- image_filename,
- error_msg));
- if (temp_map == nullptr) {
+ MemMap temp_map = MemMap::MapFile(sizeof(ImageHeader) + stored_size,
+ PROT_READ,
+ MAP_PRIVATE,
+ fd,
+ /*offset*/0,
+ /*low_4gb*/false,
+ image_filename,
+ error_msg);
+ if (!temp_map.IsValid()) {
DCHECK(error_msg == nullptr || !error_msg->empty());
- return nullptr;
+ return MemMap::Invalid();
}
- memcpy(map->Begin(), &image_header, sizeof(ImageHeader));
+ memcpy(map.Begin(), &image_header, sizeof(ImageHeader));
const uint64_t start = NanoTime();
// LZ4HC and LZ4 have same internal format, both use LZ4_decompress.
TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger);
const size_t decompressed_size = LZ4_decompress_safe(
- reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader),
- reinterpret_cast<char*>(map->Begin()) + decompress_offset,
+ reinterpret_cast<char*>(temp_map.Begin()) + sizeof(ImageHeader),
+ reinterpret_cast<char*>(map.Begin()) + decompress_offset,
stored_size,
- map->Size() - decompress_offset);
+ map.Size() - decompress_offset);
const uint64_t time = NanoTime() - start;
// Add one 1 ns to prevent possible divide by 0.
VLOG(image) << "Decompressing image took " << PrettyDuration(time) << " ("
- << PrettySize(static_cast<uint64_t>(map->Size()) * MsToNs(1000) / (time + 1))
+ << PrettySize(static_cast<uint64_t>(map.Size()) * MsToNs(1000) / (time + 1))
<< "/s)";
if (decompressed_size + sizeof(ImageHeader) != image_header.GetImageSize()) {
if (error_msg != nullptr) {
@@ -882,11 +855,11 @@
decompressed_size + sizeof(ImageHeader),
image_header.GetImageSize());
}
- return nullptr;
+ return MemMap::Invalid();
}
}
- return map.release();
+ return map;
}
class FixupVisitor : public ValueObject {
@@ -1471,6 +1444,181 @@
}
};
+class ImageSpace::BootImageLoader {
+ public:
+ BootImageLoader(const std::string& image_location, InstructionSet image_isa)
+ : image_location_(image_location),
+ image_isa_(image_isa),
+ is_zygote_(Runtime::Current()->IsZygote()),
+ has_system_(false),
+ has_cache_(false),
+ is_global_cache_(true),
+ dalvik_cache_exists_(false),
+ dalvik_cache_(),
+ cache_filename_() {
+ }
+
+ bool IsZygote() const { return is_zygote_; }
+
+ void FindImageFiles() {
+ std::string system_filename;
+ bool found_image = FindImageFilenameImpl(image_location_.c_str(),
+ image_isa_,
+ &has_system_,
+ &system_filename,
+ &dalvik_cache_exists_,
+ &dalvik_cache_,
+ &is_global_cache_,
+ &has_cache_,
+ &cache_filename_);
+ DCHECK(!dalvik_cache_exists_ || !dalvik_cache_.empty());
+ DCHECK_EQ(found_image, has_system_ || has_cache_);
+ }
+
+ bool HasSystem() const { return has_system_; }
+ bool HasCache() const { return has_cache_; }
+
+ bool DalvikCacheExists() const { return dalvik_cache_exists_; }
+ bool IsGlobalCache() const { return is_global_cache_; }
+
+ const std::string& GetDalvikCache() const {
+ return dalvik_cache_;
+ }
+
+ const std::string& GetCacheFilename() const {
+ return cache_filename_;
+ }
+
+ bool LoadFromSystem(/*out*/ std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/ uint8_t** oat_file_end,
+ /*out*/ std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string filename = GetSystemImageFilename(image_location_.c_str(), image_isa_);
+ std::vector<std::string> locations;
+ if (!GetBootClassPathImageLocations(image_location_, filename, &locations, error_msg)) {
+ return false;
+ }
+ std::vector<std::unique_ptr<ImageSpace>> spaces;
+ spaces.reserve(locations.size());
+ for (const std::string& location : locations) {
+ filename = GetSystemImageFilename(location.c_str(), image_isa_);
+ spaces.push_back(Loader::Load(location,
+ filename,
+ is_zygote_,
+ is_global_cache_,
+ /* validate_oat_file */ false,
+ error_msg));
+ if (spaces.back() == nullptr) {
+ return false;
+ }
+ }
+ *oat_file_end = GetOatFileEnd(spaces);
+ boot_image_spaces->swap(spaces);
+ return true;
+ }
+
+ bool LoadFromDalvikCache(
+ bool validate_system_checksums,
+ bool validate_oat_file,
+ /*out*/ std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/ uint8_t** oat_file_end,
+ /*out*/ std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(DalvikCacheExists());
+ std::vector<std::string> locations;
+ if (!GetBootClassPathImageLocations(image_location_, cache_filename_, &locations, error_msg)) {
+ return false;
+ }
+ std::vector<std::unique_ptr<ImageSpace>> spaces;
+ spaces.reserve(locations.size());
+ for (const std::string& location : locations) {
+ std::string filename;
+ if (!GetDalvikCacheFilename(location.c_str(), dalvik_cache_.c_str(), &filename, error_msg)) {
+ return false;
+ }
+ spaces.push_back(Loader::Load(location,
+ filename,
+ is_zygote_,
+ is_global_cache_,
+ validate_oat_file,
+ error_msg));
+ if (spaces.back() == nullptr) {
+ return false;
+ }
+ if (validate_system_checksums) {
+ ImageHeader system_hdr;
+ std::string system_filename = GetSystemImageFilename(location.c_str(), image_isa_);
+ if (!ReadSpecificImageHeader(system_filename.c_str(), &system_hdr)) {
+ *error_msg = StringPrintf("Cannot read header of %s", system_filename.c_str());
+ return false;
+ }
+ if (spaces.back()->GetImageHeader().GetOatChecksum() != system_hdr.GetOatChecksum()) {
+ *error_msg = StringPrintf("Checksum mismatch: %u(%s) vs %u(%s)",
+ spaces.back()->GetImageHeader().GetOatChecksum(),
+ filename.c_str(),
+ system_hdr.GetOatChecksum(),
+ system_filename.c_str());
+ return false;
+ }
+ }
+ }
+ *oat_file_end = GetOatFileEnd(spaces);
+ boot_image_spaces->swap(spaces);
+ return true;
+ }
+
+ private:
+ // Extract boot class path from oat file associated with `image_filename`
+ // and list all associated image locations.
+ static bool GetBootClassPathImageLocations(const std::string& image_location,
+ const std::string& image_filename,
+ /*out*/ std::vector<std::string>* all_locations,
+ /*out*/ std::string* error_msg) {
+ std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_filename);
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1,
+ oat_filename,
+ oat_filename,
+ /* requested_base */ nullptr,
+ /* oat_file_begin */ nullptr,
+ /* executable */ false,
+ /* low_4gb */ false,
+ /* abs_dex_location */ nullptr,
+ error_msg));
+ if (oat_file == nullptr) {
+ *error_msg = StringPrintf("Failed to open oat file '%s' for image file %s: %s",
+ oat_filename.c_str(),
+ image_filename.c_str(),
+ error_msg->c_str());
+ return false;
+ }
+ const OatHeader& oat_header = oat_file->GetOatHeader();
+ const char* boot_classpath = oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
+ all_locations->push_back(image_location);
+ if (boot_classpath != nullptr && boot_classpath[0] != 0) {
+ ExtractMultiImageLocations(image_location, boot_classpath, all_locations);
+ }
+ return true;
+ }
+
+ uint8_t* GetOatFileEnd(const std::vector<std::unique_ptr<ImageSpace>>& spaces) {
+ DCHECK(std::is_sorted(
+ spaces.begin(),
+ spaces.end(),
+ [](const std::unique_ptr<ImageSpace>& lhs, const std::unique_ptr<ImageSpace>& rhs) {
+ return lhs->GetOatFileEnd() < rhs->GetOatFileEnd();
+ }));
+ return AlignUp(spaces.back()->GetOatFileEnd(), kPageSize);
+ }
+
+ const std::string& image_location_;
+ InstructionSet image_isa_;
+ bool is_zygote_;
+ bool has_system_;
+ bool has_cache_;
+ bool is_global_cache_;
+ bool dalvik_cache_exists_;
+ std::string dalvik_cache_;
+ std::string cache_filename_;
+};
+
static constexpr uint64_t kLowSpaceValue = 50 * MB;
static constexpr uint64_t kTmpFsSentinelValue = 384 * MB;
@@ -1506,70 +1654,56 @@
return true;
}
-std::unique_ptr<ImageSpace> ImageSpace::CreateBootImage(const char* image_location,
- const InstructionSet image_isa,
- bool secondary_image,
- std::string* error_msg) {
+bool ImageSpace::LoadBootImage(
+ const std::string& image_location,
+ const InstructionSet image_isa,
+ /*out*/ std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/ uint8_t** oat_file_end) {
ScopedTrace trace(__FUNCTION__);
+ DCHECK(boot_image_spaces != nullptr);
+ DCHECK(boot_image_spaces->empty());
+ DCHECK(oat_file_end != nullptr);
+ DCHECK_NE(image_isa, InstructionSet::kNone);
+
+ if (image_location.empty()) {
+ return false;
+ }
+
+ BootImageLoader loader(image_location, image_isa);
+
// Step 0: Extra zygote work.
// Step 0.a: If we're the zygote, mark boot.
- const bool is_zygote = Runtime::Current()->IsZygote();
- if (is_zygote && !secondary_image && CanWriteToDalvikCache(image_isa)) {
+ if (loader.IsZygote() && CanWriteToDalvikCache(image_isa)) {
MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
}
+ loader.FindImageFiles();
+
// Step 0.b: If we're the zygote, check for free space, and prune the cache preemptively,
// if necessary. While the runtime may be fine (it is pretty tolerant to
// out-of-disk-space situations), other parts of the platform are not.
//
// The advantage of doing this proactively is that the later steps are simplified,
// i.e., we do not need to code retries.
- std::string system_filename;
- bool has_system = false;
- std::string cache_filename;
- bool has_cache = false;
- bool dalvik_cache_exists = false;
- bool is_global_cache = true;
- std::string dalvik_cache;
- bool found_image = FindImageFilenameImpl(image_location,
- image_isa,
- &has_system,
- &system_filename,
- &dalvik_cache_exists,
- &dalvik_cache,
- &is_global_cache,
- &has_cache,
- &cache_filename);
-
bool dex2oat_enabled = Runtime::Current()->IsImageDex2OatEnabled();
- if (is_zygote && dalvik_cache_exists && !secondary_image) {
+ if (loader.IsZygote() && loader.DalvikCacheExists()) {
// Extra checks for the zygote. These only apply when loading the first image, explained below.
+ const std::string& dalvik_cache = loader.GetDalvikCache();
DCHECK(!dalvik_cache.empty());
std::string local_error_msg;
// All secondary images are verified when the primary image is verified.
- bool verified = VerifyImage(image_location, dalvik_cache.c_str(), image_isa, &local_error_msg);
- // If we prune for space at a secondary image, we may end up in a crash loop with the _exit
- // path.
+ bool verified =
+ VerifyImage(image_location.c_str(), dalvik_cache.c_str(), image_isa, &local_error_msg);
bool check_space = CheckSpace(dalvik_cache, &local_error_msg);
if (!verified || !check_space) {
- // Note: it is important to only prune for space on the primary image, or we will hit the
- // restart path.
LOG(WARNING) << local_error_msg << " Preemptively pruning the dalvik cache.";
PruneDalvikCache(image_isa);
// Re-evaluate the image.
- found_image = FindImageFilenameImpl(image_location,
- image_isa,
- &has_system,
- &system_filename,
- &dalvik_cache_exists,
- &dalvik_cache,
- &is_global_cache,
- &has_cache,
- &cache_filename);
+ loader.FindImageFiles();
}
if (!check_space) {
// Disable compilation/patching - we do not want to fill up the space again.
@@ -1580,39 +1714,16 @@
// Collect all the errors.
std::vector<std::string> error_msgs;
- // Step 1: Check if we have an existing and relocated image.
-
- // Step 1.a: Have files in system and cache. Then they need to match.
- if (found_image && has_system && has_cache) {
+ // Step 1: Check if we have an existing image in the dalvik cache.
+ if (loader.HasCache()) {
std::string local_error_msg;
- // Check that the files are matching.
- if (ChecksumsMatch(system_filename.c_str(), cache_filename.c_str(), &local_error_msg)) {
- std::unique_ptr<ImageSpace> relocated_space =
- ImageSpaceLoader::Load(image_location,
- cache_filename,
- is_zygote,
- is_global_cache,
- /* validate_oat_file */ false,
- &local_error_msg);
- if (relocated_space != nullptr) {
- return relocated_space;
- }
- }
- error_msgs.push_back(local_error_msg);
- }
-
- // Step 1.b: Only have a cache file.
- if (found_image && !has_system && has_cache) {
- std::string local_error_msg;
- std::unique_ptr<ImageSpace> cache_space =
- ImageSpaceLoader::Load(image_location,
- cache_filename,
- is_zygote,
- is_global_cache,
- /* validate_oat_file */ true,
- &local_error_msg);
- if (cache_space != nullptr) {
- return cache_space;
+ // If we have system image, validate system image checksums, otherwise validate the oat file.
+ if (loader.LoadFromDalvikCache(/* validate_system_checksums */ loader.HasSystem(),
+ /* validate_oat_file */ !loader.HasSystem(),
+ boot_image_spaces,
+ oat_file_end,
+ &local_error_msg)) {
+ return true;
}
error_msgs.push_back(local_error_msg);
}
@@ -1622,83 +1733,64 @@
// Step 2.a: We are not required to relocate it. Then we can use it directly.
bool relocate = Runtime::Current()->ShouldRelocate();
- if (found_image && has_system && !relocate) {
+ if (loader.HasSystem() && !relocate) {
std::string local_error_msg;
- std::unique_ptr<ImageSpace> system_space =
- ImageSpaceLoader::Load(image_location,
- system_filename,
- is_zygote,
- is_global_cache,
- /* validate_oat_file */ false,
- &local_error_msg);
- if (system_space != nullptr) {
- return system_space;
+ if (loader.LoadFromSystem(boot_image_spaces, oat_file_end, &local_error_msg)) {
+ return true;
}
error_msgs.push_back(local_error_msg);
}
- // Step 2.b: We require a relocated image. Then we must patch it. This step fails if this is a
- // secondary image.
- if (found_image && has_system && relocate) {
+ // Step 2.b: We require a relocated image. Then we must patch it.
+ if (loader.HasSystem() && relocate) {
std::string local_error_msg;
if (!dex2oat_enabled) {
local_error_msg = "Patching disabled.";
- } else if (secondary_image) {
- // We really want a working image. Prune and restart.
- PruneDalvikCache(image_isa);
- _exit(1);
- } else if (ImageCreationAllowed(is_global_cache, image_isa, &local_error_msg)) {
- bool patch_success =
- RelocateImage(image_location, dalvik_cache.c_str(), image_isa, &local_error_msg);
+ } else if (ImageCreationAllowed(loader.IsGlobalCache(), image_isa, &local_error_msg)) {
+ bool patch_success = RelocateImage(
+ image_location.c_str(), loader.GetDalvikCache().c_str(), image_isa, &local_error_msg);
if (patch_success) {
- std::unique_ptr<ImageSpace> patched_space =
- ImageSpaceLoader::Load(image_location,
- cache_filename,
- is_zygote,
- is_global_cache,
- /* validate_oat_file */ false,
- &local_error_msg);
- if (patched_space != nullptr) {
- return patched_space;
+ if (loader.LoadFromDalvikCache(/* validate_system_checksums */ false,
+ /* validate_oat_file */ false,
+ boot_image_spaces,
+ oat_file_end,
+ &local_error_msg)) {
+ return true;
}
}
}
error_msgs.push_back(StringPrintf("Cannot relocate image %s to %s: %s",
- image_location,
- cache_filename.c_str(),
+ image_location.c_str(),
+ loader.GetCacheFilename().c_str(),
local_error_msg.c_str()));
}
- // Step 3: We do not have an existing image in /system, so generate an image into the dalvik
- // cache. This step fails if this is a secondary image.
- if (!has_system) {
+ // Step 3: We do not have an existing image in /system,
+ // so generate an image into the dalvik cache.
+ if (!loader.HasSystem() && loader.DalvikCacheExists()) {
std::string local_error_msg;
if (!dex2oat_enabled) {
local_error_msg = "Image compilation disabled.";
- } else if (secondary_image) {
- local_error_msg = "Cannot compile a secondary image.";
- } else if (ImageCreationAllowed(is_global_cache, image_isa, &local_error_msg)) {
- bool compilation_success = GenerateImage(cache_filename, image_isa, &local_error_msg);
+ } else if (ImageCreationAllowed(loader.IsGlobalCache(), image_isa, &local_error_msg)) {
+ bool compilation_success =
+ GenerateImage(loader.GetCacheFilename(), image_isa, &local_error_msg);
if (compilation_success) {
- std::unique_ptr<ImageSpace> compiled_space =
- ImageSpaceLoader::Load(image_location,
- cache_filename,
- is_zygote,
- is_global_cache,
- /* validate_oat_file */ false,
- &local_error_msg);
- if (compiled_space != nullptr) {
- return compiled_space;
+ if (loader.LoadFromDalvikCache(/* validate_system_checksums */ false,
+ /* validate_oat_file */ false,
+ boot_image_spaces,
+ oat_file_end,
+ &local_error_msg)) {
+ return true;
}
}
}
error_msgs.push_back(StringPrintf("Cannot compile image to %s: %s",
- cache_filename.c_str(),
+ loader.GetCacheFilename().c_str(),
local_error_msg.c_str()));
}
- // We failed. Prune the cache the free up space, create a compound error message and return no
- // image.
+ // We failed. Prune the cache the free up space, create a compound error message
+ // and return false.
PruneDalvikCache(image_isa);
std::ostringstream oss;
@@ -1709,84 +1801,11 @@
}
oss << msg;
}
- *error_msg = oss.str();
- return nullptr;
-}
+ LOG(ERROR) << "Could not create image space with image file '" << image_location << "'. "
+ << "Attempting to fall back to imageless running. Error was: " << oss.str();
-bool ImageSpace::LoadBootImage(const std::string& image_file_name,
- const InstructionSet image_instruction_set,
- std::vector<space::ImageSpace*>* boot_image_spaces,
- uint8_t** oat_file_end) {
- DCHECK(boot_image_spaces != nullptr);
- DCHECK(boot_image_spaces->empty());
- DCHECK(oat_file_end != nullptr);
- DCHECK_NE(image_instruction_set, InstructionSet::kNone);
-
- if (image_file_name.empty()) {
- return false;
- }
-
- // For code reuse, handle this like a work queue.
- std::vector<std::string> image_file_names;
- image_file_names.push_back(image_file_name);
-
- bool error = false;
- uint8_t* oat_file_end_tmp = *oat_file_end;
-
- for (size_t index = 0; index < image_file_names.size(); ++index) {
- std::string& image_name = image_file_names[index];
- std::string error_msg;
- std::unique_ptr<space::ImageSpace> boot_image_space_uptr = CreateBootImage(
- image_name.c_str(),
- image_instruction_set,
- index > 0,
- &error_msg);
- if (boot_image_space_uptr != nullptr) {
- space::ImageSpace* boot_image_space = boot_image_space_uptr.release();
- boot_image_spaces->push_back(boot_image_space);
- // Oat files referenced by image files immediately follow them in memory, ensure alloc space
- // isn't going to get in the middle
- uint8_t* oat_file_end_addr = boot_image_space->GetImageHeader().GetOatFileEnd();
- CHECK_GT(oat_file_end_addr, boot_image_space->End());
- oat_file_end_tmp = AlignUp(oat_file_end_addr, kPageSize);
-
- if (index == 0) {
- // If this was the first space, check whether there are more images to load.
- const OatFile* boot_oat_file = boot_image_space->GetOatFile();
- if (boot_oat_file == nullptr) {
- continue;
- }
-
- const OatHeader& boot_oat_header = boot_oat_file->GetOatHeader();
- const char* boot_classpath =
- boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
- if (boot_classpath == nullptr) {
- continue;
- }
-
- ExtractMultiImageLocations(image_file_name, boot_classpath, &image_file_names);
- }
- } else {
- error = true;
- LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
- << "Attempting to fall back to imageless running. Error was: " << error_msg
- << "\nAttempted image: " << image_name;
- break;
- }
- }
-
- if (error) {
- // Remove already loaded spaces.
- for (space::Space* loaded_space : *boot_image_spaces) {
- delete loaded_space;
- }
- boot_image_spaces->clear();
- return false;
- }
-
- *oat_file_end = oat_file_end_tmp;
- return true;
+ return false;
}
ImageSpace::~ImageSpace() {
@@ -1815,11 +1834,7 @@
std::unique_ptr<ImageSpace> ImageSpace::CreateFromAppImage(const char* image,
const OatFile* oat_file,
std::string* error_msg) {
- return ImageSpaceLoader::Init(image,
- image,
- /*validate_oat_file*/false,
- oat_file,
- /*out*/error_msg);
+ return Loader::Init(image, image, /*validate_oat_file*/false, oat_file, /*out*/error_msg);
}
const OatFile* ImageSpace::GetOatFile() const {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 3383d6b..20bce66 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -41,11 +41,11 @@
// On successful return, the loaded spaces are added to boot_image_spaces (which must be
// empty on entry) and oat_file_end is updated with the (page-aligned) end of the last
// oat file.
- static bool LoadBootImage(const std::string& image_file_name,
- const InstructionSet image_instruction_set,
- std::vector<space::ImageSpace*>* boot_image_spaces,
- uint8_t** oat_file_end)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ static bool LoadBootImage(
+ const std::string& image_location,
+ const InstructionSet image_isa,
+ /*out*/ std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/ uint8_t** oat_file_end) REQUIRES_SHARED(Locks::mutator_lock_);
// Try to open an existing app image space.
static std::unique_ptr<ImageSpace> CreateFromAppImage(const char* image,
@@ -182,7 +182,7 @@
ImageSpace(const std::string& name,
const char* image_location,
- MemMap* mem_map,
+ MemMap&& mem_map,
accounting::ContinuousSpaceBitmap* live_bitmap,
uint8_t* end);
@@ -197,23 +197,11 @@
const std::string image_location_;
- friend class ImageSpaceLoader;
friend class Space;
private:
- // Create a boot image space from an image file for a specified instruction
- // set. Cannot be used for future allocation or collected.
- //
- // Create also opens the OatFile associated with the image file so
- // that it be contiguously allocated with the image before the
- // creation of the alloc space. The ReleaseOatFile will later be
- // used to transfer ownership of the OatFile to the ClassLinker when
- // it is initialized.
- static std::unique_ptr<ImageSpace> CreateBootImage(const char* image,
- InstructionSet image_isa,
- bool secondary_image,
- std::string* error_msg)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ class Loader;
+ class BootImageLoader;
DISALLOW_COPY_AND_ASSIGN(ImageSpace);
};
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
index f202a43..a1ffa06 100644
--- a/runtime/gc/space/image_space_test.cc
+++ b/runtime/gc/space/image_space_test.cc
@@ -150,6 +150,48 @@
EXPECT_FALSE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty());
}
+class NoAccessAndroidDataTest : public ImageSpaceLoadingTest<false, true, false, true> {
+ protected:
+ void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+ const char* android_data = getenv("ANDROID_DATA");
+ CHECK(android_data != nullptr);
+ old_android_data_ = android_data;
+ bad_android_data_ = old_android_data_ + "/no-android-data";
+ int result = setenv("ANDROID_DATA", bad_android_data_.c_str(), /* replace */ 1);
+ CHECK_EQ(result, 0) << strerror(errno);
+ result = mkdir(bad_android_data_.c_str(), /* mode */ 0700);
+ CHECK_EQ(result, 0) << strerror(errno);
+ // Create a regular file "dalvik_cache". GetDalvikCache() shall get EEXIST
+ // when trying to create a directory with the same name and creating a
+ // subdirectory for a particular architecture shall fail.
+ bad_dalvik_cache_ = bad_android_data_ + "/dalvik-cache";
+ int fd = creat(bad_dalvik_cache_.c_str(), /* mode */ 0);
+ CHECK_NE(fd, -1) << strerror(errno);
+ result = close(fd);
+ CHECK_EQ(result, 0) << strerror(errno);
+ ImageSpaceLoadingTest<false, true, false, true>::SetUpRuntimeOptions(options);
+ }
+
+ void TearDown() OVERRIDE {
+ int result = unlink(bad_dalvik_cache_.c_str());
+ CHECK_EQ(result, 0) << strerror(errno);
+ result = rmdir(bad_android_data_.c_str());
+ CHECK_EQ(result, 0) << strerror(errno);
+ result = setenv("ANDROID_DATA", old_android_data_.c_str(), /* replace */ 1);
+ CHECK_EQ(result, 0) << strerror(errno);
+ ImageSpaceLoadingTest<false, true, false, true>::TearDown();
+ }
+
+ private:
+ std::string old_android_data_;
+ std::string bad_android_data_;
+ std::string bad_dalvik_cache_;
+};
+
+TEST_F(NoAccessAndroidDataTest, Test) {
+ EXPECT_TRUE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty());
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index a24ca32..76ea9fd 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -48,10 +48,6 @@
// Historical note: We were deleting large objects to keep Valgrind happy if there were
// any large objects such as Dex cache arrays which aren't freed since they are held live
// by the class linker.
- MutexLock mu(Thread::Current(), lock_);
- for (auto& m : large_objects_) {
- delete m.second.mem_map;
- }
}
mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
@@ -139,16 +135,20 @@
size_t* bytes_allocated, size_t* usable_size,
size_t* bytes_tl_bulk_allocated) {
std::string error_msg;
- MemMap* mem_map = MemMap::MapAnonymous("large object space allocation", nullptr, num_bytes,
- PROT_READ | PROT_WRITE, true, false, &error_msg);
- if (UNLIKELY(mem_map == nullptr)) {
+ MemMap mem_map = MemMap::MapAnonymous("large object space allocation",
+ /* addr */ nullptr,
+ num_bytes,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ true,
+ &error_msg);
+ if (UNLIKELY(!mem_map.IsValid())) {
LOG(WARNING) << "Large object allocation failed: " << error_msg;
return nullptr;
}
- mirror::Object* const obj = reinterpret_cast<mirror::Object*>(mem_map->Begin());
+ mirror::Object* const obj = reinterpret_cast<mirror::Object*>(mem_map.Begin());
+ const size_t allocation_size = mem_map.BaseSize();
MutexLock mu(self, lock_);
- large_objects_.Put(obj, LargeObject {mem_map, false /* not zygote */});
- const size_t allocation_size = mem_map->BaseSize();
+ large_objects_.Put(obj, LargeObject {std::move(mem_map), false /* not zygote */});
DCHECK(bytes_allocated != nullptr);
if (begin_ == nullptr || begin_ > reinterpret_cast<uint8_t*>(obj)) {
@@ -191,13 +191,11 @@
Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(FATAL_WITHOUT_ABORT));
LOG(FATAL) << "Attempted to free large object " << ptr << " which was not live";
}
- MemMap* mem_map = it->second.mem_map;
- const size_t map_size = mem_map->BaseSize();
+ const size_t map_size = it->second.mem_map.BaseSize();
DCHECK_GE(num_bytes_allocated_, map_size);
size_t allocation_size = map_size;
num_bytes_allocated_ -= allocation_size;
--num_objects_allocated_;
- delete mem_map;
large_objects_.erase(it);
return allocation_size;
}
@@ -206,7 +204,7 @@
MutexLock mu(Thread::Current(), lock_);
auto it = large_objects_.find(obj);
CHECK(it != large_objects_.end()) << "Attempted to get size of a large object which is not live";
- size_t alloc_size = it->second.mem_map->BaseSize();
+ size_t alloc_size = it->second.mem_map.BaseSize();
if (usable_size != nullptr) {
*usable_size = alloc_size;
}
@@ -227,7 +225,7 @@
void LargeObjectMapSpace::Walk(DlMallocSpace::WalkCallback callback, void* arg) {
MutexLock mu(Thread::Current(), lock_);
for (auto& pair : large_objects_) {
- MemMap* mem_map = pair.second.mem_map;
+ MemMap* mem_map = &pair.second.mem_map;
callback(mem_map->Begin(), mem_map->End(), mem_map->Size(), arg);
callback(nullptr, nullptr, 0, arg);
}
@@ -326,7 +324,7 @@
size_t FreeListSpace::GetSlotIndexForAllocationInfo(const AllocationInfo* info) const {
DCHECK_GE(info, allocation_info_);
- DCHECK_LT(info, reinterpret_cast<AllocationInfo*>(allocation_info_map_->End()));
+ DCHECK_LT(info, reinterpret_cast<AllocationInfo*>(allocation_info_map_.End()));
return info - allocation_info_;
}
@@ -350,28 +348,37 @@
FreeListSpace* FreeListSpace::Create(const std::string& name, uint8_t* requested_begin, size_t size) {
CHECK_EQ(size % kAlignment, 0U);
std::string error_msg;
- MemMap* mem_map = MemMap::MapAnonymous(name.c_str(), requested_begin, size,
- PROT_READ | PROT_WRITE, true, false, &error_msg);
- CHECK(mem_map != nullptr) << "Failed to allocate large object space mem map: " << error_msg;
- return new FreeListSpace(name, mem_map, mem_map->Begin(), mem_map->End());
+ MemMap mem_map = MemMap::MapAnonymous(name.c_str(),
+ requested_begin,
+ size,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ true,
+ &error_msg);
+ CHECK(mem_map.IsValid()) << "Failed to allocate large object space mem map: " << error_msg;
+ return new FreeListSpace(name, std::move(mem_map), mem_map.Begin(), mem_map.End());
}
-FreeListSpace::FreeListSpace(const std::string& name, MemMap* mem_map, uint8_t* begin, uint8_t* end)
+FreeListSpace::FreeListSpace(const std::string& name,
+ MemMap&& mem_map,
+ uint8_t* begin,
+ uint8_t* end)
: LargeObjectSpace(name, begin, end),
- mem_map_(mem_map),
+ mem_map_(std::move(mem_map)),
lock_("free list space lock", kAllocSpaceLock) {
const size_t space_capacity = end - begin;
free_end_ = space_capacity;
CHECK_ALIGNED(space_capacity, kAlignment);
const size_t alloc_info_size = sizeof(AllocationInfo) * (space_capacity / kAlignment);
std::string error_msg;
- allocation_info_map_.reset(
+ allocation_info_map_ =
MemMap::MapAnonymous("large object free list space allocation info map",
- nullptr, alloc_info_size, PROT_READ | PROT_WRITE,
- false, false, &error_msg));
- CHECK(allocation_info_map_.get() != nullptr) << "Failed to allocate allocation info map"
- << error_msg;
- allocation_info_ = reinterpret_cast<AllocationInfo*>(allocation_info_map_->Begin());
+ /* addr */ nullptr,
+ alloc_info_size,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ CHECK(allocation_info_map_.IsValid()) << "Failed to allocate allocation info map" << error_msg;
+ allocation_info_ = reinterpret_cast<AllocationInfo*>(allocation_info_map_.Begin());
}
FreeListSpace::~FreeListSpace() {}
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index f37d814..b69bd91 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -148,7 +148,7 @@
protected:
struct LargeObject {
- MemMap* mem_map;
+ MemMap mem_map;
bool is_zygote;
};
explicit LargeObjectMapSpace(const std::string& name);
@@ -182,7 +182,7 @@
std::pair<uint8_t*, uint8_t*> GetBeginEndAtomic() const OVERRIDE REQUIRES(!lock_);
protected:
- FreeListSpace(const std::string& name, MemMap* mem_map, uint8_t* begin, uint8_t* end);
+ FreeListSpace(const std::string& name, MemMap&& mem_map, uint8_t* begin, uint8_t* end);
size_t GetSlotIndexForAddress(uintptr_t address) const {
DCHECK(Contains(reinterpret_cast<mirror::Object*>(address)));
return (address - reinterpret_cast<uintptr_t>(Begin())) / kAlignment;
@@ -210,9 +210,9 @@
// There is not footer for any allocations at the end of the space, so we keep track of how much
// free space there is at the end manually.
- std::unique_ptr<MemMap> mem_map_;
+ MemMap mem_map_;
// Side table for allocation info, one per page.
- std::unique_ptr<MemMap> allocation_info_map_;
+ MemMap allocation_info_map_;
AllocationInfo* allocation_info_;
mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index 0965560..445560a 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -40,19 +40,26 @@
size_t MallocSpace::bitmap_index_ = 0;
-MallocSpace::MallocSpace(const std::string& name, MemMap* mem_map,
- uint8_t* begin, uint8_t* end, uint8_t* limit, size_t growth_limit,
- bool create_bitmaps, bool can_move_objects, size_t starting_size,
+MallocSpace::MallocSpace(const std::string& name,
+ MemMap&& mem_map,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
+ size_t growth_limit,
+ bool create_bitmaps,
+ bool can_move_objects,
+ size_t starting_size,
size_t initial_size)
- : ContinuousMemMapAllocSpace(name, mem_map, begin, end, limit, kGcRetentionPolicyAlwaysCollect),
+ : ContinuousMemMapAllocSpace(
+ name, std::move(mem_map), begin, end, limit, kGcRetentionPolicyAlwaysCollect),
recent_free_pos_(0), lock_("allocation space lock", kAllocSpaceLock),
growth_limit_(growth_limit), can_move_objects_(can_move_objects),
starting_size_(starting_size), initial_size_(initial_size) {
if (create_bitmaps) {
size_t bitmap_index = bitmap_index_++;
static const uintptr_t kGcCardSize = static_cast<uintptr_t>(accounting::CardTable::kCardSize);
- CHECK_ALIGNED(reinterpret_cast<uintptr_t>(mem_map->Begin()), kGcCardSize);
- CHECK_ALIGNED(reinterpret_cast<uintptr_t>(mem_map->End()), kGcCardSize);
+ CHECK_ALIGNED(reinterpret_cast<uintptr_t>(mem_map_.Begin()), kGcCardSize);
+ CHECK_ALIGNED(reinterpret_cast<uintptr_t>(mem_map_.End()), kGcCardSize);
live_bitmap_.reset(accounting::ContinuousSpaceBitmap::Create(
StringPrintf("allocspace %s live-bitmap %d", name.c_str(), static_cast<int>(bitmap_index)),
Begin(), NonGrowthLimitCapacity()));
@@ -70,8 +77,12 @@
}
}
-MemMap* MallocSpace::CreateMemMap(const std::string& name, size_t starting_size, size_t* initial_size,
- size_t* growth_limit, size_t* capacity, uint8_t* requested_begin) {
+MemMap MallocSpace::CreateMemMap(const std::string& name,
+ size_t starting_size,
+ size_t* initial_size,
+ size_t* growth_limit,
+ size_t* capacity,
+ uint8_t* requested_begin) {
// Sanity check arguments
if (starting_size > *initial_size) {
*initial_size = starting_size;
@@ -80,13 +91,13 @@
LOG(ERROR) << "Failed to create alloc space (" << name << ") where the initial size ("
<< PrettySize(*initial_size) << ") is larger than its capacity ("
<< PrettySize(*growth_limit) << ")";
- return nullptr;
+ return MemMap::Invalid();
}
if (*growth_limit > *capacity) {
LOG(ERROR) << "Failed to create alloc space (" << name << ") where the growth limit capacity ("
<< PrettySize(*growth_limit) << ") is larger than the capacity ("
<< PrettySize(*capacity) << ")";
- return nullptr;
+ return MemMap::Invalid();
}
// Page align growth limit and capacity which will be used to manage mmapped storage
@@ -94,9 +105,13 @@
*capacity = RoundUp(*capacity, kPageSize);
std::string error_msg;
- MemMap* mem_map = MemMap::MapAnonymous(name.c_str(), requested_begin, *capacity,
- PROT_READ | PROT_WRITE, true, false, &error_msg);
- if (mem_map == nullptr) {
+ MemMap mem_map = MemMap::MapAnonymous(name.c_str(),
+ requested_begin,
+ *capacity,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ true,
+ &error_msg);
+ if (!mem_map.IsValid()) {
LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size "
<< PrettySize(*capacity) << ": " << error_msg;
}
@@ -194,18 +209,24 @@
VLOG(heap) << "Capacity " << PrettySize(capacity);
// Remap the tail.
std::string error_msg;
- std::unique_ptr<MemMap> mem_map(GetMemMap()->RemapAtEnd(End(), alloc_space_name,
- PROT_READ | PROT_WRITE, &error_msg));
- CHECK(mem_map.get() != nullptr) << error_msg;
- void* allocator = CreateAllocator(End(), starting_size_, initial_size_, capacity,
- low_memory_mode);
+ MemMap mem_map = GetMemMap()->RemapAtEnd(
+ End(), alloc_space_name, PROT_READ | PROT_WRITE, &error_msg);
+ CHECK(mem_map.IsValid()) << error_msg;
+ void* allocator =
+ CreateAllocator(End(), starting_size_, initial_size_, capacity, low_memory_mode);
// Protect memory beyond the initial size.
- uint8_t* end = mem_map->Begin() + starting_size_;
+ uint8_t* end = mem_map.Begin() + starting_size_;
if (capacity > initial_size_) {
CheckedCall(mprotect, alloc_space_name, end, capacity - initial_size_, PROT_NONE);
}
- *out_malloc_space = CreateInstance(mem_map.release(), alloc_space_name, allocator, End(), end,
- limit_, growth_limit, CanMoveObjects());
+ *out_malloc_space = CreateInstance(std::move(mem_map),
+ alloc_space_name,
+ allocator,
+ End(),
+ end,
+ limit_,
+ growth_limit,
+ CanMoveObjects());
SetLimit(End());
live_bitmap_->SetHeapLimit(reinterpret_cast<uintptr_t>(End()));
CHECK_EQ(live_bitmap_->HeapLimit(), reinterpret_cast<uintptr_t>(End()));
@@ -247,7 +268,7 @@
}
}
// Use a bulk free, that merges consecutive objects before freeing or free per object?
- // Documentation suggests better free performance with merging, but this may be at the expensive
+ // Documentation suggests better free performance with merging, but this may be at the expense
// of allocation.
context->freed.objects += num_ptrs;
context->freed.bytes += space->FreeList(self, num_ptrs, ptrs);
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index c1f4841..e4a6f15 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -113,9 +113,14 @@
void SetGrowthLimit(size_t growth_limit);
- virtual MallocSpace* CreateInstance(MemMap* mem_map, const std::string& name, void* allocator,
- uint8_t* begin, uint8_t* end, uint8_t* limit,
- size_t growth_limit, bool can_move_objects) = 0;
+ virtual MallocSpace* CreateInstance(MemMap&& mem_map,
+ const std::string& name,
+ void* allocator,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
+ size_t growth_limit,
+ bool can_move_objects) = 0;
// Splits ourself into a zygote space and new malloc space which has our unused memory. When true,
// the low memory mode argument specifies that the heap wishes the created space to be more
@@ -137,12 +142,23 @@
}
protected:
- MallocSpace(const std::string& name, MemMap* mem_map, uint8_t* begin, uint8_t* end,
- uint8_t* limit, size_t growth_limit, bool create_bitmaps, bool can_move_objects,
- size_t starting_size, size_t initial_size);
+ MallocSpace(const std::string& name,
+ MemMap&& mem_map,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
+ size_t growth_limit,
+ bool create_bitmaps,
+ bool can_move_objects,
+ size_t starting_size,
+ size_t initial_size);
- static MemMap* CreateMemMap(const std::string& name, size_t starting_size, size_t* initial_size,
- size_t* growth_limit, size_t* capacity, uint8_t* requested_begin);
+ static MemMap CreateMemMap(const std::string& name,
+ size_t starting_size,
+ size_t* initial_size,
+ size_t* growth_limit,
+ size_t* capacity,
+ uint8_t* requested_begin);
// When true the low memory mode argument specifies that the heap wishes the created allocator to
// be more aggressive in releasing unused pages.
diff --git a/runtime/gc/space/memory_tool_malloc_space-inl.h b/runtime/gc/space/memory_tool_malloc_space-inl.h
index c022171..f1c1cb8 100644
--- a/runtime/gc/space/memory_tool_malloc_space-inl.h
+++ b/runtime/gc/space/memory_tool_malloc_space-inl.h
@@ -267,8 +267,8 @@
kMemoryToolRedZoneBytes,
kAdjustForRedzoneInAllocSize,
kUseObjSizeForUsable>::MemoryToolMallocSpace(
- MemMap* mem_map, size_t initial_size, Params... params)
- : S(mem_map, initial_size, params...) {
+ MemMap&& mem_map, size_t initial_size, Params... params)
+ : S(std::move(mem_map), initial_size, params...) {
// Don't want to change the memory tool states of the mem map here as the allocator is already
// initialized at this point and that may interfere with what the allocator does internally. Note
// that the tail beyond the initial size is mprotected.
diff --git a/runtime/gc/space/memory_tool_malloc_space.h b/runtime/gc/space/memory_tool_malloc_space.h
index e53f009..32bd204 100644
--- a/runtime/gc/space/memory_tool_malloc_space.h
+++ b/runtime/gc/space/memory_tool_malloc_space.h
@@ -53,7 +53,7 @@
size_t MaxBytesBulkAllocatedFor(size_t num_bytes) OVERRIDE;
template <typename... Params>
- MemoryToolMallocSpace(MemMap* mem_map, size_t initial_size, Params... params);
+ MemoryToolMallocSpace(MemMap&& mem_map, size_t initial_size, Params... params);
virtual ~MemoryToolMallocSpace() {}
private:
diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h
index c6ec174..e048515 100644
--- a/runtime/gc/space/region_space-inl.h
+++ b/runtime/gc/space/region_space-inl.h
@@ -247,14 +247,14 @@
/* out */ size_t* bytes_tl_bulk_allocated) {
DCHECK_ALIGNED(num_bytes, kAlignment);
DCHECK_GT(num_bytes, kRegionSize);
- size_t num_regs = RoundUp(num_bytes, kRegionSize) / kRegionSize;
- DCHECK_GT(num_regs, 0U);
- DCHECK_LT((num_regs - 1) * kRegionSize, num_bytes);
- DCHECK_LE(num_bytes, num_regs * kRegionSize);
+ size_t num_regs_in_large_region = RoundUp(num_bytes, kRegionSize) / kRegionSize;
+ DCHECK_GT(num_regs_in_large_region, 0U);
+ DCHECK_LT((num_regs_in_large_region - 1) * kRegionSize, num_bytes);
+ DCHECK_LE(num_bytes, num_regs_in_large_region * kRegionSize);
MutexLock mu(Thread::Current(), region_lock_);
if (!kForEvac) {
// Retain sufficient free regions for full evacuation.
- if ((num_non_free_regions_ + num_regs) * 2 > num_regions_) {
+ if ((num_non_free_regions_ + num_regs_in_large_region) * 2 > num_regions_) {
return nullptr;
}
}
@@ -265,7 +265,7 @@
size_t next_region1 = -1;
mirror::Object* region1 = AllocLargeInRange<kForEvac>(cyclic_alloc_region_index_,
num_regions_,
- num_regs,
+ num_regs_in_large_region,
bytes_allocated,
usable_size,
bytes_tl_bulk_allocated,
@@ -280,16 +280,16 @@
}
// If the previous attempt failed, try to find a range of free regions within
- // [0, cyclic_alloc_region_index_ + num_regions_ - 1).
+ // [0, min(cyclic_alloc_region_index_ + num_regs_in_large_region - 1, num_regions_)).
size_t next_region2 = -1;
- mirror::Object* region2 =
- AllocLargeInRange<kForEvac>(0,
- cyclic_alloc_region_index_ + num_regions_ - 1,
- num_regs,
- bytes_allocated,
- usable_size,
- bytes_tl_bulk_allocated,
- &next_region2);
+ mirror::Object* region2 = AllocLargeInRange<kForEvac>(
+ 0,
+ std::min(cyclic_alloc_region_index_ + num_regs_in_large_region - 1, num_regions_),
+ num_regs_in_large_region,
+ bytes_allocated,
+ usable_size,
+ bytes_tl_bulk_allocated,
+ &next_region2);
if (region2 != nullptr) {
DCHECK_LT(0u, next_region2);
DCHECK_LE(next_region2, num_regions_);
@@ -302,7 +302,7 @@
// Try to find a range of free regions within [0, num_regions_).
mirror::Object* region = AllocLargeInRange<kForEvac>(0,
num_regions_,
- num_regs,
+ num_regs_in_large_region,
bytes_allocated,
usable_size,
bytes_tl_bulk_allocated);
@@ -316,17 +316,21 @@
template<bool kForEvac>
inline mirror::Object* RegionSpace::AllocLargeInRange(size_t begin,
size_t end,
- size_t num_regs,
+ size_t num_regs_in_large_region,
/* out */ size_t* bytes_allocated,
/* out */ size_t* usable_size,
/* out */ size_t* bytes_tl_bulk_allocated,
/* out */ size_t* next_region) {
+ DCHECK_LE(0u, begin);
+ DCHECK_LT(begin, end);
+ DCHECK_LE(end, num_regions_);
size_t left = begin;
- while (left + num_regs - 1 < end) {
+ while (left + num_regs_in_large_region - 1 < end) {
bool found = true;
size_t right = left;
- DCHECK_LT(right, left + num_regs) << "The inner loop should iterate at least once";
- while (right < left + num_regs) {
+ DCHECK_LT(right, left + num_regs_in_large_region)
+ << "The inner loop should iterate at least once";
+ while (right < left + num_regs_in_large_region) {
if (regions_[right].IsFree()) {
++right;
// Ensure `right` is not going beyond the past-the-end index of the region space.
@@ -338,7 +342,7 @@
}
if (found) {
// `right` points to the one region past the last free region.
- DCHECK_EQ(left + num_regs, right);
+ DCHECK_EQ(left + num_regs_in_large_region, right);
Region* first_reg = ®ions_[left];
DCHECK(first_reg->IsFree());
first_reg->UnfreeLarge(this, time_);
@@ -347,10 +351,14 @@
} else {
++num_non_free_regions_;
}
- size_t allocated = num_regs * kRegionSize;
+ size_t allocated = num_regs_in_large_region * kRegionSize;
// We make 'top' all usable bytes, as the caller of this
// allocation may use all of 'usable_size' (see mirror::Array::Alloc).
first_reg->SetTop(first_reg->Begin() + allocated);
+ if (!kForEvac) {
+ // Evac doesn't count as newly allocated.
+ first_reg->SetNewlyAllocated();
+ }
for (size_t p = left + 1; p < right; ++p) {
DCHECK_LT(p, num_regions_);
DCHECK(regions_[p].IsFree());
@@ -360,6 +368,10 @@
} else {
++num_non_free_regions_;
}
+ if (!kForEvac) {
+ // Evac doesn't count as newly allocated.
+ regions_[p].SetNewlyAllocated();
+ }
}
*bytes_allocated = allocated;
if (usable_size != nullptr) {
@@ -403,7 +415,7 @@
--num_non_free_regions_;
}
}
- if (end_addr < Limit()) {
+ if (kIsDebugBuild && end_addr < Limit()) {
// If we aren't at the end of the space, check that the next region is not a large tail.
Region* following_reg = RefToRegionLocked(reinterpret_cast<mirror::Object*>(end_addr));
DCHECK(!following_reg->IsLargeTail());
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index 0701330..f74fa86 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -16,6 +16,7 @@
#include "bump_pointer_space-inl.h"
#include "bump_pointer_space.h"
+#include "base/dumpable.h"
#include "gc/accounting/read_barrier_table.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
@@ -29,9 +30,8 @@
// value of the region size, evaculate the region.
static constexpr uint kEvacuateLivePercentThreshold = 75U;
-// Whether we protect the cleared regions.
-// Only protect for target builds to prevent flaky test failures (b/63131961).
-static constexpr bool kProtectClearedRegions = kIsTargetBuild;
+// Whether we protect the unused and cleared regions.
+static constexpr bool kProtectClearedRegions = true;
// Wether we poison memory areas occupied by dead objects in unevacuated regions.
static constexpr bool kPoisonDeadObjectsInUnevacuatedRegions = true;
@@ -42,60 +42,67 @@
// points to a valid, non-protected memory area.
static constexpr uint32_t kPoisonDeadObject = 0xBADDB01D; // "BADDROID"
-MemMap* RegionSpace::CreateMemMap(const std::string& name, size_t capacity,
- uint8_t* requested_begin) {
+// Whether we check a region's live bytes count against the region bitmap.
+static constexpr bool kCheckLiveBytesAgainstRegionBitmap = kIsDebugBuild;
+
+MemMap RegionSpace::CreateMemMap(const std::string& name,
+ size_t capacity,
+ uint8_t* requested_begin) {
CHECK_ALIGNED(capacity, kRegionSize);
std::string error_msg;
// Ask for the capacity of an additional kRegionSize so that we can align the map by kRegionSize
// even if we get unaligned base address. This is necessary for the ReadBarrierTable to work.
- std::unique_ptr<MemMap> mem_map;
+ MemMap mem_map;
while (true) {
- mem_map.reset(MemMap::MapAnonymous(name.c_str(),
- requested_begin,
- capacity + kRegionSize,
- PROT_READ | PROT_WRITE,
- true,
- false,
- &error_msg));
- if (mem_map.get() != nullptr || requested_begin == nullptr) {
+ mem_map = MemMap::MapAnonymous(name.c_str(),
+ requested_begin,
+ capacity + kRegionSize,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ true,
+ &error_msg);
+ if (mem_map.IsValid() || requested_begin == nullptr) {
break;
}
// Retry with no specified request begin.
requested_begin = nullptr;
}
- if (mem_map.get() == nullptr) {
+ if (!mem_map.IsValid()) {
LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size "
<< PrettySize(capacity) << " with message " << error_msg;
MemMap::DumpMaps(LOG_STREAM(ERROR));
- return nullptr;
+ return MemMap::Invalid();
}
- CHECK_EQ(mem_map->Size(), capacity + kRegionSize);
- CHECK_EQ(mem_map->Begin(), mem_map->BaseBegin());
- CHECK_EQ(mem_map->Size(), mem_map->BaseSize());
- if (IsAlignedParam(mem_map->Begin(), kRegionSize)) {
+ CHECK_EQ(mem_map.Size(), capacity + kRegionSize);
+ CHECK_EQ(mem_map.Begin(), mem_map.BaseBegin());
+ CHECK_EQ(mem_map.Size(), mem_map.BaseSize());
+ if (IsAlignedParam(mem_map.Begin(), kRegionSize)) {
// Got an aligned map. Since we requested a map that's kRegionSize larger. Shrink by
// kRegionSize at the end.
- mem_map->SetSize(capacity);
+ mem_map.SetSize(capacity);
} else {
// Got an unaligned map. Align the both ends.
- mem_map->AlignBy(kRegionSize);
+ mem_map.AlignBy(kRegionSize);
}
- CHECK_ALIGNED(mem_map->Begin(), kRegionSize);
- CHECK_ALIGNED(mem_map->End(), kRegionSize);
- CHECK_EQ(mem_map->Size(), capacity);
- return mem_map.release();
+ CHECK_ALIGNED(mem_map.Begin(), kRegionSize);
+ CHECK_ALIGNED(mem_map.End(), kRegionSize);
+ CHECK_EQ(mem_map.Size(), capacity);
+ return mem_map;
}
-RegionSpace* RegionSpace::Create(const std::string& name, MemMap* mem_map) {
- return new RegionSpace(name, mem_map);
+RegionSpace* RegionSpace::Create(const std::string& name, MemMap&& mem_map) {
+ return new RegionSpace(name, std::move(mem_map));
}
-RegionSpace::RegionSpace(const std::string& name, MemMap* mem_map)
- : ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->End(), mem_map->End(),
+RegionSpace::RegionSpace(const std::string& name, MemMap&& mem_map)
+ : ContinuousMemMapAllocSpace(name,
+ std::move(mem_map),
+ mem_map.Begin(),
+ mem_map.End(),
+ mem_map.End(),
kGcRetentionPolicyAlwaysCollect),
region_lock_("Region lock", kRegionSpaceRegionLock),
time_(1U),
- num_regions_(mem_map->Size() / kRegionSize),
+ num_regions_(mem_map_.Size() / kRegionSize),
num_non_free_regions_(0U),
num_evac_regions_(0U),
max_peak_num_non_free_regions_(0U),
@@ -103,11 +110,11 @@
current_region_(&full_region_),
evac_region_(nullptr),
cyclic_alloc_region_index_(0U) {
- CHECK_ALIGNED(mem_map->Size(), kRegionSize);
- CHECK_ALIGNED(mem_map->Begin(), kRegionSize);
+ CHECK_ALIGNED(mem_map_.Size(), kRegionSize);
+ CHECK_ALIGNED(mem_map_.Begin(), kRegionSize);
DCHECK_GT(num_regions_, 0U);
regions_.reset(new Region[num_regions_]);
- uint8_t* region_addr = mem_map->Begin();
+ uint8_t* region_addr = mem_map_.Begin();
for (size_t i = 0; i < num_regions_; ++i, region_addr += kRegionSize) {
regions_[i].Init(i, region_addr, region_addr + kRegionSize);
}
@@ -128,6 +135,8 @@
DCHECK(full_region_.IsAllocated());
size_t ignored;
DCHECK(full_region_.Alloc(kAlignment, &ignored, nullptr, &ignored) == nullptr);
+ // Protect the whole region space from the start.
+ Protect();
}
size_t RegionSpace::FromSpaceSize() {
@@ -166,15 +175,54 @@
return num_regions * kRegionSize;
}
-inline bool RegionSpace::Region::ShouldBeEvacuated() {
+inline bool RegionSpace::Region::ShouldBeEvacuated(EvacMode evac_mode) {
+ // Evacuation mode `kEvacModeNewlyAllocated` is only used during sticky-bit CC collections.
+ DCHECK(kEnableGenerationalConcurrentCopyingCollection || (evac_mode != kEvacModeNewlyAllocated));
DCHECK((IsAllocated() || IsLarge()) && IsInToSpace());
// The region should be evacuated if:
+ // - the evacuation is forced (`evac_mode == kEvacModeForceAll`); or
// - the region was allocated after the start of the previous GC (newly allocated region); or
// - the live ratio is below threshold (`kEvacuateLivePercentThreshold`).
- bool result;
+ if (UNLIKELY(evac_mode == kEvacModeForceAll)) {
+ return true;
+ }
+ bool result = false;
if (is_newly_allocated_) {
- result = true;
- } else {
+ // Invariant: newly allocated regions have an undefined live bytes count.
+ DCHECK_EQ(live_bytes_, static_cast<size_t>(-1));
+ if (IsAllocated()) {
+ // We always evacuate newly-allocated non-large regions as we
+ // believe they contain many dead objects (a very simple form of
+ // the generational hypothesis, even before the Sticky-Bit CC
+ // approach).
+ //
+ // TODO: Verify that assertion by collecting statistics on the
+ // number/proportion of live objects in newly allocated regions
+ // in RegionSpace::ClearFromSpace.
+ //
+ // Note that a side effect of evacuating a newly-allocated
+ // non-large region is that the "newly allocated" status will
+ // later be removed, as its live objects will be copied to an
+ // evacuation region, which won't be marked as "newly
+ // allocated" (see RegionSpace::AllocateRegion).
+ result = true;
+ } else {
+ DCHECK(IsLarge());
+ // We never want to evacuate a large region (and the associated
+ // tail regions), except if:
+ // - we are forced to do so (see the `kEvacModeForceAll` case
+ // above); or
+ // - we know that the (sole) object contained in this region is
+ // dead (see the corresponding logic below, in the
+ // `kEvacModeLivePercentNewlyAllocated` case).
+ // For a newly allocated region (i.e. allocated since the
+ // previous GC started), we don't have any liveness information
+ // (the live bytes count is -1 -- also note this region has been
+ // a to-space one between the time of its allocation and now),
+ // so we prefer not to evacuate it.
+ result = false;
+ }
+ } else if (evac_mode == kEvacModeLivePercentNewlyAllocated) {
bool is_live_percent_valid = (live_bytes_ != static_cast<size_t>(-1));
if (is_live_percent_valid) {
DCHECK(IsInToSpace());
@@ -199,9 +247,48 @@
return result;
}
+void RegionSpace::ZeroLiveBytesForLargeObject(mirror::Object* obj) {
+ // This method is only used when Generational CC collection is enabled.
+ DCHECK(kEnableGenerationalConcurrentCopyingCollection);
+
+ // This code uses a logic similar to the one used in RegionSpace::FreeLarge
+ // to traverse the regions supporting `obj`.
+ // TODO: Refactor.
+ DCHECK(IsLargeObject(obj));
+ DCHECK_ALIGNED(obj, kRegionSize);
+ size_t obj_size = obj->SizeOf<kDefaultVerifyFlags>();
+ DCHECK_GT(obj_size, space::RegionSpace::kRegionSize);
+ // Size of the memory area allocated for `obj`.
+ size_t obj_alloc_size = RoundUp(obj_size, space::RegionSpace::kRegionSize);
+ uint8_t* begin_addr = reinterpret_cast<uint8_t*>(obj);
+ uint8_t* end_addr = begin_addr + obj_alloc_size;
+ DCHECK_ALIGNED(end_addr, kRegionSize);
+
+ // Zero the live bytes of the large region and large tail regions containing the object.
+ MutexLock mu(Thread::Current(), region_lock_);
+ for (uint8_t* addr = begin_addr; addr < end_addr; addr += kRegionSize) {
+ Region* region = RefToRegionLocked(reinterpret_cast<mirror::Object*>(addr));
+ if (addr == begin_addr) {
+ DCHECK(region->IsLarge());
+ } else {
+ DCHECK(region->IsLargeTail());
+ }
+ region->ZeroLiveBytes();
+ }
+ if (kIsDebugBuild && end_addr < Limit()) {
+ // If we aren't at the end of the space, check that the next region is not a large tail.
+ Region* following_region = RefToRegionLocked(reinterpret_cast<mirror::Object*>(end_addr));
+ DCHECK(!following_region->IsLargeTail());
+ }
+}
+
// Determine which regions to evacuate and mark them as
// from-space. Mark the rest as unevacuated from-space.
-void RegionSpace::SetFromSpace(accounting::ReadBarrierTable* rb_table, bool force_evacuate_all) {
+void RegionSpace::SetFromSpace(accounting::ReadBarrierTable* rb_table,
+ EvacMode evac_mode,
+ bool clear_live_bytes) {
+ // Live bytes are only preserved (i.e. not cleared) during sticky-bit CC collections.
+ DCHECK(kEnableGenerationalConcurrentCopyingCollection || clear_live_bytes);
++time_;
if (kUseTableLookupReadBarrier) {
DCHECK(rb_table->IsAllCleared());
@@ -227,12 +314,12 @@
DCHECK((state == RegionState::kRegionStateAllocated ||
state == RegionState::kRegionStateLarge) &&
type == RegionType::kRegionTypeToSpace);
- bool should_evacuate = force_evacuate_all || r->ShouldBeEvacuated();
+ bool should_evacuate = r->ShouldBeEvacuated(evac_mode);
if (should_evacuate) {
r->SetAsFromSpace();
DCHECK(r->IsInFromSpace());
} else {
- r->SetAsUnevacFromSpace();
+ r->SetAsUnevacFromSpace(clear_live_bytes);
DCHECK(r->IsInUnevacFromSpace());
}
if (UNLIKELY(state == RegionState::kRegionStateLarge &&
@@ -248,7 +335,7 @@
r->SetAsFromSpace();
DCHECK(r->IsInFromSpace());
} else {
- r->SetAsUnevacFromSpace();
+ r->SetAsUnevacFromSpace(clear_live_bytes);
DCHECK(r->IsInUnevacFromSpace());
}
--num_expected_large_tails;
@@ -260,6 +347,8 @@
rb_table->Clear(r->Begin(), r->End());
}
}
+ // Invariant: There should be no newly-allocated region in the from-space.
+ DCHECK(!r->is_newly_allocated_);
}
DCHECK_EQ(num_expected_large_tails, 0U);
current_region_ = &full_region_;
@@ -316,6 +405,9 @@
};
for (size_t i = 0; i < std::min(num_regions_, non_free_region_index_limit_); ++i) {
Region* r = ®ions_[i];
+ if (kCheckLiveBytesAgainstRegionBitmap) {
+ CheckLiveBytesAgainstRegionBitmap(r);
+ }
if (r->IsInFromSpace()) {
*cleared_bytes += r->BytesAllocated();
*cleared_objects += r->ObjectsAllocated();
@@ -362,8 +454,9 @@
++regions_to_clear_bitmap;
}
- // Optimization: If the live bytes are *all* live in a region
- // then the live-bit information for these objects is superfluous:
+ // Optimization (for full CC only): If the live bytes are *all* live
+ // in a region then the live-bit information for these objects is
+ // superfluous:
// - We can determine that these objects are all live by using
// Region::AllAllocatedBytesAreLive (which just checks whether
// `LiveBytes() == static_cast<size_t>(Top() - Begin())`.
@@ -372,19 +465,44 @@
// live bits (see RegionSpace::WalkInternal).
// Therefore, we can clear the bits for these objects in the
// (live) region space bitmap (and release the corresponding pages).
- GetLiveBitmap()->ClearRange(
- reinterpret_cast<mirror::Object*>(r->Begin()),
- reinterpret_cast<mirror::Object*>(r->Begin() + regions_to_clear_bitmap * kRegionSize));
+ //
+ // This optimization is incompatible with Generational CC, because:
+ // - minor (young-generation) collections need to know which objects
+ // where marked during the previous GC cycle, meaning all mark bitmaps
+ // (this includes the region space bitmap) need to be preserved
+ // between a (minor or major) collection N and a following minor
+ // collection N+1;
+ // - at this stage (in the current GC cycle), we cannot determine
+ // whether the next collection will be a minor or a major one;
+ // This means that we need to be conservative and always preserve the
+ // region space bitmap when using Generational CC.
+ // Note that major collections do not require the previous mark bitmaps
+ // to be preserved, and as matter of fact they do clear the region space
+ // bitmap. But they cannot do so before we know the next GC cycle will
+ // be a major one, so this operation happens at the beginning of such a
+ // major collection, before marking starts.
+ if (!kEnableGenerationalConcurrentCopyingCollection) {
+ GetLiveBitmap()->ClearRange(
+ reinterpret_cast<mirror::Object*>(r->Begin()),
+ reinterpret_cast<mirror::Object*>(r->Begin() + regions_to_clear_bitmap * kRegionSize));
+ }
// Skip over extra regions for which we cleared the bitmaps: we shall not clear them,
// as they are unevac regions that are live.
// Subtract one for the for-loop.
i += regions_to_clear_bitmap - 1;
} else {
- // Only some allocated bytes are live in this unevac region.
- // This should only happen for an allocated non-large region.
- DCHECK(r->IsAllocated()) << r->State();
- if (kPoisonDeadObjectsInUnevacuatedRegions) {
- PoisonDeadObjectsInUnevacuatedRegion(r);
+ // TODO: Explain why we do not poison dead objects in region
+ // `r` when it has an undefined live bytes count (i.e. when
+ // `r->LiveBytes() == static_cast<size_t>(-1)`) with
+ // Generational CC.
+ if (!kEnableGenerationalConcurrentCopyingCollection ||
+ (r->LiveBytes() != static_cast<size_t>(-1))) {
+ // Only some allocated bytes are live in this unevac region.
+ // This should only happen for an allocated non-large region.
+ DCHECK(r->IsAllocated()) << r->State();
+ if (kPoisonDeadObjectsInUnevacuatedRegions) {
+ PoisonDeadObjectsInUnevacuatedRegion(r);
+ }
}
}
}
@@ -404,6 +522,42 @@
num_evac_regions_ = 0;
}
+void RegionSpace::CheckLiveBytesAgainstRegionBitmap(Region* r) {
+ if (r->LiveBytes() == static_cast<size_t>(-1)) {
+ // Live bytes count is undefined for `r`; nothing to check here.
+ return;
+ }
+
+ // Functor walking the region space bitmap for the range corresponding
+ // to region `r` and calculating the sum of live bytes.
+ size_t live_bytes_recount = 0u;
+ auto recount_live_bytes =
+ [&r, &live_bytes_recount](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK_ALIGNED(obj, kAlignment);
+ if (r->IsLarge()) {
+ // If `r` is a large region, then it contains at most one
+ // object, which must start at the beginning of the
+ // region. The live byte count in that case is equal to the
+ // allocated regions (large region + large tails regions).
+ DCHECK_EQ(reinterpret_cast<uint8_t*>(obj), r->Begin());
+ DCHECK_EQ(live_bytes_recount, 0u);
+ live_bytes_recount = r->Top() - r->Begin();
+ } else {
+ DCHECK(r->IsAllocated())
+ << "r->State()=" << r->State() << " r->LiveBytes()=" << r->LiveBytes();
+ size_t obj_size = obj->SizeOf<kDefaultVerifyFlags>();
+ size_t alloc_size = RoundUp(obj_size, space::RegionSpace::kAlignment);
+ live_bytes_recount += alloc_size;
+ }
+ };
+ // Visit live objects in `r` and recount the live bytes.
+ GetLiveBitmap()->VisitMarkedRange(reinterpret_cast<uintptr_t>(r->Begin()),
+ reinterpret_cast<uintptr_t>(r->Top()),
+ recount_live_bytes);
+ // Check that this recount matches the region's current live bytes count.
+ DCHECK_EQ(live_bytes_recount, r->LiveBytes());
+}
+
// Poison the memory area in range [`begin`, `end`) with value `kPoisonDeadObject`.
static void PoisonUnevacuatedRange(uint8_t* begin, uint8_t* end) {
static constexpr size_t kPoisonDeadObjectSize = sizeof(kPoisonDeadObject);
@@ -423,7 +577,8 @@
// The live byte count of `r` should be different from -1, as this
// region should neither be a newly allocated region nor an
// evacuated region.
- DCHECK_NE(r->LiveBytes(), static_cast<size_t>(-1));
+ DCHECK_NE(r->LiveBytes(), static_cast<size_t>(-1))
+ << "Unexpected live bytes count of -1 in " << Dumpable<Region>(*r);
// Past-the-end address of the previously visited (live) object (or
// the beginning of the region, if `maybe_poison` has not run yet).
@@ -508,6 +663,18 @@
evac_region_ = &full_region_;
}
+void RegionSpace::Protect() {
+ if (kProtectClearedRegions) {
+ CheckedCall(mprotect, __FUNCTION__, Begin(), Size(), PROT_NONE);
+ }
+}
+
+void RegionSpace::Unprotect() {
+ if (kProtectClearedRegions) {
+ CheckedCall(mprotect, __FUNCTION__, Begin(), Size(), PROT_READ | PROT_WRITE);
+ }
+}
+
void RegionSpace::ClampGrowthLimit(size_t new_capacity) {
MutexLock mu(Thread::Current(), region_lock_);
CHECK_LE(new_capacity, NonGrowthLimitCapacity());
@@ -688,6 +855,10 @@
Region* r = ®ions_[region_index];
if (r->IsFree()) {
r->Unfree(this, time_);
+ if (kEnableGenerationalConcurrentCopyingCollection) {
+ // TODO: Add an explanation for this assertion.
+ DCHECK(!for_evac || !r->is_newly_allocated_);
+ }
if (for_evac) {
++num_evac_regions_;
// Evac doesn't count as newly allocated.
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index a129171..8ad26ba 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -43,6 +43,12 @@
public:
typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg);
+ enum EvacMode {
+ kEvacModeNewlyAllocated,
+ kEvacModeLivePercentNewlyAllocated,
+ kEvacModeForceAll,
+ };
+
SpaceType GetType() const OVERRIDE {
return kSpaceTypeRegionSpace;
}
@@ -50,8 +56,8 @@
// Create a region space mem map with the requested sizes. The requested base address is not
// guaranteed to be granted, if it is required, the caller should call Begin on the returned
// space to confirm the request was granted.
- static MemMap* CreateMemMap(const std::string& name, size_t capacity, uint8_t* requested_begin);
- static RegionSpace* Create(const std::string& name, MemMap* mem_map);
+ static MemMap CreateMemMap(const std::string& name, size_t capacity, uint8_t* requested_begin);
+ static RegionSpace* Create(const std::string& name, MemMap&& mem_map);
// Allocate `num_bytes`, returns null if the space is full.
mirror::Object* Alloc(Thread* self,
@@ -108,6 +114,17 @@
void Clear() OVERRIDE REQUIRES(!region_lock_);
+ // Remove read and write memory protection from the whole region space,
+ // i.e. make memory pages backing the region area not readable and not
+ // writable.
+ void Protect();
+
+ // Remove memory protection from the whole region space, i.e. make memory
+ // pages backing the region area readable and writable. This method is useful
+ // to avoid page protection faults when dumping information about an invalid
+ // reference.
+ void Unprotect();
+
// Change the non growth limit capacity to new capacity by shrinking or expanding the map.
// Currently, only shrinking is supported.
// Unlike implementations of this function in other spaces, we need to pass
@@ -230,6 +247,14 @@
return false;
}
+ bool IsLargeObject(mirror::Object* ref) {
+ if (HasAddress(ref)) {
+ Region* r = RefToRegionUnlocked(ref);
+ return r->IsLarge();
+ }
+ return false;
+ }
+
bool IsInToSpace(mirror::Object* ref) {
if (HasAddress(ref)) {
Region* r = RefToRegionUnlocked(ref);
@@ -255,9 +280,15 @@
return r->Type();
}
+ // Zero live bytes for a large object, used by young gen CC for marking newly allocated large
+ // objects.
+ void ZeroLiveBytesForLargeObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
+
// Determine which regions to evacuate and tag them as
// from-space. Tag the rest as unevacuated from-space.
- void SetFromSpace(accounting::ReadBarrierTable* rb_table, bool force_evacuate_all)
+ void SetFromSpace(accounting::ReadBarrierTable* rb_table,
+ EvacMode evac_mode,
+ bool clear_live_bytes)
REQUIRES(!region_lock_);
size_t FromSpaceSize() REQUIRES(!region_lock_);
@@ -290,7 +321,7 @@
}
private:
- RegionSpace(const std::string& name, MemMap* mem_map);
+ RegionSpace(const std::string& name, MemMap&& mem_map);
template<bool kToSpaceOnly, typename Visitor>
ALWAYS_INLINE void WalkInternal(Visitor&& visitor) NO_THREAD_SAFETY_ANALYSIS;
@@ -386,6 +417,10 @@
return is_large;
}
+ void ZeroLiveBytes() {
+ live_bytes_ = 0;
+ }
+
// Large-tail allocated.
bool IsLargeTail() const {
bool is_large_tail = (state_ == RegionState::kRegionStateLargeTail);
@@ -425,6 +460,18 @@
void SetAsFromSpace() {
DCHECK(!IsFree() && IsInToSpace());
type_ = RegionType::kRegionTypeFromSpace;
+ if (IsNewlyAllocated()) {
+ // Clear the "newly allocated" status here, as we do not want the
+ // GC to see it when encountering references in the from-space.
+ //
+ // Invariant: There should be no newly-allocated region in the
+ // from-space (when the from-space exists, which is between the calls
+ // to RegionSpace::SetFromSpace and RegionSpace::ClearFromSpace).
+ is_newly_allocated_ = false;
+ }
+ // Set live bytes to an invalid value, as we have made an
+ // evacuation decision (possibly based on the percentage of live
+ // bytes).
live_bytes_ = static_cast<size_t>(-1);
}
@@ -432,10 +479,32 @@
// collection, RegionSpace::ClearFromSpace will preserve the space
// used by this region, and tag it as to-space (see
// Region::SetUnevacFromSpaceAsToSpace below).
- void SetAsUnevacFromSpace() {
+ void SetAsUnevacFromSpace(bool clear_live_bytes) {
+ // Live bytes are only preserved (i.e. not cleared) during sticky-bit CC collections.
+ DCHECK(kEnableGenerationalConcurrentCopyingCollection || clear_live_bytes);
DCHECK(!IsFree() && IsInToSpace());
type_ = RegionType::kRegionTypeUnevacFromSpace;
- live_bytes_ = 0U;
+ if (IsNewlyAllocated()) {
+ // A newly allocated region set as unevac from-space must be
+ // a large or large tail region.
+ DCHECK(IsLarge() || IsLargeTail()) << static_cast<uint>(state_);
+ // Always clear the live bytes of a newly allocated (large or
+ // large tail) region.
+ clear_live_bytes = true;
+ // Clear the "newly allocated" status here, as we do not want the
+ // GC to see it when encountering (and processing) references in the
+ // from-space.
+ //
+ // Invariant: There should be no newly-allocated region in the
+ // from-space (when the from-space exists, which is between the calls
+ // to RegionSpace::SetFromSpace and RegionSpace::ClearFromSpace).
+ is_newly_allocated_ = false;
+ }
+ if (clear_live_bytes) {
+ // Reset the live bytes, as we have made a non-evacuation
+ // decision (possibly based on the percentage of live bytes).
+ live_bytes_ = 0;
+ }
}
// Set this region as to-space. Used by RegionSpace::ClearFromSpace.
@@ -446,7 +515,7 @@
}
// Return whether this region should be evacuated. Used by RegionSpace::SetFromSpace.
- ALWAYS_INLINE bool ShouldBeEvacuated();
+ ALWAYS_INLINE bool ShouldBeEvacuated(EvacMode evac_mode);
void AddLiveBytes(size_t live_bytes) {
DCHECK(IsInUnevacFromSpace());
@@ -586,22 +655,27 @@
Region* AllocateRegion(bool for_evac) REQUIRES(region_lock_);
// Scan region range [`begin`, `end`) in increasing order to try to
- // allocate a large region having a size of `num_regs` regions. If
- // there is no space in the region space to allocate this large
- // region, return null.
+ // allocate a large region having a size of `num_regs_in_large_region`
+ // regions. If there is no space in the region space to allocate this
+ // large region, return null.
//
// If argument `next_region` is not null, use `*next_region` to
// return the index to the region next to the allocated large region
// returned by this method.
template<bool kForEvac>
- mirror::Object* AllocLargeInRange(size_t num_regs,
- size_t begin,
+ mirror::Object* AllocLargeInRange(size_t begin,
size_t end,
+ size_t num_regs_in_large_region,
/* out */ size_t* bytes_allocated,
/* out */ size_t* usable_size,
/* out */ size_t* bytes_tl_bulk_allocated,
/* out */ size_t* next_region = nullptr) REQUIRES(region_lock_);
+ // Check that the value of `r->LiveBytes()` matches the number of
+ // (allocated) bytes used by live objects according to the live bits
+ // in the region space bitmap range corresponding to region `r`.
+ void CheckLiveBytesAgainstRegionBitmap(Region* r);
+
// Poison memory areas used by dead objects within unevacuated
// region `r`. This is meant to detect dangling references to dead
// objects earlier in debug mode.
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index b0402e4..10ff1c1 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -44,48 +44,88 @@
// TODO: Fix
// template class MemoryToolMallocSpace<RosAllocSpace, allocator::RosAlloc*>;
-RosAllocSpace::RosAllocSpace(MemMap* mem_map, size_t initial_size, const std::string& name,
- art::gc::allocator::RosAlloc* rosalloc, uint8_t* begin, uint8_t* end,
- uint8_t* limit, size_t growth_limit, bool can_move_objects,
- size_t starting_size, bool low_memory_mode)
- : MallocSpace(name, mem_map, begin, end, limit, growth_limit, true, can_move_objects,
+RosAllocSpace::RosAllocSpace(MemMap&& mem_map,
+ size_t initial_size,
+ const std::string& name,
+ art::gc::allocator::RosAlloc* rosalloc,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
+ size_t growth_limit,
+ bool can_move_objects,
+ size_t starting_size,
+ bool low_memory_mode)
+ : MallocSpace(name,
+ std::move(mem_map),
+ begin,
+ end,
+ limit,
+ growth_limit,
+ true,
+ can_move_objects,
starting_size, initial_size),
rosalloc_(rosalloc), low_memory_mode_(low_memory_mode) {
CHECK(rosalloc != nullptr);
}
-RosAllocSpace* RosAllocSpace::CreateFromMemMap(MemMap* mem_map, const std::string& name,
- size_t starting_size, size_t initial_size,
- size_t growth_limit, size_t capacity,
- bool low_memory_mode, bool can_move_objects) {
- DCHECK(mem_map != nullptr);
+RosAllocSpace* RosAllocSpace::CreateFromMemMap(MemMap&& mem_map,
+ const std::string& name,
+ size_t starting_size,
+ size_t initial_size,
+ size_t growth_limit,
+ size_t capacity,
+ bool low_memory_mode,
+ bool can_move_objects) {
+ DCHECK(mem_map.IsValid());
bool running_on_memory_tool = Runtime::Current()->IsRunningOnMemoryTool();
- allocator::RosAlloc* rosalloc = CreateRosAlloc(mem_map->Begin(), starting_size, initial_size,
- capacity, low_memory_mode, running_on_memory_tool);
+ allocator::RosAlloc* rosalloc = CreateRosAlloc(mem_map.Begin(),
+ starting_size,
+ initial_size,
+ capacity,
+ low_memory_mode,
+ running_on_memory_tool);
if (rosalloc == nullptr) {
LOG(ERROR) << "Failed to initialize rosalloc for alloc space (" << name << ")";
return nullptr;
}
// Protect memory beyond the starting size. MoreCore will add r/w permissions when necessory
- uint8_t* end = mem_map->Begin() + starting_size;
+ uint8_t* end = mem_map.Begin() + starting_size;
if (capacity - starting_size > 0) {
CheckedCall(mprotect, name.c_str(), end, capacity - starting_size, PROT_NONE);
}
// Everything is set so record in immutable structure and leave
- uint8_t* begin = mem_map->Begin();
+ uint8_t* begin = mem_map.Begin();
// TODO: Fix RosAllocSpace to support ASan. There is currently some issues with
// AllocationSize caused by redzones. b/12944686
if (running_on_memory_tool) {
return new MemoryToolMallocSpace<RosAllocSpace, kDefaultMemoryToolRedZoneBytes, false, true>(
- mem_map, initial_size, name, rosalloc, begin, end, begin + capacity, growth_limit,
- can_move_objects, starting_size, low_memory_mode);
+ std::move(mem_map),
+ initial_size,
+ name,
+ rosalloc,
+ begin,
+ end,
+ begin + capacity,
+ growth_limit,
+ can_move_objects,
+ starting_size,
+ low_memory_mode);
} else {
- return new RosAllocSpace(mem_map, initial_size, name, rosalloc, begin, end, begin + capacity,
- growth_limit, can_move_objects, starting_size, low_memory_mode);
+ return new RosAllocSpace(std::move(mem_map),
+ initial_size,
+ name,
+ rosalloc,
+ begin,
+ end,
+ begin + capacity,
+ growth_limit,
+ can_move_objects,
+ starting_size,
+ low_memory_mode);
}
}
@@ -111,16 +151,21 @@
// will ask for this memory from sys_alloc which will fail as the footprint (this value plus the
// size of the large allocation) will be greater than the footprint limit.
size_t starting_size = Heap::kDefaultStartingSize;
- MemMap* mem_map = CreateMemMap(name, starting_size, &initial_size, &growth_limit, &capacity,
- requested_begin);
- if (mem_map == nullptr) {
+ MemMap mem_map =
+ CreateMemMap(name, starting_size, &initial_size, &growth_limit, &capacity, requested_begin);
+ if (!mem_map.IsValid()) {
LOG(ERROR) << "Failed to create mem map for alloc space (" << name << ") of size "
<< PrettySize(capacity);
return nullptr;
}
- RosAllocSpace* space = CreateFromMemMap(mem_map, name, starting_size, initial_size,
- growth_limit, capacity, low_memory_mode,
+ RosAllocSpace* space = CreateFromMemMap(std::move(mem_map),
+ name,
+ starting_size,
+ initial_size,
+ growth_limit,
+ capacity,
+ low_memory_mode,
can_move_objects);
// We start out with only the initial size possibly containing objects.
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
@@ -175,18 +220,39 @@
return result;
}
-MallocSpace* RosAllocSpace::CreateInstance(MemMap* mem_map, const std::string& name,
- void* allocator, uint8_t* begin, uint8_t* end,
- uint8_t* limit, size_t growth_limit,
+MallocSpace* RosAllocSpace::CreateInstance(MemMap&& mem_map,
+ const std::string& name,
+ void* allocator,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
+ size_t growth_limit,
bool can_move_objects) {
if (Runtime::Current()->IsRunningOnMemoryTool()) {
return new MemoryToolMallocSpace<RosAllocSpace, kDefaultMemoryToolRedZoneBytes, false, true>(
- mem_map, initial_size_, name, reinterpret_cast<allocator::RosAlloc*>(allocator), begin, end,
- limit, growth_limit, can_move_objects, starting_size_, low_memory_mode_);
+ std::move(mem_map),
+ initial_size_,
+ name,
+ reinterpret_cast<allocator::RosAlloc*>(allocator),
+ begin,
+ end,
+ limit,
+ growth_limit,
+ can_move_objects,
+ starting_size_,
+ low_memory_mode_);
} else {
- return new RosAllocSpace(mem_map, initial_size_, name,
- reinterpret_cast<allocator::RosAlloc*>(allocator), begin, end, limit,
- growth_limit, can_move_objects, starting_size_, low_memory_mode_);
+ return new RosAllocSpace(std::move(mem_map),
+ initial_size_,
+ name,
+ reinterpret_cast<allocator::RosAlloc*>(allocator),
+ begin,
+ end,
+ limit,
+ growth_limit,
+ can_move_objects,
+ starting_size_,
+ low_memory_mode_);
}
}
@@ -364,8 +430,11 @@
mark_bitmap_->Clear();
SetEnd(begin_ + starting_size_);
delete rosalloc_;
- rosalloc_ = CreateRosAlloc(mem_map_->Begin(), starting_size_, initial_size_,
- NonGrowthLimitCapacity(), low_memory_mode_,
+ rosalloc_ = CreateRosAlloc(mem_map_.Begin(),
+ starting_size_,
+ initial_size_,
+ NonGrowthLimitCapacity(),
+ low_memory_mode_,
Runtime::Current()->IsRunningOnMemoryTool());
SetFootprintLimit(footprint_limit);
}
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index 4c17233..c630826 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -41,10 +41,14 @@
static RosAllocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit,
size_t capacity, uint8_t* requested_begin, bool low_memory_mode,
bool can_move_objects);
- static RosAllocSpace* CreateFromMemMap(MemMap* mem_map, const std::string& name,
- size_t starting_size, size_t initial_size,
- size_t growth_limit, size_t capacity,
- bool low_memory_mode, bool can_move_objects);
+ static RosAllocSpace* CreateFromMemMap(MemMap&& mem_map,
+ const std::string& name,
+ size_t starting_size,
+ size_t initial_size,
+ size_t growth_limit,
+ size_t capacity,
+ bool low_memory_mode,
+ bool can_move_objects);
mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes, size_t* bytes_allocated,
size_t* usable_size, size_t* bytes_tl_bulk_allocated)
@@ -111,8 +115,13 @@
void Clear() OVERRIDE;
- MallocSpace* CreateInstance(MemMap* mem_map, const std::string& name, void* allocator,
- uint8_t* begin, uint8_t* end, uint8_t* limit, size_t growth_limit,
+ MallocSpace* CreateInstance(MemMap&& mem_map,
+ const std::string& name,
+ void* allocator,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
+ size_t growth_limit,
bool can_move_objects) OVERRIDE;
uint64_t GetBytesAllocated() OVERRIDE;
@@ -147,9 +156,16 @@
void DumpStats(std::ostream& os);
protected:
- RosAllocSpace(MemMap* mem_map, size_t initial_size, const std::string& name,
- allocator::RosAlloc* rosalloc, uint8_t* begin, uint8_t* end, uint8_t* limit,
- size_t growth_limit, bool can_move_objects, size_t starting_size,
+ RosAllocSpace(MemMap&& mem_map,
+ size_t initial_size,
+ const std::string& name,
+ allocator::RosAlloc* rosalloc,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
+ size_t growth_limit,
+ bool can_move_objects,
+ size_t starting_size,
bool low_memory_mode);
private:
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index 4f43d9f..4e173a8 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -377,30 +377,30 @@
}
MemMap* GetMemMap() {
- return mem_map_.get();
+ return &mem_map_;
}
const MemMap* GetMemMap() const {
- return mem_map_.get();
+ return &mem_map_;
}
- MemMap* ReleaseMemMap() {
- return mem_map_.release();
+ MemMap ReleaseMemMap() {
+ return std::move(mem_map_);
}
protected:
MemMapSpace(const std::string& name,
- MemMap* mem_map,
+ MemMap&& mem_map,
uint8_t* begin,
uint8_t* end,
uint8_t* limit,
GcRetentionPolicy gc_retention_policy)
: ContinuousSpace(name, gc_retention_policy, begin, end, limit),
- mem_map_(mem_map) {
+ mem_map_(std::move(mem_map)) {
}
// Underlying storage of the space
- std::unique_ptr<MemMap> mem_map_;
+ MemMap mem_map_;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(MemMapSpace);
@@ -451,9 +451,13 @@
std::unique_ptr<accounting::ContinuousSpaceBitmap> mark_bitmap_;
std::unique_ptr<accounting::ContinuousSpaceBitmap> temp_bitmap_;
- ContinuousMemMapAllocSpace(const std::string& name, MemMap* mem_map, uint8_t* begin,
- uint8_t* end, uint8_t* limit, GcRetentionPolicy gc_retention_policy)
- : MemMapSpace(name, mem_map, begin, end, limit, gc_retention_policy) {
+ ContinuousMemMapAllocSpace(const std::string& name,
+ MemMap&& mem_map,
+ uint8_t* begin,
+ uint8_t* end,
+ uint8_t* limit,
+ GcRetentionPolicy gc_retention_policy)
+ : MemMapSpace(name, std::move(mem_map), begin, end, limit, gc_retention_policy) {
}
private:
diff --git a/runtime/gc/space/zygote_space.cc b/runtime/gc/space/zygote_space.cc
index 8c73ef9..ed85b06 100644
--- a/runtime/gc/space/zygote_space.cc
+++ b/runtime/gc/space/zygote_space.cc
@@ -41,7 +41,8 @@
size_t* const objects_allocated_;
};
-ZygoteSpace* ZygoteSpace::Create(const std::string& name, MemMap* mem_map,
+ZygoteSpace* ZygoteSpace::Create(const std::string& name,
+ MemMap&& mem_map,
accounting::ContinuousSpaceBitmap* live_bitmap,
accounting::ContinuousSpaceBitmap* mark_bitmap) {
DCHECK(live_bitmap != nullptr);
@@ -49,9 +50,9 @@
size_t objects_allocated = 0;
CountObjectsAllocated visitor(&objects_allocated);
ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
- live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(mem_map->Begin()),
- reinterpret_cast<uintptr_t>(mem_map->End()), visitor);
- ZygoteSpace* zygote_space = new ZygoteSpace(name, mem_map, objects_allocated);
+ live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(mem_map.Begin()),
+ reinterpret_cast<uintptr_t>(mem_map.End()), visitor);
+ ZygoteSpace* zygote_space = new ZygoteSpace(name, std::move(mem_map), objects_allocated);
CHECK(zygote_space->live_bitmap_.get() == nullptr);
CHECK(zygote_space->mark_bitmap_.get() == nullptr);
zygote_space->live_bitmap_.reset(live_bitmap);
@@ -64,8 +65,12 @@
UNREACHABLE();
}
-ZygoteSpace::ZygoteSpace(const std::string& name, MemMap* mem_map, size_t objects_allocated)
- : ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->End(), mem_map->End(),
+ZygoteSpace::ZygoteSpace(const std::string& name, MemMap&& mem_map, size_t objects_allocated)
+ : ContinuousMemMapAllocSpace(name,
+ std::move(mem_map),
+ mem_map.Begin(),
+ mem_map.End(),
+ mem_map.End(),
kGcRetentionPolicyFullCollect),
objects_allocated_(objects_allocated) {
}
diff --git a/runtime/gc/space/zygote_space.h b/runtime/gc/space/zygote_space.h
index 6fe21d9..200c79f 100644
--- a/runtime/gc/space/zygote_space.h
+++ b/runtime/gc/space/zygote_space.h
@@ -30,7 +30,8 @@
class ZygoteSpace FINAL : public ContinuousMemMapAllocSpace {
public:
// Returns the remaining storage in the out_map field.
- static ZygoteSpace* Create(const std::string& name, MemMap* mem_map,
+ static ZygoteSpace* Create(const std::string& name,
+ MemMap&& mem_map,
accounting::ContinuousSpaceBitmap* live_bitmap,
accounting::ContinuousSpaceBitmap* mark_bitmap)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -85,7 +86,7 @@
}
private:
- ZygoteSpace(const std::string& name, MemMap* mem_map, size_t objects_allocated);
+ ZygoteSpace(const std::string& name, MemMap&& mem_map, size_t objects_allocated);
static void SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg);
AtomicInteger objects_allocated_;
diff --git a/runtime/gc/system_weak_test.cc b/runtime/gc/system_weak_test.cc
index 21f5117..897ab01 100644
--- a/runtime/gc/system_weak_test.cc
+++ b/runtime/gc/system_weak_test.cc
@@ -26,6 +26,7 @@
#include "gc_root-inl.h"
#include "handle_scope-inl.h"
#include "heap.h"
+#include "mirror/object-inl.h"
#include "mirror/string.h"
#include "scoped_thread_state_change-inl.h"
#include "thread_list.h"
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index 464c2b7..a9230e0 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -80,9 +80,9 @@
DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_HASH_BITS), (static_cast<int32_t>(art::LeastSignificantBit(art::mirror::DexCache::kDexCacheStringCacheSize))))
#define STRING_DEX_CACHE_ELEMENT_SIZE 8
DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_ELEMENT_SIZE), (static_cast<int32_t>(sizeof(art::mirror::StringDexCachePair))))
-#define METHOD_DEX_CACHE_SIZE_MINUS_ONE 1023
+#define METHOD_DEX_CACHE_SIZE_MINUS_ONE 511
DEFINE_CHECK_EQ(static_cast<int32_t>(METHOD_DEX_CACHE_SIZE_MINUS_ONE), (static_cast<int32_t>(art::mirror::DexCache::kDexCacheMethodCacheSize - 1)))
-#define METHOD_DEX_CACHE_HASH_BITS 10
+#define METHOD_DEX_CACHE_HASH_BITS 9
DEFINE_CHECK_EQ(static_cast<int32_t>(METHOD_DEX_CACHE_HASH_BITS), (static_cast<int32_t>(art::LeastSignificantBit(art::mirror::DexCache::kDexCacheMethodCacheSize))))
#define CARD_TABLE_CARD_SHIFT 0xa
DEFINE_CHECK_EQ(static_cast<size_t>(CARD_TABLE_CARD_SHIFT), (static_cast<size_t>(art::gc::accounting::CardTable::kCardShift)))
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index e41d1d3..5729800 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -174,6 +174,10 @@
if (action_taken == kDeny) {
log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1);
}
+ const std::string& package_name = Runtime::Current()->GetProcessPackageName();
+ if (!package_name.empty()) {
+ log_maker.SetPackageName(package_name);
+ }
std::ostringstream signature_str;
Dump(signature_str);
log_maker.AddTaggedData(FIELD_HIDDEN_API_SIGNATURE, signature_str.str());
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index dc42cfa..3f44928 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -1590,7 +1590,7 @@
if (obj == nullptr) {
return;
}
- MarkRootObject(obj, 0, xlate[info.GetType()], info.GetThreadId());
+ MarkRootObject(obj, nullptr, xlate[info.GetType()], info.GetThreadId());
}
// If "direct_to_ddms" is true, the other arguments are ignored, and data is
diff --git a/runtime/image.cc b/runtime/image.cc
index 028c515..b7a872c 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '3', '\0' }; // Image relocations.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '4', '\0' }; // Half DexCache F&M arrays.
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 950a54d..8ab4a9b 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -78,14 +78,18 @@
CHECK_LE(max_count, kMaxTableSizeInBytes / sizeof(IrtEntry));
const size_t table_bytes = max_count * sizeof(IrtEntry);
- table_mem_map_.reset(MemMap::MapAnonymous("indirect ref table", nullptr, table_bytes,
- PROT_READ | PROT_WRITE, false, false, error_msg));
- if (table_mem_map_.get() == nullptr && error_msg->empty()) {
+ table_mem_map_ = MemMap::MapAnonymous("indirect ref table",
+ /* addr */ nullptr,
+ table_bytes,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ error_msg);
+ if (!table_mem_map_.IsValid() && error_msg->empty()) {
*error_msg = "Unable to map memory for indirect ref table";
}
- if (table_mem_map_.get() != nullptr) {
- table_ = reinterpret_cast<IrtEntry*>(table_mem_map_->Begin());
+ if (table_mem_map_.IsValid()) {
+ table_ = reinterpret_cast<IrtEntry*>(table_mem_map_.Begin());
} else {
table_ = nullptr;
}
@@ -125,7 +129,7 @@
}
bool IndirectReferenceTable::IsValid() const {
- return table_mem_map_.get() != nullptr;
+ return table_mem_map_.IsValid();
}
// Holes:
@@ -217,20 +221,19 @@
// Note: the above check also ensures that there is no overflow below.
const size_t table_bytes = new_size * sizeof(IrtEntry);
- std::unique_ptr<MemMap> new_map(MemMap::MapAnonymous("indirect ref table",
- nullptr,
- table_bytes,
- PROT_READ | PROT_WRITE,
- false,
- false,
- error_msg));
- if (new_map == nullptr) {
+ MemMap new_map = MemMap::MapAnonymous("indirect ref table",
+ /* addr */ nullptr,
+ table_bytes,
+ PROT_READ | PROT_WRITE,
+ /* is_low_4gb */ false,
+ error_msg);
+ if (!new_map.IsValid()) {
return false;
}
- memcpy(new_map->Begin(), table_mem_map_->Begin(), table_mem_map_->Size());
+ memcpy(new_map.Begin(), table_mem_map_.Begin(), table_mem_map_.Size());
table_mem_map_ = std::move(new_map);
- table_ = reinterpret_cast<IrtEntry*>(table_mem_map_->Begin());
+ table_ = reinterpret_cast<IrtEntry*>(table_mem_map_.Begin());
max_entries_ = new_size;
return true;
@@ -444,7 +447,7 @@
ScopedTrace trace(__PRETTY_FUNCTION__);
const size_t top_index = Capacity();
auto* release_start = AlignUp(reinterpret_cast<uint8_t*>(&table_[top_index]), kPageSize);
- uint8_t* release_end = table_mem_map_->End();
+ uint8_t* release_end = table_mem_map_.End();
madvise(release_start, release_end - release_start, MADV_DONTNEED);
}
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index d2093f2..8c63c00 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -27,6 +27,7 @@
#include "base/bit_utils.h"
#include "base/macros.h"
+#include "base/mem_map.h"
#include "base/mutex.h"
#include "gc_root.h"
#include "obj_ptr.h"
@@ -41,8 +42,6 @@
class Object;
} // namespace mirror
-class MemMap;
-
// Maintain a table of indirect references. Used for local/global JNI references.
//
// The table contains object references, where the strong (local/global) references are part of the
@@ -398,7 +397,7 @@
IRTSegmentState segment_state_;
// Mem map where we store the indirect refs.
- std::unique_ptr<MemMap> table_mem_map_;
+ MemMap table_mem_map_;
// bottom of the stack. Do not directly access the object references
// in this as they are roots. Use Get() that has a read barrier.
IrtEntry* table_;
diff --git a/runtime/interpreter/mterp/arm/op_iget.S b/runtime/interpreter/mterp/arm/op_iget.S
index c7f777b..1684a76 100644
--- a/runtime/interpreter/mterp/arm/op_iget.S
+++ b/runtime/interpreter/mterp/arm/op_iget.S
@@ -1,4 +1,4 @@
-%default { "is_object":"0", "helper":"artGet32InstanceFromCode"}
+%default { "is_object":"0", "helper":"MterpIGetU32"}
/*
* General instance field get.
*
diff --git a/runtime/interpreter/mterp/arm/op_iget_boolean.S b/runtime/interpreter/mterp/arm/op_iget_boolean.S
index 628f40a..f23cb3a 100644
--- a/runtime/interpreter/mterp/arm/op_iget_boolean.S
+++ b/runtime/interpreter/mterp/arm/op_iget_boolean.S
@@ -1 +1 @@
-%include "arm/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" }
+%include "arm/op_iget.S" { "helper":"MterpIGetU8" }
diff --git a/runtime/interpreter/mterp/arm/op_iget_byte.S b/runtime/interpreter/mterp/arm/op_iget_byte.S
index c4e08e2..9c4f37c 100644
--- a/runtime/interpreter/mterp/arm/op_iget_byte.S
+++ b/runtime/interpreter/mterp/arm/op_iget_byte.S
@@ -1 +1 @@
-%include "arm/op_iget.S" { "helper":"artGetByteInstanceFromCode" }
+%include "arm/op_iget.S" { "helper":"MterpIGetI8" }
diff --git a/runtime/interpreter/mterp/arm/op_iget_char.S b/runtime/interpreter/mterp/arm/op_iget_char.S
index 5e8da66..80c4227 100644
--- a/runtime/interpreter/mterp/arm/op_iget_char.S
+++ b/runtime/interpreter/mterp/arm/op_iget_char.S
@@ -1 +1 @@
-%include "arm/op_iget.S" { "helper":"artGetCharInstanceFromCode" }
+%include "arm/op_iget.S" { "helper":"MterpIGetU16" }
diff --git a/runtime/interpreter/mterp/arm/op_iget_object.S b/runtime/interpreter/mterp/arm/op_iget_object.S
index 1cf2e3c..e30b129 100644
--- a/runtime/interpreter/mterp/arm/op_iget_object.S
+++ b/runtime/interpreter/mterp/arm/op_iget_object.S
@@ -1 +1 @@
-%include "arm/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" }
+%include "arm/op_iget.S" { "is_object":"1", "helper":"MterpIGetObj" }
diff --git a/runtime/interpreter/mterp/arm/op_iget_short.S b/runtime/interpreter/mterp/arm/op_iget_short.S
index 460f045..dd6bc99 100644
--- a/runtime/interpreter/mterp/arm/op_iget_short.S
+++ b/runtime/interpreter/mterp/arm/op_iget_short.S
@@ -1 +1 @@
-%include "arm/op_iget.S" { "helper":"artGetShortInstanceFromCode" }
+%include "arm/op_iget.S" { "helper":"MterpIGetI16" }
diff --git a/runtime/interpreter/mterp/arm/op_iget_wide.S b/runtime/interpreter/mterp/arm/op_iget_wide.S
index e287d51..46e9ec8 100644
--- a/runtime/interpreter/mterp/arm/op_iget_wide.S
+++ b/runtime/interpreter/mterp/arm/op_iget_wide.S
@@ -9,7 +9,7 @@
GET_VREG r1, r1 @ r1<- fp[B], the object pointer
ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- referrer
mov r3, rSELF @ r3<- self
- bl artGet64InstanceFromCode
+ bl MterpIGetU64
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
ubfx r2, rINST, #8, #4 @ r2<- A
PREFETCH_INST 2
diff --git a/runtime/interpreter/mterp/arm/op_iput.S b/runtime/interpreter/mterp/arm/op_iput.S
index d224cd8..a16795d 100644
--- a/runtime/interpreter/mterp/arm/op_iput.S
+++ b/runtime/interpreter/mterp/arm/op_iput.S
@@ -1,11 +1,11 @@
-%default { "is_object":"0", "handler":"artSet32InstanceFromMterp" }
+%default { "is_object":"0", "helper":"MterpIPutU32" }
/*
* General 32-bit instance field put.
*
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern $handler
+ .extern $helper
EXPORT_PC
FETCH r0, 1 @ r0<- field ref CCCC
mov r1, rINST, lsr #12 @ r1<- B
@@ -14,7 +14,7 @@
GET_VREG r2, r2 @ r2<- fp[A]
ldr r3, [rFP, #OFF_FP_METHOD] @ r3<- referrer
PREFETCH_INST 2
- bl $handler
+ bl $helper
cmp r0, #0
bne MterpPossibleException
ADVANCE 2 @ advance rPC
diff --git a/runtime/interpreter/mterp/arm/op_iput_boolean.S b/runtime/interpreter/mterp/arm/op_iput_boolean.S
index c9e8589..57edadd 100644
--- a/runtime/interpreter/mterp/arm/op_iput_boolean.S
+++ b/runtime/interpreter/mterp/arm/op_iput_boolean.S
@@ -1 +1 @@
-%include "arm/op_iput.S" { "handler":"artSet8InstanceFromMterp" }
+%include "arm/op_iput.S" { "helper":"MterpIPutU8" }
diff --git a/runtime/interpreter/mterp/arm/op_iput_byte.S b/runtime/interpreter/mterp/arm/op_iput_byte.S
index c9e8589..ab283b9 100644
--- a/runtime/interpreter/mterp/arm/op_iput_byte.S
+++ b/runtime/interpreter/mterp/arm/op_iput_byte.S
@@ -1 +1 @@
-%include "arm/op_iput.S" { "handler":"artSet8InstanceFromMterp" }
+%include "arm/op_iput.S" { "helper":"MterpIPutI8" }
diff --git a/runtime/interpreter/mterp/arm/op_iput_char.S b/runtime/interpreter/mterp/arm/op_iput_char.S
index 5046f6b..0fe5d96 100644
--- a/runtime/interpreter/mterp/arm/op_iput_char.S
+++ b/runtime/interpreter/mterp/arm/op_iput_char.S
@@ -1 +1 @@
-%include "arm/op_iput.S" { "handler":"artSet16InstanceFromMterp" }
+%include "arm/op_iput.S" { "helper":"MterpIPutU16" }
diff --git a/runtime/interpreter/mterp/arm/op_iput_object.S b/runtime/interpreter/mterp/arm/op_iput_object.S
index d942e84..4f401eb 100644
--- a/runtime/interpreter/mterp/arm/op_iput_object.S
+++ b/runtime/interpreter/mterp/arm/op_iput_object.S
@@ -3,7 +3,7 @@
mov r1, rPC
mov r2, rINST
mov r3, rSELF
- bl MterpIputObject
+ bl MterpIPutObj
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
diff --git a/runtime/interpreter/mterp/arm/op_iput_short.S b/runtime/interpreter/mterp/arm/op_iput_short.S
index 5046f6b..cc98363 100644
--- a/runtime/interpreter/mterp/arm/op_iput_short.S
+++ b/runtime/interpreter/mterp/arm/op_iput_short.S
@@ -1 +1 @@
-%include "arm/op_iput.S" { "handler":"artSet16InstanceFromMterp" }
+%include "arm/op_iput.S" { "helper":"MterpIPutI16" }
diff --git a/runtime/interpreter/mterp/arm/op_iput_wide.S b/runtime/interpreter/mterp/arm/op_iput_wide.S
index 3dda187..6a41473 100644
--- a/runtime/interpreter/mterp/arm/op_iput_wide.S
+++ b/runtime/interpreter/mterp/arm/op_iput_wide.S
@@ -1,5 +1,5 @@
/* iput-wide vA, vB, field@CCCC */
- .extern artSet64InstanceFromMterp
+ .extern MterpIPutU64
EXPORT_PC
FETCH r0, 1 @ r0<- field ref CCCC
mov r1, rINST, lsr #12 @ r1<- B
@@ -8,7 +8,7 @@
VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[A]
ldr r3, [rFP, #OFF_FP_METHOD] @ r3<- referrer
PREFETCH_INST 2
- bl artSet64InstanceFromMterp
+ bl MterpIPutU64
cmp r0, #0
bne MterpPossibleException
ADVANCE 2 @ advance rPC
diff --git a/runtime/interpreter/mterp/arm/op_sget.S b/runtime/interpreter/mterp/arm/op_sget.S
index 3c813ef..575a8c0 100644
--- a/runtime/interpreter/mterp/arm/op_sget.S
+++ b/runtime/interpreter/mterp/arm/op_sget.S
@@ -1,4 +1,4 @@
-%default { "is_object":"0", "helper":"MterpGet32Static" }
+%default { "is_object":"0", "helper":"MterpSGetU32" }
/*
* General SGET handler wrapper.
*
diff --git a/runtime/interpreter/mterp/arm/op_sget_boolean.S b/runtime/interpreter/mterp/arm/op_sget_boolean.S
index eb06aa8..df1a024 100644
--- a/runtime/interpreter/mterp/arm/op_sget_boolean.S
+++ b/runtime/interpreter/mterp/arm/op_sget_boolean.S
@@ -1 +1 @@
-%include "arm/op_sget.S" {"helper":"MterpGetBooleanStatic"}
+%include "arm/op_sget.S" {"helper":"MterpSGetU8"}
diff --git a/runtime/interpreter/mterp/arm/op_sget_byte.S b/runtime/interpreter/mterp/arm/op_sget_byte.S
index 9f4c904..8ad3ff0 100644
--- a/runtime/interpreter/mterp/arm/op_sget_byte.S
+++ b/runtime/interpreter/mterp/arm/op_sget_byte.S
@@ -1 +1 @@
-%include "arm/op_sget.S" {"helper":"MterpGetByteStatic"}
+%include "arm/op_sget.S" {"helper":"MterpSGetI8"}
diff --git a/runtime/interpreter/mterp/arm/op_sget_char.S b/runtime/interpreter/mterp/arm/op_sget_char.S
index dd8c991..5239514 100644
--- a/runtime/interpreter/mterp/arm/op_sget_char.S
+++ b/runtime/interpreter/mterp/arm/op_sget_char.S
@@ -1 +1 @@
-%include "arm/op_sget.S" {"helper":"MterpGetCharStatic"}
+%include "arm/op_sget.S" {"helper":"MterpSGetU16"}
diff --git a/runtime/interpreter/mterp/arm/op_sget_object.S b/runtime/interpreter/mterp/arm/op_sget_object.S
index e1d9eae..e61a5a7 100644
--- a/runtime/interpreter/mterp/arm/op_sget_object.S
+++ b/runtime/interpreter/mterp/arm/op_sget_object.S
@@ -1 +1 @@
-%include "arm/op_sget.S" {"is_object":"1", "helper":"MterpGetObjStatic"}
+%include "arm/op_sget.S" {"is_object":"1", "helper":"MterpSGetObj"}
diff --git a/runtime/interpreter/mterp/arm/op_sget_short.S b/runtime/interpreter/mterp/arm/op_sget_short.S
index c0d61c4..49493eb 100644
--- a/runtime/interpreter/mterp/arm/op_sget_short.S
+++ b/runtime/interpreter/mterp/arm/op_sget_short.S
@@ -1 +1 @@
-%include "arm/op_sget.S" {"helper":"MterpGetShortStatic"}
+%include "arm/op_sget.S" {"helper":"MterpSGetI16"}
diff --git a/runtime/interpreter/mterp/arm/op_sget_wide.S b/runtime/interpreter/mterp/arm/op_sget_wide.S
index aeee016..5981ec4 100644
--- a/runtime/interpreter/mterp/arm/op_sget_wide.S
+++ b/runtime/interpreter/mterp/arm/op_sget_wide.S
@@ -4,12 +4,12 @@
*/
/* sget-wide vAA, field@BBBB */
- .extern MterpGet64Static
+ .extern MterpSGetU64
EXPORT_PC
FETCH r0, 1 @ r0<- field ref BBBB
ldr r1, [rFP, #OFF_FP_METHOD]
mov r2, rSELF
- bl MterpGet64Static
+ bl MterpSGetU64
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
mov r9, rINST, lsr #8 @ r9<- AA
VREG_INDEX_TO_ADDR lr, r9 @ r9<- &fp[AA]
diff --git a/runtime/interpreter/mterp/arm/op_sput.S b/runtime/interpreter/mterp/arm/op_sput.S
index 494df8a..c4a8978 100644
--- a/runtime/interpreter/mterp/arm/op_sput.S
+++ b/runtime/interpreter/mterp/arm/op_sput.S
@@ -1,4 +1,4 @@
-%default { "helper":"MterpSet32Static"}
+%default { "helper":"MterpSPutU32"}
/*
* General SPUT handler wrapper.
*
diff --git a/runtime/interpreter/mterp/arm/op_sput_boolean.S b/runtime/interpreter/mterp/arm/op_sput_boolean.S
index 47bed0a..0c37623 100644
--- a/runtime/interpreter/mterp/arm/op_sput_boolean.S
+++ b/runtime/interpreter/mterp/arm/op_sput_boolean.S
@@ -1 +1 @@
-%include "arm/op_sput.S" {"helper":"MterpSetBooleanStatic"}
+%include "arm/op_sput.S" {"helper":"MterpSPutU8"}
diff --git a/runtime/interpreter/mterp/arm/op_sput_byte.S b/runtime/interpreter/mterp/arm/op_sput_byte.S
index b4d22b4..8d4e754 100644
--- a/runtime/interpreter/mterp/arm/op_sput_byte.S
+++ b/runtime/interpreter/mterp/arm/op_sput_byte.S
@@ -1 +1 @@
-%include "arm/op_sput.S" {"helper":"MterpSetByteStatic"}
+%include "arm/op_sput.S" {"helper":"MterpSPutI8"}
diff --git a/runtime/interpreter/mterp/arm/op_sput_char.S b/runtime/interpreter/mterp/arm/op_sput_char.S
index 58a957d..442b56f 100644
--- a/runtime/interpreter/mterp/arm/op_sput_char.S
+++ b/runtime/interpreter/mterp/arm/op_sput_char.S
@@ -1 +1 @@
-%include "arm/op_sput.S" {"helper":"MterpSetCharStatic"}
+%include "arm/op_sput.S" {"helper":"MterpSPutU16"}
diff --git a/runtime/interpreter/mterp/arm/op_sput_object.S b/runtime/interpreter/mterp/arm/op_sput_object.S
index 6d3a9a7..c58918f 100644
--- a/runtime/interpreter/mterp/arm/op_sput_object.S
+++ b/runtime/interpreter/mterp/arm/op_sput_object.S
@@ -3,7 +3,7 @@
mov r1, rPC
mov r2, rINST
mov r3, rSELF
- bl MterpSputObject
+ bl MterpSPutObj
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
diff --git a/runtime/interpreter/mterp/arm/op_sput_short.S b/runtime/interpreter/mterp/arm/op_sput_short.S
index 88c3211..0eb533f 100644
--- a/runtime/interpreter/mterp/arm/op_sput_short.S
+++ b/runtime/interpreter/mterp/arm/op_sput_short.S
@@ -1 +1 @@
-%include "arm/op_sput.S" {"helper":"MterpSetShortStatic"}
+%include "arm/op_sput.S" {"helper":"MterpSPutI16"}
diff --git a/runtime/interpreter/mterp/arm/op_sput_wide.S b/runtime/interpreter/mterp/arm/op_sput_wide.S
index 1e8fcc9..0ed4017 100644
--- a/runtime/interpreter/mterp/arm/op_sput_wide.S
+++ b/runtime/interpreter/mterp/arm/op_sput_wide.S
@@ -3,7 +3,7 @@
*
*/
/* sput-wide vAA, field@BBBB */
- .extern MterpSet64Static
+ .extern MterpSPutU64
EXPORT_PC
FETCH r0, 1 @ r0<- field ref BBBB
mov r1, rINST, lsr #8 @ r1<- AA
@@ -11,7 +11,7 @@
ldr r2, [rFP, #OFF_FP_METHOD]
mov r3, rSELF
PREFETCH_INST 2 @ Get next inst, but don't advance rPC
- bl MterpSet64Static
+ bl MterpSPutU64
cmp r0, #0 @ 0 on success, -1 on failure
bne MterpException
ADVANCE 2 @ Past exception point - now advance rPC
diff --git a/runtime/interpreter/mterp/arm64/op_iget.S b/runtime/interpreter/mterp/arm64/op_iget.S
index 88533bd..cb453ac 100644
--- a/runtime/interpreter/mterp/arm64/op_iget.S
+++ b/runtime/interpreter/mterp/arm64/op_iget.S
@@ -1,4 +1,4 @@
-%default { "extend":"", "is_object":"0", "helper":"artGet32InstanceFromCode"}
+%default { "extend":"", "is_object":"0", "helper":"MterpIGetU32"}
/*
* General instance field get.
*
diff --git a/runtime/interpreter/mterp/arm64/op_iget_boolean.S b/runtime/interpreter/mterp/arm64/op_iget_boolean.S
index 36a9b6b..3b17144 100644
--- a/runtime/interpreter/mterp/arm64/op_iget_boolean.S
+++ b/runtime/interpreter/mterp/arm64/op_iget_boolean.S
@@ -1 +1 @@
-%include "arm64/op_iget.S" { "helper":"artGetBooleanInstanceFromCode", "extend":"uxtb w0, w0" }
+%include "arm64/op_iget.S" { "helper":"MterpIGetU8", "extend":"uxtb w0, w0" }
diff --git a/runtime/interpreter/mterp/arm64/op_iget_byte.S b/runtime/interpreter/mterp/arm64/op_iget_byte.S
index fd3f164..d5ef1d3 100644
--- a/runtime/interpreter/mterp/arm64/op_iget_byte.S
+++ b/runtime/interpreter/mterp/arm64/op_iget_byte.S
@@ -1 +1 @@
-%include "arm64/op_iget.S" { "helper":"artGetByteInstanceFromCode", "extend":"sxtb w0, w0" }
+%include "arm64/op_iget.S" { "helper":"MterpIGetI8", "extend":"sxtb w0, w0" }
diff --git a/runtime/interpreter/mterp/arm64/op_iget_char.S b/runtime/interpreter/mterp/arm64/op_iget_char.S
index ea23275..68e1435 100644
--- a/runtime/interpreter/mterp/arm64/op_iget_char.S
+++ b/runtime/interpreter/mterp/arm64/op_iget_char.S
@@ -1 +1 @@
-%include "arm64/op_iget.S" { "helper":"artGetCharInstanceFromCode", "extend":"uxth w0, w0" }
+%include "arm64/op_iget.S" { "helper":"MterpIGetU16", "extend":"uxth w0, w0" }
diff --git a/runtime/interpreter/mterp/arm64/op_iget_object.S b/runtime/interpreter/mterp/arm64/op_iget_object.S
index 03be78d..40ddadd 100644
--- a/runtime/interpreter/mterp/arm64/op_iget_object.S
+++ b/runtime/interpreter/mterp/arm64/op_iget_object.S
@@ -1 +1 @@
-%include "arm64/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" }
+%include "arm64/op_iget.S" { "is_object":"1", "helper":"MterpIGetObj" }
diff --git a/runtime/interpreter/mterp/arm64/op_iget_short.S b/runtime/interpreter/mterp/arm64/op_iget_short.S
index c347542..714f4b9 100644
--- a/runtime/interpreter/mterp/arm64/op_iget_short.S
+++ b/runtime/interpreter/mterp/arm64/op_iget_short.S
@@ -1 +1 @@
-%include "arm64/op_iget.S" { "helper":"artGetShortInstanceFromCode", "extend":"sxth w0, w0" }
+%include "arm64/op_iget.S" { "helper":"MterpIGetI16", "extend":"sxth w0, w0" }
diff --git a/runtime/interpreter/mterp/arm64/op_iget_wide.S b/runtime/interpreter/mterp/arm64/op_iget_wide.S
index 9718390..4fc735c 100644
--- a/runtime/interpreter/mterp/arm64/op_iget_wide.S
+++ b/runtime/interpreter/mterp/arm64/op_iget_wide.S
@@ -9,7 +9,7 @@
GET_VREG w1, w1 // w1<- fp[B], the object pointer
ldr x2, [xFP, #OFF_FP_METHOD] // w2<- referrer
mov x3, xSELF // w3<- self
- bl artGet64InstanceFromCode
+ bl MterpIGetU64
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
ubfx w2, wINST, #8, #4 // w2<- A
PREFETCH_INST 2
diff --git a/runtime/interpreter/mterp/arm64/op_iput.S b/runtime/interpreter/mterp/arm64/op_iput.S
index a8c0e61..5e21d5c 100644
--- a/runtime/interpreter/mterp/arm64/op_iput.S
+++ b/runtime/interpreter/mterp/arm64/op_iput.S
@@ -1,11 +1,11 @@
-%default { "is_object":"0", "handler":"artSet32InstanceFromMterp" }
+%default { "is_object":"0", "helper":"MterpIPutU32" }
/*
* General 32-bit instance field put.
*
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field//CCCC */
- .extern $handler
+ .extern $helper
EXPORT_PC
FETCH w0, 1 // w0<- field ref CCCC
lsr w1, wINST, #12 // w1<- B
@@ -14,7 +14,7 @@
GET_VREG w2, w2 // w2<- fp[A]
ldr x3, [xFP, #OFF_FP_METHOD] // w3<- referrer
PREFETCH_INST 2
- bl $handler
+ bl $helper
cbnz w0, MterpPossibleException
ADVANCE 2 // advance rPC
GET_INST_OPCODE ip // extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm64/op_iput_boolean.S b/runtime/interpreter/mterp/arm64/op_iput_boolean.S
index bbf5319..12a278c 100644
--- a/runtime/interpreter/mterp/arm64/op_iput_boolean.S
+++ b/runtime/interpreter/mterp/arm64/op_iput_boolean.S
@@ -1 +1 @@
-%include "arm64/op_iput.S" { "handler":"artSet8InstanceFromMterp" }
+%include "arm64/op_iput.S" { "helper":"MterpIPutU8" }
diff --git a/runtime/interpreter/mterp/arm64/op_iput_byte.S b/runtime/interpreter/mterp/arm64/op_iput_byte.S
index bbf5319..82b99e9 100644
--- a/runtime/interpreter/mterp/arm64/op_iput_byte.S
+++ b/runtime/interpreter/mterp/arm64/op_iput_byte.S
@@ -1 +1 @@
-%include "arm64/op_iput.S" { "handler":"artSet8InstanceFromMterp" }
+%include "arm64/op_iput.S" { "helper":"MterpIPutI8" }
diff --git a/runtime/interpreter/mterp/arm64/op_iput_char.S b/runtime/interpreter/mterp/arm64/op_iput_char.S
index 150d879..427d92d 100644
--- a/runtime/interpreter/mterp/arm64/op_iput_char.S
+++ b/runtime/interpreter/mterp/arm64/op_iput_char.S
@@ -1 +1 @@
-%include "arm64/op_iput.S" { "handler":"artSet16InstanceFromMterp" }
+%include "arm64/op_iput.S" { "helper":"MterpIPutU16" }
diff --git a/runtime/interpreter/mterp/arm64/op_iput_object.S b/runtime/interpreter/mterp/arm64/op_iput_object.S
index 37a649b..0c0441a 100644
--- a/runtime/interpreter/mterp/arm64/op_iput_object.S
+++ b/runtime/interpreter/mterp/arm64/op_iput_object.S
@@ -3,7 +3,7 @@
mov x1, xPC
mov w2, wINST
mov x3, xSELF
- bl MterpIputObject
+ bl MterpIPutObj
cbz w0, MterpException
FETCH_ADVANCE_INST 2 // advance rPC, load rINST
GET_INST_OPCODE ip // extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm64/op_iput_short.S b/runtime/interpreter/mterp/arm64/op_iput_short.S
index 150d879..67f1ace 100644
--- a/runtime/interpreter/mterp/arm64/op_iput_short.S
+++ b/runtime/interpreter/mterp/arm64/op_iput_short.S
@@ -1 +1 @@
-%include "arm64/op_iput.S" { "handler":"artSet16InstanceFromMterp" }
+%include "arm64/op_iput.S" { "helper":"MterpIPutI16" }
diff --git a/runtime/interpreter/mterp/arm64/op_iput_wide.S b/runtime/interpreter/mterp/arm64/op_iput_wide.S
index e1ab127..be6aeb0 100644
--- a/runtime/interpreter/mterp/arm64/op_iput_wide.S
+++ b/runtime/interpreter/mterp/arm64/op_iput_wide.S
@@ -1,5 +1,5 @@
/* iput-wide vA, vB, field//CCCC */
- .extern artSet64InstanceFromMterp
+ .extern MterpIPutU64
EXPORT_PC
FETCH w0, 1 // w0<- field ref CCCC
lsr w1, wINST, #12 // w1<- B
@@ -8,7 +8,7 @@
VREG_INDEX_TO_ADDR x2, x2 // w2<- &fp[A]
ldr x3, [xFP, #OFF_FP_METHOD] // w3<- referrer
PREFETCH_INST 2
- bl artSet64InstanceFromMterp
+ bl MterpIPutU64
cbnz w0, MterpPossibleException
ADVANCE 2 // advance rPC
GET_INST_OPCODE ip // extract opcode from wINST
diff --git a/runtime/interpreter/mterp/arm64/op_sget.S b/runtime/interpreter/mterp/arm64/op_sget.S
index 84e71ac..00b07fa 100644
--- a/runtime/interpreter/mterp/arm64/op_sget.S
+++ b/runtime/interpreter/mterp/arm64/op_sget.S
@@ -1,4 +1,4 @@
-%default { "is_object":"0", "helper":"MterpGet32Static", "extend":"" }
+%default { "is_object":"0", "helper":"MterpSGetU32", "extend":"" }
/*
* General SGET handler wrapper.
*
diff --git a/runtime/interpreter/mterp/arm64/op_sget_boolean.S b/runtime/interpreter/mterp/arm64/op_sget_boolean.S
index 868f41c..73f3a10 100644
--- a/runtime/interpreter/mterp/arm64/op_sget_boolean.S
+++ b/runtime/interpreter/mterp/arm64/op_sget_boolean.S
@@ -1 +1 @@
-%include "arm64/op_sget.S" {"helper":"MterpGetBooleanStatic", "extend":"uxtb w0, w0"}
+%include "arm64/op_sget.S" {"helper":"MterpSGetU8", "extend":"uxtb w0, w0"}
diff --git a/runtime/interpreter/mterp/arm64/op_sget_byte.S b/runtime/interpreter/mterp/arm64/op_sget_byte.S
index e135aa7..38c0da6 100644
--- a/runtime/interpreter/mterp/arm64/op_sget_byte.S
+++ b/runtime/interpreter/mterp/arm64/op_sget_byte.S
@@ -1 +1 @@
-%include "arm64/op_sget.S" {"helper":"MterpGetByteStatic", "extend":"sxtb w0, w0"}
+%include "arm64/op_sget.S" {"helper":"MterpSGetI8", "extend":"sxtb w0, w0"}
diff --git a/runtime/interpreter/mterp/arm64/op_sget_char.S b/runtime/interpreter/mterp/arm64/op_sget_char.S
index 05d57ac..c0801bf 100644
--- a/runtime/interpreter/mterp/arm64/op_sget_char.S
+++ b/runtime/interpreter/mterp/arm64/op_sget_char.S
@@ -1 +1 @@
-%include "arm64/op_sget.S" {"helper":"MterpGetCharStatic", "extend":"uxth w0, w0"}
+%include "arm64/op_sget.S" {"helper":"MterpSGetU16", "extend":"uxth w0, w0"}
diff --git a/runtime/interpreter/mterp/arm64/op_sget_object.S b/runtime/interpreter/mterp/arm64/op_sget_object.S
index 1faaf6e..69d6adb 100644
--- a/runtime/interpreter/mterp/arm64/op_sget_object.S
+++ b/runtime/interpreter/mterp/arm64/op_sget_object.S
@@ -1 +1 @@
-%include "arm64/op_sget.S" {"is_object":"1", "helper":"MterpGetObjStatic"}
+%include "arm64/op_sget.S" {"is_object":"1", "helper":"MterpSGetObj"}
diff --git a/runtime/interpreter/mterp/arm64/op_sget_short.S b/runtime/interpreter/mterp/arm64/op_sget_short.S
index 5900231..81e0434 100644
--- a/runtime/interpreter/mterp/arm64/op_sget_short.S
+++ b/runtime/interpreter/mterp/arm64/op_sget_short.S
@@ -1 +1 @@
-%include "arm64/op_sget.S" {"helper":"MterpGetShortStatic", "extend":"sxth w0, w0"}
+%include "arm64/op_sget.S" {"helper":"MterpSGetI16", "extend":"sxth w0, w0"}
diff --git a/runtime/interpreter/mterp/arm64/op_sget_wide.S b/runtime/interpreter/mterp/arm64/op_sget_wide.S
index 92f3f7d..546ab94 100644
--- a/runtime/interpreter/mterp/arm64/op_sget_wide.S
+++ b/runtime/interpreter/mterp/arm64/op_sget_wide.S
@@ -4,12 +4,12 @@
*/
/* sget-wide vAA, field//BBBB */
- .extern MterpGet64StaticFromCode
+ .extern MterpSGetU64
EXPORT_PC
FETCH w0, 1 // w0<- field ref BBBB
ldr x1, [xFP, #OFF_FP_METHOD]
mov x2, xSELF
- bl MterpGet64Static
+ bl MterpSGetU64
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
lsr w4, wINST, #8 // w4<- AA
cbnz x3, MterpException // bail out
diff --git a/runtime/interpreter/mterp/arm64/op_sput.S b/runtime/interpreter/mterp/arm64/op_sput.S
index e322af0..7a0dc30 100644
--- a/runtime/interpreter/mterp/arm64/op_sput.S
+++ b/runtime/interpreter/mterp/arm64/op_sput.S
@@ -1,4 +1,4 @@
-%default { "helper":"MterpSet32Static"}
+%default { "helper":"MterpSPutU32"}
/*
* General SPUT handler wrapper.
*
diff --git a/runtime/interpreter/mterp/arm64/op_sput_boolean.S b/runtime/interpreter/mterp/arm64/op_sput_boolean.S
index 9928f31..3d0c7c0 100644
--- a/runtime/interpreter/mterp/arm64/op_sput_boolean.S
+++ b/runtime/interpreter/mterp/arm64/op_sput_boolean.S
@@ -1 +1 @@
-%include "arm64/op_sput.S" {"helper":"MterpSetBooleanStatic"}
+%include "arm64/op_sput.S" {"helper":"MterpSPutU8"}
diff --git a/runtime/interpreter/mterp/arm64/op_sput_byte.S b/runtime/interpreter/mterp/arm64/op_sput_byte.S
index 16d6ba9..489cf92 100644
--- a/runtime/interpreter/mterp/arm64/op_sput_byte.S
+++ b/runtime/interpreter/mterp/arm64/op_sput_byte.S
@@ -1 +1 @@
-%include "arm64/op_sput.S" {"helper":"MterpSetByteStatic"}
+%include "arm64/op_sput.S" {"helper":"MterpSPutI8"}
diff --git a/runtime/interpreter/mterp/arm64/op_sput_char.S b/runtime/interpreter/mterp/arm64/op_sput_char.S
index ab5e815..f79d311 100644
--- a/runtime/interpreter/mterp/arm64/op_sput_char.S
+++ b/runtime/interpreter/mterp/arm64/op_sput_char.S
@@ -1 +1 @@
-%include "arm64/op_sput.S" {"helper":"MterpSetCharStatic"}
+%include "arm64/op_sput.S" {"helper":"MterpSPutU16"}
diff --git a/runtime/interpreter/mterp/arm64/op_sput_object.S b/runtime/interpreter/mterp/arm64/op_sput_object.S
index c176da2..a649656 100644
--- a/runtime/interpreter/mterp/arm64/op_sput_object.S
+++ b/runtime/interpreter/mterp/arm64/op_sput_object.S
@@ -3,7 +3,7 @@
mov x1, xPC
mov x2, xINST
mov x3, xSELF
- bl MterpSputObject
+ bl MterpSPutObj
cbz w0, MterpException
FETCH_ADVANCE_INST 2 // advance rPC, load rINST
GET_INST_OPCODE ip // extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm64/op_sput_short.S b/runtime/interpreter/mterp/arm64/op_sput_short.S
index b54f88a..06482cd 100644
--- a/runtime/interpreter/mterp/arm64/op_sput_short.S
+++ b/runtime/interpreter/mterp/arm64/op_sput_short.S
@@ -1 +1 @@
-%include "arm64/op_sput.S" {"helper":"MterpSetShortStatic"}
+%include "arm64/op_sput.S" {"helper":"MterpSPutI16"}
diff --git a/runtime/interpreter/mterp/arm64/op_sput_wide.S b/runtime/interpreter/mterp/arm64/op_sput_wide.S
index 4aeb8ff..58b3c42 100644
--- a/runtime/interpreter/mterp/arm64/op_sput_wide.S
+++ b/runtime/interpreter/mterp/arm64/op_sput_wide.S
@@ -3,7 +3,7 @@
*
*/
/* sput-wide vAA, field//BBBB */
- .extern MterpSet64Static
+ .extern MterpSPutU64
EXPORT_PC
FETCH w0, 1 // w0<- field ref BBBB
lsr w1, wINST, #8 // w1<- AA
@@ -11,7 +11,7 @@
ldr x2, [xFP, #OFF_FP_METHOD]
mov x3, xSELF
PREFETCH_INST 2 // Get next inst, but don't advance rPC
- bl MterpSet64Static
+ bl MterpSPutU64
cbnz w0, MterpException // 0 on success, -1 on failure
ADVANCE 2 // Past exception point - now advance rPC
GET_INST_OPCODE ip // extract opcode from wINST
diff --git a/runtime/interpreter/mterp/mips/op_iget.S b/runtime/interpreter/mterp/mips/op_iget.S
index 01f42d9..33717de 100644
--- a/runtime/interpreter/mterp/mips/op_iget.S
+++ b/runtime/interpreter/mterp/mips/op_iget.S
@@ -1,4 +1,4 @@
-%default { "is_object":"0", "helper":"artGet32InstanceFromCode"}
+%default { "is_object":"0", "helper":"MterpIGetU32"}
/*
* General instance field get.
*
diff --git a/runtime/interpreter/mterp/mips/op_iget_boolean.S b/runtime/interpreter/mterp/mips/op_iget_boolean.S
index e03364e..f2ef68d 100644
--- a/runtime/interpreter/mterp/mips/op_iget_boolean.S
+++ b/runtime/interpreter/mterp/mips/op_iget_boolean.S
@@ -1 +1 @@
-%include "mips/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" }
+%include "mips/op_iget.S" { "helper":"MterpIGetU8" }
diff --git a/runtime/interpreter/mterp/mips/op_iget_byte.S b/runtime/interpreter/mterp/mips/op_iget_byte.S
index dc87cfe..0c8fb7c 100644
--- a/runtime/interpreter/mterp/mips/op_iget_byte.S
+++ b/runtime/interpreter/mterp/mips/op_iget_byte.S
@@ -1 +1 @@
-%include "mips/op_iget.S" { "helper":"artGetByteInstanceFromCode" }
+%include "mips/op_iget.S" { "helper":"MterpIGetI8" }
diff --git a/runtime/interpreter/mterp/mips/op_iget_char.S b/runtime/interpreter/mterp/mips/op_iget_char.S
index 55f8a93..69d04c4 100644
--- a/runtime/interpreter/mterp/mips/op_iget_char.S
+++ b/runtime/interpreter/mterp/mips/op_iget_char.S
@@ -1 +1 @@
-%include "mips/op_iget.S" { "helper":"artGetCharInstanceFromCode" }
+%include "mips/op_iget.S" { "helper":"MterpIGetU16" }
diff --git a/runtime/interpreter/mterp/mips/op_iget_object.S b/runtime/interpreter/mterp/mips/op_iget_object.S
index 11d93a4..bea330a 100644
--- a/runtime/interpreter/mterp/mips/op_iget_object.S
+++ b/runtime/interpreter/mterp/mips/op_iget_object.S
@@ -1 +1 @@
-%include "mips/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" }
+%include "mips/op_iget.S" { "is_object":"1", "helper":"MterpIGetObj" }
diff --git a/runtime/interpreter/mterp/mips/op_iget_short.S b/runtime/interpreter/mterp/mips/op_iget_short.S
index 9086246..357c791 100644
--- a/runtime/interpreter/mterp/mips/op_iget_short.S
+++ b/runtime/interpreter/mterp/mips/op_iget_short.S
@@ -1 +1 @@
-%include "mips/op_iget.S" { "helper":"artGetShortInstanceFromCode" }
+%include "mips/op_iget.S" { "helper":"MterpIGetI16" }
diff --git a/runtime/interpreter/mterp/mips/op_iget_wide.S b/runtime/interpreter/mterp/mips/op_iget_wide.S
index cf5019e..858a889 100644
--- a/runtime/interpreter/mterp/mips/op_iget_wide.S
+++ b/runtime/interpreter/mterp/mips/op_iget_wide.S
@@ -10,7 +10,7 @@
GET_VREG(a1, a1) # a1 <- fp[B], the object pointer
lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- JAL(artGet64InstanceFromCode)
+ JAL(MterpIGetU64)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA4(a2) # a2<- A+
PREFETCH_INST(2) # load rINST
diff --git a/runtime/interpreter/mterp/mips/op_iput.S b/runtime/interpreter/mterp/mips/op_iput.S
index 9133d60..4dd4075 100644
--- a/runtime/interpreter/mterp/mips/op_iput.S
+++ b/runtime/interpreter/mterp/mips/op_iput.S
@@ -1,11 +1,11 @@
-%default { "handler":"artSet32InstanceFromMterp" }
+%default { "helper":"MterpIPutU32" }
/*
* General 32-bit instance field put.
*
* for: iput, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern $handler
+ .extern $helper
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref CCCC
GET_OPB(a1) # a1 <- B
@@ -14,7 +14,7 @@
GET_VREG(a2, a2) # a2 <- fp[A]
lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST(2) # load rINST
- JAL($handler)
+ JAL($helper)
bnez v0, MterpPossibleException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
diff --git a/runtime/interpreter/mterp/mips/op_iput_boolean.S b/runtime/interpreter/mterp/mips/op_iput_boolean.S
index da28c97..55ac4ce 100644
--- a/runtime/interpreter/mterp/mips/op_iput_boolean.S
+++ b/runtime/interpreter/mterp/mips/op_iput_boolean.S
@@ -1 +1 @@
-%include "mips/op_iput.S" { "handler":"artSet8InstanceFromMterp" }
+%include "mips/op_iput.S" { "helper":"MterpIPutU8" }
diff --git a/runtime/interpreter/mterp/mips/op_iput_byte.S b/runtime/interpreter/mterp/mips/op_iput_byte.S
index da28c97..61e489b 100644
--- a/runtime/interpreter/mterp/mips/op_iput_byte.S
+++ b/runtime/interpreter/mterp/mips/op_iput_byte.S
@@ -1 +1 @@
-%include "mips/op_iput.S" { "handler":"artSet8InstanceFromMterp" }
+%include "mips/op_iput.S" { "helper":"MterpIPutI8" }
diff --git a/runtime/interpreter/mterp/mips/op_iput_char.S b/runtime/interpreter/mterp/mips/op_iput_char.S
index 389b0bf..2caad1e 100644
--- a/runtime/interpreter/mterp/mips/op_iput_char.S
+++ b/runtime/interpreter/mterp/mips/op_iput_char.S
@@ -1 +1 @@
-%include "mips/op_iput.S" { "handler":"artSet16InstanceFromMterp" }
+%include "mips/op_iput.S" { "helper":"MterpIPutU16" }
diff --git a/runtime/interpreter/mterp/mips/op_iput_object.S b/runtime/interpreter/mterp/mips/op_iput_object.S
index cfa56ec..c96a4d4 100644
--- a/runtime/interpreter/mterp/mips/op_iput_object.S
+++ b/runtime/interpreter/mterp/mips/op_iput_object.S
@@ -9,7 +9,7 @@
move a1, rPC
move a2, rINST
move a3, rSELF
- JAL(MterpIputObject)
+ JAL(MterpIPutObj)
beqz v0, MterpException
FETCH_ADVANCE_INST(2) # advance rPC, load rINST
GET_INST_OPCODE(t0) # extract opcode from rINST
diff --git a/runtime/interpreter/mterp/mips/op_iput_short.S b/runtime/interpreter/mterp/mips/op_iput_short.S
index 389b0bf..414a15b 100644
--- a/runtime/interpreter/mterp/mips/op_iput_short.S
+++ b/runtime/interpreter/mterp/mips/op_iput_short.S
@@ -1 +1 @@
-%include "mips/op_iput.S" { "handler":"artSet16InstanceFromMterp" }
+%include "mips/op_iput.S" { "helper":"MterpIPutI16" }
diff --git a/runtime/interpreter/mterp/mips/op_iput_wide.S b/runtime/interpreter/mterp/mips/op_iput_wide.S
index bc3d758..dccb6b7 100644
--- a/runtime/interpreter/mterp/mips/op_iput_wide.S
+++ b/runtime/interpreter/mterp/mips/op_iput_wide.S
@@ -1,5 +1,5 @@
/* iput-wide vA, vB, field@CCCC */
- .extern artSet64InstanceFromMterp
+ .extern MterpIPutU64
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref CCCC
GET_OPB(a1) # a1 <- B
@@ -8,7 +8,7 @@
EAS2(a2, rFP, a2) # a2 <- &fp[A]
lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST(2) # load rINST
- JAL(artSet64InstanceFromMterp)
+ JAL(MterpIPutU64)
bnez v0, MterpPossibleException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
diff --git a/runtime/interpreter/mterp/mips/op_sget.S b/runtime/interpreter/mterp/mips/op_sget.S
index 635df8a..8750a17 100644
--- a/runtime/interpreter/mterp/mips/op_sget.S
+++ b/runtime/interpreter/mterp/mips/op_sget.S
@@ -1,4 +1,4 @@
-%default { "is_object":"0", "helper":"MterpGet32Static" }
+%default { "is_object":"0", "helper":"MterpSGetU32" }
/*
* General SGET handler.
*
diff --git a/runtime/interpreter/mterp/mips/op_sget_boolean.S b/runtime/interpreter/mterp/mips/op_sget_boolean.S
index 7829970..7a7012e 100644
--- a/runtime/interpreter/mterp/mips/op_sget_boolean.S
+++ b/runtime/interpreter/mterp/mips/op_sget_boolean.S
@@ -1 +1 @@
-%include "mips/op_sget.S" {"helper":"MterpGetBooleanStatic"}
+%include "mips/op_sget.S" {"helper":"MterpSGetU8"}
diff --git a/runtime/interpreter/mterp/mips/op_sget_byte.S b/runtime/interpreter/mterp/mips/op_sget_byte.S
index ee08342..a2f1dbf 100644
--- a/runtime/interpreter/mterp/mips/op_sget_byte.S
+++ b/runtime/interpreter/mterp/mips/op_sget_byte.S
@@ -1 +1 @@
-%include "mips/op_sget.S" {"helper":"MterpGetByteStatic"}
+%include "mips/op_sget.S" {"helper":"MterpSGetI8"}
diff --git a/runtime/interpreter/mterp/mips/op_sget_char.S b/runtime/interpreter/mterp/mips/op_sget_char.S
index d8b477a..07d4041 100644
--- a/runtime/interpreter/mterp/mips/op_sget_char.S
+++ b/runtime/interpreter/mterp/mips/op_sget_char.S
@@ -1 +1 @@
-%include "mips/op_sget.S" {"helper":"MterpGetCharStatic"}
+%include "mips/op_sget.S" {"helper":"MterpSGetU16"}
diff --git a/runtime/interpreter/mterp/mips/op_sget_object.S b/runtime/interpreter/mterp/mips/op_sget_object.S
index 2dc00c3..0a3c9ee 100644
--- a/runtime/interpreter/mterp/mips/op_sget_object.S
+++ b/runtime/interpreter/mterp/mips/op_sget_object.S
@@ -1 +1 @@
-%include "mips/op_sget.S" {"is_object":"1", "helper":"MterpGetObjStatic"}
+%include "mips/op_sget.S" {"is_object":"1", "helper":"MterpSGetObj"}
diff --git a/runtime/interpreter/mterp/mips/op_sget_short.S b/runtime/interpreter/mterp/mips/op_sget_short.S
index ab55d93..2960443 100644
--- a/runtime/interpreter/mterp/mips/op_sget_short.S
+++ b/runtime/interpreter/mterp/mips/op_sget_short.S
@@ -1 +1 @@
-%include "mips/op_sget.S" {"helper":"MterpGetShortStatic"}
+%include "mips/op_sget.S" {"helper":"MterpSGetI16"}
diff --git a/runtime/interpreter/mterp/mips/op_sget_wide.S b/runtime/interpreter/mterp/mips/op_sget_wide.S
index ec4295a..76f78cb 100644
--- a/runtime/interpreter/mterp/mips/op_sget_wide.S
+++ b/runtime/interpreter/mterp/mips/op_sget_wide.S
@@ -2,12 +2,12 @@
* 64-bit SGET handler.
*/
/* sget-wide vAA, field@BBBB */
- .extern MterpGet64Static
+ .extern MterpSGetU64
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref BBBB
lw a1, OFF_FP_METHOD(rFP) # a1 <- method
move a2, rSELF # a2 <- self
- JAL(MterpGet64Static)
+ JAL(MterpSGetU64)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
bnez a3, MterpException
GET_OPA(a1) # a1 <- AA
diff --git a/runtime/interpreter/mterp/mips/op_sput.S b/runtime/interpreter/mterp/mips/op_sput.S
index 37f8687..547de39 100644
--- a/runtime/interpreter/mterp/mips/op_sput.S
+++ b/runtime/interpreter/mterp/mips/op_sput.S
@@ -1,4 +1,4 @@
-%default { "helper":"MterpSet32Static"}
+%default { "helper":"MterpSPutU32"}
/*
* General SPUT handler.
*
diff --git a/runtime/interpreter/mterp/mips/op_sput_boolean.S b/runtime/interpreter/mterp/mips/op_sput_boolean.S
index 6426cd4..0137430 100644
--- a/runtime/interpreter/mterp/mips/op_sput_boolean.S
+++ b/runtime/interpreter/mterp/mips/op_sput_boolean.S
@@ -1 +1 @@
-%include "mips/op_sput.S" {"helper":"MterpSetBooleanStatic"}
+%include "mips/op_sput.S" {"helper":"MterpSPutU8"}
diff --git a/runtime/interpreter/mterp/mips/op_sput_byte.S b/runtime/interpreter/mterp/mips/op_sput_byte.S
index c68d18f..5ae4256 100644
--- a/runtime/interpreter/mterp/mips/op_sput_byte.S
+++ b/runtime/interpreter/mterp/mips/op_sput_byte.S
@@ -1 +1 @@
-%include "mips/op_sput.S" {"helper":"MterpSetByteStatic"}
+%include "mips/op_sput.S" {"helper":"MterpSPutI8"}
diff --git a/runtime/interpreter/mterp/mips/op_sput_char.S b/runtime/interpreter/mterp/mips/op_sput_char.S
index 9b8983e..83787a7 100644
--- a/runtime/interpreter/mterp/mips/op_sput_char.S
+++ b/runtime/interpreter/mterp/mips/op_sput_char.S
@@ -1 +1 @@
-%include "mips/op_sput.S" {"helper":"MterpSetCharStatic"}
+%include "mips/op_sput.S" {"helper":"MterpSPutU16"}
diff --git a/runtime/interpreter/mterp/mips/op_sput_object.S b/runtime/interpreter/mterp/mips/op_sput_object.S
index 4f9034e..55c88a6 100644
--- a/runtime/interpreter/mterp/mips/op_sput_object.S
+++ b/runtime/interpreter/mterp/mips/op_sput_object.S
@@ -9,7 +9,7 @@
move a1, rPC
move a2, rINST
move a3, rSELF
- JAL(MterpSputObject)
+ JAL(MterpSPutObj)
beqz v0, MterpException
FETCH_ADVANCE_INST(2) # advance rPC, load rINST
GET_INST_OPCODE(t0) # extract opcode from rINST
diff --git a/runtime/interpreter/mterp/mips/op_sput_short.S b/runtime/interpreter/mterp/mips/op_sput_short.S
index 5a57ed9..df99b44 100644
--- a/runtime/interpreter/mterp/mips/op_sput_short.S
+++ b/runtime/interpreter/mterp/mips/op_sput_short.S
@@ -1 +1 @@
-%include "mips/op_sput.S" {"helper":"MterpSetShortStatic"}
+%include "mips/op_sput.S" {"helper":"MterpSPutI16"}
diff --git a/runtime/interpreter/mterp/mips/op_sput_wide.S b/runtime/interpreter/mterp/mips/op_sput_wide.S
index c090007..cfaaaee 100644
--- a/runtime/interpreter/mterp/mips/op_sput_wide.S
+++ b/runtime/interpreter/mterp/mips/op_sput_wide.S
@@ -2,7 +2,7 @@
* 64-bit SPUT handler.
*/
/* sput-wide vAA, field@BBBB */
- .extern MterpSet64Static
+ .extern MterpSPutU64
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref CCCC
GET_OPA(a1) # a1 <- AA
@@ -10,7 +10,7 @@
lw a2, OFF_FP_METHOD(rFP) # a2 <- method
move a3, rSELF # a3 <- self
PREFETCH_INST(2) # load rINST
- JAL(MterpSet64Static)
+ JAL(MterpSPutU64)
bnez v0, MterpException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
diff --git a/runtime/interpreter/mterp/mips64/op_iget.S b/runtime/interpreter/mterp/mips64/op_iget.S
index ade4b31..a8ce94c 100644
--- a/runtime/interpreter/mterp/mips64/op_iget.S
+++ b/runtime/interpreter/mterp/mips64/op_iget.S
@@ -1,4 +1,4 @@
-%default { "is_object":"0", "helper":"artGet32InstanceFromCode"}
+%default { "is_object":"0", "helper":"MterpIGetU32"}
/*
* General instance field get.
*
diff --git a/runtime/interpreter/mterp/mips64/op_iget_boolean.S b/runtime/interpreter/mterp/mips64/op_iget_boolean.S
index cb2c8be..dc2a42a 100644
--- a/runtime/interpreter/mterp/mips64/op_iget_boolean.S
+++ b/runtime/interpreter/mterp/mips64/op_iget_boolean.S
@@ -1 +1 @@
-%include "mips64/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" }
+%include "mips64/op_iget.S" { "helper":"MterpIGetU8" }
diff --git a/runtime/interpreter/mterp/mips64/op_iget_byte.S b/runtime/interpreter/mterp/mips64/op_iget_byte.S
index 099d8d0..c5bf650 100644
--- a/runtime/interpreter/mterp/mips64/op_iget_byte.S
+++ b/runtime/interpreter/mterp/mips64/op_iget_byte.S
@@ -1 +1 @@
-%include "mips64/op_iget.S" { "helper":"artGetByteInstanceFromCode" }
+%include "mips64/op_iget.S" { "helper":"MterpIGetI8" }
diff --git a/runtime/interpreter/mterp/mips64/op_iget_char.S b/runtime/interpreter/mterp/mips64/op_iget_char.S
index 927b7af..3bf0c5a 100644
--- a/runtime/interpreter/mterp/mips64/op_iget_char.S
+++ b/runtime/interpreter/mterp/mips64/op_iget_char.S
@@ -1 +1 @@
-%include "mips64/op_iget.S" { "helper":"artGetCharInstanceFromCode" }
+%include "mips64/op_iget.S" { "helper":"MterpIGetU16" }
diff --git a/runtime/interpreter/mterp/mips64/op_iget_object.S b/runtime/interpreter/mterp/mips64/op_iget_object.S
index c658556..23fa187 100644
--- a/runtime/interpreter/mterp/mips64/op_iget_object.S
+++ b/runtime/interpreter/mterp/mips64/op_iget_object.S
@@ -1 +1 @@
-%include "mips64/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" }
+%include "mips64/op_iget.S" { "is_object":"1", "helper":"MterpIGetObj" }
diff --git a/runtime/interpreter/mterp/mips64/op_iget_short.S b/runtime/interpreter/mterp/mips64/op_iget_short.S
index 28b5093..a9927fc 100644
--- a/runtime/interpreter/mterp/mips64/op_iget_short.S
+++ b/runtime/interpreter/mterp/mips64/op_iget_short.S
@@ -1 +1 @@
-%include "mips64/op_iget.S" { "helper":"artGetShortInstanceFromCode" }
+%include "mips64/op_iget.S" { "helper":"MterpIGetI16" }
diff --git a/runtime/interpreter/mterp/mips64/op_iget_wide.S b/runtime/interpreter/mterp/mips64/op_iget_wide.S
index 85cf670..08bf544 100644
--- a/runtime/interpreter/mterp/mips64/op_iget_wide.S
+++ b/runtime/interpreter/mterp/mips64/op_iget_wide.S
@@ -3,14 +3,14 @@
*
* for: iget-wide
*/
- .extern artGet64InstanceFromCode
+ .extern MterpIGetU64
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer
ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- jal artGet64InstanceFromCode
+ jal MterpIGetU64
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
ext a2, rINST, 8, 4 # a2 <- A
PREFETCH_INST 2
diff --git a/runtime/interpreter/mterp/mips64/op_iput.S b/runtime/interpreter/mterp/mips64/op_iput.S
index a906a0f..9a789e6 100644
--- a/runtime/interpreter/mterp/mips64/op_iput.S
+++ b/runtime/interpreter/mterp/mips64/op_iput.S
@@ -1,4 +1,4 @@
-%default { "helper":"artSet32InstanceFromMterp" }
+%default { "helper":"MterpIPutU32" }
/*
* General 32-bit instance field put.
*
diff --git a/runtime/interpreter/mterp/mips64/op_iput_boolean.S b/runtime/interpreter/mterp/mips64/op_iput_boolean.S
index 3034fa5..8e1d083 100644
--- a/runtime/interpreter/mterp/mips64/op_iput_boolean.S
+++ b/runtime/interpreter/mterp/mips64/op_iput_boolean.S
@@ -1 +1 @@
-%include "mips64/op_iput.S" { "helper":"artSet8InstanceFromMterp" }
+%include "mips64/op_iput.S" { "helper":"MterpIPutU8" }
diff --git a/runtime/interpreter/mterp/mips64/op_iput_byte.S b/runtime/interpreter/mterp/mips64/op_iput_byte.S
index 3034fa5..ce3b614 100644
--- a/runtime/interpreter/mterp/mips64/op_iput_byte.S
+++ b/runtime/interpreter/mterp/mips64/op_iput_byte.S
@@ -1 +1 @@
-%include "mips64/op_iput.S" { "helper":"artSet8InstanceFromMterp" }
+%include "mips64/op_iput.S" { "helper":"MterpIPutI8" }
diff --git a/runtime/interpreter/mterp/mips64/op_iput_char.S b/runtime/interpreter/mterp/mips64/op_iput_char.S
index 4c2fa28..1d587fa 100644
--- a/runtime/interpreter/mterp/mips64/op_iput_char.S
+++ b/runtime/interpreter/mterp/mips64/op_iput_char.S
@@ -1 +1 @@
-%include "mips64/op_iput.S" { "helper":"artSet16InstanceFromMterp" }
+%include "mips64/op_iput.S" { "helper":"MterpIPutU16" }
diff --git a/runtime/interpreter/mterp/mips64/op_iput_object.S b/runtime/interpreter/mterp/mips64/op_iput_object.S
index 9a42f54..dd1938e 100644
--- a/runtime/interpreter/mterp/mips64/op_iput_object.S
+++ b/runtime/interpreter/mterp/mips64/op_iput_object.S
@@ -1,10 +1,10 @@
- .extern MterpIputObject
+ .extern MterpIPutObj
EXPORT_PC
daddu a0, rFP, OFF_FP_SHADOWFRAME
move a1, rPC
move a2, rINST
move a3, rSELF
- jal MterpIputObject
+ jal MterpIPutObj
beqzc v0, MterpException
FETCH_ADVANCE_INST 2 # advance rPC, load rINST
GET_INST_OPCODE v0 # extract opcode from rINST
diff --git a/runtime/interpreter/mterp/mips64/op_iput_short.S b/runtime/interpreter/mterp/mips64/op_iput_short.S
index 4c2fa28..dd68bbe 100644
--- a/runtime/interpreter/mterp/mips64/op_iput_short.S
+++ b/runtime/interpreter/mterp/mips64/op_iput_short.S
@@ -1 +1 @@
-%include "mips64/op_iput.S" { "helper":"artSet16InstanceFromMterp" }
+%include "mips64/op_iput.S" { "helper":"MterpIPutI16" }
diff --git a/runtime/interpreter/mterp/mips64/op_iput_wide.S b/runtime/interpreter/mterp/mips64/op_iput_wide.S
index 9b790f8..6272690 100644
--- a/runtime/interpreter/mterp/mips64/op_iput_wide.S
+++ b/runtime/interpreter/mterp/mips64/op_iput_wide.S
@@ -1,5 +1,5 @@
/* iput-wide vA, vB, field//CCCC */
- .extern artSet64InstanceFromMterp
+ .extern MterpIPutU64
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
@@ -8,7 +8,7 @@
dlsa a2, a2, rFP, 2 # a2 <- &fp[A]
ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST 2
- jal artSet64InstanceFromMterp
+ jal MterpIPutU64
bnez v0, MterpPossibleException # bail out
ADVANCE 2
GET_INST_OPCODE v0 # extract opcode from rINST
diff --git a/runtime/interpreter/mterp/mips64/op_sget.S b/runtime/interpreter/mterp/mips64/op_sget.S
index 71046db..b7b0382 100644
--- a/runtime/interpreter/mterp/mips64/op_sget.S
+++ b/runtime/interpreter/mterp/mips64/op_sget.S
@@ -1,4 +1,4 @@
-%default { "is_object":"0", "helper":"MterpGet32Static", "extend":"" }
+%default { "is_object":"0", "helper":"MterpSGetU32", "extend":"" }
/*
* General SGET handler wrapper.
*
diff --git a/runtime/interpreter/mterp/mips64/op_sget_boolean.S b/runtime/interpreter/mterp/mips64/op_sget_boolean.S
index ec1ce9e..fe2deb1 100644
--- a/runtime/interpreter/mterp/mips64/op_sget_boolean.S
+++ b/runtime/interpreter/mterp/mips64/op_sget_boolean.S
@@ -1 +1 @@
-%include "mips64/op_sget.S" {"helper":"MterpGetBooleanStatic", "extend":"and v0, v0, 0xff"}
+%include "mips64/op_sget.S" {"helper":"MterpSGetU8", "extend":"and v0, v0, 0xff"}
diff --git a/runtime/interpreter/mterp/mips64/op_sget_byte.S b/runtime/interpreter/mterp/mips64/op_sget_byte.S
index 6a802f6..a7e2bef 100644
--- a/runtime/interpreter/mterp/mips64/op_sget_byte.S
+++ b/runtime/interpreter/mterp/mips64/op_sget_byte.S
@@ -1 +1 @@
-%include "mips64/op_sget.S" {"helper":"MterpGetByteStatic", "extend":"seb v0, v0"}
+%include "mips64/op_sget.S" {"helper":"MterpSGetI8", "extend":"seb v0, v0"}
diff --git a/runtime/interpreter/mterp/mips64/op_sget_char.S b/runtime/interpreter/mterp/mips64/op_sget_char.S
index 483d085..ed86f32 100644
--- a/runtime/interpreter/mterp/mips64/op_sget_char.S
+++ b/runtime/interpreter/mterp/mips64/op_sget_char.S
@@ -1 +1 @@
-%include "mips64/op_sget.S" {"helper":"MterpGetCharStatic", "extend":"and v0, v0, 0xffff"}
+%include "mips64/op_sget.S" {"helper":"MterpSGetU16", "extend":"and v0, v0, 0xffff"}
diff --git a/runtime/interpreter/mterp/mips64/op_sget_object.S b/runtime/interpreter/mterp/mips64/op_sget_object.S
index 2250696..3b260e6 100644
--- a/runtime/interpreter/mterp/mips64/op_sget_object.S
+++ b/runtime/interpreter/mterp/mips64/op_sget_object.S
@@ -1 +1 @@
-%include "mips64/op_sget.S" {"is_object":"1", "helper":"MterpGetObjStatic"}
+%include "mips64/op_sget.S" {"is_object":"1", "helper":"MterpSGetObj"}
diff --git a/runtime/interpreter/mterp/mips64/op_sget_short.S b/runtime/interpreter/mterp/mips64/op_sget_short.S
index b257bbb..f708a20 100644
--- a/runtime/interpreter/mterp/mips64/op_sget_short.S
+++ b/runtime/interpreter/mterp/mips64/op_sget_short.S
@@ -1 +1 @@
-%include "mips64/op_sget.S" {"helper":"MterpGetShortStatic", "extend":"seh v0, v0"}
+%include "mips64/op_sget.S" {"helper":"MterpSGetI16", "extend":"seh v0, v0"}
diff --git a/runtime/interpreter/mterp/mips64/op_sget_wide.S b/runtime/interpreter/mterp/mips64/op_sget_wide.S
index ace64f8..7c31252 100644
--- a/runtime/interpreter/mterp/mips64/op_sget_wide.S
+++ b/runtime/interpreter/mterp/mips64/op_sget_wide.S
@@ -3,12 +3,12 @@
*
*/
/* sget-wide vAA, field//BBBB */
- .extern MterpGet64Static
+ .extern MterpSGetU64
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
ld a1, OFF_FP_METHOD(rFP)
move a2, rSELF
- jal MterpGet64Static
+ jal MterpSGetU64
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
srl a4, rINST, 8 # a4 <- AA
bnez a3, MterpException # bail out
diff --git a/runtime/interpreter/mterp/mips64/op_sput.S b/runtime/interpreter/mterp/mips64/op_sput.S
index 466f333..28b8c3e 100644
--- a/runtime/interpreter/mterp/mips64/op_sput.S
+++ b/runtime/interpreter/mterp/mips64/op_sput.S
@@ -1,4 +1,4 @@
-%default { "helper":"MterpSet32Static" }
+%default { "helper":"MterpSPutU32" }
/*
* General SPUT handler wrapper.
*
diff --git a/runtime/interpreter/mterp/mips64/op_sput_boolean.S b/runtime/interpreter/mterp/mips64/op_sput_boolean.S
index eba58f7..2e769d5 100644
--- a/runtime/interpreter/mterp/mips64/op_sput_boolean.S
+++ b/runtime/interpreter/mterp/mips64/op_sput_boolean.S
@@ -1 +1 @@
-%include "mips64/op_sput.S" {"helper":"MterpSetBooleanStatic"}
+%include "mips64/op_sput.S" {"helper":"MterpSPutU8"}
diff --git a/runtime/interpreter/mterp/mips64/op_sput_byte.S b/runtime/interpreter/mterp/mips64/op_sput_byte.S
index 80a26c0..0b04b59 100644
--- a/runtime/interpreter/mterp/mips64/op_sput_byte.S
+++ b/runtime/interpreter/mterp/mips64/op_sput_byte.S
@@ -1 +1 @@
-%include "mips64/op_sput.S" {"helper":"MterpSetByteStatic"}
+%include "mips64/op_sput.S" {"helper":"MterpSPutI8"}
diff --git a/runtime/interpreter/mterp/mips64/op_sput_char.S b/runtime/interpreter/mterp/mips64/op_sput_char.S
index c0d5bf3..4a80375 100644
--- a/runtime/interpreter/mterp/mips64/op_sput_char.S
+++ b/runtime/interpreter/mterp/mips64/op_sput_char.S
@@ -1 +1 @@
-%include "mips64/op_sput.S" {"helper":"MterpSetCharStatic"}
+%include "mips64/op_sput.S" {"helper":"MterpSPutU16"}
diff --git a/runtime/interpreter/mterp/mips64/op_sput_object.S b/runtime/interpreter/mterp/mips64/op_sput_object.S
index ef4c685..ff43967 100644
--- a/runtime/interpreter/mterp/mips64/op_sput_object.S
+++ b/runtime/interpreter/mterp/mips64/op_sput_object.S
@@ -1,10 +1,10 @@
- .extern MterpSputObject
+ .extern MterpSPutObj
EXPORT_PC
daddu a0, rFP, OFF_FP_SHADOWFRAME
move a1, rPC
move a2, rINST
move a3, rSELF
- jal MterpSputObject
+ jal MterpSPutObj
beqzc v0, MterpException
FETCH_ADVANCE_INST 2 # advance rPC, load rINST
GET_INST_OPCODE v0 # extract opcode from rINST
diff --git a/runtime/interpreter/mterp/mips64/op_sput_short.S b/runtime/interpreter/mterp/mips64/op_sput_short.S
index b001832..c00043b 100644
--- a/runtime/interpreter/mterp/mips64/op_sput_short.S
+++ b/runtime/interpreter/mterp/mips64/op_sput_short.S
@@ -1 +1 @@
-%include "mips64/op_sput.S" {"helper":"MterpSetShortStatic"}
+%include "mips64/op_sput.S" {"helper":"MterpSPutI16"}
diff --git a/runtime/interpreter/mterp/mips64/op_sput_wide.S b/runtime/interpreter/mterp/mips64/op_sput_wide.S
index aa3d5b4..bfb6983 100644
--- a/runtime/interpreter/mterp/mips64/op_sput_wide.S
+++ b/runtime/interpreter/mterp/mips64/op_sput_wide.S
@@ -3,7 +3,7 @@
*
*/
/* sput-wide vAA, field//BBBB */
- .extern MterpSet64Static
+ .extern MterpSPutU64
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
srl a1, rINST, 8 # a2 <- AA
@@ -11,7 +11,7 @@
ld a2, OFF_FP_METHOD(rFP)
move a3, rSELF
PREFETCH_INST 2 # Get next inst, but don't advance rPC
- jal MterpSet64Static
+ jal MterpSPutU64
bnezc v0, MterpException # 0 on success, -1 on failure
ADVANCE 2 # Past exception point - now advance rPC
GET_INST_OPCODE v0 # extract opcode from rINST
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index e4cc6d3..7b37c9a 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -490,7 +490,7 @@
return true;
}
-extern "C" size_t MterpSputObject(ShadowFrame* shadow_frame, uint16_t* dex_pc_ptr,
+extern "C" size_t MterpSPutObj(ShadowFrame* shadow_frame, uint16_t* dex_pc_ptr,
uint32_t inst_data, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
const Instruction* inst = Instruction::At(dex_pc_ptr);
@@ -498,7 +498,7 @@
(self, *shadow_frame, inst, inst_data);
}
-extern "C" size_t MterpIputObject(ShadowFrame* shadow_frame,
+extern "C" size_t MterpIPutObj(ShadowFrame* shadow_frame,
uint16_t* dex_pc_ptr,
uint32_t inst_data,
Thread* self)
@@ -681,52 +681,141 @@
return MterpShouldSwitchInterpreters();
}
-extern "C" ssize_t artSet8InstanceFromMterp(uint32_t field_idx,
- mirror::Object* obj,
- uint8_t new_value,
- ArtMethod* referrer)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int8_t));
- if (LIKELY(field != nullptr && obj != nullptr)) {
- Primitive::Type type = field->GetTypeAsPrimitiveType();
- if (type == Primitive::kPrimBoolean) {
- field->SetBoolean<false>(obj, new_value);
- } else {
- DCHECK_EQ(Primitive::kPrimByte, type);
- field->SetByte<false>(obj, new_value);
+template<typename PrimType, typename RetType, typename Getter, FindFieldType kType>
+NO_INLINE RetType artGetInstanceFromMterp(uint32_t field_idx,
+ mirror::Object* obj,
+ ArtMethod* referrer,
+ Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ StackHandleScope<1> hs(self);
+ HandleWrapper<mirror::Object> h(hs.NewHandleWrapper(&obj)); // GC might move the object.
+ ArtField* field = FindFieldFromCode<kType, /* access_checks */ false>(
+ field_idx, referrer, self, sizeof(PrimType));
+ if (UNLIKELY(field == nullptr)) {
+ return 0; // Will throw exception by checking with Thread::Current.
+ }
+ if (UNLIKELY(h == nullptr)) {
+ ThrowNullPointerExceptionForFieldAccess(field, /*is_read*/ true);
+ return 0; // Will throw exception by checking with Thread::Current.
+ }
+ return Getter::Get(obj, field);
+}
+
+template<typename PrimType, typename RetType, typename Getter>
+ALWAYS_INLINE RetType artGetInstanceFromMterpFast(uint32_t field_idx,
+ mirror::Object* obj,
+ ArtMethod* referrer,
+ Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ constexpr bool kIsObject = std::is_same<RetType, mirror::Object*>::value;
+ constexpr FindFieldType kType = kIsObject ? InstanceObjectRead : InstancePrimitiveRead;
+
+ // This effectively inlines the fast path from ArtMethod::GetDexCache.
+ // It avoids non-inlined call which in turn allows elimination of the prologue and epilogue.
+ if (LIKELY(!referrer->IsObsolete())) {
+ // Avoid read barriers, since we need only the pointer to the native (non-movable)
+ // DexCache field array which we can get even through from-space objects.
+ ObjPtr<mirror::Class> klass = referrer->GetDeclaringClass<kWithoutReadBarrier>();
+ mirror::DexCache* dex_cache = klass->GetDexCache<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ // Try to find the desired field in DexCache.
+ ArtField* field = dex_cache->GetResolvedField(field_idx, kRuntimePointerSize);
+ if (LIKELY(field != nullptr & obj != nullptr)) {
+ if (kIsDebugBuild) {
+ // Compare the fast path and slow path.
+ StackHandleScope<1> hs(self);
+ HandleWrapper<mirror::Object> h(hs.NewHandleWrapper(&obj)); // GC might move the object.
+ DCHECK_EQ(field, (FindFieldFromCode<kType, /* access_checks */ false>(
+ field_idx, referrer, self, sizeof(PrimType))));
+ }
+ return Getter::Get(obj, field);
}
+ }
+ // Slow path. Last and with identical arguments so that it becomes single instruction tail call.
+ return artGetInstanceFromMterp<PrimType, RetType, Getter, kType>(field_idx, obj, referrer, self);
+}
+
+#define ART_GET_FIELD_FROM_MTERP(Suffix, Kind, PrimType, RetType, Ptr) \
+extern "C" RetType MterpIGet ## Suffix(uint32_t field_idx, \
+ mirror::Object* obj, \
+ ArtMethod* referrer, \
+ Thread* self) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ struct Getter { /* Specialize the field load depending on the field type */ \
+ static RetType Get(mirror::Object* o, ArtField* f) REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return f->Get##Kind(o)Ptr; \
+ } \
+ }; \
+ return artGetInstanceFromMterpFast<PrimType, RetType, Getter>(field_idx, obj, referrer, self); \
+} \
+
+ART_GET_FIELD_FROM_MTERP(I8, Byte, int8_t, ssize_t, )
+ART_GET_FIELD_FROM_MTERP(U8, Boolean, uint8_t, size_t, )
+ART_GET_FIELD_FROM_MTERP(I16, Short, int16_t, ssize_t, )
+ART_GET_FIELD_FROM_MTERP(U16, Char, uint16_t, size_t, )
+ART_GET_FIELD_FROM_MTERP(U32, 32, uint32_t, size_t, )
+ART_GET_FIELD_FROM_MTERP(U64, 64, uint64_t, uint64_t, )
+ART_GET_FIELD_FROM_MTERP(Obj, Obj, mirror::HeapReference<mirror::Object>, mirror::Object*, .Ptr())
+
+#undef ART_GET_FIELD_FROM_MTERP
+
+extern "C" ssize_t MterpIPutU8(uint32_t field_idx,
+ mirror::Object* obj,
+ uint8_t new_value,
+ ArtMethod* referrer)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtField* field = referrer->GetDexCache()->GetResolvedField(field_idx, kRuntimePointerSize);
+ if (LIKELY(field != nullptr && obj != nullptr)) {
+ field->SetBoolean<false>(obj, new_value);
return 0; // success
}
return -1; // failure
}
-extern "C" ssize_t artSet16InstanceFromMterp(uint32_t field_idx,
- mirror::Object* obj,
- uint16_t new_value,
- ArtMethod* referrer)
+extern "C" ssize_t MterpIPutI8(uint32_t field_idx,
+ mirror::Object* obj,
+ uint8_t new_value,
+ ArtMethod* referrer)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
- sizeof(int16_t));
+ ArtField* field = referrer->GetDexCache()->GetResolvedField(field_idx, kRuntimePointerSize);
if (LIKELY(field != nullptr && obj != nullptr)) {
- Primitive::Type type = field->GetTypeAsPrimitiveType();
- if (type == Primitive::kPrimChar) {
- field->SetChar<false>(obj, new_value);
- } else {
- DCHECK_EQ(Primitive::kPrimShort, type);
- field->SetShort<false>(obj, new_value);
- }
+ field->SetByte<false>(obj, new_value);
return 0; // success
}
return -1; // failure
}
-extern "C" ssize_t artSet32InstanceFromMterp(uint32_t field_idx,
- mirror::Object* obj,
- uint32_t new_value,
- ArtMethod* referrer)
+extern "C" ssize_t MterpIPutU16(uint32_t field_idx,
+ mirror::Object* obj,
+ uint16_t new_value,
+ ArtMethod* referrer)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
- sizeof(int32_t));
+ ArtField* field = referrer->GetDexCache()->GetResolvedField(field_idx, kRuntimePointerSize);
+ if (LIKELY(field != nullptr && obj != nullptr)) {
+ field->SetChar<false>(obj, new_value);
+ return 0; // success
+ }
+ return -1; // failure
+}
+
+extern "C" ssize_t MterpIPutI16(uint32_t field_idx,
+ mirror::Object* obj,
+ uint16_t new_value,
+ ArtMethod* referrer)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtField* field = referrer->GetDexCache()->GetResolvedField(field_idx, kRuntimePointerSize);
+ if (LIKELY(field != nullptr && obj != nullptr)) {
+ field->SetShort<false>(obj, new_value);
+ return 0; // success
+ }
+ return -1; // failure
+}
+
+extern "C" ssize_t MterpIPutU32(uint32_t field_idx,
+ mirror::Object* obj,
+ uint32_t new_value,
+ ArtMethod* referrer)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtField* field = referrer->GetDexCache()->GetResolvedField(field_idx, kRuntimePointerSize);
if (LIKELY(field != nullptr && obj != nullptr)) {
field->Set32<false>(obj, new_value);
return 0; // success
@@ -734,13 +823,12 @@
return -1; // failure
}
-extern "C" ssize_t artSet64InstanceFromMterp(uint32_t field_idx,
- mirror::Object* obj,
- uint64_t* new_value,
- ArtMethod* referrer)
+extern "C" ssize_t MterpIPutU64(uint32_t field_idx,
+ mirror::Object* obj,
+ uint64_t* new_value,
+ ArtMethod* referrer)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
- sizeof(int64_t));
+ ArtField* field = referrer->GetDexCache()->GetResolvedField(field_idx, kRuntimePointerSize);
if (LIKELY(field != nullptr && obj != nullptr)) {
field->Set64<false>(obj, *new_value);
return 0; // success
@@ -753,8 +841,7 @@
mirror::Object* new_value,
ArtMethod* referrer)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite,
- sizeof(mirror::HeapReference<mirror::Object>));
+ ArtField* field = referrer->GetDexCache()->GetResolvedField(field_idx, kRuntimePointerSize);
if (LIKELY(field != nullptr && obj != nullptr)) {
field->SetObj<false>(obj, new_value);
return 0; // success
@@ -781,9 +868,9 @@
return res;
}
-extern "C" int32_t MterpGetBooleanStatic(uint32_t field_idx,
- ArtMethod* referrer,
- Thread* self)
+extern "C" int32_t MterpSGetU8(uint32_t field_idx,
+ ArtMethod* referrer,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpGetStatic<uint8_t, Primitive::kPrimBoolean>(field_idx,
referrer,
@@ -791,9 +878,9 @@
&ArtField::GetBoolean);
}
-extern "C" int32_t MterpGetByteStatic(uint32_t field_idx,
- ArtMethod* referrer,
- Thread* self)
+extern "C" int32_t MterpSGetI8(uint32_t field_idx,
+ ArtMethod* referrer,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpGetStatic<int8_t, Primitive::kPrimByte>(field_idx,
referrer,
@@ -801,9 +888,9 @@
&ArtField::GetByte);
}
-extern "C" uint32_t MterpGetCharStatic(uint32_t field_idx,
- ArtMethod* referrer,
- Thread* self)
+extern "C" uint32_t MterpSGetU16(uint32_t field_idx,
+ ArtMethod* referrer,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpGetStatic<uint16_t, Primitive::kPrimChar>(field_idx,
referrer,
@@ -811,9 +898,9 @@
&ArtField::GetChar);
}
-extern "C" int32_t MterpGetShortStatic(uint32_t field_idx,
- ArtMethod* referrer,
- Thread* self)
+extern "C" int32_t MterpSGetI16(uint32_t field_idx,
+ ArtMethod* referrer,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpGetStatic<int16_t, Primitive::kPrimShort>(field_idx,
referrer,
@@ -821,9 +908,9 @@
&ArtField::GetShort);
}
-extern "C" mirror::Object* MterpGetObjStatic(uint32_t field_idx,
- ArtMethod* referrer,
- Thread* self)
+extern "C" mirror::Object* MterpSGetObj(uint32_t field_idx,
+ ArtMethod* referrer,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpGetStatic<ObjPtr<mirror::Object>, Primitive::kPrimNot>(field_idx,
referrer,
@@ -831,9 +918,9 @@
&ArtField::GetObject).Ptr();
}
-extern "C" int32_t MterpGet32Static(uint32_t field_idx,
- ArtMethod* referrer,
- Thread* self)
+extern "C" int32_t MterpSGetU32(uint32_t field_idx,
+ ArtMethod* referrer,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpGetStatic<int32_t, Primitive::kPrimInt>(field_idx,
referrer,
@@ -841,7 +928,7 @@
&ArtField::GetInt);
}
-extern "C" int64_t MterpGet64Static(uint32_t field_idx, ArtMethod* referrer, Thread* self)
+extern "C" int64_t MterpSGetU64(uint32_t field_idx, ArtMethod* referrer, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpGetStatic<int64_t, Primitive::kPrimLong>(field_idx,
referrer,
@@ -869,10 +956,10 @@
return res;
}
-extern "C" int MterpSetBooleanStatic(uint32_t field_idx,
- uint8_t new_value,
- ArtMethod* referrer,
- Thread* self)
+extern "C" int MterpSPutU8(uint32_t field_idx,
+ uint8_t new_value,
+ ArtMethod* referrer,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpSetStatic<uint8_t, Primitive::kPrimBoolean>(field_idx,
new_value,
@@ -881,10 +968,10 @@
&ArtField::SetBoolean<false>);
}
-extern "C" int MterpSetByteStatic(uint32_t field_idx,
- int8_t new_value,
- ArtMethod* referrer,
- Thread* self)
+extern "C" int MterpSPutI8(uint32_t field_idx,
+ int8_t new_value,
+ ArtMethod* referrer,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpSetStatic<int8_t, Primitive::kPrimByte>(field_idx,
new_value,
@@ -893,10 +980,10 @@
&ArtField::SetByte<false>);
}
-extern "C" int MterpSetCharStatic(uint32_t field_idx,
- uint16_t new_value,
- ArtMethod* referrer,
- Thread* self)
+extern "C" int MterpSPutU16(uint32_t field_idx,
+ uint16_t new_value,
+ ArtMethod* referrer,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpSetStatic<uint16_t, Primitive::kPrimChar>(field_idx,
new_value,
@@ -905,10 +992,10 @@
&ArtField::SetChar<false>);
}
-extern "C" int MterpSetShortStatic(uint32_t field_idx,
- int16_t new_value,
- ArtMethod* referrer,
- Thread* self)
+extern "C" int MterpSPutI16(uint32_t field_idx,
+ int16_t new_value,
+ ArtMethod* referrer,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpSetStatic<int16_t, Primitive::kPrimShort>(field_idx,
new_value,
@@ -917,10 +1004,10 @@
&ArtField::SetShort<false>);
}
-extern "C" int MterpSet32Static(uint32_t field_idx,
- int32_t new_value,
- ArtMethod* referrer,
- Thread* self)
+extern "C" int MterpSPutU32(uint32_t field_idx,
+ int32_t new_value,
+ ArtMethod* referrer,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpSetStatic<int32_t, Primitive::kPrimInt>(field_idx,
new_value,
@@ -929,10 +1016,10 @@
&ArtField::SetInt<false>);
}
-extern "C" int MterpSet64Static(uint32_t field_idx,
- int64_t* new_value,
- ArtMethod* referrer,
- Thread* self)
+extern "C" int MterpSPutU64(uint32_t field_idx,
+ int64_t* new_value,
+ ArtMethod* referrer,
+ Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return MterpSetStatic<int64_t, Primitive::kPrimLong>(field_idx,
*new_value,
diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S
index 73b957f..b73067f 100644
--- a/runtime/interpreter/mterp/out/mterp_arm.S
+++ b/runtime/interpreter/mterp/out/mterp_arm.S
@@ -2255,7 +2255,7 @@
GET_VREG r1, r1 @ r1<- fp[B], the object pointer
ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- referrer
mov r3, rSELF @ r3<- self
- bl artGet32InstanceFromCode
+ bl MterpIGetU32
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
ubfx r2, rINST, #8, #4 @ r2<- A
PREFETCH_INST 2
@@ -2285,7 +2285,7 @@
GET_VREG r1, r1 @ r1<- fp[B], the object pointer
ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- referrer
mov r3, rSELF @ r3<- self
- bl artGet64InstanceFromCode
+ bl MterpIGetU64
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
ubfx r2, rINST, #8, #4 @ r2<- A
PREFETCH_INST 2
@@ -2314,7 +2314,7 @@
GET_VREG r1, r1 @ r1<- fp[B], the object pointer
ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- referrer
mov r3, rSELF @ r3<- self
- bl artGetObjInstanceFromCode
+ bl MterpIGetObj
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
ubfx r2, rINST, #8, #4 @ r2<- A
PREFETCH_INST 2
@@ -2346,7 +2346,7 @@
GET_VREG r1, r1 @ r1<- fp[B], the object pointer
ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- referrer
mov r3, rSELF @ r3<- self
- bl artGetBooleanInstanceFromCode
+ bl MterpIGetU8
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
ubfx r2, rINST, #8, #4 @ r2<- A
PREFETCH_INST 2
@@ -2378,7 +2378,7 @@
GET_VREG r1, r1 @ r1<- fp[B], the object pointer
ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- referrer
mov r3, rSELF @ r3<- self
- bl artGetByteInstanceFromCode
+ bl MterpIGetI8
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
ubfx r2, rINST, #8, #4 @ r2<- A
PREFETCH_INST 2
@@ -2410,7 +2410,7 @@
GET_VREG r1, r1 @ r1<- fp[B], the object pointer
ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- referrer
mov r3, rSELF @ r3<- self
- bl artGetCharInstanceFromCode
+ bl MterpIGetU16
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
ubfx r2, rINST, #8, #4 @ r2<- A
PREFETCH_INST 2
@@ -2442,7 +2442,7 @@
GET_VREG r1, r1 @ r1<- fp[B], the object pointer
ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- referrer
mov r3, rSELF @ r3<- self
- bl artGetShortInstanceFromCode
+ bl MterpIGetI16
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
ubfx r2, rINST, #8, #4 @ r2<- A
PREFETCH_INST 2
@@ -2468,7 +2468,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet32InstanceFromMterp
+ .extern MterpIPutU32
EXPORT_PC
FETCH r0, 1 @ r0<- field ref CCCC
mov r1, rINST, lsr #12 @ r1<- B
@@ -2477,7 +2477,7 @@
GET_VREG r2, r2 @ r2<- fp[A]
ldr r3, [rFP, #OFF_FP_METHOD] @ r3<- referrer
PREFETCH_INST 2
- bl artSet32InstanceFromMterp
+ bl MterpIPutU32
cmp r0, #0
bne MterpPossibleException
ADVANCE 2 @ advance rPC
@@ -2489,7 +2489,7 @@
.L_op_iput_wide: /* 0x5a */
/* File: arm/op_iput_wide.S */
/* iput-wide vA, vB, field@CCCC */
- .extern artSet64InstanceFromMterp
+ .extern MterpIPutU64
EXPORT_PC
FETCH r0, 1 @ r0<- field ref CCCC
mov r1, rINST, lsr #12 @ r1<- B
@@ -2498,7 +2498,7 @@
VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[A]
ldr r3, [rFP, #OFF_FP_METHOD] @ r3<- referrer
PREFETCH_INST 2
- bl artSet64InstanceFromMterp
+ bl MterpIPutU64
cmp r0, #0
bne MterpPossibleException
ADVANCE 2 @ advance rPC
@@ -2514,7 +2514,7 @@
mov r1, rPC
mov r2, rINST
mov r3, rSELF
- bl MterpIputObject
+ bl MterpIPutObj
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
@@ -2532,7 +2532,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet8InstanceFromMterp
+ .extern MterpIPutU8
EXPORT_PC
FETCH r0, 1 @ r0<- field ref CCCC
mov r1, rINST, lsr #12 @ r1<- B
@@ -2541,7 +2541,7 @@
GET_VREG r2, r2 @ r2<- fp[A]
ldr r3, [rFP, #OFF_FP_METHOD] @ r3<- referrer
PREFETCH_INST 2
- bl artSet8InstanceFromMterp
+ bl MterpIPutU8
cmp r0, #0
bne MterpPossibleException
ADVANCE 2 @ advance rPC
@@ -2560,7 +2560,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet8InstanceFromMterp
+ .extern MterpIPutI8
EXPORT_PC
FETCH r0, 1 @ r0<- field ref CCCC
mov r1, rINST, lsr #12 @ r1<- B
@@ -2569,7 +2569,7 @@
GET_VREG r2, r2 @ r2<- fp[A]
ldr r3, [rFP, #OFF_FP_METHOD] @ r3<- referrer
PREFETCH_INST 2
- bl artSet8InstanceFromMterp
+ bl MterpIPutI8
cmp r0, #0
bne MterpPossibleException
ADVANCE 2 @ advance rPC
@@ -2588,7 +2588,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet16InstanceFromMterp
+ .extern MterpIPutU16
EXPORT_PC
FETCH r0, 1 @ r0<- field ref CCCC
mov r1, rINST, lsr #12 @ r1<- B
@@ -2597,7 +2597,7 @@
GET_VREG r2, r2 @ r2<- fp[A]
ldr r3, [rFP, #OFF_FP_METHOD] @ r3<- referrer
PREFETCH_INST 2
- bl artSet16InstanceFromMterp
+ bl MterpIPutU16
cmp r0, #0
bne MterpPossibleException
ADVANCE 2 @ advance rPC
@@ -2616,7 +2616,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet16InstanceFromMterp
+ .extern MterpIPutI16
EXPORT_PC
FETCH r0, 1 @ r0<- field ref CCCC
mov r1, rINST, lsr #12 @ r1<- B
@@ -2625,7 +2625,7 @@
GET_VREG r2, r2 @ r2<- fp[A]
ldr r3, [rFP, #OFF_FP_METHOD] @ r3<- referrer
PREFETCH_INST 2
- bl artSet16InstanceFromMterp
+ bl MterpIPutI16
cmp r0, #0
bne MterpPossibleException
ADVANCE 2 @ advance rPC
@@ -2644,12 +2644,12 @@
*/
/* op vAA, field@BBBB */
- .extern MterpGet32Static
+ .extern MterpSGetU32
EXPORT_PC
FETCH r0, 1 @ r0<- field ref BBBB
ldr r1, [rFP, #OFF_FP_METHOD]
mov r2, rSELF
- bl MterpGet32Static
+ bl MterpSGetU32
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
mov r2, rINST, lsr #8 @ r2<- AA
PREFETCH_INST 2
@@ -2674,12 +2674,12 @@
*/
/* sget-wide vAA, field@BBBB */
- .extern MterpGet64Static
+ .extern MterpSGetU64
EXPORT_PC
FETCH r0, 1 @ r0<- field ref BBBB
ldr r1, [rFP, #OFF_FP_METHOD]
mov r2, rSELF
- bl MterpGet64Static
+ bl MterpSGetU64
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
mov r9, rINST, lsr #8 @ r9<- AA
VREG_INDEX_TO_ADDR lr, r9 @ r9<- &fp[AA]
@@ -2703,12 +2703,12 @@
*/
/* op vAA, field@BBBB */
- .extern MterpGetObjStatic
+ .extern MterpSGetObj
EXPORT_PC
FETCH r0, 1 @ r0<- field ref BBBB
ldr r1, [rFP, #OFF_FP_METHOD]
mov r2, rSELF
- bl MterpGetObjStatic
+ bl MterpSGetObj
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
mov r2, rINST, lsr #8 @ r2<- AA
PREFETCH_INST 2
@@ -2736,12 +2736,12 @@
*/
/* op vAA, field@BBBB */
- .extern MterpGetBooleanStatic
+ .extern MterpSGetU8
EXPORT_PC
FETCH r0, 1 @ r0<- field ref BBBB
ldr r1, [rFP, #OFF_FP_METHOD]
mov r2, rSELF
- bl MterpGetBooleanStatic
+ bl MterpSGetU8
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
mov r2, rINST, lsr #8 @ r2<- AA
PREFETCH_INST 2
@@ -2769,12 +2769,12 @@
*/
/* op vAA, field@BBBB */
- .extern MterpGetByteStatic
+ .extern MterpSGetI8
EXPORT_PC
FETCH r0, 1 @ r0<- field ref BBBB
ldr r1, [rFP, #OFF_FP_METHOD]
mov r2, rSELF
- bl MterpGetByteStatic
+ bl MterpSGetI8
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
mov r2, rINST, lsr #8 @ r2<- AA
PREFETCH_INST 2
@@ -2802,12 +2802,12 @@
*/
/* op vAA, field@BBBB */
- .extern MterpGetCharStatic
+ .extern MterpSGetU16
EXPORT_PC
FETCH r0, 1 @ r0<- field ref BBBB
ldr r1, [rFP, #OFF_FP_METHOD]
mov r2, rSELF
- bl MterpGetCharStatic
+ bl MterpSGetU16
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
mov r2, rINST, lsr #8 @ r2<- AA
PREFETCH_INST 2
@@ -2835,12 +2835,12 @@
*/
/* op vAA, field@BBBB */
- .extern MterpGetShortStatic
+ .extern MterpSGetI16
EXPORT_PC
FETCH r0, 1 @ r0<- field ref BBBB
ldr r1, [rFP, #OFF_FP_METHOD]
mov r2, rSELF
- bl MterpGetShortStatic
+ bl MterpSGetI16
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
mov r2, rINST, lsr #8 @ r2<- AA
PREFETCH_INST 2
@@ -2873,7 +2873,7 @@
ldr r2, [rFP, #OFF_FP_METHOD]
mov r3, rSELF
PREFETCH_INST 2 @ Get next inst, but don't advance rPC
- bl MterpSet32Static
+ bl MterpSPutU32
cmp r0, #0 @ 0 on success, -1 on failure
bne MterpException
ADVANCE 2 @ Past exception point - now advance rPC
@@ -2889,7 +2889,7 @@
*
*/
/* sput-wide vAA, field@BBBB */
- .extern MterpSet64Static
+ .extern MterpSPutU64
EXPORT_PC
FETCH r0, 1 @ r0<- field ref BBBB
mov r1, rINST, lsr #8 @ r1<- AA
@@ -2897,7 +2897,7 @@
ldr r2, [rFP, #OFF_FP_METHOD]
mov r3, rSELF
PREFETCH_INST 2 @ Get next inst, but don't advance rPC
- bl MterpSet64Static
+ bl MterpSPutU64
cmp r0, #0 @ 0 on success, -1 on failure
bne MterpException
ADVANCE 2 @ Past exception point - now advance rPC
@@ -2913,7 +2913,7 @@
mov r1, rPC
mov r2, rINST
mov r3, rSELF
- bl MterpSputObject
+ bl MterpSPutObj
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
@@ -2938,7 +2938,7 @@
ldr r2, [rFP, #OFF_FP_METHOD]
mov r3, rSELF
PREFETCH_INST 2 @ Get next inst, but don't advance rPC
- bl MterpSetBooleanStatic
+ bl MterpSPutU8
cmp r0, #0 @ 0 on success, -1 on failure
bne MterpException
ADVANCE 2 @ Past exception point - now advance rPC
@@ -2964,7 +2964,7 @@
ldr r2, [rFP, #OFF_FP_METHOD]
mov r3, rSELF
PREFETCH_INST 2 @ Get next inst, but don't advance rPC
- bl MterpSetByteStatic
+ bl MterpSPutI8
cmp r0, #0 @ 0 on success, -1 on failure
bne MterpException
ADVANCE 2 @ Past exception point - now advance rPC
@@ -2990,7 +2990,7 @@
ldr r2, [rFP, #OFF_FP_METHOD]
mov r3, rSELF
PREFETCH_INST 2 @ Get next inst, but don't advance rPC
- bl MterpSetCharStatic
+ bl MterpSPutU16
cmp r0, #0 @ 0 on success, -1 on failure
bne MterpException
ADVANCE 2 @ Past exception point - now advance rPC
@@ -3016,7 +3016,7 @@
ldr r2, [rFP, #OFF_FP_METHOD]
mov r3, rSELF
PREFETCH_INST 2 @ Get next inst, but don't advance rPC
- bl MterpSetShortStatic
+ bl MterpSPutI16
cmp r0, #0 @ 0 on success, -1 on failure
bne MterpException
ADVANCE 2 @ Past exception point - now advance rPC
diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S
index 2a0c4df..770b109 100644
--- a/runtime/interpreter/mterp/out/mterp_arm64.S
+++ b/runtime/interpreter/mterp/out/mterp_arm64.S
@@ -2192,7 +2192,7 @@
GET_VREG w1, w1 // w1<- fp[B], the object pointer
ldr x2, [xFP, #OFF_FP_METHOD] // w2<- referrer
mov x3, xSELF // w3<- self
- bl artGet32InstanceFromCode
+ bl MterpIGetU32
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
ubfx w2, wINST, #8, #4 // w2<- A
@@ -2222,7 +2222,7 @@
GET_VREG w1, w1 // w1<- fp[B], the object pointer
ldr x2, [xFP, #OFF_FP_METHOD] // w2<- referrer
mov x3, xSELF // w3<- self
- bl artGet64InstanceFromCode
+ bl MterpIGetU64
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
ubfx w2, wINST, #8, #4 // w2<- A
PREFETCH_INST 2
@@ -2249,7 +2249,7 @@
GET_VREG w1, w1 // w1<- fp[B], the object pointer
ldr x2, [xFP, #OFF_FP_METHOD] // w2<- referrer
mov x3, xSELF // w3<- self
- bl artGetObjInstanceFromCode
+ bl MterpIGetObj
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
ubfx w2, wINST, #8, #4 // w2<- A
@@ -2281,7 +2281,7 @@
GET_VREG w1, w1 // w1<- fp[B], the object pointer
ldr x2, [xFP, #OFF_FP_METHOD] // w2<- referrer
mov x3, xSELF // w3<- self
- bl artGetBooleanInstanceFromCode
+ bl MterpIGetU8
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
uxtb w0, w0
ubfx w2, wINST, #8, #4 // w2<- A
@@ -2313,7 +2313,7 @@
GET_VREG w1, w1 // w1<- fp[B], the object pointer
ldr x2, [xFP, #OFF_FP_METHOD] // w2<- referrer
mov x3, xSELF // w3<- self
- bl artGetByteInstanceFromCode
+ bl MterpIGetI8
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
sxtb w0, w0
ubfx w2, wINST, #8, #4 // w2<- A
@@ -2345,7 +2345,7 @@
GET_VREG w1, w1 // w1<- fp[B], the object pointer
ldr x2, [xFP, #OFF_FP_METHOD] // w2<- referrer
mov x3, xSELF // w3<- self
- bl artGetCharInstanceFromCode
+ bl MterpIGetU16
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
uxth w0, w0
ubfx w2, wINST, #8, #4 // w2<- A
@@ -2377,7 +2377,7 @@
GET_VREG w1, w1 // w1<- fp[B], the object pointer
ldr x2, [xFP, #OFF_FP_METHOD] // w2<- referrer
mov x3, xSELF // w3<- self
- bl artGetShortInstanceFromCode
+ bl MterpIGetI16
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
sxth w0, w0
ubfx w2, wINST, #8, #4 // w2<- A
@@ -2403,7 +2403,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field//CCCC */
- .extern artSet32InstanceFromMterp
+ .extern MterpIPutU32
EXPORT_PC
FETCH w0, 1 // w0<- field ref CCCC
lsr w1, wINST, #12 // w1<- B
@@ -2412,7 +2412,7 @@
GET_VREG w2, w2 // w2<- fp[A]
ldr x3, [xFP, #OFF_FP_METHOD] // w3<- referrer
PREFETCH_INST 2
- bl artSet32InstanceFromMterp
+ bl MterpIPutU32
cbnz w0, MterpPossibleException
ADVANCE 2 // advance rPC
GET_INST_OPCODE ip // extract opcode from rINST
@@ -2423,7 +2423,7 @@
.L_op_iput_wide: /* 0x5a */
/* File: arm64/op_iput_wide.S */
/* iput-wide vA, vB, field//CCCC */
- .extern artSet64InstanceFromMterp
+ .extern MterpIPutU64
EXPORT_PC
FETCH w0, 1 // w0<- field ref CCCC
lsr w1, wINST, #12 // w1<- B
@@ -2432,7 +2432,7 @@
VREG_INDEX_TO_ADDR x2, x2 // w2<- &fp[A]
ldr x3, [xFP, #OFF_FP_METHOD] // w3<- referrer
PREFETCH_INST 2
- bl artSet64InstanceFromMterp
+ bl MterpIPutU64
cbnz w0, MterpPossibleException
ADVANCE 2 // advance rPC
GET_INST_OPCODE ip // extract opcode from wINST
@@ -2447,7 +2447,7 @@
mov x1, xPC
mov w2, wINST
mov x3, xSELF
- bl MterpIputObject
+ bl MterpIPutObj
cbz w0, MterpException
FETCH_ADVANCE_INST 2 // advance rPC, load rINST
GET_INST_OPCODE ip // extract opcode from rINST
@@ -2464,7 +2464,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field//CCCC */
- .extern artSet8InstanceFromMterp
+ .extern MterpIPutU8
EXPORT_PC
FETCH w0, 1 // w0<- field ref CCCC
lsr w1, wINST, #12 // w1<- B
@@ -2473,7 +2473,7 @@
GET_VREG w2, w2 // w2<- fp[A]
ldr x3, [xFP, #OFF_FP_METHOD] // w3<- referrer
PREFETCH_INST 2
- bl artSet8InstanceFromMterp
+ bl MterpIPutU8
cbnz w0, MterpPossibleException
ADVANCE 2 // advance rPC
GET_INST_OPCODE ip // extract opcode from rINST
@@ -2491,7 +2491,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field//CCCC */
- .extern artSet8InstanceFromMterp
+ .extern MterpIPutI8
EXPORT_PC
FETCH w0, 1 // w0<- field ref CCCC
lsr w1, wINST, #12 // w1<- B
@@ -2500,7 +2500,7 @@
GET_VREG w2, w2 // w2<- fp[A]
ldr x3, [xFP, #OFF_FP_METHOD] // w3<- referrer
PREFETCH_INST 2
- bl artSet8InstanceFromMterp
+ bl MterpIPutI8
cbnz w0, MterpPossibleException
ADVANCE 2 // advance rPC
GET_INST_OPCODE ip // extract opcode from rINST
@@ -2518,7 +2518,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field//CCCC */
- .extern artSet16InstanceFromMterp
+ .extern MterpIPutU16
EXPORT_PC
FETCH w0, 1 // w0<- field ref CCCC
lsr w1, wINST, #12 // w1<- B
@@ -2527,7 +2527,7 @@
GET_VREG w2, w2 // w2<- fp[A]
ldr x3, [xFP, #OFF_FP_METHOD] // w3<- referrer
PREFETCH_INST 2
- bl artSet16InstanceFromMterp
+ bl MterpIPutU16
cbnz w0, MterpPossibleException
ADVANCE 2 // advance rPC
GET_INST_OPCODE ip // extract opcode from rINST
@@ -2545,7 +2545,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field//CCCC */
- .extern artSet16InstanceFromMterp
+ .extern MterpIPutI16
EXPORT_PC
FETCH w0, 1 // w0<- field ref CCCC
lsr w1, wINST, #12 // w1<- B
@@ -2554,7 +2554,7 @@
GET_VREG w2, w2 // w2<- fp[A]
ldr x3, [xFP, #OFF_FP_METHOD] // w3<- referrer
PREFETCH_INST 2
- bl artSet16InstanceFromMterp
+ bl MterpIPutI16
cbnz w0, MterpPossibleException
ADVANCE 2 // advance rPC
GET_INST_OPCODE ip // extract opcode from rINST
@@ -2572,12 +2572,12 @@
*/
/* op vAA, field//BBBB */
- .extern MterpGet32Static
+ .extern MterpSGetU32
EXPORT_PC
FETCH w0, 1 // w0<- field ref BBBB
ldr x1, [xFP, #OFF_FP_METHOD]
mov x2, xSELF
- bl MterpGet32Static
+ bl MterpSGetU32
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
lsr w2, wINST, #8 // w2<- AA
@@ -2607,7 +2607,7 @@
FETCH w0, 1 // w0<- field ref BBBB
ldr x1, [xFP, #OFF_FP_METHOD]
mov x2, xSELF
- bl MterpGet64Static
+ bl MterpSGetU64
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
lsr w4, wINST, #8 // w4<- AA
cbnz x3, MterpException // bail out
@@ -2628,12 +2628,12 @@
*/
/* op vAA, field//BBBB */
- .extern MterpGetObjStatic
+ .extern MterpSGetObj
EXPORT_PC
FETCH w0, 1 // w0<- field ref BBBB
ldr x1, [xFP, #OFF_FP_METHOD]
mov x2, xSELF
- bl MterpGetObjStatic
+ bl MterpSGetObj
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
lsr w2, wINST, #8 // w2<- AA
@@ -2661,12 +2661,12 @@
*/
/* op vAA, field//BBBB */
- .extern MterpGetBooleanStatic
+ .extern MterpSGetU8
EXPORT_PC
FETCH w0, 1 // w0<- field ref BBBB
ldr x1, [xFP, #OFF_FP_METHOD]
mov x2, xSELF
- bl MterpGetBooleanStatic
+ bl MterpSGetU8
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
lsr w2, wINST, #8 // w2<- AA
uxtb w0, w0
@@ -2694,12 +2694,12 @@
*/
/* op vAA, field//BBBB */
- .extern MterpGetByteStatic
+ .extern MterpSGetI8
EXPORT_PC
FETCH w0, 1 // w0<- field ref BBBB
ldr x1, [xFP, #OFF_FP_METHOD]
mov x2, xSELF
- bl MterpGetByteStatic
+ bl MterpSGetI8
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
lsr w2, wINST, #8 // w2<- AA
sxtb w0, w0
@@ -2727,12 +2727,12 @@
*/
/* op vAA, field//BBBB */
- .extern MterpGetCharStatic
+ .extern MterpSGetU16
EXPORT_PC
FETCH w0, 1 // w0<- field ref BBBB
ldr x1, [xFP, #OFF_FP_METHOD]
mov x2, xSELF
- bl MterpGetCharStatic
+ bl MterpSGetU16
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
lsr w2, wINST, #8 // w2<- AA
uxth w0, w0
@@ -2760,12 +2760,12 @@
*/
/* op vAA, field//BBBB */
- .extern MterpGetShortStatic
+ .extern MterpSGetI16
EXPORT_PC
FETCH w0, 1 // w0<- field ref BBBB
ldr x1, [xFP, #OFF_FP_METHOD]
mov x2, xSELF
- bl MterpGetShortStatic
+ bl MterpSGetI16
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
lsr w2, wINST, #8 // w2<- AA
sxth w0, w0
@@ -2798,7 +2798,7 @@
ldr x2, [xFP, #OFF_FP_METHOD]
mov x3, xSELF
PREFETCH_INST 2 // Get next inst, but don't advance rPC
- bl MterpSet32Static
+ bl MterpSPutU32
cbnz w0, MterpException // 0 on success
ADVANCE 2 // Past exception point - now advance rPC
GET_INST_OPCODE ip // extract opcode from rINST
@@ -2813,7 +2813,7 @@
*
*/
/* sput-wide vAA, field//BBBB */
- .extern MterpSet64Static
+ .extern MterpSPutU64
EXPORT_PC
FETCH w0, 1 // w0<- field ref BBBB
lsr w1, wINST, #8 // w1<- AA
@@ -2821,7 +2821,7 @@
ldr x2, [xFP, #OFF_FP_METHOD]
mov x3, xSELF
PREFETCH_INST 2 // Get next inst, but don't advance rPC
- bl MterpSet64Static
+ bl MterpSPutU64
cbnz w0, MterpException // 0 on success, -1 on failure
ADVANCE 2 // Past exception point - now advance rPC
GET_INST_OPCODE ip // extract opcode from wINST
@@ -2836,7 +2836,7 @@
mov x1, xPC
mov x2, xINST
mov x3, xSELF
- bl MterpSputObject
+ bl MterpSPutObj
cbz w0, MterpException
FETCH_ADVANCE_INST 2 // advance rPC, load rINST
GET_INST_OPCODE ip // extract opcode from rINST
@@ -2860,7 +2860,7 @@
ldr x2, [xFP, #OFF_FP_METHOD]
mov x3, xSELF
PREFETCH_INST 2 // Get next inst, but don't advance rPC
- bl MterpSetBooleanStatic
+ bl MterpSPutU8
cbnz w0, MterpException // 0 on success
ADVANCE 2 // Past exception point - now advance rPC
GET_INST_OPCODE ip // extract opcode from rINST
@@ -2885,7 +2885,7 @@
ldr x2, [xFP, #OFF_FP_METHOD]
mov x3, xSELF
PREFETCH_INST 2 // Get next inst, but don't advance rPC
- bl MterpSetByteStatic
+ bl MterpSPutI8
cbnz w0, MterpException // 0 on success
ADVANCE 2 // Past exception point - now advance rPC
GET_INST_OPCODE ip // extract opcode from rINST
@@ -2910,7 +2910,7 @@
ldr x2, [xFP, #OFF_FP_METHOD]
mov x3, xSELF
PREFETCH_INST 2 // Get next inst, but don't advance rPC
- bl MterpSetCharStatic
+ bl MterpSPutU16
cbnz w0, MterpException // 0 on success
ADVANCE 2 // Past exception point - now advance rPC
GET_INST_OPCODE ip // extract opcode from rINST
@@ -2935,7 +2935,7 @@
ldr x2, [xFP, #OFF_FP_METHOD]
mov x3, xSELF
PREFETCH_INST 2 // Get next inst, but don't advance rPC
- bl MterpSetShortStatic
+ bl MterpSPutI16
cbnz w0, MterpException // 0 on success
ADVANCE 2 // Past exception point - now advance rPC
GET_INST_OPCODE ip // extract opcode from rINST
diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S
index 3b86279..fb7d52e 100644
--- a/runtime/interpreter/mterp/out/mterp_mips.S
+++ b/runtime/interpreter/mterp/out/mterp_mips.S
@@ -2677,7 +2677,7 @@
GET_VREG(a1, a1) # a1 <- fp[B], the object pointer
lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- JAL(artGet32InstanceFromCode)
+ JAL(MterpIGetU32)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA4(a2) # a2<- A+
PREFETCH_INST(2) # load rINST
@@ -2706,7 +2706,7 @@
GET_VREG(a1, a1) # a1 <- fp[B], the object pointer
lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- JAL(artGet64InstanceFromCode)
+ JAL(MterpIGetU64)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA4(a2) # a2<- A+
PREFETCH_INST(2) # load rINST
@@ -2732,7 +2732,7 @@
GET_VREG(a1, a1) # a1 <- fp[B], the object pointer
lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- JAL(artGetObjInstanceFromCode)
+ JAL(MterpIGetObj)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA4(a2) # a2<- A+
PREFETCH_INST(2) # load rINST
@@ -2763,7 +2763,7 @@
GET_VREG(a1, a1) # a1 <- fp[B], the object pointer
lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- JAL(artGetBooleanInstanceFromCode)
+ JAL(MterpIGetU8)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA4(a2) # a2<- A+
PREFETCH_INST(2) # load rINST
@@ -2794,7 +2794,7 @@
GET_VREG(a1, a1) # a1 <- fp[B], the object pointer
lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- JAL(artGetByteInstanceFromCode)
+ JAL(MterpIGetI8)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA4(a2) # a2<- A+
PREFETCH_INST(2) # load rINST
@@ -2825,7 +2825,7 @@
GET_VREG(a1, a1) # a1 <- fp[B], the object pointer
lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- JAL(artGetCharInstanceFromCode)
+ JAL(MterpIGetU16)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA4(a2) # a2<- A+
PREFETCH_INST(2) # load rINST
@@ -2856,7 +2856,7 @@
GET_VREG(a1, a1) # a1 <- fp[B], the object pointer
lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- JAL(artGetShortInstanceFromCode)
+ JAL(MterpIGetI16)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA4(a2) # a2<- A+
PREFETCH_INST(2) # load rINST
@@ -2880,7 +2880,7 @@
* for: iput, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet32InstanceFromMterp
+ .extern MterpIPutU32
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref CCCC
GET_OPB(a1) # a1 <- B
@@ -2889,7 +2889,7 @@
GET_VREG(a2, a2) # a2 <- fp[A]
lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST(2) # load rINST
- JAL(artSet32InstanceFromMterp)
+ JAL(MterpIPutU32)
bnez v0, MterpPossibleException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -2900,7 +2900,7 @@
.L_op_iput_wide: /* 0x5a */
/* File: mips/op_iput_wide.S */
/* iput-wide vA, vB, field@CCCC */
- .extern artSet64InstanceFromMterp
+ .extern MterpIPutU64
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref CCCC
GET_OPB(a1) # a1 <- B
@@ -2909,7 +2909,7 @@
EAS2(a2, rFP, a2) # a2 <- &fp[A]
lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST(2) # load rINST
- JAL(artSet64InstanceFromMterp)
+ JAL(MterpIPutU64)
bnez v0, MterpPossibleException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -2930,7 +2930,7 @@
move a1, rPC
move a2, rINST
move a3, rSELF
- JAL(MterpIputObject)
+ JAL(MterpIPutObj)
beqz v0, MterpException
FETCH_ADVANCE_INST(2) # advance rPC, load rINST
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -2947,7 +2947,7 @@
* for: iput, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet8InstanceFromMterp
+ .extern MterpIPutU8
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref CCCC
GET_OPB(a1) # a1 <- B
@@ -2956,7 +2956,7 @@
GET_VREG(a2, a2) # a2 <- fp[A]
lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST(2) # load rINST
- JAL(artSet8InstanceFromMterp)
+ JAL(MterpIPutU8)
bnez v0, MterpPossibleException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -2974,7 +2974,7 @@
* for: iput, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet8InstanceFromMterp
+ .extern MterpIPutI8
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref CCCC
GET_OPB(a1) # a1 <- B
@@ -2983,7 +2983,7 @@
GET_VREG(a2, a2) # a2 <- fp[A]
lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST(2) # load rINST
- JAL(artSet8InstanceFromMterp)
+ JAL(MterpIPutI8)
bnez v0, MterpPossibleException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -3001,7 +3001,7 @@
* for: iput, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet16InstanceFromMterp
+ .extern MterpIPutU16
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref CCCC
GET_OPB(a1) # a1 <- B
@@ -3010,7 +3010,7 @@
GET_VREG(a2, a2) # a2 <- fp[A]
lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST(2) # load rINST
- JAL(artSet16InstanceFromMterp)
+ JAL(MterpIPutU16)
bnez v0, MterpPossibleException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -3028,7 +3028,7 @@
* for: iput, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet16InstanceFromMterp
+ .extern MterpIPutI16
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref CCCC
GET_OPB(a1) # a1 <- B
@@ -3037,7 +3037,7 @@
GET_VREG(a2, a2) # a2 <- fp[A]
lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST(2) # load rINST
- JAL(artSet16InstanceFromMterp)
+ JAL(MterpIPutI16)
bnez v0, MterpPossibleException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -3054,12 +3054,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field@BBBB */
- .extern MterpGet32Static
+ .extern MterpSGetU32
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref BBBB
lw a1, OFF_FP_METHOD(rFP) # a1 <- method
move a2, rSELF # a2 <- self
- JAL(MterpGet32Static)
+ JAL(MterpSGetU32)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA(a2) # a2 <- AA
PREFETCH_INST(2)
@@ -3080,12 +3080,12 @@
* 64-bit SGET handler.
*/
/* sget-wide vAA, field@BBBB */
- .extern MterpGet64Static
+ .extern MterpSGetU64
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref BBBB
lw a1, OFF_FP_METHOD(rFP) # a1 <- method
move a2, rSELF # a2 <- self
- JAL(MterpGet64Static)
+ JAL(MterpSGetU64)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
bnez a3, MterpException
GET_OPA(a1) # a1 <- AA
@@ -3104,12 +3104,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field@BBBB */
- .extern MterpGetObjStatic
+ .extern MterpSGetObj
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref BBBB
lw a1, OFF_FP_METHOD(rFP) # a1 <- method
move a2, rSELF # a2 <- self
- JAL(MterpGetObjStatic)
+ JAL(MterpSGetObj)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA(a2) # a2 <- AA
PREFETCH_INST(2)
@@ -3134,12 +3134,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field@BBBB */
- .extern MterpGetBooleanStatic
+ .extern MterpSGetU8
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref BBBB
lw a1, OFF_FP_METHOD(rFP) # a1 <- method
move a2, rSELF # a2 <- self
- JAL(MterpGetBooleanStatic)
+ JAL(MterpSGetU8)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA(a2) # a2 <- AA
PREFETCH_INST(2)
@@ -3164,12 +3164,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field@BBBB */
- .extern MterpGetByteStatic
+ .extern MterpSGetI8
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref BBBB
lw a1, OFF_FP_METHOD(rFP) # a1 <- method
move a2, rSELF # a2 <- self
- JAL(MterpGetByteStatic)
+ JAL(MterpSGetI8)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA(a2) # a2 <- AA
PREFETCH_INST(2)
@@ -3194,12 +3194,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field@BBBB */
- .extern MterpGetCharStatic
+ .extern MterpSGetU16
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref BBBB
lw a1, OFF_FP_METHOD(rFP) # a1 <- method
move a2, rSELF # a2 <- self
- JAL(MterpGetCharStatic)
+ JAL(MterpSGetU16)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA(a2) # a2 <- AA
PREFETCH_INST(2)
@@ -3224,12 +3224,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field@BBBB */
- .extern MterpGetShortStatic
+ .extern MterpSGetI16
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref BBBB
lw a1, OFF_FP_METHOD(rFP) # a1 <- method
move a2, rSELF # a2 <- self
- JAL(MterpGetShortStatic)
+ JAL(MterpSGetI16)
lw a3, THREAD_EXCEPTION_OFFSET(rSELF)
GET_OPA(a2) # a2 <- AA
PREFETCH_INST(2)
@@ -3260,7 +3260,7 @@
lw a2, OFF_FP_METHOD(rFP) # a2 <- method
move a3, rSELF # a3 <- self
PREFETCH_INST(2) # load rINST
- JAL(MterpSet32Static)
+ JAL(MterpSPutU32)
bnez v0, MterpException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -3274,7 +3274,7 @@
* 64-bit SPUT handler.
*/
/* sput-wide vAA, field@BBBB */
- .extern MterpSet64Static
+ .extern MterpSPutU64
EXPORT_PC()
FETCH(a0, 1) # a0 <- field ref CCCC
GET_OPA(a1) # a1 <- AA
@@ -3282,7 +3282,7 @@
lw a2, OFF_FP_METHOD(rFP) # a2 <- method
move a3, rSELF # a3 <- self
PREFETCH_INST(2) # load rINST
- JAL(MterpSet64Static)
+ JAL(MterpSPutU64)
bnez v0, MterpException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -3303,7 +3303,7 @@
move a1, rPC
move a2, rINST
move a3, rSELF
- JAL(MterpSputObject)
+ JAL(MterpSPutObj)
beqz v0, MterpException
FETCH_ADVANCE_INST(2) # advance rPC, load rINST
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -3327,7 +3327,7 @@
lw a2, OFF_FP_METHOD(rFP) # a2 <- method
move a3, rSELF # a3 <- self
PREFETCH_INST(2) # load rINST
- JAL(MterpSetBooleanStatic)
+ JAL(MterpSPutU8)
bnez v0, MterpException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -3352,7 +3352,7 @@
lw a2, OFF_FP_METHOD(rFP) # a2 <- method
move a3, rSELF # a3 <- self
PREFETCH_INST(2) # load rINST
- JAL(MterpSetByteStatic)
+ JAL(MterpSPutI8)
bnez v0, MterpException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -3377,7 +3377,7 @@
lw a2, OFF_FP_METHOD(rFP) # a2 <- method
move a3, rSELF # a3 <- self
PREFETCH_INST(2) # load rINST
- JAL(MterpSetCharStatic)
+ JAL(MterpSPutU16)
bnez v0, MterpException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
@@ -3402,7 +3402,7 @@
lw a2, OFF_FP_METHOD(rFP) # a2 <- method
move a3, rSELF # a3 <- self
PREFETCH_INST(2) # load rINST
- JAL(MterpSetShortStatic)
+ JAL(MterpSPutI16)
bnez v0, MterpException # bail out
ADVANCE(2) # advance rPC
GET_INST_OPCODE(t0) # extract opcode from rINST
diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S
index 58f98df..6561691 100644
--- a/runtime/interpreter/mterp/out/mterp_mips64.S
+++ b/runtime/interpreter/mterp/out/mterp_mips64.S
@@ -2246,14 +2246,14 @@
*
* for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
*/
- .extern artGet32InstanceFromCode
+ .extern MterpIGetU32
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer
ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- jal artGet32InstanceFromCode
+ jal MterpIGetU32
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
ext a2, rINST, 8, 4 # a2 <- A
PREFETCH_INST 2
@@ -2276,14 +2276,14 @@
*
* for: iget-wide
*/
- .extern artGet64InstanceFromCode
+ .extern MterpIGetU64
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer
ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- jal artGet64InstanceFromCode
+ jal MterpIGetU64
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
ext a2, rINST, 8, 4 # a2 <- A
PREFETCH_INST 2
@@ -2303,14 +2303,14 @@
*
* for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
*/
- .extern artGetObjInstanceFromCode
+ .extern MterpIGetObj
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer
ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- jal artGetObjInstanceFromCode
+ jal MterpIGetObj
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
ext a2, rINST, 8, 4 # a2 <- A
PREFETCH_INST 2
@@ -2335,14 +2335,14 @@
*
* for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
*/
- .extern artGetBooleanInstanceFromCode
+ .extern MterpIGetU8
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer
ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- jal artGetBooleanInstanceFromCode
+ jal MterpIGetU8
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
ext a2, rINST, 8, 4 # a2 <- A
PREFETCH_INST 2
@@ -2367,14 +2367,14 @@
*
* for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
*/
- .extern artGetByteInstanceFromCode
+ .extern MterpIGetI8
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer
ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- jal artGetByteInstanceFromCode
+ jal MterpIGetI8
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
ext a2, rINST, 8, 4 # a2 <- A
PREFETCH_INST 2
@@ -2399,14 +2399,14 @@
*
* for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
*/
- .extern artGetCharInstanceFromCode
+ .extern MterpIGetU16
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer
ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- jal artGetCharInstanceFromCode
+ jal MterpIGetU16
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
ext a2, rINST, 8, 4 # a2 <- A
PREFETCH_INST 2
@@ -2431,14 +2431,14 @@
*
* for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
*/
- .extern artGetShortInstanceFromCode
+ .extern MterpIGetI16
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer
ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer
move a3, rSELF # a3 <- self
- jal artGetShortInstanceFromCode
+ jal MterpIGetI16
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
ext a2, rINST, 8, 4 # a2 <- A
PREFETCH_INST 2
@@ -2463,7 +2463,7 @@
* for: iput, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field//CCCC */
- .extern artSet32InstanceFromMterp
+ .extern MterpIPutU32
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
@@ -2472,7 +2472,7 @@
GET_VREG a2, a2 # a2 <- fp[A]
ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST 2
- jal artSet32InstanceFromMterp
+ jal MterpIPutU32
bnez v0, MterpPossibleException # bail out
ADVANCE 2
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2483,7 +2483,7 @@
.L_op_iput_wide: /* 0x5a */
/* File: mips64/op_iput_wide.S */
/* iput-wide vA, vB, field//CCCC */
- .extern artSet64InstanceFromMterp
+ .extern MterpIPutU64
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
@@ -2492,7 +2492,7 @@
dlsa a2, a2, rFP, 2 # a2 <- &fp[A]
ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST 2
- jal artSet64InstanceFromMterp
+ jal MterpIPutU64
bnez v0, MterpPossibleException # bail out
ADVANCE 2
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2502,13 +2502,13 @@
.balign 128
.L_op_iput_object: /* 0x5b */
/* File: mips64/op_iput_object.S */
- .extern MterpIputObject
+ .extern MterpIPutObj
EXPORT_PC
daddu a0, rFP, OFF_FP_SHADOWFRAME
move a1, rPC
move a2, rINST
move a3, rSELF
- jal MterpIputObject
+ jal MterpIPutObj
beqzc v0, MterpException
FETCH_ADVANCE_INST 2 # advance rPC, load rINST
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2525,7 +2525,7 @@
* for: iput, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field//CCCC */
- .extern artSet8InstanceFromMterp
+ .extern MterpIPutU8
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
@@ -2534,7 +2534,7 @@
GET_VREG a2, a2 # a2 <- fp[A]
ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST 2
- jal artSet8InstanceFromMterp
+ jal MterpIPutU8
bnez v0, MterpPossibleException # bail out
ADVANCE 2
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2552,7 +2552,7 @@
* for: iput, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field//CCCC */
- .extern artSet8InstanceFromMterp
+ .extern MterpIPutI8
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
@@ -2561,7 +2561,7 @@
GET_VREG a2, a2 # a2 <- fp[A]
ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST 2
- jal artSet8InstanceFromMterp
+ jal MterpIPutI8
bnez v0, MterpPossibleException # bail out
ADVANCE 2
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2579,7 +2579,7 @@
* for: iput, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field//CCCC */
- .extern artSet16InstanceFromMterp
+ .extern MterpIPutU16
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
@@ -2588,7 +2588,7 @@
GET_VREG a2, a2 # a2 <- fp[A]
ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST 2
- jal artSet16InstanceFromMterp
+ jal MterpIPutU16
bnez v0, MterpPossibleException # bail out
ADVANCE 2
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2606,7 +2606,7 @@
* for: iput, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field//CCCC */
- .extern artSet16InstanceFromMterp
+ .extern MterpIPutI16
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref CCCC
srl a1, rINST, 12 # a1 <- B
@@ -2615,7 +2615,7 @@
GET_VREG a2, a2 # a2 <- fp[A]
ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer
PREFETCH_INST 2
- jal artSet16InstanceFromMterp
+ jal MterpIPutI16
bnez v0, MterpPossibleException # bail out
ADVANCE 2
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2632,12 +2632,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field//BBBB */
- .extern MterpGet32Static
+ .extern MterpSGetU32
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
ld a1, OFF_FP_METHOD(rFP)
move a2, rSELF
- jal MterpGet32Static
+ jal MterpSGetU32
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
srl a2, rINST, 8 # a2 <- AA
@@ -2661,12 +2661,12 @@
*
*/
/* sget-wide vAA, field//BBBB */
- .extern MterpGet64Static
+ .extern MterpSGetU64
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
ld a1, OFF_FP_METHOD(rFP)
move a2, rSELF
- jal MterpGet64Static
+ jal MterpSGetU64
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
srl a4, rINST, 8 # a4 <- AA
bnez a3, MterpException # bail out
@@ -2686,12 +2686,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field//BBBB */
- .extern MterpGetObjStatic
+ .extern MterpSGetObj
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
ld a1, OFF_FP_METHOD(rFP)
move a2, rSELF
- jal MterpGetObjStatic
+ jal MterpSGetObj
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
srl a2, rINST, 8 # a2 <- AA
@@ -2718,12 +2718,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field//BBBB */
- .extern MterpGetBooleanStatic
+ .extern MterpSGetU8
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
ld a1, OFF_FP_METHOD(rFP)
move a2, rSELF
- jal MterpGetBooleanStatic
+ jal MterpSGetU8
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
srl a2, rINST, 8 # a2 <- AA
and v0, v0, 0xff
@@ -2750,12 +2750,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field//BBBB */
- .extern MterpGetByteStatic
+ .extern MterpSGetI8
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
ld a1, OFF_FP_METHOD(rFP)
move a2, rSELF
- jal MterpGetByteStatic
+ jal MterpSGetI8
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
srl a2, rINST, 8 # a2 <- AA
seb v0, v0
@@ -2782,12 +2782,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field//BBBB */
- .extern MterpGetCharStatic
+ .extern MterpSGetU16
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
ld a1, OFF_FP_METHOD(rFP)
move a2, rSELF
- jal MterpGetCharStatic
+ jal MterpSGetU16
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
srl a2, rINST, 8 # a2 <- AA
and v0, v0, 0xffff
@@ -2814,12 +2814,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field//BBBB */
- .extern MterpGetShortStatic
+ .extern MterpSGetI16
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
ld a1, OFF_FP_METHOD(rFP)
move a2, rSELF
- jal MterpGetShortStatic
+ jal MterpSGetI16
ld a3, THREAD_EXCEPTION_OFFSET(rSELF)
srl a2, rINST, 8 # a2 <- AA
seh v0, v0
@@ -2845,7 +2845,7 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field//BBBB */
- .extern MterpSet32Static
+ .extern MterpSPutU32
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
srl a3, rINST, 8 # a3 <- AA
@@ -2853,7 +2853,7 @@
ld a2, OFF_FP_METHOD(rFP)
move a3, rSELF
PREFETCH_INST 2 # Get next inst, but don't advance rPC
- jal MterpSet32Static
+ jal MterpSPutU32
bnezc v0, MterpException # 0 on success
ADVANCE 2 # Past exception point - now advance rPC
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2868,7 +2868,7 @@
*
*/
/* sput-wide vAA, field//BBBB */
- .extern MterpSet64Static
+ .extern MterpSPutU64
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
srl a1, rINST, 8 # a2 <- AA
@@ -2876,7 +2876,7 @@
ld a2, OFF_FP_METHOD(rFP)
move a3, rSELF
PREFETCH_INST 2 # Get next inst, but don't advance rPC
- jal MterpSet64Static
+ jal MterpSPutU64
bnezc v0, MterpException # 0 on success, -1 on failure
ADVANCE 2 # Past exception point - now advance rPC
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2886,13 +2886,13 @@
.balign 128
.L_op_sput_object: /* 0x69 */
/* File: mips64/op_sput_object.S */
- .extern MterpSputObject
+ .extern MterpSPutObj
EXPORT_PC
daddu a0, rFP, OFF_FP_SHADOWFRAME
move a1, rPC
move a2, rINST
move a3, rSELF
- jal MterpSputObject
+ jal MterpSPutObj
beqzc v0, MterpException
FETCH_ADVANCE_INST 2 # advance rPC, load rINST
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2909,7 +2909,7 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field//BBBB */
- .extern MterpSetBooleanStatic
+ .extern MterpSPutU8
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
srl a3, rINST, 8 # a3 <- AA
@@ -2917,7 +2917,7 @@
ld a2, OFF_FP_METHOD(rFP)
move a3, rSELF
PREFETCH_INST 2 # Get next inst, but don't advance rPC
- jal MterpSetBooleanStatic
+ jal MterpSPutU8
bnezc v0, MterpException # 0 on success
ADVANCE 2 # Past exception point - now advance rPC
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2935,7 +2935,7 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field//BBBB */
- .extern MterpSetByteStatic
+ .extern MterpSPutI8
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
srl a3, rINST, 8 # a3 <- AA
@@ -2943,7 +2943,7 @@
ld a2, OFF_FP_METHOD(rFP)
move a3, rSELF
PREFETCH_INST 2 # Get next inst, but don't advance rPC
- jal MterpSetByteStatic
+ jal MterpSPutI8
bnezc v0, MterpException # 0 on success
ADVANCE 2 # Past exception point - now advance rPC
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2961,7 +2961,7 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field//BBBB */
- .extern MterpSetCharStatic
+ .extern MterpSPutU16
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
srl a3, rINST, 8 # a3 <- AA
@@ -2969,7 +2969,7 @@
ld a2, OFF_FP_METHOD(rFP)
move a3, rSELF
PREFETCH_INST 2 # Get next inst, but don't advance rPC
- jal MterpSetCharStatic
+ jal MterpSPutU16
bnezc v0, MterpException # 0 on success
ADVANCE 2 # Past exception point - now advance rPC
GET_INST_OPCODE v0 # extract opcode from rINST
@@ -2987,7 +2987,7 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field//BBBB */
- .extern MterpSetShortStatic
+ .extern MterpSPutI16
EXPORT_PC
lhu a0, 2(rPC) # a0 <- field ref BBBB
srl a3, rINST, 8 # a3 <- AA
@@ -2995,7 +2995,7 @@
ld a2, OFF_FP_METHOD(rFP)
move a3, rSELF
PREFETCH_INST 2 # Get next inst, but don't advance rPC
- jal MterpSetShortStatic
+ jal MterpSPutI16
bnezc v0, MterpException # 0 on success
ADVANCE 2 # Past exception point - now advance rPC
GET_INST_OPCODE v0 # extract opcode from rINST
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index 6be70cc..c78eb49 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -2132,7 +2132,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(artGet32InstanceFromCode)
+ call SYMBOL(MterpIGetU32)
movl rSELF, %ecx
RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
@@ -2165,7 +2165,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(artGet64InstanceFromCode)
+ call SYMBOL(MterpIGetU64)
mov rSELF, %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
@@ -2196,7 +2196,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(artGetObjInstanceFromCode)
+ call SYMBOL(MterpIGetObj)
movl rSELF, %ecx
RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
@@ -2231,7 +2231,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(artGetBooleanInstanceFromCode)
+ call SYMBOL(MterpIGetU8)
movl rSELF, %ecx
RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
@@ -2266,7 +2266,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(artGetByteInstanceFromCode)
+ call SYMBOL(MterpIGetI8)
movl rSELF, %ecx
RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
@@ -2301,7 +2301,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(artGetCharInstanceFromCode)
+ call SYMBOL(MterpIGetU16)
movl rSELF, %ecx
RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
@@ -2336,7 +2336,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(artGetShortInstanceFromCode)
+ call SYMBOL(MterpIGetI16)
movl rSELF, %ecx
RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
@@ -2360,7 +2360,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet32InstanceFromMterp
+ .extern MterpIPutU32
EXPORT_PC
movzwl 2(rPC), %eax # eax<- 0000CCCC
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2373,7 +2373,7 @@
movl %eax, OUT_ARG2(%esp) # fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call SYMBOL(artSet32InstanceFromMterp)
+ call SYMBOL(MterpIPutU32)
testb %al, %al
jnz MterpPossibleException
RESTORE_IBASE
@@ -2384,7 +2384,7 @@
.L_op_iput_wide: /* 0x5a */
/* File: x86/op_iput_wide.S */
/* iput-wide vA, vB, field@CCCC */
- .extern artSet64InstanceFromMterp
+ .extern MterpIPutU64
EXPORT_PC
movzwl 2(rPC), %eax # eax <- 0000CCCC
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2397,7 +2397,7 @@
movl %eax, OUT_ARG2(%esp) # &fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call SYMBOL(artSet64InstanceFromMterp)
+ call SYMBOL(MterpIPutU64)
testb %al, %al
jnz MterpPossibleException
RESTORE_IBASE
@@ -2415,7 +2415,7 @@
movl rINST, OUT_ARG2(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
- call SYMBOL(MterpIputObject)
+ call SYMBOL(MterpIPutObj)
testb %al, %al
jz MterpException
RESTORE_IBASE
@@ -2432,7 +2432,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet8InstanceFromMterp
+ .extern MterpIPutU8
EXPORT_PC
movzwl 2(rPC), %eax # eax<- 0000CCCC
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2445,7 +2445,7 @@
movl %eax, OUT_ARG2(%esp) # fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call SYMBOL(artSet8InstanceFromMterp)
+ call SYMBOL(MterpIPutU8)
testb %al, %al
jnz MterpPossibleException
RESTORE_IBASE
@@ -2463,7 +2463,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet8InstanceFromMterp
+ .extern MterpIPutI8
EXPORT_PC
movzwl 2(rPC), %eax # eax<- 0000CCCC
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2476,7 +2476,7 @@
movl %eax, OUT_ARG2(%esp) # fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call SYMBOL(artSet8InstanceFromMterp)
+ call SYMBOL(MterpIPutI8)
testb %al, %al
jnz MterpPossibleException
RESTORE_IBASE
@@ -2494,7 +2494,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet16InstanceFromMterp
+ .extern MterpIPutU16
EXPORT_PC
movzwl 2(rPC), %eax # eax<- 0000CCCC
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2507,7 +2507,7 @@
movl %eax, OUT_ARG2(%esp) # fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call SYMBOL(artSet16InstanceFromMterp)
+ call SYMBOL(MterpIPutU16)
testb %al, %al
jnz MterpPossibleException
RESTORE_IBASE
@@ -2525,7 +2525,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet16InstanceFromMterp
+ .extern MterpIPutI16
EXPORT_PC
movzwl 2(rPC), %eax # eax<- 0000CCCC
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2538,7 +2538,7 @@
movl %eax, OUT_ARG2(%esp) # fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call SYMBOL(artSet16InstanceFromMterp)
+ call SYMBOL(MterpIPutI16)
testb %al, %al
jnz MterpPossibleException
RESTORE_IBASE
@@ -2555,7 +2555,7 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field@BBBB */
- .extern MterpGet32Static
+ .extern MterpSGetU32
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2563,7 +2563,7 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call SYMBOL(MterpGet32Static)
+ call SYMBOL(MterpSGetU32)
movl rSELF, %ecx
RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
@@ -2584,7 +2584,7 @@
*
*/
/* sget-wide vAA, field@BBBB */
- .extern MterpGet64Static
+ .extern MterpSGetU64
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2592,7 +2592,7 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call SYMBOL(MterpGet64Static)
+ call SYMBOL(MterpSGetU64)
movl rSELF, %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
@@ -2612,7 +2612,7 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field@BBBB */
- .extern MterpGetObjStatic
+ .extern MterpSGetObj
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2620,7 +2620,7 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call SYMBOL(MterpGetObjStatic)
+ call SYMBOL(MterpSGetObj)
movl rSELF, %ecx
RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
@@ -2644,7 +2644,7 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field@BBBB */
- .extern MterpGetBooleanStatic
+ .extern MterpSGetU8
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2652,7 +2652,7 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call SYMBOL(MterpGetBooleanStatic)
+ call SYMBOL(MterpSGetU8)
movl rSELF, %ecx
RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
@@ -2676,7 +2676,7 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field@BBBB */
- .extern MterpGetByteStatic
+ .extern MterpSGetI8
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2684,7 +2684,7 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call SYMBOL(MterpGetByteStatic)
+ call SYMBOL(MterpSGetI8)
movl rSELF, %ecx
RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
@@ -2708,7 +2708,7 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field@BBBB */
- .extern MterpGetCharStatic
+ .extern MterpSGetU16
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2716,7 +2716,7 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call SYMBOL(MterpGetCharStatic)
+ call SYMBOL(MterpSGetU16)
movl rSELF, %ecx
RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
@@ -2740,7 +2740,7 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
*/
/* op vAA, field@BBBB */
- .extern MterpGetShortStatic
+ .extern MterpSGetI16
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -2748,7 +2748,7 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call SYMBOL(MterpGetShortStatic)
+ call SYMBOL(MterpSGetI16)
movl rSELF, %ecx
RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
@@ -2771,7 +2771,7 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field@BBBB */
- .extern MterpSet32Static
+ .extern MterpSPutU32
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
@@ -2781,7 +2781,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(MterpSet32Static)
+ call SYMBOL(MterpSPutU32)
testb %al, %al
jnz MterpException
RESTORE_IBASE
@@ -2796,7 +2796,7 @@
*
*/
/* sput-wide vAA, field@BBBB */
- .extern MterpSet64Static
+ .extern MterpSPutU64
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
@@ -2806,7 +2806,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(MterpSet64Static)
+ call SYMBOL(MterpSPutU64)
testb %al, %al
jnz MterpException
RESTORE_IBASE
@@ -2824,7 +2824,7 @@
movl rINST, OUT_ARG2(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
- call SYMBOL(MterpSputObject)
+ call SYMBOL(MterpSPutObj)
testb %al, %al
jz MterpException
RESTORE_IBASE
@@ -2841,7 +2841,7 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field@BBBB */
- .extern MterpSetBooleanStatic
+ .extern MterpSPutU8
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
@@ -2851,7 +2851,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(MterpSetBooleanStatic)
+ call SYMBOL(MterpSPutU8)
testb %al, %al
jnz MterpException
RESTORE_IBASE
@@ -2869,7 +2869,7 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field@BBBB */
- .extern MterpSetByteStatic
+ .extern MterpSPutI8
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
@@ -2879,7 +2879,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(MterpSetByteStatic)
+ call SYMBOL(MterpSPutI8)
testb %al, %al
jnz MterpException
RESTORE_IBASE
@@ -2897,7 +2897,7 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field@BBBB */
- .extern MterpSetCharStatic
+ .extern MterpSPutU16
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
@@ -2907,7 +2907,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(MterpSetCharStatic)
+ call SYMBOL(MterpSPutU16)
testb %al, %al
jnz MterpException
RESTORE_IBASE
@@ -2925,7 +2925,7 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field@BBBB */
- .extern MterpSetShortStatic
+ .extern MterpSPutI16
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
@@ -2935,7 +2935,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(MterpSetShortStatic)
+ call SYMBOL(MterpSPutI16)
testb %al, %al
jnz MterpException
RESTORE_IBASE
diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S
index 562cf7c..524dce4 100644
--- a/runtime/interpreter/mterp/out/mterp_x86_64.S
+++ b/runtime/interpreter/mterp/out/mterp_x86_64.S
@@ -2075,7 +2075,7 @@
GET_VREG OUT_32_ARG1, %rcx # the object pointer
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
- call SYMBOL(artGet32InstanceFromCode)
+ call SYMBOL(MterpIGetU32)
movq rSELF, %rcx
cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
@@ -2108,7 +2108,7 @@
GET_VREG OUT_32_ARG1, %rcx # the object pointer
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
- call SYMBOL(artGet64InstanceFromCode)
+ call SYMBOL(MterpIGetU64)
movq rSELF, %rcx
cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
@@ -2142,7 +2142,7 @@
GET_VREG OUT_32_ARG1, %rcx # the object pointer
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
- call SYMBOL(artGetObjInstanceFromCode)
+ call SYMBOL(MterpIGetObj)
movq rSELF, %rcx
cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
@@ -2176,7 +2176,7 @@
GET_VREG OUT_32_ARG1, %rcx # the object pointer
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
- call SYMBOL(artGetBooleanInstanceFromCode)
+ call SYMBOL(MterpIGetU8)
movq rSELF, %rcx
cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
@@ -2210,7 +2210,7 @@
GET_VREG OUT_32_ARG1, %rcx # the object pointer
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
- call SYMBOL(artGetByteInstanceFromCode)
+ call SYMBOL(MterpIGetI8)
movq rSELF, %rcx
cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
@@ -2244,7 +2244,7 @@
GET_VREG OUT_32_ARG1, %rcx # the object pointer
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
- call SYMBOL(artGetCharInstanceFromCode)
+ call SYMBOL(MterpIGetU16)
movq rSELF, %rcx
cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
@@ -2278,7 +2278,7 @@
GET_VREG OUT_32_ARG1, %rcx # the object pointer
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
- call SYMBOL(artGetShortInstanceFromCode)
+ call SYMBOL(MterpIGetI16)
movq rSELF, %rcx
cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
@@ -2305,7 +2305,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet32InstanceFromMterp
+ .extern MterpIPutU32
EXPORT_PC
movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC
movzbq rINSTbl, %rcx # rcx<- BA
@@ -2314,7 +2314,7 @@
andb $0xf, rINSTbl # rINST<- A
GET_VREG OUT_32_ARG2, rINSTq # fp[A]
movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer
- call SYMBOL(artSet32InstanceFromMterp)
+ call SYMBOL(MterpIPutU32)
testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2324,7 +2324,7 @@
.L_op_iput_wide: /* 0x5a */
/* File: x86_64/op_iput_wide.S */
/* iput-wide vA, vB, field@CCCC */
- .extern artSet64InstanceFromMterp
+ .extern MterpIPutU64
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref CCCC
movzbq rINSTbl, %rcx # rcx <- BA
@@ -2333,7 +2333,7 @@
andb $0xf, rINSTbl # rINST <- A
leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[A]
movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer
- call SYMBOL(artSet64InstanceFromMterp)
+ call SYMBOL(MterpIPutU64)
testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2348,7 +2348,7 @@
REFRESH_INST 91
movl rINST, OUT_32_ARG2
movq rSELF, OUT_ARG3
- call SYMBOL(MterpIputObject)
+ call SYMBOL(MterpIPutObj)
testb %al, %al
jz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2364,7 +2364,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet8InstanceFromMterp
+ .extern MterpIPutU8
EXPORT_PC
movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC
movzbq rINSTbl, %rcx # rcx<- BA
@@ -2373,7 +2373,7 @@
andb $0xf, rINSTbl # rINST<- A
GET_VREG OUT_32_ARG2, rINSTq # fp[A]
movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer
- call SYMBOL(artSet8InstanceFromMterp)
+ call SYMBOL(MterpIPutU8)
testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2390,7 +2390,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet8InstanceFromMterp
+ .extern MterpIPutI8
EXPORT_PC
movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC
movzbq rINSTbl, %rcx # rcx<- BA
@@ -2399,7 +2399,7 @@
andb $0xf, rINSTbl # rINST<- A
GET_VREG OUT_32_ARG2, rINSTq # fp[A]
movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer
- call SYMBOL(artSet8InstanceFromMterp)
+ call SYMBOL(MterpIPutI8)
testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2416,7 +2416,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet16InstanceFromMterp
+ .extern MterpIPutU16
EXPORT_PC
movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC
movzbq rINSTbl, %rcx # rcx<- BA
@@ -2425,7 +2425,7 @@
andb $0xf, rINSTbl # rINST<- A
GET_VREG OUT_32_ARG2, rINSTq # fp[A]
movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer
- call SYMBOL(artSet16InstanceFromMterp)
+ call SYMBOL(MterpIPutU16)
testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2442,7 +2442,7 @@
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern artSet16InstanceFromMterp
+ .extern MterpIPutI16
EXPORT_PC
movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC
movzbq rINSTbl, %rcx # rcx<- BA
@@ -2451,7 +2451,7 @@
andb $0xf, rINSTbl # rINST<- A
GET_VREG OUT_32_ARG2, rINSTq # fp[A]
movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer
- call SYMBOL(artSet16InstanceFromMterp)
+ call SYMBOL(MterpIPutI16)
testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2467,12 +2467,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide
*/
/* op vAA, field@BBBB */
- .extern MterpGet32Static
+ .extern MterpSGetU32
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref CCCC
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
- call SYMBOL(MterpGet32Static)
+ call SYMBOL(MterpSGetU32)
movq rSELF, %rcx
cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
@@ -2498,12 +2498,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide
*/
/* op vAA, field@BBBB */
- .extern MterpGet64Static
+ .extern MterpSGetU64
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref CCCC
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
- call SYMBOL(MterpGet64Static)
+ call SYMBOL(MterpSGetU64)
movq rSELF, %rcx
cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
@@ -2530,12 +2530,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide
*/
/* op vAA, field@BBBB */
- .extern MterpGetObjStatic
+ .extern MterpSGetObj
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref CCCC
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
- call SYMBOL(MterpGetObjStatic)
+ call SYMBOL(MterpSGetObj)
movq rSELF, %rcx
cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
@@ -2562,12 +2562,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide
*/
/* op vAA, field@BBBB */
- .extern MterpGetBooleanStatic
+ .extern MterpSGetU8
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref CCCC
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
- call SYMBOL(MterpGetBooleanStatic)
+ call SYMBOL(MterpSGetU8)
movq rSELF, %rcx
cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
@@ -2594,12 +2594,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide
*/
/* op vAA, field@BBBB */
- .extern MterpGetByteStatic
+ .extern MterpSGetI8
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref CCCC
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
- call SYMBOL(MterpGetByteStatic)
+ call SYMBOL(MterpSGetI8)
movq rSELF, %rcx
cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
@@ -2626,12 +2626,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide
*/
/* op vAA, field@BBBB */
- .extern MterpGetCharStatic
+ .extern MterpSGetU16
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref CCCC
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
- call SYMBOL(MterpGetCharStatic)
+ call SYMBOL(MterpSGetU16)
movq rSELF, %rcx
cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
@@ -2658,12 +2658,12 @@
* for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide
*/
/* op vAA, field@BBBB */
- .extern MterpGetShortStatic
+ .extern MterpSGetI16
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref CCCC
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
- call SYMBOL(MterpGetShortStatic)
+ call SYMBOL(MterpSGetI16)
movq rSELF, %rcx
cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
@@ -2689,13 +2689,13 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field@BBBB */
- .extern MterpSet32Static
+ .extern MterpSPutU32
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref BBBB
GET_VREG OUT_32_ARG1, rINSTq # fp[AA]
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3 # self
- call SYMBOL(MterpSet32Static)
+ call SYMBOL(MterpSPutU32)
testb %al, %al
jnz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2709,13 +2709,13 @@
*
*/
/* sput-wide vAA, field@BBBB */
- .extern MterpSet64Static
+ .extern MterpSPutU64
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref BBBB
leaq VREG_ADDRESS(rINSTq), OUT_ARG1 # &fp[AA]
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3 # self
- call SYMBOL(MterpSet64Static)
+ call SYMBOL(MterpSPutU64)
testb %al, %al
jnz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2730,7 +2730,7 @@
REFRESH_INST 105
movq rINSTq, OUT_ARG2
movq rSELF, OUT_ARG3
- call SYMBOL(MterpSputObject)
+ call SYMBOL(MterpSPutObj)
testb %al, %al
jz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2746,13 +2746,13 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field@BBBB */
- .extern MterpSetBooleanStatic
+ .extern MterpSPutU8
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref BBBB
GET_VREG OUT_32_ARG1, rINSTq # fp[AA]
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3 # self
- call SYMBOL(MterpSetBooleanStatic)
+ call SYMBOL(MterpSPutU8)
testb %al, %al
jnz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2769,13 +2769,13 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field@BBBB */
- .extern MterpSetByteStatic
+ .extern MterpSPutI8
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref BBBB
GET_VREG OUT_32_ARG1, rINSTq # fp[AA]
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3 # self
- call SYMBOL(MterpSetByteStatic)
+ call SYMBOL(MterpSPutI8)
testb %al, %al
jnz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2792,13 +2792,13 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field@BBBB */
- .extern MterpSetCharStatic
+ .extern MterpSPutU16
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref BBBB
GET_VREG OUT_32_ARG1, rINSTq # fp[AA]
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3 # self
- call SYMBOL(MterpSetCharStatic)
+ call SYMBOL(MterpSPutU16)
testb %al, %al
jnz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2815,13 +2815,13 @@
* for: sput, sput-boolean, sput-byte, sput-char, sput-short
*/
/* op vAA, field@BBBB */
- .extern MterpSetShortStatic
+ .extern MterpSPutI16
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref BBBB
GET_VREG OUT_32_ARG1, rINSTq # fp[AA]
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3 # self
- call SYMBOL(MterpSetShortStatic)
+ call SYMBOL(MterpSPutI16)
testb %al, %al
jnz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_iget.S b/runtime/interpreter/mterp/x86/op_iget.S
index e3304ba..0af1bec 100644
--- a/runtime/interpreter/mterp/x86/op_iget.S
+++ b/runtime/interpreter/mterp/x86/op_iget.S
@@ -1,4 +1,4 @@
-%default { "is_object":"0", "helper":"artGet32InstanceFromCode"}
+%default { "is_object":"0", "helper":"MterpIGetU32"}
/*
* General instance field get.
*
diff --git a/runtime/interpreter/mterp/x86/op_iget_boolean.S b/runtime/interpreter/mterp/x86/op_iget_boolean.S
index 9ddad04..ddccc41 100644
--- a/runtime/interpreter/mterp/x86/op_iget_boolean.S
+++ b/runtime/interpreter/mterp/x86/op_iget_boolean.S
@@ -1 +1 @@
-%include "x86/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" }
+%include "x86/op_iget.S" { "helper":"MterpIGetU8" }
diff --git a/runtime/interpreter/mterp/x86/op_iget_byte.S b/runtime/interpreter/mterp/x86/op_iget_byte.S
index 8250788..cd46d3d 100644
--- a/runtime/interpreter/mterp/x86/op_iget_byte.S
+++ b/runtime/interpreter/mterp/x86/op_iget_byte.S
@@ -1 +1 @@
-%include "x86/op_iget.S" { "helper":"artGetByteInstanceFromCode" }
+%include "x86/op_iget.S" { "helper":"MterpIGetI8" }
diff --git a/runtime/interpreter/mterp/x86/op_iget_char.S b/runtime/interpreter/mterp/x86/op_iget_char.S
index e9d2156..9969734 100644
--- a/runtime/interpreter/mterp/x86/op_iget_char.S
+++ b/runtime/interpreter/mterp/x86/op_iget_char.S
@@ -1 +1 @@
-%include "x86/op_iget.S" { "helper":"artGetCharInstanceFromCode" }
+%include "x86/op_iget.S" { "helper":"MterpIGetU16" }
diff --git a/runtime/interpreter/mterp/x86/op_iget_object.S b/runtime/interpreter/mterp/x86/op_iget_object.S
index 3abeefc..3d421fc 100644
--- a/runtime/interpreter/mterp/x86/op_iget_object.S
+++ b/runtime/interpreter/mterp/x86/op_iget_object.S
@@ -1 +1 @@
-%include "x86/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" }
+%include "x86/op_iget.S" { "is_object":"1", "helper":"MterpIGetObj" }
diff --git a/runtime/interpreter/mterp/x86/op_iget_short.S b/runtime/interpreter/mterp/x86/op_iget_short.S
index c8fad89..c7477f5 100644
--- a/runtime/interpreter/mterp/x86/op_iget_short.S
+++ b/runtime/interpreter/mterp/x86/op_iget_short.S
@@ -1 +1 @@
-%include "x86/op_iget.S" { "helper":"artGetShortInstanceFromCode" }
+%include "x86/op_iget.S" { "helper":"MterpIGetI16" }
diff --git a/runtime/interpreter/mterp/x86/op_iget_wide.S b/runtime/interpreter/mterp/x86/op_iget_wide.S
index a5d7e69..da27df9 100644
--- a/runtime/interpreter/mterp/x86/op_iget_wide.S
+++ b/runtime/interpreter/mterp/x86/op_iget_wide.S
@@ -14,7 +14,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(artGet64InstanceFromCode)
+ call SYMBOL(MterpIGetU64)
mov rSELF, %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
diff --git a/runtime/interpreter/mterp/x86/op_iput.S b/runtime/interpreter/mterp/x86/op_iput.S
index c847e2d..4c6603a 100644
--- a/runtime/interpreter/mterp/x86/op_iput.S
+++ b/runtime/interpreter/mterp/x86/op_iput.S
@@ -1,11 +1,11 @@
-%default { "handler":"artSet32InstanceFromMterp" }
+%default { "helper":"MterpIPutU32" }
/*
* General 32-bit instance field put.
*
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern $handler
+ .extern $helper
EXPORT_PC
movzwl 2(rPC), %eax # eax<- 0000CCCC
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -18,7 +18,7 @@
movl %eax, OUT_ARG2(%esp) # fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call SYMBOL($handler)
+ call SYMBOL($helper)
testb %al, %al
jnz MterpPossibleException
RESTORE_IBASE
diff --git a/runtime/interpreter/mterp/x86/op_iput_boolean.S b/runtime/interpreter/mterp/x86/op_iput_boolean.S
index 11cab88..fdd5303 100644
--- a/runtime/interpreter/mterp/x86/op_iput_boolean.S
+++ b/runtime/interpreter/mterp/x86/op_iput_boolean.S
@@ -1 +1 @@
-%include "x86/op_iput.S" { "handler":"artSet8InstanceFromMterp" }
+%include "x86/op_iput.S" { "helper":"MterpIPutU8" }
diff --git a/runtime/interpreter/mterp/x86/op_iput_byte.S b/runtime/interpreter/mterp/x86/op_iput_byte.S
index 11cab88..b81850c 100644
--- a/runtime/interpreter/mterp/x86/op_iput_byte.S
+++ b/runtime/interpreter/mterp/x86/op_iput_byte.S
@@ -1 +1 @@
-%include "x86/op_iput.S" { "handler":"artSet8InstanceFromMterp" }
+%include "x86/op_iput.S" { "helper":"MterpIPutI8" }
diff --git a/runtime/interpreter/mterp/x86/op_iput_char.S b/runtime/interpreter/mterp/x86/op_iput_char.S
index abbf2bd..dde3853 100644
--- a/runtime/interpreter/mterp/x86/op_iput_char.S
+++ b/runtime/interpreter/mterp/x86/op_iput_char.S
@@ -1 +1 @@
-%include "x86/op_iput.S" { "handler":"artSet16InstanceFromMterp" }
+%include "x86/op_iput.S" { "helper":"MterpIPutU16" }
diff --git a/runtime/interpreter/mterp/x86/op_iput_object.S b/runtime/interpreter/mterp/x86/op_iput_object.S
index e013697..56e026e 100644
--- a/runtime/interpreter/mterp/x86/op_iput_object.S
+++ b/runtime/interpreter/mterp/x86/op_iput_object.S
@@ -6,7 +6,7 @@
movl rINST, OUT_ARG2(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
- call SYMBOL(MterpIputObject)
+ call SYMBOL(MterpIPutObj)
testb %al, %al
jz MterpException
RESTORE_IBASE
diff --git a/runtime/interpreter/mterp/x86/op_iput_short.S b/runtime/interpreter/mterp/x86/op_iput_short.S
index abbf2bd..130e875 100644
--- a/runtime/interpreter/mterp/x86/op_iput_short.S
+++ b/runtime/interpreter/mterp/x86/op_iput_short.S
@@ -1 +1 @@
-%include "x86/op_iput.S" { "handler":"artSet16InstanceFromMterp" }
+%include "x86/op_iput.S" { "helper":"MterpIPutI16" }
diff --git a/runtime/interpreter/mterp/x86/op_iput_wide.S b/runtime/interpreter/mterp/x86/op_iput_wide.S
index 122eecf..ea22b91 100644
--- a/runtime/interpreter/mterp/x86/op_iput_wide.S
+++ b/runtime/interpreter/mterp/x86/op_iput_wide.S
@@ -1,5 +1,5 @@
/* iput-wide vA, vB, field@CCCC */
- .extern artSet64InstanceFromMterp
+ .extern MterpIPutU64
EXPORT_PC
movzwl 2(rPC), %eax # eax <- 0000CCCC
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -12,7 +12,7 @@
movl %eax, OUT_ARG2(%esp) # &fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call SYMBOL(artSet64InstanceFromMterp)
+ call SYMBOL(MterpIPutU64)
testb %al, %al
jnz MterpPossibleException
RESTORE_IBASE
diff --git a/runtime/interpreter/mterp/x86/op_sget.S b/runtime/interpreter/mterp/x86/op_sget.S
index 6e42d32..66c7b0b 100644
--- a/runtime/interpreter/mterp/x86/op_sget.S
+++ b/runtime/interpreter/mterp/x86/op_sget.S
@@ -1,4 +1,4 @@
-%default { "is_object":"0", "helper":"MterpGet32Static" }
+%default { "is_object":"0", "helper":"MterpSGetU32" }
/*
* General SGET handler wrapper.
*
diff --git a/runtime/interpreter/mterp/x86/op_sget_boolean.S b/runtime/interpreter/mterp/x86/op_sget_boolean.S
index 5fa2bf0..3936eea 100644
--- a/runtime/interpreter/mterp/x86/op_sget_boolean.S
+++ b/runtime/interpreter/mterp/x86/op_sget_boolean.S
@@ -1 +1 @@
-%include "x86/op_sget.S" {"helper":"MterpGetBooleanStatic"}
+%include "x86/op_sget.S" {"helper":"MterpSGetU8"}
diff --git a/runtime/interpreter/mterp/x86/op_sget_byte.S b/runtime/interpreter/mterp/x86/op_sget_byte.S
index ef812f1..967586d 100644
--- a/runtime/interpreter/mterp/x86/op_sget_byte.S
+++ b/runtime/interpreter/mterp/x86/op_sget_byte.S
@@ -1 +1 @@
-%include "x86/op_sget.S" {"helper":"MterpGetByteStatic"}
+%include "x86/op_sget.S" {"helper":"MterpSGetI8"}
diff --git a/runtime/interpreter/mterp/x86/op_sget_char.S b/runtime/interpreter/mterp/x86/op_sget_char.S
index 3bc34ef..b706f18 100644
--- a/runtime/interpreter/mterp/x86/op_sget_char.S
+++ b/runtime/interpreter/mterp/x86/op_sget_char.S
@@ -1 +1 @@
-%include "x86/op_sget.S" {"helper":"MterpGetCharStatic"}
+%include "x86/op_sget.S" {"helper":"MterpSGetU16"}
diff --git a/runtime/interpreter/mterp/x86/op_sget_object.S b/runtime/interpreter/mterp/x86/op_sget_object.S
index b829e75..eac8836 100644
--- a/runtime/interpreter/mterp/x86/op_sget_object.S
+++ b/runtime/interpreter/mterp/x86/op_sget_object.S
@@ -1 +1 @@
-%include "x86/op_sget.S" {"is_object":"1", "helper":"MterpGetObjStatic"}
+%include "x86/op_sget.S" {"is_object":"1", "helper":"MterpSGetObj"}
diff --git a/runtime/interpreter/mterp/x86/op_sget_short.S b/runtime/interpreter/mterp/x86/op_sget_short.S
index 449cf6f..ee058a6 100644
--- a/runtime/interpreter/mterp/x86/op_sget_short.S
+++ b/runtime/interpreter/mterp/x86/op_sget_short.S
@@ -1 +1 @@
-%include "x86/op_sget.S" {"helper":"MterpGetShortStatic"}
+%include "x86/op_sget.S" {"helper":"MterpSGetI16"}
diff --git a/runtime/interpreter/mterp/x86/op_sget_wide.S b/runtime/interpreter/mterp/x86/op_sget_wide.S
index a605bcf..994cc3a 100644
--- a/runtime/interpreter/mterp/x86/op_sget_wide.S
+++ b/runtime/interpreter/mterp/x86/op_sget_wide.S
@@ -3,7 +3,7 @@
*
*/
/* sget-wide vAA, field@BBBB */
- .extern MterpGet64Static
+ .extern MterpSGetU64
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref CCCC
@@ -11,7 +11,7 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call SYMBOL(MterpGet64Static)
+ call SYMBOL(MterpSGetU64)
movl rSELF, %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
diff --git a/runtime/interpreter/mterp/x86/op_sput.S b/runtime/interpreter/mterp/x86/op_sput.S
index 99f6088..e99e7a7 100644
--- a/runtime/interpreter/mterp/x86/op_sput.S
+++ b/runtime/interpreter/mterp/x86/op_sput.S
@@ -1,4 +1,4 @@
-%default { "helper":"MterpSet32Static"}
+%default { "helper":"MterpSPutU32"}
/*
* General SPUT handler wrapper.
*
diff --git a/runtime/interpreter/mterp/x86/op_sput_boolean.S b/runtime/interpreter/mterp/x86/op_sput_boolean.S
index a7fffda..c6aa7c4 100644
--- a/runtime/interpreter/mterp/x86/op_sput_boolean.S
+++ b/runtime/interpreter/mterp/x86/op_sput_boolean.S
@@ -1 +1 @@
-%include "x86/op_sput.S" {"helper":"MterpSetBooleanStatic"}
+%include "x86/op_sput.S" {"helper":"MterpSPutU8"}
diff --git a/runtime/interpreter/mterp/x86/op_sput_byte.S b/runtime/interpreter/mterp/x86/op_sput_byte.S
index 3a5ff92..fd504a8 100644
--- a/runtime/interpreter/mterp/x86/op_sput_byte.S
+++ b/runtime/interpreter/mterp/x86/op_sput_byte.S
@@ -1 +1 @@
-%include "x86/op_sput.S" {"helper":"MterpSetByteStatic"}
+%include "x86/op_sput.S" {"helper":"MterpSPutI8"}
diff --git a/runtime/interpreter/mterp/x86/op_sput_char.S b/runtime/interpreter/mterp/x86/op_sput_char.S
index 565cc2a..b4d0997 100644
--- a/runtime/interpreter/mterp/x86/op_sput_char.S
+++ b/runtime/interpreter/mterp/x86/op_sput_char.S
@@ -1 +1 @@
-%include "x86/op_sput.S" {"helper":"MterpSetCharStatic"}
+%include "x86/op_sput.S" {"helper":"MterpSPutU16"}
diff --git a/runtime/interpreter/mterp/x86/op_sput_object.S b/runtime/interpreter/mterp/x86/op_sput_object.S
index 0db5177..941b072 100644
--- a/runtime/interpreter/mterp/x86/op_sput_object.S
+++ b/runtime/interpreter/mterp/x86/op_sput_object.S
@@ -6,7 +6,7 @@
movl rINST, OUT_ARG2(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
- call SYMBOL(MterpSputObject)
+ call SYMBOL(MterpSPutObj)
testb %al, %al
jz MterpException
RESTORE_IBASE
diff --git a/runtime/interpreter/mterp/x86/op_sput_short.S b/runtime/interpreter/mterp/x86/op_sput_short.S
index 85c3441..eba01bd 100644
--- a/runtime/interpreter/mterp/x86/op_sput_short.S
+++ b/runtime/interpreter/mterp/x86/op_sput_short.S
@@ -1 +1 @@
-%include "x86/op_sput.S" {"helper":"MterpSetShortStatic"}
+%include "x86/op_sput.S" {"helper":"MterpSPutI16"}
diff --git a/runtime/interpreter/mterp/x86/op_sput_wide.S b/runtime/interpreter/mterp/x86/op_sput_wide.S
index 8cc7e28..f581507 100644
--- a/runtime/interpreter/mterp/x86/op_sput_wide.S
+++ b/runtime/interpreter/mterp/x86/op_sput_wide.S
@@ -3,7 +3,7 @@
*
*/
/* sput-wide vAA, field@BBBB */
- .extern MterpSet64Static
+ .extern MterpSPutU64
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
@@ -13,7 +13,7 @@
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call SYMBOL(MterpSet64Static)
+ call SYMBOL(MterpSPutU64)
testb %al, %al
jnz MterpException
RESTORE_IBASE
diff --git a/runtime/interpreter/mterp/x86_64/op_iget.S b/runtime/interpreter/mterp/x86_64/op_iget.S
index df43efe..5c6cab6 100644
--- a/runtime/interpreter/mterp/x86_64/op_iget.S
+++ b/runtime/interpreter/mterp/x86_64/op_iget.S
@@ -1,4 +1,4 @@
-%default { "is_object":"0", "helper":"artGet32InstanceFromCode", "wide":"0"}
+%default { "is_object":"0", "helper":"MterpIGetU32", "wide":"0"}
/*
* General instance field get.
*
diff --git a/runtime/interpreter/mterp/x86_64/op_iget_boolean.S b/runtime/interpreter/mterp/x86_64/op_iget_boolean.S
index 6ac5523..18e9264 100644
--- a/runtime/interpreter/mterp/x86_64/op_iget_boolean.S
+++ b/runtime/interpreter/mterp/x86_64/op_iget_boolean.S
@@ -1 +1 @@
-%include "x86_64/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" }
+%include "x86_64/op_iget.S" { "helper":"MterpIGetU8" }
diff --git a/runtime/interpreter/mterp/x86_64/op_iget_byte.S b/runtime/interpreter/mterp/x86_64/op_iget_byte.S
index 6a861b1..bec0ad5 100644
--- a/runtime/interpreter/mterp/x86_64/op_iget_byte.S
+++ b/runtime/interpreter/mterp/x86_64/op_iget_byte.S
@@ -1 +1 @@
-%include "x86_64/op_iget.S" { "helper":"artGetByteInstanceFromCode" }
+%include "x86_64/op_iget.S" { "helper":"MterpIGetI8" }
diff --git a/runtime/interpreter/mterp/x86_64/op_iget_char.S b/runtime/interpreter/mterp/x86_64/op_iget_char.S
index 021a0f1..5e22b88 100644
--- a/runtime/interpreter/mterp/x86_64/op_iget_char.S
+++ b/runtime/interpreter/mterp/x86_64/op_iget_char.S
@@ -1 +1 @@
-%include "x86_64/op_iget.S" { "helper":"artGetCharInstanceFromCode" }
+%include "x86_64/op_iget.S" { "helper":"MterpIGetU16" }
diff --git a/runtime/interpreter/mterp/x86_64/op_iget_object.S b/runtime/interpreter/mterp/x86_64/op_iget_object.S
index d92bc9c..bcef1d2 100644
--- a/runtime/interpreter/mterp/x86_64/op_iget_object.S
+++ b/runtime/interpreter/mterp/x86_64/op_iget_object.S
@@ -1 +1 @@
-%include "x86_64/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" }
+%include "x86_64/op_iget.S" { "is_object":"1", "helper":"MterpIGetObj" }
diff --git a/runtime/interpreter/mterp/x86_64/op_iget_short.S b/runtime/interpreter/mterp/x86_64/op_iget_short.S
index f158bea..14c49f7 100644
--- a/runtime/interpreter/mterp/x86_64/op_iget_short.S
+++ b/runtime/interpreter/mterp/x86_64/op_iget_short.S
@@ -1 +1 @@
-%include "x86_64/op_iget.S" { "helper":"artGetShortInstanceFromCode" }
+%include "x86_64/op_iget.S" { "helper":"MterpIGetI16" }
diff --git a/runtime/interpreter/mterp/x86_64/op_iget_wide.S b/runtime/interpreter/mterp/x86_64/op_iget_wide.S
index 74bb9ff..d9d1744 100644
--- a/runtime/interpreter/mterp/x86_64/op_iget_wide.S
+++ b/runtime/interpreter/mterp/x86_64/op_iget_wide.S
@@ -1 +1 @@
-%include "x86_64/op_iget.S" { "helper":"artGet64InstanceFromCode", "wide":"1" }
+%include "x86_64/op_iget.S" { "helper":"MterpIGetU64", "wide":"1" }
diff --git a/runtime/interpreter/mterp/x86_64/op_iput.S b/runtime/interpreter/mterp/x86_64/op_iput.S
index 6b7cb1c..12affdb 100644
--- a/runtime/interpreter/mterp/x86_64/op_iput.S
+++ b/runtime/interpreter/mterp/x86_64/op_iput.S
@@ -1,11 +1,11 @@
-%default { "handler":"artSet32InstanceFromMterp"}
+%default { "helper":"MterpIPutU32"}
/*
* General 32-bit instance field put.
*
* for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
*/
/* op vA, vB, field@CCCC */
- .extern $handler
+ .extern $helper
EXPORT_PC
movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC
movzbq rINSTbl, %rcx # rcx<- BA
@@ -14,7 +14,7 @@
andb $$0xf, rINSTbl # rINST<- A
GET_VREG OUT_32_ARG2, rINSTq # fp[A]
movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer
- call SYMBOL($handler)
+ call SYMBOL($helper)
testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86_64/op_iput_boolean.S b/runtime/interpreter/mterp/x86_64/op_iput_boolean.S
index cb4b1cd..06bbd70 100644
--- a/runtime/interpreter/mterp/x86_64/op_iput_boolean.S
+++ b/runtime/interpreter/mterp/x86_64/op_iput_boolean.S
@@ -1 +1 @@
-%include "x86_64/op_iput.S" { "handler":"artSet8InstanceFromMterp" }
+%include "x86_64/op_iput.S" { "helper":"MterpIPutU8" }
diff --git a/runtime/interpreter/mterp/x86_64/op_iput_byte.S b/runtime/interpreter/mterp/x86_64/op_iput_byte.S
index cb4b1cd..53f9008 100644
--- a/runtime/interpreter/mterp/x86_64/op_iput_byte.S
+++ b/runtime/interpreter/mterp/x86_64/op_iput_byte.S
@@ -1 +1 @@
-%include "x86_64/op_iput.S" { "handler":"artSet8InstanceFromMterp" }
+%include "x86_64/op_iput.S" { "helper":"MterpIPutI8" }
diff --git a/runtime/interpreter/mterp/x86_64/op_iput_char.S b/runtime/interpreter/mterp/x86_64/op_iput_char.S
index b4e147c..4736f5e 100644
--- a/runtime/interpreter/mterp/x86_64/op_iput_char.S
+++ b/runtime/interpreter/mterp/x86_64/op_iput_char.S
@@ -1 +1 @@
-%include "x86_64/op_iput.S" { "handler":"artSet16InstanceFromMterp" }
+%include "x86_64/op_iput.S" { "helper":"MterpIPutU16" }
diff --git a/runtime/interpreter/mterp/x86_64/op_iput_object.S b/runtime/interpreter/mterp/x86_64/op_iput_object.S
index 828712d..22648cd 100644
--- a/runtime/interpreter/mterp/x86_64/op_iput_object.S
+++ b/runtime/interpreter/mterp/x86_64/op_iput_object.S
@@ -4,7 +4,7 @@
REFRESH_INST ${opnum}
movl rINST, OUT_32_ARG2
movq rSELF, OUT_ARG3
- call SYMBOL(MterpIputObject)
+ call SYMBOL(MterpIPutObj)
testb %al, %al
jz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86_64/op_iput_short.S b/runtime/interpreter/mterp/x86_64/op_iput_short.S
index b4e147c..dca5735 100644
--- a/runtime/interpreter/mterp/x86_64/op_iput_short.S
+++ b/runtime/interpreter/mterp/x86_64/op_iput_short.S
@@ -1 +1 @@
-%include "x86_64/op_iput.S" { "handler":"artSet16InstanceFromMterp" }
+%include "x86_64/op_iput.S" { "helper":"MterpIPutI16" }
diff --git a/runtime/interpreter/mterp/x86_64/op_iput_wide.S b/runtime/interpreter/mterp/x86_64/op_iput_wide.S
index e59717b..4f8c47c 100644
--- a/runtime/interpreter/mterp/x86_64/op_iput_wide.S
+++ b/runtime/interpreter/mterp/x86_64/op_iput_wide.S
@@ -1,5 +1,5 @@
/* iput-wide vA, vB, field@CCCC */
- .extern artSet64InstanceFromMterp
+ .extern MterpIPutU64
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref CCCC
movzbq rINSTbl, %rcx # rcx <- BA
@@ -8,7 +8,7 @@
andb $$0xf, rINSTbl # rINST <- A
leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[A]
movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer
- call SYMBOL(artSet64InstanceFromMterp)
+ call SYMBOL(MterpIPutU64)
testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86_64/op_sget.S b/runtime/interpreter/mterp/x86_64/op_sget.S
index e996c77..c15ac1e 100644
--- a/runtime/interpreter/mterp/x86_64/op_sget.S
+++ b/runtime/interpreter/mterp/x86_64/op_sget.S
@@ -1,4 +1,4 @@
-%default { "is_object":"0", "helper":"MterpGet32Static", "wide":"0" }
+%default { "is_object":"0", "helper":"MterpSGetU32", "wide":"0" }
/*
* General SGET handler wrapper.
*
diff --git a/runtime/interpreter/mterp/x86_64/op_sget_boolean.S b/runtime/interpreter/mterp/x86_64/op_sget_boolean.S
index ee772ad..e5a4e41 100644
--- a/runtime/interpreter/mterp/x86_64/op_sget_boolean.S
+++ b/runtime/interpreter/mterp/x86_64/op_sget_boolean.S
@@ -1 +1 @@
-%include "x86_64/op_sget.S" {"helper":"MterpGetBooleanStatic"}
+%include "x86_64/op_sget.S" {"helper":"MterpSGetU8"}
diff --git a/runtime/interpreter/mterp/x86_64/op_sget_byte.S b/runtime/interpreter/mterp/x86_64/op_sget_byte.S
index f65ea49..4602f7d 100644
--- a/runtime/interpreter/mterp/x86_64/op_sget_byte.S
+++ b/runtime/interpreter/mterp/x86_64/op_sget_byte.S
@@ -1 +1 @@
-%include "x86_64/op_sget.S" {"helper":"MterpGetByteStatic"}
+%include "x86_64/op_sget.S" {"helper":"MterpSGetI8"}
diff --git a/runtime/interpreter/mterp/x86_64/op_sget_char.S b/runtime/interpreter/mterp/x86_64/op_sget_char.S
index 3972551..a094a54 100644
--- a/runtime/interpreter/mterp/x86_64/op_sget_char.S
+++ b/runtime/interpreter/mterp/x86_64/op_sget_char.S
@@ -1 +1 @@
-%include "x86_64/op_sget.S" {"helper":"MterpGetCharStatic"}
+%include "x86_64/op_sget.S" {"helper":"MterpSGetU16"}
diff --git a/runtime/interpreter/mterp/x86_64/op_sget_object.S b/runtime/interpreter/mterp/x86_64/op_sget_object.S
index a0bbfd8..94597b1 100644
--- a/runtime/interpreter/mterp/x86_64/op_sget_object.S
+++ b/runtime/interpreter/mterp/x86_64/op_sget_object.S
@@ -1 +1 @@
-%include "x86_64/op_sget.S" {"is_object":"1", "helper":"MterpGetObjStatic"}
+%include "x86_64/op_sget.S" {"is_object":"1", "helper":"MterpSGetObj"}
diff --git a/runtime/interpreter/mterp/x86_64/op_sget_short.S b/runtime/interpreter/mterp/x86_64/op_sget_short.S
index df212dc..dee5c24 100644
--- a/runtime/interpreter/mterp/x86_64/op_sget_short.S
+++ b/runtime/interpreter/mterp/x86_64/op_sget_short.S
@@ -1 +1 @@
-%include "x86_64/op_sget.S" {"helper":"MterpGetShortStatic"}
+%include "x86_64/op_sget.S" {"helper":"MterpSGetI16"}
diff --git a/runtime/interpreter/mterp/x86_64/op_sget_wide.S b/runtime/interpreter/mterp/x86_64/op_sget_wide.S
index 1e98e28..65ddb8a 100644
--- a/runtime/interpreter/mterp/x86_64/op_sget_wide.S
+++ b/runtime/interpreter/mterp/x86_64/op_sget_wide.S
@@ -1 +1 @@
-%include "x86_64/op_sget.S" {"helper":"MterpGet64Static", "wide":"1"}
+%include "x86_64/op_sget.S" {"helper":"MterpSGetU64", "wide":"1"}
diff --git a/runtime/interpreter/mterp/x86_64/op_sput.S b/runtime/interpreter/mterp/x86_64/op_sput.S
index 9705619..9a33d52 100644
--- a/runtime/interpreter/mterp/x86_64/op_sput.S
+++ b/runtime/interpreter/mterp/x86_64/op_sput.S
@@ -1,4 +1,4 @@
-%default { "helper":"MterpSet32Static"}
+%default { "helper":"MterpSPutU32"}
/*
* General SPUT handler wrapper.
*
diff --git a/runtime/interpreter/mterp/x86_64/op_sput_boolean.S b/runtime/interpreter/mterp/x86_64/op_sput_boolean.S
index 8bf4a62..ea9acbf 100644
--- a/runtime/interpreter/mterp/x86_64/op_sput_boolean.S
+++ b/runtime/interpreter/mterp/x86_64/op_sput_boolean.S
@@ -1 +1 @@
-%include "x86_64/op_sput.S" {"helper":"MterpSetBooleanStatic"}
+%include "x86_64/op_sput.S" {"helper":"MterpSPutU8"}
diff --git a/runtime/interpreter/mterp/x86_64/op_sput_byte.S b/runtime/interpreter/mterp/x86_64/op_sput_byte.S
index 5bb26eb..62c9e20 100644
--- a/runtime/interpreter/mterp/x86_64/op_sput_byte.S
+++ b/runtime/interpreter/mterp/x86_64/op_sput_byte.S
@@ -1 +1 @@
-%include "x86_64/op_sput.S" {"helper":"MterpSetByteStatic"}
+%include "x86_64/op_sput.S" {"helper":"MterpSPutI8"}
diff --git a/runtime/interpreter/mterp/x86_64/op_sput_char.S b/runtime/interpreter/mterp/x86_64/op_sput_char.S
index 42b244e..ab0196e 100644
--- a/runtime/interpreter/mterp/x86_64/op_sput_char.S
+++ b/runtime/interpreter/mterp/x86_64/op_sput_char.S
@@ -1 +1 @@
-%include "x86_64/op_sput.S" {"helper":"MterpSetCharStatic"}
+%include "x86_64/op_sput.S" {"helper":"MterpSPutU16"}
diff --git a/runtime/interpreter/mterp/x86_64/op_sput_object.S b/runtime/interpreter/mterp/x86_64/op_sput_object.S
index eb5a376..8a47074 100644
--- a/runtime/interpreter/mterp/x86_64/op_sput_object.S
+++ b/runtime/interpreter/mterp/x86_64/op_sput_object.S
@@ -4,7 +4,7 @@
REFRESH_INST ${opnum}
movq rINSTq, OUT_ARG2
movq rSELF, OUT_ARG3
- call SYMBOL(MterpSputObject)
+ call SYMBOL(MterpSPutObj)
testb %al, %al
jz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86_64/op_sput_short.S b/runtime/interpreter/mterp/x86_64/op_sput_short.S
index 9670092..f73a3fc 100644
--- a/runtime/interpreter/mterp/x86_64/op_sput_short.S
+++ b/runtime/interpreter/mterp/x86_64/op_sput_short.S
@@ -1 +1 @@
-%include "x86_64/op_sput.S" {"helper":"MterpSetShortStatic"}
+%include "x86_64/op_sput.S" {"helper":"MterpSPutI16"}
diff --git a/runtime/interpreter/mterp/x86_64/op_sput_wide.S b/runtime/interpreter/mterp/x86_64/op_sput_wide.S
index a21bcb5..464d169 100644
--- a/runtime/interpreter/mterp/x86_64/op_sput_wide.S
+++ b/runtime/interpreter/mterp/x86_64/op_sput_wide.S
@@ -3,13 +3,13 @@
*
*/
/* sput-wide vAA, field@BBBB */
- .extern MterpSet64Static
+ .extern MterpSPutU64
EXPORT_PC
movzwq 2(rPC), OUT_ARG0 # field ref BBBB
leaq VREG_ADDRESS(rINSTq), OUT_ARG1 # &fp[AA]
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3 # self
- call SYMBOL(MterpSet64Static)
+ call SYMBOL(MterpSPutU64)
testb %al, %al
jnz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index feabc94..d4b51af 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -517,24 +517,23 @@
result->SetZ(class_name == nullptr);
}
-static std::unique_ptr<MemMap> FindAndExtractEntry(const std::string& jar_file,
- const char* entry_name,
- size_t* size,
- std::string* error_msg) {
+static MemMap FindAndExtractEntry(const std::string& jar_file,
+ const char* entry_name,
+ size_t* size,
+ std::string* error_msg) {
CHECK(size != nullptr);
std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(jar_file.c_str(), error_msg));
if (zip_archive == nullptr) {
- return nullptr;
+ return MemMap::Invalid();
}
std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(entry_name, error_msg));
if (zip_entry == nullptr) {
- return nullptr;
+ return MemMap::Invalid();
}
- std::unique_ptr<MemMap> tmp_map(
- zip_entry->ExtractToMemMap(jar_file.c_str(), entry_name, error_msg));
- if (tmp_map == nullptr) {
- return nullptr;
+ MemMap tmp_map = zip_entry->ExtractToMemMap(jar_file.c_str(), entry_name, error_msg);
+ if (!tmp_map.IsValid()) {
+ return MemMap::Invalid();
}
// OK, from here everything seems fine.
@@ -577,18 +576,18 @@
return;
}
- std::unique_ptr<MemMap> mem_map;
+ MemMap mem_map;
size_t map_size;
std::string last_error_msg; // Only store the last message (we could concatenate).
for (const std::string& jar_file : split) {
mem_map = FindAndExtractEntry(jar_file, resource_cstr, &map_size, &last_error_msg);
- if (mem_map != nullptr) {
+ if (mem_map.IsValid()) {
break;
}
}
- if (mem_map == nullptr) {
+ if (!mem_map.IsValid()) {
// Didn't find it. There's a good chance this will be the same at runtime, but still
// conservatively abort the transaction here.
AbortTransactionOrFail(self,
@@ -607,9 +606,9 @@
return;
}
// Copy in content.
- memcpy(h_array->GetData(), mem_map->Begin(), map_size);
+ memcpy(h_array->GetData(), mem_map.Begin(), map_size);
// Be proactive releasing memory.
- mem_map.reset();
+ mem_map.Reset();
// Create a ByteArrayInputStream.
Handle<mirror::Class> h_class(hs.NewHandle(
@@ -1969,7 +1968,7 @@
const auto& iter = invoke_handlers_.find(name);
if (iter != invoke_handlers_.end()) {
// Clear out the result in case it's not zeroed out.
- result->SetL(0);
+ result->SetL(nullptr);
// Push the shadow frame. This is so the failing method can be seen in abort dumps.
self->PushShadowFrame(shadow_frame);
@@ -1990,7 +1989,7 @@
const auto& iter = jni_handlers_.find(name);
if (iter != jni_handlers_.end()) {
// Clear out the result in case it's not zeroed out.
- result->SetL(0);
+ result->SetL(nullptr);
(*iter->second)(self, method, receiver, args, result);
} else if (Runtime::Current()->IsActiveTransaction()) {
AbortTransactionF(self, "Attempt to invoke native method in non-started runtime: %s",
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index 9409b76..0353ea7 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -1159,7 +1159,7 @@
}
basket.className = Dbg::GetClassName(basket.locationClass.Get());
basket.exceptionClass.Assign(exception_object->GetClass());
- basket.caught = (pCatchLoc->method != 0);
+ basket.caught = (pCatchLoc->method != nullptr);
basket.thisPtr.Assign(thisPtr);
/* don't try to post an exception caused by the debugger */
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index b92affa..d9c7900 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -205,15 +205,16 @@
// We could do PC-relative addressing to avoid this problem, but that
// would require reserving code and data area before submitting, which
// means more windows for the code memory to be RWX.
- std::unique_ptr<MemMap> data_map(MemMap::MapAnonymous(
- "data-code-cache", nullptr,
+ MemMap data_map = MemMap::MapAnonymous(
+ "data-code-cache",
+ /* addr */ nullptr,
max_capacity,
kProtData,
/* low_4gb */ true,
/* reuse */ false,
&error_str,
- use_ashmem));
- if (data_map == nullptr) {
+ use_ashmem);
+ if (!data_map.IsValid()) {
std::ostringstream oss;
oss << "Failed to create read write cache: " << error_str << " size=" << max_capacity;
*error_msg = oss.str();
@@ -229,26 +230,23 @@
size_t data_size = max_capacity / 2;
size_t code_size = max_capacity - data_size;
DCHECK_EQ(code_size + data_size, max_capacity);
- uint8_t* divider = data_map->Begin() + data_size;
+ uint8_t* divider = data_map.Begin() + data_size;
- MemMap* code_map = data_map->RemapAtEnd(
- divider,
- "jit-code-cache",
- memmap_flags_prot_code | PROT_WRITE,
- &error_str, use_ashmem);
- if (code_map == nullptr) {
+ MemMap code_map = data_map.RemapAtEnd(
+ divider, "jit-code-cache", memmap_flags_prot_code | PROT_WRITE, &error_str, use_ashmem);
+ if (!code_map.IsValid()) {
std::ostringstream oss;
oss << "Failed to create read write execute cache: " << error_str << " size=" << max_capacity;
*error_msg = oss.str();
return nullptr;
}
- DCHECK_EQ(code_map->Begin(), divider);
+ DCHECK_EQ(code_map.Begin(), divider);
data_size = initial_capacity / 2;
code_size = initial_capacity - data_size;
DCHECK_EQ(code_size + data_size, initial_capacity);
return new JitCodeCache(
- code_map,
- data_map.release(),
+ std::move(code_map),
+ std::move(data_map),
code_size,
data_size,
max_capacity,
@@ -256,8 +254,8 @@
memmap_flags_prot_code);
}
-JitCodeCache::JitCodeCache(MemMap* code_map,
- MemMap* data_map,
+JitCodeCache::JitCodeCache(MemMap&& code_map,
+ MemMap&& data_map,
size_t initial_code_capacity,
size_t initial_data_capacity,
size_t max_capacity,
@@ -266,8 +264,8 @@
: lock_("Jit code cache", kJitCodeCacheLock),
lock_cond_("Jit code cache condition variable", lock_),
collection_in_progress_(false),
- code_map_(code_map),
- data_map_(data_map),
+ code_map_(std::move(code_map)),
+ data_map_(std::move(data_map)),
max_capacity_(max_capacity),
current_capacity_(initial_code_capacity + initial_data_capacity),
code_end_(initial_code_capacity),
@@ -287,8 +285,8 @@
memmap_flags_prot_code_(memmap_flags_prot_code) {
DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity);
- code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/);
- data_mspace_ = create_mspace_with_base(data_map_->Begin(), data_end_, false /*locked*/);
+ code_mspace_ = create_mspace_with_base(code_map_.Begin(), code_end_, false /*locked*/);
+ data_mspace_ = create_mspace_with_base(data_map_.Begin(), data_end_, false /*locked*/);
if (code_mspace_ == nullptr || data_mspace_ == nullptr) {
PLOG(FATAL) << "create_mspace_with_base failed";
@@ -298,13 +296,13 @@
CheckedCall(mprotect,
"mprotect jit code cache",
- code_map_->Begin(),
- code_map_->Size(),
+ code_map_.Begin(),
+ code_map_.Size(),
memmap_flags_prot_code_);
CheckedCall(mprotect,
"mprotect jit data cache",
- data_map_->Begin(),
- data_map_->Size(),
+ data_map_.Begin(),
+ data_map_.Size(),
kProtData);
VLOG(jit) << "Created jit code cache: initial data size="
@@ -316,7 +314,7 @@
JitCodeCache::~JitCodeCache() {}
bool JitCodeCache::ContainsPc(const void* ptr) const {
- return code_map_->Begin() <= ptr && ptr < code_map_->End();
+ return code_map_.Begin() <= ptr && ptr < code_map_.End();
}
bool JitCodeCache::WillExecuteJitCode(ArtMethod* method) {
@@ -387,8 +385,8 @@
CheckedCall(
mprotect,
"make code writable",
- code_cache_->code_map_->Begin(),
- code_cache_->code_map_->Size(),
+ code_cache_->code_map_.Begin(),
+ code_cache_->code_map_.Size(),
code_cache_->memmap_flags_prot_code_ | PROT_WRITE);
}
@@ -397,8 +395,8 @@
CheckedCall(
mprotect,
"make code protected",
- code_cache_->code_map_->Begin(),
- code_cache_->code_map_->Size(),
+ code_cache_->code_map_.Begin(),
+ code_cache_->code_map_.Size(),
code_cache_->memmap_flags_prot_code_);
}
@@ -608,17 +606,17 @@
void JitCodeCache::FreeAllMethodHeaders(
const std::unordered_set<OatQuickMethodHeader*>& method_headers) {
- {
- MutexLock mu(Thread::Current(), *Locks::cha_lock_);
- Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis()
- ->RemoveDependentsWithMethodHeaders(method_headers);
- }
-
// We need to remove entries in method_headers from CHA dependencies
// first since once we do FreeCode() below, the memory can be reused
// so it's possible for the same method_header to start representing
// different compile code.
MutexLock mu(Thread::Current(), lock_);
+ {
+ MutexLock mu2(Thread::Current(), *Locks::cha_lock_);
+ Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis()
+ ->RemoveDependentsWithMethodHeaders(method_headers);
+ }
+
ScopedCodeCacheWrite scc(this);
for (const OatQuickMethodHeader* method_header : method_headers) {
FreeCodeAndData(method_header->GetCode());
@@ -742,6 +740,18 @@
method->SetCounter(std::min(jit_warmup_threshold - 1, 1));
}
+void JitCodeCache::WaitForPotentialCollectionToCompleteRunnable(Thread* self) {
+ while (collection_in_progress_) {
+ lock_.Unlock(self);
+ {
+ ScopedThreadSuspension sts(self, kSuspended);
+ MutexLock mu(self, lock_);
+ WaitForPotentialCollectionToComplete(self);
+ }
+ lock_.Lock(self);
+ }
+}
+
uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
ArtMethod* method,
uint8_t* stack_map,
@@ -755,6 +765,13 @@
const ArenaSet<ArtMethod*>&
cha_single_implementation_list) {
DCHECK(!method->IsNative() || !osr);
+
+ if (!method->IsNative()) {
+ // We need to do this before grabbing the lock_ because it needs to be able to see the string
+ // InternTable. Native methods do not have roots.
+ DCheckRootsAreValid(roots);
+ }
+
size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
// Ensure the header ends up at expected instruction alignment.
size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment);
@@ -763,44 +780,45 @@
OatQuickMethodHeader* method_header = nullptr;
uint8_t* code_ptr = nullptr;
uint8_t* memory = nullptr;
+ MutexLock mu(self, lock_);
+ // We need to make sure that there will be no jit-gcs going on and wait for any ongoing one to
+ // finish.
+ WaitForPotentialCollectionToCompleteRunnable(self);
{
- ScopedThreadSuspension sts(self, kSuspended);
- MutexLock mu(self, lock_);
- WaitForPotentialCollectionToComplete(self);
- {
- ScopedCodeCacheWrite scc(this);
- memory = AllocateCode(total_size);
- if (memory == nullptr) {
- return nullptr;
- }
- code_ptr = memory + header_size;
+ ScopedCodeCacheWrite scc(this);
+ memory = AllocateCode(total_size);
+ if (memory == nullptr) {
+ return nullptr;
+ }
+ code_ptr = memory + header_size;
- std::copy(code, code + code_size, code_ptr);
- method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
- new (method_header) OatQuickMethodHeader(
- (stack_map != nullptr) ? code_ptr - stack_map : 0u,
- code_size);
- // Flush caches before we remove write permission because some ARMv8 Qualcomm kernels may
- // trigger a segfault if a page fault occurs when requesting a cache maintenance operation.
- // This is a kernel bug that we need to work around until affected devices (e.g. Nexus 5X and
- // 6P) stop being supported or their kernels are fixed.
- //
- // For reference, this behavior is caused by this commit:
- // https://android.googlesource.com/kernel/msm/+/3fbe6bc28a6b9939d0650f2f17eb5216c719950c
- FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
- reinterpret_cast<char*>(code_ptr + code_size));
- DCHECK(!Runtime::Current()->IsAotCompiler());
- if (has_should_deoptimize_flag) {
- method_header->SetHasShouldDeoptimizeFlag();
- }
+ std::copy(code, code + code_size, code_ptr);
+ method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
+ new (method_header) OatQuickMethodHeader(
+ (stack_map != nullptr) ? code_ptr - stack_map : 0u,
+ code_size);
+ // Flush caches before we remove write permission because some ARMv8 Qualcomm kernels may
+ // trigger a segfault if a page fault occurs when requesting a cache maintenance operation.
+ // This is a kernel bug that we need to work around until affected devices (e.g. Nexus 5X and
+ // 6P) stop being supported or their kernels are fixed.
+ //
+ // For reference, this behavior is caused by this commit:
+ // https://android.googlesource.com/kernel/msm/+/3fbe6bc28a6b9939d0650f2f17eb5216c719950c
+ FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
+ reinterpret_cast<char*>(code_ptr + code_size));
+ DCHECK(!Runtime::Current()->IsAotCompiler());
+ if (has_should_deoptimize_flag) {
+ method_header->SetHasShouldDeoptimizeFlag();
}
number_of_compilations_++;
}
// We need to update the entry point in the runnable state for the instrumentation.
{
- // Need cha_lock_ for checking all single-implementation flags and register
- // dependencies.
+ // The following needs to be guarded by cha_lock_ also. Otherwise it's possible that the
+ // compiled code is considered invalidated by some class linking, but below we still make the
+ // compiled code valid for the method. Need cha_lock_ for checking all single-implementation
+ // flags and register dependencies.
MutexLock cha_mu(self, *Locks::cha_lock_);
bool single_impl_still_valid = true;
for (ArtMethod* single_impl : cha_single_implementation_list) {
@@ -826,16 +844,6 @@
single_impl, method, method_header);
}
- if (!method->IsNative()) {
- // We need to do this before grabbing the lock_ because it needs to be able to see the string
- // InternTable. Native methods do not have roots.
- DCheckRootsAreValid(roots);
- }
-
- // The following needs to be guarded by cha_lock_ also. Otherwise it's
- // possible that the compiled code is considered invalidated by some class linking,
- // but below we still make the compiled code valid for the method.
- MutexLock mu(self, lock_);
if (UNLIKELY(method->IsNative())) {
auto it = jni_stubs_map_.find(JniStubKey(method));
DCHECK(it != jni_stubs_map_.end())
@@ -867,11 +875,6 @@
method, method_header->GetEntryPoint());
}
}
- if (collection_in_progress_) {
- // We need to update the live bitmap if there is a GC to ensure it sees this new
- // code.
- GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr));
- }
VLOG(jit)
<< "JIT added (osr=" << std::boolalpha << osr << std::noboolalpha << ") "
<< ArtMethod::PrettyMethod(method) << "@" << method
@@ -1232,8 +1235,8 @@
number_of_collections_++;
live_bitmap_.reset(CodeCacheBitmap::Create(
"code-cache-bitmap",
- reinterpret_cast<uintptr_t>(code_map_->Begin()),
- reinterpret_cast<uintptr_t>(code_map_->Begin() + current_capacity_ / 2)));
+ reinterpret_cast<uintptr_t>(code_map_.Begin()),
+ reinterpret_cast<uintptr_t>(code_map_.Begin() + current_capacity_ / 2)));
collection_in_progress_ = true;
}
}
@@ -1605,12 +1608,12 @@
if (code_mspace_ == mspace) {
size_t result = code_end_;
code_end_ += increment;
- return reinterpret_cast<void*>(result + code_map_->Begin());
+ return reinterpret_cast<void*>(result + code_map_.Begin());
} else {
DCHECK_EQ(data_mspace_, mspace);
size_t result = data_end_;
data_end_ += increment;
- return reinterpret_cast<void*>(result + data_map_->Begin());
+ return reinterpret_cast<void*>(result + data_map_.Begin());
}
}
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 29f9c9c..a4a0f8f 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -28,6 +28,7 @@
#include "base/atomic.h"
#include "base/histogram.h"
#include "base/macros.h"
+#include "base/mem_map.h"
#include "base/mutex.h"
#include "base/safe_map.h"
@@ -39,7 +40,6 @@
class InlineCache;
class IsMarkedVisitor;
class JitJniStubTestHelper;
-class MemMap;
class OatQuickMethodHeader;
struct ProfileMethodInfo;
class ProfilingInfo;
@@ -279,8 +279,8 @@
private:
// Take ownership of maps.
- JitCodeCache(MemMap* code_map,
- MemMap* data_map,
+ JitCodeCache(MemMap&& code_map,
+ MemMap&& data_map,
size_t initial_code_capacity,
size_t initial_data_capacity,
size_t max_capacity,
@@ -314,6 +314,12 @@
REQUIRES(lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // If a collection is in progress, wait for it to finish. Must be called with the mutator lock.
+ // The non-mutator lock version should be used if possible. This method will release then
+ // re-acquire the mutator lock.
+ void WaitForPotentialCollectionToCompleteRunnable(Thread* self)
+ REQUIRES(lock_, !Roles::uninterruptible_) REQUIRES_SHARED(Locks::mutator_lock_);
+
// If a collection is in progress, wait for it to finish. Return
// whether the thread actually waited.
bool WaitForPotentialCollectionToComplete(Thread* self)
@@ -390,9 +396,9 @@
// Whether there is a code cache collection in progress.
bool collection_in_progress_ GUARDED_BY(lock_);
// Mem map which holds code.
- std::unique_ptr<MemMap> code_map_;
+ MemMap code_map_;
// Mem map which holds data (stack maps and profiling info).
- std::unique_ptr<MemMap> data_map_;
+ MemMap data_map_;
// The opaque mspace for allocating code.
void* code_mspace_ GUARDED_BY(lock_);
// The opaque mspace for allocating data.
diff --git a/runtime/jni/check_jni.cc b/runtime/jni/check_jni.cc
index 66bd74b..c5e8830 100644
--- a/runtime/jni/check_jni.cc
+++ b/runtime/jni/check_jni.cc
@@ -1462,7 +1462,7 @@
break;
}
}
- return 0;
+ return nullptr;
}
void AbortF(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
diff --git a/runtime/lock_word.h b/runtime/lock_word.h
index ce7fe34..ac7890c 100644
--- a/runtime/lock_word.h
+++ b/runtime/lock_word.h
@@ -210,7 +210,7 @@
void SetReadBarrierState(uint32_t rb_state) {
DCHECK_EQ(rb_state & ~kReadBarrierStateMask, 0U);
- DCHECK(rb_state == ReadBarrier::WhiteState() ||
+ DCHECK(rb_state == ReadBarrier::NonGrayState() ||
rb_state == ReadBarrier::GrayState()) << rb_state;
DCHECK_NE(static_cast<uint32_t>(GetState()), static_cast<uint32_t>(kForwardingAddress));
// Clear and or the bits.
@@ -288,7 +288,7 @@
if (!kUseReadBarrier) {
DCHECK_EQ(rb_state, 0U);
} else {
- DCHECK(rb_state == ReadBarrier::WhiteState() ||
+ DCHECK(rb_state == ReadBarrier::NonGrayState() ||
rb_state == ReadBarrier::GrayState()) << rb_state;
}
}
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 01a32a2..570fc48 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -484,7 +484,7 @@
first_dest_reg,
new_shadow_frame)) {
DCHECK(self->IsExceptionPending());
- result->SetL(0);
+ result->SetL(nullptr);
return false;
}
} else {
@@ -500,7 +500,7 @@
operands,
new_shadow_frame)) {
DCHECK(self->IsExceptionPending());
- result->SetL(0);
+ result->SetL(nullptr);
return false;
}
}
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index bc72517..51dc1a4 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -32,12 +32,12 @@
#include "dex_cache.h"
#include "gc/heap-inl.h"
#include "iftable.h"
-#include "subtype_check.h"
#include "object-inl.h"
#include "object_array.h"
#include "read_barrier-inl.h"
#include "runtime.h"
#include "string.h"
+#include "subtype_check.h"
namespace art {
namespace mirror {
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index ab5fb85..87f4f0a 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -157,12 +157,12 @@
"String dex cache size is not a power of 2.");
// Size of field dex cache. Needs to be a power of 2 for entrypoint assumptions to hold.
- static constexpr size_t kDexCacheFieldCacheSize = 1024;
+ static constexpr size_t kDexCacheFieldCacheSize = 512;
static_assert(IsPowerOfTwo(kDexCacheFieldCacheSize),
"Field dex cache size is not a power of 2.");
// Size of method dex cache. Needs to be a power of 2 for entrypoint assumptions to hold.
- static constexpr size_t kDexCacheMethodCacheSize = 1024;
+ static constexpr size_t kDexCacheMethodCacheSize = 512;
static_assert(IsPowerOfTwo(kDexCacheMethodCacheSize),
"Method dex cache size is not a power of 2.");
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 47f0a29..bd89907 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -119,7 +119,7 @@
inline void Object::AssertReadBarrierState() const {
CHECK(kUseBakerReadBarrier);
Object* obj = const_cast<Object*>(this);
- DCHECK_EQ(obj->GetReadBarrierState(), ReadBarrier::WhiteState())
+ DCHECK_EQ(obj->GetReadBarrierState(), ReadBarrier::NonGrayState())
<< "Bad Baker pointer: obj=" << obj << " rb_state" << obj->GetReadBarrierState();
}
diff --git a/runtime/mirror/object-readbarrier-inl.h b/runtime/mirror/object-readbarrier-inl.h
index df50d06..cc375bd 100644
--- a/runtime/mirror/object-readbarrier-inl.h
+++ b/runtime/mirror/object-readbarrier-inl.h
@@ -168,10 +168,11 @@
expected_lw.SetReadBarrierState(expected_rb_state);
new_lw = lw;
new_lw.SetReadBarrierState(rb_state);
- // ConcurrentCopying::ProcessMarkStackRef uses this with kCasRelease == true.
- // If kCasRelease == true, use a CAS release so that when GC updates all the fields of
- // an object and then changes the object from gray to black, the field updates (stores) will be
- // visible (won't be reordered after this CAS.)
+ // ConcurrentCopying::ProcessMarkStackRef uses this with
+ // `kMemoryOrder` == `std::memory_order_release`.
+ // If `kMemoryOrder` == `std::memory_order_release`, use a CAS release so that when GC updates
+ // all the fields of an object and then changes the object from gray to black (non-gray), the
+ // field updates (stores) will be visible (won't be reordered after this CAS.)
} while (!CasLockWord(expected_lw, new_lw, CASMode::kWeak, kMemoryOrder));
return true;
}
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 2801928..47aded3 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -117,7 +117,7 @@
ALWAYS_INLINE bool AtomicSetMarkBit(uint32_t expected_mark_bit, uint32_t mark_bit)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Assert that the read barrier state is in the default (white) state.
+ // Assert that the read barrier state is in the default (white, i.e. non-gray) state.
ALWAYS_INLINE void AssertReadBarrierState() const REQUIRES_SHARED(Locks::mutator_lock_);
// The verifier treats all interfaces as java.lang.Object and relies on runtime checks in
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index cdba6b2..71fabd0 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -163,33 +163,33 @@
void operator=(const NullableScopedUtfChars&);
};
-static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
+static MemMap AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
if (end <= start) {
ScopedObjectAccess soa(env);
ThrowWrappedIOException("Bad range");
- return nullptr;
+ return MemMap::Invalid();
}
std::string error_message;
size_t length = static_cast<size_t>(end - start);
- std::unique_ptr<MemMap> dex_mem_map(MemMap::MapAnonymous("DEX data",
- nullptr,
- length,
- PROT_READ | PROT_WRITE,
- /* low_4gb */ false,
- /* reuse */ false,
- &error_message));
- if (dex_mem_map == nullptr) {
+ MemMap dex_mem_map = MemMap::MapAnonymous("DEX data",
+ /* addr */ nullptr,
+ length,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_message);
+ if (!dex_mem_map.IsValid()) {
ScopedObjectAccess soa(env);
ThrowWrappedIOException("%s", error_message.c_str());
+ return MemMap::Invalid();
}
return dex_mem_map;
}
-static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map) {
+static const DexFile* CreateDexFile(JNIEnv* env, MemMap&& dex_mem_map) {
std::string location = StringPrintf("Anonymous-DexFile@%p-%p",
- dex_mem_map->Begin(),
- dex_mem_map->End());
+ dex_mem_map.Begin(),
+ dex_mem_map.End());
std::string error_message;
const ArtDexFileLoader dex_file_loader;
std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(location,
@@ -213,7 +213,7 @@
return dex_file.release();
}
-static jobject CreateSingleDexFileCookie(JNIEnv* env, std::unique_ptr<MemMap> data) {
+static jobject CreateSingleDexFileCookie(JNIEnv* env, MemMap&& data) {
std::unique_ptr<const DexFile> dex_file(CreateDexFile(env, std::move(data)));
if (dex_file.get() == nullptr) {
DCHECK(env->ExceptionCheck());
@@ -233,17 +233,17 @@
if (base_address == nullptr) {
ScopedObjectAccess soa(env);
ThrowWrappedIOException("dexFileBuffer not direct");
- return 0;
+ return nullptr;
}
- std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
- if (dex_mem_map == nullptr) {
+ MemMap dex_mem_map = AllocateDexMemoryMap(env, start, end);
+ if (!dex_mem_map.IsValid()) {
DCHECK(Thread::Current()->IsExceptionPending());
- return 0;
+ return nullptr;
}
size_t length = static_cast<size_t>(end - start);
- memcpy(dex_mem_map->Begin(), base_address, length);
+ memcpy(dex_mem_map.Begin(), base_address, length);
return CreateSingleDexFileCookie(env, std::move(dex_mem_map));
}
@@ -252,13 +252,13 @@
jbyteArray buffer,
jint start,
jint end) {
- std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
- if (dex_mem_map == nullptr) {
+ MemMap dex_mem_map = AllocateDexMemoryMap(env, start, end);
+ if (!dex_mem_map.IsValid()) {
DCHECK(Thread::Current()->IsExceptionPending());
- return 0;
+ return nullptr;
}
- auto destination = reinterpret_cast<jbyte*>(dex_mem_map.get()->Begin());
+ auto destination = reinterpret_cast<jbyte*>(dex_mem_map.Begin());
env->GetByteArrayRegion(buffer, start, end - start, destination);
return CreateSingleDexFileCookie(env, std::move(dex_mem_map));
}
@@ -273,7 +273,7 @@
jobjectArray dex_elements) {
ScopedUtfChars sourceName(env, javaSourceName);
if (sourceName.c_str() == nullptr) {
- return 0;
+ return nullptr;
}
Runtime* const runtime = Runtime::Current();
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index f1e267b..7ac4086 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -388,7 +388,7 @@
// as PSS, private/shared dirty/shared data are available via
// /proc/<pid>/smaps.
static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) {
- jlong* arr = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(data, 0));
+ jlong* arr = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(data, nullptr));
if (arr == nullptr || env->GetArrayLength(data) < 9) {
return;
}
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 3227c69..9b3fd16 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -675,6 +675,13 @@
Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe);
}
+static void VMRuntime_setProcessPackageName(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jstring java_package_name) {
+ ScopedUtfChars package_name(env, java_package_name);
+ Runtime::Current()->SetProcessPackageName(package_name.c_str());
+}
+
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
@@ -718,6 +725,7 @@
NATIVE_METHOD(VMRuntime, didPruneDalvikCache, "()Z"),
NATIVE_METHOD(VMRuntime, setSystemDaemonThreadPriority, "()V"),
NATIVE_METHOD(VMRuntime, setDedupeHiddenApiWarnings, "(Z)V"),
+ NATIVE_METHOD(VMRuntime, setProcessPackageName, "(Ljava/lang/String;)V"),
};
void register_dalvik_system_VMRuntime(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index 13871f7..b7f0a7a 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -122,7 +122,7 @@
return thread->HoldsLock(object);
}
-static void Thread_nativeInterrupt(JNIEnv* env, jobject java_thread) {
+static void Thread_interrupt0(JNIEnv* env, jobject java_thread) {
ScopedFastNativeObjectAccess soa(env);
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
Thread* thread = Thread::FromManagedThread(soa, java_thread);
@@ -131,7 +131,7 @@
}
}
-static void Thread_nativeSetName(JNIEnv* env, jobject peer, jstring java_name) {
+static void Thread_setNativeName(JNIEnv* env, jobject peer, jstring java_name) {
ScopedUtfChars name(env, java_name);
{
ScopedObjectAccess soa(env);
@@ -168,7 +168,7 @@
* from Thread.MIN_PRIORITY to Thread.MAX_PRIORITY (1-10), with "normal"
* threads at Thread.NORM_PRIORITY (5).
*/
-static void Thread_nativeSetPriority(JNIEnv* env, jobject java_thread, jint new_priority) {
+static void Thread_setPriority0(JNIEnv* env, jobject java_thread, jint new_priority) {
ScopedObjectAccess soa(env);
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
Thread* thread = Thread::FromManagedThread(soa, java_thread);
@@ -200,9 +200,9 @@
NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"),
NATIVE_METHOD(Thread, nativeGetStatus, "(Z)I"),
NATIVE_METHOD(Thread, holdsLock, "(Ljava/lang/Object;)Z"),
- FAST_NATIVE_METHOD(Thread, nativeInterrupt, "()V"),
- NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"),
- NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"),
+ FAST_NATIVE_METHOD(Thread, interrupt0, "()V"),
+ NATIVE_METHOD(Thread, setNativeName, "(Ljava/lang/String;)V"),
+ NATIVE_METHOD(Thread, setPriority0, "(I)V"),
FAST_NATIVE_METHOD(Thread, sleep, "(Ljava/lang/Object;JI)V"),
NATIVE_METHOD(Thread, yield, "()V"),
};
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index e54674f..4b4d6e3 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -121,7 +121,7 @@
static jobject Constructor_newInstanceFromSerialization(JNIEnv* env, jclass unused ATTRIBUTE_UNUSED,
jclass ctorClass, jclass allocClass) {
jmethodID ctor = env->GetMethodID(ctorClass, "<init>", "()V");
- DCHECK(ctor != NULL);
+ DCHECK(ctor != nullptr);
return env->NewObject(allocClass, ctor);
}
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 58e16ed..c7daef8 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -956,7 +956,7 @@
void* dlopen_handle_; // TODO: Unique_ptr with custom deleter.
// Dummy memory map objects corresponding to the regions mapped by dlopen.
- std::vector<std::unique_ptr<MemMap>> dlopen_mmaps_;
+ std::vector<MemMap> dlopen_mmaps_;
// The number of shared objects the linker told us about before loading. Used to
// (optimistically) optimize the PreSetup stage (see comment there).
@@ -1122,8 +1122,8 @@
uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
info->dlpi_phdr[i].p_vaddr);
size_t memsz = info->dlpi_phdr[i].p_memsz;
- MemMap* mmap = MemMap::MapDummy(info->dlpi_name, vaddr, memsz);
- context->dlopen_mmaps_->push_back(std::unique_ptr<MemMap>(mmap));
+ MemMap mmap = MemMap::MapDummy(info->dlpi_name, vaddr, memsz);
+ context->dlopen_mmaps_->push_back(std::move(mmap));
}
}
return 1; // Stop iteration and return 1 from dl_iterate_phdr.
@@ -1131,7 +1131,7 @@
return 0; // Continue iteration and return 0 from dl_iterate_phdr when finished.
}
const uint8_t* const begin_;
- std::vector<std::unique_ptr<MemMap>>* const dlopen_mmaps_;
+ std::vector<MemMap>* const dlopen_mmaps_;
const size_t shared_objects_before;
size_t shared_objects_seen;
};
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 5f87bf0..4ed26fa 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -550,7 +550,7 @@
const IndexBssMapping* const method_bss_mapping_ = nullptr;
const IndexBssMapping* const type_bss_mapping_ = nullptr;
const IndexBssMapping* const string_bss_mapping_ = nullptr;
- const uint32_t* const oat_class_offsets_pointer_ = 0u;
+ const uint32_t* const oat_class_offsets_pointer_ = nullptr;
TypeLookupTable lookup_table_;
const DexLayoutSections* const dex_layout_sections_ = nullptr;
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index 640fa7e..672303a 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -255,13 +255,13 @@
}
inline bool ReadBarrier::IsGray(mirror::Object* obj, uintptr_t* fake_address_dependency) {
- return obj->GetReadBarrierState(fake_address_dependency) == gray_state_;
+ return obj->GetReadBarrierState(fake_address_dependency) == kGrayState;
}
inline bool ReadBarrier::IsGray(mirror::Object* obj) {
// Use a load-acquire to load the read barrier bit to avoid reordering with the subsequent load.
// GetReadBarrierStateAcquire() has load-acquire semantics.
- return obj->GetReadBarrierStateAcquire() == gray_state_;
+ return obj->GetReadBarrierStateAcquire() == kGrayState;
}
} // namespace art
diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h
index e8df2ad..0741da6 100644
--- a/runtime/read_barrier.h
+++ b/runtime/read_barrier.h
@@ -102,11 +102,11 @@
// ALWAYS_INLINE on this caused a performance regression b/26744236.
static mirror::Object* Mark(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
- static constexpr uint32_t WhiteState() {
- return white_state_;
+ static constexpr uint32_t NonGrayState() {
+ return kNonGrayState;
}
static constexpr uint32_t GrayState() {
- return gray_state_;
+ return kGrayState;
}
// fake_address_dependency will be zero which should be bitwise-or'ed with the address of the
@@ -122,12 +122,13 @@
REQUIRES_SHARED(Locks::mutator_lock_);
static bool IsValidReadBarrierState(uint32_t rb_state) {
- return rb_state == white_state_ || rb_state == gray_state_;
+ return rb_state == kNonGrayState || rb_state == kGrayState;
}
- static constexpr uint32_t white_state_ = 0x0; // Not marked.
- static constexpr uint32_t gray_state_ = 0x1; // Marked, but not marked through. On mark stack.
- static constexpr uint32_t rb_state_mask_ = 0x1; // The low bits for white|gray.
+ private:
+ static constexpr uint32_t kNonGrayState = 0x0; // White (not marked) or black (marked through).
+ static constexpr uint32_t kGrayState = 0x1; // Marked, but not marked through. On mark stack.
+ static constexpr uint32_t kRBStateMask = 0x1; // The low bits for non-gray|gray.
};
} // namespace art
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index facebda..30d4587 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -425,7 +425,7 @@
low_4gb_arena_pool_.reset();
arena_pool_.reset();
jit_arena_pool_.reset();
- protected_fault_page_.reset();
+ protected_fault_page_.Reset();
MemMap::Shutdown();
// TODO: acquire a static mutex on Runtime to avoid racing.
@@ -1162,18 +1162,17 @@
{
constexpr uintptr_t kSentinelAddr =
RoundDown(static_cast<uintptr_t>(Context::kBadGprBase), kPageSize);
- protected_fault_page_.reset(MemMap::MapAnonymous("Sentinel fault page",
- reinterpret_cast<uint8_t*>(kSentinelAddr),
- kPageSize,
- PROT_NONE,
- /* low_4g */ true,
- /* reuse */ false,
- /* error_msg */ nullptr));
- if (protected_fault_page_ == nullptr) {
+ protected_fault_page_ = MemMap::MapAnonymous("Sentinel fault page",
+ reinterpret_cast<uint8_t*>(kSentinelAddr),
+ kPageSize,
+ PROT_NONE,
+ /* low_4g */ true,
+ /* error_msg */ nullptr);
+ if (!protected_fault_page_.IsValid()) {
LOG(WARNING) << "Could not reserve sentinel fault page";
- } else if (reinterpret_cast<uintptr_t>(protected_fault_page_->Begin()) != kSentinelAddr) {
+ } else if (reinterpret_cast<uintptr_t>(protected_fault_page_.Begin()) != kSentinelAddr) {
LOG(WARNING) << "Could not reserve sentinel fault page at the right address.";
- protected_fault_page_.reset();
+ protected_fault_page_.Reset();
}
}
diff --git a/runtime/runtime.h b/runtime/runtime.h
index ca93e24..f98d7b9 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -29,6 +29,7 @@
#include "arch/instruction_set.h"
#include "base/macros.h"
+#include "base/mem_map.h"
#include "base/mutex.h"
#include "deoptimization_kind.h"
#include "dex/dex_file_types.h"
@@ -86,7 +87,6 @@
class IsMarkedVisitor;
class JavaVMExt;
class LinearAlloc;
-class MemMap;
class MonitorList;
class MonitorPool;
class NullPointerHandler;
@@ -579,6 +579,18 @@
return hidden_api_access_event_log_rate_;
}
+ const std::string& GetProcessPackageName() const {
+ return process_package_name_;
+ }
+
+ void SetProcessPackageName(const char* package_name) {
+ if (package_name == nullptr) {
+ process_package_name_.clear();
+ } else {
+ process_package_name_ = package_name;
+ }
+ }
+
bool IsDexFileFallbackEnabled() const {
return allow_dex_file_fallback_;
}
@@ -1031,10 +1043,13 @@
// when there is a warning. This is only used for testing.
bool always_set_hidden_api_warning_flag_;
- // How often to log hidden API access to the event log. An integer between 0 (never)
- // and 0x10000 (always).
+ // How often to log hidden API access to the event log. An integer between 0
+ // (never) and 0x10000 (always).
uint32_t hidden_api_access_event_log_rate_;
+ // The package of the app running in this process.
+ std::string process_package_name_;
+
// Whether threads should dump their native stack on SIGQUIT.
bool dump_native_stack_on_sig_quit_;
@@ -1075,7 +1090,7 @@
std::atomic<uint32_t> deoptimization_counts_[
static_cast<uint32_t>(DeoptimizationKind::kLast) + 1];
- std::unique_ptr<MemMap> protected_fault_page_;
+ MemMap protected_fault_page_;
uint32_t verifier_logging_threshold_ms_;
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index 794ac19..ed0472f 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -190,19 +190,18 @@
TEST_F(ThreadLifecycleCallbackRuntimeCallbacksTest, ThreadLifecycleCallbackAttach) {
std::string error_msg;
- std::unique_ptr<MemMap> stack(MemMap::MapAnonymous("ThreadLifecycleCallback Thread",
- nullptr,
- 128 * kPageSize, // Just some small stack.
- PROT_READ | PROT_WRITE,
- false,
- false,
- &error_msg));
- ASSERT_FALSE(stack == nullptr) << error_msg;
+ MemMap stack = MemMap::MapAnonymous("ThreadLifecycleCallback Thread",
+ /* addr */ nullptr,
+ 128 * kPageSize, // Just some small stack.
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ ASSERT_TRUE(stack.IsValid()) << error_msg;
const char* reason = "ThreadLifecycleCallback test thread";
pthread_attr_t attr;
CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), reason);
- CHECK_PTHREAD_CALL(pthread_attr_setstack, (&attr, stack->Begin(), stack->Size()), reason);
+ CHECK_PTHREAD_CALL(pthread_attr_setstack, (&attr, stack.Begin(), stack.Size()), reason);
pthread_t pthread;
CHECK_PTHREAD_CALL(pthread_create,
(&pthread,
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index d1000c5..62dec15 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -31,83 +31,70 @@
: CodeInfo(header->GetOptimizedCodeInfoPtr(), flags) {
}
+// Returns true if the decoded table was deduped.
template<typename Accessor>
-ALWAYS_INLINE static void DecodeTable(BitTable<Accessor>& table,
- BitMemoryReader& reader,
- const uint8_t* data) {
+ALWAYS_INLINE static bool DecodeTable(BitTable<Accessor>& table, BitMemoryReader& reader) {
bool is_deduped = reader.ReadBit();
if (is_deduped) {
- // 'data' points to the start of the reader's data.
- uint32_t current_bit_offset = reader.GetBitOffset();
- uint32_t bit_offset_backwards = DecodeVarintBits(reader) - current_bit_offset;
- uint32_t byte_offset_backwards = BitsToBytesRoundUp(bit_offset_backwards);
- BitMemoryReader reader2(data - byte_offset_backwards,
- byte_offset_backwards * kBitsPerByte - bit_offset_backwards);
+ ssize_t bit_offset = reader.NumberOfReadBits() - reader.ReadVarint();
+ BitMemoryReader reader2(reader.data(), bit_offset); // The offset is negative.
table.Decode(reader2);
} else {
table.Decode(reader);
}
+ return is_deduped;
}
void CodeInfo::Decode(const uint8_t* data, DecodeFlags flags) {
BitMemoryReader reader(data);
- packed_frame_size_ = DecodeVarintBits(reader);
- core_spill_mask_ = DecodeVarintBits(reader);
- fp_spill_mask_ = DecodeVarintBits(reader);
- number_of_dex_registers_ = DecodeVarintBits(reader);
- DecodeTable(stack_maps_, reader, data);
- DecodeTable(register_masks_, reader, data);
- DecodeTable(stack_masks_, reader, data);
- if (flags & DecodeFlags::GcMasksOnly) {
- return;
- }
- DecodeTable(inline_infos_, reader, data);
- DecodeTable(method_infos_, reader, data);
- if (flags & DecodeFlags::InlineInfoOnly) {
- return;
- }
- DecodeTable(dex_register_masks_, reader, data);
- DecodeTable(dex_register_maps_, reader, data);
- DecodeTable(dex_register_catalog_, reader, data);
- size_in_bits_ = reader.GetBitOffset();
+ ForEachHeaderField([this, &reader](auto member_pointer) {
+ this->*member_pointer = reader.ReadVarint();
+ });
+ ForEachBitTableField([this, &reader](auto member_pointer) {
+ DecodeTable(this->*member_pointer, reader);
+ }, flags);
+ size_in_bits_ = reader.NumberOfReadBits();
}
-template<typename Accessor>
-ALWAYS_INLINE static void DedupeTable(BitMemoryWriter<std::vector<uint8_t>>& writer,
- BitMemoryReader& reader,
- CodeInfo::DedupeMap* dedupe_map) {
- bool is_deduped = reader.ReadBit();
- DCHECK(!is_deduped);
- BitTable<Accessor> bit_table(reader);
- BitMemoryRegion region = reader.Tail(bit_table.BitSize());
- auto it = dedupe_map->insert(std::make_pair(region, writer.GetBitOffset() + 1 /* dedupe bit */));
- if (it.second /* new bit table */ || region.size_in_bits() < 32) {
- writer.WriteBit(false); // Is not deduped.
- writer.WriteRegion(region);
- } else {
- writer.WriteBit(true); // Is deduped.
- EncodeVarintBits(writer, writer.GetBitOffset() - it.first->second);
- }
-}
+size_t CodeInfo::Deduper::Dedupe(const uint8_t* code_info_data) {
+ writer_.ByteAlign();
+ size_t deduped_offset = writer_.NumberOfWrittenBits() / kBitsPerByte;
+ BitMemoryReader reader(code_info_data);
+ CodeInfo code_info; // Temporary storage for decoded data.
+ ForEachHeaderField([this, &reader, &code_info](auto member_pointer) {
+ code_info.*member_pointer = reader.ReadVarint();
+ writer_.WriteVarint(code_info.*member_pointer);
+ });
+ ForEachBitTableField([this, &reader, &code_info](auto member_pointer) {
+ bool is_deduped = reader.ReadBit();
+ DCHECK(!is_deduped);
+ size_t bit_table_start = reader.NumberOfReadBits();
+ (code_info.*member_pointer).Decode(reader);
+ BitMemoryRegion region = reader.GetReadRegion().Subregion(bit_table_start);
+ auto it = dedupe_map_.insert(std::make_pair(region, /* placeholder */ 0));
+ if (it.second /* new bit table */ || region.size_in_bits() < 32) {
+ writer_.WriteBit(false); // Is not deduped.
+ it.first->second = writer_.NumberOfWrittenBits();
+ writer_.WriteRegion(region);
+ } else {
+ writer_.WriteBit(true); // Is deduped.
+ size_t bit_offset = writer_.NumberOfWrittenBits();
+ writer_.WriteVarint(bit_offset - it.first->second);
+ }
+ });
-size_t CodeInfo::Dedupe(std::vector<uint8_t>* out, const uint8_t* in, DedupeMap* dedupe_map) {
- // Remember the current offset in the output buffer so that we can return it later.
- const size_t result = out->size();
- BitMemoryReader reader(in);
- BitMemoryWriter<std::vector<uint8_t>> writer(out, /* bit_offset */ out->size() * kBitsPerByte);
- EncodeVarintBits(writer, DecodeVarintBits(reader)); // packed_frame_size_.
- EncodeVarintBits(writer, DecodeVarintBits(reader)); // core_spill_mask_.
- EncodeVarintBits(writer, DecodeVarintBits(reader)); // fp_spill_mask_.
- EncodeVarintBits(writer, DecodeVarintBits(reader)); // number_of_dex_registers_.
- DedupeTable<StackMap>(writer, reader, dedupe_map);
- DedupeTable<RegisterMask>(writer, reader, dedupe_map);
- DedupeTable<MaskInfo>(writer, reader, dedupe_map);
- DedupeTable<InlineInfo>(writer, reader, dedupe_map);
- DedupeTable<MethodInfo>(writer, reader, dedupe_map);
- DedupeTable<MaskInfo>(writer, reader, dedupe_map);
- DedupeTable<DexRegisterMapInfo>(writer, reader, dedupe_map);
- DedupeTable<DexRegisterInfo>(writer, reader, dedupe_map);
- return result;
+ if (kIsDebugBuild) {
+ CodeInfo old_code_info(code_info_data);
+ CodeInfo new_code_info(writer_.data() + deduped_offset);
+ ForEachHeaderField([&old_code_info, &new_code_info](auto member_pointer) {
+ DCHECK_EQ(old_code_info.*member_pointer, new_code_info.*member_pointer);
+ });
+ ForEachBitTableField([&old_code_info, &new_code_info](auto member_pointer) {
+ DCHECK((old_code_info.*member_pointer).Equals(new_code_info.*member_pointer));
+ });
+ }
+
+ return deduped_offset;
}
BitTable<StackMap>::const_iterator CodeInfo::BinarySearchNativePc(uint32_t packed_pc) const {
@@ -194,33 +181,32 @@
}
}
-template<typename Accessor>
-static void AddTableSizeStats(const char* table_name,
- const BitTable<Accessor>& table,
- /*out*/ Stats* parent) {
- Stats* table_stats = parent->Child(table_name);
- table_stats->AddBits(table.BitSize());
- table_stats->Child("Header")->AddBits(table.HeaderBitSize());
- const char* const* column_names = GetBitTableColumnNames<Accessor>();
- for (size_t c = 0; c < table.NumColumns(); c++) {
- if (table.NumColumnBits(c) > 0) {
- Stats* column_stats = table_stats->Child(column_names[c]);
- column_stats->AddBits(table.NumRows() * table.NumColumnBits(c), table.NumRows());
+// Decode the CodeInfo while collecting size statistics.
+void CodeInfo::CollectSizeStats(const uint8_t* code_info_data, /*out*/ Stats* parent) {
+ Stats* codeinfo_stats = parent->Child("CodeInfo");
+ BitMemoryReader reader(code_info_data);
+ ForEachHeaderField([&reader](auto) { reader.ReadVarint(); });
+ codeinfo_stats->Child("Header")->AddBits(reader.NumberOfReadBits());
+ CodeInfo code_info; // Temporary storage for decoded tables.
+ ForEachBitTableField([codeinfo_stats, &reader, &code_info](auto member_pointer) {
+ auto& table = code_info.*member_pointer;
+ size_t bit_offset = reader.NumberOfReadBits();
+ bool deduped = DecodeTable(table, reader);
+ if (deduped) {
+ codeinfo_stats->Child("DedupeOffset")->AddBits(reader.NumberOfReadBits() - bit_offset);
+ } else {
+ Stats* table_stats = codeinfo_stats->Child(table.GetName());
+ table_stats->AddBits(reader.NumberOfReadBits() - bit_offset);
+ const char* const* column_names = table.GetColumnNames();
+ for (size_t c = 0; c < table.NumColumns(); c++) {
+ if (table.NumColumnBits(c) > 0) {
+ Stats* column_stats = table_stats->Child(column_names[c]);
+ column_stats->AddBits(table.NumRows() * table.NumColumnBits(c), table.NumRows());
+ }
+ }
}
- }
-}
-
-void CodeInfo::AddSizeStats(/*out*/ Stats* parent) const {
- Stats* stats = parent->Child("CodeInfo");
- stats->AddBytes(Size());
- AddTableSizeStats<StackMap>("StackMaps", stack_maps_, stats);
- AddTableSizeStats<RegisterMask>("RegisterMasks", register_masks_, stats);
- AddTableSizeStats<MaskInfo>("StackMasks", stack_masks_, stats);
- AddTableSizeStats<InlineInfo>("InlineInfos", inline_infos_, stats);
- AddTableSizeStats<MethodInfo>("MethodInfo", method_infos_, stats);
- AddTableSizeStats<MaskInfo>("DexRegisterMasks", dex_register_masks_, stats);
- AddTableSizeStats<DexRegisterMapInfo>("DexRegisterMaps", dex_register_maps_, stats);
- AddTableSizeStats<DexRegisterInfo>("DexRegisterCatalog", dex_register_catalog_, stats);
+ });
+ codeinfo_stats->AddBytes(BitsToBytesRoundUp(reader.NumberOfReadBits()));
}
void DexRegisterMap::Dump(VariableIndentationOutputStream* vios) const {
@@ -236,56 +222,49 @@
}
}
-template<typename Accessor>
-static void DumpTable(VariableIndentationOutputStream* vios,
- const char* table_name,
- const BitTable<Accessor>& table,
- bool verbose,
- bool is_mask = false) {
- if (table.NumRows() != 0) {
- vios->Stream() << table_name << " BitSize=" << table.BitSize();
- vios->Stream() << " Rows=" << table.NumRows() << " Bits={";
- const char* const* column_names = GetBitTableColumnNames<Accessor>();
- for (size_t c = 0; c < table.NumColumns(); c++) {
- vios->Stream() << (c != 0 ? " " : "");
- vios->Stream() << column_names[c] << "=" << table.NumColumnBits(c);
- }
- vios->Stream() << "}\n";
- if (verbose) {
- ScopedIndentation indent1(vios);
- for (size_t r = 0; r < table.NumRows(); r++) {
- vios->Stream() << "[" << std::right << std::setw(3) << r << "]={";
- for (size_t c = 0; c < table.NumColumns(); c++) {
- vios->Stream() << (c != 0 ? " " : "");
- if (is_mask) {
- BitMemoryRegion bits = table.GetBitMemoryRegion(r, c);
- for (size_t b = 0, e = bits.size_in_bits(); b < e; b++) {
- vios->Stream() << bits.LoadBit(e - b - 1);
- }
- } else {
- vios->Stream() << std::right << std::setw(8) << static_cast<int32_t>(table.Get(r, c));
- }
- }
- vios->Stream() << "}\n";
- }
- }
- }
-}
-
void CodeInfo::Dump(VariableIndentationOutputStream* vios,
uint32_t code_offset,
bool verbose,
InstructionSet instruction_set) const {
- vios->Stream() << "CodeInfo\n";
+ vios->Stream() << "CodeInfo BitSize=" << size_in_bits_
+ << " FrameSize:" << packed_frame_size_ * kStackAlignment
+ << " CoreSpillMask:" << std::hex << core_spill_mask_
+ << " FpSpillMask:" << std::hex << fp_spill_mask_
+ << " NumberOfDexRegisters:" << std::dec << number_of_dex_registers_
+ << "\n";
ScopedIndentation indent1(vios);
- DumpTable<StackMap>(vios, "StackMaps", stack_maps_, verbose);
- DumpTable<RegisterMask>(vios, "RegisterMasks", register_masks_, verbose);
- DumpTable<MaskInfo>(vios, "StackMasks", stack_masks_, verbose, true /* is_mask */);
- DumpTable<InlineInfo>(vios, "InlineInfos", inline_infos_, verbose);
- DumpTable<MethodInfo>(vios, "MethodInfo", method_infos_, verbose);
- DumpTable<MaskInfo>(vios, "DexRegisterMasks", dex_register_masks_, verbose, true /* is_mask */);
- DumpTable<DexRegisterMapInfo>(vios, "DexRegisterMaps", dex_register_maps_, verbose);
- DumpTable<DexRegisterInfo>(vios, "DexRegisterCatalog", dex_register_catalog_, verbose);
+ ForEachBitTableField([this, &vios, verbose](auto member_pointer) {
+ const auto& table = this->*member_pointer;
+ if (table.NumRows() != 0) {
+ vios->Stream() << table.GetName() << " BitSize=" << table.DataBitSize();
+ vios->Stream() << " Rows=" << table.NumRows() << " Bits={";
+ const char* const* column_names = table.GetColumnNames();
+ for (size_t c = 0; c < table.NumColumns(); c++) {
+ vios->Stream() << (c != 0 ? " " : "");
+ vios->Stream() << column_names[c] << "=" << table.NumColumnBits(c);
+ }
+ vios->Stream() << "}\n";
+ if (verbose) {
+ ScopedIndentation indent1(vios);
+ for (size_t r = 0; r < table.NumRows(); r++) {
+ vios->Stream() << "[" << std::right << std::setw(3) << r << "]={";
+ for (size_t c = 0; c < table.NumColumns(); c++) {
+ vios->Stream() << (c != 0 ? " " : "");
+ if (&table == static_cast<const void*>(&stack_masks_) ||
+ &table == static_cast<const void*>(&dex_register_masks_)) {
+ BitMemoryRegion bits = table.GetBitMemoryRegion(r, c);
+ for (size_t b = 0, e = bits.size_in_bits(); b < e; b++) {
+ vios->Stream() << bits.LoadBit(e - b - 1);
+ }
+ } else {
+ vios->Stream() << std::right << std::setw(8) << static_cast<int32_t>(table.Get(r, c));
+ }
+ }
+ vios->Stream() << "}\n";
+ }
+ }
+ }
+ });
// Display stack maps along with (live) Dex register maps.
if (verbose) {
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index d6db05a..5f44286 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -128,7 +128,7 @@
OSR = 1,
Debug = 2,
};
- BIT_TABLE_HEADER()
+ BIT_TABLE_HEADER(StackMap)
BIT_TABLE_COLUMN(0, Kind)
BIT_TABLE_COLUMN(1, PackedNativePc)
BIT_TABLE_COLUMN(2, DexPc)
@@ -174,7 +174,7 @@
*/
class InlineInfo : public BitTableAccessor<6> {
public:
- BIT_TABLE_HEADER()
+ BIT_TABLE_HEADER(InlineInfo)
BIT_TABLE_COLUMN(0, IsLast) // Determines if there are further rows for further depths.
BIT_TABLE_COLUMN(1, DexPc)
BIT_TABLE_COLUMN(2, MethodInfoIndex)
@@ -201,21 +201,27 @@
const StackMap& stack_map) const;
};
-class MaskInfo : public BitTableAccessor<1> {
+class StackMask : public BitTableAccessor<1> {
public:
- BIT_TABLE_HEADER()
+ BIT_TABLE_HEADER(StackMask)
+ BIT_TABLE_COLUMN(0, Mask)
+};
+
+class DexRegisterMask : public BitTableAccessor<1> {
+ public:
+ BIT_TABLE_HEADER(DexRegisterMask)
BIT_TABLE_COLUMN(0, Mask)
};
class DexRegisterMapInfo : public BitTableAccessor<1> {
public:
- BIT_TABLE_HEADER()
+ BIT_TABLE_HEADER(DexRegisterMapInfo)
BIT_TABLE_COLUMN(0, CatalogueIndex)
};
class DexRegisterInfo : public BitTableAccessor<2> {
public:
- BIT_TABLE_HEADER()
+ BIT_TABLE_HEADER(DexRegisterInfo)
BIT_TABLE_COLUMN(0, Kind)
BIT_TABLE_COLUMN(1, PackedValue)
@@ -246,7 +252,7 @@
// therefore it is worth encoding the mask as value+shift.
class RegisterMask : public BitTableAccessor<2> {
public:
- BIT_TABLE_HEADER()
+ BIT_TABLE_HEADER(RegisterMask)
BIT_TABLE_COLUMN(0, Value)
BIT_TABLE_COLUMN(1, Shift)
@@ -259,7 +265,7 @@
// Separating them greatly improves dedup efficiency of the other tables.
class MethodInfo : public BitTableAccessor<1> {
public:
- BIT_TABLE_HEADER()
+ BIT_TABLE_HEADER(MethodInfo)
BIT_TABLE_COLUMN(0, MethodIndex)
};
@@ -269,8 +275,25 @@
*/
class CodeInfo {
public:
+ class Deduper {
+ public:
+ explicit Deduper(std::vector<uint8_t>* output) : writer_(output) {
+ DCHECK_EQ(output->size(), 0u);
+ }
+
+ // Copy CodeInfo into output while de-duplicating the internal bit tables.
+ // It returns the byte offset of the copied CodeInfo within the output.
+ size_t Dedupe(const uint8_t* code_info);
+
+ private:
+ BitMemoryWriter<std::vector<uint8_t>> writer_;
+
+ // Deduplicate at BitTable level. The value is bit offset within the output.
+ std::map<BitMemoryRegion, uint32_t, BitMemoryRegion::Less> dedupe_map_;
+ };
+
enum DecodeFlags {
- Default = 0,
+ AllTables = 0,
// Limits the decoding only to the data needed by GC.
GcMasksOnly = 1,
// Limits the decoding only to the main stack map table and inline info table.
@@ -278,11 +301,11 @@
InlineInfoOnly = 2,
};
- explicit CodeInfo(const uint8_t* data, DecodeFlags flags = DecodeFlags::Default) {
+ explicit CodeInfo(const uint8_t* data, DecodeFlags flags = AllTables) {
Decode(reinterpret_cast<const uint8_t*>(data), flags);
}
- explicit CodeInfo(const OatQuickMethodHeader* header, DecodeFlags flags = DecodeFlags::Default);
+ explicit CodeInfo(const OatQuickMethodHeader* header, DecodeFlags flags = AllTables);
size_t Size() const {
return BitsToBytesRoundUp(size_in_bits_);
@@ -411,27 +434,19 @@
InstructionSet instruction_set) const;
// Accumulate code info size statistics into the given Stats tree.
- void AddSizeStats(/*out*/ Stats* parent) const;
+ static void CollectSizeStats(const uint8_t* code_info, /*out*/ Stats* parent);
ALWAYS_INLINE static QuickMethodFrameInfo DecodeFrameInfo(const uint8_t* data) {
BitMemoryReader reader(data);
return QuickMethodFrameInfo(
- DecodeVarintBits(reader) * kStackAlignment, // Decode packed_frame_size_ and unpack.
- DecodeVarintBits(reader), // core_spill_mask_.
- DecodeVarintBits(reader)); // fp_spill_mask_.
+ reader.ReadVarint() * kStackAlignment, // Decode packed_frame_size_ and unpack.
+ reader.ReadVarint(), // core_spill_mask_.
+ reader.ReadVarint()); // fp_spill_mask_.
}
- typedef std::map<BitMemoryRegion, uint32_t, BitMemoryRegion::Less> DedupeMap;
-
- // Copy CodeInfo data while de-duplicating the internal bit tables.
- // The 'out' vector must be reused between Dedupe calls (it does not have to be empty).
- // The 'dedupe_map' stores the bit offsets of bit tables within the 'out' vector.
- // It returns the byte offset of the copied CodeInfo within the 'out' vector.
- static size_t Dedupe(std::vector<uint8_t>* out,
- const uint8_t* in,
- /*inout*/ DedupeMap* dedupe_map);
-
private:
+ CodeInfo() {}
+
// Returns lower bound (fist stack map which has pc greater or equal than the desired one).
// It ignores catch stack maps at the end (it is the same as if they had maximum pc value).
BitTable<StackMap>::const_iterator BinarySearchNativePc(uint32_t packed_pc) const;
@@ -443,16 +458,44 @@
void Decode(const uint8_t* data, DecodeFlags flags);
+ // Invokes the callback with member pointer of each header field.
+ template<typename Callback>
+ ALWAYS_INLINE static void ForEachHeaderField(Callback callback) {
+ callback(&CodeInfo::packed_frame_size_);
+ callback(&CodeInfo::core_spill_mask_);
+ callback(&CodeInfo::fp_spill_mask_);
+ callback(&CodeInfo::number_of_dex_registers_);
+ }
+
+ // Invokes the callback with member pointer of each BitTable field.
+ template<typename Callback>
+ ALWAYS_INLINE static void ForEachBitTableField(Callback callback, DecodeFlags flags = AllTables) {
+ callback(&CodeInfo::stack_maps_);
+ callback(&CodeInfo::register_masks_);
+ callback(&CodeInfo::stack_masks_);
+ if (flags & DecodeFlags::GcMasksOnly) {
+ return;
+ }
+ callback(&CodeInfo::inline_infos_);
+ callback(&CodeInfo::method_infos_);
+ if (flags & DecodeFlags::InlineInfoOnly) {
+ return;
+ }
+ callback(&CodeInfo::dex_register_masks_);
+ callback(&CodeInfo::dex_register_maps_);
+ callback(&CodeInfo::dex_register_catalog_);
+ }
+
uint32_t packed_frame_size_; // Frame size in kStackAlignment units.
uint32_t core_spill_mask_;
uint32_t fp_spill_mask_;
uint32_t number_of_dex_registers_;
BitTable<StackMap> stack_maps_;
BitTable<RegisterMask> register_masks_;
- BitTable<MaskInfo> stack_masks_;
+ BitTable<StackMask> stack_masks_;
BitTable<InlineInfo> inline_infos_;
BitTable<MethodInfo> method_infos_;
- BitTable<MaskInfo> dex_register_masks_;
+ BitTable<DexRegisterMask> dex_register_masks_;
BitTable<DexRegisterMapInfo> dex_register_maps_;
BitTable<DexRegisterInfo> dex_register_catalog_;
uint32_t size_in_bits_ = 0;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 0703a07..df7f19d 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3605,7 +3605,7 @@
reinterpret_cast<uintptr_t>(cur_quick_frame));
uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc());
CodeInfo code_info(method_header, kPrecise
- ? CodeInfo::DecodeFlags::Default // We will need dex register maps.
+ ? CodeInfo::DecodeFlags::AllTables // We will need dex register maps.
: CodeInfo::DecodeFlags::GcMasksOnly);
StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
DCHECK(map.IsValid());
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index ba333f6..9222024 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -1476,6 +1476,9 @@
list_.remove(self);
break;
}
+ // In the case where we are not suspended yet, sleep to leave other threads time to execute.
+ // This is important if there are realtime threads. b/111277984
+ usleep(1);
}
// We failed to remove the thread due to a suspend request, loop and try again.
}
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 26ca190..28fc59c 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -46,19 +46,23 @@
// Add an inaccessible page to catch stack overflow.
stack_size += kPageSize;
std::string error_msg;
- stack_.reset(MemMap::MapAnonymous(name.c_str(), nullptr, stack_size, PROT_READ | PROT_WRITE,
- false, false, &error_msg));
- CHECK(stack_.get() != nullptr) << error_msg;
- CHECK_ALIGNED(stack_->Begin(), kPageSize);
+ stack_ = MemMap::MapAnonymous(name.c_str(),
+ /* addr */ nullptr,
+ stack_size,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ &error_msg);
+ CHECK(stack_.IsValid()) << error_msg;
+ CHECK_ALIGNED(stack_.Begin(), kPageSize);
CheckedCall(mprotect,
"mprotect bottom page of thread pool worker stack",
- stack_->Begin(),
+ stack_.Begin(),
kPageSize,
PROT_NONE);
const char* reason = "new thread pool worker thread";
pthread_attr_t attr;
CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), reason);
- CHECK_PTHREAD_CALL(pthread_attr_setstack, (&attr, stack_->Begin(), stack_->Size()), reason);
+ CHECK_PTHREAD_CALL(pthread_attr_setstack, (&attr, stack_.Begin(), stack_.Size()), reason);
CHECK_PTHREAD_CALL(pthread_create, (&pthread_, &attr, &Callback, this), reason);
CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), reason);
}
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index 2784953..98a1193 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -53,8 +53,8 @@
static const size_t kDefaultStackSize = 1 * MB;
size_t GetStackSize() const {
- DCHECK(stack_.get() != nullptr);
- return stack_->Size();
+ DCHECK(stack_.IsValid());
+ return stack_.Size();
}
virtual ~ThreadPoolWorker();
@@ -71,7 +71,7 @@
ThreadPool* const thread_pool_;
const std::string name_;
- std::unique_ptr<MemMap> stack_;
+ MemMap stack_;
pthread_t pthread_;
Thread* thread_;
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 32aa86d..a7dc225 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -144,7 +144,7 @@
mmap_reuse = false;
}
CHECK(!mmap_reuse || mmap_addr != nullptr);
- std::unique_ptr<MemMap> mmap(MemMap::MapFileAtAddress(
+ MemMap mmap = MemMap::MapFileAtAddress(
mmap_addr,
vdex_length,
(writable || unquicken) ? PROT_READ | PROT_WRITE : PROT_READ,
@@ -154,13 +154,13 @@
low_4gb,
mmap_reuse,
vdex_filename.c_str(),
- error_msg));
- if (mmap == nullptr) {
+ error_msg);
+ if (!mmap.IsValid()) {
*error_msg = "Failed to mmap file " + vdex_filename + " : " + *error_msg;
return nullptr;
}
- std::unique_ptr<VdexFile> vdex(new VdexFile(mmap.release()));
+ std::unique_ptr<VdexFile> vdex(new VdexFile(std::move(mmap)));
if (!vdex->IsValid()) {
*error_msg = "Vdex file is not valid";
return nullptr;
@@ -175,7 +175,7 @@
/* decompile_return_instruction */ false);
// Update the quickening info size to pretend there isn't any.
size_t offset = vdex->GetDexSectionHeaderOffset();
- reinterpret_cast<DexSectionHeader*>(vdex->mmap_->Begin() + offset)->quickening_info_size_ = 0;
+ reinterpret_cast<DexSectionHeader*>(vdex->mmap_.Begin() + offset)->quickening_info_size_ = 0;
}
*error_msg = "Success";
@@ -299,10 +299,6 @@
decompile_return_instruction);
}
}
- method.UnHideAccessFlags();
- }
- for (const ClassAccessor::Field& field : class_accessor.GetFields()) {
- field.UnHideAccessFlags();
}
}
}
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 866a57e..a39ec31 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -153,7 +153,7 @@
typedef uint32_t VdexChecksum;
using QuickeningTableOffsetType = uint32_t;
- explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
+ explicit VdexFile(MemMap&& mmap) : mmap_(std::move(mmap)) {}
// Returns nullptr if the vdex file cannot be opened or is not valid.
// The mmap_* parameters can be left empty (nullptr/0/false) to allocate at random address.
@@ -215,9 +215,9 @@
error_msg);
}
- const uint8_t* Begin() const { return mmap_->Begin(); }
- const uint8_t* End() const { return mmap_->End(); }
- size_t Size() const { return mmap_->Size(); }
+ const uint8_t* Begin() const { return mmap_.Begin(); }
+ const uint8_t* End() const { return mmap_.End(); }
+ size_t Size() const { return mmap_.Size(); }
const VerifierDepsHeader& GetVerifierDepsHeader() const {
return *reinterpret_cast<const VerifierDepsHeader*>(Begin());
@@ -260,7 +260,7 @@
}
bool IsValid() const {
- return mmap_->Size() >= sizeof(VerifierDepsHeader) && GetVerifierDepsHeader().IsValid();
+ return mmap_.Size() >= sizeof(VerifierDepsHeader) && GetVerifierDepsHeader().IsValid();
}
// This method is for iterating over the dex files in the vdex. If `cursor` is null,
@@ -328,7 +328,7 @@
return DexBegin() + GetDexSectionHeader().GetDexSize();
}
- std::unique_ptr<MemMap> mmap_;
+ MemMap mmap_;
DISALLOW_COPY_AND_ASSIGN(VdexFile);
};
diff --git a/test/1000-non-moving-space-stress/expected.txt b/test/1000-non-moving-space-stress/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/1000-non-moving-space-stress/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/1000-non-moving-space-stress/info.txt b/test/1000-non-moving-space-stress/info.txt
new file mode 100644
index 0000000..a20459f
--- /dev/null
+++ b/test/1000-non-moving-space-stress/info.txt
@@ -0,0 +1,5 @@
+Regression test for a bug that used to trigger GC crashes during a
+sticky-bit CC (young-generation) collection involving an unreachable
+newly allocated object in the non-moving space with a dangling
+reference to an object cleared or moved from a newly allocated region
+of the region space.
diff --git a/test/1000-non-moving-space-stress/src-art/Main.java b/test/1000-non-moving-space-stress/src-art/Main.java
new file mode 100644
index 0000000..18bfdd3
--- /dev/null
+++ b/test/1000-non-moving-space-stress/src-art/Main.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.VMRuntime;
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ VMRuntime runtime = VMRuntime.getRuntime();
+
+ try {
+ int N = 1024 * 1024;
+ int S = 512;
+ for (int n = 0; n < N; ++n) {
+ // Allocate unreachable objects.
+ $noinline$Alloc(runtime);
+ // Allocate an object with a substantial size to increase memory
+ // pressure and eventually trigger non-explicit garbage collection
+ // (explicit garbage collections triggered by java.lang.Runtime.gc()
+ // are always full GCs). Upon garbage collection, the objects
+ // allocated in $noinline$Alloc used to trigger a crash.
+ Object[] moving_array = new Object[S];
+ }
+ } catch (OutOfMemoryError e) {
+ // Stop here.
+ }
+ System.out.println("passed");
+ }
+
+ // When using the Concurrent Copying (CC) collector (default collector),
+ // this method allocates an object in the non-moving space and an object
+ // in the region space, make the former reference the later, and returns
+ // nothing (so that none of these objects are reachable when upon return).
+ static void $noinline$Alloc(VMRuntime runtime) {
+ Object[] non_moving_array = (Object[]) runtime.newNonMovableArray(Object.class, 1);
+ // Small object, unlikely to trigger garbage collection.
+ non_moving_array[0] = new Object();
+ }
+
+}
diff --git a/test/160-read-barrier-stress/src/Main.java b/test/160-read-barrier-stress/src/Main.java
index 7e130ce..5865094 100644
--- a/test/160-read-barrier-stress/src/Main.java
+++ b/test/160-read-barrier-stress/src/Main.java
@@ -14,23 +14,117 @@
* limitations under the License.
*/
+import java.lang.reflect.Field;
+import sun.misc.Unsafe;
+
public class Main {
- public static void main(String[] args) {
+ public static void main(String[] args) throws Exception {
+ testFieldReads();
+ testArrayReadsWithConstIndex();
+ testArrayReadsWithNonConstIndex();
+ testGcRoots();
+ testUnsafeGet();
+ testUnsafeCas();
+ testUnsafeCasRegression();
+ }
+
+ public static void testFieldReads() {
// Initialize local variables for comparison.
Object f0000 = manyFields.testField0000;
Object f1024 = manyFields.testField1024;
Object f4444 = manyFields.testField4444;
Object f4999 = manyFields.testField4999;
+
+ // Continually check reads from `manyFields` while allocating
+ // over 64MiB memory (with heap size limited to 16MiB), ensuring we run GC and
+ // stress the read barrier implementation if concurrent collector is enabled.
+ for (int i = 0; i != 64 * 1024; ++i) {
+ allocateAtLeast1KiB();
+ ManyFields mf = manyFields; // Load the volatile `manyFields` once on each iteration.
+ // Test reference field access.
+ assertSameObject(f0000, mf.testField0000);
+ assertDifferentObject(f0000, mf.testField0001);
+ assertSameObject(f1024, mf.testField1024);
+ assertSameObject(f4444, mf.testField4444);
+ assertDifferentObject(f4999, mf.testField4998);
+ assertSameObject(f4999, mf.testField4999);
+ }
+ }
+
+ public static void testArrayReadsWithConstIndex() {
+ // Initialize local variables for comparison.
+ Object f0000 = new Integer(0);
+ Object f1024 = new Integer(1024);
+ Object f4444 = new Integer(4444);
+ Object f4999 = new Integer(4999);
// Initialize largeArray for comparison.
largeArray[0] = f0000;
+ Object tmp = new Integer(1);
+ largeArray[1] = tmp;
largeArray[1024] = f1024;
largeArray[4444] = f4444;
+ tmp = new Integer(4998);
+ largeArray[4998] = tmp;
largeArray[4999] = f4999;
+ tmp = null; // Do not keep a reference to objects in largeArray[1] or largeArray[4998].
+
+ // Continually check reads from `largeArray` with constant indexes while allocating
+ // over 64MiB memory (with heap size limited to 16MiB), ensuring we run GC and
+ // stress the read barrier implementation if concurrent collector is enabled.
+ for (int i = 0; i != 64 * 1024; ++i) {
+ allocateAtLeast1KiB();
+ Object[] la = largeArray; // Load the volatile `largeArray` once on each iteration.
+ // Test array access with constant index.
+ assertSameObject(f0000, la[0]);
+ assertDifferentObject(f0000, la[1]);
+ assertSameObject(f1024, la[1024]);
+ assertSameObject(f4444, la[4444]);
+ assertDifferentObject(f4999, la[4998]);
+ assertSameObject(f4999, la[4999]);
+ }
+ }
+
+ public static void testArrayReadsWithNonConstIndex() {
+ // Initialize local variables for comparison.
+ Object f0000 = new Integer(0);
+ Object f1024 = new Integer(1024);
+ Object f4444 = new Integer(4444);
+ Object f4999 = new Integer(4999);
+ // Initialize largeArray for comparison.
+ largeArray[0] = f0000;
+ Object tmp = new Integer(1);
+ largeArray[1] = tmp;
+ largeArray[1024] = f1024;
+ largeArray[4444] = f4444;
+ tmp = new Integer(4998);
+ largeArray[4998] = tmp;
+ largeArray[4999] = f4999;
+ tmp = null; // Do not keep a reference to objects in largeArray[1] or largeArray[4998].
// Read indexes, they cannot be considered constant because the variables are volatile.
int i0 = index0;
+ int i1 = index1;
int i1024 = index1024;
int i4444 = index4444;
+ int i4998 = index4998;
int i4999 = index4999;
+
+ // Continually check reads from `largeArray` with non-constant indexes while allocating
+ // over 64MiB memory (with heap size limited to 16MiB), ensuring we run GC and
+ // stress the read barrier implementation if concurrent collector is enabled.
+ for (int i = 0; i != 64 * 1024; ++i) {
+ allocateAtLeast1KiB();
+ Object[] la = largeArray; // Load the volatile `largeArray` once on each iteration.
+ // Test array access with non-constant index.
+ assertSameObject(f0000, la[i0]);
+ assertDifferentObject(f0000, la[i1]);
+ assertSameObject(f1024, la[i1024]);
+ assertSameObject(f4444, la[i4444]);
+ assertDifferentObject(f4999, la[i4998]);
+ assertSameObject(f4999, la[i4999]);
+ }
+ }
+
+ public static void testGcRoots() {
// Initialize strings, hide this under a condition based on a volatile field.
String testString0 = null;
String testString1 = null;
@@ -50,23 +144,6 @@
// stress the read barrier implementation if concurrent collector is enabled.
for (int i = 0; i != 64 * 1024; ++i) {
allocateAtLeast1KiB();
- ManyFields mf = manyFields; // Load the volatile `manyFields` once on each iteration.
- Object[] la = largeArray; // Load the volatile `largeArray` once on each iteration.
- // Test reference field access.
- assertSameObject(f0000, mf.testField0000);
- assertSameObject(f1024, mf.testField1024);
- assertSameObject(f4444, mf.testField4444);
- assertSameObject(f4999, mf.testField4999);
- // Test array access with constant index.
- assertSameObject(f0000, la[0]);
- assertSameObject(f1024, la[1024]);
- assertSameObject(f4444, la[4444]);
- assertSameObject(f4999, la[4999]);
- // Test array access with non-constant index.
- assertSameObject(f0000, la[i0]);
- assertSameObject(f1024, la[i1024]);
- assertSameObject(f4444, la[i4444]);
- assertSameObject(f4999, la[i4999]);
// Test GC roots.
if (index0 != 12345678) {
assertSameObject(testString0, "testString0");
@@ -74,14 +151,142 @@
assertSameObject(testString2, "testString2");
assertSameObject(testString3, "testString3");
}
- // TODO: Stress GC roots (const-string/const-class, kBssEntry/kReferrersClass).
+ // TODO: Stress GC roots (const-class, kBssEntry/kReferrersClass).
+ }
+ }
+
+ public static void testUnsafeGet() throws Exception {
+ // Initialize local variables for comparison.
+ Object f0000 = manyFields.testField0000;
+ Object f1024 = manyFields.testField1024;
+ Object f4444 = manyFields.testField4444;
+ Object f4999 = manyFields.testField4999;
+ // Initialize Unsafe.
+ Unsafe unsafe = getUnsafe();
+ long f0000Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField0000"));
+ long f0001Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField0001"));
+ long f1024Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField1024"));
+ long f4444Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField4444"));
+ long f4998Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField4998"));
+ long f4999Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField4999"));
+
+ // Continually check unsafe.GetObject() while allocating
+ // over 64MiB memory (with heap size limited to 16MiB), ensuring we run GC and
+ // stress the read barrier implementation if concurrent collector is enabled.
+ for (int i = 0; i != 64 * 1024; ++i) {
+ allocateAtLeast1KiB();
+ ManyFields mf = manyFields; // Load the volatile `manyFields` once on each iteration.
+ // Test Unsafe.getObject().
+ assertSameObject(f0000, unsafe.getObject(mf, f0000Offset));
+ assertDifferentObject(f0000, unsafe.getObject(mf, f0001Offset));
+ assertSameObject(f1024, unsafe.getObject(mf, f1024Offset));
+ assertSameObject(f4444, unsafe.getObject(mf, f4444Offset));
+ assertDifferentObject(f4999, unsafe.getObject(mf, f4998Offset));
+ assertSameObject(f4999, unsafe.getObject(mf, f4999Offset));
+ }
+ }
+
+ public static void testUnsafeCas() throws Exception {
+ // Initialize local variables for comparison.
+ Object f0000 = manyFields.testField0000;
+ Object f1024 = manyFields.testField1024;
+ Object f4444 = manyFields.testField4444;
+ Object f4999 = manyFields.testField4999;
+ // Initialize Unsafe.
+ Unsafe unsafe = getUnsafe();
+ long f0000Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField0000"));
+ long f0001Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField0001"));
+ long f1024Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField1024"));
+ long f4444Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField4444"));
+ long f4998Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField4998"));
+ long f4999Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField4999"));
+
+ // Continually check Unsafe.compareAndSwapObject() while allocating
+ // over 64MiB memory (with heap size limited to 16MiB), ensuring we run GC and
+ // stress the read barrier implementation if concurrent collector is enabled.
+ for (int i = 0; i != 64 * 1024; ++i) {
+ allocateAtLeast1KiB();
+ ManyFields mf = manyFields; // Load the volatile `manyFields` once on each iteration.
+ // Test Unsafe.compareAndSwapObject().
+ assertEqual(false, unsafe.compareAndSwapObject(mf, f0000Offset, f1024, f4444));
+ assertEqual(false, unsafe.compareAndSwapObject(mf, f0001Offset, f1024, f4444));
+ assertEqual(true, unsafe.compareAndSwapObject(mf, f1024Offset, f1024, f4444));
+ assertEqual(true, unsafe.compareAndSwapObject(mf, f1024Offset, f4444, f1024));
+ assertEqual(false, unsafe.compareAndSwapObject(mf, f1024Offset, f4444, f1024));
+ assertEqual(false, unsafe.compareAndSwapObject(mf, f4444Offset, f1024, f4444));
+ assertEqual(false, unsafe.compareAndSwapObject(mf, f4998Offset, f1024, f4444));
+ assertEqual(false, unsafe.compareAndSwapObject(mf, f4999Offset, f1024, f4444));
+ }
+ }
+
+ public static void testUnsafeCasRegression() throws Exception {
+ // Initialize local variables for comparison.
+ Object f0000 = manyFields.testField0000;
+ // Initialize Unsafe.
+ Unsafe unsafe = getUnsafe();
+ long f0001Offset =
+ unsafe.objectFieldOffset(ManyFields.class.getField("testField0001"));
+
+ // Continually check Unsafe.compareAndSwapObject() while allocating
+ // over 64MiB memory (with heap size limited to 16MiB), ensuring we run GC and
+ // stress the read barrier implementation if concurrent collector is enabled.
+ for (int i = 0; i != 64 * 1024; ++i) {
+ allocateAtLeast1KiB();
+ ManyFields mf = manyFields; // Load the volatile `manyFields` once on each iteration.
+
+ // With https://android-review.googlesource.com/729224 , the intrinsic could
+ // erroneously clobber r0 on ARM for Baker read barriers because the introspection
+ // entrypoint would read the destination register from bits 12-15 of the instruction
+ // ADD (register, T3) with no shift, assuming to see LDR (immediate, T3), getting
+ // the output register number as 0 instead of the actual destination in bits 8-11.
+ // As a regression test, call a $noinline$ method which returns the result in r0,
+ // do the UnsafeCasObject and check the result of the $noinline$ call (register
+ // allocator should leave the result in r0, clobbered by the broken intrinsic).
+ int x = $noinline$foo();
+ unsafe.compareAndSwapObject(mf, f0001Offset, f0000, null); // Ignore the result.
+ if (x != 42) {
+ throw new Error();
+ }
+ }
+ }
+
+ public static int $noinline$foo() { return 42; }
+
+ public static void assertDifferentObject(Object lhs, Object rhs) {
+ if (lhs == rhs) {
+ throw new Error("Same objects: " + lhs + " and " + rhs);
}
}
public static void assertSameObject(Object lhs, Object rhs) {
- if (lhs != rhs) {
- throw new Error("Different objects: " + lhs + " and " + rhs);
- }
+ if (lhs != rhs) {
+ throw new Error("Different objects: " + lhs + " and " + rhs);
+ }
+ }
+
+ public static void assertEqual(boolean expected, boolean actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected +", got " + actual);
+ }
+ }
+
+ public static Unsafe getUnsafe() throws Exception {
+ Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
+ Field f = unsafeClass.getDeclaredField("theUnsafe");
+ f.setAccessible(true);
+ return (Unsafe) f.get(null);
}
public static void allocateAtLeast1KiB() {
@@ -97,8 +302,10 @@
public static volatile ManyFields manyFields = new ManyFields();
public static volatile Object[] largeArray = new Object[5000];
public static volatile int index0 = 0;
+ public static volatile int index1 = 1;
public static volatile int index1024 = 1024;
public static volatile int index4444 = 4444;
+ public static volatile int index4998 = 4998;
public static volatile int index4999 = 4999;
// We shall retain some allocated memory and release old allocations
diff --git a/test/305-other-fault-handler/fault_handler.cc b/test/305-other-fault-handler/fault_handler.cc
index 211d142..093a93f 100644
--- a/test/305-other-fault-handler/fault_handler.cc
+++ b/test/305-other-fault-handler/fault_handler.cc
@@ -33,7 +33,7 @@
public:
explicit TestFaultHandler(FaultManager* manager)
: FaultHandler(manager),
- map_error_(""),
+ map_error_(),
target_map_(MemMap::MapAnonymous("test-305-mmap",
/* addr */ nullptr,
/* byte_count */ kPageSize,
@@ -43,7 +43,7 @@
/* error_msg */ &map_error_,
/* use_ashmem */ false)),
was_hit_(false) {
- CHECK(target_map_ != nullptr) << "Unable to create segfault target address " << map_error_;
+ CHECK(target_map_.IsValid()) << "Unable to create segfault target address " << map_error_;
manager_->AddHandler(this, /*in_generated_code*/false);
}
@@ -59,16 +59,16 @@
was_hit_ = true;
LOG(INFO) << "SEGV Caught. mprotecting map.";
- CHECK(target_map_->Protect(PROT_READ | PROT_WRITE)) << "Failed to mprotect R/W";
+ CHECK(target_map_.Protect(PROT_READ | PROT_WRITE)) << "Failed to mprotect R/W";
LOG(INFO) << "Setting value to be read.";
*GetTargetPointer() = kDataValue;
LOG(INFO) << "Changing prot to be read-only.";
- CHECK(target_map_->Protect(PROT_READ)) << "Failed to mprotect R-only";
+ CHECK(target_map_.Protect(PROT_READ)) << "Failed to mprotect R-only";
return true;
}
void CauseSegfault() {
- CHECK_EQ(target_map_->GetProtect(), PROT_NONE);
+ CHECK_EQ(target_map_.GetProtect(), PROT_NONE);
// This will segfault. The handler should deal with it though and we will get a value out of it.
uint32_t data = *GetTargetPointer();
@@ -78,19 +78,19 @@
CHECK(was_hit_);
CHECK_EQ(data, kDataValue) << "Unexpected read value from mmap";
- CHECK_EQ(target_map_->GetProtect(), PROT_READ);
+ CHECK_EQ(target_map_.GetProtect(), PROT_READ);
LOG(INFO) << "Success!";
}
private:
uint32_t* GetTargetPointer() {
- return reinterpret_cast<uint32_t*>(target_map_->Begin() + 8);
+ return reinterpret_cast<uint32_t*>(target_map_.Begin() + 8);
}
static constexpr uint32_t kDataValue = 0xDEADBEEF;
std::string map_error_;
- std::unique_ptr<MemMap> target_map_;
+ MemMap target_map_;
bool was_hit_;
};
diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java
index 9e714f5..5ffb75f 100644
--- a/test/458-checker-instruct-simplification/src/Main.java
+++ b/test/458-checker-instruct-simplification/src/Main.java
@@ -1070,7 +1070,7 @@
/// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Const0>>,<<Const1>>]
/// CHECK-DAG: <<Cond:z\d+>> Equal [<<Phi1>>,<<Const2>>]
/// CHECK-DAG: If [<<Cond>>]
- /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Const1>>,<<Const0>>]
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Const0>>,<<Const1>>]
/// CHECK-DAG: Return [<<Phi2>>]
/// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) dead_code_elimination$after_inlining (after)
@@ -1096,7 +1096,7 @@
/// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Const0>>,<<Const1>>]
/// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Phi1>>,<<Const2>>]
/// CHECK-DAG: If [<<Cond>>]
- /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Const1>>,<<Const0>>]
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Const0>>,<<Const1>>]
/// CHECK-DAG: Return [<<Phi2>>]
/// CHECK-START: boolean Main.$noinline$NotEqualBoolVsIntConst(boolean) dead_code_elimination$after_inlining (after)
diff --git a/test/563-checker-fakestring/smali/TestCase.smali b/test/563-checker-fakestring/smali/TestCase.smali
index 9d10bd7..0fe39ee 100644
--- a/test/563-checker-fakestring/smali/TestCase.smali
+++ b/test/563-checker-fakestring/smali/TestCase.smali
@@ -305,3 +305,35 @@
return-object v0
.end method
+
+## CHECK-START: java.lang.String TestCase.loopAndStringInitAndPhi(byte[], boolean) register (after)
+## CHECK: NewInstance
+## CHECK-NOT: NewInstance
+## CHECK-DAG: <<Invoke1:l\d+>> InvokeStaticOrDirect method_name:java.lang.String.<init>
+## CHECK-DAG: <<Invoke2:l\d+>> InvokeStaticOrDirect method_name:java.lang.String.<init>
+## CHECK-DAG: <<Phi:l\d+>> Phi [<<Invoke2>>,<<Invoke1>>]
+## CHECK-DAG: Return [<<Phi>>]
+.method public static loopAndStringInitAndPhi([BZ)Ljava/lang/String;
+ .registers 4
+
+ if-nez p1, :allocate_other
+ new-instance v0, Ljava/lang/String;
+
+ # Loop
+ :loop_header
+ if-eqz p1, :loop_exit
+ goto :loop_header
+
+ :loop_exit
+ const-string v1, "UTF8"
+ invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V
+ goto : exit
+
+ :allocate_other
+ const-string v1, "UTF8"
+ new-instance v0, Ljava/lang/String;
+ invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V
+ :exit
+ return-object v0
+
+.end method
diff --git a/test/563-checker-fakestring/src/Main.java b/test/563-checker-fakestring/src/Main.java
index 3639d59..df9e9dc 100644
--- a/test/563-checker-fakestring/src/Main.java
+++ b/test/563-checker-fakestring/src/Main.java
@@ -113,7 +113,6 @@
result = (String) m.invoke(null, new Object[] { testData, false });
assertEqual(testString, result);
}
-
{
Method m = c.getMethod(
"deoptimizeNewInstanceAfterLoop", int[].class, byte[].class, int.class);
@@ -127,6 +126,13 @@
}
}
}
+ {
+ Method m = c.getMethod("loopAndStringInitAndPhi", byte[].class, boolean.class);
+ String result = (String) m.invoke(null, new Object[] { testData, true });
+ assertEqual(testString, result);
+ result = (String) m.invoke(null, new Object[] { testData, false });
+ assertEqual(testString, result);
+ }
}
public static boolean doThrow = false;
diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java
index 1460725..dd76e41 100644
--- a/test/618-checker-induction/src/Main.java
+++ b/test/618-checker-induction/src/Main.java
@@ -290,7 +290,7 @@
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
/// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
/// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:<<Loop1>>
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
//
/// CHECK-START: int Main.closedFormNested() loop_optimization (after)
/// CHECK-NOT: Phi
@@ -313,7 +313,7 @@
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
/// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
/// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:<<Loop1>>
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
//
/// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (after)
/// CHECK-NOT: Phi
@@ -547,7 +547,7 @@
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
/// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi4>>] loop:none
+ /// CHECK-DAG: Return [<<Phi3>>] loop:none
/// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
//
/// CHECK-START: int Main.closedFeed() loop_optimization (after)
@@ -634,7 +634,7 @@
/// CHECK-START: boolean Main.periodicBoolIdiom1() loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
//
/// CHECK-START: boolean Main.periodicBoolIdiom1() loop_optimization (after)
/// CHECK-NOT: Phi
@@ -653,7 +653,7 @@
/// CHECK-START: boolean Main.periodicBoolIdiom2() loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
//
/// CHECK-START: boolean Main.periodicBoolIdiom2() loop_optimization (after)
/// CHECK-NOT: Phi
@@ -672,7 +672,7 @@
/// CHECK-START: boolean Main.periodicBoolIdiom3() loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
//
/// CHECK-START: boolean Main.periodicBoolIdiom3() loop_optimization (after)
/// CHECK-NOT: Phi
@@ -691,7 +691,7 @@
/// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
//
/// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (after)
/// CHECK-NOT: Phi
@@ -705,7 +705,7 @@
/// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
//
/// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (after)
/// CHECK-NOT: Phi
@@ -719,7 +719,7 @@
/// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
//
/// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (after)
/// CHECK-NOT: Phi
diff --git a/test/635-checker-arm64-volatile-load-cc/src/Main.java b/test/635-checker-arm64-volatile-load-cc/src/Main.java
index 6a26e94..89fad4c 100644
--- a/test/635-checker-arm64-volatile-load-cc/src/Main.java
+++ b/test/635-checker-arm64-volatile-load-cc/src/Main.java
@@ -255,9 +255,9 @@
/// CHECK-START-ARM64: void Main.testStaticVolatileFieldGetWithLargeOffset() disassembly (after)
/// CHECK: StaticFieldGet
- /// CHECK: mov x17, #<<Offset:0x[0-9a-f]{4}>>
- /// CHECK: add x16, {{x\d+}}, x17
- /// CHECK: ldar {{w\d+}}, [x16]
+ /// CHECK: mov <<Kind:x|w>><<Temp1:\d+>>, #<<Offset:0x[0-9a-f]{4}>>
+ /// CHECK: add <<Kind>><<Temp2:\d+>>, <<Kind>>{{\d+}}, <<Kind>><<Temp1>>
+ /// CHECK: ldar {{w\d+}}, [x<<Temp2>>]
static void testStaticVolatileFieldGetWithLargeOffset() {
// The offset of this static field cannot be encoded as an immediate on ARM64.
Object s = s999;
@@ -265,9 +265,9 @@
/// CHECK-START-ARM64: void Main.testInstanceVolatileFieldGetWithLargeOffset() disassembly (after)
/// CHECK: InstanceFieldGet
- /// CHECK: mov x17, #<<Offset:0x[0-9a-f]{4}>>
- /// CHECK: add x16, {{x\d+}}, x17
- /// CHECK: ldar {{w\d+}}, [x16]
+ /// CHECK: mov <<Kind:x|w>><<Temp1:\d+>>, #<<Offset:0x[0-9a-f]{4}>>
+ /// CHECK: add <<Kind>><<Temp2:\d+>>, <<Kind>>{{\d+}}, <<Kind>><<Temp1>>
+ /// CHECK: ldar {{w\d+}}, [x<<Temp2>>]
void testInstanceVolatileFieldGetWithLargeOffset() {
// The offset of this instance field cannot be encoded as an immediate on ARM64.
Object i = i1029;
diff --git a/test/669-checker-break/src/Main.java b/test/669-checker-break/src/Main.java
index e59061b..c40e4a6 100644
--- a/test/669-checker-break/src/Main.java
+++ b/test/669-checker-break/src/Main.java
@@ -232,8 +232,8 @@
/// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
/// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
/// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Red:i\d+>> Phi [<<Zero>>,<<RedI:i\d+>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:i\d+>> Phi [<<Zero>>,<<RedI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Nil>>,<<Bnd>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<RedI>> Add [<<Red>>,<<Get>>] loop:<<Loop>> outer_loop:none
@@ -248,8 +248,8 @@
/// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
/// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
/// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Red:i\d+>> Phi [<<Zero>>,<<RedI:i\d+>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Red:i\d+>> Phi [<<Zero>>,<<RedI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: If [<<LE>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index 04c3fbf..96754c3 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -63,8 +63,8 @@
static jobject NewInstance(JNIEnv* env, jclass klass) {
jmethodID constructor = env->GetMethodID(klass, "<init>", "()V");
- if (constructor == NULL) {
- return NULL;
+ if (constructor == nullptr) {
+ return nullptr;
}
return env->NewObject(klass, constructor);
}
@@ -74,7 +74,7 @@
ScopedUtfChars utf_name(env, name);
jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
: env->GetFieldID(klass, utf_name.c_str(), "I");
- if (field == NULL) {
+ if (field == nullptr) {
env->ExceptionClear();
return JNI_FALSE;
}
@@ -87,7 +87,7 @@
ScopedUtfChars utf_name(env, name);
jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
: env->GetFieldID(klass, utf_name.c_str(), "I");
- if (field == NULL) {
+ if (field == nullptr) {
env->ExceptionClear();
return JNI_FALSE;
}
@@ -95,7 +95,7 @@
env->GetStaticIntField(klass, field);
} else {
jobject obj = NewInstance(env, klass);
- if (obj == NULL) {
+ if (obj == nullptr) {
env->ExceptionDescribe();
env->ExceptionClear();
return JNI_FALSE;
@@ -117,7 +117,7 @@
ScopedUtfChars utf_name(env, name);
jfieldID field = is_static ? env->GetStaticFieldID(klass, utf_name.c_str(), "I")
: env->GetFieldID(klass, utf_name.c_str(), "I");
- if (field == NULL) {
+ if (field == nullptr) {
env->ExceptionClear();
return JNI_FALSE;
}
@@ -125,7 +125,7 @@
env->SetStaticIntField(klass, field, 42);
} else {
jobject obj = NewInstance(env, klass);
- if (obj == NULL) {
+ if (obj == nullptr) {
env->ExceptionDescribe();
env->ExceptionClear();
return JNI_FALSE;
@@ -147,7 +147,7 @@
ScopedUtfChars utf_name(env, name);
jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
: env->GetMethodID(klass, utf_name.c_str(), "()I");
- if (method == NULL) {
+ if (method == nullptr) {
env->ExceptionClear();
return JNI_FALSE;
}
@@ -160,7 +160,7 @@
ScopedUtfChars utf_name(env, name);
jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
: env->GetMethodID(klass, utf_name.c_str(), "()I");
- if (method == NULL) {
+ if (method == nullptr) {
env->ExceptionClear();
return JNI_FALSE;
}
@@ -169,7 +169,7 @@
env->CallStaticIntMethodA(klass, method, nullptr);
} else {
jobject obj = NewInstance(env, klass);
- if (obj == NULL) {
+ if (obj == nullptr) {
env->ExceptionDescribe();
env->ExceptionClear();
return JNI_FALSE;
@@ -191,7 +191,7 @@
ScopedUtfChars utf_name(env, name);
jmethodID method = is_static ? env->GetStaticMethodID(klass, utf_name.c_str(), "()I")
: env->GetMethodID(klass, utf_name.c_str(), "()I");
- if (method == NULL) {
+ if (method == nullptr) {
env->ExceptionClear();
return JNI_FALSE;
}
@@ -200,7 +200,7 @@
env->CallStaticIntMethod(klass, method);
} else {
jobject obj = NewInstance(env, klass);
- if (obj == NULL) {
+ if (obj == nullptr) {
env->ExceptionDescribe();
env->ExceptionClear();
return JNI_FALSE;
@@ -224,7 +224,7 @@
JNIEnv* env, jclass, jclass klass, jstring args) {
ScopedUtfChars utf_args(env, args);
jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
- if (constructor == NULL) {
+ if (constructor == nullptr) {
env->ExceptionClear();
return JNI_FALSE;
}
@@ -236,7 +236,7 @@
JNIEnv* env, jclass, jclass klass, jstring args) {
ScopedUtfChars utf_args(env, args);
jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
- if (constructor == NULL) {
+ if (constructor == nullptr) {
env->ExceptionClear();
return JNI_FALSE;
}
@@ -261,7 +261,7 @@
JNIEnv* env, jclass, jclass klass, jstring args) {
ScopedUtfChars utf_args(env, args);
jmethodID constructor = env->GetMethodID(klass, "<init>", utf_args.c_str());
- if (constructor == NULL) {
+ if (constructor == nullptr) {
env->ExceptionClear();
return JNI_FALSE;
}
diff --git a/test/988-method-trace/expected.txt b/test/988-method-trace/expected.txt
index 75ee112..b263308 100644
--- a/test/988-method-trace/expected.txt
+++ b/test/988-method-trace/expected.txt
@@ -70,6 +70,27 @@
fibonacci(5)=5
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
+=> art.Test988$NativeOp()
+.=> public java.lang.Object()
+.<= public java.lang.Object() -> <null: null>
+<= art.Test988$NativeOp() -> <null: null>
+=> public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator)
+.=> public int art.Test988$NativeOp.applyAsInt(int)
+..=> static int art.Test988.nativeFibonacci(int)
+..<= static int art.Test988.nativeFibonacci(int) -> <class java.lang.Integer: 5>
+.<= public int art.Test988$NativeOp.applyAsInt(int) -> <class java.lang.Integer: 5>
+.=> public art.Test988$FibResult(java.lang.String,int,int)
+..=> public java.lang.Object()
+..<= public java.lang.Object() -> <null: null>
+.<= public art.Test988$FibResult(java.lang.String,int,int) -> <null: null>
+.=> public boolean java.util.ArrayList.add(java.lang.Object)
+..=> private void java.util.ArrayList.ensureCapacityInternal(int)
+...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
+...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
+..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+fibonacci(5)=5
+.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> art.Test988$IterOp()
.=> public java.lang.Object()
.<= public java.lang.Object() -> <null: null>
@@ -147,8 +168,8 @@
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
art.Test988.iter_fibonacci(Test988.java:255)
art.Test988$IterOp.applyAsInt(Test988.java:250)
- art.Test988.doFibTest(Test988.java:378)
- art.Test988.run(Test988.java:336)
+ art.Test988.doFibTest(Test988.java:388)
+ art.Test988.run(Test988.java:344)
<additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -167,8 +188,8 @@
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
art.Test988.iter_fibonacci(Test988.java:255)
art.Test988$IterOp.applyAsInt(Test988.java:250)
- art.Test988.doFibTest(Test988.java:378)
- art.Test988.run(Test988.java:336)
+ art.Test988.doFibTest(Test988.java:388)
+ art.Test988.run(Test988.java:344)
<additional hidden frames>
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
@@ -250,8 +271,8 @@
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
art.Test988.fibonacci(Test988.java:277)
art.Test988$RecurOp.applyAsInt(Test988.java:272)
- art.Test988.doFibTest(Test988.java:378)
- art.Test988.run(Test988.java:337)
+ art.Test988.doFibTest(Test988.java:388)
+ art.Test988.run(Test988.java:345)
<additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -270,8 +291,53 @@
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
art.Test988.fibonacci(Test988.java:277)
art.Test988$RecurOp.applyAsInt(Test988.java:272)
- art.Test988.doFibTest(Test988.java:378)
- art.Test988.run(Test988.java:337)
+ art.Test988.doFibTest(Test988.java:388)
+ art.Test988.run(Test988.java:345)
+ <additional hidden frames>
+
+.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
+=> art.Test988$NativeOp()
+.=> public java.lang.Object()
+.<= public java.lang.Object() -> <null: null>
+<= art.Test988$NativeOp() -> <null: null>
+=> public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator)
+.=> public int art.Test988$NativeOp.applyAsInt(int)
+..=> static int art.Test988.nativeFibonacci(int)
+...=> public java.lang.Error(java.lang.String)
+....=> public java.lang.Throwable(java.lang.String)
+.....=> public java.lang.Object()
+.....<= public java.lang.Object() -> <null: null>
+.....=> public static final java.util.List java.util.Collections.emptyList()
+.....<= public static final java.util.List java.util.Collections.emptyList() -> <class java.util.Collections$EmptyList: []>
+.....=> public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace()
+......=> private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
+......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
+.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: bad argument
+ art.Test988.nativeFibonacci(Native Method)
+ art.Test988$NativeOp.applyAsInt(Test988.java:287)
+ art.Test988.doFibTest(Test988.java:388)
+ art.Test988.run(Test988.java:346)
+ <additional hidden frames>
+>
+....<= public java.lang.Throwable(java.lang.String) -> <null: null>
+...<= public java.lang.Error(java.lang.String) -> <null: null>
+..<= static int art.Test988.nativeFibonacci(int) EXCEPTION
+.<= public int art.Test988$NativeOp.applyAsInt(int) EXCEPTION
+.=> public art.Test988$FibThrow(java.lang.String,int,java.lang.Throwable)
+..=> public java.lang.Object()
+..<= public java.lang.Object() -> <null: null>
+.<= public art.Test988$FibThrow(java.lang.String,int,java.lang.Throwable) -> <null: null>
+.=> public boolean java.util.ArrayList.add(java.lang.Object)
+..=> private void java.util.ArrayList.ensureCapacityInternal(int)
+...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
+...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
+..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+fibonacci(-19) -> java.lang.Error: bad argument
+ art.Test988.nativeFibonacci(Native Method)
+ art.Test988$NativeOp.applyAsInt(Test988.java:287)
+ art.Test988.doFibTest(Test988.java:388)
+ art.Test988.run(Test988.java:346)
<additional hidden frames>
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
diff --git a/test/988-method-trace/src/art/Test988.java b/test/988-method-trace/src/art/Test988.java
index 5720d1d..075e075 100644
--- a/test/988-method-trace/src/art/Test988.java
+++ b/test/988-method-trace/src/art/Test988.java
@@ -282,6 +282,13 @@
}
}
+ static final class NativeOp implements IntUnaryOperator {
+ public int applyAsInt(int x) {
+ return nativeFibonacci(x);
+ }
+ }
+ static native int nativeFibonacci(int n);
+
static final class TestRunnableInvokeHandler implements InvocationHandler {
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
return null;
@@ -333,8 +340,10 @@
Thread.currentThread());
doFibTest(30, new IterOp());
doFibTest(5, new RecurOp());
+ doFibTest(5, new NativeOp());
doFibTest(-19, new IterOp());
doFibTest(-19, new RecurOp());
+ doFibTest(-19, new NativeOp());
runnable.run();
@@ -358,6 +367,7 @@
ArrayList.class.toString();
RecurOp.class.toString();
IterOp.class.toString();
+ NativeOp.class.toString();
StringBuilder.class.toString();
Runnable.class.toString();
TestRunnableInvokeHandler.class.toString();
diff --git a/test/988-method-trace/trace_fib.cc b/test/988-method-trace/trace_fib.cc
new file mode 100644
index 0000000..682f273
--- /dev/null
+++ b/test/988-method-trace/trace_fib.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+
+namespace art {
+namespace Test988MethodTrace {
+
+extern "C" JNIEXPORT jint JNICALL Java_art_Test988_nativeFibonacci(JNIEnv* env, jclass, jint n) {
+ if (n < 0) {
+ env->ThrowNew(env->FindClass("java/lang/Error"), "bad argument");
+ return -1;
+ } else if (n == 0) {
+ return 0;
+ }
+ jint x = 1;
+ jint y = 1;
+ for (jint i = 3; i <= n; i++) {
+ jint z = x + y;
+ x = y;
+ y = z;
+ }
+ return y;
+}
+
+} // namespace Test988MethodTrace
+} // namespace art
+
diff --git a/test/Android.bp b/test/Android.bp
index a3de382..e265651 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -262,6 +262,7 @@
"984-obsolete-invoke/obsolete_invoke.cc",
"986-native-method-bind/native_bind.cc",
"987-agent-bind/agent_bind.cc",
+ "988-method-trace/trace_fib.cc",
"989-method-trace-throw/method_trace.cc",
"991-field-trace-2/field_trace.cc",
"992-source-data/source_file.cc",
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 9378bff..a280f85 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -986,6 +986,7 @@
"678-quickening",
"679-locks",
"999-redefine-hiddenapi",
+ "1000-non-moving-space-stress",
"1951-monitor-enter-no-suspend"],
"variant": "jvm",
"description": ["Doesn't run on RI."]
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index d5a4d45..47b1e4e 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -265,13 +265,11 @@
'ART_USE_READ_BARRIER' : 'false'
}
},
- # TODO: Remove this configuration, when the ART Buildbot is no
- # longer using it for 'host-x86_64-valgrind'.
- 'art-gtest-valgrind64': {
- # Disabled: Valgrind is no longer supported.
- # 'make' : 'valgrind-test-art-host64',
- 'env': {
- 'ART_USE_READ_BARRIER' : 'false'
+ 'art-generational-cc': {
+ 'make' : 'test-art-host-gtest',
+ 'run-test' : [],
+ 'env' : {
+ 'ART_USE_GENERATIONAL_CC' : 'true'
}
},
diff --git a/tools/art b/tools/art
index 62df7eb..aebf5a6 100644
--- a/tools/art
+++ b/tools/art
@@ -359,7 +359,7 @@
fi
if [ "$PERF" != "" ]; then
- LAUNCH_WRAPPER="perf record -g -o $ANDROID_DATA/perf.data -e cycles:u $LAUNCH_WRAPPER"
+ LAUNCH_WRAPPER="perf record -g --call-graph dwarf -F 10000 -o $ANDROID_DATA/perf.data -e cycles:u $LAUNCH_WRAPPER"
EXTRA_OPTIONS+=(-Xcompiler-option --generate-debug-info)
fi
diff --git a/tools/art_verifier/Android.bp b/tools/art_verifier/Android.bp
index af9744f..afd52fb 100644
--- a/tools/art_verifier/Android.bp
+++ b/tools/art_verifier/Android.bp
@@ -36,6 +36,9 @@
"libtombstoned_client_static",
],
},
+ darwin: {
+ enabled: false,
+ },
},
}
diff --git a/tools/class2greylist/Android.bp b/tools/class2greylist/Android.bp
index 7b1233b..1e3cdff 100644
--- a/tools/class2greylist/Android.bp
+++ b/tools/class2greylist/Android.bp
@@ -20,6 +20,7 @@
static_libs: [
"commons-cli-1.2",
"apache-bcel",
+ "guava",
],
}
diff --git a/tools/class2greylist/src/com/android/class2greylist/AnnotationContext.java b/tools/class2greylist/src/com/android/class2greylist/AnnotationContext.java
new file mode 100644
index 0000000..eb54a33
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/AnnotationContext.java
@@ -0,0 +1,66 @@
+package com.android.class2greylist;
+
+import org.apache.bcel.Const;
+import org.apache.bcel.classfile.FieldOrMethod;
+import org.apache.bcel.classfile.JavaClass;
+
+import java.util.Locale;
+
+/**
+ * Encapsulates context for a single annotation on a class member.
+ */
+public class AnnotationContext {
+
+ public final Status status;
+ public final FieldOrMethod member;
+ public final JavaClass definingClass;
+ public final String signatureFormatString;
+
+ public AnnotationContext(
+ Status status,
+ FieldOrMethod member,
+ JavaClass definingClass,
+ String signatureFormatString) {
+ this.status = status;
+ this.member = member;
+ this.definingClass = definingClass;
+ this.signatureFormatString = signatureFormatString;
+ }
+
+ /**
+ * @return the full descriptor of enclosing class.
+ */
+ public String getClassDescriptor() {
+ // JavaClass.getName() returns the Java-style name (with . not /), so we must fetch
+ // the original class name from the constant pool.
+ return definingClass.getConstantPool().getConstantString(
+ definingClass.getClassNameIndex(), Const.CONSTANT_Class);
+ }
+
+ /**
+ * @return the full descriptor of this member, in the format expected in
+ * the greylist.
+ */
+ public String getMemberDescriptor() {
+ return String.format(Locale.US, signatureFormatString,
+ getClassDescriptor(), member.getName(), member.getSignature());
+ }
+
+ /**
+ * Report an error in this context. The final error message will include
+ * the class and member names, and the source file name.
+ */
+ public void reportError(String message, Object... args) {
+ StringBuilder error = new StringBuilder();
+ error.append(definingClass.getSourceFileName())
+ .append(": ")
+ .append(definingClass.getClassName())
+ .append(".")
+ .append(member.getName())
+ .append(": ")
+ .append(String.format(Locale.US, message, args));
+
+ status.error(error.toString());
+ }
+
+}
diff --git a/tools/class2greylist/src/com/android/class2greylist/AnnotationHandler.java b/tools/class2greylist/src/com/android/class2greylist/AnnotationHandler.java
new file mode 100644
index 0000000..92d2ab6
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/AnnotationHandler.java
@@ -0,0 +1,11 @@
+package com.android.class2greylist;
+
+import org.apache.bcel.classfile.AnnotationEntry;
+
+/**
+ * Interface for an annotation handler, which handle individual annotations on
+ * class members.
+ */
+public interface AnnotationHandler {
+ void handleAnnotation(AnnotationEntry annotation, AnnotationContext context);
+}
diff --git a/tools/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java b/tools/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
index 6b9ef16..b805b30 100644
--- a/tools/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
+++ b/tools/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
@@ -16,41 +16,40 @@
package com.android.class2greylist;
-import org.apache.bcel.Const;
import org.apache.bcel.classfile.AnnotationEntry;
import org.apache.bcel.classfile.DescendingVisitor;
-import org.apache.bcel.classfile.ElementValuePair;
import org.apache.bcel.classfile.EmptyVisitor;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.FieldOrMethod;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
-import java.util.Locale;
+import java.util.Map;
/**
- * Visits a JavaClass instance and pulls out all members annotated with a
- * specific annotation. The signatures of such members are passed to {@link
- * Status#greylistEntry(String)}. Any errors result in a call to {@link
- * Status#error(String)}.
- *
- * If the annotation has a property "expectedSignature" the generated signature
- * will be verified against the one specified there. If it differs, an error
- * will be generated.
+ * Visits a JavaClass instance and passes any annotated members to a {@link AnnotationHandler}
+ * according to the map provided.
*/
public class AnnotationVisitor extends EmptyVisitor {
- private static final String EXPECTED_SIGNATURE = "expectedSignature";
-
private final JavaClass mClass;
- private final String mAnnotationType;
private final Status mStatus;
private final DescendingVisitor mDescendingVisitor;
+ private final Map<String, AnnotationHandler> mAnnotationHandlers;
- public AnnotationVisitor(JavaClass clazz, String annotation, Status d) {
+ /**
+ * Creates a visitor for a class.
+ *
+ * @param clazz Class to visit
+ * @param status For reporting debug information
+ * @param handlers Map of {@link AnnotationHandler}. The keys should be annotation names, as
+ * class descriptors.
+ */
+ public AnnotationVisitor(JavaClass clazz, Status status,
+ Map<String, AnnotationHandler> handlers) {
mClass = clazz;
- mAnnotationType = annotation;
- mStatus = d;
+ mStatus = status;
+ mAnnotationHandlers = handlers;
mDescendingVisitor = new DescendingVisitor(clazz, this);
}
@@ -59,13 +58,6 @@
mDescendingVisitor.visit();
}
- private static String getClassDescriptor(JavaClass clazz) {
- // JavaClass.getName() returns the Java-style name (with . not /), so we must fetch
- // the original class name from the constant pool.
- return clazz.getConstantPool().getConstantString(
- clazz.getClassNameIndex(), Const.CONSTANT_Class);
- }
-
@Override
public void visitMethod(Method method) {
visitMember(method, "L%s;->%s%s");
@@ -77,47 +69,15 @@
}
private void visitMember(FieldOrMethod member, String signatureFormatString) {
- JavaClass definingClass = (JavaClass) mDescendingVisitor.predecessor();
mStatus.debug("Visit member %s : %s", member.getName(), member.getSignature());
+ AnnotationContext context = new AnnotationContext(mStatus, member,
+ (JavaClass) mDescendingVisitor.predecessor(), signatureFormatString);
for (AnnotationEntry a : member.getAnnotationEntries()) {
- if (mAnnotationType.equals(a.getAnnotationType())) {
- mStatus.debug("Member has annotation %s", mAnnotationType);
- boolean bridge = (member.getAccessFlags() & Const.ACC_BRIDGE) != 0;
- if (bridge) {
- mStatus.debug("Member is a bridge", mAnnotationType);
- }
- String signature = String.format(Locale.US, signatureFormatString,
- getClassDescriptor(definingClass), member.getName(), member.getSignature());
- for (ElementValuePair property : a.getElementValuePairs()) {
- switch (property.getNameString()) {
- case EXPECTED_SIGNATURE:
- String expected = property.getValue().stringifyValue();
- // Don't enforce for bridge methods; they're generated so won't match.
- if (!bridge && !signature.equals(expected)) {
- error(definingClass, member,
- "Expected signature does not match generated:\n"
- + "Expected: %s\n"
- + "Generated: %s", expected, signature);
- }
- break;
- }
- }
- mStatus.greylistEntry(signature);
+ if (mAnnotationHandlers.containsKey(a.getAnnotationType())) {
+ mStatus.debug("Member has annotation %s for which we have a handler",
+ a.getAnnotationType());
+ mAnnotationHandlers.get(a.getAnnotationType()).handleAnnotation(a, context);
}
}
}
-
- private void error(JavaClass clazz, FieldOrMethod member, String message, Object... args) {
- StringBuilder error = new StringBuilder();
- error.append(clazz.getSourceFileName())
- .append(": ")
- .append(clazz.getClassName())
- .append(".")
- .append(member.getName())
- .append(": ")
- .append(String.format(Locale.US, message, args));
-
- mStatus.error(error.toString());
- }
-
}
diff --git a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
index 3e9e320..694a5f4 100644
--- a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
+++ b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
@@ -16,6 +16,12 @@
package com.android.class2greylist;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
@@ -23,9 +29,16 @@
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
-import org.apache.commons.cli.PatternOptionBuilder;
+import java.io.File;
import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
/**
* Build time tool for extracting a list of members from jar files that have the @UsedByApps
@@ -33,11 +46,41 @@
*/
public class Class2Greylist {
- private static final String ANNOTATION_TYPE = "Landroid/annotation/UnsupportedAppUsage;";
+ private static final String GREYLIST_ANNOTATION = "Landroid/annotation/UnsupportedAppUsage;";
+ private static final Set<String> WHITELIST_ANNOTATIONS = ImmutableSet.of();
+
+ private final Status mStatus;
+ private final String mPublicApiListFile;
+ private final String[] mPerSdkOutputFiles;
+ private final String mWhitelistFile;
+ private final String[] mJarFiles;
+ private final GreylistConsumer mOutput;
+ private final Set<Integer> mAllowedSdkVersions;
+ private final Set<String> mPublicApis;
+
public static void main(String[] args) {
Options options = new Options();
options.addOption(OptionBuilder
+ .withLongOpt("public-api-list")
+ .hasArgs(1)
+ .withDescription("Public API list file. Used to de-dupe bridge methods.")
+ .create("p"));
+ options.addOption(OptionBuilder
+ .withLongOpt("write-greylist")
+ .hasArgs()
+ .withDescription(
+ "Specify file to write greylist to. Can be specified multiple times. " +
+ "Format is either just a filename, or \"int:filename\". If an integer is " +
+ "given, members with a matching maxTargetSdk are written to the file; if " +
+ "no integer is given, members with no maxTargetSdk are written.")
+ .create("g"));
+ options.addOption(OptionBuilder
+ .withLongOpt("write-whitelist")
+ .hasArgs(1)
+ .withDescription("Specify file to write whitelist to.")
+ .create('w'));
+ options.addOption(OptionBuilder
.withLongOpt("debug")
.hasArgs(0)
.withDescription("Enable debug")
@@ -62,6 +105,7 @@
help(options);
}
+
String[] jarFiles = cmd.getArgs();
if (jarFiles.length == 0) {
System.err.println("Error: no jar files specified.");
@@ -69,18 +113,18 @@
}
Status status = new Status(cmd.hasOption('d'));
-
- for (String jarFile : jarFiles) {
- status.debug("Processing jar file %s", jarFile);
- try {
- JarReader reader = new JarReader(status, jarFile);
- reader.stream().forEach(clazz -> new AnnotationVisitor(
- clazz, ANNOTATION_TYPE, status).visit());
- reader.close();
- } catch (IOException e) {
- status.error(e);
- }
+ try {
+ Class2Greylist c2gl = new Class2Greylist(
+ status,
+ cmd.getOptionValue('p', null),
+ cmd.getOptionValues('g'),
+ cmd.getOptionValue('w', null),
+ jarFiles);
+ c2gl.main();
+ } catch (IOException e) {
+ status.error(e);
}
+
if (status.ok()) {
System.exit(0);
} else {
@@ -89,6 +133,94 @@
}
+ @VisibleForTesting
+ Class2Greylist(Status status, String publicApiListFile, String[] perSdkLevelOutputFiles,
+ String whitelistOutputFile, String[] jarFiles) throws IOException {
+ mStatus = status;
+ mPublicApiListFile = publicApiListFile;
+ mPerSdkOutputFiles = perSdkLevelOutputFiles;
+ mWhitelistFile = whitelistOutputFile;
+ mJarFiles = jarFiles;
+ if (mPerSdkOutputFiles != null) {
+ Map<Integer, String> outputFiles = readGreylistMap(mStatus, mPerSdkOutputFiles);
+ mOutput = new FileWritingGreylistConsumer(mStatus, outputFiles, mWhitelistFile);
+ mAllowedSdkVersions = outputFiles.keySet();
+ } else {
+ // TODO remove this once per-SDK greylist support integrated into the build.
+ // Right now, mPerSdkOutputFiles is always null as the build never passes the
+ // corresponding command lind flags. Once the build is updated, can remove this.
+ mOutput = new SystemOutGreylistConsumer();
+ mAllowedSdkVersions = new HashSet<>(Arrays.asList(null, 26, 28));
+ }
+
+ if (mPublicApiListFile != null) {
+ mPublicApis = Sets.newHashSet(
+ Files.readLines(new File(mPublicApiListFile), Charset.forName("UTF-8")));
+ } else {
+ mPublicApis = Collections.emptySet();
+ }
+ }
+
+ private Map<String, AnnotationHandler> createAnnotationHandlers() {
+ return ImmutableMap.<String, AnnotationHandler>builder()
+ .put(GreylistAnnotationHandler.ANNOTATION_NAME,
+ new GreylistAnnotationHandler(
+ mStatus, mOutput, mPublicApis, mAllowedSdkVersions))
+ .put(CovariantReturnTypeHandler.ANNOTATION_NAME,
+ new CovariantReturnTypeHandler(mOutput, mPublicApis))
+ .put(CovariantReturnTypeMultiHandler.ANNOTATION_NAME,
+ new CovariantReturnTypeMultiHandler(mOutput, mPublicApis))
+ .build();
+ }
+
+ private void main() throws IOException {
+ Map<String, AnnotationHandler> handlers = createAnnotationHandlers();
+ for (String jarFile : mJarFiles) {
+ mStatus.debug("Processing jar file %s", jarFile);
+ try {
+ JarReader reader = new JarReader(mStatus, jarFile);
+ reader.stream().forEach(clazz -> new AnnotationVisitor(clazz, mStatus, handlers)
+ .visit());
+ reader.close();
+ } catch (IOException e) {
+ mStatus.error(e);
+ }
+ }
+ mOutput.close();
+ }
+
+ @VisibleForTesting
+ static Map<Integer, String> readGreylistMap(Status status, String[] argValues) {
+ Map<Integer, String> map = new HashMap<>();
+ for (String sdkFile : argValues) {
+ Integer maxTargetSdk = null;
+ String filename;
+ int colonPos = sdkFile.indexOf(':');
+ if (colonPos != -1) {
+ try {
+ maxTargetSdk = Integer.valueOf(sdkFile.substring(0, colonPos));
+ } catch (NumberFormatException nfe) {
+ status.error("Not a valid integer: %s from argument value '%s'",
+ sdkFile.substring(0, colonPos), sdkFile);
+ }
+ filename = sdkFile.substring(colonPos + 1);
+ if (filename.length() == 0) {
+ status.error("Not a valid file name: %s from argument value '%s'",
+ filename, sdkFile);
+ }
+ } else {
+ maxTargetSdk = null;
+ filename = sdkFile;
+ }
+ if (map.containsKey(maxTargetSdk)) {
+ status.error("Multiple output files for maxTargetSdk %s", maxTargetSdk);
+ } else {
+ map.put(maxTargetSdk, filename);
+ }
+ }
+ return map;
+ }
+
private static void help(Options options) {
new HelpFormatter().printHelp(
"class2greylist path/to/classes.jar [classes2.jar ...]",
diff --git a/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java b/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java
new file mode 100644
index 0000000..afd15b4
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java
@@ -0,0 +1,89 @@
+package com.android.class2greylist;
+
+import com.google.common.base.Preconditions;
+
+import org.apache.bcel.classfile.AnnotationEntry;
+import org.apache.bcel.classfile.ElementValuePair;
+import org.apache.bcel.classfile.Method;
+
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Handles {@code CovariantReturnType} annotations, generating whitelist
+ * entries from them.
+ *
+ * <p>A whitelist entry is generated with the same descriptor as the original
+ * method, but with the return type replaced with than specified by the
+ * {@link #RETURN_TYPE} property.
+ *
+ * <p>Methods are also validated against the public API list, to assert that
+ * the annotated method is already a public API.
+ */
+public class CovariantReturnTypeHandler implements AnnotationHandler {
+
+ private static final String SHORT_NAME = "CovariantReturnType";
+ public static final String ANNOTATION_NAME = "Ldalvik/annotation/codegen/CovariantReturnType;";
+
+ private static final String RETURN_TYPE = "returnType";
+
+ private final GreylistConsumer mConsumer;
+ private final Set<String> mPublicApis;
+
+ public CovariantReturnTypeHandler(GreylistConsumer consumer, Set<String> publicApis) {
+ mConsumer = consumer;
+ mPublicApis = publicApis;
+ }
+
+ @Override
+ public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) {
+ // Verify that the annotation has been applied to what we expect, and
+ // has the right form. Note, this should not strictly be necessary, as
+ // the annotation has a target of just 'method' and the property
+ // returnType does not have a default value, but checking makes the code
+ // less brittle to future changes.
+ if (!(context.member instanceof Method)) {
+ context.reportError("Cannot specify %s on a field", RETURN_TYPE);
+ return;
+ }
+ String returnType = findReturnType(annotation);
+ if (returnType == null) {
+ context.reportError("No %s set on @%s", RETURN_TYPE, SHORT_NAME);
+ return;
+ }
+ if (!mPublicApis.contains(context.getMemberDescriptor())) {
+ context.reportError("Found @%s on non-SDK method", SHORT_NAME);
+ return;
+ }
+
+ // Generate the signature of overload that we expect the annotation will
+ // cause the platform dexer to create.
+ String typeSignature = context.member.getSignature();
+ int closingBrace = typeSignature.indexOf(')');
+ Preconditions.checkState(closingBrace != -1,
+ "No ) found in method type signature %s", typeSignature);
+ typeSignature = new StringBuilder()
+ .append(typeSignature.substring(0, closingBrace + 1))
+ .append(returnType)
+ .toString();
+ String signature = String.format(Locale.US, context.signatureFormatString,
+ context.getClassDescriptor(), context.member.getName(), typeSignature);
+
+ if (mPublicApis.contains(signature)) {
+ context.reportError("Signature %s generated from @%s already exists as a public API",
+ signature, SHORT_NAME);
+ return;
+ }
+ mConsumer.whitelistEntry(signature);
+ }
+
+ private String findReturnType(AnnotationEntry a) {
+ for (ElementValuePair property : a.getElementValuePairs()) {
+ if (property.getNameString().equals(RETURN_TYPE)) {
+ return property.getValue().stringifyValue();
+ }
+ }
+ // not found
+ return null;
+ }
+}
diff --git a/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeMultiHandler.java b/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeMultiHandler.java
new file mode 100644
index 0000000..bd0bf79
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeMultiHandler.java
@@ -0,0 +1,72 @@
+package com.android.class2greylist;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+
+import org.apache.bcel.classfile.AnnotationElementValue;
+import org.apache.bcel.classfile.AnnotationEntry;
+import org.apache.bcel.classfile.ArrayElementValue;
+import org.apache.bcel.classfile.ElementValue;
+import org.apache.bcel.classfile.ElementValuePair;
+
+import java.util.Set;
+
+/**
+ * Handles {@code CovariantReturnType$CovariantReturnTypes} annotations, which
+ * are generated by the compiler when multiple {@code CovariantReturnType}
+ * annotations appear on a single method.
+ *
+ * <p>The enclosed annotations are passed to {@link CovariantReturnTypeHandler}.
+ */
+public class CovariantReturnTypeMultiHandler implements AnnotationHandler {
+
+ public static final String ANNOTATION_NAME =
+ "Ldalvik/annotation/codegen/CovariantReturnType$CovariantReturnTypes;";
+
+ private static final String VALUE = "value";
+
+ private final CovariantReturnTypeHandler mWrappedHandler;
+ private final String mInnerAnnotationName;
+
+ public CovariantReturnTypeMultiHandler(GreylistConsumer consumer, Set<String> publicApis) {
+ this(consumer, publicApis, CovariantReturnTypeHandler.ANNOTATION_NAME);
+ }
+
+ @VisibleForTesting
+ public CovariantReturnTypeMultiHandler(GreylistConsumer consumer, Set<String> publicApis,
+ String innerAnnotationName) {
+ mWrappedHandler = new CovariantReturnTypeHandler(consumer, publicApis);
+ mInnerAnnotationName = innerAnnotationName;
+ }
+
+ @Override
+ public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) {
+ // Verify that the annotation has the form we expect
+ ElementValuePair value = findValue(annotation);
+ if (value == null) {
+ context.reportError("No value found on CovariantReturnType$CovariantReturnTypes");
+ return;
+ }
+ Preconditions.checkArgument(value.getValue() instanceof ArrayElementValue);
+ ArrayElementValue array = (ArrayElementValue) value.getValue();
+
+ // call wrapped handler on each enclosed annotation:
+ for (ElementValue v : array.getElementValuesArray()) {
+ Preconditions.checkArgument(v instanceof AnnotationElementValue);
+ AnnotationElementValue aev = (AnnotationElementValue) v;
+ Preconditions.checkArgument(
+ aev.getAnnotationEntry().getAnnotationType().equals(mInnerAnnotationName));
+ mWrappedHandler.handleAnnotation(aev.getAnnotationEntry(), context);
+ }
+ }
+
+ private ElementValuePair findValue(AnnotationEntry a) {
+ for (ElementValuePair property : a.getElementValuePairs()) {
+ if (property.getNameString().equals(VALUE)) {
+ return property;
+ }
+ }
+ // not found
+ return null;
+ }
+}
diff --git a/tools/class2greylist/src/com/android/class2greylist/FileWritingGreylistConsumer.java b/tools/class2greylist/src/com/android/class2greylist/FileWritingGreylistConsumer.java
new file mode 100644
index 0000000..9f33467
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/FileWritingGreylistConsumer.java
@@ -0,0 +1,66 @@
+package com.android.class2greylist;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Map;
+
+public class FileWritingGreylistConsumer implements GreylistConsumer {
+
+ private final Status mStatus;
+ private final Map<Integer, PrintStream> mSdkToPrintStreamMap;
+ private final PrintStream mWhitelistStream;
+
+ private static PrintStream openFile(String filename) throws FileNotFoundException {
+ if (filename == null) {
+ return null;
+ }
+ return new PrintStream(new FileOutputStream(new File(filename)));
+ }
+
+ private static Map<Integer, PrintStream> openFiles(
+ Map<Integer, String> filenames) throws FileNotFoundException {
+ Map<Integer, PrintStream> streams = new HashMap<>();
+ for (Map.Entry<Integer, String> entry : filenames.entrySet()) {
+ streams.put(entry.getKey(), openFile(entry.getValue()));
+ }
+ return streams;
+ }
+
+ public FileWritingGreylistConsumer(Status status, Map<Integer, String> sdkToFilenameMap,
+ String whitelistFile) throws FileNotFoundException {
+ mStatus = status;
+ mSdkToPrintStreamMap = openFiles(sdkToFilenameMap);
+ mWhitelistStream = openFile(whitelistFile);
+ }
+
+ @Override
+ public void greylistEntry(String signature, Integer maxTargetSdk) {
+ PrintStream p = mSdkToPrintStreamMap.get(maxTargetSdk);
+ if (p == null) {
+ mStatus.error("No output file for signature %s with maxTargetSdk of %d", signature,
+ maxTargetSdk == null ? "<absent>" : maxTargetSdk.toString());
+ return;
+ }
+ p.println(signature);
+ }
+
+ @Override
+ public void whitelistEntry(String signature) {
+ if (mWhitelistStream != null) {
+ mWhitelistStream.println(signature);
+ }
+ }
+
+ @Override
+ public void close() {
+ for (PrintStream p : mSdkToPrintStreamMap.values()) {
+ p.close();
+ }
+ if (mWhitelistStream != null) {
+ mWhitelistStream.close();
+ }
+ }
+}
diff --git a/tools/class2greylist/src/com/android/class2greylist/GreylistAnnotationHandler.java b/tools/class2greylist/src/com/android/class2greylist/GreylistAnnotationHandler.java
new file mode 100644
index 0000000..460f2c3
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/GreylistAnnotationHandler.java
@@ -0,0 +1,146 @@
+package com.android.class2greylist;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
+
+import org.apache.bcel.Const;
+import org.apache.bcel.classfile.AnnotationEntry;
+import org.apache.bcel.classfile.ElementValue;
+import org.apache.bcel.classfile.ElementValuePair;
+import org.apache.bcel.classfile.FieldOrMethod;
+import org.apache.bcel.classfile.Method;
+import org.apache.bcel.classfile.SimpleElementValue;
+
+import java.util.Set;
+import java.util.function.Predicate;
+
+/**
+ * Processes {@code UnsupportedAppUsage} annotations to generate greylist
+ * entries.
+ *
+ * Any annotations with a {@link #EXPECTED_SIGNATURE} property will have their
+ * generated signature verified against this, and an error will be reported if
+ * it does not match. Exclusions are made for bridge methods.
+ *
+ * Any {@link #MAX_TARGET_SDK} properties will be validated against the given
+ * set of valid values, then passed through to the greylist consumer.
+ */
+public class GreylistAnnotationHandler implements AnnotationHandler {
+
+ public static final String ANNOTATION_NAME = "Landroid/annotation/UnsupportedAppUsage;";
+
+ // properties of greylist annotations:
+ private static final String EXPECTED_SIGNATURE = "expectedSignature";
+ private static final String MAX_TARGET_SDK = "maxTargetSdk";
+
+ private final Status mStatus;
+ private final Predicate<GreylistMember> mGreylistFilter;
+ private final GreylistConsumer mGreylistConsumer;
+ private final Set<Integer> mValidMaxTargetSdkValues;
+
+ /**
+ * Represents a member of a class file (a field or method).
+ */
+ @VisibleForTesting
+ public static class GreylistMember {
+
+ /**
+ * Signature of this member.
+ */
+ public final String signature;
+ /**
+ * Indicates if this is a synthetic bridge method.
+ */
+ public final boolean bridge;
+ /**
+ * Max target SDK of property this member, if it is set, else null.
+ *
+ * Note: even though the annotation itself specified a default value,
+ * that default value is not encoded into instances of the annotation
+ * in class files. So when no value is specified in source, it will
+ * result in null appearing in here.
+ */
+ public final Integer maxTargetSdk;
+
+ public GreylistMember(String signature, boolean bridge, Integer maxTargetSdk) {
+ this.signature = signature;
+ this.bridge = bridge;
+ this.maxTargetSdk = maxTargetSdk;
+ }
+ }
+
+ public GreylistAnnotationHandler(
+ Status status,
+ GreylistConsumer greylistConsumer,
+ Set<String> publicApis,
+ Set<Integer> validMaxTargetSdkValues) {
+ this(status, greylistConsumer,
+ member -> !(member.bridge && publicApis.contains(member.signature)),
+ validMaxTargetSdkValues);
+ }
+
+ @VisibleForTesting
+ public GreylistAnnotationHandler(
+ Status status,
+ GreylistConsumer greylistConsumer,
+ Predicate<GreylistMember> greylistFilter,
+ Set<Integer> validMaxTargetSdkValues) {
+ mStatus = status;
+ mGreylistConsumer = greylistConsumer;
+ mGreylistFilter = greylistFilter;
+ mValidMaxTargetSdkValues = validMaxTargetSdkValues;
+ }
+
+ @Override
+ public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) {
+ FieldOrMethod member = context.member;
+ boolean bridge = (member instanceof Method)
+ && (member.getAccessFlags() & Const.ACC_BRIDGE) != 0;
+ if (bridge) {
+ mStatus.debug("Member is a bridge");
+ }
+ String signature = context.getMemberDescriptor();
+ Integer maxTargetSdk = null;
+ for (ElementValuePair property : annotation.getElementValuePairs()) {
+ switch (property.getNameString()) {
+ case EXPECTED_SIGNATURE:
+ verifyExpectedSignature(context, property, signature, bridge);
+ break;
+ case MAX_TARGET_SDK:
+ maxTargetSdk = verifyAndGetMaxTargetSdk(context, property);
+ break;
+ }
+ }
+ if (mGreylistFilter.test(new GreylistMember(signature, bridge, maxTargetSdk))) {
+ mGreylistConsumer.greylistEntry(signature, maxTargetSdk);
+ }
+ }
+
+ private void verifyExpectedSignature(AnnotationContext context, ElementValuePair property,
+ String signature, boolean isBridge) {
+ String expected = property.getValue().stringifyValue();
+ // Don't enforce for bridge methods; they're generated so won't match.
+ if (!isBridge && !signature.equals(expected)) {
+ context.reportError("Expected signature does not match generated:\n"
+ + "Expected: %s\n"
+ + "Generated: %s", expected, signature);
+ }
+ }
+
+ private Integer verifyAndGetMaxTargetSdk(AnnotationContext context, ElementValuePair property) {
+ if (property.getValue().getElementValueType() != ElementValue.PRIMITIVE_INT) {
+ context.reportError("Expected property %s to be of type int; got %d",
+ property.getNameString(), property.getValue().getElementValueType());
+ }
+ int value = ((SimpleElementValue) property.getValue()).getValueInt();
+ if (!mValidMaxTargetSdkValues.contains(value)) {
+ context.reportError("Invalid value for %s: got %d, expected one of [%s]",
+ property.getNameString(),
+ value,
+ Joiner.on(",").join(mValidMaxTargetSdkValues));
+ return null;
+ }
+ return value;
+ }
+
+}
diff --git a/tools/class2greylist/src/com/android/class2greylist/GreylistConsumer.java b/tools/class2greylist/src/com/android/class2greylist/GreylistConsumer.java
new file mode 100644
index 0000000..fd855e8
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/GreylistConsumer.java
@@ -0,0 +1,20 @@
+package com.android.class2greylist;
+
+public interface GreylistConsumer {
+ /**
+ * Handle a new greylist entry.
+ *
+ * @param signature Signature of the member.
+ * @param maxTargetSdk maxTargetSdk value from the annotation, or null if none set.
+ */
+ void greylistEntry(String signature, Integer maxTargetSdk);
+
+ /**
+ * Handle a new whitelist entry.
+ *
+ * @param signature Signature of the member.
+ */
+ void whitelistEntry(String signature);
+
+ void close();
+}
diff --git a/tools/class2greylist/src/com/android/class2greylist/Status.java b/tools/class2greylist/src/com/android/class2greylist/Status.java
index d707898..b5ee9f1 100644
--- a/tools/class2greylist/src/com/android/class2greylist/Status.java
+++ b/tools/class2greylist/src/com/android/class2greylist/Status.java
@@ -42,16 +42,12 @@
mHasErrors = true;
}
- public void error(String message) {
+ public void error(String message, Object... args) {
System.err.print(ERROR);
- System.err.println(message);
+ System.err.println(String.format(Locale.US, message, args));
mHasErrors = true;
}
- public void greylistEntry(String signature) {
- System.out.println(signature);
- }
-
public boolean ok() {
return !mHasErrors;
}
diff --git a/tools/class2greylist/src/com/android/class2greylist/SystemOutGreylistConsumer.java b/tools/class2greylist/src/com/android/class2greylist/SystemOutGreylistConsumer.java
new file mode 100644
index 0000000..ad5ad70
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/SystemOutGreylistConsumer.java
@@ -0,0 +1,18 @@
+package com.android.class2greylist;
+
+public class SystemOutGreylistConsumer implements GreylistConsumer {
+ @Override
+ public void greylistEntry(String signature, Integer maxTargetSdk) {
+ System.out.println(signature);
+ }
+
+ @Override
+ public void whitelistEntry(String signature) {
+ // Ignore. This class is only used when no grey/white lists are
+ // specified, so we have nowhere to write whitelist entries.
+ }
+
+ @Override
+ public void close() {
+ }
+}
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/AnnotationHandlerTestBase.java b/tools/class2greylist/test/src/com/android/class2greylist/AnnotationHandlerTestBase.java
new file mode 100644
index 0000000..8f4a76f
--- /dev/null
+++ b/tools/class2greylist/test/src/com/android/class2greylist/AnnotationHandlerTestBase.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.class2greylist;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.withSettings;
+
+import com.android.javac.Javac;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+
+import java.io.IOException;
+
+public class AnnotationHandlerTestBase {
+
+ @Rule
+ public TestName mTestName = new TestName();
+
+ protected Javac mJavac;
+ protected GreylistConsumer mConsumer;
+ protected Status mStatus;
+
+ @Before
+ public void baseSetup() throws IOException {
+ System.out.println(String.format("\n============== STARTING TEST: %s ==============\n",
+ mTestName.getMethodName()));
+ mConsumer = mock(GreylistConsumer.class);
+ mStatus = mock(Status.class, withSettings().verboseLogging());
+ mJavac = new Javac();
+ }
+
+ protected void assertNoErrors() {
+ verify(mStatus, never()).error(any(Throwable.class));
+ verify(mStatus, never()).error(any(), any());
+ }
+}
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/Class2GreylistTest.java b/tools/class2greylist/test/src/com/android/class2greylist/Class2GreylistTest.java
new file mode 100644
index 0000000..cb75dd3
--- /dev/null
+++ b/tools/class2greylist/test/src/com/android/class2greylist/Class2GreylistTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.class2greylist;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.mockito.Mock;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class Class2GreylistTest {
+
+ @Mock
+ Status mStatus;
+ @Rule
+ public TestName mTestName = new TestName();
+
+ @Before
+ public void setup() throws IOException {
+ System.out.println(String.format("\n============== STARTING TEST: %s ==============\n",
+ mTestName.getMethodName()));
+ initMocks(this);
+ }
+
+ @Test
+ public void testReadGreylistMap() throws IOException {
+ Map<Integer, String> map = Class2Greylist.readGreylistMap(mStatus,
+ new String[]{"noApi", "1:apiOne", "3:apiThree"});
+ verifyZeroInteractions(mStatus);
+ assertThat(map).containsExactly(null, "noApi", 1, "apiOne", 3, "apiThree");
+ }
+
+ @Test
+ public void testReadGreylistMapDuplicate() throws IOException {
+ Class2Greylist.readGreylistMap(mStatus,
+ new String[]{"noApi", "1:apiOne", "1:anotherOne"});
+ verify(mStatus, atLeastOnce()).error(any(), any());
+ }
+
+ @Test
+ public void testReadGreylistMapDuplicateNoApi() {
+ Class2Greylist.readGreylistMap(mStatus,
+ new String[]{"noApi", "anotherNoApi", "1:apiOne"});
+ verify(mStatus, atLeastOnce()).error(any(), any());
+ }
+
+ @Test
+ public void testReadGreylistMapInvalidInt() throws IOException {
+ Class2Greylist.readGreylistMap(mStatus, new String[]{"noApi", "a:apiOne"});
+ verify(mStatus, atLeastOnce()).error(any(), any());
+ }
+
+ @Test
+ public void testReadGreylistMapNoFilename() throws IOException {
+ Class2Greylist.readGreylistMap(mStatus, new String[]{"noApi", "1:"});
+ verify(mStatus, atLeastOnce()).error(any(), any());
+ }
+}
+
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeHandlerTest.java b/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeHandlerTest.java
new file mode 100644
index 0000000..10fae9b
--- /dev/null
+++ b/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeHandlerTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.class2greylist;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import static java.util.Collections.emptySet;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class CovariantReturnTypeHandlerTest extends AnnotationHandlerTestBase {
+
+ private static final String ANNOTATION = "Lannotation/Annotation;";
+
+ @Before
+ public void setup() throws IOException {
+ // To keep the test simpler and more concise, we don't use the real
+ // @CovariantReturnType annotation here, but use our own @Annotation.
+ // It doesn't have to match the real annotation, just have the same
+ // property (returnType).
+ mJavac.addSource("annotation.Annotation", Joiner.on('\n').join(
+ "package annotation;",
+ "import static java.lang.annotation.RetentionPolicy.CLASS;",
+ "import java.lang.annotation.Retention;",
+ "@Retention(CLASS)",
+ "public @interface Annotation {",
+ " Class<?> returnType();",
+ "}"));
+ }
+
+ @Test
+ public void testReturnTypeWhitelisted() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Annotation;",
+ "public class Class {",
+ " @Annotation(returnType=Integer.class)",
+ " public String method() {return null;}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION,
+ new CovariantReturnTypeHandler(
+ mConsumer,
+ ImmutableSet.of("La/b/Class;->method()Ljava/lang/String;")));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+
+ assertNoErrors();
+ verify(mConsumer, times(1)).whitelistEntry(eq("La/b/Class;->method()Ljava/lang/Integer;"));
+ }
+
+ @Test
+ public void testAnnotatedMemberNotPublicApi() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Annotation;",
+ "public class Class {",
+ " @Annotation(returnType=Integer.class)",
+ " public String method() {return null;}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION,
+ new CovariantReturnTypeHandler(
+ mConsumer,
+ emptySet()));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+
+ verify(mStatus, atLeastOnce()).error(any(), any());
+ }
+
+ @Test
+ public void testReturnTypeAlreadyWhitelisted() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Annotation;",
+ "public class Class {",
+ " @Annotation(returnType=Integer.class)",
+ " public String method() {return null;}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION,
+ new CovariantReturnTypeHandler(
+ mConsumer,
+ ImmutableSet.of(
+ "La/b/Class;->method()Ljava/lang/String;",
+ "La/b/Class;->method()Ljava/lang/Integer;"
+ )));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+
+ verify(mStatus, atLeastOnce()).error(any(), any());
+ }
+
+ @Test
+ public void testAnnotationOnField() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Annotation;",
+ "public class Class {",
+ " @Annotation(returnType=Integer.class)",
+ " public String field;",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION,
+ new CovariantReturnTypeHandler(
+ mConsumer,
+ emptySet()));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+
+ verify(mStatus, atLeastOnce()).error(any(), any());
+ }
+}
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java b/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java
new file mode 100644
index 0000000..7f4ce62
--- /dev/null
+++ b/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.class2greylist;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import static java.util.Collections.emptySet;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class CovariantReturnTypeMultiHandlerTest extends AnnotationHandlerTestBase {
+
+
+ @Before
+ public void setup() throws IOException {
+ // To keep the test simpler and more concise, we don't use the real
+ // @CovariantReturnType annotation here, but use our own @Annotation
+ // and @Annotation.Multi that have the same semantics. It doesn't have
+ // to match the real annotation, just have the same properties
+ // (returnType and value).
+ mJavac.addSource("annotation.Annotation", Joiner.on('\n').join(
+ "package annotation;",
+ "import static java.lang.annotation.RetentionPolicy.CLASS;",
+ "import java.lang.annotation.Repeatable;",
+ "import java.lang.annotation.Retention;",
+ "@Repeatable(Annotation.Multi.class)",
+ "@Retention(CLASS)",
+ "public @interface Annotation {",
+ " Class<?> returnType();",
+ " @Retention(CLASS)",
+ " @interface Multi {",
+ " Annotation[] value();",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void testReturnTypeMulti() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Annotation;",
+ "public class Class {",
+ " @Annotation(returnType=Integer.class)",
+ " @Annotation(returnType=Long.class)",
+ " public String method() {return null;}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of("Lannotation/Annotation$Multi;",
+ new CovariantReturnTypeMultiHandler(
+ mConsumer,
+ ImmutableSet.of("La/b/Class;->method()Ljava/lang/String;"),
+ "Lannotation/Annotation;"));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> whitelist = ArgumentCaptor.forClass(String.class);
+ verify(mConsumer, times(2)).whitelistEntry(whitelist.capture());
+ assertThat(whitelist.getAllValues()).containsExactly(
+ "La/b/Class;->method()Ljava/lang/Integer;",
+ "La/b/Class;->method()Ljava/lang/Long;"
+ );
+ }
+
+ @Test
+ public void testReturnTypeMultiNotPublicApi() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Annotation;",
+ "public class Class {",
+ " @Annotation(returnType=Integer.class)",
+ " @Annotation(returnType=Long.class)",
+ " public String method() {return null;}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of("Lannotation/Annotation$Multi;",
+ new CovariantReturnTypeMultiHandler(
+ mConsumer,
+ emptySet(),
+ "Lannotation/Annotation;"));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+
+ verify(mStatus, atLeastOnce()).error(any(), any());
+ }
+}
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/GreylistAnnotationHandlerTest.java b/tools/class2greylist/test/src/com/android/class2greylist/GreylistAnnotationHandlerTest.java
new file mode 100644
index 0000000..1a4bfb8
--- /dev/null
+++ b/tools/class2greylist/test/src/com/android/class2greylist/GreylistAnnotationHandlerTest.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.class2greylist;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import static java.util.Collections.emptySet;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+
+public class GreylistAnnotationHandlerTest extends AnnotationHandlerTestBase {
+
+ private static final String ANNOTATION = "Lannotation/Anno;";
+
+ @Before
+ public void setup() throws IOException {
+ mJavac.addSource("annotation.Anno", Joiner.on('\n').join(
+ "package annotation;",
+ "import static java.lang.annotation.RetentionPolicy.CLASS;",
+ "import java.lang.annotation.Retention;",
+ "@Retention(CLASS)",
+ "public @interface Anno {",
+ " String expectedSignature() default \"\";",
+ " int maxTargetSdk() default Integer.MAX_VALUE;",
+ "}"));
+ }
+
+ private GreylistAnnotationHandler createGreylistHandler(
+ Predicate<GreylistAnnotationHandler.GreylistMember> greylistFilter,
+ Set<Integer> validMaxTargetSdkValues) {
+ return new GreylistAnnotationHandler(
+ mStatus, mConsumer, greylistFilter, validMaxTargetSdkValues);
+ }
+
+ @Test
+ public void testGreylistMethod() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno",
+ " public void method() {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, emptySet()))
+ ).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mConsumer, times(1)).greylistEntry(greylist.capture(), any());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V");
+ }
+
+ @Test
+ public void testGreylistConstructor() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno",
+ " public Class() {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, emptySet()))
+ ).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mConsumer, times(1)).greylistEntry(greylist.capture(), any());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class;-><init>()V");
+ }
+
+ @Test
+ public void testGreylistField() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno",
+ " public int i;",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, emptySet()))
+ ).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mConsumer, times(1)).greylistEntry(greylist.capture(), any());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class;->i:I");
+ }
+
+ @Test
+ public void testGreylistMethodExpectedSignature() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno(expectedSignature=\"La/b/Class;->method()V\")",
+ " public void method() {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, emptySet()))
+ ).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mConsumer, times(1)).greylistEntry(greylist.capture(), any());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V");
+ }
+
+ @Test
+ public void testGreylistMethodExpectedSignatureWrong() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno(expectedSignature=\"La/b/Class;->nomethod()V\")",
+ " public void method() {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, emptySet()))
+ ).visit();
+
+ verify(mStatus, times(1)).error(any(), any());
+ }
+
+ @Test
+ public void testGreylistInnerClassMethod() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " public class Inner {",
+ " @Anno",
+ " public void method() {}",
+ " }",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class$Inner"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, emptySet()))
+ ).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mConsumer, times(1)).greylistEntry(greylist.capture(), any());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class$Inner;->method()V");
+ }
+
+ @Test
+ public void testMethodNotGreylisted() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "public class Class {",
+ " public void method() {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, emptySet()))
+ ).visit();
+
+ assertNoErrors();
+ verify(mConsumer, never()).greylistEntry(any(String.class), any());
+ }
+
+ @Test
+ public void testMethodArgGenerics() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class<T extends String> {",
+ " @Anno(expectedSignature=\"La/b/Class;->method(Ljava/lang/String;)V\")",
+ " public void method(T arg) {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, emptySet()))
+ ).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mConsumer, times(1)).greylistEntry(greylist.capture(), any());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method(Ljava/lang/String;)V");
+ }
+
+ @Test
+ public void testOverrideMethodWithBridge() throws IOException {
+ mJavac.addSource("a.b.Base", Joiner.on('\n').join(
+ "package a.b;",
+ "abstract class Base<T> {",
+ " protected abstract void method(T arg);",
+ "}"));
+
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class<T extends String> extends Base<T> {",
+ " @Override",
+ " @Anno(expectedSignature=\"La/b/Class;->method(Ljava/lang/String;)V\")",
+ " public void method(T arg) {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, emptySet()));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Base"), mStatus, handlerMap).visit();
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ // A bridge method is generated for the above, so we expect 2 greylist entries.
+ verify(mConsumer, times(2)).greylistEntry(greylist.capture(), any());
+ assertThat(greylist.getAllValues()).containsExactly(
+ "La/b/Class;->method(Ljava/lang/Object;)V",
+ "La/b/Class;->method(Ljava/lang/String;)V");
+ }
+
+ @Test
+ public void testOverridePublicMethodWithBridge() throws IOException {
+ mJavac.addSource("a.b.Base", Joiner.on('\n').join(
+ "package a.b;",
+ "public abstract class Base<T> {",
+ " public void method(T arg) {}",
+ "}"));
+
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class<T extends String> extends Base<T> {",
+ " @Override",
+ " @Anno(expectedSignature=\"La/b/Class;->method(Ljava/lang/String;)V\")",
+ " public void method(T arg) {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, emptySet()));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Base"), mStatus, handlerMap).visit();
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ // A bridge method is generated for the above, so we expect 2 greylist entries.
+ verify(mConsumer, times(2)).greylistEntry(greylist.capture(), any());
+ assertThat(greylist.getAllValues()).containsExactly(
+ "La/b/Class;->method(Ljava/lang/Object;)V",
+ "La/b/Class;->method(Ljava/lang/String;)V");
+ }
+
+ @Test
+ public void testBridgeMethodsFromInterface() throws IOException {
+ mJavac.addSource("a.b.Interface", Joiner.on('\n').join(
+ "package a.b;",
+ "public interface Interface {",
+ " public void method(Object arg);",
+ "}"));
+
+ mJavac.addSource("a.b.Base", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "class Base {",
+ " @Anno(expectedSignature=\"La/b/Base;->method(Ljava/lang/Object;)V\")",
+ " public void method(Object arg) {}",
+ "}"));
+
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "public class Class extends Base implements Interface {",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, emptySet()));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Interface"), mStatus, handlerMap)
+ .visit();
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Base"), mStatus, handlerMap).visit();
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ // A bridge method is generated for the above, so we expect 2 greylist entries.
+ verify(mConsumer, times(2)).greylistEntry(greylist.capture(), any());
+ assertThat(greylist.getAllValues()).containsExactly(
+ "La/b/Class;->method(Ljava/lang/Object;)V",
+ "La/b/Base;->method(Ljava/lang/Object;)V");
+ }
+
+ @Test
+ public void testPublicBridgeExcluded() throws IOException {
+ mJavac.addSource("a.b.Base", Joiner.on('\n').join(
+ "package a.b;",
+ "public abstract class Base<T> {",
+ " public void method(T arg) {}",
+ "}"));
+
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class<T extends String> extends Base<T> {",
+ " @Override",
+ " @Anno",
+ " public void method(T arg) {}",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Set<String> publicApis = Sets.newHashSet(
+ "La/b/Base;->method(Ljava/lang/Object;)V",
+ "La/b/Class;->method(Ljava/lang/Object;)V");
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION,
+ new GreylistAnnotationHandler(
+ mStatus,
+ mConsumer,
+ publicApis,
+ emptySet()));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Base"), mStatus, handlerMap).visit();
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ // The bridge method generated for the above, is a public API so should be excluded
+ verify(mConsumer, times(1)).greylistEntry(greylist.capture(), any());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method(Ljava/lang/String;)V");
+ }
+
+ @Test
+ public void testVolatileField() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno(expectedSignature=\"La/b/Class;->field:I\")",
+ " public volatile int field;",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(
+ member -> !member.bridge, // exclude bridge methods
+ emptySet()));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mConsumer, times(1)).greylistEntry(greylist.capture(), any());
+ assertThat(greylist.getValue()).isEqualTo("La/b/Class;->field:I");
+ }
+
+ @Test
+ public void testVolatileFieldWrongSignature() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno(expectedSignature=\"La/b/Class;->wrong:I\")",
+ " public volatile int field;",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, emptySet()));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+ verify(mStatus, times(1)).error(any(), any());
+ }
+
+ @Test
+ public void testMethodMaxTargetSdk() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno(maxTargetSdk=1)",
+ " public int field;",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(
+ x -> true,
+ ImmutableSet.of(1)));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+ assertNoErrors();
+ ArgumentCaptor<Integer> maxTargetSdk = ArgumentCaptor.forClass(Integer.class);
+ verify(mConsumer, times(1)).greylistEntry(any(), maxTargetSdk.capture());
+ assertThat(maxTargetSdk.getValue()).isEqualTo(1);
+ }
+
+ @Test
+ public void testMethodNoMaxTargetSdk() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno",
+ " public int field;",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(
+ x -> true,
+ ImmutableSet.of(1)));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+ assertNoErrors();
+ ArgumentCaptor<Integer> maxTargetSdk = ArgumentCaptor.forClass(Integer.class);
+ verify(mConsumer, times(1)).greylistEntry(any(), maxTargetSdk.capture());
+ assertThat(maxTargetSdk.getValue()).isEqualTo(null);
+ }
+
+ @Test
+ public void testMethodMaxTargetSdkOutOfRange() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public class Class {",
+ " @Anno(maxTargetSdk=2)",
+ " public int field;",
+ "}"));
+ assertThat(mJavac.compile()).isTrue();
+
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(
+ x -> true,
+ ImmutableSet.of(1)));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+ verify(mStatus, times(1)).error(any(), any());
+ }
+
+}
diff --git a/tools/class2greylist/test/src/com/android/javac/AnnotationVisitorTest.java b/tools/class2greylist/test/src/com/android/javac/AnnotationVisitorTest.java
deleted file mode 100644
index a4ad20c..0000000
--- a/tools/class2greylist/test/src/com/android/javac/AnnotationVisitorTest.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-package com.android.javac;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.withSettings;
-
-import com.android.class2greylist.AnnotationVisitor;
-import com.android.class2greylist.Status;
-
-import com.google.common.base.Joiner;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestName;
-import org.mockito.ArgumentCaptor;
-
-import java.io.IOException;
-
-public class AnnotationVisitorTest {
-
- private static final String ANNOTATION = "Lannotation/Anno;";
-
- @Rule
- public TestName mTestName = new TestName();
-
- private Javac mJavac;
- private Status mStatus;
-
- @Before
- public void setup() throws IOException {
- System.out.println(String.format("\n============== STARTING TEST: %s ==============\n",
- mTestName.getMethodName()));
- mStatus = mock(Status.class, withSettings().verboseLogging());
- mJavac = new Javac();
- mJavac.addSource("annotation.Anno", Joiner.on('\n').join(
- "package annotation;",
- "import static java.lang.annotation.RetentionPolicy.CLASS;",
- "import java.lang.annotation.Retention;",
- "import java.lang.annotation.Target;",
- "@Retention(CLASS)",
- "public @interface Anno {",
- " String expectedSignature() default \"\";",
- "}"));
- }
-
- private void assertNoErrors() {
- verify(mStatus, never()).error(any(Throwable.class));
- verify(mStatus, never()).error(any(String.class));
- }
-
- @Test
- public void testGreylistMethod() throws IOException {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "import annotation.Anno;",
- "public class Class {",
- " @Anno",
- " public void method() {}",
- "}"));
- assertThat(mJavac.compile()).isTrue();
-
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
- .visit();
-
- assertNoErrors();
- ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
- verify(mStatus, times(1)).greylistEntry(greylist.capture());
- assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V");
- }
-
- @Test
- public void testGreylistConstructor() throws IOException {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "import annotation.Anno;",
- "public class Class {",
- " @Anno",
- " public Class() {}",
- "}"));
- assertThat(mJavac.compile()).isTrue();
-
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
- .visit();
-
- assertNoErrors();
- ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
- verify(mStatus, times(1)).greylistEntry(greylist.capture());
- assertThat(greylist.getValue()).isEqualTo("La/b/Class;-><init>()V");
- }
-
- @Test
- public void testGreylistField() throws IOException {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "import annotation.Anno;",
- "public class Class {",
- " @Anno",
- " public int i;",
- "}"));
- assertThat(mJavac.compile()).isTrue();
-
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
- .visit();
-
- assertNoErrors();
- ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
- verify(mStatus, times(1)).greylistEntry(greylist.capture());
- assertThat(greylist.getValue()).isEqualTo("La/b/Class;->i:I");
- }
-
- @Test
- public void testGreylistMethodExpectedSignature() throws IOException {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "import annotation.Anno;",
- "public class Class {",
- " @Anno(expectedSignature=\"La/b/Class;->method()V\")",
- " public void method() {}",
- "}"));
- assertThat(mJavac.compile()).isTrue();
-
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
- .visit();
-
- assertNoErrors();
- ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
- verify(mStatus, times(1)).greylistEntry(greylist.capture());
- assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V");
- }
-
- @Test
- public void testGreylistMethodExpectedSignatureWrong() throws IOException {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "import annotation.Anno;",
- "public class Class {",
- " @Anno(expectedSignature=\"La/b/Class;->nomethod()V\")",
- " public void method() {}",
- "}"));
- assertThat(mJavac.compile()).isTrue();
-
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
- .visit();
-
- verify(mStatus, times(1)).error(any(String.class));
- }
-
- @Test
- public void testGreylistInnerClassMethod() throws IOException {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "import annotation.Anno;",
- "public class Class {",
- " public class Inner {",
- " @Anno",
- " public void method() {}",
- " }",
- "}"));
- assertThat(mJavac.compile()).isTrue();
-
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class$Inner"), ANNOTATION,
- mStatus).visit();
-
- assertNoErrors();
- ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
- verify(mStatus, times(1)).greylistEntry(greylist.capture());
- assertThat(greylist.getValue()).isEqualTo("La/b/Class$Inner;->method()V");
- }
-
- @Test
- public void testMethodNotGreylisted() throws IOException {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "public class Class {",
- " public void method() {}",
- "}"));
- assertThat(mJavac.compile()).isTrue();
-
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
- .visit();
-
- assertNoErrors();
- verify(mStatus, never()).greylistEntry(any(String.class));
- }
-
- @Test
- public void testMethodArgGenerics() throws IOException {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "import annotation.Anno;",
- "public class Class<T extends String> {",
- " @Anno(expectedSignature=\"La/b/Class;->method(Ljava/lang/String;)V\")",
- " public void method(T arg) {}",
- "}"));
- assertThat(mJavac.compile()).isTrue();
-
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
- .visit();
-
- assertNoErrors();
- ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
- verify(mStatus, times(1)).greylistEntry(greylist.capture());
- assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method(Ljava/lang/String;)V");
- }
-
- @Test
- public void testOverrideMethodWithBridge() throws IOException {
- mJavac.addSource("a.b.Base", Joiner.on('\n').join(
- "package a.b;",
- "abstract class Base<T> {",
- " protected abstract void method(T arg);",
- "}"));
-
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "import annotation.Anno;",
- "public class Class<T extends String> extends Base<T> {",
- " @Override",
- " @Anno(expectedSignature=\"La/b/Class;->method(Ljava/lang/String;)V\")",
- " public void method(T arg) {}",
- "}"));
- assertThat(mJavac.compile()).isTrue();
-
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Base"), ANNOTATION, mStatus)
- .visit();
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
- .visit();
-
- assertNoErrors();
- ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
- // A bridge method is generated for the above, so we expect 2 greylist entries.
- verify(mStatus, times(2)).greylistEntry(greylist.capture());
- assertThat(greylist.getAllValues()).containsExactly(
- "La/b/Class;->method(Ljava/lang/Object;)V",
- "La/b/Class;->method(Ljava/lang/String;)V");
- }
-
- @Test
- public void testOverridePublicMethodWithBridge() throws IOException {
- mJavac.addSource("a.b.Base", Joiner.on('\n').join(
- "package a.b;",
- "public abstract class Base<T> {",
- " public void method(T arg) {}",
- "}"));
-
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "import annotation.Anno;",
- "public class Class<T extends String> extends Base<T> {",
- " @Override",
- " @Anno(expectedSignature=\"La/b/Class;->method(Ljava/lang/String;)V\")",
- " public void method(T arg) {}",
- "}"));
- assertThat(mJavac.compile()).isTrue();
-
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Base"), ANNOTATION, mStatus)
- .visit();
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
- .visit();
-
- assertNoErrors();
- ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
- // A bridge method is generated for the above, so we expect 2 greylist entries.
- verify(mStatus, times(2)).greylistEntry(greylist.capture());
- assertThat(greylist.getAllValues()).containsExactly(
- "La/b/Class;->method(Ljava/lang/Object;)V",
- "La/b/Class;->method(Ljava/lang/String;)V");
- }
-
- @Test
- public void testBridgeMethodsFromInterface() throws IOException {
- mJavac.addSource("a.b.Interface", Joiner.on('\n').join(
- "package a.b;",
- "public interface Interface {",
- " public void method(Object arg);",
- "}"));
-
- mJavac.addSource("a.b.Base", Joiner.on('\n').join(
- "package a.b;",
- "import annotation.Anno;",
- "class Base {",
- " @Anno(expectedSignature=\"La/b/Base;->method(Ljava/lang/Object;)V\")",
- " public void method(Object arg) {}",
- "}"));
-
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "public class Class extends Base implements Interface {",
- "}"));
- assertThat(mJavac.compile()).isTrue();
-
- new AnnotationVisitor(
- mJavac.getCompiledClass("a.b.Interface"), ANNOTATION, mStatus).visit();
- new AnnotationVisitor(
- mJavac.getCompiledClass("a.b.Base"), ANNOTATION, mStatus).visit();
- new AnnotationVisitor(
- mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus).visit();
-
- assertNoErrors();
- ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
- // A bridge method is generated for the above, so we expect 2 greylist entries.
- verify(mStatus, times(2)).greylistEntry(greylist.capture());
- assertThat(greylist.getAllValues()).containsExactly(
- "La/b/Class;->method(Ljava/lang/Object;)V",
- "La/b/Base;->method(Ljava/lang/Object;)V");
- }
-}
diff --git a/tools/dexanalyze/dexanalyze_bytecode.cc b/tools/dexanalyze/dexanalyze_bytecode.cc
index 1c5a5d5..0bb3f91 100644
--- a/tools/dexanalyze/dexanalyze_bytecode.cc
+++ b/tools/dexanalyze/dexanalyze_bytecode.cc
@@ -164,13 +164,7 @@
std::map<size_t, TypeLinkage>& types) {
TypeLinkage& current_type = types[current_class_type.index_];
bool skip_next = false;
- size_t last_start = 0u;
for (auto inst = code_item.begin(); ; ++inst) {
- if (!count_types && last_start != buffer_.size()) {
- // Register the instruction blob.
- ++instruction_freq_[std::vector<uint8_t>(buffer_.begin() + last_start, buffer_.end())];
- last_start = buffer_.size();
- }
if (inst == code_item.end()) {
break;
}
@@ -334,31 +328,31 @@
}
}
- bool result = false;
uint32_t type_idx = current_type.types_.Get(receiver_type.index_);
uint32_t local_idx = types[receiver_type.index_].methods_.Get(method_idx);
- ExtendPrefix(&type_idx, &local_idx);
- ExtendPrefix(&dest_reg, &local_idx);
- if (arg_count == 0) {
- result = InstNibbles(opcode, {dest_reg, type_idx, local_idx});
- } else if (arg_count == 1) {
- result = InstNibbles(opcode, {dest_reg, type_idx, local_idx, args[0]});
- } else if (arg_count == 2) {
- result = InstNibbles(opcode, {dest_reg, type_idx, local_idx, args[0],
- args[1]});
- } else if (arg_count == 3) {
- result = InstNibbles(opcode, {dest_reg, type_idx, local_idx, args[0],
- args[1], args[2]});
- } else if (arg_count == 4) {
- result = InstNibbles(opcode, {dest_reg, type_idx, local_idx, args[0],
- args[1], args[2], args[3]});
- } else if (arg_count == 5) {
- result = InstNibbles(opcode, {dest_reg, type_idx, local_idx, args[0],
- args[1], args[2], args[3], args[4]});
- }
- if (result) {
+ // If true, we always put the return value in r0.
+ static constexpr bool kMoveToDestReg = true;
+
+ std::vector<uint32_t> new_args;
+ if (kMoveToDestReg && arg_count % 2 == 1) {
+ // Use the extra nibble to sneak in part of the type index.
+ new_args.push_back(local_idx >> 4);
+ local_idx ^= local_idx & 0xF0;
+ }
+ ExtendPrefix(&type_idx, &local_idx);
+ new_args.push_back(type_idx);
+ new_args.push_back(local_idx);
+ if (!kMoveToDestReg) {
+ ExtendPrefix(&dest_reg, &local_idx);
+ new_args.push_back(dest_reg);
+ }
+ new_args.insert(new_args.end(), args, args + arg_count);
+ if (InstNibbles(opcode, new_args)) {
skip_next = next_move_result;
+ if (kMoveToDestReg && dest_reg != 0u) {
+ CHECK(InstNibbles(Instruction::MOVE, {dest_reg >> 4, dest_reg & 0xF}));
+ }
continue;
}
}
@@ -466,8 +460,11 @@
void NewRegisterInstructions::Add(Instruction::Code opcode, const Instruction& inst) {
const uint8_t* start = reinterpret_cast<const uint8_t*>(&inst);
+ const size_t buffer_start = buffer_.size();
buffer_.push_back(opcode);
buffer_.insert(buffer_.end(), start + 1, start + 2 * inst.SizeInCodeUnits());
+ // Register the instruction blob.
+ ++instruction_freq_[std::vector<uint8_t>(buffer_.begin() + buffer_start, buffer_.end())];
}
void NewRegisterInstructions::ExtendPrefix(uint32_t* value1, uint32_t* value2) {
@@ -500,17 +497,6 @@
*value2 &= 0XF;
}
-bool NewRegisterInstructions::InstNibblesAndIndex(uint8_t opcode,
- uint16_t idx,
- const std::vector<uint32_t>& args) {
- if (!InstNibbles(opcode, args)) {
- return false;
- }
- buffer_.push_back(static_cast<uint8_t>(idx >> 8));
- buffer_.push_back(static_cast<uint8_t>(idx));
- return true;
-}
-
bool NewRegisterInstructions::InstNibbles(uint8_t opcode, const std::vector<uint32_t>& args) {
if (verbose_level_ >= VerboseLevel::kEverything) {
std::cout << " ==> " << Instruction::Name(static_cast<Instruction::Code>(opcode)) << " ";
@@ -526,6 +512,7 @@
return false;
}
}
+ const size_t buffer_start = buffer_.size();
buffer_.push_back(opcode);
for (size_t i = 0; i < args.size(); i += 2) {
buffer_.push_back(args[i] << 4);
@@ -536,6 +523,8 @@
while (buffer_.size() % alignment_ != 0) {
buffer_.push_back(0);
}
+ // Register the instruction blob.
+ ++instruction_freq_[std::vector<uint8_t>(buffer_.begin() + buffer_start, buffer_.end())];
return true;
}
diff --git a/tools/dexanalyze/dexanalyze_bytecode.h b/tools/dexanalyze/dexanalyze_bytecode.h
index ed40ba7..db009b0 100644
--- a/tools/dexanalyze/dexanalyze_bytecode.h
+++ b/tools/dexanalyze/dexanalyze_bytecode.h
@@ -64,7 +64,6 @@
bool count_types,
std::map<size_t, TypeLinkage>& types);
void Add(Instruction::Code opcode, const Instruction& inst);
- bool InstNibblesAndIndex(uint8_t opcode, uint16_t idx, const std::vector<uint32_t>& args);
bool InstNibbles(uint8_t opcode, const std::vector<uint32_t>& args);
void ExtendPrefix(uint32_t* value1, uint32_t* value2);
bool Enabled(BytecodeExperiment experiment) const {
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index 0381381..bf8a1b7 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -26,6 +26,7 @@
#include "base/os.h"
#include "base/unix_file/fd_file.h"
#include "dex/art_dex_file_loader.h"
+#include "dex/class_accessor-inl.h"
#include "dex/dex_file-inl.h"
#include "dex/hidden_api_access_flags.h"
@@ -75,7 +76,9 @@
UsageError("");
UsageError(" Command \"list\": dump lists of public and private API");
UsageError(" --boot-dex=<filename>: dex file which belongs to boot class path");
- UsageError(" --stub-dex=<filename>: dex/apk file which belongs to SDK API stubs");
+ UsageError(" --stub-classpath=<filenames>: colon-separated list of dex/apk files");
+ UsageError(" which form API stubs of boot class path. Multiple classpaths can");
+ UsageError(" be specified");
UsageError("");
UsageError(" --out-public=<filename>: output file for a list of all public APIs");
UsageError(" --out-private=<filename>: output file for a list of all private APIs");
@@ -89,42 +92,33 @@
return std::find(vec.begin(), vec.end(), elem) != vec.end();
}
-class DexClass {
+class DexClass : public ClassAccessor {
public:
- DexClass(const DexFile& dex_file, uint32_t idx)
- : dex_file_(dex_file), class_def_(dex_file.GetClassDef(idx)) {}
+ explicit DexClass(const ClassAccessor& accessor) : ClassAccessor(accessor) {}
- const DexFile& GetDexFile() const { return dex_file_; }
- const uint8_t* GetData() const { return dex_file_.GetClassData(class_def_); }
+ const uint8_t* GetData() const { return dex_file_.GetClassData(GetClassDef()); }
- const dex::TypeIndex GetClassIndex() const { return class_def_.class_idx_; }
- const dex::TypeIndex GetSuperclassIndex() const { return class_def_.superclass_idx_; }
+ const dex::TypeIndex GetSuperclassIndex() const { return GetClassDef().superclass_idx_; }
bool HasSuperclass() const { return dex_file_.IsTypeIndexValid(GetSuperclassIndex()); }
- std::string GetDescriptor() const { return dex_file_.GetClassDescriptor(class_def_); }
-
std::string GetSuperclassDescriptor() const {
- if (HasSuperclass()) {
- return dex_file_.StringByTypeIdx(GetSuperclassIndex());
- } else {
- return "";
- }
+ return HasSuperclass() ? dex_file_.StringByTypeIdx(GetSuperclassIndex()) : "";
}
std::set<std::string> GetInterfaceDescriptors() const {
std::set<std::string> list;
- const DexFile::TypeList* ifaces = dex_file_.GetInterfacesList(class_def_);
+ const DexFile::TypeList* ifaces = dex_file_.GetInterfacesList(GetClassDef());
for (uint32_t i = 0; ifaces != nullptr && i < ifaces->Size(); ++i) {
list.insert(dex_file_.StringByTypeIdx(ifaces->GetTypeItem(i).type_idx_));
}
return list;
}
- inline bool IsVisible() const { return HasAccessFlags(kAccPublic); }
+ inline bool IsPublic() const { return HasAccessFlags(kAccPublic); }
inline bool Equals(const DexClass& other) const {
- bool equals = GetDescriptor() == other.GetDescriptor();
+ bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0;
if (equals) {
// TODO(dbrazdil): Check that methods/fields match as well once b/111116543 is fixed.
CHECK_EQ(GetAccessFlags(), other.GetAccessFlags());
@@ -135,39 +129,40 @@
}
private:
- uint32_t GetAccessFlags() const { return class_def_.access_flags_; }
+ uint32_t GetAccessFlags() const { return GetClassDef().access_flags_; }
bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
-
- const DexFile& dex_file_;
- const DexFile::ClassDef& class_def_;
};
class DexMember {
public:
- DexMember(const DexClass& klass, const ClassDataItemIterator& it)
- : klass_(klass), it_(it) {
- DCHECK_EQ(IsMethod() ? GetMethodId().class_idx_ : GetFieldId().class_idx_,
- klass_.GetClassIndex());
+ DexMember(const DexClass& klass, const ClassAccessor::Field& item)
+ : klass_(klass), item_(item), is_method_(false) {
+ DCHECK_EQ(GetFieldId().class_idx_, klass.GetClassIdx());
+ }
+
+ DexMember(const DexClass& klass, const ClassAccessor::Method& item)
+ : klass_(klass), item_(item), is_method_(true) {
+ DCHECK_EQ(GetMethodId().class_idx_, klass.GetClassIdx());
}
inline const DexClass& GetDeclaringClass() const { return klass_; }
// Sets hidden bits in access flags and writes them back into the DEX in memory.
- // Note that this will not update the cached data of ClassDataItemIterator
+ // Note that this will not update the cached data of the class accessor
// until it iterates over this item again and therefore will fail a CHECK if
// it is called multiple times on the same DexMember.
- void SetHidden(HiddenApiAccessFlags::ApiList value) {
- const uint32_t old_flags = it_.GetRawMemberAccessFlags();
+ void SetHidden(HiddenApiAccessFlags::ApiList value) const {
+ const uint32_t old_flags = item_.GetRawAccessFlags();
const uint32_t new_flags = HiddenApiAccessFlags::EncodeForDex(old_flags, value);
CHECK_EQ(UnsignedLeb128Size(new_flags), UnsignedLeb128Size(old_flags));
// Locate the LEB128-encoded access flags in class data.
// `ptr` initially points to the next ClassData item. We iterate backwards
// until we hit the terminating byte of the previous Leb128 value.
- const uint8_t* ptr = it_.DataPointer();
+ const uint8_t* ptr = item_.GetDataPointer();
if (IsMethod()) {
ptr = ReverseSearchUnsignedLeb128(ptr);
- DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), it_.GetMethodCodeItemOffset());
+ DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), GetMethod().GetCodeItemOffset());
}
ptr = ReverseSearchUnsignedLeb128(ptr);
DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), old_flags);
@@ -176,23 +171,23 @@
UpdateUnsignedLeb128(const_cast<uint8_t*>(ptr), new_flags);
}
- inline bool IsMethod() const { return it_.IsAtMethod(); }
- inline bool IsVirtualMethod() const { return it_.IsAtVirtualMethod(); }
+ inline bool IsMethod() const { return is_method_; }
+ inline bool IsVirtualMethod() const { return IsMethod() && !GetMethod().IsStaticOrDirect(); }
+ inline bool IsConstructor() const { return IsMethod() && HasAccessFlags(kAccConstructor); }
- // Returns true if the member is public/protected and is in a public class.
- inline bool IsVisible() const {
- return GetDeclaringClass().IsVisible() &&
- (HasAccessFlags(kAccPublic) || HasAccessFlags(kAccProtected));
+ inline bool IsPublicOrProtected() const {
+ return HasAccessFlags(kAccPublic) || HasAccessFlags(kAccProtected);
}
// Constructs a string with a unique signature of this class member.
std::string GetApiEntry() const {
std::stringstream ss;
- ss << klass_.GetDescriptor() << "->" << GetName() << (IsMethod() ? "" : ":") << GetSignature();
+ ss << klass_.GetDescriptor() << "->" << GetName() << (IsMethod() ? "" : ":")
+ << GetSignature();
return ss.str();
}
- inline bool operator==(const DexMember& other) {
+ inline bool operator==(const DexMember& other) const {
// These need to match if they should resolve to one another.
bool equals = IsMethod() == other.IsMethod() &&
GetName() == other.GetName() &&
@@ -207,31 +202,37 @@
}
private:
- inline uint32_t GetAccessFlags() const { return it_.GetMemberAccessFlags(); }
+ inline uint32_t GetAccessFlags() const { return item_.GetAccessFlags(); }
inline uint32_t HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
inline std::string GetName() const {
- return IsMethod() ? klass_.GetDexFile().GetMethodName(GetMethodId())
- : klass_.GetDexFile().GetFieldName(GetFieldId());
+ return IsMethod() ? item_.GetDexFile().GetMethodName(GetMethodId())
+ : item_.GetDexFile().GetFieldName(GetFieldId());
}
inline std::string GetSignature() const {
- return IsMethod() ? klass_.GetDexFile().GetMethodSignature(GetMethodId()).ToString()
- : klass_.GetDexFile().GetFieldTypeDescriptor(GetFieldId());
+ return IsMethod() ? item_.GetDexFile().GetMethodSignature(GetMethodId()).ToString()
+ : item_.GetDexFile().GetFieldTypeDescriptor(GetFieldId());
+ }
+
+ inline const ClassAccessor::Method& GetMethod() const {
+ DCHECK(IsMethod());
+ return down_cast<const ClassAccessor::Method&>(item_);
}
inline const DexFile::MethodId& GetMethodId() const {
DCHECK(IsMethod());
- return klass_.GetDexFile().GetMethodId(it_.GetMemberIndex());
+ return item_.GetDexFile().GetMethodId(item_.GetIndex());
}
inline const DexFile::FieldId& GetFieldId() const {
DCHECK(!IsMethod());
- return klass_.GetDexFile().GetFieldId(it_.GetMemberIndex());
+ return item_.GetDexFile().GetFieldId(item_.GetIndex());
}
const DexClass& klass_;
- const ClassDataItemIterator& it_;
+ const ClassAccessor::BaseItem& item_;
+ const bool is_method_;
};
class ClassPath FINAL {
@@ -243,22 +244,20 @@
template<typename Fn>
void ForEachDexClass(Fn fn) {
for (auto& dex_file : dex_files_) {
- for (uint32_t class_idx = 0; class_idx < dex_file->NumClassDefs(); ++class_idx) {
- DexClass klass(*dex_file, class_idx);
- fn(klass);
+ for (ClassAccessor accessor : dex_file->GetClasses()) {
+ fn(DexClass(accessor));
}
}
}
template<typename Fn>
void ForEachDexMember(Fn fn) {
- ForEachDexClass([&fn](DexClass& klass) {
- const uint8_t* klass_data = klass.GetData();
- if (klass_data != nullptr) {
- for (ClassDataItemIterator it(klass.GetDexFile(), klass_data); it.HasNext(); it.Next()) {
- DexMember member(klass, it);
- fn(member);
- }
+ ForEachDexClass([&fn](const DexClass& klass) {
+ for (const ClassAccessor::Field& field : klass.GetFields()) {
+ fn(DexMember(klass, field));
+ }
+ for (const ClassAccessor::Method& method : klass.GetMethods()) {
+ fn(DexMember(klass, method));
}
});
}
@@ -344,6 +343,24 @@
return ForEachResolvableMember_Impl(other, fn) != ResolutionResult::kNotFound;
}
+ // Returns true if this class contains at least one member matching `other`.
+ bool HasMatchingMember(const DexMember& other) {
+ return ForEachMatchingMember(
+ other, [](const DexMember&) { return true; }) != ResolutionResult::kNotFound;
+ }
+
+ // Recursively iterates over all subclasses of this class and invokes `fn`
+ // on each one. If `fn` returns false for a particular subclass, exploring its
+ // subclasses is skipped.
+ template<typename Fn>
+ void ForEachSubClass(Fn fn) {
+ for (HierarchyClass* subclass : extended_by_) {
+ if (fn(subclass)) {
+ subclass->ForEachSubClass(fn);
+ }
+ }
+ }
+
private:
// Result of resolution which takes into account whether the member was found
// for the first time or not. This is just a performance optimization to prevent
@@ -397,16 +414,18 @@
template<typename Fn>
ResolutionResult ForEachMatchingMember(const DexMember& other, Fn fn) {
ResolutionResult found = ResolutionResult::kNotFound;
+ auto compare_member = [&](const DexMember& member) {
+ if (member == other) {
+ found = Accumulate(found, fn(member) ? ResolutionResult::kFoundNew
+ : ResolutionResult::kFoundOld);
+ }
+ };
for (const DexClass& dex_class : dex_classes_) {
- const uint8_t* data = dex_class.GetData();
- if (data != nullptr) {
- for (ClassDataItemIterator it(dex_class.GetDexFile(), data); it.HasNext(); it.Next()) {
- DexMember member(dex_class, it);
- if (member == other) {
- found = Accumulate(found, fn(member) ? ResolutionResult::kFoundNew
- : ResolutionResult::kFoundOld);
- }
- }
+ for (const ClassAccessor::Field& field : dex_class.GetFields()) {
+ compare_member(DexMember(dex_class, field));
+ }
+ for (const ClassAccessor::Method& method : dex_class.GetMethods()) {
+ compare_member(DexMember(dex_class, method));
}
}
return found;
@@ -438,7 +457,7 @@
class Hierarchy FINAL {
public:
- explicit Hierarchy(ClassPath& class_path) : class_path_(class_path) {
+ explicit Hierarchy(ClassPath& classpath) : classpath_(classpath) {
BuildClassHierarchy();
}
@@ -454,6 +473,48 @@
return (klass != nullptr) && klass->ForEachResolvableMember(other, fn);
}
+ // Returns true if `member`, which belongs to this classpath, is visible to
+ // code in child class loaders.
+ bool IsMemberVisible(const DexMember& member) {
+ if (!member.IsPublicOrProtected()) {
+ // Member is private or package-private. Cannot be visible.
+ return false;
+ } else if (member.GetDeclaringClass().IsPublic()) {
+ // Member is public or protected, and class is public. It must be visible.
+ return true;
+ } else if (member.IsConstructor()) {
+ // Member is public or protected constructor and class is not public.
+ // Must be hidden because it cannot be implicitly exposed by a subclass.
+ return false;
+ } else {
+ // Member is public or protected method, but class is not public. Check if
+ // it is exposed through a public subclass.
+ // Example code (`foo` exposed by ClassB):
+ // class ClassA { public void foo() { ... } }
+ // public class ClassB extends ClassA {}
+ HierarchyClass* klass = FindClass(member.GetDeclaringClass().GetDescriptor());
+ CHECK(klass != nullptr);
+ bool visible = false;
+ klass->ForEachSubClass([&visible, &member](HierarchyClass* subclass) {
+ if (subclass->HasMatchingMember(member)) {
+ // There is a member which matches `member` in `subclass`, either
+ // a virtual method overriding `member` or a field overshadowing
+ // `member`. In either case, `member` remains hidden.
+ CHECK(member.IsVirtualMethod() || !member.IsMethod());
+ return false; // do not explore deeper
+ } else if (subclass->GetOneDexClass().IsPublic()) {
+ // `subclass` inherits and exposes `member`.
+ visible = true;
+ return false; // do not explore deeper
+ } else {
+ // `subclass` inherits `member` but does not expose it.
+ return true; // explore deeper
+ }
+ });
+ return visible;
+ }
+ }
+
private:
HierarchyClass* FindClass(const std::string& descriptor) {
auto it = classes_.find(descriptor);
@@ -467,7 +528,7 @@
void BuildClassHierarchy() {
// Create one HierarchyClass entry in `classes_` per class descriptor
// and add all DexClass objects with the same descriptor to that entry.
- class_path_.ForEachDexClass([this](DexClass& klass) {
+ classpath_.ForEachDexClass([this](const DexClass& klass) {
classes_[klass.GetDescriptor()].AddDexClass(klass);
});
@@ -494,7 +555,7 @@
}
}
- ClassPath& class_path_;
+ ClassPath& classpath_;
std::map<std::string, HierarchyClass> classes_;
};
@@ -547,8 +608,9 @@
const StringPiece option(argv[i]);
if (option.starts_with("--boot-dex=")) {
boot_dex_paths_.push_back(option.substr(strlen("--boot-dex=")).ToString());
- } else if (option.starts_with("--stub-dex=")) {
- stub_dex_paths_.push_back(option.substr(strlen("--stub-dex=")).ToString());
+ } else if (option.starts_with("--stub-classpath=")) {
+ stub_classpaths_.push_back(android::base::Split(
+ option.substr(strlen("--stub-classpath=")).ToString(), ":"));
} else if (option.starts_with("--out-public=")) {
out_public_path_ = option.substr(strlen("--out-public=")).ToString();
} else if (option.starts_with("--out-private=")) {
@@ -578,19 +640,15 @@
OpenApiFile(blacklist_path_, api_list, HiddenApiAccessFlags::kBlacklist);
// Open all dex files.
- ClassPath boot_class_path(boot_dex_paths_, /* open_writable */ true);
+ ClassPath boot_classpath(boot_dex_paths_, /* open_writable */ true);
// Set access flags of all members.
- boot_class_path.ForEachDexMember([&api_list](DexMember& boot_member) {
+ boot_classpath.ForEachDexMember([&api_list](const DexMember& boot_member) {
auto it = api_list.find(boot_member.GetApiEntry());
- if (it == api_list.end()) {
- boot_member.SetHidden(HiddenApiAccessFlags::kWhitelist);
- } else {
- boot_member.SetHidden(it->second);
- }
+ boot_member.SetHidden(it == api_list.end() ? HiddenApiAccessFlags::kWhitelist : it->second);
});
- boot_class_path.UpdateDexChecksums();
+ boot_classpath.UpdateDexChecksums();
}
void OpenApiFile(const std::string& path,
@@ -614,7 +672,7 @@
void ListApi() {
if (boot_dex_paths_.empty()) {
Usage("No boot DEX files specified");
- } else if (stub_dex_paths_.empty()) {
+ } else if (stub_classpaths_.empty()) {
Usage("No stub DEX files specified");
} else if (out_public_path_.empty()) {
Usage("No public API output path specified");
@@ -630,39 +688,43 @@
std::set<std::string> unresolved;
// Open all dex files.
- ClassPath stub_class_path(stub_dex_paths_, /* open_writable */ false);
- ClassPath boot_class_path(boot_dex_paths_, /* open_writable */ false);
- Hierarchy boot_hierarchy(boot_class_path);
+ ClassPath boot_classpath(boot_dex_paths_, /* open_writable */ false);
+ Hierarchy boot_hierarchy(boot_classpath);
// Mark all boot dex members private.
- boot_class_path.ForEachDexMember([&boot_members](DexMember& boot_member) {
+ boot_classpath.ForEachDexMember([&boot_members](const DexMember& boot_member) {
boot_members[boot_member.GetApiEntry()] = false;
});
// Resolve each SDK dex member against the framework and mark it white.
- stub_class_path.ForEachDexMember(
- [&boot_hierarchy, &boot_members, &unresolved](DexMember& stub_member) {
- if (!stub_member.IsVisible()) {
- // Typically fake constructors and inner-class `this` fields.
- return;
- }
- bool resolved = boot_hierarchy.ForEachResolvableMember(
- stub_member,
- [&boot_members](DexMember& boot_member) {
- std::string entry = boot_member.GetApiEntry();
- auto it = boot_members.find(entry);
- CHECK(it != boot_members.end());
- if (it->second) {
- return false; // has been marked before
- } else {
- it->second = true;
- return true; // marked for the first time
- }
- });
- if (!resolved) {
- unresolved.insert(stub_member.GetApiEntry());
- }
- });
+ for (const std::vector<std::string>& stub_classpath_dex : stub_classpaths_) {
+ ClassPath stub_classpath(stub_classpath_dex, /* open_writable */ false);
+ Hierarchy stub_hierarchy(stub_classpath);
+ stub_classpath.ForEachDexMember(
+ [&stub_hierarchy, &boot_hierarchy, &boot_members, &unresolved](
+ const DexMember& stub_member) {
+ if (!stub_hierarchy.IsMemberVisible(stub_member)) {
+ // Typically fake constructors and inner-class `this` fields.
+ return;
+ }
+ bool resolved = boot_hierarchy.ForEachResolvableMember(
+ stub_member,
+ [&boot_members](const DexMember& boot_member) {
+ std::string entry = boot_member.GetApiEntry();
+ auto it = boot_members.find(entry);
+ CHECK(it != boot_members.end());
+ if (it->second) {
+ return false; // has been marked before
+ } else {
+ it->second = true;
+ return true; // marked for the first time
+ }
+ });
+ if (!resolved) {
+ unresolved.insert(stub_member.GetApiEntry());
+ }
+ });
+ }
// Print errors.
for (const std::string& str : unresolved) {
@@ -685,7 +747,10 @@
// Paths to DEX files which should be processed.
std::vector<std::string> boot_dex_paths_;
- std::vector<std::string> stub_dex_paths_;
+
+ // Set of public API stub classpaths. Each classpath is formed by a list
+ // of DEX/APK files in the order they appear on the classpath.
+ std::vector<std::vector<std::string>> stub_classpaths_;
// Paths to text files which contain the lists of API members.
std::string light_greylist_path_;
diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc
index aa87f21..b50f684 100644
--- a/tools/hiddenapi/hiddenapi_test.cc
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -20,6 +20,7 @@
#include "base/zip_archive.h"
#include "common_runtime_test.h"
#include "dex/art_dex_file_loader.h"
+#include "dex/class_accessor-inl.h"
#include "dex/dex_file-inl.h"
#include "exec_utils.h"
@@ -114,40 +115,27 @@
}
const DexFile::ClassDef& FindClass(const char* desc, const DexFile& dex_file) {
- for (uint32_t i = 0; i < dex_file.NumClassDefs(); ++i) {
- const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
- if (strcmp(desc, dex_file.GetClassDescriptor(class_def)) == 0) {
- return class_def;
- }
- }
- LOG(FATAL) << "Could not find class " << desc;
- UNREACHABLE();
+ const DexFile::TypeId* type_id = dex_file.FindTypeId(desc);
+ CHECK(type_id != nullptr) << "Could not find class " << desc;
+ const DexFile::ClassDef* found = dex_file.FindClassDef(dex_file.GetIndexForTypeId(*type_id));
+ CHECK(found != nullptr) << "Could not find class " << desc;
+ return *found;
}
HiddenApiAccessFlags::ApiList GetFieldHiddenFlags(const char* name,
uint32_t expected_visibility,
const DexFile::ClassDef& class_def,
const DexFile& dex_file) {
- const uint8_t* class_data = dex_file.GetClassData(class_def);
- if (class_data == nullptr) {
- LOG(FATAL) << "Class " << dex_file.GetClassDescriptor(class_def) << " has no data";
- UNREACHABLE();
- }
+ ClassAccessor accessor(dex_file, class_def);
+ CHECK(accessor.HasClassData()) << "Class " << accessor.GetDescriptor() << " has no data";
- for (ClassDataItemIterator it(dex_file, class_data); it.HasNext(); it.Next()) {
- if (it.IsAtMethod()) {
- break;
- }
- const DexFile::FieldId& fid = dex_file.GetFieldId(it.GetMemberIndex());
+ for (const ClassAccessor::Field& field : accessor.GetFields()) {
+ const DexFile::FieldId& fid = dex_file.GetFieldId(field.GetIndex());
if (strcmp(name, dex_file.GetFieldName(fid)) == 0) {
- uint32_t actual_visibility = it.GetFieldAccessFlags() & kAccVisibilityFlags;
- if (actual_visibility != expected_visibility) {
- LOG(FATAL) << "Field " << name << " in class " << dex_file.GetClassDescriptor(class_def)
- << " does not have the expected visibility flags (" << expected_visibility
- << " != " << actual_visibility << ")";
- UNREACHABLE();
- }
- return it.DecodeHiddenAccessFlags();
+ const uint32_t actual_visibility = field.GetAccessFlags() & kAccVisibilityFlags;
+ CHECK_EQ(actual_visibility, expected_visibility)
+ << "Field " << name << " in class " << accessor.GetDescriptor();
+ return field.DecodeHiddenAccessFlags();
}
}
@@ -161,31 +149,18 @@
bool expected_native,
const DexFile::ClassDef& class_def,
const DexFile& dex_file) {
- const uint8_t* class_data = dex_file.GetClassData(class_def);
- if (class_data == nullptr) {
- LOG(FATAL) << "Class " << dex_file.GetClassDescriptor(class_def) << " has no data";
- UNREACHABLE();
- }
+ ClassAccessor accessor(dex_file, class_def);
+ CHECK(accessor.HasClassData()) << "Class " << accessor.GetDescriptor() << " has no data";
- for (ClassDataItemIterator it(dex_file, class_data); it.HasNext(); it.Next()) {
- if (!it.IsAtMethod()) {
- continue;
- }
- const DexFile::MethodId& mid = dex_file.GetMethodId(it.GetMemberIndex());
+ for (const ClassAccessor::Method& method : accessor.GetMethods()) {
+ const DexFile::MethodId& mid = dex_file.GetMethodId(method.GetIndex());
if (strcmp(name, dex_file.GetMethodName(mid)) == 0) {
- if (expected_native != it.MemberIsNative()) {
- LOG(FATAL) << "Expected native=" << expected_native << " for method " << name
- << " in class " << dex_file.GetClassDescriptor(class_def);
- UNREACHABLE();
- }
- uint32_t actual_visibility = it.GetMethodAccessFlags() & kAccVisibilityFlags;
- if (actual_visibility != expected_visibility) {
- LOG(FATAL) << "Method " << name << " in class " << dex_file.GetClassDescriptor(class_def)
- << " does not have the expected visibility flags (" << expected_visibility
- << " != " << actual_visibility << ")";
- UNREACHABLE();
- }
- return it.DecodeHiddenAccessFlags();
+ CHECK_EQ(expected_native, method.MemberIsNative())
+ << "Method " << name << " in class " << accessor.GetDescriptor();
+ const uint32_t actual_visibility = method.GetAccessFlags() & kAccVisibilityFlags;
+ CHECK_EQ(actual_visibility, expected_visibility)
+ << "Method " << name << " in class " << accessor.GetDescriptor();
+ return method.DecodeHiddenAccessFlags();
}
}
diff --git a/tools/jfuzz/jfuzz.cc b/tools/jfuzz/jfuzz.cc
index 60c6275..a97a99c 100644
--- a/tools/jfuzz/jfuzz.cc
+++ b/tools/jfuzz/jfuzz.cc
@@ -1302,7 +1302,7 @@
int32_t main(int32_t argc, char** argv) {
// Time-based seed.
struct timeval tp;
- gettimeofday(&tp, NULL);
+ gettimeofday(&tp, nullptr);
// Defaults.
uint32_t seed = (tp.tv_sec * 1000000 + tp.tv_usec);
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 60aac3c..264217e 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -214,5 +214,15 @@
description: "java.io.IOException: Error writing ASN.1 encoding",
result: EXEC_FAILED,
names: ["libcore.javax.crypto.spec.AlgorithmParametersTestGCM#testEncoding"]
+},
+{
+ description: "Tests fail because mockito can not read android.os.Build$VERSION",
+ result: EXEC_FAILED,
+ bug: 111704422,
+ names: ["libcore.java.lang.ThreadTest#testUncaughtExceptionPreHandler_calledBeforeDefaultHandler",
+ "libcore.java.lang.ThreadTest#testUncaughtExceptionPreHandler_noDefaultHandler",
+ "libcore.javax.crypto.CipherInputStreamTest#testCloseTwice",
+ "libcore.libcore.io.BlockGuardOsTest#test_android_getaddrinfo_networkPolicy",
+ "libcore.libcore.io.BlockGuardOsTest#test_checkNewMethodsInPosix"]
}
]
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index f0e4932..942a4e0 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -9,12 +9,14 @@
result: EXEC_FAILED,
modes: [device],
names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed",
+ "jsr166.CompletableFutureTest#testDelayedExecutor",
"libcore.libcore.icu.TransliteratorTest#testAll",
"libcore.libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045",
"libcore.libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157",
"libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout",
"libcore.java.util.TimeZoneTest#testSetDefaultDeadlock",
"libcore.javax.crypto.CipherBasicsTest#testBasicEncryption",
+ "org.apache.harmony.tests.java.text.MessageFormatTest#test_parseLjava_lang_String",
"org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"]
},
{
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index aff009a..2d39b2a 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -156,7 +156,11 @@
# Increase the timeout, as vogar cannot set individual test
# timeout when being asked to run packages, and some tests go above
# the default timeout.
-vogar_args="$vogar_args --timeout 480"
+if $gcstress && $debug && $device_mode; then
+ vogar_args="$vogar_args --timeout 1440"
+else
+ vogar_args="$vogar_args --timeout 480"
+fi
# set the toolchain to use.
vogar_args="$vogar_args --toolchain d8 --language CUR"
@@ -191,4 +195,7 @@
# Run the tests using vogar.
echo "Running tests for the following test packages:"
echo ${working_packages[@]} | tr " " "\n"
-vogar $vogar_args $expectations $(cparg $DEPS) ${working_packages[@]}
+
+cmd="vogar $vogar_args $expectations $(cparg $DEPS) ${working_packages[@]}"
+echo "Running $cmd"
+eval $cmd
diff --git a/tools/teardown-buildbot-device.sh b/tools/teardown-buildbot-device.sh
index be68b9f..6634fb4 100755
--- a/tools/teardown-buildbot-device.sh
+++ b/tools/teardown-buildbot-device.sh
@@ -42,15 +42,14 @@
# that started this process.
for_all_chroot_process() {
local action=$1
- for link in $(adb shell ls -d "/proc/*/root"); do
- local root=$(adb shell readlink "$link")
- if [[ "x$root" = "x$ART_TEST_CHROOT" ]]; then
- local dir=$(dirname "$link")
- local pid=$(basename "$dir")
- local cmdline=$(adb shell cat "$dir"/cmdline | tr '\000' ' ')
- $action "$pid" "$cmdline"
- fi
- done
+ adb shell ls -ld "/proc/*/root" \
+ | sed -n -e "s,^.* \\(/proc/.*/root\\) -> $ART_TEST_CHROOT\$,\\1,p" \
+ | while read link; do
+ local dir=$(dirname "$link")
+ local pid=$(basename "$dir")
+ local cmdline=$(adb shell cat "$dir"/cmdline | tr '\000' ' ')
+ $action "$pid" "$cmdline"
+ done
}
# display_process PID CMDLINE
diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc
index d4f7e5f..f5eb4ea 100644
--- a/tools/veridex/flow_analysis.cc
+++ b/tools/veridex/flow_analysis.cc
@@ -17,6 +17,7 @@
#include "flow_analysis.h"
#include "dex/bytecode_utils.h"
+#include "dex/class_accessor-inl.h"
#include "dex/code_item_accessors-inl.h"
#include "dex/dex_instruction-inl.h"
#include "dex/dex_file-inl.h"
@@ -26,6 +27,13 @@
namespace art {
+VeriFlowAnalysis::VeriFlowAnalysis(VeridexResolver* resolver,
+ const ClassAccessor::Method& method)
+ : resolver_(resolver),
+ method_id_(method.GetIndex()),
+ code_item_accessor_(method.GetInstructionsAndData()),
+ dex_registers_(code_item_accessor_.InsnsSizeInCodeUnits()),
+ instruction_infos_(code_item_accessor_.InsnsSizeInCodeUnits()) {}
void VeriFlowAnalysis::SetAsBranchTarget(uint32_t dex_pc) {
if (dex_registers_[dex_pc] == nullptr) {
diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h
index fc09360..9c86024 100644
--- a/tools/veridex/flow_analysis.h
+++ b/tools/veridex/flow_analysis.h
@@ -17,6 +17,7 @@
#ifndef ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
#define ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
+#include "dex/class_accessor.h"
#include "dex/code_item_accessors.h"
#include "dex/dex_file_reference.h"
#include "dex/method_reference.h"
@@ -108,12 +109,7 @@
class VeriFlowAnalysis {
public:
- VeriFlowAnalysis(VeridexResolver* resolver, const ClassDataItemIterator& it)
- : resolver_(resolver),
- method_id_(it.GetMemberIndex()),
- code_item_accessor_(resolver->GetDexFile(), it.GetMethodCodeItem()),
- dex_registers_(code_item_accessor_.InsnsSizeInCodeUnits()),
- instruction_infos_(code_item_accessor_.InsnsSizeInCodeUnits()) {}
+ VeriFlowAnalysis(VeridexResolver* resolver, const ClassAccessor::Method& method);
void Run();
@@ -189,8 +185,8 @@
// Collects all reflection uses.
class FlowAnalysisCollector : public VeriFlowAnalysis {
public:
- FlowAnalysisCollector(VeridexResolver* resolver, const ClassDataItemIterator& it)
- : VeriFlowAnalysis(resolver, it) {}
+ FlowAnalysisCollector(VeridexResolver* resolver, const ClassAccessor::Method& method)
+ : VeriFlowAnalysis(resolver, method) {}
const std::vector<ReflectAccessInfo>& GetUses() const {
return uses_;
@@ -208,9 +204,9 @@
class FlowAnalysisSubstitutor : public VeriFlowAnalysis {
public:
FlowAnalysisSubstitutor(VeridexResolver* resolver,
- const ClassDataItemIterator& it,
+ const ClassAccessor::Method& method,
const std::map<MethodReference, std::vector<ReflectAccessInfo>>& accesses)
- : VeriFlowAnalysis(resolver, it), accesses_(accesses) {}
+ : VeriFlowAnalysis(resolver, method), accesses_(accesses) {}
const std::vector<ReflectAccessInfo>& GetUses() const {
return uses_;
diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc
index 8c6139f..4eba10e 100644
--- a/tools/veridex/hidden_api_finder.cc
+++ b/tools/veridex/hidden_api_finder.cc
@@ -16,6 +16,7 @@
#include "hidden_api_finder.h"
+#include "dex/class_accessor-inl.h"
#include "dex/code_item_accessors-inl.h"
#include "dex/dex_instruction-inl.h"
#include "dex/dex_file.h"
@@ -62,23 +63,9 @@
}
// Note: we collect strings constants only referenced in code items as the string table
// contains other kind of strings (eg types).
- size_t class_def_count = dex_file.NumClassDefs();
- for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
- const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- const uint8_t* class_data = dex_file.GetClassData(class_def);
- if (class_data == nullptr) {
- // Empty class.
- continue;
- }
- ClassDataItemIterator it(dex_file, class_data);
- it.SkipAllFields();
- for (; it.HasNextMethod(); it.Next()) {
- const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
- if (code_item == nullptr) {
- continue;
- }
- CodeItemDataAccessor code_item_accessor(dex_file, code_item);
- for (const DexInstructionPcPair& inst : code_item_accessor) {
+ for (ClassAccessor accessor : dex_file.GetClasses()) {
+ for (const ClassAccessor::Method& method : accessor.GetMethods()) {
+ for (const DexInstructionPcPair& inst : method.GetInstructions()) {
switch (inst->Opcode()) {
case Instruction::CONST_STRING: {
dex::StringIndex string_index(inst->VRegB_21c());
@@ -103,8 +90,7 @@
// We only keep track of the location for strings, as these will be the
// field/method names the user is interested in.
strings_.insert(name);
- reflection_locations_[name].push_back(
- MethodReference(&dex_file, it.GetMemberIndex()));
+ reflection_locations_[name].push_back(method.GetReference());
}
}
break;
@@ -114,8 +100,7 @@
case Instruction::INVOKE_STATIC:
case Instruction::INVOKE_SUPER:
case Instruction::INVOKE_VIRTUAL: {
- CheckMethod(
- inst->VRegB_35c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+ CheckMethod(inst->VRegB_35c(), resolver, method.GetReference());
break;
}
@@ -124,8 +109,7 @@
case Instruction::INVOKE_STATIC_RANGE:
case Instruction::INVOKE_SUPER_RANGE:
case Instruction::INVOKE_VIRTUAL_RANGE: {
- CheckMethod(
- inst->VRegB_3rc(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+ CheckMethod(inst->VRegB_3rc(), resolver, method.GetReference());
break;
}
@@ -136,8 +120,7 @@
case Instruction::IGET_BYTE:
case Instruction::IGET_CHAR:
case Instruction::IGET_SHORT: {
- CheckField(
- inst->VRegC_22c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+ CheckField(inst->VRegC_22c(), resolver, method.GetReference());
break;
}
@@ -148,8 +131,7 @@
case Instruction::IPUT_BYTE:
case Instruction::IPUT_CHAR:
case Instruction::IPUT_SHORT: {
- CheckField(
- inst->VRegC_22c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+ CheckField(inst->VRegC_22c(), resolver, method.GetReference());
break;
}
@@ -160,8 +142,7 @@
case Instruction::SGET_BYTE:
case Instruction::SGET_CHAR:
case Instruction::SGET_SHORT: {
- CheckField(
- inst->VRegB_21c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+ CheckField(inst->VRegB_21c(), resolver, method.GetReference());
break;
}
@@ -172,8 +153,7 @@
case Instruction::SPUT_BYTE:
case Instruction::SPUT_CHAR:
case Instruction::SPUT_SHORT: {
- CheckField(
- inst->VRegB_21c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+ CheckField(inst->VRegB_21c(), resolver, method.GetReference());
break;
}
diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc
index 89754c2..445221e 100644
--- a/tools/veridex/precise_hidden_api_finder.cc
+++ b/tools/veridex/precise_hidden_api_finder.cc
@@ -16,6 +16,7 @@
#include "precise_hidden_api_finder.h"
+#include "dex/class_accessor-inl.h"
#include "dex/code_item_accessors-inl.h"
#include "dex/dex_instruction-inl.h"
#include "dex/dex_file.h"
@@ -31,25 +32,13 @@
void PreciseHiddenApiFinder::RunInternal(
const std::vector<std::unique_ptr<VeridexResolver>>& resolvers,
- const std::function<void(VeridexResolver*, const ClassDataItemIterator&)>& action) {
+ const std::function<void(VeridexResolver*, const ClassAccessor::Method&)>& action) {
for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
- const DexFile& dex_file = resolver->GetDexFile();
- size_t class_def_count = dex_file.NumClassDefs();
- for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
- const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- const uint8_t* class_data = dex_file.GetClassData(class_def);
- if (class_data == nullptr) {
- // Empty class.
- continue;
- }
- ClassDataItemIterator it(dex_file, class_data);
- it.SkipAllFields();
- for (; it.HasNextMethod(); it.Next()) {
- const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
- if (code_item == nullptr) {
- continue;
+ for (ClassAccessor accessor : resolver->GetDexFile().GetClasses()) {
+ for (const ClassAccessor::Method& method : accessor.GetMethods()) {
+ if (method.GetCodeItem() != nullptr) {
+ action(resolver.get(), method);
}
- action(resolver.get(), it);
}
}
}
@@ -68,10 +57,10 @@
void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
// Collect reflection uses.
- RunInternal(resolvers, [this] (VeridexResolver* resolver, const ClassDataItemIterator& it) {
- FlowAnalysisCollector collector(resolver, it);
+ RunInternal(resolvers, [this] (VeridexResolver* resolver, const ClassAccessor::Method& method) {
+ FlowAnalysisCollector collector(resolver, method);
collector.Run();
- AddUsesAt(collector.GetUses(), MethodReference(&resolver->GetDexFile(), it.GetMemberIndex()));
+ AddUsesAt(collector.GetUses(), method.GetReference());
});
// For non-final reflection uses, do a limited fixed point calculation over the code to try
@@ -84,11 +73,11 @@
std::map<MethodReference, std::vector<ReflectAccessInfo>> current_uses
= std::move(abstract_uses_);
RunInternal(resolvers,
- [this, current_uses] (VeridexResolver* resolver, const ClassDataItemIterator& it) {
- FlowAnalysisSubstitutor substitutor(resolver, it, current_uses);
+ [this, current_uses] (VeridexResolver* resolver,
+ const ClassAccessor::Method& method) {
+ FlowAnalysisSubstitutor substitutor(resolver, method, current_uses);
substitutor.Run();
- AddUsesAt(substitutor.GetUses(),
- MethodReference(&resolver->GetDexFile(), it.GetMemberIndex()));
+ AddUsesAt(substitutor.GetUses(), method.GetReference());
});
}
}
diff --git a/tools/veridex/precise_hidden_api_finder.h b/tools/veridex/precise_hidden_api_finder.h
index 1c4d0ae..8c5126c 100644
--- a/tools/veridex/precise_hidden_api_finder.h
+++ b/tools/veridex/precise_hidden_api_finder.h
@@ -48,7 +48,7 @@
// Run over all methods of all dex files, and call `action` on each.
void RunInternal(
const std::vector<std::unique_ptr<VeridexResolver>>& resolvers,
- const std::function<void(VeridexResolver*, const ClassDataItemIterator&)>& action);
+ const std::function<void(VeridexResolver*, const ClassAccessor::Method&)>& action);
// Add uses found in method `ref`.
void AddUsesAt(const std::vector<ReflectAccessInfo>& accesses, MethodReference ref);
diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc
index 9113039..56729ff 100644
--- a/tools/veridex/resolver.cc
+++ b/tools/veridex/resolver.cc
@@ -16,6 +16,7 @@
#include "resolver.h"
+#include "dex/class_accessor-inl.h"
#include "dex/dex_file-inl.h"
#include "dex/primitive.h"
#include "hidden_api.h"
@@ -24,34 +25,22 @@
namespace art {
void VeridexResolver::Run() {
- size_t class_def_count = dex_file_.NumClassDefs();
- for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
- const DexFile::ClassDef& class_def = dex_file_.GetClassDef(class_def_index);
- std::string name(dex_file_.StringByTypeIdx(class_def.class_idx_));
+ for (ClassAccessor accessor : dex_file_.GetClasses()) {
+ std::string name(accessor.GetDescriptor());
auto existing = type_map_.find(name);
+ const uint32_t type_idx = accessor.GetClassIdx().index_;
if (existing != type_map_.end()) {
// Class already exists, cache it and move on.
- type_infos_[class_def.class_idx_.index_] = *existing->second;
+ type_infos_[type_idx] = *existing->second;
continue;
}
- type_infos_[class_def.class_idx_.index_] = VeriClass(Primitive::Type::kPrimNot, 0, &class_def);
- type_map_[name] = &(type_infos_[class_def.class_idx_.index_]);
-
- const uint8_t* class_data = dex_file_.GetClassData(class_def);
- if (class_data == nullptr) {
- // Empty class.
- continue;
+ type_infos_[type_idx] = VeriClass(Primitive::Type::kPrimNot, 0, &accessor.GetClassDef());
+ type_map_[name] = &type_infos_[type_idx];
+ for (const ClassAccessor::Field& field : accessor.GetFields()) {
+ field_infos_[field.GetIndex()] = field.GetDataPointer();
}
-
- ClassDataItemIterator it(dex_file_, class_data);
- for (; it.HasNextStaticField(); it.Next()) {
- field_infos_[it.GetMemberIndex()] = it.DataPointer();
- }
- for (; it.HasNextInstanceField(); it.Next()) {
- field_infos_[it.GetMemberIndex()] = it.DataPointer();
- }
- for (; it.HasNextMethod(); it.Next()) {
- method_infos_[it.GetMemberIndex()] = it.DataPointer();
+ for (const ClassAccessor::Method& method : accessor.GetMethods()) {
+ method_infos_[method.GetIndex()] = method.GetDataPointer();
}
}
}
@@ -148,18 +137,14 @@
// Look at methods declared in `kls`.
const DexFile& other_dex_file = resolver->dex_file_;
- const uint8_t* class_data = other_dex_file.GetClassData(*kls.GetClassDef());
- if (class_data != nullptr) {
- ClassDataItemIterator it(other_dex_file, class_data);
- it.SkipAllFields();
- for (; it.HasNextMethod(); it.Next()) {
- const DexFile::MethodId& other_method_id = other_dex_file.GetMethodId(it.GetMemberIndex());
- if (HasSameNameAndSignature(other_dex_file,
- other_method_id,
- method_name,
- method_signature)) {
- return it.DataPointer();
- }
+ ClassAccessor other_dex_accessor(other_dex_file, *kls.GetClassDef());
+ for (const ClassAccessor::Method& method : other_dex_accessor.GetMethods()) {
+ const DexFile::MethodId& other_method_id = other_dex_file.GetMethodId(method.GetIndex());
+ if (HasSameNameAndSignature(other_dex_file,
+ other_method_id,
+ method_name,
+ method_signature)) {
+ return method.GetDataPointer();
}
}
@@ -207,17 +192,14 @@
// Look at fields declared in `kls`.
const DexFile& other_dex_file = resolver->dex_file_;
- const uint8_t* class_data = other_dex_file.GetClassData(*kls.GetClassDef());
- if (class_data != nullptr) {
- ClassDataItemIterator it(other_dex_file, class_data);
- for (; it.HasNextStaticField() || it.HasNextInstanceField(); it.Next()) {
- const DexFile::FieldId& other_field_id = other_dex_file.GetFieldId(it.GetMemberIndex());
- if (HasSameNameAndType(other_dex_file,
- other_field_id,
- field_name,
- field_type)) {
- return it.DataPointer();
- }
+ ClassAccessor other_dex_accessor(other_dex_file, *kls.GetClassDef());
+ for (const ClassAccessor::Field& field : other_dex_accessor.GetFields()) {
+ const DexFile::FieldId& other_field_id = other_dex_file.GetFieldId(field.GetIndex());
+ if (HasSameNameAndType(other_dex_file,
+ other_field_id,
+ field_name,
+ field_type)) {
+ return field.GetDataPointer();
}
}
@@ -260,18 +242,13 @@
}
VeridexResolver* resolver = GetResolverOf(kls);
const DexFile& other_dex_file = resolver->dex_file_;
- const uint8_t* class_data = other_dex_file.GetClassData(*kls.GetClassDef());
- if (class_data != nullptr) {
- ClassDataItemIterator it(other_dex_file, class_data);
- it.SkipAllFields();
- for (; it.HasNextMethod(); it.Next()) {
- const DexFile::MethodId& other_method_id = other_dex_file.GetMethodId(it.GetMemberIndex());
- if (HasSameNameAndSignature(other_dex_file,
- other_method_id,
- method_name,
- type)) {
- return it.DataPointer();
- }
+ ClassAccessor other_dex_accessor(other_dex_file, *kls.GetClassDef());
+ for (const ClassAccessor::Method& method : other_dex_accessor.GetMethods()) {
+ if (HasSameNameAndSignature(other_dex_file,
+ other_dex_file.GetMethodId(method.GetIndex()),
+ method_name,
+ type)) {
+ return method.GetDataPointer();
}
}
return nullptr;