Merge "MIPS32: Fix string loads that use kDexCacheViaMethod."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 4f273e5..c785bef 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -105,7 +105,7 @@
ART_GTEST_stub_test_DEX_DEPS := AllFields
ART_GTEST_transaction_test_DEX_DEPS := Transaction
ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup
-ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps
+ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps MultiDex
# The elf writer test has dependencies on core.oat.
ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_optimizing_no-pic_64) $(HOST_CORE_IMAGE_optimizing_no-pic_32)
diff --git a/compiler/compiler.h b/compiler/compiler.h
index 9a69456..2ca0b77 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_COMPILER_H_
#include "dex_file.h"
+#include "base/mutex.h"
#include "os.h"
namespace art {
@@ -34,6 +35,7 @@
class CompiledMethod;
template<class T> class Handle;
class OatWriter;
+class Thread;
class Compiler {
public:
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 56b4ebd..1b87725 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -24,6 +24,8 @@
#include <malloc.h> // For mallinfo
#endif
+#include "android-base/strings.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/array_ref.h"
@@ -2011,12 +2013,18 @@
CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous())
<< klass->PrettyDescriptor() << ": state=" << klass->GetStatus();
- // It is *very* problematic if there are verification errors in the boot classpath.
- // For example, we rely on things working OK without verification when the
- // decryption dialog is brought up. So abort in a debug build if we find this violated.
- DCHECK(!manager_->GetCompiler()->GetCompilerOptions().IsBootImage() || klass->IsVerified())
- << "Boot classpath class " << klass->PrettyClass()
- << " failed to fully verify.";
+ // It is *very* problematic if there are verification errors in the boot classpath. For example,
+ // we rely on things working OK without verification when the decryption dialog is brought up.
+ // So abort in a debug build if we find this violated.
+ if (kIsDebugBuild) {
+ // TODO(narayan): Remove this special case for signature polymorphic
+ // invokes once verifier support is fully implemented.
+ if (manager_->GetCompiler()->GetCompilerOptions().IsBootImage() &&
+ !android::base::StartsWith(descriptor, "Ljava/lang/invoke/")) {
+ DCHECK(klass->IsVerified()) << "Boot classpath class " << klass->PrettyClass()
+ << " failed to fully verify: state= " << klass->GetStatus();
+ }
+ }
} else {
// Make the skip a soft failure, essentially being considered as verify at runtime.
failure_kind = verifier::MethodVerifier::kSoftFailure;
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index d1ac139..59f339a 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -963,21 +963,21 @@
mirror::String* ImageWriter::FindInternedString(mirror::String* string) {
Thread* const self = Thread::Current();
for (const ImageInfo& image_info : image_infos_) {
- mirror::String* const found = image_info.intern_table_->LookupStrong(self, string);
+ ObjPtr<mirror::String> const found = image_info.intern_table_->LookupStrong(self, string);
DCHECK(image_info.intern_table_->LookupWeak(self, string) == nullptr)
<< string->ToModifiedUtf8();
if (found != nullptr) {
- return found;
+ return found.Ptr();
}
}
if (compile_app_image_) {
Runtime* const runtime = Runtime::Current();
- mirror::String* found = runtime->GetInternTable()->LookupStrong(self, string);
+ ObjPtr<mirror::String> found = runtime->GetInternTable()->LookupStrong(self, string);
// If we found it in the runtime intern table it could either be in the boot image or interned
// during app image compilation. If it was in the boot image return that, otherwise return null
// since it belongs to another image space.
- if (found != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(found)) {
- return found;
+ if (found != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(found.Ptr())) {
+ return found.Ptr();
}
DCHECK(runtime->GetInternTable()->LookupWeak(self, string) == nullptr)
<< string->ToModifiedUtf8();
@@ -1088,7 +1088,8 @@
mirror::String* interned = FindInternedString(obj->AsString());
if (interned == nullptr) {
// Not in another image space, insert to our table.
- interned = GetImageInfo(oat_index).intern_table_->InternStrongImageString(obj->AsString());
+ interned =
+ GetImageInfo(oat_index).intern_table_->InternStrongImageString(obj->AsString()).Ptr();
DCHECK_EQ(interned, obj);
}
} else if (obj->IsDexCache()) {
@@ -1448,7 +1449,7 @@
for (size_t i = 0, count = dex_file->NumStringIds(); i < count; ++i) {
uint32_t utf16_length;
const char* utf8_data = dex_file->StringDataAndUtf16LengthByIdx(i, &utf16_length);
- mirror::String* string = intern_table->LookupStrong(self, utf16_length, utf8_data);
+ mirror::String* string = intern_table->LookupStrong(self, utf16_length, utf8_data).Ptr();
TryAssignBinSlot(work_stack, string, oat_index);
}
}
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index ca1dc69..4960a73 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -532,6 +532,25 @@
BaseHandleScope* const handle_scope_;
};
+// Number of references allocated in JNI ShadowFrames on the given thread.
+static size_t NumJniShadowFrameReferences(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return self->GetManagedStack()->NumJniShadowFrameReferences();
+}
+
+// Number of references in handle scope on the given thread.
+static size_t NumHandleReferences(Thread* self) {
+ size_t count = 0;
+ for (BaseHandleScope* cur = self->GetTopHandleScope(); cur != nullptr; cur = cur->GetLink()) {
+ count += cur->NumberOfReferences();
+ }
+ return count;
+}
+
+// Number of references allocated in handle scopes & JNI shadow frames on this thread.
+static size_t NumStackReferences(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return NumHandleReferences(self) + NumJniShadowFrameReferences(self);
+}
+
static void expectNumStackReferences(size_t val1, size_t val2) {
// In rare cases when JNI functions call themselves recursively,
// disable this test because it will have a false negative.
@@ -539,7 +558,7 @@
/* @CriticalNative doesn't build a HandleScope, so this test is meaningless then. */
ScopedObjectAccess soa(Thread::Current());
- size_t actual_num = Thread::Current()->NumStackReferences();
+ size_t actual_num = NumStackReferences(Thread::Current());
// XX: Not too sure what's going on.
// Sometimes null references get placed and sometimes they don't?
EXPECT_TRUE(val1 == actual_num || val2 == actual_num)
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 6cbca7a..f9173f5 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1759,7 +1759,7 @@
}
std::vector<uint8_t> buffer;
- verifier_deps->Encode(&buffer);
+ verifier_deps->Encode(*dex_files_, &buffer);
if (!vdex_out->WriteFully(buffer.data(), buffer.size())) {
PLOG(ERROR) << "Failed to write verifier deps."
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 529fc9e..d2357a5 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -1845,8 +1845,8 @@
// that value dominated by that instruction fits in that range. Range of that
// value can be narrowed further down in the dominator tree.
BCEVisitor visitor(graph_, side_effects_, induction_analysis_);
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* current = it.Current();
+ for (size_t i = 0, size = graph_->GetReversePostOrder().size(); i != size; ++i) {
+ HBasicBlock* current = graph_->GetReversePostOrder()[i];
if (visitor.IsAddedBlock(current)) {
// Skip added blocks. Their effects are already taken care of.
continue;
@@ -1855,8 +1855,11 @@
// Skip forward to the current block in case new basic blocks were inserted
// (which always appear earlier in reverse post order) to avoid visiting the
// same basic block twice.
- for ( ; !it.Done() && it.Current() != current; it.Advance()) {
- }
+ size_t new_size = graph_->GetReversePostOrder().size();
+ DCHECK_GE(new_size, size);
+ i += new_size - size;
+ DCHECK_EQ(current, graph_->GetReversePostOrder()[i]);
+ size = new_size;
}
// Perform cleanup.
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 0f8cdbb..8b450e1 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -671,9 +671,9 @@
return;
}
ArenaVector<HSuspendCheck*> loop_headers(graph.GetArena()->Adapter(kArenaAllocMisc));
- for (HReversePostOrderIterator it(graph); !it.Done(); it.Advance()) {
- if (it.Current()->IsLoopHeader()) {
- HSuspendCheck* suspend_check = it.Current()->GetLoopInformation()->GetSuspendCheck();
+ for (HBasicBlock* block : graph.GetReversePostOrder()) {
+ if (block->IsLoopHeader()) {
+ HSuspendCheck* suspend_check = block->GetLoopInformation()->GetSuspendCheck();
if (!suspend_check->GetEnvironment()->IsFromInlinedInvoke()) {
loop_headers.push_back(suspend_check);
}
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 9f92b20..be65f89 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -601,11 +601,21 @@
DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM);
};
-// Slow path marking an object during a read barrier.
+// 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 (see
+// ReadBarrierMarkAndUpdateFieldSlowPathARM 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`).
class ReadBarrierMarkSlowPathARM : public SlowPathCodeARM {
public:
- ReadBarrierMarkSlowPathARM(HInstruction* instruction, Location obj)
- : SlowPathCodeARM(instruction), obj_(obj) {
+ ReadBarrierMarkSlowPathARM(HInstruction* instruction, Location ref)
+ : SlowPathCodeARM(instruction), ref_(ref) {
DCHECK(kEmitCompilerReadBarrier);
}
@@ -613,9 +623,9 @@
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
- Register reg = obj_.AsRegister<Register>();
+ Register ref_reg = ref_.AsRegister<Register>();
DCHECK(locations->CanCall());
- DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg));
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
DCHECK(instruction_->IsInstanceFieldGet() ||
instruction_->IsStaticFieldGet() ||
instruction_->IsArrayGet() ||
@@ -634,40 +644,213 @@
// entrypoint. Also, there is no need to update the stack mask,
// as this runtime call will not trigger a garbage collection.
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
- DCHECK_NE(reg, SP);
- DCHECK_NE(reg, LR);
- DCHECK_NE(reg, PC);
+ DCHECK_NE(ref_reg, SP);
+ DCHECK_NE(ref_reg, LR);
+ DCHECK_NE(ref_reg, PC);
// IP is used internally by the ReadBarrierMarkRegX entry point
// as a temporary, it cannot be the entry point's input/output.
- DCHECK_NE(reg, IP);
- DCHECK(0 <= reg && reg < kNumberOfCoreRegisters) << reg;
+ DCHECK_NE(ref_reg, IP);
+ DCHECK(0 <= ref_reg && ref_reg < kNumberOfCoreRegisters) << ref_reg;
// "Compact" slow path, saving two moves.
//
// Instead of using the standard runtime calling convention (input
// and output in R0):
//
- // R0 <- obj
+ // R0 <- ref
// R0 <- ReadBarrierMark(R0)
- // obj <- R0
+ // ref <- R0
//
- // we just use rX (the register holding `obj`) as input and output
+ // we just use rX (the register containing `ref`) as input and output
// of a dedicated entrypoint:
//
// rX <- ReadBarrierMarkRegX(rX)
//
int32_t entry_point_offset =
- CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(reg);
+ CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg);
// This runtime call does not require a stack map.
arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
__ b(GetExitLabel());
}
private:
- const Location obj_;
+ // The location (register) of the marked object reference.
+ const Location ref_;
DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARM);
};
+// Slow path marking an object reference `ref` during a read barrier,
+// and if needed, atomically updating the field `obj.field` in the
+// object `obj` holding this reference after marking (contrary to
+// ReadBarrierMarkSlowPathARM 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`).
+class ReadBarrierMarkAndUpdateFieldSlowPathARM : public SlowPathCodeARM {
+ public:
+ ReadBarrierMarkAndUpdateFieldSlowPathARM(HInstruction* instruction,
+ Location ref,
+ Register obj,
+ Location field_offset,
+ Register temp1,
+ Register temp2)
+ : SlowPathCodeARM(instruction),
+ ref_(ref),
+ obj_(obj),
+ field_offset_(field_offset),
+ temp1_(temp1),
+ temp2_(temp2) {
+ DCHECK(kEmitCompilerReadBarrier);
+ }
+
+ const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkAndUpdateFieldSlowPathARM"; }
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ Register ref_reg = ref_.AsRegister<Register>();
+ DCHECK(locations->CanCall());
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
+ // This slow path is only used by the UnsafeCASObject intrinsic.
+ 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(field_offset_.IsRegisterPair()) << field_offset_;
+
+ __ Bind(GetEntryLabel());
+
+ // Save the old reference.
+ // 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(temp1_, IP);
+ __ Mov(temp1_, ref_reg);
+
+ // 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.
+ CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
+ DCHECK_NE(ref_reg, SP);
+ DCHECK_NE(ref_reg, LR);
+ DCHECK_NE(ref_reg, PC);
+ // IP is used internally by the ReadBarrierMarkRegX entry point
+ // as a temporary, it cannot be the entry point's input/output.
+ DCHECK_NE(ref_reg, IP);
+ DCHECK(0 <= ref_reg && ref_reg < kNumberOfCoreRegisters) << 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)
+ //
+ int32_t entry_point_offset =
+ CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg);
+ // This runtime call does not require a stack map.
+ arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
+
+ // 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/SUBS/ITNE sequence of instructions in the compare-and-set
+ // (CAS) operation below would abort the CAS, leaving the field
+ // as-is.
+ Label done;
+ __ cmp(temp1_, ShifterOperand(ref_reg));
+ __ b(&done, EQ);
+
+ // 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.
+
+ // Convenience aliases.
+ Register base = obj_;
+ // The UnsafeCASObject intrinsic uses a register pair as field
+ // offset ("long offset"), of which only the low part contains
+ // data.
+ Register offset = field_offset_.AsRegisterPairLow<Register>();
+ Register expected = temp1_;
+ Register value = ref_reg;
+ Register tmp_ptr = IP; // Pointer to actual memory.
+ Register tmp = temp2_; // Value in memory.
+
+ __ add(tmp_ptr, base, ShifterOperand(offset));
+
+ if (kPoisonHeapReferences) {
+ __ PoisonHeapReference(expected);
+ if (value == expected) {
+ // Do not poison `value`, as it is the same register as
+ // `expected`, which has just been poisoned.
+ } else {
+ __ PoisonHeapReference(value);
+ }
+ }
+
+ // do {
+ // tmp = [r_ptr] - expected;
+ // } while (tmp == 0 && failure([r_ptr] <- r_new_value));
+
+ Label loop_head, exit_loop;
+ __ Bind(&loop_head);
+
+ __ ldrex(tmp, tmp_ptr);
+
+ __ subs(tmp, tmp, ShifterOperand(expected));
+
+ __ it(NE);
+ __ clrex(NE);
+
+ __ b(&exit_loop, NE);
+
+ __ strex(tmp, value, tmp_ptr);
+ __ cmp(tmp, ShifterOperand(1));
+ __ b(&loop_head, EQ);
+
+ __ Bind(&exit_loop);
+
+ if (kPoisonHeapReferences) {
+ __ UnpoisonHeapReference(expected);
+ if (value == expected) {
+ // Do not unpoison `value`, as it is the same register as
+ // `expected`, which has just been unpoisoned.
+ } else {
+ __ UnpoisonHeapReference(value);
+ }
+ }
+
+ __ Bind(&done);
+ __ b(GetExitLabel());
+ }
+
+ private:
+ // The location (register) of the marked object reference.
+ const Location ref_;
+ // The register containing the object holding the marked object reference field.
+ const Register obj_;
+ // The location of the offset of the marked reference field within `obj_`.
+ Location field_offset_;
+
+ const Register temp1_;
+ const Register temp2_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkAndUpdateFieldSlowPathARM);
+};
+
// Slow path generating a read barrier for a heap reference.
class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCodeARM {
public:
@@ -5773,7 +5956,7 @@
__ movt(temp, /* placeholder */ 0u);
__ BindTrackedLabel(&labels->add_pc_label);
__ add(temp, temp, ShifterOperand(PC));
- GenerateGcRootFieldLoad(load, out_loc, temp, 0);
+ GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kEmitCompilerReadBarrier);
SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
codegen_->AddSlowPath(slow_path);
__ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
@@ -6644,7 +6827,9 @@
Location index,
ScaleFactor scale_factor,
Location temp,
- bool needs_null_check) {
+ bool needs_null_check,
+ bool always_update_field,
+ Register* temp2) {
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
@@ -6689,8 +6874,9 @@
// The actual reference load.
if (index.IsValid()) {
- // Load types involving an "index": ArrayGet and
- // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
+ // Load types involving an "index": ArrayGet,
+ // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
+ // intrinsics.
// /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor))
if (index.IsConstant()) {
size_t computed_offset =
@@ -6698,9 +6884,9 @@
__ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
} else {
// Handle the special case of the
- // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics, which use
- // a register pair as index ("long offset"), of which only the low
- // part contains data.
+ // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
+ // intrinsics, which use a register pair as index ("long
+ // offset"), of which only the low part contains data.
Register index_reg = index.IsRegisterPair()
? index.AsRegisterPairLow<Register>()
: index.AsRegister<Register>();
@@ -6716,8 +6902,21 @@
__ MaybeUnpoisonHeapReference(ref_reg);
// Slow path marking the object `ref` when it is gray.
- SlowPathCodeARM* slow_path =
- new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, ref);
+ SlowPathCodeARM* slow_path;
+ if (always_update_field) {
+ DCHECK(temp2 != nullptr);
+ // ReadBarrierMarkAndUpdateFieldSlowPathARM only supports address
+ // of the form `obj + field_offset`, where `obj` is a register and
+ // `field_offset` is a register pair (of which only the lower half
+ // is used). Thus `offset` and `scale_factor` above are expected
+ // to be null in this code path.
+ DCHECK_EQ(offset, 0u);
+ DCHECK_EQ(scale_factor, ScaleFactor::TIMES_1);
+ slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkAndUpdateFieldSlowPathARM(
+ instruction, ref, obj, /* field_offset */ index, temp_reg, *temp2);
+ } else {
+ slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, ref);
+ }
AddSlowPath(slow_path);
// if (rb_state == ReadBarrier::gray_ptr_)
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 4d59b47..3d46aab 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -283,12 +283,12 @@
//
// root <- *(obj + offset)
//
- // while honoring read barriers if requires_read_barrier is true.
+ // while honoring read barriers if `requires_read_barrier` is true.
void GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
Register obj,
uint32_t offset,
- bool requires_read_barrier = kEmitCompilerReadBarrier);
+ bool requires_read_barrier);
void GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
Label* true_target,
@@ -508,6 +508,18 @@
bool needs_null_check);
// Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
// and GenerateArrayLoadWithBakerReadBarrier.
+
+ // 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.
+ //
+ // If `always_update_field` is true, the value of the reference is
+ // atomically updated in the holder (`obj`). This operation
+ // requires an extra temporary register, which must be provided as a
+ // non-null pointer (`temp2`).
void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
Location ref,
Register obj,
@@ -515,7 +527,9 @@
Location index,
ScaleFactor scale_factor,
Location temp,
- bool needs_null_check);
+ bool needs_null_check,
+ bool always_update_field = false,
+ Register* temp2 = nullptr);
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 9e59d8c..b537509 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -589,11 +589,21 @@
}
}
-// Slow path marking an object during a read barrier.
+// 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 (see
+// ReadBarrierMarkAndUpdateFieldSlowPathARM64 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`).
class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 {
public:
- ReadBarrierMarkSlowPathARM64(HInstruction* instruction, Location obj)
- : SlowPathCodeARM64(instruction), obj_(obj) {
+ ReadBarrierMarkSlowPathARM64(HInstruction* instruction, Location ref)
+ : SlowPathCodeARM64(instruction), ref_(ref) {
DCHECK(kEmitCompilerReadBarrier);
}
@@ -602,7 +612,8 @@
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
DCHECK(locations->CanCall());
- DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(obj_.reg()));
+ DCHECK(ref_.IsRegister()) << ref_;
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_.reg())) << ref_.reg();
DCHECK(instruction_->IsInstanceFieldGet() ||
instruction_->IsStaticFieldGet() ||
instruction_->IsArrayGet() ||
@@ -621,40 +632,207 @@
// 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(obj_.reg(), LR);
- DCHECK_NE(obj_.reg(), WSP);
- DCHECK_NE(obj_.reg(), WZR);
+ 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(obj_.reg(), IP0);
- DCHECK(0 <= obj_.reg() && obj_.reg() < kNumberOfWRegisters) << obj_.reg();
+ 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 <- obj
+ // W0 <- ref
// W0 <- ReadBarrierMark(W0)
- // obj <- W0
+ // ref <- W0
//
- // we just use rX (the register holding `obj`) as input and output
+ // we just use rX (the register containing `ref`) as input and output
// of a dedicated entrypoint:
//
// rX <- ReadBarrierMarkRegX(rX)
//
int32_t entry_point_offset =
- CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(obj_.reg());
+ CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ref_.reg());
// This runtime call does not require a stack map.
arm64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
__ B(GetExitLabel());
}
private:
- const Location obj_;
+ // The location (register) of the marked object reference.
+ const Location ref_;
DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARM64);
};
+// Slow path marking an object reference `ref` during a read barrier,
+// and if needed, atomically updating the field `obj.field` in the
+// object `obj` holding this reference after marking (contrary to
+// ReadBarrierMarkSlowPathARM64 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`).
+class ReadBarrierMarkAndUpdateFieldSlowPathARM64 : public SlowPathCodeARM64 {
+ public:
+ ReadBarrierMarkAndUpdateFieldSlowPathARM64(HInstruction* instruction,
+ Location ref,
+ Register obj,
+ Location field_offset,
+ Register temp)
+ : SlowPathCodeARM64(instruction),
+ ref_(ref),
+ obj_(obj),
+ field_offset_(field_offset),
+ temp_(temp) {
+ DCHECK(kEmitCompilerReadBarrier);
+ }
+
+ const char* GetDescription() const OVERRIDE {
+ return "ReadBarrierMarkAndUpdateFieldSlowPathARM64";
+ }
+
+ 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();
+ // This slow path is only used by the UnsafeCASObject intrinsic.
+ 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(field_offset_.IsRegister()) << field_offset_;
+
+ __ Bind(GetEntryLabel());
+
+ // Save the old reference.
+ // 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);
+
+ // 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)
+ //
+ int32_t entry_point_offset =
+ CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ref_.reg());
+ // This runtime call does not require a stack map.
+ arm64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
+
+ // 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.
+ vixl::aarch64::Label done;
+ __ Cmp(temp_.W(), ref_reg);
+ __ B(eq, &done);
+
+ // 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);
+ }
+ }
+
+ __ Bind(&done);
+ __ B(GetExitLabel());
+ }
+
+ private:
+ // The location (register) of the marked object reference.
+ const Location ref_;
+ // The register containing the object holding the marked object reference field.
+ const Register obj_;
+ // The location of the offset of the marked reference field within `obj_`.
+ Location field_offset_;
+
+ const Register temp_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkAndUpdateFieldSlowPathARM64);
+};
+
// Slow path generating a read barrier for a heap reference.
class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 {
public:
@@ -768,7 +946,7 @@
DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
(instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
<< instruction_->AsInvoke()->GetIntrinsic();
- DCHECK_EQ(offset_, 0U);
+ DCHECK_EQ(offset_, 0u);
DCHECK(index_.IsRegister());
}
}
@@ -4098,7 +4276,7 @@
out_loc,
current_method,
ArtMethod::DeclaringClassOffset().Int32Value(),
- /*fixup_label*/ nullptr,
+ /* fixup_label */ nullptr,
requires_read_barrier);
break;
}
@@ -4143,7 +4321,7 @@
out_loc,
out.X(),
offset,
- /*fixup_label*/ nullptr,
+ /* fixup_label */ nullptr,
requires_read_barrier);
generate_null_check = !cls->IsInDexCache();
break;
@@ -4180,7 +4358,7 @@
out_loc,
out.X(),
CodeGenerator::GetCacheOffset(cls->GetTypeIndex()),
- /*fixup_label*/ nullptr,
+ /* fixup_label */ nullptr,
requires_read_barrier);
generate_null_check = !cls->IsInDexCache();
break;
@@ -4319,8 +4497,9 @@
GenerateGcRootFieldLoad(load,
load->GetLocations()->Out(),
temp,
- /* placeholder */ 0u,
- ldr_label);
+ /* offset placeholder */ 0u,
+ ldr_label,
+ kEmitCompilerReadBarrier);
SlowPathCodeARM64* slow_path =
new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load, temp, adrp_label);
codegen_->AddSlowPath(slow_path);
@@ -5174,7 +5353,7 @@
// /* HeapReference<Object> */ ref = *(obj + offset)
Location no_index = Location::NoLocation();
- size_t no_scale_factor = 0U;
+ size_t no_scale_factor = 0u;
GenerateReferenceLoadWithBakerReadBarrier(instruction,
ref,
obj,
@@ -5225,7 +5404,8 @@
size_t scale_factor,
Register temp,
bool needs_null_check,
- bool use_load_acquire) {
+ bool use_load_acquire,
+ bool always_update_field) {
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
// If we are emitting an array load, we should not be using a
@@ -5278,7 +5458,9 @@
// The actual reference load.
if (index.IsValid()) {
- // Load types involving an "index".
+ // 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
@@ -5287,9 +5469,9 @@
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, 0U);
+ DCHECK_EQ(offset, 0u);
+ DCHECK_EQ(scale_factor, 0u);
+ DCHECK_EQ(needs_null_check, 0u);
// /* HeapReference<Object> */ ref = *(obj + index)
MemOperand field = HeapOperand(obj, XRegisterFrom(index));
LoadAcquire(instruction, ref_reg, field, /* needs_null_check */ false);
@@ -5300,10 +5482,10 @@
uint32_t computed_offset = offset + (Int64ConstantFrom(index) << scale_factor);
Load(type, ref_reg, HeapOperand(obj, computed_offset));
} else {
- Register temp2 = temps.AcquireW();
- __ Add(temp2, obj, offset);
- Load(type, ref_reg, HeapOperand(temp2, XRegisterFrom(index), LSL, scale_factor));
- temps.Release(temp2);
+ Register temp3 = temps.AcquireW();
+ __ Add(temp3, obj, offset);
+ Load(type, ref_reg, HeapOperand(temp3, XRegisterFrom(index), LSL, scale_factor));
+ temps.Release(temp3);
}
}
} else {
@@ -5320,8 +5502,19 @@
GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
// Slow path marking the object `ref` when it is gray.
- SlowPathCodeARM64* slow_path =
- new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM64(instruction, ref);
+ SlowPathCodeARM64* slow_path;
+ if (always_update_field) {
+ // ReadBarrierMarkAndUpdateFieldSlowPathARM64 only supports
+ // address of the form `obj + field_offset`, where `obj` is a
+ // register and `field_offset` is a register. Thus `offset` and
+ // `scale_factor` above are expected to be null in this code path.
+ DCHECK_EQ(offset, 0u);
+ DCHECK_EQ(scale_factor, 0u); /* "times 1" */
+ slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkAndUpdateFieldSlowPathARM64(
+ instruction, ref, obj, /* field_offset */ index, temp);
+ } else {
+ slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM64(instruction, ref);
+ }
AddSlowPath(slow_path);
// if (rb_state == ReadBarrier::gray_ptr_)
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index eb28ecb..7f54b4b 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -289,13 +289,13 @@
//
// root <- *(obj + offset)
//
- // while honoring read barriers (if any).
+ // while honoring read barriers if `requires_read_barrier` is true.
void GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
vixl::aarch64::Register obj,
uint32_t offset,
- vixl::aarch64::Label* fixup_label = nullptr,
- bool requires_read_barrier = kEmitCompilerReadBarrier);
+ vixl::aarch64::Label* fixup_label,
+ bool requires_read_barrier);
// Generate a floating-point comparison.
void GenerateFcmp(HInstruction* instruction);
@@ -594,6 +594,13 @@
bool needs_null_check);
// Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
// and GenerateArrayLoadWithBakerReadBarrier.
+ //
+ // Load the object reference located at the address
+ // `obj + offset + (index << scale_factor)`, held by object `obj`, into
+ // `ref`, and mark it if needed.
+ //
+ // If `always_update_field` is true, the value of the reference is
+ // atomically updated in the holder (`obj`).
void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
Location ref,
vixl::aarch64::Register obj,
@@ -602,7 +609,8 @@
size_t scale_factor,
vixl::aarch64::Register temp,
bool needs_null_check,
- bool use_load_acquire);
+ bool use_load_acquire,
+ bool always_update_field = false);
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index bfade3c..cc40522 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -37,11 +37,12 @@
namespace vixl32 = vixl::aarch32;
using namespace vixl32; // NOLINT(build/namespaces)
+using helpers::DRegisterFrom;
using helpers::DWARFReg;
-using helpers::FromLowSToD;
using helpers::HighDRegisterFrom;
using helpers::HighRegisterFrom;
using helpers::InputOperandAt;
+using helpers::InputRegister;
using helpers::InputRegisterAt;
using helpers::InputSRegisterAt;
using helpers::InputVRegisterAt;
@@ -339,6 +340,46 @@
DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
};
+class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
+ public:
+ explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
+ : SlowPathCodeARMVIXL(instruction) {}
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
+ LocationSummary* locations = instruction_->GetLocations();
+
+ __ Bind(GetEntryLabel());
+ if (instruction_->CanThrowIntoCatchBlock()) {
+ // Live registers will be restored in the catch block if caught.
+ SaveLiveRegisters(codegen, instruction_->GetLocations());
+ }
+ // We're moving two locations to locations that could overlap, so we need a parallel
+ // move resolver.
+ InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ codegen->EmitParallelMoves(
+ locations->InAt(0),
+ LocationFrom(calling_convention.GetRegisterAt(0)),
+ Primitive::kPrimInt,
+ locations->InAt(1),
+ LocationFrom(calling_convention.GetRegisterAt(1)),
+ Primitive::kPrimInt);
+ QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? kQuickThrowStringBounds
+ : kQuickThrowArrayBounds;
+ arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
+ CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
+ }
+
+ bool IsFatal() const OVERRIDE { return true; }
+
+ const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARMVIXL"; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
+};
+
class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
public:
LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit)
@@ -393,6 +434,66 @@
DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
};
+class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
+ public:
+ explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
+ : SlowPathCodeARMVIXL(instruction) {}
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
+ __ Bind(GetEntryLabel());
+ arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
+ CheckEntrypointTypes<kQuickDeoptimize, void, void>();
+ }
+
+ const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARMVIXL"; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
+};
+
+class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
+ public:
+ explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ __ Bind(GetEntryLabel());
+ SaveLiveRegisters(codegen, locations);
+
+ InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ HParallelMove parallel_move(codegen->GetGraph()->GetArena());
+ parallel_move.AddMove(
+ locations->InAt(0),
+ LocationFrom(calling_convention.GetRegisterAt(0)),
+ Primitive::kPrimNot,
+ nullptr);
+ parallel_move.AddMove(
+ locations->InAt(1),
+ LocationFrom(calling_convention.GetRegisterAt(1)),
+ Primitive::kPrimInt,
+ nullptr);
+ parallel_move.AddMove(
+ locations->InAt(2),
+ LocationFrom(calling_convention.GetRegisterAt(2)),
+ Primitive::kPrimNot,
+ nullptr);
+ codegen->GetMoveResolver()->EmitNativeCode(¶llel_move);
+
+ CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
+ arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
+ CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
+ RestoreLiveRegisters(codegen, locations);
+ __ B(GetExitLabel());
+ }
+
+ const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARMVIXL"; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
+};
+
+
inline vixl32::Condition ARMCondition(IfCondition cond) {
switch (cond) {
case kCondEQ: return eq;
@@ -724,110 +825,6 @@
__ Blx(lr);
}
-void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
- locations->SetInAt(0, Location::RequiresRegister());
- if (check->HasUses()) {
- locations->SetOut(Location::SameAsFirstInput());
- }
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
- // We assume the class is not null.
- LoadClassSlowPathARMVIXL* slow_path =
- new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(check->GetLoadClass(),
- check,
- check->GetDexPc(),
- /* do_clinit */ true);
- codegen_->AddSlowPath(slow_path);
- GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
-}
-
-void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
- LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
- UseScratchRegisterScope temps(GetVIXLAssembler());
- vixl32::Register temp = temps.Acquire();
- GetAssembler()->LoadFromOffset(kLoadWord,
- temp,
- class_reg,
- mirror::Class::StatusOffset().Int32Value());
- __ Cmp(temp, mirror::Class::kStatusInitialized);
- __ B(lt, slow_path->GetEntryLabel());
- // Even if the initialized flag is set, we may be in a situation where caches are not synced
- // properly. Therefore, we do a memory fence.
- __ Dmb(ISH);
- __ Bind(slow_path->GetExitLabel());
-}
-
-// Check if the desired_string_load_kind is supported. If it is, return it,
-// otherwise return a fall-back kind that should be used instead.
-HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
- HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
- // TODO(VIXL): Implement optimized code paths. For now we always use the simpler fallback code.
- return HLoadString::LoadKind::kDexCacheViaMethod;
-}
-
-void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = load->NeedsEnvironment()
- ? LocationSummary::kCallOnMainOnly
- : LocationSummary::kNoCall;
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
-
- // TODO(VIXL): Implement optimized code paths.
- // See InstructionCodeGeneratorARMVIXL::VisitLoadString.
- HLoadString::LoadKind load_kind = load->GetLoadKind();
- if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
- locations->SetInAt(0, Location::RequiresRegister());
- // TODO(VIXL): Use InvokeRuntimeCallingConventionARMVIXL instead.
- locations->SetOut(LocationFrom(r0));
- } else {
- locations->SetOut(Location::RequiresRegister());
- }
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) {
- // TODO(VIXL): Implement optimized code paths.
- // We implemented the simplest solution to get first ART tests passing, we deferred the
- // optimized path until later, we should implement it using ARM64 implementation as a
- // reference. The same related to LocationsBuilderARMVIXL::VisitLoadString.
-
- // TODO: Re-add the compiler code to do string dex cache lookup again.
- DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
- InvokeRuntimeCallingConventionARMVIXL calling_convention;
- __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex());
- codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
- CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
-}
-
-// Check if the desired_class_load_kind is supported. If it is, return it,
-// otherwise return a fall-back kind that should be used instead.
-HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
- HLoadClass::LoadKind desired_class_load_kind ATTRIBUTE_UNUSED) {
- // TODO(VIXL): Implement optimized code paths.
- return HLoadClass::LoadKind::kDexCacheViaMethod;
-}
-
-// Check if the desired_dispatch_info is supported. If it is, return it,
-// otherwise return a fall-back info that should be used instead.
-HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
- const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info ATTRIBUTE_UNUSED,
- HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
- // TODO(VIXL): Implement optimized code paths.
- return {
- HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- 0u,
- 0u
- };
-}
-
-// Copy the result of a call into the given target.
-void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg ATTRIBUTE_UNUSED,
- Primitive::Type type ATTRIBUTE_UNUSED) {
- TODO_VIXL32(FATAL);
-}
-
void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
DCHECK(!successor->IsExitBlock());
HBasicBlock* block = got->GetBlock();
@@ -898,14 +895,14 @@
__ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
} else {
DCHECK_EQ(type, Primitive::kPrimDouble);
- __ Vcmp(F64, FromLowSToD(LowSRegisterFrom(lhs_loc)), 0.0);
+ __ Vcmp(F64, DRegisterFrom(lhs_loc), 0.0);
}
} else {
if (type == Primitive::kPrimFloat) {
__ Vcmp(InputSRegisterAt(instruction, 0), InputSRegisterAt(instruction, 1));
} else {
DCHECK_EQ(type, Primitive::kPrimDouble);
- __ Vcmp(FromLowSToD(LowSRegisterFrom(lhs_loc)), FromLowSToD(LowSRegisterFrom(rhs_loc)));
+ __ Vcmp(DRegisterFrom(lhs_loc), DRegisterFrom(rhs_loc));
}
}
}
@@ -1131,6 +1128,24 @@
GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
}
+void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
+ LocationSummary* locations = new (GetGraph()->GetArena())
+ LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
+ locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
+ if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
+ SlowPathCodeARMVIXL* slow_path =
+ deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
+ GenerateTestAndBranch(deoptimize,
+ /* condition_input_index */ 0,
+ slow_path->GetEntryLabel(),
+ /* false_target */ nullptr);
+}
+
void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
if (Primitive::IsFloatingPointType(select->GetType())) {
@@ -1347,6 +1362,28 @@
// Will be generated at use site.
}
+void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
+ locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
+ HFloatConstant* constant ATTRIBUTE_UNUSED) {
+ // Will be generated at use site.
+}
+
+void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
+ locations->SetOut(Location::ConstantLocation(constant));
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
+ HDoubleConstant* constant ATTRIBUTE_UNUSED) {
+ // Will be generated at use site.
+}
+
void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
memory_barrier->SetLocations(nullptr);
}
@@ -1419,6 +1456,65 @@
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
+void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
+ switch (neg->GetResultType()) {
+ case Primitive::kPrimInt: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ break;
+ }
+ case Primitive::kPrimLong: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
+ }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
+ LocationSummary* locations = neg->GetLocations();
+ Location out = locations->Out();
+ Location in = locations->InAt(0);
+ switch (neg->GetResultType()) {
+ case Primitive::kPrimInt:
+ __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
+ break;
+
+ case Primitive::kPrimLong:
+ // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
+ __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
+ // We cannot emit an RSC (Reverse Subtract with Carry)
+ // instruction here, as it does not exist in the Thumb-2
+ // instruction set. We use the following approach
+ // using SBC and SUB instead.
+ //
+ // out.hi = -C
+ __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
+ // out.hi = out.hi - in.hi
+ __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
+ break;
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ __ Vneg(OutputVRegister(neg), InputVRegisterAt(neg, 0));
+ break;
+
+ default:
+ LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
+ }
+}
+
void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
Primitive::Type result_type = conversion->GetResultType();
Primitive::Type input_type = conversion->GetInputType();
@@ -1717,7 +1813,7 @@
case Primitive::kPrimDouble: {
// Processing a Dex `double-to-int' instruction.
vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
- __ Vcvt(I32, F64, temp_s, FromLowSToD(LowSRegisterFrom(in)));
+ __ Vcvt(I32, F64, temp_s, DRegisterFrom(in));
__ Vmov(OutputRegister(conversion), temp_s);
break;
}
@@ -1805,7 +1901,7 @@
case Primitive::kPrimDouble:
// Processing a Dex `double-to-float' instruction.
- __ Vcvt(F32, F64, OutputSRegister(conversion), FromLowSToD(LowSRegisterFrom(in)));
+ __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
break;
default:
@@ -1824,7 +1920,7 @@
case Primitive::kPrimChar: {
// Processing a Dex `int-to-double' instruction.
__ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
- __ Vcvt(F64, I32, FromLowSToD(LowSRegisterFrom(out)), LowSRegisterFrom(out));
+ __ Vcvt(F64, I32, DRegisterFrom(out), LowSRegisterFrom(out));
break;
}
@@ -1834,13 +1930,12 @@
vixl32::Register high = HighRegisterFrom(in);
vixl32::SRegister out_s = LowSRegisterFrom(out);
- vixl32::DRegister out_d = FromLowSToD(out_s);
+ vixl32::DRegister out_d = DRegisterFrom(out);
vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
- vixl32::DRegister temp_d = FromLowSToD(temp_s);
+ vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
- vixl32::SRegister constant_s = LowSRegisterFrom(locations->GetTemp(1));
- vixl32::DRegister constant_d = FromLowSToD(constant_s);
+ vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(0));
// temp_d = int-to-double(high)
__ Vmov(temp_s, high);
@@ -1857,7 +1952,7 @@
case Primitive::kPrimFloat:
// Processing a Dex `float-to-double' instruction.
- __ Vcvt(F64, F32, FromLowSToD(LowSRegisterFrom(out)), InputSRegisterAt(conversion, 0));
+ __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
break;
default:
@@ -2071,267 +2166,6 @@
}
}
-void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
- InvokeRuntimeCallingConventionARMVIXL calling_convention;
- locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
- locations->SetOut(LocationFrom(r0));
- locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
- locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
- InvokeRuntimeCallingConventionARMVIXL calling_convention;
- __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
- // Note: if heap poisoning is enabled, the entry point takes cares
- // of poisoning the reference.
- codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
- CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
-}
-
-void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
- if (instruction->IsStringAlloc()) {
- locations->AddTemp(LocationFrom(kMethodRegister));
- } else {
- InvokeRuntimeCallingConventionARMVIXL calling_convention;
- locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
- locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
- }
- locations->SetOut(LocationFrom(r0));
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
- // Note: if heap poisoning is enabled, the entry point takes cares
- // of poisoning the reference.
- if (instruction->IsStringAlloc()) {
- // String is allocated through StringFactory. Call NewEmptyString entry point.
- vixl32::Register temp = RegisterFrom(instruction->GetLocations()->GetTemp(0));
- MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
- GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
- GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
- AssemblerAccurateScope aas(GetVIXLAssembler(),
- kArmInstrMaxSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
- __ blx(lr);
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
- } else {
- codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
- CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
- }
-}
-
-void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
- if (location.IsStackSlot()) {
- location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
- } else if (location.IsDoubleStackSlot()) {
- location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
- }
- locations->SetOut(location);
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
- HParameterValue* instruction ATTRIBUTE_UNUSED) {
- // Nothing to do, the parameter is already at its location.
-}
-
-void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- locations->SetOut(LocationFrom(kMethodRegister));
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
- HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
- // Nothing to do, the method is already at its location.
-}
-
-void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
- LocationSummary* locations = not_->GetLocations();
- Location out = locations->Out();
- Location in = locations->InAt(0);
- switch (not_->GetResultType()) {
- case Primitive::kPrimInt:
- __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
- break;
-
- case Primitive::kPrimLong:
- __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
- __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
- break;
-
- default:
- LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
- }
-}
-
-void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
- switch (compare->InputAt(0)->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimShort:
- case Primitive::kPrimChar:
- case Primitive::kPrimInt:
- case Primitive::kPrimLong: {
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- // Output overlaps because it is written before doing the low comparison.
- locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
- break;
- }
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble: {
- locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
- locations->SetOut(Location::RequiresRegister());
- break;
- }
- default:
- LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
- }
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
- LocationSummary* locations = compare->GetLocations();
- vixl32::Register out = OutputRegister(compare);
- Location left = locations->InAt(0);
- Location right = locations->InAt(1);
-
- vixl32::Label less, greater, done;
- Primitive::Type type = compare->InputAt(0)->GetType();
- vixl32::Condition less_cond = vixl32::Condition(kNone);
- switch (type) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimShort:
- case Primitive::kPrimChar:
- case Primitive::kPrimInt: {
- // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
- __ Mov(out, 0);
- __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
- less_cond = lt;
- break;
- }
- case Primitive::kPrimLong: {
- __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
- __ B(lt, &less);
- __ B(gt, &greater);
- // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
- __ Mov(out, 0);
- __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
- less_cond = lo;
- break;
- }
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble: {
- __ Mov(out, 0);
- GenerateVcmp(compare);
- // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
- __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
- less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
- break;
- }
- default:
- LOG(FATAL) << "Unexpected compare type " << type;
- UNREACHABLE();
- }
-
- __ B(eq, &done);
- __ B(less_cond, &less);
-
- __ Bind(&greater);
- __ Mov(out, 1);
- __ B(&done);
-
- __ Bind(&less);
- __ Mov(out, -1);
-
- __ Bind(&done);
-}
-
-void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
- for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
- locations->SetInAt(i, Location::Any());
- }
- locations->SetOut(Location::Any());
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
- LOG(FATAL) << "Unreachable";
-}
-
-void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
- // TODO (ported from quick): revisit ARM barrier kinds.
- DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
- switch (kind) {
- case MemBarrierKind::kAnyStore:
- case MemBarrierKind::kLoadAny:
- case MemBarrierKind::kAnyAny: {
- flavor = DmbOptions::ISH;
- break;
- }
- case MemBarrierKind::kStoreStore: {
- flavor = DmbOptions::ISHST;
- break;
- }
- default:
- LOG(FATAL) << "Unexpected memory barrier " << kind;
- }
- __ Dmb(flavor);
-}
-
-void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
- uint32_t offset,
- vixl32::Register out_lo,
- vixl32::Register out_hi) {
- UseScratchRegisterScope temps(GetVIXLAssembler());
- if (offset != 0) {
- vixl32::Register temp = temps.Acquire();
- __ Add(temp, addr, offset);
- addr = temp;
- }
- __ Ldrexd(out_lo, out_hi, addr);
-}
-
-void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
- uint32_t offset,
- vixl32::Register value_lo,
- vixl32::Register value_hi,
- vixl32::Register temp1,
- vixl32::Register temp2,
- HInstruction* instruction) {
- UseScratchRegisterScope temps(GetVIXLAssembler());
- vixl32::Label fail;
- if (offset != 0) {
- vixl32::Register temp = temps.Acquire();
- __ Add(temp, addr, offset);
- addr = temp;
- }
- __ Bind(&fail);
- // We need a load followed by store. (The address used in a STREX instruction must
- // be the same as the address in the most recently executed LDREX instruction.)
- __ Ldrexd(temp1, temp2, addr);
- codegen_->MaybeRecordImplicitNullCheck(instruction);
- __ Strexd(temp1, value_lo, value_hi, addr);
- __ Cbnz(temp1, &fail);
-}
-
void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
DCHECK(instruction->IsDiv() || instruction->IsRem());
DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
@@ -2592,6 +2426,647 @@
}
}
+void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
+ LocationSummary* locations = ror->GetLocations();
+ vixl32::Register in = InputRegisterAt(ror, 0);
+ Location rhs = locations->InAt(1);
+ vixl32::Register out = OutputRegister(ror);
+
+ if (rhs.IsConstant()) {
+ // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
+ // so map all rotations to a +ve. equivalent in that range.
+ // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
+ uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
+ if (rot) {
+ // Rotate, mapping left rotations to right equivalents if necessary.
+ // (e.g. left by 2 bits == right by 30.)
+ __ Ror(out, in, rot);
+ } else if (!out.Is(in)) {
+ __ Mov(out, in);
+ }
+ } else {
+ __ Ror(out, in, RegisterFrom(rhs));
+ }
+}
+
+// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
+// rotates by swapping input regs (effectively rotating by the first 32-bits of
+// a larger rotation) or flipping direction (thus treating larger right/left
+// rotations as sub-word sized rotations in the other direction) as appropriate.
+void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
+ LocationSummary* locations = ror->GetLocations();
+ vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
+ vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
+ Location rhs = locations->InAt(1);
+ vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
+ vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
+
+ if (rhs.IsConstant()) {
+ uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
+ // Map all rotations to +ve. equivalents on the interval [0,63].
+ rot &= kMaxLongShiftDistance;
+ // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
+ // logic below to a simple pair of binary orr.
+ // (e.g. 34 bits == in_reg swap + 2 bits right.)
+ if (rot >= kArmBitsPerWord) {
+ rot -= kArmBitsPerWord;
+ std::swap(in_reg_hi, in_reg_lo);
+ }
+ // Rotate, or mov to out for zero or word size rotations.
+ if (rot != 0u) {
+ __ Lsr(out_reg_hi, in_reg_hi, rot);
+ __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
+ __ Lsr(out_reg_lo, in_reg_lo, rot);
+ __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
+ } else {
+ __ Mov(out_reg_lo, in_reg_lo);
+ __ Mov(out_reg_hi, in_reg_hi);
+ }
+ } else {
+ vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
+ vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
+ vixl32::Label end;
+ vixl32::Label shift_by_32_plus_shift_right;
+
+ __ And(shift_right, RegisterFrom(rhs), 0x1F);
+ __ Lsrs(shift_left, RegisterFrom(rhs), 6);
+ // TODO(VIXL): Check that flags are kept after "vixl32::LeaveFlags" enabled.
+ __ Rsb(shift_left, shift_right, kArmBitsPerWord);
+ __ B(cc, &shift_by_32_plus_shift_right);
+
+ // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
+ // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
+ __ Lsl(out_reg_hi, in_reg_hi, shift_left);
+ __ Lsr(out_reg_lo, in_reg_lo, shift_right);
+ __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
+ __ Lsl(out_reg_lo, in_reg_lo, shift_left);
+ __ Lsr(shift_left, in_reg_hi, shift_right);
+ __ Add(out_reg_lo, out_reg_lo, shift_left);
+ __ B(&end);
+
+ __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
+ // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
+ // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
+ __ Lsr(out_reg_hi, in_reg_hi, shift_right);
+ __ Lsl(out_reg_lo, in_reg_lo, shift_left);
+ __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
+ __ Lsr(out_reg_lo, in_reg_lo, shift_right);
+ __ Lsl(shift_right, in_reg_hi, shift_left);
+ __ Add(out_reg_lo, out_reg_lo, shift_right);
+
+ __ Bind(&end);
+ }
+}
+
+void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall);
+ switch (ror->GetResultType()) {
+ case Primitive::kPrimInt: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ break;
+ }
+ case Primitive::kPrimLong: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ if (ror->InputAt(1)->IsConstant()) {
+ locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
+ }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
+ Primitive::Type type = ror->GetResultType();
+ switch (type) {
+ case Primitive::kPrimInt: {
+ HandleIntegerRotate(ror);
+ break;
+ }
+ case Primitive::kPrimLong: {
+ HandleLongRotate(ror);
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected operation type " << type;
+ UNREACHABLE();
+ }
+}
+
+void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
+ DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
+
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall);
+
+ switch (op->GetResultType()) {
+ case Primitive::kPrimInt: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ if (op->InputAt(1)->IsConstant()) {
+ locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ // Make the output overlap, as it will be used to hold the masked
+ // second input.
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ }
+ break;
+ }
+ case Primitive::kPrimLong: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ if (op->InputAt(1)->IsConstant()) {
+ locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
+ // For simplicity, use kOutputOverlap even though we only require that low registers
+ // don't clash with high registers which the register allocator currently guarantees.
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
+ }
+}
+
+void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
+ DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
+
+ LocationSummary* locations = op->GetLocations();
+ Location out = locations->Out();
+ Location first = locations->InAt(0);
+ Location second = locations->InAt(1);
+
+ Primitive::Type type = op->GetResultType();
+ switch (type) {
+ case Primitive::kPrimInt: {
+ vixl32::Register out_reg = OutputRegister(op);
+ vixl32::Register first_reg = InputRegisterAt(op, 0);
+ if (second.IsRegister()) {
+ vixl32::Register second_reg = RegisterFrom(second);
+ // ARM doesn't mask the shift count so we need to do it ourselves.
+ __ And(out_reg, second_reg, kMaxIntShiftDistance);
+ if (op->IsShl()) {
+ __ Lsl(out_reg, first_reg, out_reg);
+ } else if (op->IsShr()) {
+ __ Asr(out_reg, first_reg, out_reg);
+ } else {
+ __ Lsr(out_reg, first_reg, out_reg);
+ }
+ } else {
+ int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
+ uint32_t shift_value = cst & kMaxIntShiftDistance;
+ if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
+ __ Mov(out_reg, first_reg);
+ } else if (op->IsShl()) {
+ __ Lsl(out_reg, first_reg, shift_value);
+ } else if (op->IsShr()) {
+ __ Asr(out_reg, first_reg, shift_value);
+ } else {
+ __ Lsr(out_reg, first_reg, shift_value);
+ }
+ }
+ break;
+ }
+ case Primitive::kPrimLong: {
+ vixl32::Register o_h = HighRegisterFrom(out);
+ vixl32::Register o_l = LowRegisterFrom(out);
+
+ vixl32::Register high = HighRegisterFrom(first);
+ vixl32::Register low = LowRegisterFrom(first);
+
+ if (second.IsRegister()) {
+ vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
+
+ vixl32::Register second_reg = RegisterFrom(second);
+
+ if (op->IsShl()) {
+ __ And(o_l, second_reg, kMaxLongShiftDistance);
+ // Shift the high part
+ __ Lsl(o_h, high, o_l);
+ // Shift the low part and `or` what overflew on the high part
+ __ Rsb(temp, o_l, kArmBitsPerWord);
+ __ Lsr(temp, low, temp);
+ __ Orr(o_h, o_h, temp);
+ // If the shift is > 32 bits, override the high part
+ __ Subs(temp, o_l, kArmBitsPerWord);
+ {
+ AssemblerAccurateScope guard(GetVIXLAssembler(),
+ 3 * kArmInstrMaxSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ __ it(pl);
+ __ lsl(pl, o_h, low, temp);
+ }
+ // Shift the low part
+ __ Lsl(o_l, low, o_l);
+ } else if (op->IsShr()) {
+ __ And(o_h, second_reg, kMaxLongShiftDistance);
+ // Shift the low part
+ __ Lsr(o_l, low, o_h);
+ // Shift the high part and `or` what underflew on the low part
+ __ Rsb(temp, o_h, kArmBitsPerWord);
+ __ Lsl(temp, high, temp);
+ __ Orr(o_l, o_l, temp);
+ // If the shift is > 32 bits, override the low part
+ __ Subs(temp, o_h, kArmBitsPerWord);
+ {
+ AssemblerAccurateScope guard(GetVIXLAssembler(),
+ 3 * kArmInstrMaxSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ __ it(pl);
+ __ asr(pl, o_l, high, temp);
+ }
+ // Shift the high part
+ __ Asr(o_h, high, o_h);
+ } else {
+ __ And(o_h, second_reg, kMaxLongShiftDistance);
+ // same as Shr except we use `Lsr`s and not `Asr`s
+ __ Lsr(o_l, low, o_h);
+ __ Rsb(temp, o_h, kArmBitsPerWord);
+ __ Lsl(temp, high, temp);
+ __ Orr(o_l, o_l, temp);
+ __ Subs(temp, o_h, kArmBitsPerWord);
+ {
+ AssemblerAccurateScope guard(GetVIXLAssembler(),
+ 3 * kArmInstrMaxSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ __ it(pl);
+ __ lsr(pl, o_l, high, temp);
+ }
+ __ Lsr(o_h, high, o_h);
+ }
+ } else {
+ // Register allocator doesn't create partial overlap.
+ DCHECK(!o_l.Is(high));
+ DCHECK(!o_h.Is(low));
+ int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
+ uint32_t shift_value = cst & kMaxLongShiftDistance;
+ if (shift_value > 32) {
+ if (op->IsShl()) {
+ __ Lsl(o_h, low, shift_value - 32);
+ __ Mov(o_l, 0);
+ } else if (op->IsShr()) {
+ __ Asr(o_l, high, shift_value - 32);
+ __ Asr(o_h, high, 31);
+ } else {
+ __ Lsr(o_l, high, shift_value - 32);
+ __ Mov(o_h, 0);
+ }
+ } else if (shift_value == 32) {
+ if (op->IsShl()) {
+ __ Mov(o_h, low);
+ __ Mov(o_l, 0);
+ } else if (op->IsShr()) {
+ __ Mov(o_l, high);
+ __ Asr(o_h, high, 31);
+ } else {
+ __ Mov(o_l, high);
+ __ Mov(o_h, 0);
+ }
+ } else if (shift_value == 1) {
+ if (op->IsShl()) {
+ __ Lsls(o_l, low, 1);
+ __ Adc(o_h, high, high);
+ } else if (op->IsShr()) {
+ __ Asrs(o_h, high, 1);
+ __ Rrx(o_l, low);
+ } else {
+ __ Lsrs(o_h, high, 1);
+ __ Rrx(o_l, low);
+ }
+ } else {
+ DCHECK(2 <= shift_value && shift_value < 32) << shift_value;
+ if (op->IsShl()) {
+ __ Lsl(o_h, high, shift_value);
+ __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
+ __ Lsl(o_l, low, shift_value);
+ } else if (op->IsShr()) {
+ __ Lsr(o_l, low, shift_value);
+ __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
+ __ Asr(o_h, high, shift_value);
+ } else {
+ __ Lsr(o_l, low, shift_value);
+ __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
+ __ Lsr(o_h, high, shift_value);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected operation type " << type;
+ UNREACHABLE();
+ }
+}
+
+void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
+ HandleShift(shl);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
+ HandleShift(shl);
+}
+
+void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
+ HandleShift(shr);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
+ HandleShift(shr);
+}
+
+void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
+ HandleShift(ushr);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
+ HandleShift(ushr);
+}
+
+void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
+ if (instruction->IsStringAlloc()) {
+ locations->AddTemp(LocationFrom(kMethodRegister));
+ } else {
+ InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
+ locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
+ }
+ locations->SetOut(LocationFrom(r0));
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
+ // Note: if heap poisoning is enabled, the entry point takes cares
+ // of poisoning the reference.
+ if (instruction->IsStringAlloc()) {
+ // String is allocated through StringFactory. Call NewEmptyString entry point.
+ vixl32::Register temp = RegisterFrom(instruction->GetLocations()->GetTemp(0));
+ MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
+ GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
+ GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
+ AssemblerAccurateScope aas(GetVIXLAssembler(),
+ kArmInstrMaxSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ __ blx(lr);
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+ } else {
+ codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
+ }
+}
+
+void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
+ InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
+ locations->SetOut(LocationFrom(r0));
+ locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
+ InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
+ // Note: if heap poisoning is enabled, the entry point takes cares
+ // of poisoning the reference.
+ codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
+}
+
+void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
+ if (location.IsStackSlot()) {
+ location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
+ } else if (location.IsDoubleStackSlot()) {
+ location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
+ }
+ locations->SetOut(location);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
+ HParameterValue* instruction ATTRIBUTE_UNUSED) {
+ // Nothing to do, the parameter is already at its location.
+}
+
+void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetOut(LocationFrom(kMethodRegister));
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
+ HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
+ // Nothing to do, the method is already at its location.
+}
+
+void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
+ LocationSummary* locations = not_->GetLocations();
+ Location out = locations->Out();
+ Location in = locations->InAt(0);
+ switch (not_->GetResultType()) {
+ case Primitive::kPrimInt:
+ __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
+ break;
+
+ case Primitive::kPrimLong:
+ __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
+ __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
+ break;
+
+ default:
+ LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
+ }
+}
+
+void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(bool_not, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
+ __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
+}
+
+void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
+ switch (compare->InputAt(0)->GetType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimLong: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ // Output overlaps because it is written before doing the low comparison.
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ break;
+ }
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister());
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
+ }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
+ LocationSummary* locations = compare->GetLocations();
+ vixl32::Register out = OutputRegister(compare);
+ Location left = locations->InAt(0);
+ Location right = locations->InAt(1);
+
+ vixl32::Label less, greater, done;
+ Primitive::Type type = compare->InputAt(0)->GetType();
+ vixl32::Condition less_cond = vixl32::Condition(kNone);
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimInt: {
+ // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
+ __ Mov(out, 0);
+ __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
+ less_cond = lt;
+ break;
+ }
+ case Primitive::kPrimLong: {
+ __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
+ __ B(lt, &less);
+ __ B(gt, &greater);
+ // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
+ __ Mov(out, 0);
+ __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
+ less_cond = lo;
+ break;
+ }
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ __ Mov(out, 0);
+ GenerateVcmp(compare);
+ // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
+ __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
+ less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected compare type " << type;
+ UNREACHABLE();
+ }
+
+ __ B(eq, &done);
+ __ B(less_cond, &less);
+
+ __ Bind(&greater);
+ __ Mov(out, 1);
+ __ B(&done);
+
+ __ Bind(&less);
+ __ Mov(out, -1);
+
+ __ Bind(&done);
+}
+
+void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
+ locations->SetInAt(i, Location::Any());
+ }
+ locations->SetOut(Location::Any());
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "Unreachable";
+}
+
+void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
+ // TODO (ported from quick): revisit ARM barrier kinds.
+ DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
+ switch (kind) {
+ case MemBarrierKind::kAnyStore:
+ case MemBarrierKind::kLoadAny:
+ case MemBarrierKind::kAnyAny: {
+ flavor = DmbOptions::ISH;
+ break;
+ }
+ case MemBarrierKind::kStoreStore: {
+ flavor = DmbOptions::ISHST;
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected memory barrier " << kind;
+ }
+ __ Dmb(flavor);
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
+ uint32_t offset,
+ vixl32::Register out_lo,
+ vixl32::Register out_hi) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ if (offset != 0) {
+ vixl32::Register temp = temps.Acquire();
+ __ Add(temp, addr, offset);
+ addr = temp;
+ }
+ __ Ldrexd(out_lo, out_hi, addr);
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
+ uint32_t offset,
+ vixl32::Register value_lo,
+ vixl32::Register value_hi,
+ vixl32::Register temp1,
+ vixl32::Register temp2,
+ HInstruction* instruction) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Label fail;
+ if (offset != 0) {
+ vixl32::Register temp = temps.Acquire();
+ __ Add(temp, addr, offset);
+ addr = temp;
+ }
+ __ Bind(&fail);
+ // We need a load followed by store. (The address used in a STREX instruction must
+ // be the same as the address in the most recently executed LDREX instruction.)
+ __ Ldrexd(temp1, temp2, addr);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ Strexd(temp1, value_lo, value_hi, addr);
+ __ Cbnz(temp1, &fail);
+}
+
void LocationsBuilderARMVIXL::HandleFieldSet(
HInstruction* instruction, const FieldInfo& field_info) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
@@ -2708,7 +3183,7 @@
}
case Primitive::kPrimDouble: {
- vixl32::DRegister value_reg = FromLowSToD(LowSRegisterFrom(value));
+ vixl32::DRegister value_reg = DRegisterFrom(value);
if (is_volatile && !atomic_ldrd_strd) {
vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
@@ -2750,16 +3225,6 @@
}
}
-Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
- DCHECK(Primitive::IsFloatingPointType(input->GetType())) << input->GetType();
- if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
- (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
- return Location::ConstantLocation(input->AsConstant());
- } else {
- return Location::RequiresFpuRegister();
- }
-}
-
void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
const FieldInfo& field_info) {
DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
@@ -2809,6 +3274,78 @@
}
}
+Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
+ DCHECK(Primitive::IsFloatingPointType(input->GetType())) << input->GetType();
+ if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
+ (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
+ return Location::ConstantLocation(input->AsConstant());
+ } else {
+ return Location::RequiresFpuRegister();
+ }
+}
+
+Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
+ Opcode opcode) {
+ DCHECK(!Primitive::IsFloatingPointType(constant->GetType()));
+ if (constant->IsConstant() &&
+ CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
+ return Location::ConstantLocation(constant->AsConstant());
+ }
+ return Location::RequiresRegister();
+}
+
+bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst,
+ Opcode opcode) {
+ uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
+ if (Primitive::Is64BitType(input_cst->GetType())) {
+ Opcode high_opcode = opcode;
+ SetCc low_set_cc = kCcDontCare;
+ switch (opcode) {
+ case SUB:
+ // Flip the operation to an ADD.
+ value = -value;
+ opcode = ADD;
+ FALLTHROUGH_INTENDED;
+ case ADD:
+ if (Low32Bits(value) == 0u) {
+ return CanEncodeConstantAsImmediate(High32Bits(value), opcode, kCcDontCare);
+ }
+ high_opcode = ADC;
+ low_set_cc = kCcSet;
+ break;
+ default:
+ break;
+ }
+ return CanEncodeConstantAsImmediate(Low32Bits(value), opcode, low_set_cc) &&
+ CanEncodeConstantAsImmediate(High32Bits(value), high_opcode, kCcDontCare);
+ } else {
+ return CanEncodeConstantAsImmediate(Low32Bits(value), opcode);
+ }
+}
+
+// TODO(VIXL): Replace art::arm::SetCc` with `vixl32::FlagsUpdate after flags set optimization
+// enabled.
+bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(uint32_t value,
+ Opcode opcode,
+ SetCc set_cc) {
+ ArmVIXLAssembler* assembler = codegen_->GetAssembler();
+ if (assembler->ShifterOperandCanHold(opcode, value, set_cc)) {
+ return true;
+ }
+ Opcode neg_opcode = kNoOperand;
+ switch (opcode) {
+ case AND: neg_opcode = BIC; value = ~value; break;
+ case ORR: neg_opcode = ORN; value = ~value; break;
+ case ADD: neg_opcode = SUB; value = -value; break;
+ case ADC: neg_opcode = SBC; value = ~value; break;
+ case SUB: neg_opcode = ADD; value = -value; break;
+ case SBC: neg_opcode = ADC; value = ~value; break;
+ default:
+ return false;
+ }
+ return assembler->ShifterOperandCanHold(neg_opcode, value, set_cc);
+}
+
void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
const FieldInfo& field_info) {
DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
@@ -2874,7 +3411,7 @@
break;
case Primitive::kPrimDouble: {
- vixl32::DRegister out_dreg = FromLowSToD(LowSRegisterFrom(out));
+ vixl32::DRegister out_dreg = DRegisterFrom(out);
if (is_volatile && !atomic_ldrd_strd) {
vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
@@ -2938,6 +3475,14 @@
HandleFieldGet(instruction, instruction->GetFieldInfo());
}
+void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo());
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+}
+
void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
// TODO(VIXL): https://android-review.googlesource.com/#/c/275337/
LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
@@ -2974,6 +3519,554 @@
codegen_->GenerateNullCheck(instruction);
}
+static LoadOperandType GetLoadOperandType(Primitive::Type type) {
+ switch (type) {
+ case Primitive::kPrimNot:
+ return kLoadWord;
+ case Primitive::kPrimBoolean:
+ return kLoadUnsignedByte;
+ case Primitive::kPrimByte:
+ return kLoadSignedByte;
+ case Primitive::kPrimChar:
+ return kLoadUnsignedHalfword;
+ case Primitive::kPrimShort:
+ return kLoadSignedHalfword;
+ case Primitive::kPrimInt:
+ return kLoadWord;
+ case Primitive::kPrimLong:
+ return kLoadWordPair;
+ case Primitive::kPrimFloat:
+ return kLoadSWord;
+ case Primitive::kPrimDouble:
+ return kLoadDWord;
+ default:
+ LOG(FATAL) << "Unreachable type " << type;
+ UNREACHABLE();
+ }
+}
+
+static StoreOperandType GetStoreOperandType(Primitive::Type type) {
+ switch (type) {
+ case Primitive::kPrimNot:
+ return kStoreWord;
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ return kStoreByte;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ return kStoreHalfword;
+ case Primitive::kPrimInt:
+ return kStoreWord;
+ case Primitive::kPrimLong:
+ return kStoreWordPair;
+ case Primitive::kPrimFloat:
+ return kStoreSWord;
+ case Primitive::kPrimDouble:
+ return kStoreDWord;
+ default:
+ LOG(FATAL) << "Unreachable type " << type;
+ UNREACHABLE();
+ }
+}
+
+void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(Primitive::Type type,
+ Location out_loc,
+ vixl32::Register base,
+ vixl32::Register reg_index,
+ vixl32::Condition cond) {
+ uint32_t shift_count = Primitive::ComponentSizeShift(type);
+ MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
+
+ switch (type) {
+ case Primitive::kPrimByte:
+ __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
+ break;
+ case Primitive::kPrimBoolean:
+ __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
+ break;
+ case Primitive::kPrimShort:
+ __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
+ break;
+ case Primitive::kPrimChar:
+ __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
+ break;
+ case Primitive::kPrimNot:
+ case Primitive::kPrimInt:
+ __ Ldr(cond, RegisterFrom(out_loc), mem_address);
+ break;
+ // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
+ case Primitive::kPrimLong:
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ default:
+ LOG(FATAL) << "Unreachable type " << type;
+ UNREACHABLE();
+ }
+}
+
+void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(Primitive::Type type,
+ Location loc,
+ vixl32::Register base,
+ vixl32::Register reg_index,
+ vixl32::Condition cond) {
+ uint32_t shift_count = Primitive::ComponentSizeShift(type);
+ MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
+
+ switch (type) {
+ case Primitive::kPrimByte:
+ case Primitive::kPrimBoolean:
+ __ Strb(cond, RegisterFrom(loc), mem_address);
+ break;
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ __ Strh(cond, RegisterFrom(loc), mem_address);
+ break;
+ case Primitive::kPrimNot:
+ case Primitive::kPrimInt:
+ __ Str(cond, RegisterFrom(loc), mem_address);
+ break;
+ // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
+ case Primitive::kPrimLong:
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ default:
+ LOG(FATAL) << "Unreachable type " << type;
+ UNREACHABLE();
+ }
+}
+
+void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
+ bool object_array_get_with_read_barrier =
+ kEmitCompilerReadBarrier && (instruction->GetType() == Primitive::kPrimNot);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction,
+ object_array_get_with_read_barrier ?
+ LocationSummary::kCallOnSlowPath :
+ LocationSummary::kNoCall);
+ if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
+ TODO_VIXL32(FATAL);
+ }
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ if (Primitive::IsFloatingPointType(instruction->GetType())) {
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+ } else {
+ // The output overlaps in the case of an object array get with
+ // read barriers enabled: we do not want the move to overwrite the
+ // array's location, as we need it to emit the read barrier.
+ locations->SetOut(
+ Location::RequiresRegister(),
+ object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
+ }
+ // We need a temporary register for the read barrier marking slow
+ // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
+ // Also need for String compression feature.
+ if ((object_array_get_with_read_barrier && kUseBakerReadBarrier)
+ || (mirror::kUseStringCompression && instruction->IsStringCharAt())) {
+ TODO_VIXL32(FATAL);
+ }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
+ UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
+ LocationSummary* locations = instruction->GetLocations();
+ Location obj_loc = locations->InAt(0);
+ vixl32::Register obj = InputRegisterAt(instruction, 0);
+ Location index = locations->InAt(1);
+ Location out_loc = locations->Out();
+ uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
+ Primitive::Type type = instruction->GetType();
+ const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
+ instruction->IsStringCharAt();
+ HInstruction* array_instr = instruction->GetArray();
+ bool has_intermediate_address = array_instr->IsIntermediateAddress();
+ // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
+ DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
+
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimInt: {
+ if (index.IsConstant()) {
+ int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
+ if (maybe_compressed_char_at) {
+ TODO_VIXL32(FATAL);
+ } else {
+ uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type));
+
+ LoadOperandType load_type = GetLoadOperandType(type);
+ GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
+ }
+ } else {
+ vixl32::Register temp = temps.Acquire();
+
+ if (has_intermediate_address) {
+ TODO_VIXL32(FATAL);
+ } else {
+ __ Add(temp, obj, data_offset);
+ }
+ if (maybe_compressed_char_at) {
+ TODO_VIXL32(FATAL);
+ } else {
+ codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
+ }
+ }
+ break;
+ }
+
+ case Primitive::kPrimNot: {
+ static_assert(
+ sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
+ // /* HeapReference<Object> */ out =
+ // *(obj + data_offset + index * sizeof(HeapReference<Object>))
+ if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ TODO_VIXL32(FATAL);
+ } else {
+ vixl32::Register out = OutputRegister(instruction);
+ if (index.IsConstant()) {
+ size_t offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ // If read barriers are enabled, emit read barriers other than
+ // Baker's using a slow path (and also unpoison the loaded
+ // reference, if heap poisoning is enabled).
+ codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
+ } else {
+ vixl32::Register temp = temps.Acquire();
+
+ if (has_intermediate_address) {
+ TODO_VIXL32(FATAL);
+ } else {
+ __ Add(temp, obj, data_offset);
+ }
+ codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
+
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ // If read barriers are enabled, emit read barriers other than
+ // Baker's using a slow path (and also unpoison the loaded
+ // reference, if heap poisoning is enabled).
+ codegen_->MaybeGenerateReadBarrierSlow(
+ instruction, out_loc, out_loc, obj_loc, data_offset, index);
+ }
+ }
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ if (index.IsConstant()) {
+ size_t offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
+ } else {
+ vixl32::Register temp = temps.Acquire();
+ __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
+ GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimFloat: {
+ vixl32::SRegister out = SRegisterFrom(out_loc);
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ GetAssembler()->LoadSFromOffset(out, obj, offset);
+ } else {
+ vixl32::Register temp = temps.Acquire();
+ __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
+ GetAssembler()->LoadSFromOffset(out, temp, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimDouble: {
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
+ } else {
+ vixl32::Register temp = temps.Acquire();
+ __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
+ GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << type;
+ UNREACHABLE();
+ }
+
+ if (type == Primitive::kPrimNot) {
+ // Potential implicit null checks, in the case of reference
+ // arrays, are handled in the previous switch statement.
+ } else if (!maybe_compressed_char_at) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
+}
+
+void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
+ Primitive::Type value_type = instruction->GetComponentType();
+
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
+ bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
+
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+ instruction,
+ may_need_runtime_call_for_type_check ?
+ LocationSummary::kCallOnSlowPath :
+ LocationSummary::kNoCall);
+
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ if (Primitive::IsFloatingPointType(value_type)) {
+ locations->SetInAt(2, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ if (needs_write_barrier) {
+ // Temporary registers for the write barrier.
+ locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
+ locations->AddTemp(Location::RequiresRegister());
+ }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
+ UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::Register array = InputRegisterAt(instruction, 0);
+ Location index = locations->InAt(1);
+ Primitive::Type value_type = instruction->GetComponentType();
+ bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
+ bool needs_write_barrier =
+ CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
+ uint32_t data_offset =
+ mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value();
+ Location value_loc = locations->InAt(2);
+ HInstruction* array_instr = instruction->GetArray();
+ bool has_intermediate_address = array_instr->IsIntermediateAddress();
+ // The read barrier instrumentation does not support the HIntermediateAddress instruction yet.
+ DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
+
+ switch (value_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimInt: {
+ if (index.IsConstant()) {
+ int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
+ uint32_t full_offset =
+ data_offset + (const_index << Primitive::ComponentSizeShift(value_type));
+ StoreOperandType store_type = GetStoreOperandType(value_type);
+ GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
+ } else {
+ vixl32::Register temp = temps.Acquire();
+
+ if (has_intermediate_address) {
+ TODO_VIXL32(FATAL);
+ } else {
+ __ Add(temp, array, data_offset);
+ }
+ codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
+ }
+ break;
+ }
+
+ case Primitive::kPrimNot: {
+ vixl32::Register value = RegisterFrom(value_loc);
+ // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
+ // See the comment in instruction_simplifier_shared.cc.
+ DCHECK(!has_intermediate_address);
+
+ if (instruction->InputAt(2)->IsNullConstant()) {
+ // Just setting null.
+ if (index.IsConstant()) {
+ size_t offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
+ } else {
+ DCHECK(index.IsRegister()) << index;
+ vixl32::Register temp = temps.Acquire();
+ __ Add(temp, array, data_offset);
+ codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
+ }
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ DCHECK(!needs_write_barrier);
+ DCHECK(!may_need_runtime_call_for_type_check);
+ break;
+ }
+
+ DCHECK(needs_write_barrier);
+ Location temp1_loc = locations->GetTemp(0);
+ vixl32::Register temp1 = RegisterFrom(temp1_loc);
+ Location temp2_loc = locations->GetTemp(1);
+ vixl32::Register temp2 = RegisterFrom(temp2_loc);
+ uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+ uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
+ uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
+ vixl32::Label done;
+ SlowPathCodeARMVIXL* slow_path = nullptr;
+
+ if (may_need_runtime_call_for_type_check) {
+ slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathARMVIXL(instruction);
+ codegen_->AddSlowPath(slow_path);
+ if (instruction->GetValueCanBeNull()) {
+ vixl32::Label non_zero;
+ __ Cbnz(value, &non_zero);
+ if (index.IsConstant()) {
+ size_t offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
+ } else {
+ DCHECK(index.IsRegister()) << index;
+ vixl32::Register temp = temps.Acquire();
+ __ Add(temp, array, data_offset);
+ codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
+ }
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ __ B(&done);
+ __ Bind(&non_zero);
+ }
+
+ // Note that when read barriers are enabled, the type checks
+ // are performed without read barriers. This is fine, even in
+ // the case where a class object is in the from-space after
+ // the flip, as a comparison involving such a type would not
+ // produce a false positive; it may of course produce a false
+ // negative, in which case we would take the ArraySet slow
+ // path.
+
+ // /* HeapReference<Class> */ temp1 = array->klass_
+ GetAssembler()->LoadFromOffset(kLoadWord, temp1, array, class_offset);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ GetAssembler()->MaybeUnpoisonHeapReference(temp1);
+
+ // /* HeapReference<Class> */ temp1 = temp1->component_type_
+ GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
+ // /* HeapReference<Class> */ temp2 = value->klass_
+ GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
+ // If heap poisoning is enabled, no need to unpoison `temp1`
+ // nor `temp2`, as we are comparing two poisoned references.
+ __ Cmp(temp1, temp2);
+
+ if (instruction->StaticTypeOfArrayIsObjectArray()) {
+ vixl32::Label do_put;
+ __ B(eq, &do_put);
+ // If heap poisoning is enabled, the `temp1` reference has
+ // not been unpoisoned yet; unpoison it now.
+ GetAssembler()->MaybeUnpoisonHeapReference(temp1);
+
+ // /* HeapReference<Class> */ temp1 = temp1->super_class_
+ GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
+ // If heap poisoning is enabled, no need to unpoison
+ // `temp1`, as we are comparing against null below.
+ __ Cbnz(temp1, slow_path->GetEntryLabel());
+ __ Bind(&do_put);
+ } else {
+ __ B(ne, slow_path->GetEntryLabel());
+ }
+ }
+
+ vixl32::Register source = value;
+ if (kPoisonHeapReferences) {
+ // Note that in the case where `value` is a null reference,
+ // we do not enter this block, as a null reference does not
+ // need poisoning.
+ DCHECK_EQ(value_type, Primitive::kPrimNot);
+ __ Mov(temp1, value);
+ GetAssembler()->PoisonHeapReference(temp1);
+ source = temp1;
+ }
+
+ if (index.IsConstant()) {
+ size_t offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
+ } else {
+ DCHECK(index.IsRegister()) << index;
+
+ vixl32::Register temp = temps.Acquire();
+ __ Add(temp, array, data_offset);
+ codegen_->StoreToShiftedRegOffset(value_type,
+ LocationFrom(source),
+ temp,
+ RegisterFrom(index));
+ }
+
+ if (!may_need_runtime_call_for_type_check) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
+
+ codegen_->MarkGCCard(temp1, temp2, array, value, instruction->GetValueCanBeNull());
+
+ if (done.IsReferenced()) {
+ __ Bind(&done);
+ }
+
+ if (slow_path != nullptr) {
+ __ Bind(slow_path->GetExitLabel());
+ }
+
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ Location value = locations->InAt(2);
+ if (index.IsConstant()) {
+ size_t offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
+ } else {
+ vixl32::Register temp = temps.Acquire();
+ __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
+ GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimFloat: {
+ Location value = locations->InAt(2);
+ DCHECK(value.IsFpuRegister());
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
+ } else {
+ vixl32::Register temp = temps.Acquire();
+ __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
+ GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimDouble: {
+ Location value = locations->InAt(2);
+ DCHECK(value.IsFpuRegisterPair());
+ if (index.IsConstant()) {
+ size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
+ } else {
+ vixl32::Register temp = temps.Acquire();
+ __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
+ GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
+ }
+ break;
+ }
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << value_type;
+ UNREACHABLE();
+ }
+
+ // Objects are handled in the switch.
+ if (value_type != Primitive::kPrimNot) {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
+}
+
void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
@@ -2990,6 +4083,28 @@
// TODO(VIXL): https://android-review.googlesource.com/#/c/272625/
}
+void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
+ caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
+ LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
+ SlowPathCodeARMVIXL* slow_path =
+ new (GetGraph()->GetArena()) BoundsCheckSlowPathARMVIXL(instruction);
+ codegen_->AddSlowPath(slow_path);
+
+ vixl32::Register index = InputRegisterAt(instruction, 0);
+ vixl32::Register length = InputRegisterAt(instruction, 1);
+
+ __ Cmp(index, length);
+ __ B(hs, slow_path->GetEntryLabel());
+}
+
void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
vixl32::Register card,
vixl32::Register object,
@@ -3101,7 +4216,14 @@
GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
}
} else if (source.IsFpuRegister()) {
- TODO_VIXL32(FATAL);
+ if (destination.IsRegister()) {
+ __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
+ } else if (destination.IsFpuRegister()) {
+ __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
+ } else {
+ DCHECK(destination.IsStackSlot());
+ GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
+ }
} else if (source.IsDoubleStackSlot()) {
if (destination.IsDoubleStackSlot()) {
vixl32::DRegister temp = temps.AcquireD();
@@ -3112,16 +4234,15 @@
GetAssembler()->LoadFromOffset(
kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
} else {
- TODO_VIXL32(FATAL);
+ DCHECK(destination.IsFpuRegisterPair()) << destination;
+ GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
}
} else if (source.IsRegisterPair()) {
if (destination.IsRegisterPair()) {
__ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
__ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
} else if (destination.IsFpuRegisterPair()) {
- __ Vmov(FromLowSToD(LowSRegisterFrom(destination)),
- LowRegisterFrom(source),
- HighRegisterFrom(source));
+ __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
} else {
DCHECK(destination.IsDoubleStackSlot()) << destination;
DCHECK(ExpectedPairLayout(source));
@@ -3131,7 +4252,14 @@
destination.GetStackIndex());
}
} else if (source.IsFpuRegisterPair()) {
- TODO_VIXL32(FATAL);
+ if (destination.IsRegisterPair()) {
+ __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
+ } else if (destination.IsFpuRegisterPair()) {
+ __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
+ } else {
+ DCHECK(destination.IsDoubleStackSlot()) << destination;
+ GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
+ }
} else {
DCHECK(source.IsConstant()) << source;
HConstant* constant = source.GetConstant();
@@ -3164,7 +4292,7 @@
} else if (constant->IsDoubleConstant()) {
double value = constant->AsDoubleConstant()->GetValue();
if (destination.IsFpuRegisterPair()) {
- __ Vmov(FromLowSToD(LowSRegisterFrom(destination)), value);
+ __ Vmov(DRegisterFrom(destination), value);
} else {
DCHECK(destination.IsDoubleStackSlot()) << destination;
uint64_t int_value = bit_cast<uint64_t, double>(value);
@@ -3273,6 +4401,14 @@
TODO_VIXL32(FATAL);
}
+// Check if the desired_class_load_kind is supported. If it is, return it,
+// otherwise return a fall-back kind that should be used instead.
+HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind ATTRIBUTE_UNUSED) {
+ // TODO(VIXL): Implement optimized code paths.
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+}
+
void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
if (cls->NeedsAccessCheck()) {
InvokeRuntimeCallingConventionARMVIXL calling_convention;
@@ -3321,7 +4457,8 @@
GenerateGcRootFieldLoad(cls,
out_loc,
current_method,
- ArtMethod::DeclaringClassOffset().Int32Value());
+ ArtMethod::DeclaringClassOffset().Int32Value(),
+ kEmitCompilerReadBarrier);
break;
}
case HLoadClass::LoadKind::kDexCacheViaMethod: {
@@ -3333,7 +4470,7 @@
GetAssembler()->LoadFromOffset(kLoadWord, out, current_method, resolved_types_offset);
// /* GcRoot<mirror::Class> */ out = out[type_index]
size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex());
- GenerateGcRootFieldLoad(cls, out_loc, out, offset);
+ GenerateGcRootFieldLoad(cls, out_loc, out, offset, kEmitCompilerReadBarrier);
generate_null_check = !cls->IsInDexCache();
break;
}
@@ -3357,6 +4494,292 @@
}
}
+void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
+ locations->SetInAt(0, Location::RequiresRegister());
+ if (check->HasUses()) {
+ locations->SetOut(Location::SameAsFirstInput());
+ }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
+ // We assume the class is not null.
+ LoadClassSlowPathARMVIXL* slow_path =
+ new (GetGraph()->GetArena()) LoadClassSlowPathARMVIXL(check->GetLoadClass(),
+ check,
+ check->GetDexPc(),
+ /* do_clinit */ true);
+ codegen_->AddSlowPath(slow_path);
+ GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
+}
+
+void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
+ LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
+ GetAssembler()->LoadFromOffset(kLoadWord,
+ temp,
+ class_reg,
+ mirror::Class::StatusOffset().Int32Value());
+ __ Cmp(temp, mirror::Class::kStatusInitialized);
+ __ B(lt, slow_path->GetEntryLabel());
+ // Even if the initialized flag is set, we may be in a situation where caches are not synced
+ // properly. Therefore, we do a memory fence.
+ __ Dmb(ISH);
+ __ Bind(slow_path->GetExitLabel());
+}
+
+// Check if the desired_string_load_kind is supported. If it is, return it,
+// otherwise return a fall-back kind that should be used instead.
+HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
+ // TODO(VIXL): Implement optimized code paths. For now we always use the simpler fallback code.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+}
+
+void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
+ LocationSummary::CallKind call_kind = load->NeedsEnvironment()
+ ? LocationSummary::kCallOnMainOnly
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
+
+ // TODO(VIXL): Implement optimized code paths.
+ // See InstructionCodeGeneratorARMVIXL::VisitLoadString.
+ HLoadString::LoadKind load_kind = load->GetLoadKind();
+ if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ // TODO(VIXL): Use InvokeRuntimeCallingConventionARMVIXL instead.
+ locations->SetOut(LocationFrom(r0));
+ } else {
+ locations->SetOut(Location::RequiresRegister());
+ }
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) {
+ // TODO(VIXL): Implement optimized code paths.
+ // We implemented the simplest solution to get first ART tests passing, we deferred the
+ // optimized path until later, we should implement it using ARM64 implementation as a
+ // reference. The same related to LocationsBuilderARMVIXL::VisitLoadString.
+
+ // TODO: Re-add the compiler code to do string dex cache lookup again.
+ DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
+ InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex());
+ codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
+ CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
+}
+
+static int32_t GetExceptionTlsOffset() {
+ return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
+}
+
+void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
+ vixl32::Register out = OutputRegister(load);
+ GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
+}
+
+
+void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
+ new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
+ __ Mov(temp, 0);
+ GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
+}
+
+void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
+ InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
+ codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
+}
+
+void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
+ HandleBitwiseOperation(instruction, AND);
+}
+
+void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
+ HandleBitwiseOperation(instruction, ORR);
+}
+
+void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
+ HandleBitwiseOperation(instruction, EOR);
+}
+
+void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ DCHECK(instruction->GetResultType() == Primitive::kPrimInt
+ || instruction->GetResultType() == Primitive::kPrimLong);
+ // Note: GVN reorders commutative operations to have the constant on the right hand side.
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
+ HandleBitwiseOperation(instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
+ HandleBitwiseOperation(instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
+ HandleBitwiseOperation(instruction);
+}
+
+// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
+void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
+ vixl32::Register first,
+ uint32_t value) {
+ // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
+ if (value == 0xffffffffu) {
+ if (!out.Is(first)) {
+ __ Mov(out, first);
+ }
+ return;
+ }
+ if (value == 0u) {
+ __ Mov(out, 0);
+ return;
+ }
+ if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
+ __ And(out, first, value);
+ } else {
+ DCHECK(GetAssembler()->ShifterOperandCanHold(BIC, ~value));
+ __ Bic(out, first, ~value);
+ }
+}
+
+// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
+void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
+ vixl32::Register first,
+ uint32_t value) {
+ // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
+ if (value == 0u) {
+ if (!out.Is(first)) {
+ __ Mov(out, first);
+ }
+ return;
+ }
+ if (value == 0xffffffffu) {
+ __ Mvn(out, 0);
+ return;
+ }
+ if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
+ __ Orr(out, first, value);
+ } else {
+ DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
+ __ Orn(out, first, ~value);
+ }
+}
+
+// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
+void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
+ vixl32::Register first,
+ uint32_t value) {
+ // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
+ if (value == 0u) {
+ if (!out.Is(first)) {
+ __ Mov(out, first);
+ }
+ return;
+ }
+ __ Eor(out, first, value);
+}
+
+void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Location first = locations->InAt(0);
+ Location second = locations->InAt(1);
+ Location out = locations->Out();
+
+ if (second.IsConstant()) {
+ uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
+ uint32_t value_low = Low32Bits(value);
+ if (instruction->GetResultType() == Primitive::kPrimInt) {
+ vixl32::Register first_reg = InputRegisterAt(instruction, 0);
+ vixl32::Register out_reg = OutputRegister(instruction);
+ if (instruction->IsAnd()) {
+ GenerateAndConst(out_reg, first_reg, value_low);
+ } else if (instruction->IsOr()) {
+ GenerateOrrConst(out_reg, first_reg, value_low);
+ } else {
+ DCHECK(instruction->IsXor());
+ GenerateEorConst(out_reg, first_reg, value_low);
+ }
+ } else {
+ DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
+ uint32_t value_high = High32Bits(value);
+ vixl32::Register first_low = LowRegisterFrom(first);
+ vixl32::Register first_high = HighRegisterFrom(first);
+ vixl32::Register out_low = LowRegisterFrom(out);
+ vixl32::Register out_high = HighRegisterFrom(out);
+ if (instruction->IsAnd()) {
+ GenerateAndConst(out_low, first_low, value_low);
+ GenerateAndConst(out_high, first_high, value_high);
+ } else if (instruction->IsOr()) {
+ GenerateOrrConst(out_low, first_low, value_low);
+ GenerateOrrConst(out_high, first_high, value_high);
+ } else {
+ DCHECK(instruction->IsXor());
+ GenerateEorConst(out_low, first_low, value_low);
+ GenerateEorConst(out_high, first_high, value_high);
+ }
+ }
+ return;
+ }
+
+ if (instruction->GetResultType() == Primitive::kPrimInt) {
+ vixl32::Register first_reg = InputRegisterAt(instruction, 0);
+ vixl32::Register second_reg = InputRegisterAt(instruction, 1);
+ vixl32::Register out_reg = OutputRegister(instruction);
+ if (instruction->IsAnd()) {
+ __ And(out_reg, first_reg, second_reg);
+ } else if (instruction->IsOr()) {
+ __ Orr(out_reg, first_reg, second_reg);
+ } else {
+ DCHECK(instruction->IsXor());
+ __ Eor(out_reg, first_reg, second_reg);
+ }
+ } else {
+ DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
+ vixl32::Register first_low = LowRegisterFrom(first);
+ vixl32::Register first_high = HighRegisterFrom(first);
+ vixl32::Register second_low = LowRegisterFrom(second);
+ vixl32::Register second_high = HighRegisterFrom(second);
+ vixl32::Register out_low = LowRegisterFrom(out);
+ vixl32::Register out_high = HighRegisterFrom(out);
+ if (instruction->IsAnd()) {
+ __ And(out_low, first_low, second_low);
+ __ And(out_high, first_high, second_high);
+ } else if (instruction->IsOr()) {
+ __ Orr(out_low, first_low, second_low);
+ __ Orr(out_high, first_high, second_high);
+ } else {
+ DCHECK(instruction->IsXor());
+ __ Eor(out_low, first_low, second_low);
+ __ Eor(out_high, first_high, second_high);
+ }
+ }
+}
+
void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
HInstruction* instruction ATTRIBUTE_UNUSED,
Location root,
@@ -3375,6 +4798,34 @@
}
}
+void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction ATTRIBUTE_UNUSED,
+ Location out,
+ Location ref ATTRIBUTE_UNUSED,
+ Location obj ATTRIBUTE_UNUSED,
+ uint32_t offset ATTRIBUTE_UNUSED,
+ Location index ATTRIBUTE_UNUSED) {
+ if (kEmitCompilerReadBarrier) {
+ DCHECK(!kUseBakerReadBarrier);
+ TODO_VIXL32(FATAL);
+ } else if (kPoisonHeapReferences) {
+ GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
+ }
+}
+
+// Check if the desired_dispatch_info is supported. If it is, return it,
+// otherwise return a fall-back info that should be used instead.
+HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info ATTRIBUTE_UNUSED,
+ HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
+ // TODO(VIXL): Implement optimized code paths.
+ return {
+ HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ 0u,
+ 0u
+ };
+}
+
vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
HInvokeStaticOrDirect* invoke, vixl32::Register temp) {
DCHECK_EQ(invoke->InputCount(), invoke->GetNumberOfArguments() + 1u);
@@ -3490,56 +4941,10 @@
__ Blx(lr);
}
-static int32_t GetExceptionTlsOffset() {
- return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
-}
-
-void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
- locations->SetOut(Location::RequiresRegister());
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
- vixl32::Register out = OutputRegister(load);
- GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
-}
-
-void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
- new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
- UseScratchRegisterScope temps(GetVIXLAssembler());
- vixl32::Register temp = temps.Acquire();
- __ Mov(temp, 0);
- GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
-}
-
-void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
- InvokeRuntimeCallingConventionARMVIXL calling_convention;
- locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
- codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
- CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
-}
-
-void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction ATTRIBUTE_UNUSED,
- Location out,
- Location ref ATTRIBUTE_UNUSED,
- Location obj ATTRIBUTE_UNUSED,
- uint32_t offset ATTRIBUTE_UNUSED,
- Location index ATTRIBUTE_UNUSED) {
- if (kEmitCompilerReadBarrier) {
- DCHECK(!kUseBakerReadBarrier);
- TODO_VIXL32(FATAL);
- } else if (kPoisonHeapReferences) {
- GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
- }
+// Copy the result of a call into the given target.
+void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg ATTRIBUTE_UNUSED,
+ Primitive::Type type ATTRIBUTE_UNUSED) {
+ TODO_VIXL32(FATAL);
}
#undef __
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 7815db2..df7d467 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -105,17 +105,25 @@
M(Above) \
M(AboveOrEqual) \
M(Add) \
+ M(And) \
+ M(ArrayGet) \
M(ArrayLength) \
+ M(ArraySet) \
M(Below) \
M(BelowOrEqual) \
+ M(BooleanNot) \
+ M(BoundsCheck) \
M(ClearException) \
M(ClinitCheck) \
M(Compare) \
M(CurrentMethod) \
+ M(Deoptimize) \
M(Div) \
M(DivZeroCheck) \
+ M(DoubleConstant) \
M(Equal) \
M(Exit) \
+ M(FloatConstant) \
M(Goto) \
M(GreaterThan) \
M(GreaterThanOrEqual) \
@@ -133,57 +141,49 @@
M(LongConstant) \
M(MemoryBarrier) \
M(Mul) \
+ M(Neg) \
M(NewArray) \
M(NewInstance) \
M(Not) \
M(NotEqual) \
M(NullCheck) \
M(NullConstant) \
+ M(Or) \
M(ParallelMove) \
M(ParameterValue) \
M(Phi) \
M(Return) \
M(ReturnVoid) \
+ M(Ror) \
M(Select) \
+ M(Shl) \
+ M(Shr) \
M(StaticFieldGet) \
+ M(StaticFieldSet) \
M(Sub) \
M(SuspendCheck) \
M(Throw) \
M(TryBoundary) \
M(TypeConversion) \
+ M(UShr) \
+ M(Xor) \
// TODO: Remove once the VIXL32 backend is implemented completely.
#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \
- M(And) \
- M(ArrayGet) \
- M(ArraySet) \
- M(BooleanNot) \
- M(BoundsCheck) \
M(BoundType) \
M(CheckCast) \
M(ClassTableGet) \
- M(Deoptimize) \
- M(DoubleConstant) \
- M(FloatConstant) \
M(InstanceOf) \
M(InvokeInterface) \
M(InvokeUnresolved) \
M(MonitorOperation) \
M(NativeDebugInfo) \
- M(Neg) \
- M(Or) \
M(PackedSwitch) \
M(Rem) \
- M(Ror) \
- M(Shl) \
- M(Shr) \
- M(StaticFieldSet) \
M(UnresolvedInstanceFieldGet) \
M(UnresolvedInstanceFieldSet) \
M(UnresolvedStaticFieldGet) \
M(UnresolvedStaticFieldSet) \
- M(UShr) \
- M(Xor) \
class CodeGeneratorARMVIXL;
@@ -276,11 +276,18 @@
}
void HandleInvoke(HInvoke* invoke);
+ void HandleBitwiseOperation(HBinaryOperation* operation, Opcode opcode);
void HandleCondition(HCondition* condition);
+ void HandleIntegerRotate(LocationSummary* locations);
+ void HandleLongRotate(LocationSummary* locations);
+ void HandleShift(HBinaryOperation* operation);
void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
Location ArithmeticZeroOrFpuRegister(HInstruction* input);
+ Location ArmEncodableConstantOrRegister(HInstruction* constant, Opcode opcode);
+ bool CanEncodeConstantAsImmediate(HConstant* input_cst, Opcode opcode);
+ bool CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode, SetCc set_cc = kCcDontCare);
CodeGeneratorARMVIXL* const codegen_;
InvokeDexCallingConventionVisitorARM parameter_visitor_;
@@ -311,7 +318,14 @@
void GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL* slow_path,
vixl32::Register class_reg);
void HandleGoto(HInstruction* got, HBasicBlock* successor);
+ void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
+ void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
+ void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
+ void HandleBitwiseOperation(HBinaryOperation* operation);
void HandleCondition(HCondition* condition);
+ void HandleIntegerRotate(HRor* ror);
+ void HandleLongRotate(HRor* ror);
+ void HandleShift(HBinaryOperation* operation);
void GenerateWideAtomicStore(vixl::aarch32::Register addr,
uint32_t offset,
@@ -339,7 +353,7 @@
Location root,
vixl::aarch32::Register obj,
uint32_t offset,
- bool requires_read_barrier = kEmitCompilerReadBarrier);
+ bool requires_read_barrier);
void GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
vixl::aarch32::Label* true_target,
@@ -424,6 +438,17 @@
// Helper method to move a 32-bit value between two locations.
void Move32(Location destination, Location source);
+ void LoadFromShiftedRegOffset(Primitive::Type type,
+ Location out_loc,
+ vixl::aarch32::Register base,
+ vixl::aarch32::Register reg_index,
+ vixl::aarch32::Condition cond = vixl::aarch32::al);
+ void StoreToShiftedRegOffset(Primitive::Type type,
+ Location out_loc,
+ vixl::aarch32::Register base,
+ vixl::aarch32::Register reg_index,
+ vixl::aarch32::Condition cond = vixl::aarch32::al);
+
const ArmInstructionSetFeatures& GetInstructionSetFeatures() const { return isa_features_; }
vixl::aarch32::Label* GetFrameEntryLabel() { return &frame_entry_label_; }
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 02c1c3b..efd33c7 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -426,11 +426,25 @@
DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathX86);
};
-// Slow path marking an object during a read barrier.
+// 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 (see
+// ReadBarrierMarkAndUpdateFieldSlowPathX86 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`).
class ReadBarrierMarkSlowPathX86 : public SlowPathCode {
public:
- ReadBarrierMarkSlowPathX86(HInstruction* instruction, Location obj, bool unpoison)
- : SlowPathCode(instruction), obj_(obj), unpoison_(unpoison) {
+ ReadBarrierMarkSlowPathX86(HInstruction* instruction,
+ Location ref,
+ bool unpoison_ref_before_marking)
+ : SlowPathCode(instruction),
+ ref_(ref),
+ unpoison_ref_before_marking_(unpoison_ref_before_marking) {
DCHECK(kEmitCompilerReadBarrier);
}
@@ -438,9 +452,9 @@
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
- Register reg = obj_.AsRegister<Register>();
+ Register ref_reg = ref_.AsRegister<Register>();
DCHECK(locations->CanCall());
- DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg));
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
DCHECK(instruction_->IsInstanceFieldGet() ||
instruction_->IsStaticFieldGet() ||
instruction_->IsArrayGet() ||
@@ -455,44 +469,211 @@
<< instruction_->DebugName();
__ Bind(GetEntryLabel());
- if (unpoison_) {
+ if (unpoison_ref_before_marking_) {
// Object* ref = ref_addr->AsMirrorPtr()
- __ MaybeUnpoisonHeapReference(reg);
+ __ MaybeUnpoisonHeapReference(ref_reg);
}
// 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.
CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
- DCHECK_NE(reg, ESP);
- DCHECK(0 <= reg && reg < kNumberOfCpuRegisters) << reg;
+ DCHECK_NE(ref_reg, ESP);
+ DCHECK(0 <= ref_reg && ref_reg < kNumberOfCpuRegisters) << ref_reg;
// "Compact" slow path, saving two moves.
//
// Instead of using the standard runtime calling convention (input
// and output in EAX):
//
- // EAX <- obj
+ // EAX <- ref
// EAX <- ReadBarrierMark(EAX)
- // obj <- EAX
+ // ref <- EAX
//
- // we just use rX (the register holding `obj`) as input and output
+ // we just use rX (the register containing `ref`) as input and output
// of a dedicated entrypoint:
//
// rX <- ReadBarrierMarkRegX(rX)
//
int32_t entry_point_offset =
- CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86PointerSize>(reg);
+ CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86PointerSize>(ref_reg);
// This runtime call does not require a stack map.
x86_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
__ jmp(GetExitLabel());
}
private:
- const Location obj_;
- const bool unpoison_;
+ // The location (register) of the marked object reference.
+ const Location ref_;
+ // Should the reference in `ref_` be unpoisoned prior to marking it?
+ const bool unpoison_ref_before_marking_;
DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathX86);
};
+// Slow path marking an object reference `ref` during a read barrier,
+// and if needed, atomically updating the field `obj.field` in the
+// object `obj` holding this reference after marking (contrary to
+// ReadBarrierMarkSlowPathX86 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`).
+class ReadBarrierMarkAndUpdateFieldSlowPathX86 : public SlowPathCode {
+ public:
+ ReadBarrierMarkAndUpdateFieldSlowPathX86(HInstruction* instruction,
+ Location ref,
+ Register obj,
+ const Address& field_addr,
+ bool unpoison_ref_before_marking,
+ Register temp)
+ : SlowPathCode(instruction),
+ ref_(ref),
+ obj_(obj),
+ field_addr_(field_addr),
+ unpoison_ref_before_marking_(unpoison_ref_before_marking),
+ temp_(temp) {
+ DCHECK(kEmitCompilerReadBarrier);
+ }
+
+ const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkAndUpdateFieldSlowPathX86"; }
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ Register ref_reg = ref_.AsRegister<Register>();
+ DCHECK(locations->CanCall());
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
+ // This slow path is only used by the UnsafeCASObject intrinsic.
+ 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);
+
+ __ Bind(GetEntryLabel());
+ if (unpoison_ref_before_marking_) {
+ // Object* ref = ref_addr->AsMirrorPtr()
+ __ MaybeUnpoisonHeapReference(ref_reg);
+ }
+
+ // Save the old (unpoisoned) reference.
+ __ movl(temp_, ref_reg);
+
+ // 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.
+ CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
+ DCHECK_NE(ref_reg, ESP);
+ DCHECK(0 <= ref_reg && ref_reg < kNumberOfCpuRegisters) << ref_reg;
+ // "Compact" slow path, saving two moves.
+ //
+ // Instead of using the standard runtime calling convention (input
+ // and output in EAX):
+ //
+ // EAX <- ref
+ // EAX <- ReadBarrierMark(EAX)
+ // ref <- EAX
+ //
+ // we just use rX (the register containing `ref`) as input and output
+ // of a dedicated entrypoint:
+ //
+ // rX <- ReadBarrierMarkRegX(rX)
+ //
+ int32_t entry_point_offset =
+ CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86PointerSize>(ref_reg);
+ // This runtime call does not require a stack map.
+ x86_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
+
+ // If the new reference is different from the old reference,
+ // update the field in the holder (`*field_addr`).
+ //
+ // Note that this field could also hold a different object, if
+ // another thread had concurrently changed it. In that case, the
+ // LOCK CMPXCHGL instruction in the compare-and-set (CAS)
+ // operation below would abort the CAS, leaving the field as-is.
+ NearLabel done;
+ __ cmpl(temp_, ref_reg);
+ __ j(kEqual, &done);
+
+ // 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.
+ // This operation is implemented with a 32-bit LOCK CMPXLCHG
+ // instruction, which requires the expected value (the old
+ // reference) to be in EAX. Save EAX beforehand, and move the
+ // expected value (stored in `temp_`) into EAX.
+ __ pushl(EAX);
+ __ movl(EAX, temp_);
+
+ // Convenience aliases.
+ Register base = obj_;
+ Register expected = EAX;
+ Register value = ref_reg;
+
+ bool base_equals_value = (base == value);
+ if (kPoisonHeapReferences) {
+ if (base_equals_value) {
+ // If `base` and `value` are the same register location, move
+ // `value` to a temporary register. This way, poisoning
+ // `value` won't invalidate `base`.
+ value = temp_;
+ __ movl(value, base);
+ }
+
+ // Check that the register allocator did not assign the location
+ // of `expected` (EAX) to `value` nor to `base`, so that heap
+ // poisoning (when enabled) works as intended below.
+ // - If `value` were equal to `expected`, both references would
+ // be poisoned twice, meaning they would not be poisoned at
+ // all, as heap poisoning uses address negation.
+ // - If `base` were equal to `expected`, poisoning `expected`
+ // would invalidate `base`.
+ DCHECK_NE(value, expected);
+ DCHECK_NE(base, expected);
+
+ __ PoisonHeapReference(expected);
+ __ PoisonHeapReference(value);
+ }
+
+ __ LockCmpxchgl(field_addr_, value);
+
+ // If heap poisoning is enabled, we need to unpoison the values
+ // that were poisoned earlier.
+ if (kPoisonHeapReferences) {
+ if (base_equals_value) {
+ // `value` has been moved to a temporary register, no need
+ // to unpoison it.
+ } else {
+ __ UnpoisonHeapReference(value);
+ }
+ // No need to unpoison `expected` (EAX), as it is be overwritten below.
+ }
+
+ // Restore EAX.
+ __ popl(EAX);
+
+ __ Bind(&done);
+ __ jmp(GetExitLabel());
+ }
+
+ private:
+ // The location (register) of the marked object reference.
+ const Location ref_;
+ // The register containing the object holding the marked object reference field.
+ const Register obj_;
+ // The address of the marked reference field. The base of this address must be `obj_`.
+ const Address field_addr_;
+
+ // Should the reference in `ref_` be unpoisoned prior to marking it?
+ const bool unpoison_ref_before_marking_;
+
+ const Register temp_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkAndUpdateFieldSlowPathX86);
+};
+
// Slow path generating a read barrier for a heap reference.
class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode {
public:
@@ -5897,7 +6078,7 @@
cls,
out_loc,
Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()),
- /*fixup_label*/ nullptr,
+ /* fixup_label */ nullptr,
requires_read_barrier);
break;
}
@@ -5929,7 +6110,7 @@
GenerateGcRootFieldLoad(cls,
out_loc,
Address::Absolute(address),
- /*fixup_label*/ nullptr,
+ /* fixup_label */ nullptr,
requires_read_barrier);
generate_null_check = !cls->IsInDexCache();
break;
@@ -5957,7 +6138,7 @@
GenerateGcRootFieldLoad(cls,
out_loc,
Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())),
- /*fixup_label*/ nullptr,
+ /* fixup_label */ nullptr,
requires_read_barrier);
generate_null_check = !cls->IsInDexCache();
break;
@@ -6099,7 +6280,7 @@
Address address = Address(method_address, CodeGeneratorX86::kDummy32BitOffset);
Label* fixup_label = codegen_->NewStringBssEntryPatch(load);
// /* GcRoot<mirror::Class> */ out = *address /* PC-relative */
- GenerateGcRootFieldLoad(load, out_loc, address, fixup_label);
+ GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kEmitCompilerReadBarrier);
SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load);
codegen_->AddSlowPath(slow_path);
__ testl(out, out);
@@ -6831,7 +7012,7 @@
// Slow path marking the GC root `root`.
SlowPathCode* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86(
- instruction, root, /* unpoison */ false);
+ instruction, root, /* unpoison_ref_before_marking */ false);
codegen_->AddSlowPath(slow_path);
__ fs()->cmpl(Address::Absolute(Thread::IsGcMarkingOffset<kX86PointerSize>().Int32Value()),
@@ -6896,7 +7077,9 @@
Location ref,
Register obj,
const Address& src,
- bool needs_null_check) {
+ bool needs_null_check,
+ bool always_update_field,
+ Register* temp) {
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
@@ -6953,8 +7136,15 @@
// Note: Reference unpoisoning modifies the flags, so we need to delay it after the branch.
// Slow path marking the object `ref` when it is gray.
- SlowPathCode* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86(
- instruction, ref, /* unpoison */ true);
+ SlowPathCode* slow_path;
+ if (always_update_field) {
+ DCHECK(temp != nullptr);
+ slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkAndUpdateFieldSlowPathX86(
+ instruction, ref, obj, src, /* unpoison_ref_before_marking */ true, *temp);
+ } else {
+ slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86(
+ instruction, ref, /* unpoison_ref_before_marking */ true);
+ }
AddSlowPath(slow_path);
// We have done the "if" of the gray bit check above, now branch based on the flags.
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index e7d9a43..1b51999 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -259,12 +259,12 @@
//
// root <- *address
//
- // while honoring read barriers (if any).
+ // while honoring read barriers if `requires_read_barrier` is true.
void GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
const Address& address,
- Label* fixup_label = nullptr,
- bool requires_read_barrier = kEmitCompilerReadBarrier);
+ Label* fixup_label,
+ bool requires_read_barrier);
// Push value to FPU stack. `is_fp` specifies whether the value is floating point or not.
// `is_wide` specifies whether it is long/double or not.
@@ -499,13 +499,24 @@
uint32_t data_offset,
Location index,
bool needs_null_check);
- // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
- // and GenerateArrayLoadWithBakerReadBarrier.
+ // Factored implementation, used by GenerateFieldLoadWithBakerReadBarrier,
+ // GenerateArrayLoadWithBakerReadBarrier and some intrinsics.
+ //
+ // Load the object reference located at address `src`, held by
+ // object `obj`, into `ref`, and mark it if needed. The base of
+ // address `src` must be `obj`.
+ //
+ // If `always_update_field` is true, the value of the reference is
+ // atomically updated in the holder (`obj`). This operation
+ // requires a temporary register, which must be provided as a
+ // non-null pointer (`temp`).
void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
Location ref,
Register obj,
const Address& src,
- bool needs_null_check);
+ bool needs_null_check,
+ bool always_update_field = false,
+ Register* temp = nullptr);
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 4b64c1b..fcabeea 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -445,11 +445,25 @@
DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathX86_64);
};
-// Slow path marking an object during a read barrier.
+// 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 (see
+// ReadBarrierMarkAndUpdateFieldSlowPathX86_64 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`).
class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode {
public:
- ReadBarrierMarkSlowPathX86_64(HInstruction* instruction, Location obj, bool unpoison)
- : SlowPathCode(instruction), obj_(obj), unpoison_(unpoison) {
+ ReadBarrierMarkSlowPathX86_64(HInstruction* instruction,
+ Location ref,
+ bool unpoison_ref_before_marking)
+ : SlowPathCode(instruction),
+ ref_(ref),
+ unpoison_ref_before_marking_(unpoison_ref_before_marking) {
DCHECK(kEmitCompilerReadBarrier);
}
@@ -457,10 +471,10 @@
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
- CpuRegister cpu_reg = obj_.AsRegister<CpuRegister>();
- Register reg = cpu_reg.AsRegister();
+ CpuRegister ref_cpu_reg = ref_.AsRegister<CpuRegister>();
+ Register ref_reg = ref_cpu_reg.AsRegister();
DCHECK(locations->CanCall());
- DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg));
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
DCHECK(instruction_->IsInstanceFieldGet() ||
instruction_->IsStaticFieldGet() ||
instruction_->IsArrayGet() ||
@@ -475,44 +489,218 @@
<< instruction_->DebugName();
__ Bind(GetEntryLabel());
- if (unpoison_) {
+ if (unpoison_ref_before_marking_) {
// Object* ref = ref_addr->AsMirrorPtr()
- __ MaybeUnpoisonHeapReference(cpu_reg);
+ __ MaybeUnpoisonHeapReference(ref_cpu_reg);
}
// 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.
CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
- DCHECK_NE(reg, RSP);
- DCHECK(0 <= reg && reg < kNumberOfCpuRegisters) << reg;
+ DCHECK_NE(ref_reg, RSP);
+ DCHECK(0 <= ref_reg && ref_reg < kNumberOfCpuRegisters) << ref_reg;
// "Compact" slow path, saving two moves.
//
// Instead of using the standard runtime calling convention (input
// and output in R0):
//
- // RDI <- obj
+ // RDI <- ref
// RAX <- ReadBarrierMark(RDI)
- // obj <- RAX
+ // ref <- RAX
//
- // we just use rX (the register holding `obj`) as input and output
+ // we just use rX (the register containing `ref`) as input and output
// of a dedicated entrypoint:
//
// rX <- ReadBarrierMarkRegX(rX)
//
int32_t entry_point_offset =
- CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(reg);
+ CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(ref_reg);
// This runtime call does not require a stack map.
x86_64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
__ jmp(GetExitLabel());
}
private:
- const Location obj_;
- const bool unpoison_;
+ // The location (register) of the marked object reference.
+ const Location ref_;
+ // Should the reference in `ref_` be unpoisoned prior to marking it?
+ const bool unpoison_ref_before_marking_;
DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathX86_64);
};
+// Slow path marking an object reference `ref` during a read barrier,
+// and if needed, atomically updating the field `obj.field` in the
+// object `obj` holding this reference after marking (contrary to
+// ReadBarrierMarkSlowPathX86_64 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`).
+class ReadBarrierMarkAndUpdateFieldSlowPathX86_64 : public SlowPathCode {
+ public:
+ ReadBarrierMarkAndUpdateFieldSlowPathX86_64(HInstruction* instruction,
+ Location ref,
+ CpuRegister obj,
+ const Address& field_addr,
+ bool unpoison_ref_before_marking,
+ CpuRegister temp1,
+ CpuRegister temp2)
+ : SlowPathCode(instruction),
+ ref_(ref),
+ obj_(obj),
+ field_addr_(field_addr),
+ unpoison_ref_before_marking_(unpoison_ref_before_marking),
+ temp1_(temp1),
+ temp2_(temp2) {
+ DCHECK(kEmitCompilerReadBarrier);
+ }
+
+ const char* GetDescription() const OVERRIDE {
+ return "ReadBarrierMarkAndUpdateFieldSlowPathX86_64";
+ }
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ CpuRegister ref_cpu_reg = ref_.AsRegister<CpuRegister>();
+ Register ref_reg = ref_cpu_reg.AsRegister();
+ DCHECK(locations->CanCall());
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg;
+ // This slow path is only used by the UnsafeCASObject intrinsic.
+ 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);
+
+ __ Bind(GetEntryLabel());
+ if (unpoison_ref_before_marking_) {
+ // Object* ref = ref_addr->AsMirrorPtr()
+ __ MaybeUnpoisonHeapReference(ref_cpu_reg);
+ }
+
+ // Save the old (unpoisoned) reference.
+ __ movl(temp1_, ref_cpu_reg);
+
+ // 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.
+ CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
+ DCHECK_NE(ref_reg, RSP);
+ DCHECK(0 <= ref_reg && ref_reg < kNumberOfCpuRegisters) << ref_reg;
+ // "Compact" slow path, saving two moves.
+ //
+ // Instead of using the standard runtime calling convention (input
+ // and output in R0):
+ //
+ // RDI <- ref
+ // RAX <- ReadBarrierMark(RDI)
+ // ref <- RAX
+ //
+ // we just use rX (the register containing `ref`) as input and output
+ // of a dedicated entrypoint:
+ //
+ // rX <- ReadBarrierMarkRegX(rX)
+ //
+ int32_t entry_point_offset =
+ CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(ref_reg);
+ // This runtime call does not require a stack map.
+ x86_64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
+
+ // If the new reference is different from the old reference,
+ // update the field in the holder (`*field_addr`).
+ //
+ // Note that this field could also hold a different object, if
+ // another thread had concurrently changed it. In that case, the
+ // LOCK CMPXCHGL instruction in the compare-and-set (CAS)
+ // operation below would abort the CAS, leaving the field as-is.
+ NearLabel done;
+ __ cmpl(temp1_, ref_cpu_reg);
+ __ j(kEqual, &done);
+
+ // Update the the holder's field atomically. This may fail if
+ // mutator updates before us, but it's OK. This is achived
+ // 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.
+ // This operation is implemented with a 32-bit LOCK CMPXLCHG
+ // instruction, which requires the expected value (the old
+ // reference) to be in EAX. Save RAX beforehand, and move the
+ // expected value (stored in `temp1_`) into EAX.
+ __ movq(temp2_, CpuRegister(RAX));
+ __ movl(CpuRegister(RAX), temp1_);
+
+ // Convenience aliases.
+ CpuRegister base = obj_;
+ CpuRegister expected = CpuRegister(RAX);
+ CpuRegister value = ref_cpu_reg;
+
+ bool base_equals_value = (base.AsRegister() == value.AsRegister());
+ Register value_reg = ref_reg;
+ if (kPoisonHeapReferences) {
+ if (base_equals_value) {
+ // If `base` and `value` are the same register location, move
+ // `value_reg` to a temporary register. This way, poisoning
+ // `value_reg` won't invalidate `base`.
+ value_reg = temp1_.AsRegister();
+ __ movl(CpuRegister(value_reg), base);
+ }
+
+ // Check that the register allocator did not assign the location
+ // of `expected` (RAX) to `value` nor to `base`, so that heap
+ // poisoning (when enabled) works as intended below.
+ // - If `value` were equal to `expected`, both references would
+ // be poisoned twice, meaning they would not be poisoned at
+ // all, as heap poisoning uses address negation.
+ // - If `base` were equal to `expected`, poisoning `expected`
+ // would invalidate `base`.
+ DCHECK_NE(value_reg, expected.AsRegister());
+ DCHECK_NE(base.AsRegister(), expected.AsRegister());
+
+ __ PoisonHeapReference(expected);
+ __ PoisonHeapReference(CpuRegister(value_reg));
+ }
+
+ __ LockCmpxchgl(field_addr_, CpuRegister(value_reg));
+
+ // If heap poisoning is enabled, we need to unpoison the values
+ // that were poisoned earlier.
+ if (kPoisonHeapReferences) {
+ if (base_equals_value) {
+ // `value_reg` has been moved to a temporary register, no need
+ // to unpoison it.
+ } else {
+ __ UnpoisonHeapReference(CpuRegister(value_reg));
+ }
+ // No need to unpoison `expected` (RAX), as it is be overwritten below.
+ }
+
+ // Restore RAX.
+ __ movq(CpuRegister(RAX), temp2_);
+
+ __ Bind(&done);
+ __ jmp(GetExitLabel());
+ }
+
+ private:
+ // The location (register) of the marked object reference.
+ const Location ref_;
+ // The register containing the object holding the marked object reference field.
+ const CpuRegister obj_;
+ // The address of the marked reference field. The base of this address must be `obj_`.
+ const Address field_addr_;
+
+ // Should the reference in `ref_` be unpoisoned prior to marking it?
+ const bool unpoison_ref_before_marking_;
+
+ const CpuRegister temp1_;
+ const CpuRegister temp2_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkAndUpdateFieldSlowPathX86_64);
+};
+
// Slow path generating a read barrier for a heap reference.
class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode {
public:
@@ -4122,7 +4310,7 @@
// /* HeapReference<Object> */ out = *(base + offset)
if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
// Note that a potential implicit null check is handled in this
- // CodeGeneratorX86::GenerateFieldLoadWithBakerReadBarrier call.
+ // CodeGeneratorX86_64::GenerateFieldLoadWithBakerReadBarrier call.
codegen_->GenerateFieldLoadWithBakerReadBarrier(
instruction, out, base, offset, /* needs_null_check */ true);
if (is_volatile) {
@@ -4569,7 +4757,7 @@
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
// Note that a potential implicit null check is handled in this
- // CodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier call.
+ // CodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier call.
codegen_->GenerateArrayLoadWithBakerReadBarrier(
instruction, out_loc, obj, data_offset, index, /* needs_null_check */ true);
} else {
@@ -5318,7 +5506,7 @@
cls,
out_loc,
Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()),
- /*fixup_label*/nullptr,
+ /* fixup_label */ nullptr,
requires_read_barrier);
break;
}
@@ -5343,7 +5531,7 @@
GenerateGcRootFieldLoad(cls,
out_loc,
address,
- /*fixup_label*/nullptr,
+ /* fixup_label */ nullptr,
requires_read_barrier);
} else {
// TODO: Consider using opcode A1, i.e. movl eax, moff32 (with 64-bit address).
@@ -5351,7 +5539,7 @@
GenerateGcRootFieldLoad(cls,
out_loc,
Address(out, 0),
- /*fixup_label*/nullptr,
+ /* fixup_label */ nullptr,
requires_read_barrier);
}
generate_null_check = !cls->IsInDexCache();
@@ -5379,7 +5567,7 @@
cls,
out_loc,
Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())),
- /*fixup_label*/nullptr,
+ /* fixup_label */ nullptr,
requires_read_barrier);
generate_null_check = !cls->IsInDexCache();
break;
@@ -5496,7 +5684,7 @@
/* no_rip */ false);
Label* fixup_label = codegen_->NewStringBssEntryPatch(load);
// /* GcRoot<mirror::Class> */ out = *address /* PC-relative */
- GenerateGcRootFieldLoad(load, out_loc, address, fixup_label);
+ GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kEmitCompilerReadBarrier);
SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86_64(load);
codegen_->AddSlowPath(slow_path);
__ testl(out, out);
@@ -6264,7 +6452,7 @@
// Slow path marking the GC root `root`.
SlowPathCode* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86_64(
- instruction, root, /* unpoison */ false);
+ instruction, root, /* unpoison_ref_before_marking */ false);
codegen_->AddSlowPath(slow_path);
__ gs()->cmpl(Address::Absolute(Thread::IsGcMarkingOffset<kX86_64PointerSize>().Int32Value(),
@@ -6330,7 +6518,10 @@
Location ref,
CpuRegister obj,
const Address& src,
- bool needs_null_check) {
+ bool needs_null_check,
+ bool always_update_field,
+ CpuRegister* temp1,
+ CpuRegister* temp2) {
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
@@ -6387,8 +6578,16 @@
// Note: Reference unpoisoning modifies the flags, so we need to delay it after the branch.
// Slow path marking the object `ref` when it is gray.
- SlowPathCode* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86_64(
- instruction, ref, /* unpoison */ true);
+ SlowPathCode* slow_path;
+ if (always_update_field) {
+ DCHECK(temp1 != nullptr);
+ DCHECK(temp2 != nullptr);
+ slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkAndUpdateFieldSlowPathX86_64(
+ instruction, ref, obj, src, /* unpoison_ref_before_marking */ true, *temp1, *temp2);
+ } else {
+ slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86_64(
+ instruction, ref, /* unpoison_ref_before_marking */ true);
+ }
AddSlowPath(slow_path);
// We have done the "if" of the gray bit check above, now branch based on the flags.
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 57ef83f..8b19dad 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -253,12 +253,12 @@
//
// root <- *address
//
- // while honoring read barriers (if any).
+ // while honoring read barriers if `requires_read_barrier` is true.
void GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
const Address& address,
- Label* fixup_label = nullptr,
- bool requires_read_barrier = kEmitCompilerReadBarrier);
+ Label* fixup_label,
+ bool requires_read_barrier);
void PushOntoFPStack(Location source, uint32_t temp_offset,
uint32_t stack_adjustment, bool is_float);
@@ -434,13 +434,25 @@
uint32_t data_offset,
Location index,
bool needs_null_check);
- // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
- // and GenerateArrayLoadWithBakerReadBarrier.
+ // Factored implementation, used by GenerateFieldLoadWithBakerReadBarrier,
+ // GenerateArrayLoadWithBakerReadBarrier and some intrinsics.
+ //
+ // Load the object reference located at address `src`, held by
+ // object `obj`, into `ref`, and mark it if needed. The base of
+ // address `src` must be `obj`.
+ //
+ // If `always_update_field` is true, the value of the reference is
+ // atomically updated in the holder (`obj`). This operation
+ // requires two temporary registers, which must be provided as
+ // non-null pointers (`temp1` and `temp2`).
void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
Location ref,
CpuRegister obj,
const Address& src,
- bool needs_null_check);
+ bool needs_null_check,
+ bool always_update_field = false,
+ CpuRegister* temp1 = nullptr,
+ CpuRegister* temp2 = nullptr);
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index 5d92bfd..8c08a9c 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -37,11 +37,6 @@
return dwarf::Reg::ArmFp(static_cast<int>(reg.GetCode()));
}
-inline vixl::aarch32::DRegister FromLowSToD(vixl::aarch32::SRegister reg) {
- DCHECK_EQ(reg.GetCode() % 2, 0u) << reg;
- return vixl::aarch32::DRegister(reg.GetCode() / 2);
-}
-
inline vixl::aarch32::Register HighRegisterFrom(Location location) {
DCHECK(location.IsRegisterPair()) << location;
return vixl::aarch32::Register(location.AsRegisterPairHigh<vixl32::Register>());
@@ -135,6 +130,11 @@
instr->InputAt(input_index)->GetType());
}
+inline vixl::aarch32::Register InputRegister(HInstruction* instr) {
+ DCHECK_EQ(instr->InputCount(), 1u);
+ return InputRegisterAt(instr, 0);
+}
+
inline int64_t Int64ConstantFrom(Location location) {
HConstant* instr = location.GetConstant();
if (instr->IsIntConstant()) {
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index adfe09b..9de521a 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -18,6 +18,7 @@
#include "base/array_ref.h"
#include "base/bit_vector-inl.h"
+#include "base/stl_util.h"
#include "ssa_phi_elimination.h"
namespace art {
@@ -168,8 +169,7 @@
bool simplified_one_or_more_ifs = false;
bool rerun_dominance_and_loop_analysis = false;
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
HInstruction* last = block->GetLastInstruction();
HInstruction* first = block->GetFirstInstruction();
if (last->IsIf() &&
@@ -271,20 +271,22 @@
}
void HDeadCodeElimination::ConnectSuccessiveBlocks() {
- // Order does not matter.
- for (HReversePostOrderIterator it(*graph_); !it.Done();) {
- HBasicBlock* block = it.Current();
- if (block->IsEntryBlock() || !block->GetLastInstruction()->IsGoto()) {
- it.Advance();
- continue;
+ // Order does not matter. Skip the entry block by starting at index 1 in reverse post order.
+ for (size_t i = 1u, size = graph_->GetReversePostOrder().size(); i != size; ++i) {
+ HBasicBlock* block = graph_->GetReversePostOrder()[i];
+ DCHECK(!block->IsEntryBlock());
+ while (block->GetLastInstruction()->IsGoto()) {
+ HBasicBlock* successor = block->GetSingleSuccessor();
+ if (successor->IsExitBlock() || successor->GetPredecessors().size() != 1u) {
+ break;
+ }
+ DCHECK_LT(i, IndexOfElement(graph_->GetReversePostOrder(), successor));
+ block->MergeWith(successor);
+ --size;
+ DCHECK_EQ(size, graph_->GetReversePostOrder().size());
+ DCHECK_EQ(block, graph_->GetReversePostOrder()[i]);
+ // Reiterate on this block in case it can be merged with its new successor.
}
- HBasicBlock* successor = block->GetSingleSuccessor();
- if (successor->IsExitBlock() || successor->GetPredecessors().size() != 1u) {
- it.Advance();
- continue;
- }
- block->MergeWith(successor);
- // Reiterate on this block in case it can be merged with its new successor.
}
}
@@ -300,8 +302,7 @@
// Remove all dead blocks. Iterate in post order because removal needs the
// block's chain of dominators and nested loops need to be updated from the
// inside out.
- for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph_->GetPostOrder()) {
int id = block->GetBlockId();
if (!live_blocks.IsBitSet(id)) {
MaybeRecordDeadBlock(block);
@@ -332,8 +333,7 @@
void HDeadCodeElimination::RemoveDeadInstructions() {
// Process basic blocks in post-order in the dominator tree, so that
// a dead instruction depending on another dead instruction is removed.
- for (HPostOrderIterator b(*graph_); !b.Done(); b.Advance()) {
- HBasicBlock* block = b.Current();
+ for (HBasicBlock* block : graph_->GetPostOrder()) {
// Traverse this block's instructions in backward order and remove
// the unused ones.
HBackwardInstructionIterator i(block->GetInstructions());
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index 1e86b75..f5931a2 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -411,8 +411,8 @@
// Use the reverse post order to ensure the non back-edge predecessors of a block are
// visited before the block itself.
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- VisitBasicBlock(it.Current());
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ VisitBasicBlock(block);
}
}
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 1d1921a..f2602fb 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -103,8 +103,7 @@
// Detects sequence variables (generalized induction variables) during an outer to inner
// traversal of all loops using Gerlek's algorithm. The order is important to enable
// range analysis on outer loop while visiting inner loops.
- for (HReversePostOrderIterator it_graph(*graph_); !it_graph.Done(); it_graph.Advance()) {
- HBasicBlock* graph_block = it_graph.Current();
+ for (HBasicBlock* graph_block : graph_->GetReversePostOrder()) {
// Don't analyze irreducible loops.
if (graph_block->IsLoopHeader() && !graph_block->GetLoopInformation()->IsIrreducible()) {
VisitLoop(graph_block->GetLoopInformation());
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 9faa98a..cc420b3 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1219,16 +1219,13 @@
return false;
}
- HReversePostOrderIterator it(*callee_graph);
- it.Advance(); // Past the entry block, it does not contain instructions that prevent inlining.
size_t number_of_instructions = 0;
bool can_inline_environment =
total_number_of_dex_registers_ < kMaximumNumberOfCumulatedDexRegisters;
- for (; !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
-
+ // Skip the entry block, it does not contain instructions that prevent inlining.
+ for (HBasicBlock* block : callee_graph->GetReversePostOrderSkipEntryBlock()) {
if (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible()) {
// Don't inline methods with irreducible loops, they could prevent some
// optimizations to run.
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 613e008..c8c4ca7 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -81,8 +81,7 @@
// locals (guaranteed by HGraphBuilder) and that all try blocks have been
// visited already (from HTryBoundary scoping and reverse post order).
bool catch_block_visited = false;
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* current = it.Current();
+ for (HBasicBlock* current : graph_->GetReversePostOrder()) {
if (current == current_block_) {
catch_block_visited = true;
} else if (current->IsTryBlock()) {
@@ -276,8 +275,8 @@
FindNativeDebugInfoLocations(native_debug_info_locations);
}
- for (HReversePostOrderIterator block_it(*graph_); !block_it.Done(); block_it.Advance()) {
- current_block_ = block_it.Current();
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ current_block_ = block;
uint32_t block_dex_pc = current_block_->GetDexPc();
InitializeBlockLocals();
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 3bb1c1d..e4d280f 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -124,20 +124,16 @@
void InstructionSimplifierVisitor::Run() {
// Iterate in reverse post order to open up more simplifications to users
// of instructions that got simplified.
- for (HReversePostOrderIterator it(*GetGraph()); !it.Done();) {
+ for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) {
// The simplification of an instruction to another instruction may yield
// possibilities for other simplifications. So although we perform a reverse
// post order visit, we sometimes need to revisit an instruction index.
- simplification_occurred_ = false;
- VisitBasicBlock(it.Current());
- if (simplification_occurred_ &&
- (simplifications_at_current_position_ < kMaxSamePositionSimplifications)) {
- // New simplifications may be applicable to the instruction at the
- // current index, so don't advance the iterator.
- continue;
- }
+ do {
+ simplification_occurred_ = false;
+ VisitBasicBlock(block);
+ } while (simplification_occurred_ &&
+ (simplifications_at_current_position_ < kMaxSamePositionSimplifications));
simplifications_at_current_position_ = 0;
- it.Advance();
}
}
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 8327a4c..fc6ff7b 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -133,8 +133,7 @@
void IntrinsicsRecognizer::Run() {
ScopedObjectAccess soa(Thread::Current());
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
inst_it.Advance()) {
HInstruction* inst = inst_it.Current();
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 96a6ecb..8790c1e 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -652,9 +652,9 @@
(invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
LocationSummary* locations = new (arena) LocationSummary(invoke,
- can_call ?
- LocationSummary::kCallOnSlowPath :
- LocationSummary::kNoCall,
+ (can_call
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall),
kIntrinsified);
if (can_call && kUseBakerReadBarrier) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
@@ -663,7 +663,7 @@
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(),
- can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
+ (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
// We need a temporary register for the read barrier marking slow
// path in InstructionCodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier.
@@ -891,8 +891,13 @@
static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
HInvoke* invoke,
Primitive::Type type) {
+ bool can_call = kEmitCompilerReadBarrier &&
+ kUseBakerReadBarrier &&
+ (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
LocationSummary* locations = new (arena) LocationSummary(invoke,
- LocationSummary::kNoCall,
+ (can_call
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall),
kIntrinsified);
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
@@ -901,36 +906,65 @@
locations->SetInAt(4, Location::RequiresRegister());
// If heap poisoning is enabled, we don't want the unpoisoning
- // operations to potentially clobber the output.
- Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot)
+ // operations to potentially clobber the output. Likewise when
+ // emitting a (Baker) read barrier, which may call.
+ Location::OutputOverlap overlaps =
+ ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
? Location::kOutputOverlap
: Location::kNoOutputOverlap;
locations->SetOut(Location::RequiresRegister(), overlaps);
+ // Temporary registers used in CAS. In the object case
+ // (UnsafeCASObject intrinsic), these are also used for
+ // card-marking, and possibly for (Baker) read barrier.
locations->AddTemp(Location::RequiresRegister()); // Pointer.
locations->AddTemp(Location::RequiresRegister()); // Temp 1.
}
-static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM* codegen) {
+static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARM* codegen) {
DCHECK_NE(type, Primitive::kPrimLong);
ArmAssembler* assembler = codegen->GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
- Register out = locations->Out().AsRegister<Register>(); // Boolean result.
+ Location out_loc = locations->Out();
+ Register out = out_loc.AsRegister<Register>(); // Boolean result.
- Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
- Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); // Offset (discard high 4B).
- Register expected_lo = locations->InAt(3).AsRegister<Register>(); // Expected.
- Register value_lo = locations->InAt(4).AsRegister<Register>(); // Value.
+ Register base = locations->InAt(1).AsRegister<Register>(); // Object pointer.
+ Location offset_loc = locations->InAt(2);
+ Register offset = offset_loc.AsRegisterPairLow<Register>(); // Offset (discard high 4B).
+ Register expected = locations->InAt(3).AsRegister<Register>(); // Expected.
+ Register value = locations->InAt(4).AsRegister<Register>(); // Value.
- Register tmp_ptr = locations->GetTemp(0).AsRegister<Register>(); // Pointer to actual memory.
- Register tmp_lo = locations->GetTemp(1).AsRegister<Register>(); // Value in memory.
+ Location tmp_ptr_loc = locations->GetTemp(0);
+ Register tmp_ptr = tmp_ptr_loc.AsRegister<Register>(); // Pointer to actual memory.
+ Register tmp = locations->GetTemp(1).AsRegister<Register>(); // Value in memory.
if (type == Primitive::kPrimNot) {
+ // The only read barrier implementation supporting the
+ // UnsafeCASObject intrinsic is the Baker-style read barriers.
+ DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
+
// Mark card for object assuming new value is stored. Worst case we will mark an unchanged
// object and scan the receiver at the next GC for nothing.
bool value_can_be_null = true; // TODO: Worth finding out this information?
- codegen->MarkGCCard(tmp_ptr, tmp_lo, base, value_lo, value_can_be_null);
+ 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->GenerateReferenceLoadWithBakerReadBarrier(
+ invoke,
+ out_loc, // Unused, used only as a "temporary" within the read barrier.
+ base,
+ /* offset */ 0u,
+ /* index */ offset_loc,
+ ScaleFactor::TIMES_1,
+ tmp_ptr_loc,
+ /* needs_null_check */ false,
+ /* always_update_field */ true,
+ &tmp);
+ }
}
// Prevent reordering with prior memory operations.
@@ -942,12 +976,12 @@
__ add(tmp_ptr, base, ShifterOperand(offset));
if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
- codegen->GetAssembler()->PoisonHeapReference(expected_lo);
- if (value_lo == expected_lo) {
- // Do not poison `value_lo`, as it is the same register as
- // `expected_lo`, which has just been poisoned.
+ __ PoisonHeapReference(expected);
+ if (value == expected) {
+ // Do not poison `value`, as it is the same register as
+ // `expected`, which has just been poisoned.
} else {
- codegen->GetAssembler()->PoisonHeapReference(value_lo);
+ __ PoisonHeapReference(value);
}
}
@@ -959,37 +993,29 @@
Label loop_head;
__ Bind(&loop_head);
- // TODO: When `type == Primitive::kPrimNot`, add a read barrier for
- // the reference stored in the object before attempting the CAS,
- // similar to the one in the art::Unsafe_compareAndSwapObject JNI
- // implementation.
- //
- // Note that this code is not (yet) used when read barriers are
- // enabled (see IntrinsicLocationsBuilderARM::VisitUnsafeCASObject).
- DCHECK(!(type == Primitive::kPrimNot && kEmitCompilerReadBarrier));
- __ ldrex(tmp_lo, tmp_ptr);
+ __ ldrex(tmp, tmp_ptr);
- __ subs(tmp_lo, tmp_lo, ShifterOperand(expected_lo));
+ __ subs(tmp, tmp, ShifterOperand(expected));
__ it(EQ, ItState::kItT);
- __ strex(tmp_lo, value_lo, tmp_ptr, EQ);
- __ cmp(tmp_lo, ShifterOperand(1), EQ);
+ __ strex(tmp, value, tmp_ptr, EQ);
+ __ cmp(tmp, ShifterOperand(1), EQ);
__ b(&loop_head, EQ);
__ dmb(ISH);
- __ rsbs(out, tmp_lo, ShifterOperand(1));
+ __ rsbs(out, tmp, ShifterOperand(1));
__ it(CC);
__ mov(out, ShifterOperand(0), CC);
if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
- codegen->GetAssembler()->UnpoisonHeapReference(expected_lo);
- if (value_lo == expected_lo) {
- // Do not unpoison `value_lo`, as it is the same register as
- // `expected_lo`, which has just been unpoisoned.
+ __ UnpoisonHeapReference(expected);
+ if (value == expected) {
+ // Do not unpoison `value`, as it is the same register as
+ // `expected`, which has just been unpoisoned.
} else {
- codegen->GetAssembler()->UnpoisonHeapReference(value_lo);
+ __ UnpoisonHeapReference(value);
}
}
}
@@ -998,33 +1024,23 @@
CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimInt);
}
void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) {
- // The UnsafeCASObject intrinsic is missing a read barrier, and
- // therefore sometimes does not work as expected (b/25883050).
- // Turn it off temporarily as a quick fix, until the read barrier is
- // implemented (see TODO in GenCAS).
- //
- // TODO(rpl): Implement read barrier support in GenCAS and re-enable
- // this intrinsic.
- if (kEmitCompilerReadBarrier) {
+ // The only read barrier implementation supporting the
+ // UnsafeCASObject intrinsic is the Baker-style read barriers.
+ if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
return;
}
CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimNot);
}
void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
- GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
+ GenCas(invoke, Primitive::kPrimInt, codegen_);
}
void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
- // The UnsafeCASObject intrinsic is missing a read barrier, and
- // therefore sometimes does not work as expected (b/25883050).
- // Turn it off temporarily as a quick fix, until the read barrier is
- // implemented (see TODO in GenCAS).
- //
- // TODO(rpl): Implement read barrier support in GenCAS and re-enable
- // this intrinsic.
- DCHECK(!kEmitCompilerReadBarrier);
+ // The only read barrier implementation supporting the
+ // UnsafeCASObject intrinsic is the Baker-style read barriers.
+ DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
- GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
+ GenCas(invoke, Primitive::kPrimNot, codegen_);
}
void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) {
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index e2c1802..db1c022 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -863,9 +863,9 @@
codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
trg_loc,
base,
- /* offset */ 0U,
+ /* offset */ 0u,
/* index */ offset_loc,
- /* scale_factor */ 0U,
+ /* scale_factor */ 0u,
temp,
/* needs_null_check */ false,
is_volatile);
@@ -880,7 +880,7 @@
if (type == Primitive::kPrimNot) {
DCHECK(trg.IsW());
- codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
+ codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0u, offset_loc);
}
}
}
@@ -890,9 +890,9 @@
(invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
LocationSummary* locations = new (arena) LocationSummary(invoke,
- can_call ?
- LocationSummary::kCallOnSlowPath :
- LocationSummary::kNoCall,
+ (can_call
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall),
kIntrinsified);
if (can_call && kUseBakerReadBarrier) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
@@ -901,7 +901,7 @@
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(),
- can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
+ (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
}
void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
@@ -1086,8 +1086,13 @@
static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
HInvoke* invoke,
Primitive::Type type) {
+ bool can_call = kEmitCompilerReadBarrier &&
+ kUseBakerReadBarrier &&
+ (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
LocationSummary* locations = new (arena) LocationSummary(invoke,
- LocationSummary::kNoCall,
+ (can_call
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall),
kIntrinsified);
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
@@ -1096,20 +1101,29 @@
locations->SetInAt(4, Location::RequiresRegister());
// If heap poisoning is enabled, we don't want the unpoisoning
- // operations to potentially clobber the output.
- Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot)
+ // operations to potentially clobber the output. Likewise when
+ // emitting a (Baker) read barrier, which may call.
+ Location::OutputOverlap overlaps =
+ ((kPoisonHeapReferences && type == Primitive::kPrimNot) || can_call)
? Location::kOutputOverlap
: Location::kNoOutputOverlap;
locations->SetOut(Location::RequiresRegister(), overlaps);
+ if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ // Temporary register for (Baker) read barrier.
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
-static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) {
+static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARM64* codegen) {
MacroAssembler* masm = codegen->GetVIXLAssembler();
+ LocationSummary* locations = invoke->GetLocations();
- Register out = WRegisterFrom(locations->Out()); // Boolean result.
+ Location out_loc = locations->Out();
+ Register out = WRegisterFrom(out_loc); // Boolean result.
Register base = WRegisterFrom(locations->InAt(1)); // Object pointer.
- Register offset = XRegisterFrom(locations->InAt(2)); // Long offset.
+ 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.
@@ -1118,6 +1132,27 @@
// 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->GenerateReferenceLoadWithBakerReadBarrier(
+ invoke,
+ out_loc, // Unused, used only as a "temporary" within the read barrier.
+ base,
+ /* offset */ 0u,
+ /* index */ offset_loc,
+ /* scale_factor */ 0u,
+ temp,
+ /* needs_null_check */ false,
+ /* use_load_acquire */ false,
+ /* always_update_field */ true);
+ }
}
UseScratchRegisterScope temps(masm);
@@ -1145,14 +1180,6 @@
vixl::aarch64::Label loop_head, exit_loop;
__ Bind(&loop_head);
- // TODO: When `type == Primitive::kPrimNot`, add a read barrier for
- // the reference stored in the object before attempting the CAS,
- // similar to the one in the art::Unsafe_compareAndSwapObject JNI
- // implementation.
- //
- // Note that this code is not (yet) used when read barriers are
- // enabled (see IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject).
- DCHECK(!(type == Primitive::kPrimNot && kEmitCompilerReadBarrier));
__ Ldaxr(tmp_value, MemOperand(tmp_ptr));
__ Cmp(tmp_value, expected);
__ B(&exit_loop, ne);
@@ -1179,14 +1206,9 @@
CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
}
void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
- // The UnsafeCASObject intrinsic is missing a read barrier, and
- // therefore sometimes does not work as expected (b/25883050).
- // Turn it off temporarily as a quick fix, until the read barrier is
- // implemented (see TODO in GenCAS).
- //
- // TODO(rpl): Implement read barrier support in GenCAS and re-enable
- // this intrinsic.
- if (kEmitCompilerReadBarrier) {
+ // The only read barrier implementation supporting the
+ // UnsafeCASObject intrinsic is the Baker-style read barriers.
+ if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -1194,22 +1216,17 @@
}
void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
- GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
+ GenCas(invoke, Primitive::kPrimInt, codegen_);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) {
- GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
+ GenCas(invoke, Primitive::kPrimLong, codegen_);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
- // The UnsafeCASObject intrinsic is missing a read barrier, and
- // therefore sometimes does not work as expected (b/25883050).
- // Turn it off temporarily as a quick fix, until the read barrier is
- // implemented (see TODO in GenCAS).
- //
- // TODO(rpl): Implement read barrier support in GenCAS and re-enable
- // this intrinsic.
- DCHECK(!kEmitCompilerReadBarrier);
+ // The only read barrier implementation supporting the
+ // UnsafeCASObject intrinsic is the Baker-style read barriers.
+ DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
- GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
+ GenCas(invoke, Primitive::kPrimNot, codegen_);
}
void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index f41e4d9..aae3899 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2056,9 +2056,9 @@
(invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
LocationSummary* locations = new (arena) LocationSummary(invoke,
- can_call ?
- LocationSummary::kCallOnSlowPath :
- LocationSummary::kNoCall,
+ (can_call
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall),
kIntrinsified);
if (can_call && kUseBakerReadBarrier) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
@@ -2076,7 +2076,7 @@
}
} else {
locations->SetOut(Location::RequiresRegister(),
- can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
+ (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
}
}
@@ -2255,10 +2255,16 @@
GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, /* is_volatile */ true, codegen_);
}
-static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, Primitive::Type type,
+static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
+ Primitive::Type type,
HInvoke* invoke) {
+ bool can_call = kEmitCompilerReadBarrier &&
+ kUseBakerReadBarrier &&
+ (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
LocationSummary* locations = new (arena) LocationSummary(invoke,
- LocationSummary::kNoCall,
+ (can_call
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall),
kIntrinsified);
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
@@ -2278,7 +2284,8 @@
// Force a byte register for the output.
locations->SetOut(Location::RegisterLocation(EAX));
if (type == Primitive::kPrimNot) {
- // Need temp registers for card-marking.
+ // Need temporary registers for card-marking, and possibly for
+ // (Baker) read barrier.
locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
// Need a byte register for marking.
locations->AddTemp(Location::RegisterLocation(ECX));
@@ -2294,14 +2301,9 @@
}
void IntrinsicLocationsBuilderX86::VisitUnsafeCASObject(HInvoke* invoke) {
- // The UnsafeCASObject intrinsic is missing a read barrier, and
- // therefore sometimes does not work as expected (b/25883050).
- // Turn it off temporarily as a quick fix, until the read barrier is
- // implemented (see TODO in GenCAS).
- //
- // TODO(rpl): Implement read barrier support in GenCAS and re-enable
- // this intrinsic.
- if (kEmitCompilerReadBarrier) {
+ // The only read barrier implementation supporting the
+ // UnsafeCASObject intrinsic is the Baker-style read barriers.
+ if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -2317,7 +2319,18 @@
Location out = locations->Out();
DCHECK_EQ(out.AsRegister<Register>(), EAX);
+ // The address of the field within the holding object.
+ Address field_addr(base, offset, ScaleFactor::TIMES_1, 0);
+
if (type == Primitive::kPrimNot) {
+ // The only read barrier implementation supporting the
+ // UnsafeCASObject intrinsic is the Baker-style read barriers.
+ DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
+
+ Location temp1_loc = locations->GetTemp(0);
+ Register temp1 = temp1_loc.AsRegister<Register>();
+ Register temp2 = locations->GetTemp(1).AsRegister<Register>();
+
Register expected = locations->InAt(3).AsRegister<Register>();
// Ensure `expected` is in EAX (required by the CMPXCHG instruction).
DCHECK_EQ(expected, EAX);
@@ -2325,11 +2338,20 @@
// Mark card for object assuming new value is stored.
bool value_can_be_null = true; // TODO: Worth finding out this information?
- codegen->MarkGCCard(locations->GetTemp(0).AsRegister<Register>(),
- locations->GetTemp(1).AsRegister<Register>(),
- base,
- value,
- value_can_be_null);
+ codegen->MarkGCCard(temp1, temp2, 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->GenerateReferenceLoadWithBakerReadBarrier(
+ invoke,
+ temp1_loc, // Unused, used only as a "temporary" within the read barrier.
+ base,
+ field_addr,
+ /* needs_null_check */ false,
+ /* always_update_field */ true,
+ &temp2);
+ }
bool base_equals_value = (base == value);
if (kPoisonHeapReferences) {
@@ -2337,7 +2359,7 @@
// If `base` and `value` are the same register location, move
// `value` to a temporary register. This way, poisoning
// `value` won't invalidate `base`.
- value = locations->GetTemp(0).AsRegister<Register>();
+ value = temp1;
__ movl(value, base);
}
@@ -2356,19 +2378,12 @@
__ PoisonHeapReference(value);
}
- // TODO: Add a read barrier for the reference stored in the object
- // before attempting the CAS, similar to the one in the
- // art::Unsafe_compareAndSwapObject JNI implementation.
- //
- // Note that this code is not (yet) used when read barriers are
- // enabled (see IntrinsicLocationsBuilderX86::VisitUnsafeCASObject).
- DCHECK(!kEmitCompilerReadBarrier);
- __ LockCmpxchgl(Address(base, offset, TIMES_1, 0), value);
+ __ LockCmpxchgl(field_addr, value);
// LOCK CMPXCHG has full barrier semantics, and we don't need
// scheduling barriers at this time.
- // Convert ZF into the boolean result.
+ // Convert ZF into the Boolean result.
__ setb(kZero, out.AsRegister<Register>());
__ movzxb(out.AsRegister<Register>(), out.AsRegister<ByteRegister>());
@@ -2392,8 +2407,7 @@
// Ensure the expected value is in EAX (required by the CMPXCHG
// instruction).
DCHECK_EQ(locations->InAt(3).AsRegister<Register>(), EAX);
- __ LockCmpxchgl(Address(base, offset, TIMES_1, 0),
- locations->InAt(4).AsRegister<Register>());
+ __ LockCmpxchgl(field_addr, locations->InAt(4).AsRegister<Register>());
} else if (type == Primitive::kPrimLong) {
// Ensure the expected value is in EAX:EDX and that the new
// value is in EBX:ECX (required by the CMPXCHG8B instruction).
@@ -2401,7 +2415,7 @@
DCHECK_EQ(locations->InAt(3).AsRegisterPairHigh<Register>(), EDX);
DCHECK_EQ(locations->InAt(4).AsRegisterPairLow<Register>(), EBX);
DCHECK_EQ(locations->InAt(4).AsRegisterPairHigh<Register>(), ECX);
- __ LockCmpxchg8b(Address(base, offset, TIMES_1, 0));
+ __ LockCmpxchg8b(field_addr);
} else {
LOG(FATAL) << "Unexpected CAS type " << type;
}
@@ -2409,7 +2423,7 @@
// LOCK CMPXCHG/LOCK CMPXCHG8B have full barrier semantics, and we
// don't need scheduling barriers at this time.
- // Convert ZF into the boolean result.
+ // Convert ZF into the Boolean result.
__ setb(kZero, out.AsRegister<Register>());
__ movzxb(out.AsRegister<Register>(), out.AsRegister<ByteRegister>());
}
@@ -2424,14 +2438,9 @@
}
void IntrinsicCodeGeneratorX86::VisitUnsafeCASObject(HInvoke* invoke) {
- // The UnsafeCASObject intrinsic is missing a read barrier, and
- // therefore sometimes does not work as expected (b/25883050).
- // Turn it off temporarily as a quick fix, until the read barrier is
- // implemented (see TODO in GenCAS).
- //
- // TODO(rpl): Implement read barrier support in GenCAS and re-enable
- // this intrinsic.
- DCHECK(!kEmitCompilerReadBarrier);
+ // The only read barrier implementation supporting the
+ // UnsafeCASObject intrinsic is the Baker-style read barriers.
+ DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
GenCAS(Primitive::kPrimNot, invoke, codegen_);
}
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 4b0afca..cdef22f 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2172,9 +2172,9 @@
(invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
LocationSummary* locations = new (arena) LocationSummary(invoke,
- can_call ?
- LocationSummary::kCallOnSlowPath :
- LocationSummary::kNoCall,
+ (can_call
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall),
kIntrinsified);
if (can_call && kUseBakerReadBarrier) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
@@ -2183,7 +2183,7 @@
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(),
- can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
+ (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
}
void IntrinsicLocationsBuilderX86_64::VisitUnsafeGet(HInvoke* invoke) {
@@ -2333,10 +2333,16 @@
GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, /* is_volatile */ true, codegen_);
}
-static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, Primitive::Type type,
+static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
+ Primitive::Type type,
HInvoke* invoke) {
+ bool can_call = kEmitCompilerReadBarrier &&
+ kUseBakerReadBarrier &&
+ (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject);
LocationSummary* locations = new (arena) LocationSummary(invoke,
- LocationSummary::kNoCall,
+ (can_call
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall),
kIntrinsified);
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
@@ -2347,7 +2353,8 @@
locations->SetOut(Location::RequiresRegister());
if (type == Primitive::kPrimNot) {
- // Need temp registers for card-marking.
+ // Need temporary registers for card-marking, and possibly for
+ // (Baker) read barrier.
locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
locations->AddTemp(Location::RequiresRegister());
}
@@ -2362,14 +2369,9 @@
}
void IntrinsicLocationsBuilderX86_64::VisitUnsafeCASObject(HInvoke* invoke) {
- // The UnsafeCASObject intrinsic is missing a read barrier, and
- // therefore sometimes does not work as expected (b/25883050).
- // Turn it off temporarily as a quick fix, until the read barrier is
- // implemented (see TODO in GenCAS).
- //
- // TODO(rpl): Implement read barrier support in GenCAS and re-enable
- // this intrinsic.
- if (kEmitCompilerReadBarrier) {
+ // The only read barrier implementation supporting the
+ // UnsafeCASObject intrinsic is the Baker-style read barriers.
+ if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -2386,16 +2388,37 @@
// Ensure `expected` is in RAX (required by the CMPXCHG instruction).
DCHECK_EQ(expected.AsRegister(), RAX);
CpuRegister value = locations->InAt(4).AsRegister<CpuRegister>();
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ Location out_loc = locations->Out();
+ CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (type == Primitive::kPrimNot) {
+ // The only read barrier implementation supporting the
+ // UnsafeCASObject intrinsic is the Baker-style read barriers.
+ DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
+
+ CpuRegister temp1 = locations->GetTemp(0).AsRegister<CpuRegister>();
+ CpuRegister temp2 = locations->GetTemp(1).AsRegister<CpuRegister>();
+
// Mark card for object assuming new value is stored.
bool value_can_be_null = true; // TODO: Worth finding out this information?
- codegen->MarkGCCard(locations->GetTemp(0).AsRegister<CpuRegister>(),
- locations->GetTemp(1).AsRegister<CpuRegister>(),
- base,
- value,
- value_can_be_null);
+ codegen->MarkGCCard(temp1, temp2, base, value, value_can_be_null);
+
+ // The address of the field within the holding object.
+ Address field_addr(base, offset, ScaleFactor::TIMES_1, 0);
+
+ 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->GenerateReferenceLoadWithBakerReadBarrier(
+ invoke,
+ out_loc, // Unused, used only as a "temporary" within the read barrier.
+ base,
+ field_addr,
+ /* needs_null_check */ false,
+ /* always_update_field */ true,
+ &temp1,
+ &temp2);
+ }
bool base_equals_value = (base.AsRegister() == value.AsRegister());
Register value_reg = value.AsRegister();
@@ -2404,7 +2427,7 @@
// If `base` and `value` are the same register location, move
// `value_reg` to a temporary register. This way, poisoning
// `value_reg` won't invalidate `base`.
- value_reg = locations->GetTemp(0).AsRegister<CpuRegister>().AsRegister();
+ value_reg = temp1.AsRegister();
__ movl(CpuRegister(value_reg), base);
}
@@ -2423,19 +2446,12 @@
__ PoisonHeapReference(CpuRegister(value_reg));
}
- // TODO: Add a read barrier for the reference stored in the object
- // before attempting the CAS, similar to the one in the
- // art::Unsafe_compareAndSwapObject JNI implementation.
- //
- // Note that this code is not (yet) used when read barriers are
- // enabled (see IntrinsicLocationsBuilderX86_64::VisitUnsafeCASObject).
- DCHECK(!kEmitCompilerReadBarrier);
- __ LockCmpxchgl(Address(base, offset, TIMES_1, 0), CpuRegister(value_reg));
+ __ LockCmpxchgl(field_addr, CpuRegister(value_reg));
// LOCK CMPXCHG has full barrier semantics, and we don't need
// scheduling barriers at this time.
- // Convert ZF into the boolean result.
+ // Convert ZF into the Boolean result.
__ setcc(kZero, out);
__ movzxb(out, out);
@@ -2468,7 +2484,7 @@
// LOCK CMPXCHG has full barrier semantics, and we don't need
// scheduling barriers at this time.
- // Convert ZF into the boolean result.
+ // Convert ZF into the Boolean result.
__ setcc(kZero, out);
__ movzxb(out, out);
}
@@ -2483,14 +2499,9 @@
}
void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASObject(HInvoke* invoke) {
- // The UnsafeCASObject intrinsic is missing a read barrier, and
- // therefore sometimes does not work as expected (b/25883050).
- // Turn it off temporarily as a quick fix, until the read barrier is
- // implemented (see TODO in GenCAS).
- //
- // TODO(rpl): Implement read barrier support in GenCAS and re-enable
- // this intrinsic.
- DCHECK(!kEmitCompilerReadBarrier);
+ // The only read barrier implementation supporting the
+ // UnsafeCASObject intrinsic is the Baker-style read barriers.
+ DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier);
GenCAS(Primitive::kPrimNot, invoke, codegen_);
}
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
index a0ded74..eb2d18d 100644
--- a/compiler/optimizing/licm.cc
+++ b/compiler/optimizing/licm.cc
@@ -15,6 +15,7 @@
*/
#include "licm.h"
+
#include "side_effects_analysis.h"
namespace art {
@@ -90,8 +91,7 @@
}
// Post order visit to visit inner loops before outer loops.
- for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph_->GetPostOrder()) {
if (!block->IsLoopHeader()) {
// Only visit the loop when we reach the header.
continue;
diff --git a/compiler/optimizing/linear_order.cc b/compiler/optimizing/linear_order.cc
index 3af212f..80cecd4 100644
--- a/compiler/optimizing/linear_order.cc
+++ b/compiler/optimizing/linear_order.cc
@@ -94,8 +94,7 @@
// for it.
ArenaVector<uint32_t> forward_predecessors(graph->GetBlocks().size(),
allocator->Adapter(kArenaAllocLinearOrder));
- for (HReversePostOrderIterator it(*graph); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph->GetReversePostOrder()) {
size_t number_of_forward_predecessors = block->GetPredecessors().size();
if (block->IsLoopHeader()) {
number_of_forward_predecessors -= block->GetLoopInformation()->NumberOfBackEdges();
diff --git a/compiler/optimizing/linear_order.h b/compiler/optimizing/linear_order.h
index cdbdd07..7122d67 100644
--- a/compiler/optimizing/linear_order.h
+++ b/compiler/optimizing/linear_order.h
@@ -30,16 +30,12 @@
//
// for (HBasicBlock* block : linear_order) // linear order
//
-// for (HBasicBlock* block : LinearPostOrder(linear_order)) // linear post order
+// for (HBasicBlock* block : ReverseRange(linear_order)) // linear post order
//
void LinearizeGraph(const HGraph* graph,
ArenaAllocator* allocator,
ArenaVector<HBasicBlock*>* linear_order);
-inline auto LinearPostOrder(const ArenaVector<HBasicBlock*>& linear_order) {
- return MakeIterationRange(linear_order.rbegin(), linear_order.rend());
-}
-
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_LINEAR_ORDER_H_
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index bd74368..37b58de 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -56,8 +56,7 @@
liveness.Analyze();
std::ostringstream buffer;
- for (HInsertionOrderIterator it(*graph); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph->GetBlocks()) {
buffer << "Block " << block->GetBlockId() << std::endl;
size_t ssa_values = liveness.GetNumberOfSsaValues();
BitVector* live_in = liveness.GetLiveInSet(*block);
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 820fa29..b91e9e6 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -1046,8 +1046,8 @@
return;
}
HeapLocationCollector heap_location_collector(graph_);
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- heap_location_collector.VisitBasicBlock(it.Current());
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ heap_location_collector.VisitBasicBlock(block);
}
if (heap_location_collector.GetNumberOfHeapLocations() > kMaxNumberOfHeapLocations) {
// Bail out if there are too many heap locations to deal with.
@@ -1065,8 +1065,8 @@
}
heap_location_collector.BuildAliasingMatrix();
LSEVisitor lse_visitor(graph_, heap_location_collector, side_effects_);
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- lse_visitor.VisitBasicBlock(it.Current());
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ lse_visitor.VisitBasicBlock(block);
}
lse_visitor.RemoveInstructions();
}
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 59cc009..45c7eb1 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -179,16 +179,16 @@
}
void HGraph::ClearDominanceInformation() {
- for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
- it.Current()->ClearDominanceInformation();
+ for (HBasicBlock* block : GetReversePostOrder()) {
+ block->ClearDominanceInformation();
}
reverse_post_order_.clear();
}
void HGraph::ClearLoopInformation() {
SetHasIrreducibleLoops(false);
- for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
- it.Current()->SetLoopInformation(nullptr);
+ for (HBasicBlock* block : GetReversePostOrder()) {
+ block->SetLoopInformation(nullptr);
}
}
@@ -275,8 +275,7 @@
bool update_occurred = true;
while (update_occurred) {
update_occurred = false;
- for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : GetReversePostOrder()) {
for (HBasicBlock* successor : block->GetSuccessors()) {
update_occurred |= UpdateDominatorOfSuccessor(block, successor);
}
@@ -287,8 +286,7 @@
// Make sure that there are no remaining blocks whose dominator information
// needs to be updated.
if (kIsDebugBuild) {
- for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : GetReversePostOrder()) {
for (HBasicBlock* successor : block->GetSuccessors()) {
DCHECK(!UpdateDominatorOfSuccessor(block, successor));
}
@@ -297,8 +295,7 @@
// Populate `dominated_blocks_` information after computing all dominators.
// The potential presence of irreducible loops requires to do it after.
- for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : GetReversePostOrder()) {
if (!block->IsEntryBlock()) {
block->GetDominator()->AddDominatedBlock(block);
}
@@ -375,8 +372,7 @@
void HGraph::ComputeTryBlockInformation() {
// Iterate in reverse post order to propagate try membership information from
// predecessors to their successors.
- for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : GetReversePostOrder()) {
if (block->IsEntryBlock() || block->IsCatchBlock()) {
// Catch blocks after simplification have only exceptional predecessors
// and hence are never in tries.
@@ -446,8 +442,7 @@
// We iterate post order to ensure we visit inner loops before outer loops.
// `PopulateRecursive` needs this guarantee to know whether a natural loop
// contains an irreducible loop.
- for (HPostOrderIterator it(*this); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : GetPostOrder()) {
if (block->IsLoopHeader()) {
if (block->IsCatchBlock()) {
// TODO: Dealing with exceptional back edges could be tricky because
@@ -1134,8 +1129,8 @@
}
void HGraphVisitor::VisitReversePostOrder() {
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- VisitBasicBlock(it.Current());
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ VisitBasicBlock(block);
}
}
@@ -1986,10 +1981,8 @@
// Update the environments in this graph to have the invoke's environment
// as parent.
{
- HReversePostOrderIterator it(*this);
- it.Advance(); // Skip the entry block, we do not need to update the entry's suspend check.
- for (; !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ // Skip the entry block, we do not need to update the entry's suspend check.
+ for (HBasicBlock* block : GetReversePostOrderSkipEntryBlock()) {
for (HInstructionIterator instr_it(block->GetInstructions());
!instr_it.Done();
instr_it.Advance()) {
@@ -2070,8 +2063,7 @@
// Do a reverse post order of the blocks in the callee and do (1), (2), (3)
// and (4) to the blocks that apply.
- for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
- HBasicBlock* current = it.Current();
+ for (HBasicBlock* current : GetReversePostOrder()) {
if (current != exit_block_ && current != entry_block_ && current != first) {
DCHECK(current->GetTryCatchInformation() == nullptr);
DCHECK(current->GetGraph() == this);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 257ccea..6a45149 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -25,6 +25,7 @@
#include "base/arena_containers.h"
#include "base/arena_object.h"
#include "base/array_ref.h"
+#include "base/iteration_range.h"
#include "base/stl_util.h"
#include "base/transform_array_ref.h"
#include "dex_file.h"
@@ -460,10 +461,23 @@
return reverse_post_order_;
}
+ ArrayRef<HBasicBlock* const> GetReversePostOrderSkipEntryBlock() {
+ DCHECK(GetReversePostOrder()[0] == entry_block_);
+ return ArrayRef<HBasicBlock* const>(GetReversePostOrder()).SubArray(1);
+ }
+
+ IterationRange<ArenaVector<HBasicBlock*>::const_reverse_iterator> GetPostOrder() const {
+ return ReverseRange(GetReversePostOrder());
+ }
+
const ArenaVector<HBasicBlock*>& GetLinearOrder() const {
return linear_order_;
}
+ IterationRange<ArenaVector<HBasicBlock*>::const_reverse_iterator> GetLinearPostOrder() const {
+ return ReverseRange(GetLinearOrder());
+ }
+
bool HasBoundsChecks() const {
return has_bounds_checks_;
}
@@ -6618,58 +6632,6 @@
DISALLOW_COPY_AND_ASSIGN(HGraphDelegateVisitor);
};
-class HInsertionOrderIterator : public ValueObject {
- public:
- explicit HInsertionOrderIterator(const HGraph& graph) : graph_(graph), index_(0) {}
-
- bool Done() const { return index_ == graph_.GetBlocks().size(); }
- HBasicBlock* Current() const { return graph_.GetBlocks()[index_]; }
- void Advance() { ++index_; }
-
- private:
- const HGraph& graph_;
- size_t index_;
-
- DISALLOW_COPY_AND_ASSIGN(HInsertionOrderIterator);
-};
-
-class HReversePostOrderIterator : public ValueObject {
- public:
- explicit HReversePostOrderIterator(const HGraph& graph) : graph_(graph), index_(0) {
- // Check that reverse post order of the graph has been built.
- DCHECK(!graph.GetReversePostOrder().empty());
- }
-
- bool Done() const { return index_ == graph_.GetReversePostOrder().size(); }
- HBasicBlock* Current() const { return graph_.GetReversePostOrder()[index_]; }
- void Advance() { ++index_; }
-
- private:
- const HGraph& graph_;
- size_t index_;
-
- DISALLOW_COPY_AND_ASSIGN(HReversePostOrderIterator);
-};
-
-class HPostOrderIterator : public ValueObject {
- public:
- explicit HPostOrderIterator(const HGraph& graph)
- : graph_(graph), index_(graph_.GetReversePostOrder().size()) {
- // Check that reverse post order of the graph has been built.
- DCHECK(!graph.GetReversePostOrder().empty());
- }
-
- bool Done() const { return index_ == 0; }
- HBasicBlock* Current() const { return graph_.GetReversePostOrder()[index_ - 1u]; }
- void Advance() { --index_; }
-
- private:
- const HGraph& graph_;
- size_t index_;
-
- DISALLOW_COPY_AND_ASSIGN(HPostOrderIterator);
-};
-
// Iterator over the blocks that art part of the loop. Includes blocks part
// of an inner loop. The order in which the blocks are iterated is on their
// block id.
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 03870ab..19fd6f9 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -18,6 +18,8 @@
#include <fstream>
#include <memory>
+#include <sstream>
+
#include <stdint.h>
#ifdef ART_ENABLE_CODEGEN_arm
@@ -46,6 +48,7 @@
#include "base/arena_containers.h"
#include "base/dumpable.h"
#include "base/macros.h"
+#include "base/mutex.h"
#include "base/timing_logger.h"
#include "bounds_check_elimination.h"
#include "builder.h"
@@ -135,14 +138,18 @@
PassObserver(HGraph* graph,
CodeGenerator* codegen,
std::ostream* visualizer_output,
- CompilerDriver* compiler_driver)
+ CompilerDriver* compiler_driver,
+ Mutex& dump_mutex)
: graph_(graph),
cached_method_name_(),
timing_logger_enabled_(compiler_driver->GetDumpPasses()),
timing_logger_(timing_logger_enabled_ ? GetMethodName() : "", true, true),
disasm_info_(graph->GetArena()),
+ visualizer_oss_(),
+ visualizer_output_(visualizer_output),
visualizer_enabled_(!compiler_driver->GetCompilerOptions().GetDumpCfgFileName().empty()),
- visualizer_(visualizer_output, graph, *codegen),
+ visualizer_(&visualizer_oss_, graph, *codegen),
+ visualizer_dump_mutex_(dump_mutex),
graph_in_bad_state_(false) {
if (timing_logger_enabled_ || visualizer_enabled_) {
if (!IsVerboseMethod(compiler_driver, GetMethodName())) {
@@ -160,6 +167,19 @@
LOG(INFO) << "TIMINGS " << GetMethodName();
LOG(INFO) << Dumpable<TimingLogger>(timing_logger_);
}
+ if (visualizer_enabled_) {
+ MutexLock mu(Thread::Current(), visualizer_dump_mutex_);
+ *visualizer_output_ << visualizer_oss_.str();
+ // The destructor of `visualizer_output_` is normally
+ // responsible for flushing (and closing) the stream, but it
+ // won't be invoked during fast exits in non-debug mode -- see
+ // art::Dex2Oat::~Dex2Oat, which explicitly abandons some
+ // objects (such as the compiler driver) in non-debug mode, to
+ // avoid the cost of destructing them. Therefore we explicitly
+ // flush the stream here to prevent truncated CFG visualizer
+ // files.
+ visualizer_output_->flush();
+ }
}
void DumpDisassembly() const {
@@ -237,8 +257,11 @@
DisassemblyInformation disasm_info_;
+ std::ostringstream visualizer_oss_;
+ std::ostream* visualizer_output_;
bool visualizer_enabled_;
HGraphVisualizer visualizer_;
+ Mutex& visualizer_dump_mutex_;
// Flag to be set by the compiler if the pass failed and the graph is not
// expected to validate.
@@ -369,13 +392,16 @@
std::unique_ptr<std::ostream> visualizer_output_;
+ mutable Mutex dump_mutex_; // To synchronize visualizer writing.
+
DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler);
};
static const int kMaximumCompilationTimeBeforeWarning = 100; /* ms */
OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver)
- : Compiler(driver, kMaximumCompilationTimeBeforeWarning) {}
+ : Compiler(driver, kMaximumCompilationTimeBeforeWarning),
+ dump_mutex_("Visualizer dump lock") {}
void OptimizingCompiler::Init() {
// Enable C1visualizer output. Must be done in Init() because the compiler
@@ -383,9 +409,6 @@
CompilerDriver* driver = GetCompilerDriver();
const std::string cfg_file_name = driver->GetCompilerOptions().GetDumpCfgFileName();
if (!cfg_file_name.empty()) {
- CHECK_EQ(driver->GetThreadCount(), 1U)
- << "Graph visualizer requires the compiler to run single-threaded. "
- << "Invoke the compiler with '-j1'.";
std::ios_base::openmode cfg_file_mode =
driver->GetCompilerOptions().GetDumpCfgAppend() ? std::ofstream::app : std::ofstream::out;
visualizer_output_.reset(new std::ofstream(cfg_file_name, cfg_file_mode));
@@ -951,7 +974,8 @@
PassObserver pass_observer(graph,
codegen.get(),
visualizer_output_.get(),
- compiler_driver);
+ compiler_driver,
+ dump_mutex_);
VLOG(compiler) << "Building " << pass_observer.GetMethodName();
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 7b66ef3..0db6088 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -20,8 +20,7 @@
void PrepareForRegisterAllocation::Run() {
// Order does not matter.
- for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) {
// No need to visit the phis.
for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
inst_it.Advance()) {
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index d93c9dd..d588dea 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -123,8 +123,7 @@
// TODO: move this to the graph checker.
if (kIsDebugBuild) {
ScopedObjectAccess soa(Thread::Current());
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
for (HInstructionIterator iti(block->GetInstructions()); !iti.Done(); iti.Advance()) {
HInstruction* instr = iti.Current();
if (instr->GetType() == Primitive::kPrimNot) {
@@ -158,8 +157,8 @@
// To properly propagate type info we need to visit in the dominator-based order.
// Reverse post order guarantees a node's dominators are visited first.
// We take advantage of this order in `VisitBasicBlock`.
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- VisitBasicBlock(it.Current());
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ VisitBasicBlock(block);
}
ProcessWorklist();
diff --git a/compiler/optimizing/register_allocator_graph_color.cc b/compiler/optimizing/register_allocator_graph_color.cc
index 9610774..aa0d371 100644
--- a/compiler/optimizing/register_allocator_graph_color.cc
+++ b/compiler/optimizing/register_allocator_graph_color.cc
@@ -758,7 +758,7 @@
}
void RegisterAllocatorGraphColor::ProcessInstructions() {
- for (HBasicBlock* block : LinearPostOrder(codegen_->GetGraph()->GetLinearOrder())) {
+ for (HBasicBlock* block : codegen_->GetGraph()->GetLinearPostOrder()) {
// Note that we currently depend on this ordering, since some helper
// code is designed for linear scan register allocation.
for (HBackwardInstructionIterator instr_it(block->GetInstructions());
diff --git a/compiler/optimizing/register_allocator_linear_scan.cc b/compiler/optimizing/register_allocator_linear_scan.cc
index 4e69bc8..1a391ce 100644
--- a/compiler/optimizing/register_allocator_linear_scan.cc
+++ b/compiler/optimizing/register_allocator_linear_scan.cc
@@ -163,7 +163,7 @@
void RegisterAllocatorLinearScan::AllocateRegistersInternal() {
// Iterate post-order, to ensure the list is sorted, and the last added interval
// is the one with the lowest start position.
- for (HBasicBlock* block : LinearPostOrder(codegen_->GetGraph()->GetLinearOrder())) {
+ for (HBasicBlock* block : codegen_->GetGraph()->GetLinearPostOrder()) {
for (HBackwardInstructionIterator back_it(block->GetInstructions()); !back_it.Done();
back_it.Advance()) {
ProcessInstruction(back_it.Current());
diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc
index e409035..46d0d0e 100644
--- a/compiler/optimizing/select_generator.cc
+++ b/compiler/optimizing/select_generator.cc
@@ -76,8 +76,7 @@
// Iterate in post order in the unlikely case that removing one occurrence of
// the selection pattern empties a branch block of another occurrence.
// Otherwise the order does not matter.
- for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph_->GetPostOrder()) {
if (!block->EndsWithIf()) continue;
// Find elements of the diamond pattern.
diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc
index 1dc6986..6d82e8e 100644
--- a/compiler/optimizing/side_effects_analysis.cc
+++ b/compiler/optimizing/side_effects_analysis.cc
@@ -26,8 +26,7 @@
// In DEBUG mode, ensure side effects are properly initialized to empty.
if (kIsDebugBuild) {
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
SideEffects effects = GetBlockEffects(block);
DCHECK(effects.DoesNothing());
if (block->IsLoopHeader()) {
@@ -38,9 +37,7 @@
}
// Do a post order visit to ensure we visit a loop header after its loop body.
- for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
-
+ for (HBasicBlock* block : graph_->GetPostOrder()) {
SideEffects effects = SideEffects::None();
// Update `effects` with the side effects of all instructions in this block.
for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 03807ba..ae1e369 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -25,8 +25,8 @@
void SsaBuilder::FixNullConstantType() {
// The order doesn't matter here.
- for (HReversePostOrderIterator itb(*graph_); !itb.Done(); itb.Advance()) {
- for (HInstructionIterator it(itb.Current()->GetInstructions()); !it.Done(); it.Advance()) {
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* equality_instr = it.Current();
if (!equality_instr->IsEqual() && !equality_instr->IsNotEqual()) {
continue;
@@ -57,8 +57,8 @@
void SsaBuilder::EquivalentPhisCleanup() {
// The order doesn't matter here.
- for (HReversePostOrderIterator itb(*graph_); !itb.Done(); itb.Advance()) {
- for (HInstructionIterator it(itb.Current()->GetPhis()); !it.Done(); it.Advance()) {
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
HPhi* phi = it.Current()->AsPhi();
HPhi* next = phi->GetNextEquivalentPhiWithSameType();
if (next != nullptr) {
@@ -79,8 +79,7 @@
}
void SsaBuilder::FixEnvironmentPhis() {
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
for (HInstructionIterator it_phis(block->GetPhis()); !it_phis.Done(); it_phis.Advance()) {
HPhi* phi = it_phis.Current()->AsPhi();
// If the phi is not dead, or has no environment uses, there is nothing to do.
@@ -228,8 +227,7 @@
void SsaBuilder::RunPrimitiveTypePropagation() {
ArenaVector<HPhi*> worklist(graph_->GetArena()->Adapter(kArenaAllocGraphBuilder));
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
if (block->IsLoopHeader()) {
for (HInstructionIterator phi_it(block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
HPhi* phi = phi_it.Current()->AsPhi();
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 76cf8fe..e8e12e1 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -139,7 +139,7 @@
void SsaLivenessAnalysis::ComputeLiveRanges() {
// Do a post order visit, adding inputs of instructions live in the block where
// that instruction is defined, and killing instructions that are being visited.
- for (HBasicBlock* block : LinearPostOrder(graph_->GetLinearOrder())) {
+ for (HBasicBlock* block : ReverseRange(graph_->GetLinearOrder())) {
BitVector* kill = GetKillSet(*block);
BitVector* live_in = GetLiveInSet(*block);
@@ -256,15 +256,13 @@
do {
changed = false;
- for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- const HBasicBlock& block = *it.Current();
-
+ for (const HBasicBlock* block : graph_->GetPostOrder()) {
// The live_in set depends on the kill set (which does not
// change in this loop), and the live_out set. If the live_out
// set does not change, there is no need to update the live_in set.
- if (UpdateLiveOut(block) && UpdateLiveIn(block)) {
+ if (UpdateLiveOut(*block) && UpdateLiveIn(*block)) {
if (kIsDebugBuild) {
- CheckNoLiveInIrreducibleLoop(block);
+ CheckNoLiveInIrreducibleLoop(*block);
}
changed = true;
}
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index b1ec99a..aec7a3c 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -34,8 +34,7 @@
ArenaSet<HPhi*> initially_live(graph_->GetArena()->Adapter(kArenaAllocSsaPhiElimination));
// Add to the worklist phis referenced by non-phi instructions.
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
HPhi* phi = inst_it.Current()->AsPhi();
if (phi->IsDead()) {
@@ -84,8 +83,7 @@
// Remove phis that are not live. Visit in post order so that phis
// that are not inputs of loop phis can be removed when they have
// no users left (dead phis might use dead phis).
- for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph_->GetPostOrder()) {
HInstruction* current = block->GetFirstPhi();
HInstruction* next = nullptr;
HPhi* phi;
@@ -119,8 +117,7 @@
void SsaRedundantPhiElimination::Run() {
// Add all phis in the worklist. Order does not matter for correctness, and
// neither will necessarily converge faster.
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
worklist_.push_back(inst_it.Current()->AsPhi());
}
diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h
index c5575fa..68fd32e 100644
--- a/compiler/utils/arm/assembler_arm_vixl.h
+++ b/compiler/utils/arm/assembler_arm_vixl.h
@@ -94,7 +94,7 @@
void StoreRegisterList(RegList regs, size_t stack_offset);
bool ShifterOperandCanAlwaysHold(uint32_t immediate);
- bool ShifterOperandCanHold(Opcode opcode, uint32_t immediate, SetCc set_cc);
+ bool ShifterOperandCanHold(Opcode opcode, uint32_t immediate, SetCc set_cc = kCcDontCare);
bool CanSplitLoadStoreOffset(int32_t allowed_offset_bits,
int32_t offset,
/*out*/ int32_t* add_to_base,
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 9664e43..8d2a0e7 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -79,17 +79,24 @@
callbacks->SetVerifierDeps(verifier_deps_.get());
}
+ void LoadDexFile(ScopedObjectAccess* soa, const char* name1, const char* name2 = nullptr)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ class_loader_ = (name2 == nullptr) ? LoadDex(name1) : LoadMultiDex(name1, name2);
+ dex_files_ = GetDexFiles(class_loader_);
+ primary_dex_file_ = dex_files_.front();
+
+ SetVerifierDeps(dex_files_);
+ StackHandleScope<1> hs(soa->Self());
+ Handle<mirror::ClassLoader> loader =
+ hs.NewHandle(soa->Decode<mirror::ClassLoader>(class_loader_));
+ for (const DexFile* dex_file : dex_files_) {
+ class_linker_->RegisterDexFile(*dex_file, loader.Get());
+ }
+ }
+
void LoadDexFile(ScopedObjectAccess* soa) REQUIRES_SHARED(Locks::mutator_lock_) {
- class_loader_ = LoadDex("VerifierDeps");
- std::vector<const DexFile*> dex_files = GetDexFiles(class_loader_);
- CHECK_EQ(dex_files.size(), 1u);
- dex_file_ = dex_files.front();
-
- SetVerifierDeps(dex_files);
-
- ObjPtr<mirror::ClassLoader> loader = soa->Decode<mirror::ClassLoader>(class_loader_);
- class_linker_->RegisterDexFile(*dex_file_, loader.Ptr());
-
+ LoadDexFile(soa, "VerifierDeps");
+ CHECK_EQ(dex_files_.size(), 1u);
klass_Main_ = FindClassByName("LMain;", soa);
CHECK(klass_Main_ != nullptr);
}
@@ -98,16 +105,16 @@
ScopedObjectAccess soa(Thread::Current());
LoadDexFile(&soa);
- StackHandleScope<2> hs(Thread::Current());
+ StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader_handle(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_)));
Handle<mirror::DexCache> dex_cache_handle(hs.NewHandle(klass_Main_->GetDexCache()));
const DexFile::ClassDef* class_def = klass_Main_->GetClassDef();
- const uint8_t* class_data = dex_file_->GetClassData(*class_def);
+ const uint8_t* class_data = primary_dex_file_->GetClassData(*class_def);
CHECK(class_data != nullptr);
- ClassDataItemIterator it(*dex_file_, class_data);
+ ClassDataItemIterator it(*primary_dex_file_, class_data);
while (it.HasNextStaticField() || it.HasNextInstanceField()) {
it.Next();
}
@@ -115,7 +122,7 @@
ArtMethod* method = nullptr;
while (it.HasNextDirectMethod()) {
ArtMethod* resolved_method = class_linker_->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
- *dex_file_,
+ *primary_dex_file_,
it.GetMemberIndex(),
dex_cache_handle,
class_loader_handle,
@@ -131,7 +138,7 @@
CHECK(method != nullptr);
MethodVerifier verifier(Thread::Current(),
- dex_file_,
+ primary_dex_file_,
dex_cache_handle,
class_loader_handle,
*class_def,
@@ -148,19 +155,16 @@
return !verifier.HasFailures();
}
- void VerifyDexFile() {
+ void VerifyDexFile(const char* multidex = nullptr) {
std::string error_msg;
{
ScopedObjectAccess soa(Thread::Current());
- LoadDexFile(&soa);
+ LoadDexFile(&soa, "VerifierDeps", multidex);
}
- SetVerifierDeps({ dex_file_ });
TimingLogger timings("Verify", false, false);
- std::vector<const DexFile*> dex_files;
- dex_files.push_back(dex_file_);
compiler_options_->boot_image_ = false;
compiler_driver_->InitializeThreadPools();
- compiler_driver_->Verify(class_loader_, dex_files, &timings);
+ compiler_driver_->Verify(class_loader_, dex_files_, &timings);
}
bool TestAssignabilityRecording(const std::string& dst,
@@ -173,7 +177,7 @@
DCHECK(klass_dst != nullptr);
mirror::Class* klass_src = FindClassByName(src, &soa);
DCHECK(klass_src != nullptr);
- verifier_deps_->AddAssignability(*dex_file_,
+ verifier_deps_->AddAssignability(*primary_dex_file_,
klass_dst,
klass_src,
is_strict,
@@ -182,9 +186,9 @@
}
bool HasUnverifiedClass(const std::string& cls) {
- const DexFile::TypeId* type_id = dex_file_->FindTypeId(cls.c_str());
+ const DexFile::TypeId* type_id = primary_dex_file_->FindTypeId(cls.c_str());
DCHECK(type_id != nullptr);
- uint16_t index = dex_file_->GetIndexForTypeId(*type_id);
+ uint16_t index = primary_dex_file_->GetIndexForTypeId(*type_id);
MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
for (const auto& dex_dep : verifier_deps_->dex_deps_) {
for (uint16_t entry : dex_dep.second->unverified_classes_) {
@@ -395,8 +399,21 @@
has_unverified_classes;
}
+ static std::set<VerifierDeps::MethodResolution>* GetMethods(
+ VerifierDeps::DexFileDeps* deps, MethodResolutionKind resolution_kind) {
+ if (resolution_kind == kDirectMethodResolution) {
+ return &deps->direct_methods_;
+ } else if (resolution_kind == kVirtualMethodResolution) {
+ return &deps->virtual_methods_;
+ } else {
+ DCHECK_EQ(resolution_kind, kInterfaceMethodResolution);
+ return &deps->interface_methods_;
+ }
+ }
+
std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
- const DexFile* dex_file_;
+ std::vector<const DexFile*> dex_files_;
+ const DexFile* primary_dex_file_;
jobject class_loader_;
mirror::Class* klass_Main_;
};
@@ -407,21 +424,21 @@
MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
- uint32_t id_Main1 = verifier_deps_->GetIdFromString(*dex_file_, "LMain;");
- ASSERT_LT(id_Main1, dex_file_->NumStringIds());
- ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*dex_file_, id_Main1));
+ uint32_t id_Main1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;");
+ ASSERT_LT(id_Main1, primary_dex_file_->NumStringIds());
+ ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Main1));
- uint32_t id_Main2 = verifier_deps_->GetIdFromString(*dex_file_, "LMain;");
- ASSERT_LT(id_Main2, dex_file_->NumStringIds());
- ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*dex_file_, id_Main2));
+ uint32_t id_Main2 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;");
+ ASSERT_LT(id_Main2, primary_dex_file_->NumStringIds());
+ ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Main2));
- uint32_t id_Lorem1 = verifier_deps_->GetIdFromString(*dex_file_, "Lorem ipsum");
- ASSERT_GE(id_Lorem1, dex_file_->NumStringIds());
- ASSERT_EQ("Lorem ipsum", verifier_deps_->GetStringFromId(*dex_file_, id_Lorem1));
+ uint32_t id_Lorem1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "Lorem ipsum");
+ ASSERT_GE(id_Lorem1, primary_dex_file_->NumStringIds());
+ ASSERT_EQ("Lorem ipsum", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Lorem1));
- uint32_t id_Lorem2 = verifier_deps_->GetIdFromString(*dex_file_, "Lorem ipsum");
- ASSERT_GE(id_Lorem2, dex_file_->NumStringIds());
- ASSERT_EQ("Lorem ipsum", verifier_deps_->GetStringFromId(*dex_file_, id_Lorem2));
+ uint32_t id_Lorem2 = verifier_deps_->GetIdFromString(*primary_dex_file_, "Lorem ipsum");
+ ASSERT_GE(id_Lorem2, primary_dex_file_->NumStringIds());
+ ASSERT_EQ("Lorem ipsum", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Lorem2));
ASSERT_EQ(id_Main1, id_Main2);
ASSERT_EQ(id_Lorem1, id_Lorem2);
@@ -1068,13 +1085,41 @@
ASSERT_TRUE(HasEachKindOfRecord());
std::vector<uint8_t> buffer;
- verifier_deps_->Encode(&buffer);
+ verifier_deps_->Encode(dex_files_, &buffer);
ASSERT_FALSE(buffer.empty());
- VerifierDeps decoded_deps({ dex_file_ }, ArrayRef<uint8_t>(buffer));
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
ASSERT_TRUE(verifier_deps_->Equals(decoded_deps));
}
+TEST_F(VerifierDepsTest, EncodeDecodeMulti) {
+ VerifyDexFile("MultiDex");
+
+ ASSERT_GT(NumberOfCompiledDexFiles(), 1u);
+ std::vector<uint8_t> buffer;
+ verifier_deps_->Encode(dex_files_, &buffer);
+ ASSERT_FALSE(buffer.empty());
+
+ // Create new DexFile, to mess with std::map order: the verifier deps used
+ // to iterate over the map, which doesn't guarantee insertion order. We fixed
+ // this by passing the expected order when encoding/decoding.
+ std::vector<std::unique_ptr<const DexFile>> first_dex_files = OpenTestDexFiles("VerifierDeps");
+ std::vector<std::unique_ptr<const DexFile>> second_dex_files = OpenTestDexFiles("MultiDex");
+ std::vector<const DexFile*> dex_files;
+ for (auto& dex_file : first_dex_files) {
+ dex_files.push_back(dex_file.get());
+ }
+ for (auto& dex_file : second_dex_files) {
+ dex_files.push_back(dex_file.get());
+ }
+
+ // Dump the new verifier deps to ensure it can properly read the data.
+ VerifierDeps decoded_deps(dex_files, ArrayRef<const uint8_t>(buffer));
+ std::ostringstream stream;
+ VariableIndentationOutputStream os(&stream);
+ decoded_deps.Dump(&os);
+}
+
TEST_F(VerifierDepsTest, UnverifiedClasses) {
VerifyDexFile();
ASSERT_FALSE(HasUnverifiedClass("LMyThread;"));
@@ -1088,5 +1133,311 @@
ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuperButFailures;"));
}
+// Returns the next resolution kind in the enum.
+static MethodResolutionKind GetNextResolutionKind(MethodResolutionKind resolution_kind) {
+ if (resolution_kind == kDirectMethodResolution) {
+ return kVirtualMethodResolution;
+ } else if (resolution_kind == kVirtualMethodResolution) {
+ return kInterfaceMethodResolution;
+ } else {
+ DCHECK_EQ(resolution_kind, kInterfaceMethodResolution);
+ return kDirectMethodResolution;
+ }
+}
+
+TEST_F(VerifierDepsTest, VerifyDeps) {
+ VerifyDexFile();
+
+ ASSERT_EQ(1u, NumberOfCompiledDexFiles());
+ ASSERT_TRUE(HasEachKindOfRecord());
+
+ // When validating, we create a new class loader, as
+ // the existing `class_loader_` may contain erroneous classes,
+ // that ClassLinker::FindClass won't return.
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ MutableHandle<mirror::ClassLoader> new_class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
+ {
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_TRUE(verifier_deps_->Verify(new_class_loader, soa.Self()));
+ }
+
+ std::vector<uint8_t> buffer;
+ verifier_deps_->Encode(dex_files_, &buffer);
+ ASSERT_FALSE(buffer.empty());
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_TRUE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ // Fiddle with the dependencies to make sure we catch any change and fail to verify.
+
+ {
+ // Mess up with the assignable_types.
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ deps->assignable_types_.insert(*deps->unassignable_types_.begin());
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ // Mess up with the unassignable_types.
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ deps->unassignable_types_.insert(*deps->assignable_types_.begin());
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ // Mess up with classes.
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->classes_) {
+ if (entry.IsResolved()) {
+ deps->classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->classes_) {
+ if (!entry.IsResolved()) {
+ deps->classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker - 1));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->classes_) {
+ if (entry.IsResolved()) {
+ deps->classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), entry.GetAccessFlags() - 1));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ // Mess up with fields.
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->fields_) {
+ if (entry.IsResolved()) {
+ deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+ VerifierDeps::kUnresolvedMarker,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->fields_) {
+ if (!entry.IsResolved()) {
+ deps->fields_.insert(VerifierDeps::FieldResolution(0 /* we know there is a field there */,
+ VerifierDeps::kUnresolvedMarker - 1,
+ 0 /* we know there is a class there */));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->fields_) {
+ if (entry.IsResolved()) {
+ deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+ entry.GetAccessFlags() - 1,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->fields_) {
+ static constexpr uint32_t kNewTypeIndex = 0;
+ if (entry.GetDeclaringClassIndex() != kNewTypeIndex) {
+ deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+ entry.GetAccessFlags(),
+ kNewTypeIndex));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ // Mess up with methods.
+ for (MethodResolutionKind resolution_kind :
+ { kDirectMethodResolution, kVirtualMethodResolution, kInterfaceMethodResolution }) {
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ VerifierDeps::kUnresolvedMarker,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (!entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */,
+ VerifierDeps::kUnresolvedMarker - 1,
+ 0 /* we know there is a class there */));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags() - 1,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ static constexpr uint32_t kNewTypeIndex = 0;
+ if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags(),
+ kNewTypeIndex));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ GetMethods(deps, GetNextResolutionKind(resolution_kind))->insert(
+ VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags(),
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ GetMethods(deps, GetNextResolutionKind(GetNextResolutionKind(resolution_kind)))->insert(
+ VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags(),
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ }
+ }
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ }
+ }
+}
+
} // namespace verifier
} // namespace art
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index da0db01..3d208b5 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -66,6 +66,7 @@
#include "type_lookup_table.h"
#include "vdex_file.h"
#include "verifier/method_verifier.h"
+#include "verifier/verifier_deps.h"
#include "well_known_classes.h"
#include <sys/stat.h>
@@ -483,6 +484,28 @@
os << "\n";
if (!options_.dump_header_only_) {
+ VariableIndentationOutputStream vios(&os);
+ VdexFile::Header vdex_header = oat_file_.GetVdexFile()->GetHeader();
+ if (vdex_header.IsValid()) {
+ std::string error_msg;
+ std::vector<const DexFile*> dex_files;
+ for (size_t i = 0; i < oat_dex_files_.size(); i++) {
+ const DexFile* dex_file = OpenDexFile(oat_dex_files_[i], &error_msg);
+ if (dex_file == nullptr) {
+ os << "Error opening dex file: " << error_msg << std::endl;
+ return false;
+ }
+ dex_files.push_back(dex_file);
+ }
+ verifier::VerifierDeps deps(dex_files, oat_file_.GetVdexFile()->GetVerifierDepsData());
+ deps.Dump(&vios);
+ } else {
+ os << "UNRECOGNIZED vdex file, magic "
+ << vdex_header.GetMagic()
+ << ", version "
+ << vdex_header.GetVersion()
+ << "\n";
+ }
for (size_t i = 0; i < oat_dex_files_.size(); i++) {
const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
CHECK(oat_dex_file != nullptr);
diff --git a/runtime/Android.bp b/runtime/Android.bp
index b498573..8f961af 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -123,6 +123,7 @@
"mirror/array.cc",
"mirror/class.cc",
"mirror/dex_cache.cc",
+ "mirror/emulated_stack_frame.cc",
"mirror/executable.cc",
"mirror/field.cc",
"mirror/method.cc",
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 4638c3f..c151f00 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -819,34 +819,60 @@
ScopedObjectAccess soa(self);
// garbage is created during ClassLinker::Init
- StackHandleScope<2> hs(soa.Self());
+ StackHandleScope<4> hs(soa.Self());
Handle<mirror::Class> c(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")));
Handle<mirror::Class> c2(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;")));
+ Handle<mirror::Class> list(
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/util/List;")));
+ Handle<mirror::Class> array_list(
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/util/ArrayList;")));
EXPECT_FALSE(self->IsExceptionPending());
- Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(c.Get()), 0U,
- art_quick_check_cast, self);
-
+ Invoke3(reinterpret_cast<size_t>(c.Get()),
+ reinterpret_cast<size_t>(c.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
EXPECT_FALSE(self->IsExceptionPending());
- Invoke3(reinterpret_cast<size_t>(c2.Get()), reinterpret_cast<size_t>(c2.Get()), 0U,
- art_quick_check_cast, self);
-
+ Invoke3(reinterpret_cast<size_t>(c2.Get()),
+ reinterpret_cast<size_t>(c2.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
EXPECT_FALSE(self->IsExceptionPending());
- Invoke3(reinterpret_cast<size_t>(c.Get()), reinterpret_cast<size_t>(c2.Get()), 0U,
- art_quick_check_cast, self);
-
+ Invoke3(reinterpret_cast<size_t>(c.Get()),
+ reinterpret_cast<size_t>(c2.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
EXPECT_FALSE(self->IsExceptionPending());
+ Invoke3(reinterpret_cast<size_t>(list.Get()),
+ reinterpret_cast<size_t>(array_list.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
+ EXPECT_FALSE(self->IsExceptionPending());
+
+ Invoke3(reinterpret_cast<size_t>(list.Get()),
+ reinterpret_cast<size_t>(c2.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
+ EXPECT_TRUE(self->IsExceptionPending());
+ self->ClearException();
+
// TODO: Make the following work. But that would require correct managed frames.
-
- Invoke3(reinterpret_cast<size_t>(c2.Get()), reinterpret_cast<size_t>(c.Get()), 0U,
- art_quick_check_cast, self);
-
+ Invoke3(reinterpret_cast<size_t>(c2.Get()),
+ reinterpret_cast<size_t>(c.Get()),
+ 0U,
+ art_quick_check_cast,
+ self);
EXPECT_TRUE(self->IsExceptionPending());
self->ClearException();
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index afa1c0f..2856766 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1481,6 +1481,32 @@
END_FUNCTION art_quick_unlock_object_no_inline
DEFINE_FUNCTION art_quick_check_cast
+ testl LITERAL(ACCESS_FLAGS_CLASS_IS_INTERFACE), MIRROR_CLASS_ACCESS_FLAGS_OFFSET(%rdi)
+ jz .Lnot_interface
+
+ // There are no read barriers since the iftable is immutable. There can be false negatives for
+ // the read barrier case if classes in the IfTable are in the from-space. In the case where
+ // we do not find a matching interface we call into artIsAssignableFromCode which will have
+ // read barriers.
+ movl MIRROR_CLASS_IF_TABLE_OFFSET(%rsi), %ecx
+ UNPOISON_HEAP_REF ecx
+ testl %ecx, %ecx
+ jz .Lnot_interface
+ movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), %r8d
+.Lstart_loop:
+ // Re-poison before comparing to prevent rare possible false positives. This is done inside
+ // the loop since heap poisoning is only for testing builds.
+ POISON_HEAP_REF edi
+ cmpl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rcx), %edi
+ je .Lreturn // Return if same class.
+ UNPOISON_HEAP_REF edi
+ // Go to next interface.
+ add LITERAL(COMPRESSED_REFERENCE_SIZE * 2), %rcx
+ sub LITERAL(2), %r8
+ jnz .Lstart_loop
+
+.Lnot_interface:
+ // We could check the super classes here but that is usually already checked in the caller.
PUSH rdi // Save args for exc
PUSH rsi
subq LITERAL(8), %rsp // Alignment padding.
@@ -1493,6 +1519,7 @@
addq LITERAL(24), %rsp // pop arguments
CFI_ADJUST_CFA_OFFSET(-24)
+.Lreturn:
ret
CFI_ADJUST_CFA_OFFSET(24 + 4 * 8) // Reset unwind info so following code unwinds.
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index cd8815b..1e5e127 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -172,6 +172,9 @@
#define MIRROR_CLASS_COMPONENT_TYPE_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_COMPONENT_TYPE_OFFSET,
art::mirror::Class::ComponentTypeOffset().Int32Value())
+#define MIRROR_CLASS_IF_TABLE_OFFSET (12 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_CLASS_IF_TABLE_OFFSET,
+ art::mirror::Class::IfTableOffset().Int32Value())
#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (64 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET,
art::mirror::Class::AccessFlagsOffset().Int32Value())
diff --git a/runtime/base/iteration_range.h b/runtime/base/iteration_range.h
index 54ab174..9d45707 100644
--- a/runtime/base/iteration_range.h
+++ b/runtime/base/iteration_range.h
@@ -54,6 +54,17 @@
return IterationRange<Iter>(it, it);
}
+template <typename Container>
+inline auto ReverseRange(Container& c) {
+ typedef typename std::reverse_iterator<decltype(c.begin())> riter;
+ return MakeIterationRange(riter(c.end()), riter(c.begin()));
+}
+
+template <typename T, size_t size>
+inline auto ReverseRange(T (&array)[size]) {
+ return ReverseRange(MakeIterationRange<T*>(array, array+size));
+}
+
} // namespace art
#endif // ART_RUNTIME_BASE_ITERATION_RANGE_H_
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c23b1b1..73524af 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -70,6 +70,7 @@
#include "mirror/class_loader.h"
#include "mirror/dex_cache.h"
#include "mirror/dex_cache-inl.h"
+#include "mirror/emulated_stack_frame.h"
#include "mirror/field.h"
#include "mirror/iftable-inl.h"
#include "mirror/method.h"
@@ -650,6 +651,11 @@
SetClassRoot(kJavaLangInvokeMethodHandleImpl, class_root);
mirror::MethodHandleImpl::SetClass(class_root);
+ class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;");
+ CHECK(class_root != nullptr);
+ SetClassRoot(kDalvikSystemEmulatedStackFrame, class_root);
+ mirror::EmulatedStackFrame::SetClass(class_root);
+
// java.lang.ref classes need to be specially flagged, but otherwise are normal classes
// finish initializing Reference class
mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusNotReady, self);
@@ -1059,6 +1065,7 @@
mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
+ mirror::EmulatedStackFrame::SetClass(GetClassRoot(kDalvikSystemEmulatedStackFrame));
for (gc::space::ImageSpace* image_space : spaces) {
// Boot class loader, use a null handle.
@@ -1119,7 +1126,7 @@
DCHECK(error_msg != nullptr);
ScopedObjectAccessUnchecked soa(Thread::Current());
ArtField* const dex_path_list_field =
- soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList);
+ soa.DecodeField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
ArtField* const dex_elements_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements);
CHECK(dex_path_list_field != nullptr);
@@ -1309,12 +1316,9 @@
const size_t num_types = dex_file->NumTypeIds();
const size_t num_methods = dex_file->NumMethodIds();
const size_t num_fields = dex_file->NumFieldIds();
- size_t num_method_types = 0;
- if (Runtime::Current()->IsMethodHandlesEnabled()) {
- num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
- if (dex_file->NumProtoIds() < num_method_types) {
- num_method_types = dex_file->NumProtoIds();
- }
+ size_t num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
+ if (dex_file->NumProtoIds() < num_method_types) {
+ num_method_types = dex_file->NumProtoIds();
}
CHECK_EQ(num_strings, dex_cache->NumStrings());
@@ -2041,6 +2045,7 @@
mirror::ShortArray::ResetArrayClass();
mirror::MethodType::ResetClass();
mirror::MethodHandleImpl::ResetClass();
+ mirror::EmulatedStackFrame::ResetClass();
Thread* const self = Thread::Current();
for (const ClassLoaderData& data : class_loaders_) {
DeleteClassLoader(self, data);
@@ -2110,21 +2115,18 @@
//
// If this needs to be mitigated in a production system running this code,
// DexCache::kDexCacheMethodTypeCacheSize can be set to zero.
- const bool is_method_handles_enabled = Runtime::Current()->IsMethodHandlesEnabled();
mirror::MethodTypeDexCacheType* method_types = nullptr;
size_t num_method_types = 0;
- if (is_method_handles_enabled) {
- if (dex_file.NumProtoIds() < mirror::DexCache::kDexCacheMethodTypeCacheSize) {
- num_method_types = dex_file.NumProtoIds();
- } else {
- num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
- }
+ if (dex_file.NumProtoIds() < mirror::DexCache::kDexCacheMethodTypeCacheSize) {
+ num_method_types = dex_file.NumProtoIds();
+ } else {
+ num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
+ }
- if (num_method_types > 0) {
- method_types = reinterpret_cast<mirror::MethodTypeDexCacheType*>(
- raw_arrays + layout.MethodTypesOffset());
- }
+ if (num_method_types > 0) {
+ method_types = reinterpret_cast<mirror::MethodTypeDexCacheType*>(
+ raw_arrays + layout.MethodTypesOffset());
}
DCHECK_ALIGNED(raw_arrays, alignof(mirror::StringDexCacheType)) <<
@@ -2389,7 +2391,7 @@
ArtField* const dex_file_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
ObjPtr<mirror::Object> dex_path_list =
- soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+ soa.DecodeField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
GetObject(class_loader.Get());
if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
@@ -8056,6 +8058,7 @@
"Ljava/lang/Throwable;",
"Ljava/lang/ClassNotFoundException;",
"Ljava/lang/StackTraceElement;",
+ "Ldalvik/system/EmulatedStackFrame;",
"Z",
"B",
"C",
@@ -8162,7 +8165,7 @@
DCHECK(h_path_class_loader.Get() != nullptr);
// Set DexPathList.
ArtField* path_list_field =
- soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList);
+ soa.DecodeField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
DCHECK(path_list_field != nullptr);
path_list_field->SetObject<false>(h_path_class_loader.Get(), h_dex_path_list.Get());
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index e99dfe3..3248d0e 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -109,6 +109,7 @@
kJavaLangThrowable,
kJavaLangClassNotFoundException,
kJavaLangStackTraceElement,
+ kDalvikSystemEmulatedStackFrame,
kPrimitiveBoolean,
kPrimitiveByte,
kPrimitiveChar,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index ab18627..5878bf3 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -31,6 +31,7 @@
#include "mirror/accessible_object.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache.h"
+#include "mirror/emulated_stack_frame.h"
#include "mirror/executable.h"
#include "mirror/field.h"
#include "mirror/method_type.h"
@@ -740,6 +741,15 @@
}
};
+struct EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFrame> {
+ EmulatedStackFrameOffsets() : CheckOffsets<mirror::EmulatedStackFrame>(
+ false, "Ldalvik/system/EmulatedStackFrame;") {
+ addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, references_), "references");
+ addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, stack_frame_), "stackFrame");
+ addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, type_), "type");
+ }
+};
+
// C++ fields must exactly match the fields in the Java classes. If this fails,
// reorder the fields in the C++ class. Managed class fields are ordered by
// ClassLinker::LinkFields.
@@ -760,6 +770,7 @@
EXPECT_TRUE(ExecutableOffsets().Check());
EXPECT_TRUE(MethodTypeOffsets().Check());
EXPECT_TRUE(MethodHandleImplOffsets().Check());
+ EXPECT_TRUE(EmulatedStackFrameOffsets().Check());
}
TEST_F(ClassLinkerTest, FindClassNonexistent) {
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 5409fcb..3409938 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -524,7 +524,7 @@
ArtField* dex_file_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
ObjPtr<mirror::Object> dex_path_list =
- soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+ soa.DecodeField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
GetObject(class_loader.Get());
if (dex_path_list != nullptr && dex_file_field!= nullptr && cookie_field != nullptr) {
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
@@ -572,6 +572,29 @@
return ret;
}
+jobject CommonRuntimeTestImpl::LoadMultiDex(const char* first_dex_name,
+ const char* second_dex_name) {
+ std::vector<std::unique_ptr<const DexFile>> first_dex_files = OpenTestDexFiles(first_dex_name);
+ std::vector<std::unique_ptr<const DexFile>> second_dex_files = OpenTestDexFiles(second_dex_name);
+ std::vector<const DexFile*> class_path;
+ CHECK_NE(0U, first_dex_files.size());
+ CHECK_NE(0U, second_dex_files.size());
+ for (auto& dex_file : first_dex_files) {
+ class_path.push_back(dex_file.get());
+ loaded_dex_files_.push_back(std::move(dex_file));
+ }
+ for (auto& dex_file : second_dex_files) {
+ class_path.push_back(dex_file.get());
+ loaded_dex_files_.push_back(std::move(dex_file));
+ }
+
+ Thread* self = Thread::Current();
+ jobject class_loader = Runtime::Current()->GetClassLinker()->CreatePathClassLoader(self,
+ class_path);
+ self->SetClassLoaderOverride(class_loader);
+ return class_loader;
+}
+
jobject CommonRuntimeTestImpl::LoadDex(const char* dex_name) {
std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name);
std::vector<const DexFile*> class_path;
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 92934c6..827e85e 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -133,6 +133,8 @@
REQUIRES_SHARED(Locks::mutator_lock_);
jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_);
+ jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name)
+ REQUIRES_SHARED(Locks::mutator_lock_);
std::string android_data_;
std::string dalvik_cache_;
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 0251776..9f0dbbb 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -813,13 +813,11 @@
void ThrowWrongMethodTypeException(mirror::MethodType* callee_type,
mirror::MethodType* callsite_type) {
- // TODO(narayan): Should we provide more detail here ? The RI doesn't bother.
- UNUSED(callee_type);
- UNUSED(callsite_type);
-
ThrowException("Ljava/lang/invoke/WrongMethodTypeException;",
nullptr,
- "Invalid method type for signature polymorphic call");
+ StringPrintf("Expected %s but was %s",
+ callee_type->PrettyDescriptor().c_str(),
+ callsite_type->PrettyDescriptor().c_str()).c_str());
}
} // namespace art
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index b3317a5..2ef7509 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -29,25 +29,17 @@
#include "base/enums.h"
#include "base/file_magic.h"
-#include "base/hash_map.h"
#include "base/logging.h"
-#include "base/stl_util.h"
#include "base/stringprintf.h"
#include "base/systrace.h"
#include "base/unix_file/fd_file.h"
#include "dex_file-inl.h"
#include "dex_file_verifier.h"
-#include "globals.h"
#include "jvalue.h"
#include "leb128.h"
-#include "oat_file.h"
#include "os.h"
-#include "safe_map.h"
-#include "thread.h"
-#include "type_lookup_table.h"
#include "utf-inl.h"
#include "utils.h"
-#include "well_known_classes.h"
#include "zip_archive.h"
namespace art {
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index da828dc..da9fa50 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -22,7 +22,6 @@
#include <vector>
#include "base/logging.h"
-#include "base/mutex.h" // For Locks::mutator_lock_.
#include "base/value_object.h"
#include "globals.h"
#include "invoke_type.h"
@@ -36,10 +35,8 @@
class OatDexFile;
class Signature;
class StringPiece;
-class TypeLookupTable;
class ZipArchive;
-// TODO: move all of the macro functionality into the DexCache class.
class DexFile {
public:
// First Dex format version supporting default methods.
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 5d70076..7b1fb95 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -25,7 +25,6 @@
#include "dex_file-inl.h"
#include "experimental_flags.h"
#include "leb128.h"
-#include "runtime.h"
#include "safe_map.h"
#include "utf-inl.h"
#include "utils.h"
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 31811fb..ed60f59 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -483,15 +483,15 @@
template<InvokeType type, bool access_check>
inline ArtMethod* FindMethodFromCode(uint32_t method_idx,
- mirror::Object** this_object,
+ ObjPtr<mirror::Object>* this_object,
ArtMethod* referrer,
Thread* self) {
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
ArtMethod* resolved_method = class_linker->GetResolvedMethod(method_idx, referrer);
if (resolved_method == nullptr) {
StackHandleScope<1> hs(self);
- mirror::Object* null_this = nullptr;
- HandleWrapper<mirror::Object> h_this(
+ ObjPtr<mirror::Object> null_this = nullptr;
+ HandleWrapperObjPtr<mirror::Object> h_this(
hs.NewHandleWrapper(type == kStatic ? &null_this : this_object));
constexpr ClassLinker::ResolveMode resolve_mode =
access_check ? ClassLinker::kForceICCECheck
@@ -560,7 +560,7 @@
// defaults. What we actually need is a GetContainingClass that says which classes virtuals
// this method is coming from.
StackHandleScope<2> hs2(self);
- HandleWrapper<mirror::Object> h_this(hs2.NewHandleWrapper(this_object));
+ HandleWrapperObjPtr<mirror::Object> h_this(hs2.NewHandleWrapper(this_object));
Handle<mirror::Class> h_referring_class(hs2.NewHandle(referrer->GetDeclaringClass()));
const uint16_t method_type_idx =
h_referring_class->GetDexFile().GetMethodId(method_idx).class_idx_;
@@ -652,7 +652,7 @@
#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
template REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE \
ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \
- mirror::Object** this_object, \
+ ObjPtr<mirror::Object>* this_object, \
ArtMethod* referrer, \
Thread* self)
#define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
@@ -722,8 +722,11 @@
}
// Fast path method resolution that can't throw exceptions.
-inline ArtMethod* FindMethodFast(uint32_t method_idx, mirror::Object* this_object,
- ArtMethod* referrer, bool access_check, InvokeType type) {
+inline ArtMethod* FindMethodFast(uint32_t method_idx,
+ ObjPtr<mirror::Object> this_object,
+ ArtMethod* referrer,
+ bool access_check,
+ InvokeType type) {
ScopedAssertNoThreadSuspension ants(__FUNCTION__);
if (UNLIKELY(this_object == nullptr && type != kStatic)) {
return nullptr;
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index cbefbba..1ccb4b0 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -237,7 +237,7 @@
}
}
-bool FillArrayData(mirror::Object* obj, const Instruction::ArrayDataPayload* payload) {
+bool FillArrayData(ObjPtr<mirror::Object> obj, const Instruction::ArrayDataPayload* payload) {
DCHECK_EQ(payload->ident, static_cast<uint16_t>(Instruction::kArrayDataSignature));
if (UNLIKELY(obj == nullptr)) {
ThrowNullPointerException("null array in FILL_ARRAY_DATA");
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 20c8401..bcddfb0 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -156,7 +156,7 @@
template<InvokeType type, bool access_check>
inline ArtMethod* FindMethodFromCode(uint32_t method_idx,
- mirror::Object** this_object,
+ ObjPtr<mirror::Object>* this_object,
ArtMethod* referrer,
Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -171,7 +171,7 @@
// Fast path method resolution that can't throw exceptions.
inline ArtMethod* FindMethodFast(uint32_t method_idx,
- mirror::Object* this_object,
+ ObjPtr<mirror::Object> this_object,
ArtMethod* referrer,
bool access_check,
InvokeType type)
@@ -203,7 +203,7 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
-bool FillArrayData(mirror::Object* obj, const Instruction::ArrayDataPayload* payload)
+bool FillArrayData(ObjPtr<mirror::Object> obj, const Instruction::ArrayDataPayload* payload)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 0bb6581..fe82878 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2121,7 +2121,9 @@
// to hold the mutator lock (see REQUIRES_SHARED(Locks::mutator_lock_) annotations).
template<InvokeType type, bool access_check>
-static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object, Thread* self,
+static TwoWordReturn artInvokeCommon(uint32_t method_idx,
+ ObjPtr<mirror::Object> this_object,
+ Thread* self,
ArtMethod** sp) {
ScopedQuickEntrypointChecks sqec(self);
DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(Runtime::kSaveRefsAndArgs));
@@ -2136,7 +2138,9 @@
ScopedObjectAccessUnchecked soa(self->GetJniEnv());
RememberForGcArgumentVisitor visitor(sp, type == kStatic, shorty, shorty_len, &soa);
visitor.VisitArguments();
- method = FindMethodFromCode<type, access_check>(method_idx, &this_object, caller_method,
+ method = FindMethodFromCode<type, access_check>(method_idx,
+ &this_object,
+ caller_method,
self);
visitor.FixupReferences();
}
@@ -2162,7 +2166,7 @@
#define EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(type, access_check) \
template REQUIRES_SHARED(Locks::mutator_lock_) \
TwoWordReturn artInvokeCommon<type, access_check>( \
- uint32_t method_idx, mirror::Object* this_object, Thread* self, ArtMethod** sp)
+ uint32_t method_idx, ObjPtr<mirror::Object> his_object, Thread* self, ArtMethod** sp)
EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kVirtual, false);
EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kVirtual, true);
@@ -2190,9 +2194,13 @@
}
extern "C" TwoWordReturn artInvokeStaticTrampolineWithAccessCheck(
- uint32_t method_idx, mirror::Object* this_object, Thread* self, ArtMethod** sp)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return artInvokeCommon<kStatic, true>(method_idx, this_object, self, sp);
+ uint32_t method_idx,
+ mirror::Object* this_object ATTRIBUTE_UNUSED,
+ Thread* self,
+ ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // For static, this_object is not required and may be random garbage. Don't pass it down so that
+ // it doesn't cause ObjPtr alignment failure check.
+ return artInvokeCommon<kStatic, true>(method_idx, nullptr, self, sp);
}
extern "C" TwoWordReturn artInvokeSuperTrampolineWithAccessCheck(
@@ -2211,10 +2219,11 @@
// is there for consistency but should not be used, as some architectures overwrite it
// in the assembly trampoline.
extern "C" TwoWordReturn artInvokeInterfaceTrampoline(uint32_t deadbeef ATTRIBUTE_UNUSED,
- mirror::Object* this_object,
+ mirror::Object* raw_this_object,
Thread* self,
ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> this_object(raw_this_object);
ScopedQuickEntrypointChecks sqec(self);
StackHandleScope<1> hs(self);
Handle<mirror::Class> cls(hs.NewHandle(this_object->GetClass()));
@@ -2285,7 +2294,9 @@
ScopedObjectAccessUnchecked soa(self->GetJniEnv());
RememberForGcArgumentVisitor visitor(sp, false, shorty, shorty_len, &soa);
visitor.VisitArguments();
- method = FindMethodFromCode<kInterface, false>(dex_method_idx, &this_object, caller_method,
+ method = FindMethodFromCode<kInterface, false>(dex_method_idx,
+ &this_object,
+ caller_method,
self);
visitor.FixupReferences();
}
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 8bb90e1..6dfab8b 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -2335,7 +2335,6 @@
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());
- region_space_ = nullptr;
}
{
MutexLock mu(self, skipped_blocks_lock_);
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index 1d377a4..7ff845d 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -38,10 +38,10 @@
case kGcCauseDebugger: return "Debugger";
case kGcCauseClassLinker: return "ClassLinker";
case kGcCauseJitCodeCache: return "JitCodeCache";
- default:
- LOG(FATAL) << "Unreachable";
- UNREACHABLE();
+ case kGcCauseAddRemoveSystemWeakHolder: return "SystemWeakHolder";
}
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
}
std::ostream& operator<<(std::ostream& os, const GcCause& gc_cause) {
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index 03f5bf6..6c189b0 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -52,6 +52,8 @@
DEFINE_CHECK_EQ(static_cast<uint32_t>(MIRROR_CLASS_STATUS_INITIALIZED), (static_cast<uint32_t>((art::mirror::Class::kStatusInitialized))))
#define ACCESS_FLAGS_CLASS_IS_FINALIZABLE 0x80000000
DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE), (static_cast<uint32_t>((art::kAccClassIsFinalizable))))
+#define ACCESS_FLAGS_CLASS_IS_INTERFACE 0x200
+DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_INTERFACE), (static_cast<uint32_t>((art::kAccInterface))))
#define ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT 0x1f
DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT), (static_cast<uint32_t>((art::MostSignificantBit(art::kAccClassIsFinalizable)))))
#define ART_METHOD_DEX_CACHE_METHODS_OFFSET_32 20
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index a61a187..7b75109 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -63,9 +63,9 @@
strong_interns_.VisitRoots(visitor);
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_strong_intern_roots_) {
- mirror::String* old_ref = root.Read<kWithoutReadBarrier>();
+ ObjPtr<mirror::String> old_ref = root.Read<kWithoutReadBarrier>();
root.VisitRoot(visitor, RootInfo(kRootInternedString));
- mirror::String* new_ref = root.Read<kWithoutReadBarrier>();
+ ObjPtr<mirror::String> new_ref = root.Read<kWithoutReadBarrier>();
if (new_ref != old_ref) {
// The GC moved a root in the log. Need to search the strong interns and update the
// corresponding object. This is slow, but luckily for us, this may only happen with a
@@ -86,17 +86,17 @@
// Note: we deliberately don't visit the weak_interns_ table and the immutable image roots.
}
-mirror::String* InternTable::LookupWeak(Thread* self, mirror::String* s) {
+ObjPtr<mirror::String> InternTable::LookupWeak(Thread* self, ObjPtr<mirror::String> s) {
MutexLock mu(self, *Locks::intern_table_lock_);
return LookupWeakLocked(s);
}
-mirror::String* InternTable::LookupStrong(Thread* self, mirror::String* s) {
+ObjPtr<mirror::String> InternTable::LookupStrong(Thread* self, ObjPtr<mirror::String> s) {
MutexLock mu(self, *Locks::intern_table_lock_);
return LookupStrongLocked(s);
}
-mirror::String* InternTable::LookupStrong(Thread* self,
+ObjPtr<mirror::String> InternTable::LookupStrong(Thread* self,
uint32_t utf16_length,
const char* utf8_data) {
DCHECK_EQ(utf16_length, CountModifiedUtf8Chars(utf8_data));
@@ -107,11 +107,11 @@
return strong_interns_.Find(string);
}
-mirror::String* InternTable::LookupWeakLocked(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::LookupWeakLocked(ObjPtr<mirror::String> s) {
return weak_interns_.Find(s);
}
-mirror::String* InternTable::LookupStrongLocked(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::LookupStrongLocked(ObjPtr<mirror::String> s) {
return strong_interns_.Find(s);
}
@@ -121,7 +121,7 @@
strong_interns_.AddNewTable();
}
-mirror::String* InternTable::InsertStrong(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::InsertStrong(ObjPtr<mirror::String> s) {
Runtime* runtime = Runtime::Current();
if (runtime->IsActiveTransaction()) {
runtime->RecordStrongStringInsertion(s);
@@ -133,7 +133,7 @@
return s;
}
-mirror::String* InternTable::InsertWeak(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::InsertWeak(ObjPtr<mirror::String> s) {
Runtime* runtime = Runtime::Current();
if (runtime->IsActiveTransaction()) {
runtime->RecordWeakStringInsertion(s);
@@ -142,11 +142,11 @@
return s;
}
-void InternTable::RemoveStrong(mirror::String* s) {
+void InternTable::RemoveStrong(ObjPtr<mirror::String> s) {
strong_interns_.Remove(s);
}
-void InternTable::RemoveWeak(mirror::String* s) {
+void InternTable::RemoveWeak(ObjPtr<mirror::String> s) {
Runtime* runtime = Runtime::Current();
if (runtime->IsActiveTransaction()) {
runtime->RecordWeakStringRemoval(s);
@@ -155,19 +155,22 @@
}
// Insert/remove methods used to undo changes made during an aborted transaction.
-mirror::String* InternTable::InsertStrongFromTransaction(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::InsertStrongFromTransaction(ObjPtr<mirror::String> s) {
DCHECK(!Runtime::Current()->IsActiveTransaction());
return InsertStrong(s);
}
-mirror::String* InternTable::InsertWeakFromTransaction(mirror::String* s) {
+
+ObjPtr<mirror::String> InternTable::InsertWeakFromTransaction(ObjPtr<mirror::String> s) {
DCHECK(!Runtime::Current()->IsActiveTransaction());
return InsertWeak(s);
}
-void InternTable::RemoveStrongFromTransaction(mirror::String* s) {
+
+void InternTable::RemoveStrongFromTransaction(ObjPtr<mirror::String> s) {
DCHECK(!Runtime::Current()->IsActiveTransaction());
RemoveStrong(s);
}
-void InternTable::RemoveWeakFromTransaction(mirror::String* s) {
+
+void InternTable::RemoveWeakFromTransaction(ObjPtr<mirror::String> s) {
DCHECK(!Runtime::Current()->IsActiveTransaction());
RemoveWeak(s);
}
@@ -203,7 +206,9 @@
Locks::intern_table_lock_->ExclusiveLock(self);
}
-mirror::String* InternTable::Insert(mirror::String* s, bool is_strong, bool holding_locks) {
+ObjPtr<mirror::String> InternTable::Insert(ObjPtr<mirror::String> s,
+ bool is_strong,
+ bool holding_locks) {
if (s == nullptr) {
return nullptr;
}
@@ -222,7 +227,7 @@
}
}
// Check the strong table for a match.
- mirror::String* strong = LookupStrongLocked(s);
+ ObjPtr<mirror::String> strong = LookupStrongLocked(s);
if (strong != nullptr) {
return strong;
}
@@ -244,7 +249,7 @@
CHECK(self->GetWeakRefAccessEnabled());
}
// There is no match in the strong table, check the weak table.
- mirror::String* weak = LookupWeakLocked(s);
+ ObjPtr<mirror::String> weak = LookupWeakLocked(s);
if (weak != nullptr) {
if (is_strong) {
// A match was found in the weak table. Promote to the strong table.
@@ -257,11 +262,11 @@
return is_strong ? InsertStrong(s) : InsertWeak(s);
}
-mirror::String* InternTable::InternStrong(int32_t utf16_length, const char* utf8_data) {
+ObjPtr<mirror::String> InternTable::InternStrong(int32_t utf16_length, const char* utf8_data) {
DCHECK(utf8_data != nullptr);
Thread* self = Thread::Current();
// Try to avoid allocation.
- mirror::String* s = LookupStrong(self, utf16_length, utf8_data);
+ ObjPtr<mirror::String> s = LookupStrong(self, utf16_length, utf8_data);
if (s != nullptr) {
return s;
}
@@ -269,25 +274,25 @@
self, utf16_length, utf8_data));
}
-mirror::String* InternTable::InternStrong(const char* utf8_data) {
+ObjPtr<mirror::String> InternTable::InternStrong(const char* utf8_data) {
DCHECK(utf8_data != nullptr);
return InternStrong(mirror::String::AllocFromModifiedUtf8(Thread::Current(), utf8_data));
}
-mirror::String* InternTable::InternStrongImageString(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::InternStrongImageString(ObjPtr<mirror::String> s) {
// May be holding the heap bitmap lock.
return Insert(s, true, true);
}
-mirror::String* InternTable::InternStrong(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::InternStrong(ObjPtr<mirror::String> s) {
return Insert(s, true, false);
}
-mirror::String* InternTable::InternWeak(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::InternWeak(ObjPtr<mirror::String> s) {
return Insert(s, false, false);
}
-bool InternTable::ContainsWeak(mirror::String* s) {
+bool InternTable::ContainsWeak(ObjPtr<mirror::String> s) {
return LookupWeak(Thread::Current(), s) == s;
}
@@ -314,7 +319,7 @@
if (kIsDebugBuild) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
- return static_cast<size_t>(root.Read()->GetHashCode());
+ return static_cast<size_t>(root.Read<kWithoutReadBarrier>()->GetHashCode());
}
bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
@@ -322,7 +327,7 @@
if (kIsDebugBuild) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
- return a.Read()->Equals(b.Read());
+ return a.Read<kWithoutReadBarrier>()->Equals(b.Read<kWithoutReadBarrier>());
}
bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
@@ -330,7 +335,7 @@
if (kIsDebugBuild) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
- mirror::String* a_string = a.Read();
+ ObjPtr<mirror::String> a_string = a.Read<kWithoutReadBarrier>();
uint32_t a_length = static_cast<uint32_t>(a_string->GetLength());
if (a_length != b.GetUtf16Length()) {
return false;
@@ -392,7 +397,7 @@
return table_to_write->WriteToMemory(ptr);
}
-void InternTable::Table::Remove(mirror::String* s) {
+void InternTable::Table::Remove(ObjPtr<mirror::String> s) {
for (UnorderedSet& table : tables_) {
auto it = table.Find(GcRoot<mirror::String>(s));
if (it != table.end()) {
@@ -403,7 +408,7 @@
LOG(FATAL) << "Attempting to remove non-interned string " << s->ToModifiedUtf8();
}
-mirror::String* InternTable::Table::Find(mirror::String* s) {
+ObjPtr<mirror::String> InternTable::Table::Find(ObjPtr<mirror::String> s) {
Locks::intern_table_lock_->AssertHeld(Thread::Current());
for (UnorderedSet& table : tables_) {
auto it = table.Find(GcRoot<mirror::String>(s));
@@ -414,7 +419,7 @@
return nullptr;
}
-mirror::String* InternTable::Table::Find(const Utf8String& string) {
+ObjPtr<mirror::String> InternTable::Table::Find(const Utf8String& string) {
Locks::intern_table_lock_->AssertHeld(Thread::Current());
for (UnorderedSet& table : tables_) {
auto it = table.Find(string);
@@ -429,7 +434,7 @@
tables_.push_back(UnorderedSet());
}
-void InternTable::Table::Insert(mirror::String* s) {
+void InternTable::Table::Insert(ObjPtr<mirror::String> s) {
// Always insert the last table, the image tables are before and we avoid inserting into these
// to prevent dirty pages.
DCHECK(!tables_.empty());
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index 30ff55d..acb2067 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -57,43 +57,44 @@
InternTable();
// Interns a potentially new string in the 'strong' table. May cause thread suspension.
- mirror::String* InternStrong(int32_t utf16_length, const char* utf8_data)
+ ObjPtr<mirror::String> InternStrong(int32_t utf16_length, const char* utf8_data)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
// Only used by image writer. Special version that may not cause thread suspension since the GC
// cannot be running while we are doing image writing. Maybe be called while while holding a
// lock since there will not be thread suspension.
- mirror::String* InternStrongImageString(mirror::String* s)
+ ObjPtr<mirror::String> InternStrongImageString(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_);
// Interns a potentially new string in the 'strong' table. May cause thread suspension.
- mirror::String* InternStrong(const char* utf8_data) REQUIRES_SHARED(Locks::mutator_lock_)
+ ObjPtr<mirror::String> InternStrong(const char* utf8_data) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
// Interns a potentially new string in the 'strong' table. May cause thread suspension.
- mirror::String* InternStrong(mirror::String* s) REQUIRES_SHARED(Locks::mutator_lock_)
+ ObjPtr<mirror::String> InternStrong(ObjPtr<mirror::String> s)
+ REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
// Interns a potentially new string in the 'weak' table. May cause thread suspension.
- mirror::String* InternWeak(mirror::String* s) REQUIRES_SHARED(Locks::mutator_lock_)
+ ObjPtr<mirror::String> InternWeak(ObjPtr<mirror::String> s) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
void SweepInternTableWeaks(IsMarkedVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::intern_table_lock_);
- bool ContainsWeak(mirror::String* s) REQUIRES_SHARED(Locks::mutator_lock_)
+ bool ContainsWeak(ObjPtr<mirror::String> s) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::intern_table_lock_);
// Lookup a strong intern, returns null if not found.
- mirror::String* LookupStrong(Thread* self, mirror::String* s)
+ ObjPtr<mirror::String> LookupStrong(Thread* self, ObjPtr<mirror::String> s)
REQUIRES(!Locks::intern_table_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- mirror::String* LookupStrong(Thread* self, uint32_t utf16_length, const char* utf8_data)
+ ObjPtr<mirror::String> LookupStrong(Thread* self, uint32_t utf16_length, const char* utf8_data)
REQUIRES(!Locks::intern_table_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
// Lookup a weak intern, returns null if not found.
- mirror::String* LookupWeak(Thread* self, mirror::String* s)
+ ObjPtr<mirror::String> LookupWeak(Thread* self, ObjPtr<mirror::String> s)
REQUIRES(!Locks::intern_table_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -181,13 +182,13 @@
class Table {
public:
Table();
- mirror::String* Find(mirror::String* s) REQUIRES_SHARED(Locks::mutator_lock_)
+ ObjPtr<mirror::String> Find(ObjPtr<mirror::String> s) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::intern_table_lock_);
- mirror::String* Find(const Utf8String& string) REQUIRES_SHARED(Locks::mutator_lock_)
+ ObjPtr<mirror::String> Find(const Utf8String& string) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::intern_table_lock_);
- void Insert(mirror::String* s) REQUIRES_SHARED(Locks::mutator_lock_)
+ void Insert(ObjPtr<mirror::String> s) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::intern_table_lock_);
- void Remove(mirror::String* s)
+ void Remove(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
void VisitRoots(RootVisitor* visitor)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
@@ -221,30 +222,30 @@
// Insert if non null, otherwise return null. Must be called holding the mutator lock.
// If holding_locks is true, then we may also hold other locks. If holding_locks is true, then we
// require GC is not running since it is not safe to wait while holding locks.
- mirror::String* Insert(mirror::String* s, bool is_strong, bool holding_locks)
+ ObjPtr<mirror::String> Insert(ObjPtr<mirror::String> s, bool is_strong, bool holding_locks)
REQUIRES(!Locks::intern_table_lock_) REQUIRES_SHARED(Locks::mutator_lock_);
- mirror::String* LookupStrongLocked(mirror::String* s)
+ ObjPtr<mirror::String> LookupStrongLocked(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- mirror::String* LookupWeakLocked(mirror::String* s)
+ ObjPtr<mirror::String> LookupWeakLocked(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- mirror::String* InsertStrong(mirror::String* s)
+ ObjPtr<mirror::String> InsertStrong(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- mirror::String* InsertWeak(mirror::String* s)
+ ObjPtr<mirror::String> InsertWeak(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- void RemoveStrong(mirror::String* s)
+ void RemoveStrong(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- void RemoveWeak(mirror::String* s)
+ void RemoveWeak(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
// Transaction rollback access.
- mirror::String* InsertStrongFromTransaction(mirror::String* s)
+ ObjPtr<mirror::String> InsertStrongFromTransaction(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- mirror::String* InsertWeakFromTransaction(mirror::String* s)
+ ObjPtr<mirror::String> InsertWeakFromTransaction(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- void RemoveStrongFromTransaction(mirror::String* s)
+ void RemoveStrongFromTransaction(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
- void RemoveWeakFromTransaction(mirror::String* s)
+ void RemoveWeakFromTransaction(ObjPtr<mirror::String> s)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
size_t AddTableFromMemoryLocked(const uint8_t* ptr)
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index 74cec57..b91d946 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -193,22 +193,22 @@
ASSERT_NE(foo.Get(), bar.Get());
ASSERT_NE(foo.Get(), foobar.Get());
ASSERT_NE(bar.Get(), foobar.Get());
- mirror::String* lookup_foo = intern_table.LookupStrong(soa.Self(), 3, "foo");
- EXPECT_EQ(lookup_foo, foo.Get());
- mirror::String* lookup_bar = intern_table.LookupStrong(soa.Self(), 3, "bar");
- EXPECT_EQ(lookup_bar, bar.Get());
- mirror::String* lookup_foobar = intern_table.LookupStrong(soa.Self(), 6, "foobar");
- EXPECT_EQ(lookup_foobar, foobar.Get());
- mirror::String* lookup_foox = intern_table.LookupStrong(soa.Self(), 4, "foox");
+ ObjPtr<mirror::String> lookup_foo = intern_table.LookupStrong(soa.Self(), 3, "foo");
+ EXPECT_OBJ_PTR_EQ(lookup_foo, foo.Get());
+ ObjPtr<mirror::String> lookup_bar = intern_table.LookupStrong(soa.Self(), 3, "bar");
+ EXPECT_OBJ_PTR_EQ(lookup_bar, bar.Get());
+ ObjPtr<mirror::String> lookup_foobar = intern_table.LookupStrong(soa.Self(), 6, "foobar");
+ EXPECT_OBJ_PTR_EQ(lookup_foobar, foobar.Get());
+ ObjPtr<mirror::String> lookup_foox = intern_table.LookupStrong(soa.Self(), 4, "foox");
EXPECT_TRUE(lookup_foox == nullptr);
- mirror::String* lookup_fooba = intern_table.LookupStrong(soa.Self(), 5, "fooba");
+ ObjPtr<mirror::String> lookup_fooba = intern_table.LookupStrong(soa.Self(), 5, "fooba");
EXPECT_TRUE(lookup_fooba == nullptr);
- mirror::String* lookup_foobaR = intern_table.LookupStrong(soa.Self(), 6, "foobaR");
+ ObjPtr<mirror::String> lookup_foobaR = intern_table.LookupStrong(soa.Self(), 6, "foobaR");
EXPECT_TRUE(lookup_foobaR == nullptr);
// Try a hash conflict.
ASSERT_EQ(ComputeUtf16HashFromModifiedUtf8("foobar", 6),
ComputeUtf16HashFromModifiedUtf8("foobbS", 6));
- mirror::String* lookup_foobbS = intern_table.LookupStrong(soa.Self(), 6, "foobbS");
+ ObjPtr<mirror::String> lookup_foobbS = intern_table.LookupStrong(soa.Self(), 6, "foobbS");
EXPECT_TRUE(lookup_foobbS == nullptr);
}
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 2e00770..a32c800 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -35,8 +35,17 @@
namespace art {
namespace interpreter {
-static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& shorty,
- Object* receiver, uint32_t* args, JValue* result)
+ALWAYS_INLINE static ObjPtr<mirror::Object> ObjArg(uint32_t arg)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return ObjPtr<mirror::Object>(reinterpret_cast<mirror::Object*>(arg));
+}
+
+static void InterpreterJni(Thread* self,
+ ArtMethod* method,
+ const StringPiece& shorty,
+ ObjPtr<mirror::Object> receiver,
+ uint32_t* args,
+ JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
// TODO: The following enters JNI code using a typedef-ed function rather than the JNI compiler,
// it should be removed and JNI compiled stubs used instead.
@@ -52,7 +61,7 @@
ScopedThreadStateChange tsc(self, kNative);
jresult = fn(soa.Env(), klass.get());
}
- result->SetL(soa.Decode<Object>(jresult));
+ result->SetL(soa.Decode<mirror::Object>(jresult));
} else if (shorty == "V") {
typedef void (fntype)(JNIEnv*, jclass);
fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
@@ -87,14 +96,13 @@
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedLocalRef<jobject> arg0(soa.Env(),
- soa.AddLocalReference<jobject>(
- reinterpret_cast<Object*>(args[0])));
+ soa.AddLocalReference<jobject>(ObjArg(args[0])));
jobject jresult;
{
ScopedThreadStateChange tsc(self, kNative);
jresult = fn(soa.Env(), klass.get(), arg0.get());
}
- result->SetL(soa.Decode<Object>(jresult));
+ result->SetL(soa.Decode<mirror::Object>(jresult));
} else if (shorty == "IIZ") {
typedef jint (fntype)(JNIEnv*, jclass, jint, jboolean);
fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
@@ -109,8 +117,7 @@
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedLocalRef<jobject> arg0(soa.Env(),
- soa.AddLocalReference<jobject>(
- reinterpret_cast<Object*>(args[0])));
+ soa.AddLocalReference<jobject>(ObjArg(args[0])));
ScopedThreadStateChange tsc(self, kNative);
result->SetI(fn(soa.Env(), klass.get(), arg0.get(), args[1]));
} else if (shorty == "SIZ") {
@@ -134,11 +141,9 @@
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedLocalRef<jobject> arg0(soa.Env(),
- soa.AddLocalReference<jobject>(
- reinterpret_cast<Object*>(args[0])));
+ soa.AddLocalReference<jobject>(ObjArg(args[0])));
ScopedLocalRef<jobject> arg1(soa.Env(),
- soa.AddLocalReference<jobject>(
- reinterpret_cast<Object*>(args[1])));
+ soa.AddLocalReference<jobject>(ObjArg(args[1])));
ScopedThreadStateChange tsc(self, kNative);
result->SetZ(fn(soa.Env(), klass.get(), arg0.get(), arg1.get()));
} else if (shorty == "ZILL") {
@@ -147,11 +152,9 @@
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedLocalRef<jobject> arg1(soa.Env(),
- soa.AddLocalReference<jobject>(
- reinterpret_cast<Object*>(args[1])));
+ soa.AddLocalReference<jobject>(ObjArg(args[1])));
ScopedLocalRef<jobject> arg2(soa.Env(),
- soa.AddLocalReference<jobject>(
- reinterpret_cast<Object*>(args[2])));
+ soa.AddLocalReference<jobject>(ObjArg(args[2])));
ScopedThreadStateChange tsc(self, kNative);
result->SetZ(fn(soa.Env(), klass.get(), args[0], arg1.get(), arg2.get()));
} else if (shorty == "VILII") {
@@ -160,8 +163,7 @@
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedLocalRef<jobject> arg1(soa.Env(),
- soa.AddLocalReference<jobject>(
- reinterpret_cast<Object*>(args[1])));
+ soa.AddLocalReference<jobject>(ObjArg(args[1])));
ScopedThreadStateChange tsc(self, kNative);
fn(soa.Env(), klass.get(), args[0], arg1.get(), args[2], args[3]);
} else if (shorty == "VLILII") {
@@ -170,11 +172,9 @@
ScopedLocalRef<jclass> klass(soa.Env(),
soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
ScopedLocalRef<jobject> arg0(soa.Env(),
- soa.AddLocalReference<jobject>(
- reinterpret_cast<Object*>(args[0])));
+ soa.AddLocalReference<jobject>(ObjArg(args[0])));
ScopedLocalRef<jobject> arg2(soa.Env(),
- soa.AddLocalReference<jobject>(
- reinterpret_cast<Object*>(args[2])));
+ soa.AddLocalReference<jobject>(ObjArg(args[2])));
ScopedThreadStateChange tsc(self, kNative);
fn(soa.Env(), klass.get(), arg0.get(), args[1], arg2.get(), args[3], args[4]);
} else {
@@ -192,7 +192,7 @@
ScopedThreadStateChange tsc(self, kNative);
jresult = fn(soa.Env(), rcvr.get());
}
- result->SetL(soa.Decode<Object>(jresult));
+ result->SetL(soa.Decode<mirror::Object>(jresult));
} else if (shorty == "V") {
typedef void (fntype)(JNIEnv*, jobject);
fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni());
@@ -206,14 +206,13 @@
ScopedLocalRef<jobject> rcvr(soa.Env(),
soa.AddLocalReference<jobject>(receiver));
ScopedLocalRef<jobject> arg0(soa.Env(),
- soa.AddLocalReference<jobject>(
- reinterpret_cast<Object*>(args[0])));
+ soa.AddLocalReference<jobject>(ObjArg(args[0])));
jobject jresult;
{
ScopedThreadStateChange tsc(self, kNative);
jresult = fn(soa.Env(), rcvr.get(), arg0.get());
}
- result->SetL(soa.Decode<Object>(jresult));
+ result->SetL(soa.Decode<mirror::Object>(jresult));
ScopedThreadStateChange tsc(self, kNative);
} else if (shorty == "III") {
typedef jint (fntype)(JNIEnv*, jobject, jint, jint);
@@ -312,7 +311,7 @@
} else {
// Mterp didn't like that instruction. Single-step it with the reference interpreter.
result_register = ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame,
- result_register, true);
+ result_register, true);
if (shadow_frame.GetDexPC() == DexFile::kDexNoIndex) {
// Single-stepped a return or an exception not handled locally. Return to caller.
return result_register;
@@ -354,8 +353,11 @@
}
}
-void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receiver,
- uint32_t* args, JValue* result,
+void EnterInterpreterFromInvoke(Thread* self,
+ ArtMethod* method,
+ ObjPtr<mirror::Object> receiver,
+ uint32_t* args,
+ JValue* result,
bool stay_in_interpreter) {
DCHECK_EQ(self, Thread::Current());
bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
@@ -393,7 +395,7 @@
size_t cur_reg = num_regs - num_ins;
if (!method->IsStatic()) {
CHECK(receiver != nullptr);
- shadow_frame->SetVRegReference(cur_reg, receiver);
+ shadow_frame->SetVRegReference(cur_reg, receiver.Ptr());
++cur_reg;
}
uint32_t shorty_len = 0;
@@ -402,8 +404,9 @@
DCHECK_LT(shorty_pos + 1, shorty_len);
switch (shorty[shorty_pos + 1]) {
case 'L': {
- Object* o = reinterpret_cast<StackReference<Object>*>(&args[arg_pos])->AsMirrorPtr();
- shadow_frame->SetVRegReference(cur_reg, o);
+ ObjPtr<mirror::Object> o =
+ reinterpret_cast<StackReference<mirror::Object>*>(&args[arg_pos])->AsMirrorPtr();
+ shadow_frame->SetVRegReference(cur_reg, o.Ptr());
break;
}
case 'J': case 'D': {
@@ -442,7 +445,7 @@
// references pointers due to moving GC.
args = shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1);
if (!Runtime::Current()->IsStarted()) {
- UnstartedRuntime::Jni(self, method, receiver, args, result);
+ UnstartedRuntime::Jni(self, method, receiver.Ptr(), args, result);
} else {
InterpreterJni(self, method, shorty, receiver, args, result);
}
@@ -539,7 +542,7 @@
if (kIsDebugBuild) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
// This is a suspend point. But it's ok since value has been set into shadow_frame.
- mirror::Class* klass = class_linker->ResolveType(
+ ObjPtr<mirror::Class> klass = class_linker->ResolveType(
instr->VRegB_21c(), shadow_frame->GetMethod());
DCHECK(klass->IsStringClass());
}
@@ -582,8 +585,10 @@
return Execute(self, code_item, *shadow_frame, JValue());
}
-void ArtInterpreterToInterpreterBridge(Thread* self, const DexFile::CodeItem* code_item,
- ShadowFrame* shadow_frame, JValue* result) {
+void ArtInterpreterToInterpreterBridge(Thread* self,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame* shadow_frame,
+ JValue* result) {
bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) {
ThrowStackOverflowError(self);
@@ -595,10 +600,10 @@
// Ensure static methods are initialized.
const bool is_static = method->IsStatic();
if (is_static) {
- mirror::Class* declaring_class = method->GetDeclaringClass();
+ ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
if (UNLIKELY(!declaring_class->IsInitialized())) {
StackHandleScope<1> hs(self);
- HandleWrapper<Class> h_declaring_class(hs.NewHandleWrapper(&declaring_class));
+ HandleWrapperObjPtr<mirror::Class> h_declaring_class(hs.NewHandleWrapper(&declaring_class));
if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(
self, h_declaring_class, true, true))) {
DCHECK(self->IsExceptionPending());
@@ -615,9 +620,9 @@
// We don't expect to be asked to interpret native code (which is entered via a JNI compiler
// generated stub) except during testing and image writing.
CHECK(!Runtime::Current()->IsStarted());
- Object* receiver = is_static ? nullptr : shadow_frame->GetVRegReference(0);
+ ObjPtr<mirror::Object> receiver = is_static ? nullptr : shadow_frame->GetVRegReference(0);
uint32_t* args = shadow_frame->GetVRegArgs(is_static ? 0 : 1);
- UnstartedRuntime::Jni(self, shadow_frame->GetMethod(), receiver, args, result);
+ UnstartedRuntime::Jni(self, shadow_frame->GetMethod(), receiver.Ptr(), args, result);
}
self->PopShadowFrame();
diff --git a/runtime/interpreter/interpreter.h b/runtime/interpreter/interpreter.h
index 38ce851..65cfade 100644
--- a/runtime/interpreter/interpreter.h
+++ b/runtime/interpreter/interpreter.h
@@ -19,6 +19,7 @@
#include "base/mutex.h"
#include "dex_file.h"
+#include "obj_ptr.h"
namespace art {
namespace mirror {
@@ -36,7 +37,9 @@
// The optional stay_in_interpreter parameter (false by default) can be used by clients to
// explicitly force interpretation in the remaining path that implements method invocation.
extern void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method,
- mirror::Object* receiver, uint32_t* args, JValue* result,
+ ObjPtr<mirror::Object> receiver,
+ uint32_t* args,
+ JValue* result,
bool stay_in_interpreter = false)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index a0d712e..4fc92a3 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -27,6 +27,7 @@
#include "method_handles-inl.h"
#include "mirror/array-inl.h"
#include "mirror/class.h"
+#include "mirror/emulated_stack_frame.h"
#include "mirror/method_handle_impl.h"
#include "reflection.h"
#include "reflection-inl.h"
@@ -42,6 +43,59 @@
ThrowNullPointerExceptionFromDexPC();
}
+template<Primitive::Type field_type>
+static ALWAYS_INLINE void DoFieldGetCommon(Thread* self,
+ const ShadowFrame& shadow_frame,
+ ObjPtr<mirror::Object>& obj,
+ ArtField* field,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ field->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
+
+ // Report this field access to instrumentation if needed.
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasFieldReadListeners())) {
+ StackHandleScope<1> hs(self);
+ // Wrap in handle wrapper in case the listener does thread suspension.
+ HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj));
+ ObjPtr<mirror::Object> this_object;
+ if (!field->IsStatic()) {
+ this_object = obj;
+ }
+ instrumentation->FieldReadEvent(self,
+ this_object.Ptr(),
+ shadow_frame.GetMethod(),
+ shadow_frame.GetDexPC(),
+ field);
+ }
+
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ result->SetZ(field->GetBoolean(obj));
+ break;
+ case Primitive::kPrimByte:
+ result->SetB(field->GetByte(obj));
+ break;
+ case Primitive::kPrimChar:
+ result->SetC(field->GetChar(obj));
+ break;
+ case Primitive::kPrimShort:
+ result->SetS(field->GetShort(obj));
+ break;
+ case Primitive::kPrimInt:
+ result->SetI(field->GetInt(obj));
+ break;
+ case Primitive::kPrimLong:
+ result->SetJ(field->GetLong(obj));
+ break;
+ case Primitive::kPrimNot:
+ result->SetL(field->GetObject(obj));
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ UNREACHABLE();
+ }
+}
+
template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,
uint16_t inst_data) {
@@ -54,7 +108,7 @@
CHECK(self->IsExceptionPending());
return false;
}
- ObjPtr<Object> obj;
+ ObjPtr<mirror::Object> obj;
if (is_static) {
obj = f->GetDeclaringClass();
} else {
@@ -64,45 +118,31 @@
return false;
}
}
- f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
- // Report this field access to instrumentation if needed.
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
- if (UNLIKELY(instrumentation->HasFieldReadListeners())) {
- StackHandleScope<1> hs(self);
- // Wrap in handle wrapper in case the listener does thread suspension.
- HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj));
- ObjPtr<Object> this_object;
- if (!f->IsStatic()) {
- this_object = obj;
- }
- instrumentation->FieldReadEvent(self,
- this_object.Ptr(),
- shadow_frame.GetMethod(),
- shadow_frame.GetDexPC(),
- f);
- }
+
+ JValue result;
+ DoFieldGetCommon<field_type>(self, shadow_frame, obj, f, &result);
uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
switch (field_type) {
case Primitive::kPrimBoolean:
- shadow_frame.SetVReg(vregA, f->GetBoolean(obj));
+ shadow_frame.SetVReg(vregA, result.GetZ());
break;
case Primitive::kPrimByte:
- shadow_frame.SetVReg(vregA, f->GetByte(obj));
+ shadow_frame.SetVReg(vregA, result.GetB());
break;
case Primitive::kPrimChar:
- shadow_frame.SetVReg(vregA, f->GetChar(obj));
+ shadow_frame.SetVReg(vregA, result.GetC());
break;
case Primitive::kPrimShort:
- shadow_frame.SetVReg(vregA, f->GetShort(obj));
+ shadow_frame.SetVReg(vregA, result.GetS());
break;
case Primitive::kPrimInt:
- shadow_frame.SetVReg(vregA, f->GetInt(obj));
+ shadow_frame.SetVReg(vregA, result.GetI());
break;
case Primitive::kPrimLong:
- shadow_frame.SetVRegLong(vregA, f->GetLong(obj));
+ shadow_frame.SetVRegLong(vregA, result.GetJ());
break;
case Primitive::kPrimNot:
- shadow_frame.SetVRegReference(vregA, f->GetObject(obj).Ptr());
+ shadow_frame.SetVRegReference(vregA, result.GetL());
break;
default:
LOG(FATAL) << "Unreachable: " << field_type;
@@ -143,11 +183,53 @@
#undef EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL
#undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL
+// Helper for getters in invoke-polymorphic.
+inline static void DoFieldGetForInvokePolymorphic(Thread* self,
+ const ShadowFrame& shadow_frame,
+ ObjPtr<mirror::Object>& obj,
+ ArtField* field,
+ Primitive::Type field_type,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ DoFieldGetCommon<Primitive::kPrimBoolean>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimByte:
+ DoFieldGetCommon<Primitive::kPrimByte>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimChar:
+ DoFieldGetCommon<Primitive::kPrimChar>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimShort:
+ DoFieldGetCommon<Primitive::kPrimShort>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimInt:
+ DoFieldGetCommon<Primitive::kPrimInt>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimLong:
+ DoFieldGetCommon<Primitive::kPrimLong>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimFloat:
+ DoFieldGetCommon<Primitive::kPrimInt>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimDouble:
+ DoFieldGetCommon<Primitive::kPrimLong>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimNot:
+ DoFieldGetCommon<Primitive::kPrimNot>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ UNREACHABLE();
+ }
+}
+
// Handles iget-quick, iget-wide-quick and iget-object-quick instructions.
// Returns true on success, otherwise throws an exception and returns false.
template<Primitive::Type field_type>
bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) {
- Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
if (UNLIKELY(obj == nullptr)) {
// We lost the reference to the field index so we cannot get a more
// precised exception message.
@@ -163,8 +245,14 @@
field_offset.Uint32Value());
DCHECK(f != nullptr);
DCHECK(!f->IsStatic());
- instrumentation->FieldReadEvent(Thread::Current(), obj, shadow_frame.GetMethod(),
- shadow_frame.GetDexPC(), f);
+ StackHandleScope<1> hs(Thread::Current());
+ // Save obj in case the instrumentation event has thread suspension.
+ HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&obj);
+ instrumentation->FieldReadEvent(Thread::Current(),
+ obj.Ptr(),
+ shadow_frame.GetMethod(),
+ shadow_frame.GetDexPC(),
+ f);
}
// Note: iget-x-quick instructions are only for non-volatile fields.
const uint32_t vregA = inst->VRegA_22c(inst_data);
@@ -244,32 +332,14 @@
return field_value;
}
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check,
- bool transaction_active>
-bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction* inst,
- uint16_t inst_data) {
- bool do_assignability_check = do_access_check;
- bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
- uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
- ArtField* f =
- FindFieldFromCode<find_type, do_access_check>(field_idx, shadow_frame.GetMethod(), self,
- Primitive::ComponentSize(field_type));
- if (UNLIKELY(f == nullptr)) {
- CHECK(self->IsExceptionPending());
- return false;
- }
- ObjPtr<Object> obj;
- if (is_static) {
- obj = f->GetDeclaringClass();
- } else {
- obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(f, false);
- return false;
- }
- }
+template<Primitive::Type field_type, bool do_assignability_check, bool transaction_active>
+static inline bool DoFieldPutCommon(Thread* self,
+ const ShadowFrame& shadow_frame,
+ ObjPtr<mirror::Object>& obj,
+ ArtField* f,
+ size_t vregA) REQUIRES_SHARED(Locks::mutator_lock_) {
f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
- uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+
// Report this field access to instrumentation if needed. Since we only have the offset of
// the field from the base of the object, we need to look for it first.
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
@@ -278,13 +348,14 @@
// Wrap in handle wrapper in case the listener does thread suspension.
HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj));
JValue field_value = GetFieldValue<field_type>(shadow_frame, vregA);
- ObjPtr<Object> this_object = f->IsStatic() ? nullptr : obj;
+ ObjPtr<mirror::Object> this_object = f->IsStatic() ? nullptr : obj;
instrumentation->FieldWriteEvent(self, this_object.Ptr(),
shadow_frame.GetMethod(),
shadow_frame.GetDexPC(),
f,
field_value);
}
+
switch (field_type) {
case Primitive::kPrimBoolean:
f->SetBoolean<transaction_active>(obj, shadow_frame.GetVReg(vregA));
@@ -305,14 +376,14 @@
f->SetLong<transaction_active>(obj, shadow_frame.GetVRegLong(vregA));
break;
case Primitive::kPrimNot: {
- Object* reg = shadow_frame.GetVRegReference(vregA);
+ ObjPtr<mirror::Object> reg = shadow_frame.GetVRegReference(vregA);
if (do_assignability_check && reg != nullptr) {
// FieldHelper::GetType can resolve classes, use a handle wrapper which will restore the
// object in the destructor.
- ObjPtr<Class> field_class;
+ ObjPtr<mirror::Class> field_class;
{
StackHandleScope<2> hs(self);
- HandleWrapper<mirror::Object> h_reg(hs.NewHandleWrapper(®));
+ HandleWrapperObjPtr<mirror::Object> h_reg(hs.NewHandleWrapper(®));
HandleWrapperObjPtr<mirror::Object> h_obj(hs.NewHandleWrapper(&obj));
field_class = f->GetType<true>();
}
@@ -337,6 +408,39 @@
return true;
}
+template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check,
+ bool transaction_active>
+bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction* inst,
+ uint16_t inst_data) {
+ const bool do_assignability_check = do_access_check;
+ bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
+ uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
+ ArtField* f =
+ FindFieldFromCode<find_type, do_access_check>(field_idx, shadow_frame.GetMethod(), self,
+ Primitive::ComponentSize(field_type));
+ if (UNLIKELY(f == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ return false;
+ }
+ ObjPtr<mirror::Object> obj;
+ if (is_static) {
+ obj = f->GetDeclaringClass();
+ } else {
+ obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ if (UNLIKELY(obj == nullptr)) {
+ ThrowNullPointerExceptionForFieldAccess(f, false);
+ return false;
+ }
+ }
+
+ uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+ return DoFieldPutCommon<field_type, do_assignability_check, transaction_active>(self,
+ shadow_frame,
+ obj,
+ f,
+ vregA);
+}
+
// Explicitly instantiate all DoFieldPut functions.
#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check, _transaction_active) \
template bool DoFieldPut<_find_type, _field_type, _do_check, _transaction_active>(Thread* self, \
@@ -369,9 +473,52 @@
#undef EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL
#undef EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL
+// Helper for setters in invoke-polymorphic.
+bool DoFieldPutForInvokePolymorphic(Thread* self,
+ ShadowFrame& shadow_frame,
+ ObjPtr<mirror::Object>& obj,
+ ArtField* field,
+ Primitive::Type field_type,
+ size_t vregA) REQUIRES_SHARED(Locks::mutator_lock_) {
+ static const bool kDoCheckAssignability = false;
+ static const bool kTransaction = false;
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ return DoFieldPutCommon<Primitive::kPrimBoolean, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimByte:
+ return DoFieldPutCommon<Primitive::kPrimByte, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimChar:
+ return DoFieldPutCommon<Primitive::kPrimChar, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimShort:
+ return DoFieldPutCommon<Primitive::kPrimShort, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimInt:
+ return DoFieldPutCommon<Primitive::kPrimInt, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimLong:
+ return DoFieldPutCommon<Primitive::kPrimLong, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimFloat:
+ return DoFieldPutCommon<Primitive::kPrimInt, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimDouble:
+ return DoFieldPutCommon<Primitive::kPrimLong, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimNot:
+ return DoFieldPutCommon<Primitive::kPrimNot, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ UNREACHABLE();
+ }
+}
+
template<Primitive::Type field_type, bool transaction_active>
bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) {
- Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
if (UNLIKELY(obj == nullptr)) {
// We lost the reference to the field index so we cannot get a more
// precised exception message.
@@ -389,8 +536,15 @@
DCHECK(f != nullptr);
DCHECK(!f->IsStatic());
JValue field_value = GetFieldValue<field_type>(shadow_frame, vregA);
- instrumentation->FieldWriteEvent(Thread::Current(), obj, shadow_frame.GetMethod(),
- shadow_frame.GetDexPC(), f, field_value);
+ StackHandleScope<1> hs(Thread::Current());
+ // Save obj in case the instrumentation event has thread suspension.
+ HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&obj);
+ instrumentation->FieldWriteEvent(Thread::Current(),
+ obj.Ptr(),
+ shadow_frame.GetMethod(),
+ shadow_frame.GetDexPC(),
+ f,
+ field_value);
}
// Note: iput-x-quick instructions are only for non-volatile fields.
switch (field_type) {
@@ -516,15 +670,19 @@
ShadowFrame& shadow_frame,
JValue* result,
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- uint32_t vregC) ALWAYS_INLINE;
+ uint32_t vregC,
+ const MethodHandleKind handle_kind) ALWAYS_INLINE;
-REQUIRES_SHARED(Locks::mutator_lock_)
+template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
static inline bool DoCallTransform(ArtMethod* called_method,
Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
Thread* self,
ShadowFrame& shadow_frame,
Handle<mirror::MethodHandleImpl> receiver,
- JValue* result) ALWAYS_INLINE;
+ JValue* result,
+ uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ uint32_t vregC) ALWAYS_INLINE;
REQUIRES_SHARED(Locks::mutator_lock_)
inline void PerformCall(Thread* self,
@@ -554,7 +712,7 @@
ArtMethod* method = shadow_frame->GetMethod();
// Ensure static methods are initialized.
if (method->IsStatic()) {
- mirror::Class* declaringClass = method->GetDeclaringClass();
+ ObjPtr<mirror::Class> declaringClass = method->GetDeclaringClass();
if (UNLIKELY(!declaringClass->IsInitialized())) {
self->PushShadowFrame(shadow_frame);
StackHandleScope<1> hs(self);
@@ -587,7 +745,7 @@
uint16_t this_obj_vreg,
JValue result)
REQUIRES_SHARED(Locks::mutator_lock_) {
- Object* existing = shadow_frame->GetVRegReference(this_obj_vreg);
+ ObjPtr<mirror::Object> existing = shadow_frame->GetVRegReference(this_obj_vreg);
if (existing == nullptr) {
// If it's null, we come from compiled code that was deoptimized. Nothing to do,
// as the compiler verified there was no alias.
@@ -607,15 +765,33 @@
}
}
+inline static bool IsInvokeExact(const DexFile& dex_file, int invoke_method_idx) {
+ // This check uses string comparison as it needs less code and data
+ // to do than fetching the associated ArtMethod from the DexCache
+ // and checking against ArtMethods in the well known classes. The
+ // verifier needs to perform a more rigorous check.
+ const char* method_name = dex_file.GetMethodName(dex_file.GetMethodId(invoke_method_idx));
+ bool is_invoke_exact = (0 == strcmp(method_name, "invokeExact"));
+ DCHECK(is_invoke_exact || (0 == strcmp(method_name, "invoke")));
+ return is_invoke_exact;
+}
+
template<bool is_range, bool do_access_check>
- REQUIRES_SHARED(Locks::mutator_lock_)
-inline bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, uint16_t inst_data,
- JValue* result) {
+inline bool DoInvokePolymorphic(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
// Invoke-polymorphic instructions always take a receiver. i.e, they are never static.
const uint32_t vRegC = (is_range) ? inst->VRegC_4rcc() : inst->VRegC_45cc();
+ const int invoke_method_idx = (is_range) ? inst->VRegB_4rcc() : inst->VRegB_45cc();
- // The method_idx here is the name of the signature polymorphic method that
+ // Determine if this invocation is MethodHandle.invoke() or
+ // MethodHandle.invokeExact().
+ bool is_invoke_exact = IsInvokeExact(shadow_frame.GetMethod()->GetDeclaringClass()->GetDexFile(),
+ invoke_method_idx);
+
+ // The invoke_method_idx here is the name of the signature polymorphic method that
// was symbolically invoked in bytecode (say MethodHandle.invoke or MethodHandle.invokeExact)
// and not the method that we'll dispatch to in the end.
//
@@ -625,13 +801,12 @@
// that vRegC really is a reference type.
StackHandleScope<6> hs(self);
Handle<mirror::MethodHandleImpl> method_handle(hs.NewHandle(
- reinterpret_cast<mirror::MethodHandleImpl*>(shadow_frame.GetVRegReference(vRegC))));
+ ObjPtr<mirror::MethodHandleImpl>::DownCast(
+ MakeObjPtr(shadow_frame.GetVRegReference(vRegC)))));
if (UNLIKELY(method_handle.Get() == nullptr)) {
- const int method_idx = (is_range) ? inst->VRegB_4rcc() : inst->VRegB_45cc();
// Note that the invoke type is kVirtual here because a call to a signature
// polymorphic method is shaped like a virtual call at the bytecode level.
- ThrowNullPointerExceptionForMethodAccess(method_idx, InvokeType::kVirtual);
-
+ ThrowNullPointerExceptionForMethodAccess(invoke_method_idx, InvokeType::kVirtual);
result->SetJ(0);
return false;
}
@@ -657,20 +832,18 @@
return false;
}
- // Get the method we're actually invoking along with the kind of
- // invoke that is desired. We don't need to perform access checks at this
- // point because they would have been performed on our behalf at the point
- // of creation of the method handle.
- ArtMethod* called_method = method_handle->GetTargetMethod();
const MethodHandleKind handle_kind = method_handle->GetHandleKind();
Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType()));
- CHECK(called_method != nullptr);
CHECK(handle_type.Get() != nullptr);
+ if (UNLIKELY(is_invoke_exact && !callsite_type->IsExactMatch(handle_type.Get()))) {
+ ThrowWrongMethodTypeException(handle_type.Get(), callsite_type.Get());
+ return false;
+ }
uint32_t arg[Instruction::kMaxVarArgRegs] = {};
- uint32_t receiver_vregC = 0;
+ uint32_t first_src_reg = 0;
if (is_range) {
- receiver_vregC = (inst->VRegC_4rcc() + 1);
+ first_src_reg = (inst->VRegC_4rcc() + 1);
} else {
inst->GetVarArgs(arg, inst_data);
arg[0] = arg[1];
@@ -678,20 +851,22 @@
arg[2] = arg[3];
arg[3] = arg[4];
arg[4] = 0;
- receiver_vregC = arg[0];
+ first_src_reg = arg[0];
}
if (IsInvoke(handle_kind)) {
+ // Get the method we're actually invoking along with the kind of
+ // invoke that is desired. We don't need to perform access checks at this
+ // point because they would have been performed on our behalf at the point
+ // of creation of the method handle.
+ ArtMethod* called_method = method_handle->GetTargetMethod();
+ CHECK(called_method != nullptr);
+
if (handle_kind == kInvokeVirtual || handle_kind == kInvokeInterface) {
- mirror::Object* receiver = shadow_frame.GetVRegReference(receiver_vregC);
- mirror::Class* declaring_class = called_method->GetDeclaringClass();
- // Verify that _vRegC is an object reference and of the type expected by
- // the receiver.
- called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(
- called_method, kRuntimePointerSize);
- if (!VerifyObjectIsClass(receiver, declaring_class)) {
- return false;
- }
+ // TODO: Unfortunately, we have to postpone dynamic receiver based checks
+ // because the receiver might be cast or might come from an emulated stack
+ // frame, which means that it is unknown at this point. We perform these
+ // checks inside DoCallPolymorphic right before we do the actualy invoke.
} else if (handle_kind == kInvokeDirect) {
if (called_method->IsConstructor()) {
// TODO(narayan) : We need to handle the case where the target method is a
@@ -704,15 +879,15 @@
// constructor. It's a private method, and we've already access checked at
// the point of creating the handle.
} else if (handle_kind == kInvokeSuper) {
- mirror::Class* declaring_class = called_method->GetDeclaringClass();
+ ObjPtr<mirror::Class> declaring_class = called_method->GetDeclaringClass();
// Note that we're not dynamically dispatching on the type of the receiver
// here. We use the static type of the "receiver" object that we've
// recorded in the method handle's type, which will be the same as the
// special caller that was specified at the point of lookup.
- mirror::Class* referrer_class = handle_type->GetPTypes()->Get(0);
+ ObjPtr<mirror::Class> referrer_class = handle_type->GetPTypes()->Get(0);
if (!declaring_class->IsInterface()) {
- mirror::Class* super_class = referrer_class->GetSuperClass();
+ ObjPtr<mirror::Class> super_class = referrer_class->GetSuperClass();
uint16_t vtable_index = called_method->GetMethodIndex();
DCHECK(super_class != nullptr);
DCHECK(super_class->HasVTable());
@@ -729,12 +904,15 @@
}
if (handle_kind == kInvokeTransform) {
- return DoCallTransform(called_method,
- callsite_type,
- self,
- shadow_frame,
- method_handle /* receiver */,
- result);
+ return DoCallTransform<is_range>(called_method,
+ callsite_type,
+ handle_type,
+ self,
+ shadow_frame,
+ method_handle /* receiver */,
+ result,
+ arg,
+ first_src_reg);
} else {
return DoCallPolymorphic<is_range>(called_method,
callsite_type,
@@ -743,12 +921,42 @@
shadow_frame,
result,
arg,
- receiver_vregC);
+ first_src_reg,
+ handle_kind);
}
} else {
- // TODO(narayan): Implement field getters and setters.
- UNIMPLEMENTED(FATAL) << "Field references in method handles are not implemented yet.";
- return false;
+ DCHECK(!is_range);
+ ArtField* field = method_handle->GetTargetField();
+ Primitive::Type field_type = field->GetTypeAsPrimitiveType();;
+
+ if (!is_invoke_exact) {
+ // TODO(oth): conversion plumbing for invoke().
+ UNIMPLEMENTED(FATAL);
+ }
+
+ switch (handle_kind) {
+ case kInstanceGet: {
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(first_src_reg);
+ DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result);
+ return true;
+ }
+ case kInstancePut: {
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(first_src_reg);
+ return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, arg[1]);
+ }
+ case kStaticGet: {
+ ObjPtr<mirror::Object> obj = field->GetDeclaringClass();
+ DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result);
+ return true;
+ }
+ case kStaticPut: {
+ ObjPtr<mirror::Object> obj = field->GetDeclaringClass();
+ return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, arg[0]);
+ }
+ default:
+ LOG(FATAL) << "Unreachable: " << handle_kind;
+ UNREACHABLE();
+ }
}
}
@@ -845,7 +1053,8 @@
ShadowFrame& shadow_frame,
JValue* result,
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- uint32_t first_src_reg) {
+ uint32_t first_src_reg,
+ const MethodHandleKind handle_kind) {
// TODO(narayan): Wire in the String.init hacks.
// Compute method information.
@@ -878,6 +1087,8 @@
CREATE_SHADOW_FRAME(num_regs, &shadow_frame, called_method, /* dex pc */ 0);
ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
+ // Whether this polymorphic invoke was issued by a transformer method.
+ bool is_caller_transformer = false;
// Thread might be suspended during PerformArgumentConversions due to the
// allocations performed during boxing.
{
@@ -899,18 +1110,16 @@
// case, we'll have to unmarshal the EmulatedStackFrame into the
// new_shadow_frame and perform argument conversions on it.
if (IsCallerTransformer(callsite_type)) {
- // The emulated stack frame will be the first ahnd only argument
- // when we're coming through from a transformer.
- //
- // TODO(narayan): This should be a mirror::EmulatedStackFrame after that
- // type is introduced.
- ObjPtr<mirror::Object> emulated_stack_frame(
- shadow_frame.GetVRegReference(first_src_reg));
- if (!ConvertAndCopyArgumentsFromEmulatedStackFrame<is_range>(self,
- emulated_stack_frame,
- target_type,
- first_dest_reg,
- new_shadow_frame)) {
+ is_caller_transformer = true;
+ // The emulated stack frame is the first and only argument when we're coming
+ // through from a transformer.
+ ObjPtr<mirror::EmulatedStackFrame> emulated_stack_frame(
+ reinterpret_cast<mirror::EmulatedStackFrame*>(
+ shadow_frame.GetVRegReference(first_src_reg)));
+ if (!emulated_stack_frame->WriteToShadowFrame(self,
+ target_type,
+ first_dest_reg,
+ new_shadow_frame)) {
DCHECK(self->IsExceptionPending());
result->SetL(0);
return false;
@@ -930,20 +1139,51 @@
}
}
+ // See TODO in DoInvokePolymorphic : We need to perform this dynamic, receiver
+ // based dispatch right before we perform the actual call, because the
+ // receiver isn't known very early.
+ if (handle_kind == kInvokeVirtual || handle_kind == kInvokeInterface) {
+ ObjPtr<mirror::Object> receiver(new_shadow_frame->GetVRegReference(first_dest_reg));
+ ObjPtr<mirror::Class> declaring_class(called_method->GetDeclaringClass());
+ // Verify that _vRegC is an object reference and of the type expected by
+ // the receiver.
+ if (!VerifyObjectIsClass(receiver, declaring_class)) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+
+ called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(
+ called_method, kRuntimePointerSize);
+ }
+
PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
// TODO(narayan): Perform return value conversions.
+ // If the caller of this signature polymorphic method was a transformer,
+ // we need to copy the result back out to the emulated stack frame.
+ if (is_caller_transformer && !self->IsExceptionPending()) {
+ ObjPtr<mirror::EmulatedStackFrame> emulated_stack_frame(
+ reinterpret_cast<mirror::EmulatedStackFrame*>(
+ shadow_frame.GetVRegReference(first_src_reg)));
+
+ emulated_stack_frame->SetReturnValue(self, *result);
+ }
+
return !self->IsExceptionPending();
}
+template <bool is_range>
static inline bool DoCallTransform(ArtMethod* called_method,
Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
Thread* self,
ShadowFrame& shadow_frame,
Handle<mirror::MethodHandleImpl> receiver,
- JValue* result) {
- // This can be fixed, because the method we're calling here
+ JValue* result,
+ uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ uint32_t first_src_reg) {
+ // This can be fixed to two, because the method we're calling here
// (MethodHandle.transformInternal) doesn't have any locals and the signature
// is known :
//
@@ -963,18 +1203,33 @@
CREATE_SHADOW_FRAME(kNumRegsForTransform, &shadow_frame, called_method, /* dex pc */ 0);
ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
- // TODO(narayan): Perform argument conversions first (if this is an inexact invoke), and
- // then construct an argument list object that's passed through to the
- // method. Note that the ArgumentList reference is currently a nullptr.
- //
- // NOTE(narayan): If the caller is a transformer method (i.e, there is only
- // one argument and its type is EmulatedStackFrame), we can directly pass that
- // through without having to do any additional work.
- UNUSED(callsite_type);
+ StackHandleScope<1> hs(self);
+ MutableHandle<mirror::EmulatedStackFrame> sf(hs.NewHandle<mirror::EmulatedStackFrame>(nullptr));
+ if (IsCallerTransformer(callsite_type)) {
+ // If we're entering this transformer from another transformer, we can pass
+ // through the handle directly to the callee, instead of having to
+ // instantiate a new stack frame based on the shadow frame.
+ sf.Assign(reinterpret_cast<mirror::EmulatedStackFrame*>(
+ shadow_frame.GetVRegReference(first_src_reg)));
+ } else {
+ sf.Assign(mirror::EmulatedStackFrame::CreateFromShadowFrameAndArgs<is_range>(
+ self,
+ callsite_type,
+ callee_type,
+ shadow_frame,
+ first_src_reg,
+ arg));
+
+ // Something went wrong while creating the emulated stack frame, we should
+ // throw the pending exception.
+ if (sf.Get() == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+ }
new_shadow_frame->SetVRegReference(0, receiver.Get());
- // TODO(narayan): This is the EmulatedStackFrame, currently nullptr.
- new_shadow_frame->SetVRegReference(1, nullptr);
+ new_shadow_frame->SetVRegReference(1, sf.Get());
PerformCall(self,
code_item,
@@ -983,6 +1238,12 @@
new_shadow_frame,
result);
+ // If the called transformer method we called has returned a value, then we
+ // need to copy it back to |result|.
+ if (!self->IsExceptionPending()) {
+ sf->GetReturnValue(self, result);
+ }
+
return !self->IsExceptionPending();
}
@@ -1104,15 +1365,22 @@
switch (shorty[shorty_pos + 1]) {
// Handle Object references. 1 virtual register slot.
case 'L': {
- Object* o = shadow_frame.GetVRegReference(src_reg);
+ ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference(src_reg);
if (do_assignability_check && o != nullptr) {
PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- Class* arg_type =
- method->GetClassFromTypeIndex(
- params->GetTypeItem(shorty_pos).type_idx_, true /* resolve */, pointer_size);
+ const uint32_t type_idx = params->GetTypeItem(shorty_pos).type_idx_;
+ ObjPtr<mirror::Class> arg_type = method->GetDexCacheResolvedType(type_idx,
+ pointer_size);
if (arg_type == nullptr) {
- CHECK(self->IsExceptionPending());
- return false;
+ StackHandleScope<1> hs(self);
+ // Preserve o since it is used below and GetClassFromTypeIndex may cause thread
+ // suspension.
+ HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&o);
+ arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */, pointer_size);
+ if (arg_type == nullptr) {
+ CHECK(self->IsExceptionPending());
+ return false;
+ }
}
if (!o->VerifierInstanceOf(arg_type)) {
// This should never happen.
@@ -1125,7 +1393,7 @@
return false;
}
}
- new_shadow_frame->SetVRegReference(dest_reg, o);
+ new_shadow_frame->SetVRegReference(dest_reg, o.Ptr());
break;
}
// Handle doubles and longs. 2 consecutive virtual register slots.
@@ -1192,8 +1460,10 @@
}
template <bool is_range, bool do_access_check, bool transaction_active>
-bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
- Thread* self, JValue* result) {
+bool DoFilledNewArray(const Instruction* inst,
+ const ShadowFrame& shadow_frame,
+ Thread* self,
+ JValue* result) {
DCHECK(inst->Opcode() == Instruction::FILLED_NEW_ARRAY ||
inst->Opcode() == Instruction::FILLED_NEW_ARRAY_RANGE);
const int32_t length = is_range ? inst->VRegA_3rc() : inst->VRegA_35c();
@@ -1206,14 +1476,17 @@
return false;
}
uint16_t type_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
- Class* array_class = ResolveVerifyAndClinit(type_idx, shadow_frame.GetMethod(),
- self, false, do_access_check);
+ ObjPtr<mirror::Class> array_class = ResolveVerifyAndClinit(type_idx,
+ shadow_frame.GetMethod(),
+ self,
+ false,
+ do_access_check);
if (UNLIKELY(array_class == nullptr)) {
DCHECK(self->IsExceptionPending());
return false;
}
CHECK(array_class->IsArrayClass());
- Class* component_class = array_class->GetComponentType();
+ ObjPtr<mirror::Class> component_class = array_class->GetComponentType();
const bool is_primitive_int_component = component_class->IsPrimitiveInt();
if (UNLIKELY(component_class->IsPrimitive() && !is_primitive_int_component)) {
if (component_class->IsPrimitiveLong() || component_class->IsPrimitiveDouble()) {
@@ -1226,9 +1499,12 @@
}
return false;
}
- Object* new_array = Array::Alloc<true>(self, array_class, length,
- array_class->GetComponentSizeShift(),
- Runtime::Current()->GetHeap()->GetCurrentAllocator());
+ ObjPtr<mirror::Object> new_array = mirror::Array::Alloc<true>(
+ self,
+ array_class,
+ length,
+ array_class->GetComponentSizeShift(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator());
if (UNLIKELY(new_array == nullptr)) {
self->AssertPendingOOMException();
return false;
@@ -1246,7 +1522,7 @@
new_array->AsIntArray()->SetWithoutChecks<transaction_active>(
i, shadow_frame.GetVReg(src_reg));
} else {
- new_array->AsObjectArray<Object>()->SetWithoutChecks<transaction_active>(
+ new_array->AsObjectArray<mirror::Object>()->SetWithoutChecks<transaction_active>(
i, shadow_frame.GetVRegReference(src_reg));
}
}
@@ -1255,17 +1531,18 @@
return true;
}
-// TODO fix thread analysis: should be REQUIRES_SHARED(Locks::mutator_lock_).
+// TODO: Use ObjPtr here.
template<typename T>
-static void RecordArrayElementsInTransactionImpl(mirror::PrimitiveArray<T>* array, int32_t count)
- NO_THREAD_SAFETY_ANALYSIS {
+static void RecordArrayElementsInTransactionImpl(mirror::PrimitiveArray<T>* array,
+ int32_t count)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
Runtime* runtime = Runtime::Current();
for (int32_t i = 0; i < count; ++i) {
runtime->RecordWriteArray(array, i, array->GetWithoutChecks(i));
}
}
-void RecordArrayElementsInTransaction(mirror::Array* array, int32_t count)
+void RecordArrayElementsInTransaction(ObjPtr<mirror::Array> array, int32_t count)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(Runtime::Current()->IsActiveTransaction());
DCHECK(array != nullptr);
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 40d6f03..9c26d24 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -43,25 +43,11 @@
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/string-inl.h"
+#include "obj_ptr.h"
#include "stack.h"
#include "thread.h"
#include "well_known_classes.h"
-using ::art::ArtMethod;
-using ::art::mirror::Array;
-using ::art::mirror::BooleanArray;
-using ::art::mirror::ByteArray;
-using ::art::mirror::CharArray;
-using ::art::mirror::Class;
-using ::art::mirror::ClassLoader;
-using ::art::mirror::IntArray;
-using ::art::mirror::LongArray;
-using ::art::mirror::Object;
-using ::art::mirror::ObjectArray;
-using ::art::mirror::ShortArray;
-using ::art::mirror::String;
-using ::art::mirror::Throwable;
-
namespace art {
namespace interpreter {
@@ -69,13 +55,11 @@
REQUIRES_SHARED(Locks::mutator_lock_);
template <bool kMonitorCounting>
-static inline void DoMonitorEnter(Thread* self,
- ShadowFrame* frame,
- Object* ref)
+static inline void DoMonitorEnter(Thread* self, ShadowFrame* frame, ObjPtr<mirror::Object> ref)
NO_THREAD_SAFETY_ANALYSIS
REQUIRES(!Roles::uninterruptible_) {
StackHandleScope<1> hs(self);
- Handle<Object> h_ref(hs.NewHandle(ref));
+ Handle<mirror::Object> h_ref(hs.NewHandle(ref));
h_ref->MonitorEnter(self);
if (kMonitorCounting && frame->GetMethod()->MustCountLocks()) {
frame->GetLockCountData().AddMonitor(self, h_ref.Get());
@@ -83,13 +67,11 @@
}
template <bool kMonitorCounting>
-static inline void DoMonitorExit(Thread* self,
- ShadowFrame* frame,
- Object* ref)
+static inline void DoMonitorExit(Thread* self, ShadowFrame* frame, ObjPtr<mirror::Object> ref)
NO_THREAD_SAFETY_ANALYSIS
REQUIRES(!Roles::uninterruptible_) {
StackHandleScope<1> hs(self);
- Handle<Object> h_ref(hs.NewHandle(ref));
+ Handle<mirror::Object> h_ref(hs.NewHandle(ref));
h_ref->MonitorExit(self);
if (kMonitorCounting && frame->GetMethod()->MustCountLocks()) {
frame->GetLockCountData().RemoveMonitorOrThrow(self, h_ref.Get());
@@ -113,7 +95,7 @@
void AbortTransactionV(Thread* self, const char* fmt, va_list args)
REQUIRES_SHARED(Locks::mutator_lock_);
-void RecordArrayElementsInTransaction(mirror::Array* array, int32_t count)
+void RecordArrayElementsInTransaction(ObjPtr<mirror::Array> array, int32_t count)
REQUIRES_SHARED(Locks::mutator_lock_);
// Invokes the given method. This is part of the invocation support and is used by DoInvoke and
@@ -126,11 +108,14 @@
// Handles all invoke-XXX/range instructions except for invoke-polymorphic[/range].
// Returns true on success, otherwise throws an exception and returns false.
template<InvokeType type, bool is_range, bool do_access_check>
-static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,
- uint16_t inst_data, JValue* result) {
+static inline bool DoInvoke(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ JValue* result) {
const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
- Object* receiver = (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC);
+ ObjPtr<mirror::Object> receiver = (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC);
ArtMethod* sf_method = shadow_frame.GetMethod();
ArtMethod* const called_method = FindMethodFromCode<type, do_access_check>(
method_idx, &receiver, sf_method, self);
@@ -156,7 +141,7 @@
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) {
instrumentation->InvokeVirtualOrInterface(
- self, receiver, sf_method, shadow_frame.GetDexPC(), called_method);
+ self, receiver.Ptr(), sf_method, shadow_frame.GetDexPC(), called_method);
}
}
return DoCall<is_range, do_access_check>(called_method, self, shadow_frame, inst, inst_data,
@@ -177,7 +162,7 @@
const Instruction* inst, uint16_t inst_data,
JValue* result) {
const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
- Object* const receiver = shadow_frame.GetVRegReference(vregC);
+ ObjPtr<mirror::Object> const receiver = shadow_frame.GetVRegReference(vregC);
if (UNLIKELY(receiver == nullptr)) {
// We lost the reference to the method index so we cannot get a more
// precised exception message.
@@ -190,7 +175,7 @@
CHECK(receiver->GetClass() != nullptr)
<< "Null class found in object " << receiver << " in region type "
<< Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->
- RegionSpace()->GetRegionType(receiver);
+ RegionSpace()->GetRegionType(receiver.Ptr());
}
CHECK(receiver->GetClass()->ShouldHaveEmbeddedVTable());
ArtMethod* const called_method = receiver->GetClass()->GetEmbeddedVTableEntry(
@@ -214,7 +199,7 @@
// TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT.
if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) {
instrumentation->InvokeVirtualOrInterface(
- self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method);
+ self, receiver.Ptr(), shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method);
}
// No need to check since we've been quickened.
return DoCall<is_range, false>(called_method, self, shadow_frame, inst, inst_data, result);
@@ -249,9 +234,11 @@
// Handles string resolution for const-string and const-string-jumbo instructions. Also ensures the
// java.lang.String class is initialized.
-static inline String* ResolveString(Thread* self, ShadowFrame& shadow_frame, uint32_t string_idx)
+static inline ObjPtr<mirror::String> ResolveString(Thread* self,
+ ShadowFrame& shadow_frame,
+ uint32_t string_idx)
REQUIRES_SHARED(Locks::mutator_lock_) {
- Class* java_lang_string_class = String::GetJavaLangString();
+ ObjPtr<mirror::Class> java_lang_string_class = mirror::String::GetJavaLangString();
if (UNLIKELY(!java_lang_string_class->IsInitialized())) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
StackHandleScope<1> hs(self);
@@ -262,11 +249,11 @@
}
}
ArtMethod* method = shadow_frame.GetMethod();
- mirror::Class* declaring_class = method->GetDeclaringClass();
+ ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
// MethodVerifier refuses methods with string_idx out of bounds.
DCHECK_LT(string_idx % mirror::DexCache::kDexCacheStringCacheSize,
declaring_class->GetDexFile().NumStringIds());
- mirror::String* string_ptr =
+ ObjPtr<mirror::String> string_ptr =
mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(),
string_idx,
mirror::DexCache::kDexCacheStringCacheSize).Read();
@@ -318,8 +305,10 @@
// Handles div-long and div-long-2addr instructions.
// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false.
-static inline bool DoLongDivide(ShadowFrame& shadow_frame, size_t result_reg,
- int64_t dividend, int64_t divisor)
+static inline bool DoLongDivide(ShadowFrame& shadow_frame,
+ size_t result_reg,
+ int64_t dividend,
+ int64_t divisor)
REQUIRES_SHARED(Locks::mutator_lock_) {
const int64_t kMinLong = std::numeric_limits<int64_t>::min();
if (UNLIKELY(divisor == 0)) {
@@ -336,8 +325,10 @@
// Handles rem-long and rem-long-2addr instructions.
// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false.
-static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg,
- int64_t dividend, int64_t divisor)
+static inline bool DoLongRemainder(ShadowFrame& shadow_frame,
+ size_t result_reg,
+ int64_t dividend,
+ int64_t divisor)
REQUIRES_SHARED(Locks::mutator_lock_) {
const int64_t kMinLong = std::numeric_limits<int64_t>::min();
if (UNLIKELY(divisor == 0)) {
@@ -443,7 +434,7 @@
<< inst->DumpString(shadow_frame.GetMethod()->GetDexFile()) << "\n";
for (uint32_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) {
uint32_t raw_value = shadow_frame.GetVReg(i);
- Object* ref_value = shadow_frame.GetVRegReference(i);
+ ObjPtr<mirror::Object> ref_value = shadow_frame.GetVRegReference(i);
oss << StringPrintf(" vreg%u=0x%08X", i, raw_value);
if (ref_value != nullptr) {
if (ref_value->GetClass()->IsStringClass() &&
@@ -469,13 +460,13 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
// Uint required, so that sign extension does not make this wrong on 64b systems
uint32_t src_value = shadow_frame.GetVReg(src_reg);
- mirror::Object* o = shadow_frame.GetVRegReference<kVerifyNone>(src_reg);
+ ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference<kVerifyNone>(src_reg);
// If both register locations contains the same value, the register probably holds a reference.
// Note: As an optimization, non-moving collectors leave a stale reference value
// in the references array even after the original vreg was overwritten to a non-reference.
- if (src_value == reinterpret_cast<uintptr_t>(o)) {
- new_shadow_frame->SetVRegReference(dest_reg, o);
+ if (src_value == reinterpret_cast<uintptr_t>(o.Ptr())) {
+ new_shadow_frame->SetVRegReference(dest_reg, o.Ptr());
} else {
new_shadow_frame->SetVReg(dest_reg, src_value);
}
diff --git a/runtime/interpreter/interpreter_mterp_impl.h b/runtime/interpreter/interpreter_mterp_impl.h
index 90d9f89..1be20fa 100644
--- a/runtime/interpreter/interpreter_mterp_impl.h
+++ b/runtime/interpreter/interpreter_mterp_impl.h
@@ -21,6 +21,7 @@
#include "base/mutex.h"
#include "dex_file.h"
#include "jvalue.h"
+#include "obj_ptr.h"
namespace art {
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 78afe56..435ac62 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -192,9 +192,9 @@
break;
case Instruction::MOVE_EXCEPTION: {
PREAMBLE();
- Throwable* exception = self->GetException();
+ ObjPtr<mirror::Throwable> exception = self->GetException();
DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
- shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
+ shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception.Ptr());
self->ClearException();
inst = inst->Next_1xx();
break;
@@ -273,11 +273,11 @@
self->AllowThreadSuspension();
HANDLE_MONITOR_CHECKS();
const size_t ref_idx = inst->VRegA_11x(inst_data);
- Object* obj_result = shadow_frame.GetVRegReference(ref_idx);
+ ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
if (do_assignability_check && obj_result != nullptr) {
PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- Class* return_type = shadow_frame.GetMethod()->GetReturnType(true /* resolve */,
- pointer_size);
+ ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */,
+ pointer_size);
// Re-load since it might have moved.
obj_result = shadow_frame.GetVRegReference(ref_idx);
if (return_type == nullptr) {
@@ -373,41 +373,44 @@
break;
case Instruction::CONST_STRING: {
PREAMBLE();
- String* s = ResolveString(self, shadow_frame, inst->VRegB_21c());
+ ObjPtr<mirror::String> s = ResolveString(self, shadow_frame, inst->VRegB_21c());
if (UNLIKELY(s == nullptr)) {
HANDLE_PENDING_EXCEPTION();
} else {
- shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s);
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s.Ptr());
inst = inst->Next_2xx();
}
break;
}
case Instruction::CONST_STRING_JUMBO: {
PREAMBLE();
- String* s = ResolveString(self, shadow_frame, inst->VRegB_31c());
+ ObjPtr<mirror::String> s = ResolveString(self, shadow_frame, inst->VRegB_31c());
if (UNLIKELY(s == nullptr)) {
HANDLE_PENDING_EXCEPTION();
} else {
- shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s);
+ shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s.Ptr());
inst = inst->Next_3xx();
}
break;
}
case Instruction::CONST_CLASS: {
PREAMBLE();
- Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
- self, false, do_access_check);
+ ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(inst->VRegB_21c(),
+ shadow_frame.GetMethod(),
+ self,
+ false,
+ do_access_check);
if (UNLIKELY(c == nullptr)) {
HANDLE_PENDING_EXCEPTION();
} else {
- shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c);
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c.Ptr());
inst = inst->Next_2xx();
}
break;
}
case Instruction::MONITOR_ENTER: {
PREAMBLE();
- Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
if (UNLIKELY(obj == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
@@ -419,7 +422,7 @@
}
case Instruction::MONITOR_EXIT: {
PREAMBLE();
- Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
if (UNLIKELY(obj == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
@@ -431,12 +434,15 @@
}
case Instruction::CHECK_CAST: {
PREAMBLE();
- Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
- self, false, do_access_check);
+ ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(inst->VRegB_21c(),
+ shadow_frame.GetMethod(),
+ self,
+ false,
+ do_access_check);
if (UNLIKELY(c == nullptr)) {
HANDLE_PENDING_EXCEPTION();
} else {
- Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data));
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data));
if (UNLIKELY(obj != nullptr && !obj->InstanceOf(c))) {
ThrowClassCastException(c, obj->GetClass());
HANDLE_PENDING_EXCEPTION();
@@ -448,12 +454,15 @@
}
case Instruction::INSTANCE_OF: {
PREAMBLE();
- Class* c = ResolveVerifyAndClinit(inst->VRegC_22c(), shadow_frame.GetMethod(),
- self, false, do_access_check);
+ ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(inst->VRegC_22c(),
+ shadow_frame.GetMethod(),
+ self,
+ false,
+ do_access_check);
if (UNLIKELY(c == nullptr)) {
HANDLE_PENDING_EXCEPTION();
} else {
- Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
shadow_frame.SetVReg(inst->VRegA_22c(inst_data),
(obj != nullptr && obj->InstanceOf(c)) ? 1 : 0);
inst = inst->Next_2xx();
@@ -462,7 +471,7 @@
}
case Instruction::ARRAY_LENGTH: {
PREAMBLE();
- Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
+ ObjPtr<mirror::Object> array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
if (UNLIKELY(array == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
@@ -474,9 +483,12 @@
}
case Instruction::NEW_INSTANCE: {
PREAMBLE();
- Object* obj = nullptr;
- Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
- self, false, do_access_check);
+ ObjPtr<mirror::Object> obj = nullptr;
+ ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(inst->VRegB_21c(),
+ shadow_frame.GetMethod(),
+ self,
+ false,
+ do_access_check);
if (LIKELY(c != nullptr)) {
if (UNLIKELY(c->IsStringClass())) {
gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
@@ -499,7 +511,7 @@
HANDLE_PENDING_EXCEPTION();
break;
}
- shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj);
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj.Ptr());
inst = inst->Next_2xx();
}
break;
@@ -507,13 +519,13 @@
case Instruction::NEW_ARRAY: {
PREAMBLE();
int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
- Object* obj = AllocArrayFromCode<do_access_check, true>(
+ ObjPtr<mirror::Object> obj = AllocArrayFromCode<do_access_check, true>(
inst->VRegC_22c(), length, shadow_frame.GetMethod(), self,
Runtime::Current()->GetHeap()->GetCurrentAllocator());
if (UNLIKELY(obj == nullptr)) {
HANDLE_PENDING_EXCEPTION();
} else {
- shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj);
+ shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj.Ptr());
inst = inst->Next_2xx();
}
break;
@@ -539,7 +551,7 @@
const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
const Instruction::ArrayDataPayload* payload =
reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
- Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
bool success = FillArrayData(obj, payload);
if (!success) {
HANDLE_PENDING_EXCEPTION();
@@ -553,7 +565,8 @@
}
case Instruction::THROW: {
PREAMBLE();
- Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ ObjPtr<mirror::Object> exception =
+ shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
if (UNLIKELY(exception == nullptr)) {
ThrowNullPointerException("throw with null exception");
} else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
@@ -911,14 +924,14 @@
}
case Instruction::AGET_BOOLEAN: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
break;
}
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- BooleanArray* array = a->AsBooleanArray();
+ ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray();
if (array->CheckIsValidIndex(index)) {
shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
inst = inst->Next_2xx();
@@ -929,14 +942,14 @@
}
case Instruction::AGET_BYTE: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
break;
}
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ByteArray* array = a->AsByteArray();
+ ObjPtr<mirror::ByteArray> array = a->AsByteArray();
if (array->CheckIsValidIndex(index)) {
shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
inst = inst->Next_2xx();
@@ -947,14 +960,14 @@
}
case Instruction::AGET_CHAR: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
break;
}
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- CharArray* array = a->AsCharArray();
+ ObjPtr<mirror::CharArray> array = a->AsCharArray();
if (array->CheckIsValidIndex(index)) {
shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
inst = inst->Next_2xx();
@@ -965,14 +978,14 @@
}
case Instruction::AGET_SHORT: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
break;
}
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ShortArray* array = a->AsShortArray();
+ ObjPtr<mirror::ShortArray> array = a->AsShortArray();
if (array->CheckIsValidIndex(index)) {
shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
inst = inst->Next_2xx();
@@ -983,7 +996,7 @@
}
case Instruction::AGET: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
@@ -991,7 +1004,7 @@
}
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf();
- auto* array = down_cast<IntArray*>(a);
+ ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a);
if (array->CheckIsValidIndex(index)) {
shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
inst = inst->Next_2xx();
@@ -1002,7 +1015,7 @@
}
case Instruction::AGET_WIDE: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
@@ -1010,7 +1023,7 @@
}
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf();
- auto* array = down_cast<LongArray*>(a);
+ ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a);
if (array->CheckIsValidIndex(index)) {
shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
inst = inst->Next_2xx();
@@ -1021,14 +1034,14 @@
}
case Instruction::AGET_OBJECT: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
break;
}
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjectArray<Object>* array = a->AsObjectArray<Object>();
+ ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>();
if (array->CheckIsValidIndex(index)) {
shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
inst = inst->Next_2xx();
@@ -1039,7 +1052,7 @@
}
case Instruction::APUT_BOOLEAN: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
@@ -1047,7 +1060,7 @@
}
uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- BooleanArray* array = a->AsBooleanArray();
+ ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray();
if (array->CheckIsValidIndex(index)) {
array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
@@ -1058,7 +1071,7 @@
}
case Instruction::APUT_BYTE: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
@@ -1066,7 +1079,7 @@
}
int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ByteArray* array = a->AsByteArray();
+ ObjPtr<mirror::ByteArray> array = a->AsByteArray();
if (array->CheckIsValidIndex(index)) {
array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
@@ -1077,7 +1090,7 @@
}
case Instruction::APUT_CHAR: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
@@ -1085,7 +1098,7 @@
}
uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- CharArray* array = a->AsCharArray();
+ ObjPtr<mirror::CharArray> array = a->AsCharArray();
if (array->CheckIsValidIndex(index)) {
array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
@@ -1096,7 +1109,7 @@
}
case Instruction::APUT_SHORT: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
@@ -1104,7 +1117,7 @@
}
int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ShortArray* array = a->AsShortArray();
+ ObjPtr<mirror::ShortArray> array = a->AsShortArray();
if (array->CheckIsValidIndex(index)) {
array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
@@ -1115,7 +1128,7 @@
}
case Instruction::APUT: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
@@ -1124,7 +1137,7 @@
int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf();
- auto* array = down_cast<IntArray*>(a);
+ ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a);
if (array->CheckIsValidIndex(index)) {
array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
@@ -1135,7 +1148,7 @@
}
case Instruction::APUT_WIDE: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
@@ -1144,7 +1157,7 @@
int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data));
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf();
- LongArray* array = down_cast<LongArray*>(a);
+ ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a);
if (array->CheckIsValidIndex(index)) {
array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
@@ -1155,15 +1168,15 @@
}
case Instruction::APUT_OBJECT: {
PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
ThrowNullPointerExceptionFromInterpreter();
HANDLE_PENDING_EXCEPTION();
break;
}
int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
- ObjectArray<Object>* array = a->AsObjectArray<Object>();
+ ObjPtr<mirror::Object> val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
+ ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>();
if (array->CheckIsValidIndex(index) && array->CheckAssignable(val)) {
array->SetWithoutChecks<transaction_active>(index, val);
inst = inst->Next_2xx();
@@ -1545,6 +1558,7 @@
}
case Instruction::INVOKE_POLYMORPHIC: {
PREAMBLE();
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
bool success = DoInvokePolymorphic<false, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
@@ -1552,11 +1566,11 @@
}
case Instruction::INVOKE_POLYMORPHIC_RANGE: {
PREAMBLE();
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
bool success = DoInvokePolymorphic<true, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
break;
- break;
}
case Instruction::NEG_INT:
PREAMBLE();
diff --git a/runtime/interpreter/interpreter_switch_impl.h b/runtime/interpreter/interpreter_switch_impl.h
index d0c9386..267df2e 100644
--- a/runtime/interpreter/interpreter_switch_impl.h
+++ b/runtime/interpreter/interpreter_switch_impl.h
@@ -21,6 +21,7 @@
#include "base/mutex.h"
#include "dex_file.h"
#include "jvalue.h"
+#include "obj_ptr.h"
namespace art {
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index cf8d4bd..46d5af1 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -291,11 +291,11 @@
ShadowFrame* shadow_frame,
Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- String* s = ResolveString(self, *shadow_frame, index);
+ ObjPtr<mirror::String> s = ResolveString(self, *shadow_frame, index);
if (UNLIKELY(s == nullptr)) {
return true;
}
- shadow_frame->SetVRegReference(tgt_vreg, s);
+ shadow_frame->SetVRegReference(tgt_vreg, s.Ptr());
return false;
}
@@ -304,7 +304,7 @@
ShadowFrame* shadow_frame,
Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- Class* c = ResolveVerifyAndClinit(index, shadow_frame->GetMethod(), self, false, false);
+ mirror::Class* c = ResolveVerifyAndClinit(index, shadow_frame->GetMethod(), self, false, false);
if (UNLIKELY(c == nullptr)) {
return true;
}
@@ -317,12 +317,12 @@
art::ArtMethod* method,
Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- Class* c = ResolveVerifyAndClinit(index, method, self, false, false);
+ ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(index, method, self, false, false);
if (UNLIKELY(c == nullptr)) {
return true;
}
// Must load obj from vreg following ResolveVerifyAndClinit due to moving gc.
- Object* obj = vreg_addr->AsMirrorPtr();
+ mirror::Object* obj = vreg_addr->AsMirrorPtr();
if (UNLIKELY(obj != nullptr && !obj->InstanceOf(c))) {
ThrowClassCastException(c, obj->GetClass());
return true;
@@ -335,16 +335,16 @@
art::ArtMethod* method,
Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- Class* c = ResolveVerifyAndClinit(index, method, self, false, false);
+ ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(index, method, self, false, false);
if (UNLIKELY(c == nullptr)) {
return false; // Caller will check for pending exception. Return value unimportant.
}
// Must load obj from vreg following ResolveVerifyAndClinit due to moving gc.
- Object* obj = vreg_addr->AsMirrorPtr();
+ mirror::Object* obj = vreg_addr->AsMirrorPtr();
return (obj != nullptr) && obj->InstanceOf(c);
}
-extern "C" size_t MterpFillArrayData(Object* obj, const Instruction::ArrayDataPayload* payload)
+extern "C" size_t MterpFillArrayData(mirror::Object* obj, const Instruction::ArrayDataPayload* payload)
REQUIRES_SHARED(Locks::mutator_lock_) {
return FillArrayData(obj, payload);
}
@@ -352,9 +352,12 @@
extern "C" size_t MterpNewInstance(ShadowFrame* shadow_frame, Thread* self, uint32_t inst_data)
REQUIRES_SHARED(Locks::mutator_lock_) {
const Instruction* inst = Instruction::At(shadow_frame->GetDexPCPtr());
- Object* obj = nullptr;
- Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame->GetMethod(),
- self, false, false);
+ mirror::Object* obj = nullptr;
+ mirror::Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(),
+ shadow_frame->GetMethod(),
+ self,
+ false,
+ false);
if (LIKELY(c != nullptr)) {
if (UNLIKELY(c->IsStringClass())) {
gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
@@ -404,13 +407,13 @@
uint32_t inst_data)
REQUIRES_SHARED(Locks::mutator_lock_) {
const Instruction* inst = Instruction::At(dex_pc_ptr);
- Object* a = shadow_frame->GetVRegReference(inst->VRegB_23x());
+ mirror::Object* a = shadow_frame->GetVRegReference(inst->VRegB_23x());
if (UNLIKELY(a == nullptr)) {
return false;
}
int32_t index = shadow_frame->GetVReg(inst->VRegC_23x());
- Object* val = shadow_frame->GetVRegReference(inst->VRegA_23x(inst_data));
- ObjectArray<Object>* array = a->AsObjectArray<Object>();
+ mirror::Object* val = shadow_frame->GetVRegReference(inst->VRegA_23x(inst_data));
+ mirror::ObjectArray<mirror::Object>* array = a->AsObjectArray<mirror::Object>();
if (array->CheckIsValidIndex(index) && array->CheckAssignable(val)) {
array->SetWithoutChecks<false>(index, val);
return true;
@@ -442,7 +445,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
const Instruction* inst = Instruction::At(dex_pc_ptr);
int32_t length = shadow_frame->GetVReg(inst->VRegB_22c(inst_data));
- Object* obj = AllocArrayFromCode<false, true>(
+ mirror::Object* obj = AllocArrayFromCode<false, true>(
inst->VRegC_22c(), length, shadow_frame->GetMethod(), self,
Runtime::Current()->GetHeap()->GetCurrentAllocator());
if (UNLIKELY(obj == nullptr)) {
@@ -678,7 +681,7 @@
ThrowNullPointerExceptionFromInterpreter();
return nullptr;
}
- ObjectArray<Object>* array = arr->AsObjectArray<Object>();
+ mirror::ObjectArray<mirror::Object>* array = arr->AsObjectArray<mirror::Object>();
if (LIKELY(array->CheckIsValidIndex(index))) {
return array->GetWithoutChecks(index);
} else {
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 5a62bd7..6bf7e15 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -564,7 +564,7 @@
this_classloader_class.Get()) {
AbortTransactionOrFail(self,
"Unsupported classloader type %s for getResourceAsStream",
- Class::PrettyClass(this_classloader_class.Get()).c_str());
+ mirror::Class::PrettyClass(this_classloader_class.Get()).c_str());
return;
}
}
@@ -608,10 +608,11 @@
int32_t length)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (src_array->GetClass()->GetComponentType() != dst_array->GetClass()->GetComponentType()) {
- AbortTransactionOrFail(self, "Types mismatched in arraycopy: %s vs %s.",
- Class::PrettyDescriptor(
+ AbortTransactionOrFail(self,
+ "Types mismatched in arraycopy: %s vs %s.",
+ mirror::Class::PrettyDescriptor(
src_array->GetClass()->GetComponentType()).c_str(),
- Class::PrettyDescriptor(
+ mirror::Class::PrettyDescriptor(
dst_array->GetClass()->GetComponentType()).c_str());
return;
}
@@ -677,9 +678,9 @@
GetComponentType();
if (trg_type->IsPrimitiveInt()) {
AbortTransactionOrFail(self, "Type mismatch in arraycopy: %s vs %s",
- Class::PrettyDescriptor(
+ mirror::Class::PrettyDescriptor(
src_array->GetClass()->GetComponentType()).c_str(),
- Class::PrettyDescriptor(
+ mirror::Class::PrettyDescriptor(
dst_array->GetClass()->GetComponentType()).c_str());
return;
}
@@ -1271,7 +1272,7 @@
mirror::HeapReference<mirror::Object>* field_addr =
reinterpret_cast<mirror::HeapReference<mirror::Object>*>(
reinterpret_cast<uint8_t*>(obj) + static_cast<size_t>(offset));
- ReadBarrier::Barrier<mirror::Object, kWithReadBarrier, /*kAlwaysUpdateField*/true>(
+ ReadBarrier::Barrier<mirror::Object, kWithReadBarrier, /* kAlwaysUpdateField */ true>(
obj,
MemberOffset(offset),
field_addr);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 953b1c0..4c10063 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -683,7 +683,7 @@
}
}
-void Jit::InvokeVirtualOrInterface(mirror::Object* this_object,
+void Jit::InvokeVirtualOrInterface(ObjPtr<mirror::Object> this_object,
ArtMethod* caller,
uint32_t dex_pc,
ArtMethod* callee ATTRIBUTE_UNUSED) {
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index d3178b0..a782437 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -22,9 +22,10 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "base/timing_logger.h"
+#include "jit/profile_saver_options.h"
+#include "obj_ptr.h"
#include "object_callbacks.h"
#include "offline_profiling_info.h"
-#include "jit/profile_saver_options.h"
#include "thread_pool.h"
namespace art {
@@ -114,7 +115,7 @@
void AddSamples(Thread* self, ArtMethod* method, uint16_t samples, bool with_backedges)
REQUIRES_SHARED(Locks::mutator_lock_);
- void InvokeVirtualOrInterface(mirror::Object* this_object,
+ void InvokeVirtualOrInterface(ObjPtr<mirror::Object> this_object,
ArtMethod* caller,
uint32_t dex_pc,
ArtMethod* callee)
diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h
index b488133..ff5d2a1 100644
--- a/runtime/method_handles-inl.h
+++ b/runtime/method_handles-inl.h
@@ -64,44 +64,11 @@
}
}
-// A convenience class that allows for iteration through a list of
-// input argument registers |arg| for non-range invokes or a list of
-// consecutive registers starting with a given based for range
-// invokes.
-template <bool is_range> class ArgIterator {
- public:
- ArgIterator(size_t first_src_reg,
- const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) :
- first_src_reg_(first_src_reg),
- arg_(arg),
- arg_index_(0) {
- }
-
- uint32_t Next() {
- const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]);
- ++arg_index_;
-
- return next;
- }
-
- uint32_t NextPair() {
- const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]);
- arg_index_ += 2;
-
- return next;
- }
-
- private:
- const size_t first_src_reg_;
- const uint32_t (&arg_)[Instruction::kMaxVarArgRegs];
- size_t arg_index_;
-};
-
REQUIRES_SHARED(Locks::mutator_lock_)
-bool ConvertJValue(Handle<mirror::Class> from,
- Handle<mirror::Class> to,
- const JValue& from_value,
- JValue* to_value) {
+inline bool ConvertJValue(Handle<mirror::Class> from,
+ Handle<mirror::Class> to,
+ const JValue& from_value,
+ JValue* to_value) {
const Primitive::Type from_type = from->GetPrimitiveType();
const Primitive::Type to_type = to->GetPrimitiveType();
@@ -161,6 +128,66 @@
return true;
}
+template <typename G, typename S>
+bool PerformConversions(Thread* self,
+ Handle<mirror::ObjectArray<mirror::Class>> from_types,
+ Handle<mirror::ObjectArray<mirror::Class>> to_types,
+ G* getter,
+ S* setter,
+ int32_t num_conversions) {
+ StackHandleScope<2> hs(self);
+ MutableHandle<mirror::Class> from(hs.NewHandle<mirror::Class>(nullptr));
+ MutableHandle<mirror::Class> to(hs.NewHandle<mirror::Class>(nullptr));
+
+ for (int32_t i = 0; i < num_conversions; ++i) {
+ from.Assign(from_types->GetWithoutChecks(i));
+ to.Assign(to_types->GetWithoutChecks(i));
+
+ const Primitive::Type from_type = from->GetPrimitiveType();
+ const Primitive::Type to_type = to->GetPrimitiveType();
+
+ if (from.Get() == to.Get()) {
+ // Easy case - the types are identical. Nothing left to do except to pass
+ // the arguments along verbatim.
+ if (Primitive::Is64BitType(from_type)) {
+ setter->SetLong(getter->GetLong());
+ } else if (from_type == Primitive::kPrimNot) {
+ setter->SetReference(getter->GetReference());
+ } else {
+ setter->Set(getter->Get());
+ }
+
+ continue;
+ } else {
+ JValue from_value;
+ JValue to_value;
+
+ if (Primitive::Is64BitType(from_type)) {
+ from_value.SetJ(getter->GetLong());
+ } else if (from_type == Primitive::kPrimNot) {
+ from_value.SetL(getter->GetReference());
+ } else {
+ from_value.SetI(getter->Get());
+ }
+
+ if (!ConvertJValue(from, to, from_value, &to_value)) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+
+ if (Primitive::Is64BitType(to_type)) {
+ setter->SetLong(to_value.GetJ());
+ } else if (to_type == Primitive::kPrimNot) {
+ setter->SetReference(to_value.GetL());
+ } else {
+ setter->Set(to_value.GetI());
+ }
+ }
+ }
+
+ return true;
+}
+
template <bool is_range>
bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self,
Handle<mirror::MethodType> callsite_type,
@@ -180,89 +207,17 @@
return false;
}
- ArgIterator<is_range> input_args(first_src_reg, arg);
- size_t to_arg_index = 0;
- MutableHandle<mirror::Class> from(hs.NewHandle<mirror::Class>(nullptr));
- MutableHandle<mirror::Class> to(hs.NewHandle<mirror::Class>(nullptr));
- for (int32_t i = 0; i < num_method_params; ++i) {
- from.Assign(from_types->GetWithoutChecks(i));
- to.Assign(to_types->GetWithoutChecks(i));
+ ShadowFrameGetter<is_range> getter(first_src_reg, arg, caller_frame);
+ ShadowFrameSetter setter(callee_frame, first_dest_reg);
- const Primitive::Type from_type = from->GetPrimitiveType();
- const Primitive::Type to_type = to->GetPrimitiveType();
-
- // Easy case - the types are identical. Nothing left to do except to pass
- // the arguments along verbatim.
- if (from.Get() == to.Get()) {
- interpreter::AssignRegister(callee_frame,
- caller_frame,
- first_dest_reg + to_arg_index,
- input_args.Next());
- ++to_arg_index;
-
- // This is a wide argument, we must use the second half of the register
- // pair as well.
- if (Primitive::Is64BitType(from_type)) {
- interpreter::AssignRegister(callee_frame,
- caller_frame,
- first_dest_reg + to_arg_index,
- input_args.Next());
- ++to_arg_index;
- }
-
- continue;
- } else {
- JValue from_value;
- JValue to_value;
-
- if (Primitive::Is64BitType(from_type)) {
- from_value.SetJ(caller_frame.GetVRegLong(input_args.NextPair()));
- } else if (from_type == Primitive::kPrimNot) {
- from_value.SetL(caller_frame.GetVRegReference(input_args.Next()));
- } else {
- from_value.SetI(caller_frame.GetVReg(input_args.Next()));
- }
-
- if (!ConvertJValue(from, to, from_value, &to_value)) {
- DCHECK(self->IsExceptionPending());
- return false;
- }
-
- if (Primitive::Is64BitType(to_type)) {
- callee_frame->SetVRegLong(first_dest_reg + to_arg_index, to_value.GetJ());
- to_arg_index += 2;
- } else if (to_type == Primitive::kPrimNot) {
- callee_frame->SetVRegReference(first_dest_reg + to_arg_index, to_value.GetL());
- ++to_arg_index;
- } else {
- callee_frame->SetVReg(first_dest_reg + to_arg_index, to_value.GetI());
- ++to_arg_index;
- }
- }
- }
-
- return true;
+ return PerformConversions<ShadowFrameGetter<is_range>, ShadowFrameSetter>(self,
+ from_types,
+ to_types,
+ &getter,
+ &setter,
+ num_method_params);
}
-// Similar to |ConvertAndCopyArgumentsFromCallerFrame|, except that the
-// arguments are copied from an |EmulatedStackFrame|.
-template <bool is_range>
-bool ConvertAndCopyArgumentsFromEmulatedStackFrame(Thread* self,
- ObjPtr<mirror::Object> emulated_stack_frame,
- Handle<mirror::MethodType> callee_type,
- const uint32_t first_dest_reg,
- ShadowFrame* callee_frame) {
- UNUSED(self);
- UNUSED(emulated_stack_frame);
- UNUSED(callee_type);
- UNUSED(first_dest_reg);
- UNUSED(callee_frame);
-
- UNIMPLEMENTED(FATAL) << "ConvertAndCopyArgumentsFromEmulatedStackFrame is unimplemented";
- return false;
-}
-
-
} // namespace art
#endif // ART_RUNTIME_METHOD_HANDLES_INL_H_
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 5175dce..0d3f9f1 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -59,16 +59,66 @@
// Performs a single argument conversion from type |from| to a distinct
// type |to|. Returns true on success, false otherwise.
REQUIRES_SHARED(Locks::mutator_lock_)
-bool ConvertJValue(Handle<mirror::Class> from,
- Handle<mirror::Class> to,
- const JValue& from_value,
- JValue* to_value) ALWAYS_INLINE;
+inline bool ConvertJValue(Handle<mirror::Class> from,
+ Handle<mirror::Class> to,
+ const JValue& from_value,
+ JValue* to_value) ALWAYS_INLINE;
// Perform argument conversions between |callsite_type| (the type of the
// incoming arguments) and |callee_type| (the type of the method being
// invoked). These include widening and narrowing conversions as well as
// boxing and unboxing. Returns true on success, on false on failure. A
// pending exception will always be set on failure.
+//
+// The values to be converted are read from an input source (of type G)
+// that provides three methods :
+//
+// class G {
+// // Used to read the next boolean/short/int or float value from the
+// // source.
+// uint32_t Get();
+//
+// // Used to the read the next reference value from the source.
+// ObjPtr<mirror::Object> GetReference();
+//
+// // Used to read the next double or long value from the source.
+// int64_t GetLong();
+// }
+//
+// After conversion, the values are written to an output sink (of type S)
+// that provides three methods :
+//
+// class S {
+// void Set(uint32_t);
+// void SetReference(ObjPtr<mirror::Object>)
+// void SetLong(int64_t);
+// }
+//
+// The semantics and usage of the Set methods are analagous to the getter
+// class.
+//
+// This method is instantiated in three different scenarions :
+// - <S = ShadowFrameSetter, G = ShadowFrameGetter> : copying from shadow
+// frame to shadow frame, used in a regular polymorphic non-exact invoke.
+// - <S = EmulatedShadowFrameAccessor, G = ShadowFrameGetter> : entering into
+// a transformer method from a polymorphic invoke.
+// - <S = ShadowFrameStter, G = EmulatedStackFrameAccessor> : entering into
+// a regular poly morphic invoke from a transformer method.
+//
+// TODO(narayan): If we find that the instantiations of this function take
+// up too much space, we can make G / S abstract base classes that are
+// overridden by concrete classes.
+template <typename G, typename S>
+REQUIRES_SHARED(Locks::mutator_lock_)
+bool PerformConversions(Thread* self,
+ Handle<mirror::ObjectArray<mirror::Class>> from_types,
+ Handle<mirror::ObjectArray<mirror::Class>> to_types,
+ G* getter,
+ S* setter,
+ int32_t num_conversions);
+
+// A convenience wrapper around |PerformConversions|, for the case where
+// the setter and getter are both ShadowFrame based.
template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self,
Handle<mirror::MethodType> callsite_type,
@@ -79,15 +129,80 @@
const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
ShadowFrame* callee_frame);
-// Similar to |ConvertAndCopyArgumentsFromCallerFrame|, except that the
-// arguments are copied from an |EmulatedStackFrame|.
-template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
-bool ConvertAndCopyArgumentsFromEmulatedStackFrame(Thread* self,
- ObjPtr<mirror::Object> emulated_stack_frame,
- Handle<mirror::MethodType> callee_type,
- const uint32_t first_dest_reg,
- ShadowFrame* callee_frame);
+// A convenience class that allows for iteration through a list of
+// input argument registers |arg| for non-range invokes or a list of
+// consecutive registers starting with a given based for range
+// invokes.
+//
+// This is used to iterate over input arguments while performing standard
+// argument conversions.
+template <bool is_range> class ShadowFrameGetter {
+ public:
+ ShadowFrameGetter(size_t first_src_reg,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ const ShadowFrame& shadow_frame) :
+ first_src_reg_(first_src_reg),
+ arg_(arg),
+ shadow_frame_(shadow_frame),
+ arg_index_(0) {
+ }
+ ALWAYS_INLINE uint32_t Get() REQUIRES_SHARED(Locks::mutator_lock_) {
+ const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]);
+ ++arg_index_;
+
+ return shadow_frame_.GetVReg(next);
+ }
+
+ ALWAYS_INLINE int64_t GetLong() REQUIRES_SHARED(Locks::mutator_lock_) {
+ const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]);
+ arg_index_ += 2;
+
+ return shadow_frame_.GetVRegLong(next);
+ }
+
+ ALWAYS_INLINE ObjPtr<mirror::Object> GetReference() REQUIRES_SHARED(Locks::mutator_lock_) {
+ const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]);
+ ++arg_index_;
+
+ return shadow_frame_.GetVRegReference(next);
+ }
+
+ private:
+ const size_t first_src_reg_;
+ const uint32_t (&arg_)[Instruction::kMaxVarArgRegs];
+ const ShadowFrame& shadow_frame_;
+ size_t arg_index_;
+};
+
+// A convenience class that allows values to be written to a given shadow frame,
+// starting at location |first_dst_reg|.
+class ShadowFrameSetter {
+ public:
+ ShadowFrameSetter(ShadowFrame* shadow_frame,
+ size_t first_dst_reg) :
+ shadow_frame_(shadow_frame),
+ arg_index_(first_dst_reg) {
+ }
+
+ ALWAYS_INLINE void Set(uint32_t value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame_->SetVReg(arg_index_++, value);
+ }
+
+ ALWAYS_INLINE void SetReference(ObjPtr<mirror::Object> value)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame_->SetVRegReference(arg_index_++, value.Ptr());
+ }
+
+ ALWAYS_INLINE void SetLong(int64_t value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame_->SetVRegLong(arg_index_, value);
+ arg_index_ += 2;
+ }
+
+ private:
+ ShadowFrame* shadow_frame_;
+ size_t arg_index_;
+};
} // namespace art
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 9992a9e..bbdb2af 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -526,8 +526,7 @@
template<VerifyObjectFlags kVerifyFlags,
ReadBarrierOption kReadBarrierOption>
inline IfTable* Class::GetIfTable() {
- return GetFieldObject<IfTable, kVerifyFlags, kReadBarrierOption>(
- OFFSET_OF_OBJECT_MEMBER(Class, iftable_));
+ return GetFieldObject<IfTable, kVerifyFlags, kReadBarrierOption>(IfTableOffset());
}
inline int32_t Class::GetIfTableCount() {
@@ -539,7 +538,7 @@
}
inline void Class::SetIfTable(ObjPtr<IfTable> new_iftable) {
- SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, iftable_), new_iftable);
+ SetFieldObject<false>(IfTableOffset(), new_iftable);
}
inline LengthPrefixedArray<ArtField>* Class::GetIFieldsPtr() {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 5793795..57bb2ed 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -561,7 +561,7 @@
// The size of java.lang.Class.class.
static uint32_t ClassClassSize(PointerSize pointer_size) {
// The number of vtable entries in java.lang.Class.
- uint32_t vtable_entries = Object::kVTableLength + 72;
+ uint32_t vtable_entries = Object::kVTableLength + 73;
return ComputeClassSize(true, vtable_entries, 0, 0, 4, 1, 0, pointer_size);
}
@@ -680,6 +680,10 @@
return MemberOffset(OFFSETOF_MEMBER(Class, dex_cache_));
}
+ static MemberOffset IfTableOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(Class, iftable_));
+ }
+
enum {
kDumpClassFullDetail = 1,
kDumpClassClassLoader = (1 << 1),
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index df3865b..c7a123b 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -174,14 +174,9 @@
// tell the compiler to treat "Read" as a template rather than a field or
// function. Otherwise, on encountering the "<" token, the compiler would
// treat "Read" as a field.
- T* before = source.object.template Read<kReadBarrierOption>();
- // TODO(narayan): This additional GC root construction and assignment
- // is unnecessary. We're already operating on a copy of the DexCachePair
- // that's in the cache.
- GcRoot<T> root(before);
- visitor.VisitRootIfNonNull(root.AddressWithoutBarrier());
- if (root.Read() != before) {
- source.object = GcRoot<T>(root.Read());
+ T* const before = source.object.template Read<kReadBarrierOption>();
+ visitor.VisitRootIfNonNull(source.object.AddressWithoutBarrier());
+ if (source.object.template Read<kReadBarrierOption>() != before) {
pairs[i].store(source, std::memory_order_relaxed);
}
}
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index f5d1b80..1ae694d 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -74,6 +74,7 @@
static GcRoot<T> Lookup(std::atomic<DexCachePair<T>>* dex_cache,
uint32_t idx,
uint32_t cache_size) {
+ DCHECK_NE(cache_size, 0u);
DexCachePair<T> element = dex_cache[idx % cache_size].load(std::memory_order_relaxed);
if (idx != element.index) {
return GcRoot<T>(nullptr);
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index e95ca21..916f1cf 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -55,9 +55,8 @@
EXPECT_EQ(java_lang_dex_file_->NumTypeIds(), dex_cache->NumResolvedTypes());
EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods());
EXPECT_EQ(java_lang_dex_file_->NumFieldIds(), dex_cache->NumResolvedFields());
- // This should always be zero because the -Xexperimental:method-handles isn't
- // set.
- EXPECT_EQ(0u, dex_cache->NumResolvedMethodTypes());
+ EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes()
+ || java_lang_dex_file_->NumProtoIds() == dex_cache->NumResolvedMethodTypes());
}
TEST_F(DexCacheMethodHandlesTest, Open) {
diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc
new file mode 100644
index 0000000..4ba71ea
--- /dev/null
+++ b/runtime/mirror/emulated_stack_frame.cc
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2016 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 "emulated_stack_frame.h"
+
+#include "class-inl.h"
+#include "gc_root-inl.h"
+#include "jvalue-inl.h"
+#include "method_handles.h"
+#include "method_handles-inl.h"
+#include "reflection-inl.h"
+
+namespace art {
+namespace mirror {
+
+GcRoot<mirror::Class> EmulatedStackFrame::static_class_;
+
+// Calculates the size of a stack frame based on the size of its argument
+// types and return types.
+static void CalculateFrameAndReferencesSize(ObjPtr<mirror::ObjectArray<mirror::Class>> p_types,
+ ObjPtr<mirror::Class> r_type,
+ size_t* frame_size_out,
+ size_t* references_size_out)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const size_t length = p_types->GetLength();
+ size_t frame_size = 0;
+ size_t references_size = 0;
+ for (size_t i = 0; i < length; ++i) {
+ ObjPtr<mirror::Class> type = p_types->GetWithoutChecks(i);
+ const Primitive::Type primitive_type = type->GetPrimitiveType();
+ if (primitive_type == Primitive::kPrimNot) {
+ references_size++;
+ } else if (Primitive::Is64BitType(primitive_type)) {
+ frame_size += 8;
+ } else {
+ frame_size += 4;
+ }
+ }
+
+ const Primitive::Type return_type = r_type->GetPrimitiveType();
+ if (return_type == Primitive::kPrimNot) {
+ references_size++;
+ } else if (Primitive::Is64BitType(return_type)) {
+ frame_size += 8;
+ } else {
+ frame_size += 4;
+ }
+
+ (*frame_size_out) = frame_size;
+ (*references_size_out) = references_size;
+}
+
+// Allows for read or write access to an emulated stack frame. Each
+// accessor index has an associated index into the references / stack frame
+// arrays which is incremented on every read or write to the frame.
+//
+// This class is used in conjunction with PerformConversions, either as a setter
+// or as a getter.
+class EmulatedStackFrameAccessor {
+ public:
+ EmulatedStackFrameAccessor(Handle<mirror::ObjectArray<mirror::Object>> references,
+ Handle<mirror::ByteArray> stack_frame,
+ size_t stack_frame_size) :
+ references_(references),
+ stack_frame_(stack_frame),
+ stack_frame_size_(stack_frame_size),
+ reference_idx_(0u),
+ stack_frame_idx_(0u) {
+ }
+
+ ALWAYS_INLINE void SetReference(ObjPtr<mirror::Object> reference)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ references_->Set(reference_idx_++, reference);
+ }
+
+ ALWAYS_INLINE void Set(const uint32_t value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ int8_t* array = stack_frame_->GetData();
+
+ CHECK_LE((stack_frame_idx_ + 4u), stack_frame_size_);
+ memcpy(array + stack_frame_idx_, &value, sizeof(uint32_t));
+ stack_frame_idx_ += 4u;
+ }
+
+ ALWAYS_INLINE void SetLong(const int64_t value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ int8_t* array = stack_frame_->GetData();
+
+ CHECK_LE((stack_frame_idx_ + 8u), stack_frame_size_);
+ memcpy(array + stack_frame_idx_, &value, sizeof(int64_t));
+ stack_frame_idx_ += 8u;
+ }
+
+ ALWAYS_INLINE ObjPtr<mirror::Object> GetReference() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return ObjPtr<mirror::Object>(references_->Get(reference_idx_++));
+ }
+
+ ALWAYS_INLINE uint32_t Get() REQUIRES_SHARED(Locks::mutator_lock_) {
+ const int8_t* array = stack_frame_->GetData();
+
+ CHECK_LE((stack_frame_idx_ + 4u), stack_frame_size_);
+ uint32_t val = 0;
+
+ memcpy(&val, array + stack_frame_idx_, sizeof(uint32_t));
+ stack_frame_idx_ += 4u;
+ return val;
+ }
+
+ ALWAYS_INLINE int64_t GetLong() REQUIRES_SHARED(Locks::mutator_lock_) {
+ const int8_t* array = stack_frame_->GetData();
+
+ CHECK_LE((stack_frame_idx_ + 8u), stack_frame_size_);
+ int64_t val = 0;
+
+ memcpy(&val, array + stack_frame_idx_, sizeof(int64_t));
+ stack_frame_idx_ += 8u;
+ return val;
+ }
+
+ private:
+ Handle<mirror::ObjectArray<mirror::Object>> references_;
+ Handle<mirror::ByteArray> stack_frame_;
+ const size_t stack_frame_size_;
+
+ size_t reference_idx_;
+ size_t stack_frame_idx_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmulatedStackFrameAccessor);
+};
+
+template <bool is_range>
+mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs(
+ Thread* self,
+ Handle<mirror::MethodType> caller_type,
+ Handle<mirror::MethodType> callee_type,
+ const ShadowFrame& caller_frame,
+ const uint32_t first_src_reg,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) {
+ StackHandleScope<6> hs(self);
+
+ // Step 1: We must throw a WrongMethodTypeException if there's a mismatch in the
+ // number of arguments between the caller and the callsite.
+ Handle<mirror::ObjectArray<mirror::Class>> from_types(hs.NewHandle(caller_type->GetPTypes()));
+ Handle<mirror::ObjectArray<mirror::Class>> to_types(hs.NewHandle(callee_type->GetPTypes()));
+
+ const int32_t num_method_params = from_types->GetLength();
+ if (to_types->GetLength() != num_method_params) {
+ ThrowWrongMethodTypeException(callee_type.Get(), caller_type.Get());
+ return nullptr;
+ }
+
+ // Step 2: Calculate the size of the reference / byte arrays in the emulated
+ // stack frame.
+ size_t frame_size = 0;
+ size_t refs_size = 0;
+ Handle<mirror::Class> r_type(hs.NewHandle(callee_type->GetRType()));
+ CalculateFrameAndReferencesSize(to_types.Get(), r_type.Get(), &frame_size, &refs_size);
+
+ // Step 3 : Allocate the arrays.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ObjPtr<mirror::Class> array_class(class_linker->GetClassRoot(ClassLinker::kObjectArrayClass));
+
+ Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(
+ mirror::ObjectArray<mirror::Object>::Alloc(self, array_class, refs_size)));
+ Handle<ByteArray> stack_frame(hs.NewHandle(ByteArray::Alloc(self, frame_size)));
+
+ // Step 4 : Perform argument conversions (if required).
+ ShadowFrameGetter<is_range> getter(first_src_reg, arg, caller_frame);
+ EmulatedStackFrameAccessor setter(references, stack_frame, stack_frame->GetLength());
+ if (!PerformConversions<ShadowFrameGetter<is_range>, EmulatedStackFrameAccessor>(
+ self, from_types, to_types, &getter, &setter, num_method_params)) {
+ return nullptr;
+ }
+
+ // Step 5: Construct the EmulatedStackFrame object.
+ Handle<EmulatedStackFrame> sf(hs.NewHandle(
+ ObjPtr<EmulatedStackFrame>::DownCast(StaticClass()->AllocObject(self))));
+ sf->SetFieldObject<false>(TypeOffset(), callee_type.Get());
+ sf->SetFieldObject<false>(ReferencesOffset(), references.Get());
+ sf->SetFieldObject<false>(StackFrameOffset(), stack_frame.Get());
+
+ return sf.Get();
+}
+
+bool EmulatedStackFrame::WriteToShadowFrame(Thread* self,
+ Handle<mirror::MethodType> callee_type,
+ const uint32_t first_dest_reg,
+ ShadowFrame* callee_frame) {
+ StackHandleScope<4> hs(self);
+ Handle<mirror::ObjectArray<mirror::Class>> from_types(hs.NewHandle(GetType()->GetPTypes()));
+ Handle<mirror::ObjectArray<mirror::Class>> to_types(hs.NewHandle(callee_type->GetPTypes()));
+
+ const int32_t num_method_params = from_types->GetLength();
+ if (to_types->GetLength() != num_method_params) {
+ ThrowWrongMethodTypeException(callee_type.Get(), GetType());
+ return false;
+ }
+
+ Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(GetReferences()));
+ Handle<ByteArray> stack_frame(hs.NewHandle(GetStackFrame()));
+
+ EmulatedStackFrameAccessor getter(references, stack_frame, stack_frame->GetLength());
+ ShadowFrameSetter setter(callee_frame, first_dest_reg);
+
+ return PerformConversions<EmulatedStackFrameAccessor, ShadowFrameSetter>(
+ self, from_types, to_types, &getter, &setter, num_method_params);
+}
+
+void EmulatedStackFrame::GetReturnValue(Thread* self, JValue* value) {
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Class> r_type(hs.NewHandle(GetType()->GetRType()));
+
+ const Primitive::Type type = r_type->GetPrimitiveType();
+ if (type == Primitive::kPrimNot) {
+ Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(GetReferences()));
+ value->SetL(references->GetWithoutChecks(references->GetLength() - 1));
+ } else {
+ Handle<ByteArray> stack_frame(hs.NewHandle(GetStackFrame()));
+ const int8_t* array = stack_frame->GetData();
+ const size_t length = stack_frame->GetLength();
+ if (Primitive::Is64BitType(type)) {
+ int64_t primitive = 0;
+ memcpy(&primitive, array + length - sizeof(int64_t), sizeof(int64_t));
+ value->SetJ(primitive);
+ } else {
+ uint32_t primitive = 0;
+ memcpy(&primitive, array + length - sizeof(uint32_t), sizeof(uint32_t));
+ value->SetI(primitive);
+ }
+ }
+}
+
+void EmulatedStackFrame::SetReturnValue(Thread* self, const JValue& value) {
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Class> r_type(hs.NewHandle(GetType()->GetRType()));
+
+ const Primitive::Type type = r_type->GetPrimitiveType();
+ if (type == Primitive::kPrimNot) {
+ Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(GetReferences()));
+ references->SetWithoutChecks<false>(references->GetLength() - 1, value.GetL());
+ } else {
+ Handle<ByteArray> stack_frame(hs.NewHandle(GetStackFrame()));
+ int8_t* array = stack_frame->GetData();
+ const size_t length = stack_frame->GetLength();
+ if (Primitive::Is64BitType(type)) {
+ const int64_t primitive = value.GetJ();
+ memcpy(array + length - sizeof(int64_t), &primitive, sizeof(int64_t));
+ } else {
+ const uint32_t primitive = value.GetI();
+ memcpy(array + length - sizeof(uint32_t), &primitive, sizeof(uint32_t));
+ }
+ }
+}
+
+void EmulatedStackFrame::SetClass(Class* klass) {
+ CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+ CHECK(klass != nullptr);
+ static_class_ = GcRoot<Class>(klass);
+}
+
+void EmulatedStackFrame::ResetClass() {
+ CHECK(!static_class_.IsNull());
+ static_class_ = GcRoot<Class>(nullptr);
+}
+
+void EmulatedStackFrame::VisitRoots(RootVisitor* visitor) {
+ static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+// Explicit DoInvokePolymorphic template function declarations.
+#define EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(_is_range) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs<_is_range>( \
+ Thread* self, \
+ Handle<mirror::MethodType> caller_type, \
+ Handle<mirror::MethodType> callee_type, \
+ const ShadowFrame& caller_frame, \
+ const uint32_t first_src_reg, \
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) \
+
+EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(true);
+EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(false);
+#undef EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL
+
+
+} // namespace mirror
+} // namespace art
diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h
new file mode 100644
index 0000000..9fa06b7
--- /dev/null
+++ b/runtime/mirror/emulated_stack_frame.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_EMULATED_STACK_FRAME_H_
+#define ART_RUNTIME_MIRROR_EMULATED_STACK_FRAME_H_
+
+#include "dex_instruction.h"
+#include "method_type.h"
+#include "object.h"
+#include "stack.h"
+#include "string.h"
+#include "utils.h"
+
+namespace art {
+
+struct EmulatedStackFrameOffsets;
+
+namespace mirror {
+
+// C++ mirror of dalvik.system.EmulatedStackFrame
+class MANAGED EmulatedStackFrame : public Object {
+ public:
+ // Creates an emulated stack frame whose type is |frame_type| from
+ // a shadow frame.
+ template <bool is_range>
+ static mirror::EmulatedStackFrame* CreateFromShadowFrameAndArgs(
+ Thread* self,
+ Handle<mirror::MethodType> args_type,
+ Handle<mirror::MethodType> frame_type,
+ const ShadowFrame& caller_frame,
+ const uint32_t first_src_reg,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Writes the contents of this emulated stack frame to the |callee_frame|
+ // whose type is |callee_type|, starting at |first_dest_reg|.
+ bool WriteToShadowFrame(
+ Thread* self,
+ Handle<mirror::MethodType> callee_type,
+ const uint32_t first_dest_reg,
+ ShadowFrame* callee_frame) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Sets |value| to the return value written to this emulated stack frame (if any).
+ void GetReturnValue(Thread* self, JValue* value) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Sets the return value slot of this emulated stack frame to |value|.
+ void SetReturnValue(Thread* self, const JValue& value) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_class_.Read();
+ }
+
+ mirror::MethodType* GetType() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldObject<MethodType>(OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, type_));
+ }
+
+ mirror::ObjectArray<mirror::Object>* GetReferences() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldObject<mirror::ObjectArray<mirror::Object>>(
+ OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, references_));
+ }
+
+ mirror::ByteArray* GetStackFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldObject<mirror::ByteArray>(
+ OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, stack_frame_));
+ }
+
+ static MemberOffset TypeOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, type_));
+ }
+
+ static MemberOffset ReferencesOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, references_));
+ }
+
+ static MemberOffset StackFrameOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, stack_frame_));
+ }
+
+ HeapReference<mirror::ObjectArray<mirror::Object>> references_;
+ HeapReference<mirror::ByteArray> stack_frame_;
+ HeapReference<mirror::MethodType> type_;
+
+ static GcRoot<mirror::Class> static_class_; // dalvik.system.EmulatedStackFrame.class
+
+ friend struct art::EmulatedStackFrameOffsets; // for verifying offset information
+ DISALLOW_IMPLICIT_CONSTRUCTORS(EmulatedStackFrame);
+};
+
+} // namespace mirror
+} // namespace art
+
+#endif // ART_RUNTIME_MIRROR_EMULATED_STACK_FRAME_H_
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index 40716ad..7bf9c5b 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -36,6 +36,11 @@
return GetFieldObject<mirror::MethodType>(OFFSET_OF_OBJECT_MEMBER(MethodHandle, method_type_));
}
+ ArtField* GetTargetField() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return reinterpret_cast<ArtField*>(
+ GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
+ }
+
ArtMethod* GetTargetMethod() REQUIRES_SHARED(Locks::mutator_lock_) {
return reinterpret_cast<ArtMethod*>(
GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc
index 0b52931..9b0f872 100644
--- a/runtime/mirror/method_type.cc
+++ b/runtime/mirror/method_type.cc
@@ -65,6 +65,25 @@
return true;
}
+std::string MethodType::PrettyDescriptor() REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::ostringstream ss;
+ ss << "(";
+
+ mirror::ObjectArray<Class>* const p_types = GetPTypes();
+ const int32_t params_length = p_types->GetLength();
+ for (int32_t i = 0; i < params_length; ++i) {
+ ss << p_types->GetWithoutChecks(i)->PrettyDescriptor();
+ if (i != (params_length - 1)) {
+ ss << ", ";
+ }
+ }
+
+ ss << ")";
+ ss << GetRType()->PrettyDescriptor();
+
+ return ss.str();
+}
+
void MethodType::SetClass(Class* klass) {
CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
CHECK(klass != nullptr);
diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h
index 5b50409..fa700b6 100644
--- a/runtime/mirror/method_type.h
+++ b/runtime/mirror/method_type.h
@@ -56,6 +56,10 @@
// iff. they have the same return types and parameter types.
bool IsExactMatch(mirror::MethodType* other) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns the pretty descriptor for this method type, suitable for display in
+ // exception messages and the like.
+ std::string PrettyDescriptor() REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
static MemberOffset FormOffset() {
return MemberOffset(OFFSETOF_MEMBER(MethodType, form_));
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 2e70c9b..3bf9d94 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -275,7 +275,7 @@
}
template<VerifyObjectFlags kVerifyFlags>
-inline bool Object::VerifierInstanceOf(Class* klass) {
+inline bool Object::VerifierInstanceOf(ObjPtr<Class> klass) {
DCHECK(klass != nullptr);
DCHECK(GetClass<kVerifyFlags>() != nullptr);
return klass->IsInterface() || InstanceOf(klass);
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index f1ab72a..886637b 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -119,7 +119,7 @@
// The verifier treats all interfaces as java.lang.Object and relies on runtime checks in
// invoke-interface to detect incompatible interface types.
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- bool VerifierInstanceOf(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ bool VerifierInstanceOf(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ALWAYS_INLINE bool InstanceOf(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index d42bb92..d94b39f 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -160,7 +160,7 @@
const int32_t offset_;
};
-inline String* String::Intern() {
+inline ObjPtr<String> String::Intern() {
return Runtime::Current()->GetInternTable()->InternWeak(this);
}
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index a1b674a..6ce75bc 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -93,7 +93,7 @@
void SetCharAt(int32_t index, uint16_t c) REQUIRES_SHARED(Locks::mutator_lock_);
- String* Intern() REQUIRES_SHARED(Locks::mutator_lock_);
+ ObjPtr<String> Intern() REQUIRES_SHARED(Locks::mutator_lock_);
template <bool kIsInstrumented>
ALWAYS_INLINE static String* AllocFromByteArray(Thread* self, int32_t byte_length,
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 329aae9..6206948 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -446,6 +446,12 @@
return soa.AddLocalReference<jobject>(annotations::GetAnnotationForField(field, klass));
}
+static jlong Field_getArtField(JNIEnv* env, jobject javaField) {
+ ScopedFastNativeObjectAccess soa(env);
+ ArtField* field = soa.Decode<mirror::Field>(javaField)->GetArtField();
+ return reinterpret_cast<jlong>(field);
+}
+
static jobjectArray Field_getDeclaredAnnotations(JNIEnv* env, jobject javaField) {
ScopedFastNativeObjectAccess soa(env);
ArtField* field = soa.Decode<mirror::Field>(javaField)->GetArtField();
@@ -489,6 +495,7 @@
NATIVE_METHOD(Field, getChar, "!(Ljava/lang/Object;)C"),
NATIVE_METHOD(Field, getAnnotationNative,
"!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"),
+ NATIVE_METHOD(Field, getArtField, "!()J"),
NATIVE_METHOD(Field, getDeclaredAnnotations, "!()[Ljava/lang/annotation/Annotation;"),
NATIVE_METHOD(Field, getSignatureAnnotation, "!()[Ljava/lang/String;"),
NATIVE_METHOD(Field, getDouble, "!(Ljava/lang/Object;)D"),
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index cdf4b14..644df07 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -65,7 +65,7 @@
mirror::HeapReference<mirror::Object>* field_addr =
reinterpret_cast<mirror::HeapReference<mirror::Object>*>(
reinterpret_cast<uint8_t*>(obj.Ptr()) + static_cast<size_t>(offset));
- ReadBarrier::Barrier<mirror::Object, kWithReadBarrier, /*kAlwaysUpdateField*/true>(
+ ReadBarrier::Barrier<mirror::Object, kWithReadBarrier, /* kAlwaysUpdateField */ true>(
obj.Ptr(),
MemberOffset(offset),
field_addr);
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 68f71f7..fde0a2c 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -260,7 +260,7 @@
ArtField* const dex_file_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
ObjPtr<mirror::Object> dex_path_list =
- soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+ soa.DecodeField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
GetObject(class_loader.Get());
if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index de6683c..b323aef 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -18,9 +18,12 @@
defaults: ["art_defaults"],
host_supported: true,
srcs: ["events.cc",
- "heap.cc",
"object_tagging.cc",
"OpenjdkJvmTi.cc",
+ "ti_class.cc",
+ "ti_heap.cc",
+ "ti_method.cc",
+ "ti_stack.cc",
"transform.cc"],
include_dirs: ["art/runtime"],
shared_libs: [
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 7292946..d9031ea 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -39,7 +39,6 @@
#include "art_jvmti.h"
#include "base/mutex.h"
#include "events-inl.h"
-#include "heap.h"
#include "jni_env_ext-inl.h"
#include "object_tagging.h"
#include "obj_ptr-inl.h"
@@ -47,6 +46,10 @@
#include "scoped_thread_state_change-inl.h"
#include "thread_list.h"
#include "thread-inl.h"
+#include "ti_class.h"
+#include "ti_heap.h"
+#include "ti_method.h"
+#include "ti_stack.h"
#include "transform.h"
// TODO Remove this at some point by annotating all the methods. It was put in to make the skeleton
@@ -201,7 +204,12 @@
jint max_frame_count,
jvmtiFrameInfo* frame_buffer,
jint* count_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return StackUtil::GetStackTrace(env,
+ thread,
+ start_depth,
+ max_frame_count,
+ frame_buffer,
+ count_ptr);
}
static jvmtiError GetAllStackTraces(jvmtiEnv* env,
@@ -323,11 +331,22 @@
jint* count_ptr,
jobject** object_result_ptr,
jlong** tag_result_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ JNIEnv* jni_env = GetJniEnv(env);
+ if (jni_env == nullptr) {
+ return ERR(INTERNAL);
+ }
+
+ art::ScopedObjectAccess soa(jni_env);
+ return gObjectTagTable.GetTaggedObjects(env,
+ tag_count,
+ tags,
+ count_ptr,
+ object_result_ptr,
+ tag_result_ptr);
}
static jvmtiError ForceGarbageCollection(jvmtiEnv* env) {
- return ERR(NOT_IMPLEMENTED);
+ return HeapUtil::ForceGarbageCollection(env);
}
static jvmtiError IterateOverObjectsReachableFromObject(
@@ -488,7 +507,7 @@
jclass klass,
char** signature_ptr,
char** generic_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassSignature(env, klass, signature_ptr, generic_ptr);
}
static jvmtiError GetClassStatus(jvmtiEnv* env, jclass klass, jint* status_ptr) {
@@ -624,19 +643,19 @@
char** name_ptr,
char** signature_ptr,
char** generic_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetMethodName(env, method, name_ptr, signature_ptr, generic_ptr);
}
static jvmtiError GetMethodDeclaringClass(jvmtiEnv* env,
jmethodID method,
jclass* declaring_class_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetMethodDeclaringClass(env, method, declaring_class_ptr);
}
static jvmtiError GetMethodModifiers(jvmtiEnv* env,
jmethodID method,
jint* modifiers_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetMethodModifiers(env, method, modifiers_ptr);
}
static jvmtiError GetMaxLocals(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 66d0937..a321124 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -37,6 +37,8 @@
#include <jni.h>
#include "base/casts.h"
+#include "base/logging.h"
+#include "base/macros.h"
#include "events.h"
#include "java_vm_ext.h"
#include "jni_env_ext.h"
@@ -80,6 +82,45 @@
return ret_value;
}
+class JvmtiDeleter {
+ public:
+ JvmtiDeleter() : env_(nullptr) {}
+ explicit JvmtiDeleter(jvmtiEnv* env) : env_(env) {}
+
+ JvmtiDeleter(JvmtiDeleter&) = default;
+ JvmtiDeleter(JvmtiDeleter&&) = default;
+ JvmtiDeleter& operator=(const JvmtiDeleter&) = default;
+
+ void operator()(unsigned char* ptr) const {
+ CHECK(env_ != nullptr);
+ jvmtiError ret = env_->Deallocate(ptr);
+ CHECK(ret == ERR(NONE));
+ }
+
+ private:
+ mutable jvmtiEnv* env_;
+};
+
+using JvmtiUniquePtr = std::unique_ptr<unsigned char, JvmtiDeleter>;
+
+ALWAYS_INLINE
+static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, unsigned char* mem) {
+ return JvmtiUniquePtr(mem, JvmtiDeleter(env));
+}
+
+ALWAYS_INLINE
+static inline jvmtiError CopyString(jvmtiEnv* env, const char* src, unsigned char** copy) {
+ size_t len = strlen(src) + 1;
+ unsigned char* buf;
+ jvmtiError ret = env->Allocate(len, &buf);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ strcpy(reinterpret_cast<char*>(buf), src);
+ *copy = buf;
+ return ret;
+}
+
} // namespace openjdkjvmti
#endif // ART_RUNTIME_OPENJDKJVMTI_ART_JVMTI_H_
diff --git a/runtime/openjdkjvmti/jvmti_allocator.h b/runtime/openjdkjvmti/jvmti_allocator.h
new file mode 100644
index 0000000..1225c14
--- /dev/null
+++ b/runtime/openjdkjvmti/jvmti_allocator.h
@@ -0,0 +1,170 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_
+#define ART_RUNTIME_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+template <typename T> class JvmtiAllocator;
+
+template <>
+class JvmtiAllocator<void> {
+ public:
+ typedef void value_type;
+ typedef void* pointer;
+ typedef const void* const_pointer;
+
+ template <typename U>
+ struct rebind {
+ typedef JvmtiAllocator<U> other;
+ };
+
+ explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
+
+ template <typename U>
+ JvmtiAllocator(const JvmtiAllocator<U>& other) // NOLINT, implicit
+ : env_(other.env_) {}
+
+ JvmtiAllocator(const JvmtiAllocator& other) = default;
+ JvmtiAllocator& operator=(const JvmtiAllocator& other) = default;
+ ~JvmtiAllocator() = default;
+
+ private:
+ jvmtiEnv* env_;
+
+ template <typename U>
+ friend class JvmtiAllocator;
+
+ template <typename U>
+ friend bool operator==(const JvmtiAllocator<U>& lhs, const JvmtiAllocator<U>& rhs);
+};
+
+template <typename T>
+class JvmtiAllocator {
+ public:
+ typedef T value_type;
+ typedef T* pointer;
+ typedef T& reference;
+ typedef const T* const_pointer;
+ typedef const T& const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+
+ template <typename U>
+ struct rebind {
+ typedef JvmtiAllocator<U> other;
+ };
+
+ explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
+
+ template <typename U>
+ JvmtiAllocator(const JvmtiAllocator<U>& other) // NOLINT, implicit
+ : env_(other.env_) {}
+
+ JvmtiAllocator(const JvmtiAllocator& other) = default;
+ JvmtiAllocator& operator=(const JvmtiAllocator& other) = default;
+ ~JvmtiAllocator() = default;
+
+ size_type max_size() const {
+ return static_cast<size_type>(-1) / sizeof(T);
+ }
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+
+ pointer allocate(size_type n, JvmtiAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
+ DCHECK_LE(n, max_size());
+ if (env_ == nullptr) {
+ T* result = reinterpret_cast<T*>(malloc(n * sizeof(T)));
+ CHECK(result != nullptr || n == 0u); // Abort if malloc() fails.
+ return result;
+ } else {
+ unsigned char* result;
+ jvmtiError alloc_error = env_->Allocate(n * sizeof(T), &result);
+ CHECK(alloc_error == JVMTI_ERROR_NONE);
+ return reinterpret_cast<T*>(result);
+ }
+ }
+ void deallocate(pointer p, size_type n ATTRIBUTE_UNUSED) {
+ if (env_ == nullptr) {
+ free(p);
+ } else {
+ jvmtiError dealloc_error = env_->Deallocate(reinterpret_cast<unsigned char*>(p));
+ CHECK(dealloc_error == JVMTI_ERROR_NONE);
+ }
+ }
+
+ void construct(pointer p, const_reference val) {
+ new (static_cast<void*>(p)) value_type(val);
+ }
+ template <class U, class... Args>
+ void construct(U* p, Args&&... args) {
+ ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
+ }
+ void destroy(pointer p) {
+ p->~value_type();
+ }
+
+ inline bool operator==(JvmtiAllocator const& other) {
+ return env_ == other.env_;
+ }
+ inline bool operator!=(JvmtiAllocator const& other) {
+ return !operator==(other);
+ }
+
+ private:
+ jvmtiEnv* env_;
+
+ template <typename U>
+ friend class JvmtiAllocator;
+
+ template <typename U>
+ friend bool operator==(const JvmtiAllocator<U>& lhs, const JvmtiAllocator<U>& rhs);
+};
+
+template <typename T>
+inline bool operator==(const JvmtiAllocator<T>& lhs, const JvmtiAllocator<T>& rhs) {
+ return lhs.env_ == rhs.env_;
+}
+
+template <typename T>
+inline bool operator!=(const JvmtiAllocator<T>& lhs, const JvmtiAllocator<T>& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_
diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc
index f16b023..b359f36 100644
--- a/runtime/openjdkjvmti/object_tagging.cc
+++ b/runtime/openjdkjvmti/object_tagging.cc
@@ -39,6 +39,7 @@
#include "gc/allocation_listener.h"
#include "instrumentation.h"
#include "jni_env_ext-inl.h"
+#include "jvmti_allocator.h"
#include "mirror/class.h"
#include "mirror/object.h"
#include "runtime.h"
@@ -211,4 +212,142 @@
// TODO: consider rehash here.
}
+template <typename T, class Allocator = std::allocator<T>>
+struct ReleasableContainer {
+ using allocator_type = Allocator;
+
+ explicit ReleasableContainer(const allocator_type& alloc, size_t reserve = 10)
+ : allocator(alloc),
+ data(reserve > 0 ? allocator.allocate(reserve) : nullptr),
+ size(0),
+ capacity(reserve) {
+ }
+
+ ~ReleasableContainer() {
+ if (data != nullptr) {
+ allocator.deallocate(data, capacity);
+ capacity = 0;
+ size = 0;
+ }
+ }
+
+ T* Release() {
+ T* tmp = data;
+
+ data = nullptr;
+ size = 0;
+ capacity = 0;
+
+ return tmp;
+ }
+
+ void Resize(size_t new_capacity) {
+ CHECK_GT(new_capacity, capacity);
+
+ T* tmp = allocator.allocate(new_capacity);
+ DCHECK(tmp != nullptr);
+ if (data != nullptr) {
+ memcpy(tmp, data, sizeof(T) * size);
+ }
+ T* old = data;
+ data = tmp;
+ allocator.deallocate(old, capacity);
+ capacity = new_capacity;
+ }
+
+ void Pushback(const T& elem) {
+ if (size == capacity) {
+ size_t new_capacity = 2 * capacity + 1;
+ Resize(new_capacity);
+ }
+ data[size++] = elem;
+ }
+
+ Allocator allocator;
+ T* data;
+ size_t size;
+ size_t capacity;
+};
+
+jvmtiError ObjectTagTable::GetTaggedObjects(jvmtiEnv* jvmti_env,
+ jint tag_count,
+ const jlong* tags,
+ jint* count_ptr,
+ jobject** object_result_ptr,
+ jlong** tag_result_ptr) {
+ if (tag_count < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ if (tag_count > 0) {
+ for (size_t i = 0; i != static_cast<size_t>(tag_count); ++i) {
+ if (tags[i] == 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ }
+ }
+ if (tags == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ if (count_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::Thread* self = art::Thread::Current();
+ art::MutexLock mu(self, allow_disallow_lock_);
+ Wait(self);
+
+ art::JNIEnvExt* jni_env = self->GetJniEnv();
+
+ constexpr size_t kDefaultSize = 10;
+ size_t initial_object_size;
+ size_t initial_tag_size;
+ if (tag_count == 0) {
+ initial_object_size = (object_result_ptr != nullptr) ? tagged_objects_.size() : 0;
+ initial_tag_size = (tag_result_ptr != nullptr) ? tagged_objects_.size() : 0;
+ } else {
+ initial_object_size = initial_tag_size = kDefaultSize;
+ }
+ JvmtiAllocator<void> allocator(jvmti_env);
+ ReleasableContainer<jobject, JvmtiAllocator<jobject>> selected_objects(allocator, initial_object_size);
+ ReleasableContainer<jlong, JvmtiAllocator<jlong>> selected_tags(allocator, initial_tag_size);
+
+ size_t count = 0;
+ for (auto& pair : tagged_objects_) {
+ bool select;
+ if (tag_count > 0) {
+ select = false;
+ for (size_t i = 0; i != static_cast<size_t>(tag_count); ++i) {
+ if (tags[i] == pair.second) {
+ select = true;
+ break;
+ }
+ }
+ } else {
+ select = true;
+ }
+
+ if (select) {
+ art::mirror::Object* obj = pair.first.Read<art::kWithReadBarrier>();
+ if (obj != nullptr) {
+ count++;
+ if (object_result_ptr != nullptr) {
+ selected_objects.Pushback(jni_env->AddLocalReference<jobject>(obj));
+ }
+ if (tag_result_ptr != nullptr) {
+ selected_tags.Pushback(pair.second);
+ }
+ }
+ }
+ }
+
+ if (object_result_ptr != nullptr) {
+ *object_result_ptr = selected_objects.Release();
+ }
+ if (tag_result_ptr != nullptr) {
+ *tag_result_ptr = selected_tags.Release();
+ }
+ *count_ptr = static_cast<jint>(count);
+ return ERR(NONE);
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/object_tagging.h b/runtime/openjdkjvmti/object_tagging.h
index 579dc22..071d139 100644
--- a/runtime/openjdkjvmti/object_tagging.h
+++ b/runtime/openjdkjvmti/object_tagging.h
@@ -23,6 +23,7 @@
#include "gc/system_weak.h"
#include "gc_root-inl.h"
#include "globals.h"
+#include "jvmti.h"
#include "mirror/object.h"
#include "thread-inl.h"
@@ -64,6 +65,15 @@
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(!allow_disallow_lock_);
+ jvmtiError GetTaggedObjects(jvmtiEnv* jvmti_env,
+ jint tag_count,
+ const jlong* tags,
+ jint* count_ptr,
+ jobject** object_result_ptr,
+ jlong** tag_result_ptr)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ REQUIRES(!allow_disallow_lock_);
+
private:
bool SetLocked(art::Thread* self, art::mirror::Object* obj, jlong tag)
REQUIRES_SHARED(art::Locks::mutator_lock_)
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
new file mode 100644
index 0000000..de2076a
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -0,0 +1,73 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_class.h"
+
+#include "art_jvmti.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env,
+ jclass jklass,
+ char** signature_ptr,
+ char** generic_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+
+ JvmtiUniquePtr sig_copy;
+ if (signature_ptr != nullptr) {
+ std::string storage;
+ const char* descriptor = klass->GetDescriptor(&storage);
+
+ unsigned char* tmp;
+ jvmtiError ret = CopyString(env, descriptor, &tmp);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ sig_copy = MakeJvmtiUniquePtr(env, tmp);
+ *signature_ptr = reinterpret_cast<char*>(tmp);
+ }
+
+ // TODO: Support generic signature.
+ *generic_ptr = nullptr;
+
+ // Everything is fine, release the buffers.
+ sig_copy.release();
+
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h
new file mode 100644
index 0000000..caa77d4
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class ClassUtil {
+ public:
+ static jvmtiError GetClassSignature(jvmtiEnv* env,
+ jclass klass,
+ char** signature_ptr,
+ char** generic_ptr);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_H_
diff --git a/runtime/openjdkjvmti/heap.cc b/runtime/openjdkjvmti/ti_heap.cc
similarity index 97%
rename from runtime/openjdkjvmti/heap.cc
rename to runtime/openjdkjvmti/ti_heap.cc
index 1799e19..6b20743 100644
--- a/runtime/openjdkjvmti/heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "heap.h"
+#include "ti_heap.h"
#include "art_jvmti.h"
#include "base/macros.h"
@@ -210,4 +210,10 @@
return ERR(NONE);
}
+jvmtiError HeapUtil::ForceGarbageCollection(jvmtiEnv* env ATTRIBUTE_UNUSED) {
+ art::Runtime::Current()->GetHeap()->CollectGarbage(false);
+
+ return ERR(NONE);
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/heap.h b/runtime/openjdkjvmti/ti_heap.h
similarity index 86%
rename from runtime/openjdkjvmti/heap.h
rename to runtime/openjdkjvmti/ti_heap.h
index b6becb9..570dd0c 100644
--- a/runtime/openjdkjvmti/heap.h
+++ b/runtime/openjdkjvmti/ti_heap.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_OPENJDKJVMTI_HEAP_H_
-#define ART_RUNTIME_OPENJDKJVMTI_HEAP_H_
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_
#include "jvmti.h"
@@ -36,6 +36,8 @@
const jvmtiHeapCallbacks* callbacks,
const void* user_data);
+ static jvmtiError ForceGarbageCollection(jvmtiEnv* env);
+
ObjectTagTable* GetTags() {
return tags_;
}
@@ -46,4 +48,4 @@
} // namespace openjdkjvmti
-#endif // ART_RUNTIME_OPENJDKJVMTI_HEAP_H_
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
new file mode 100644
index 0000000..6210936
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -0,0 +1,132 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_method.h"
+
+#include "art_jvmti.h"
+#include "art_method-inl.h"
+#include "base/enums.h"
+#include "modifiers.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace openjdkjvmti {
+
+jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env,
+ jmethodID method,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtMethod* art_method = soa.DecodeMethod(method);
+ art_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
+
+ JvmtiUniquePtr name_copy;
+ if (name_ptr != nullptr) {
+ const char* method_name = art_method->GetName();
+ if (method_name == nullptr) {
+ method_name = "<error>";
+ }
+ unsigned char* tmp;
+ jvmtiError ret = CopyString(env, method_name, &tmp);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ name_copy = MakeJvmtiUniquePtr(env, tmp);
+ *name_ptr = reinterpret_cast<char*>(tmp);
+ }
+
+ JvmtiUniquePtr signature_copy;
+ if (signature_ptr != nullptr) {
+ const art::Signature sig = art_method->GetSignature();
+ std::string str = sig.ToString();
+ unsigned char* tmp;
+ jvmtiError ret = CopyString(env, str.c_str(), &tmp);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ signature_copy = MakeJvmtiUniquePtr(env, tmp);
+ *signature_ptr = reinterpret_cast<char*>(tmp);
+ }
+
+ // TODO: Support generic signature.
+ *generic_ptr = nullptr;
+
+ // Everything is fine, release the buffers.
+ name_copy.release();
+ signature_copy.release();
+
+ return ERR(NONE);
+}
+
+jvmtiError MethodUtil::GetMethodDeclaringClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jclass* declaring_class_ptr) {
+ if (declaring_class_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtMethod* art_method = soa.DecodeMethod(method);
+ // Note: No GetInterfaceMethodIfProxy, we want to actual class.
+
+ art::mirror::Class* klass = art_method->GetDeclaringClass();
+ *declaring_class_ptr = soa.AddLocalReference<jclass>(klass);
+
+ return ERR(NONE);
+}
+
+jvmtiError MethodUtil::GetMethodModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jint* modifiers_ptr) {
+ if (modifiers_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtMethod* art_method = soa.DecodeMethod(method);
+
+ uint32_t modifiers = art_method->GetAccessFlags();
+
+ // Note: Keep this code in sync with Executable.fixMethodFlags.
+ if ((modifiers & art::kAccAbstract) != 0) {
+ modifiers &= ~art::kAccNative;
+ }
+ modifiers &= ~art::kAccSynchronized;
+ if ((modifiers & art::kAccDeclaredSynchronized) != 0) {
+ modifiers |= art::kAccSynchronized;
+ }
+ modifiers &= art::kAccJavaFlagsMask;
+
+ *modifiers_ptr = modifiers;
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
new file mode 100644
index 0000000..43f11f9
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_METHOD_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_METHOD_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class MethodUtil {
+ public:
+ static jvmtiError GetMethodName(jvmtiEnv* env,
+ jmethodID method,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr);
+
+ static jvmtiError GetMethodDeclaringClass(jvmtiEnv* env,
+ jmethodID method,
+ jclass* declaring_class_ptr);
+
+ static jvmtiError GetMethodModifiers(jvmtiEnv* env,
+ jmethodID method,
+ jint* modifiers_ptr);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_METHOD_H_
diff --git a/runtime/openjdkjvmti/ti_stack.cc b/runtime/openjdkjvmti/ti_stack.cc
new file mode 100644
index 0000000..33e677f
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_stack.cc
@@ -0,0 +1,195 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_stack.h"
+
+#include "art_jvmti.h"
+#include "art_method-inl.h"
+#include "base/enums.h"
+#include "dex_file.h"
+#include "dex_file_annotations.h"
+#include "jni_env_ext.h"
+#include "mirror/class.h"
+#include "mirror/dex_cache.h"
+#include "scoped_thread_state_change-inl.h"
+#include "stack.h"
+#include "thread.h"
+#include "thread_pool.h"
+
+namespace openjdkjvmti {
+
+struct GetStackTraceVisitor : public art::StackVisitor {
+ GetStackTraceVisitor(art::Thread* thread_in,
+ art::ScopedObjectAccessAlreadyRunnable& soa_,
+ size_t start_,
+ size_t stop_)
+ : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ soa(soa_),
+ start(start_),
+ stop(stop_) {}
+
+ bool VisitFrame() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::ArtMethod* m = GetMethod();
+ if (m->IsRuntimeMethod()) {
+ return true;
+ }
+
+ if (start == 0) {
+ m = m->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
+ jmethodID id = soa.EncodeMethod(m);
+
+ art::mirror::DexCache* dex_cache = m->GetDexCache();
+ int32_t line_number = -1;
+ if (dex_cache != nullptr) { // be tolerant of bad input
+ const art::DexFile* dex_file = dex_cache->GetDexFile();
+ line_number = art::annotations::GetLineNumFromPC(dex_file, m, GetDexPc(false));
+ }
+
+ jvmtiFrameInfo info = { id, static_cast<jlong>(line_number) };
+ frames.push_back(info);
+
+ if (stop == 1) {
+ return false; // We're done.
+ } else if (stop > 0) {
+ stop--;
+ }
+ } else {
+ start--;
+ }
+
+ return true;
+ }
+
+ art::ScopedObjectAccessAlreadyRunnable& soa;
+ std::vector<jvmtiFrameInfo> frames;
+ size_t start;
+ size_t stop;
+};
+
+struct GetStackTraceClosure : public art::Closure {
+ public:
+ GetStackTraceClosure(size_t start, size_t stop)
+ : start_input(start),
+ stop_input(stop),
+ start_result(0),
+ stop_result(0) {}
+
+ void Run(art::Thread* self) OVERRIDE {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+
+ GetStackTraceVisitor visitor(self, soa, start_input, stop_input);
+ visitor.WalkStack(false);
+
+ frames.swap(visitor.frames);
+ start_result = visitor.start;
+ stop_result = visitor.stop;
+ }
+
+ const size_t start_input;
+ const size_t stop_input;
+
+ std::vector<jvmtiFrameInfo> frames;
+ size_t start_result;
+ size_t stop_result;
+};
+
+jvmtiError StackUtil::GetStackTrace(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED,
+ jthread java_thread,
+ jint start_depth,
+ jint max_frame_count,
+ jvmtiFrameInfo* frame_buffer,
+ jint* count_ptr) {
+ if (java_thread == nullptr) {
+ return ERR(INVALID_THREAD);
+ }
+
+ art::Thread* thread;
+ {
+ // TODO: Need non-aborting call here, to return JVMTI_ERROR_INVALID_THREAD.
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
+ thread = art::Thread::FromManagedThread(soa, java_thread);
+ DCHECK(thread != nullptr);
+ }
+
+ art::ThreadState state = thread->GetState();
+ if (state == art::ThreadState::kStarting ||
+ state == art::ThreadState::kTerminated ||
+ thread->IsStillStarting()) {
+ return ERR(THREAD_NOT_ALIVE);
+ }
+
+ if (max_frame_count < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ if (frame_buffer == nullptr || count_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ if (max_frame_count == 0) {
+ *count_ptr = 0;
+ return ERR(NONE);
+ }
+
+ GetStackTraceClosure closure(start_depth >= 0 ? static_cast<size_t>(start_depth) : 0,
+ start_depth >= 0 ?static_cast<size_t>(max_frame_count) : 0);
+ thread->RequestSynchronousCheckpoint(&closure);
+
+ size_t collected_frames = closure.frames.size();
+
+ // Frames from the top.
+ if (start_depth >= 0) {
+ if (closure.start_result != 0) {
+ // Not enough frames.
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ DCHECK_LE(collected_frames, static_cast<size_t>(max_frame_count));
+ if (closure.frames.size() > 0) {
+ memcpy(frame_buffer, closure.frames.data(), collected_frames * sizeof(jvmtiFrameInfo));
+ }
+ *count_ptr = static_cast<jint>(closure.frames.size());
+ return ERR(NONE);
+ }
+
+ // Frames from the bottom.
+ if (collected_frames < static_cast<size_t>(-start_depth)) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+
+ size_t count = std::min(static_cast<size_t>(-start_depth), static_cast<size_t>(max_frame_count));
+ memcpy(frame_buffer,
+ &closure.frames.data()[collected_frames + start_depth],
+ count * sizeof(jvmtiFrameInfo));
+ *count_ptr = static_cast<jint>(count);
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_stack.h b/runtime/openjdkjvmti/ti_stack.h
new file mode 100644
index 0000000..1931ed3
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_stack.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_STACK_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_STACK_H_
+
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class StackUtil {
+ public:
+ static jvmtiError GetStackTrace(jvmtiEnv* env,
+ jthread thread,
+ jint start_depth,
+ jint max_frame_count,
+ jvmtiFrameInfo* frame_buffer,
+ jint* count_ptr);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_STACK_H_
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index 67e5a81..be95600 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -54,7 +54,7 @@
// Slow-path.
ref = reinterpret_cast<MirrorType*>(Mark(ref));
// If kAlwaysUpdateField is true, update the field atomically. This may fail if mutator
- // updates before us, but it's ok.
+ // updates before us, but it's OK.
if (kAlwaysUpdateField && ref != old_ref) {
obj->CasFieldStrongRelaxedObjectWithoutWriteBarrier<false, false>(
offset, old_ref, ref);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 4e600ae..262608d 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -86,6 +86,7 @@
#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
+#include "mirror/emulated_stack_frame.h"
#include "mirror/field.h"
#include "mirror/method.h"
#include "mirror/method_handle_impl.h"
@@ -1593,6 +1594,7 @@
mirror::Field::VisitRoots(visitor);
mirror::MethodType::VisitRoots(visitor);
mirror::MethodHandleImpl::VisitRoots(visitor);
+ mirror::EmulatedStackFrame::VisitRoots(visitor);
// Visit all the primitive array types classes.
mirror::PrimitiveArray<uint8_t>::VisitRoots(visitor); // BooleanArray
mirror::PrimitiveArray<int8_t>::VisitRoots(visitor); // ByteArray
@@ -1951,31 +1953,31 @@
preinitialization_transaction_->RecordWriteArray(array, index, value);
}
-void Runtime::RecordStrongStringInsertion(mirror::String* s) const {
+void Runtime::RecordStrongStringInsertion(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordStrongStringInsertion(s);
}
-void Runtime::RecordWeakStringInsertion(mirror::String* s) const {
+void Runtime::RecordWeakStringInsertion(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordWeakStringInsertion(s);
}
-void Runtime::RecordStrongStringRemoval(mirror::String* s) const {
+void Runtime::RecordStrongStringRemoval(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordStrongStringRemoval(s);
}
-void Runtime::RecordWeakStringRemoval(mirror::String* s) const {
+void Runtime::RecordWeakStringRemoval(ObjPtr<mirror::String> s) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordWeakStringRemoval(s);
}
-void Runtime::RecordResolveString(mirror::DexCache* dex_cache, uint32_t string_idx) const {
+void Runtime::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx) const {
DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordResolveString(dex_cache, string_idx);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index b25ec23..86464ab 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -316,7 +316,8 @@
}
bool IsMethodHandlesEnabled() const {
- return experimental_flags_ & ExperimentalFlags::kMethodHandles;
+ // return experimental_flags_ & ExperimentalFlags::kMethodHandles;
+ return true;
}
void DisallowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -509,15 +510,15 @@
REQUIRES_SHARED(Locks::mutator_lock_);
void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const
REQUIRES_SHARED(Locks::mutator_lock_);
- void RecordStrongStringInsertion(mirror::String* s) const
+ void RecordStrongStringInsertion(ObjPtr<mirror::String> s) const
REQUIRES(Locks::intern_table_lock_);
- void RecordWeakStringInsertion(mirror::String* s) const
+ void RecordWeakStringInsertion(ObjPtr<mirror::String> s) const
REQUIRES(Locks::intern_table_lock_);
- void RecordStrongStringRemoval(mirror::String* s) const
+ void RecordStrongStringRemoval(ObjPtr<mirror::String> s) const
REQUIRES(Locks::intern_table_lock_);
- void RecordWeakStringRemoval(mirror::String* s) const
+ void RecordWeakStringRemoval(ObjPtr<mirror::String> s) const
REQUIRES(Locks::intern_table_lock_);
- void RecordResolveString(mirror::DexCache* dex_cache, uint32_t string_idx) const
+ void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx) const
REQUIRES_SHARED(Locks::mutator_lock_);
void SetFaultMessage(const std::string& message) REQUIRES(!fault_message_lock_);
diff --git a/runtime/stack.h b/runtime/stack.h
index e9ed497..8a446ec 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -572,8 +572,7 @@
};
protected:
- StackVisitor(Thread* thread, Context* context, StackWalkKind walk_kind)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ StackVisitor(Thread* thread, Context* context, StackWalkKind walk_kind);
bool GetRegisterIfAccessible(uint32_t reg, VRegKind kind, uint32_t* val) const
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index ace5e67..3f7d086 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1175,6 +1175,85 @@
return success;
}
+class BarrierClosure : public Closure {
+ public:
+ explicit BarrierClosure(Closure* wrapped) : wrapped_(wrapped), barrier_(0) {}
+
+ void Run(Thread* self) OVERRIDE {
+ wrapped_->Run(self);
+ barrier_.Pass(self);
+ }
+
+ void Wait(Thread* self) {
+ barrier_.Increment(self, 1);
+ }
+
+ private:
+ Closure* wrapped_;
+ Barrier barrier_;
+};
+
+void Thread::RequestSynchronousCheckpoint(Closure* function) {
+ if (this == Thread::Current()) {
+ // Asked to run on this thread. Just run.
+ function->Run(this);
+ return;
+ }
+ Thread* self = Thread::Current();
+
+ // The current thread is not this thread.
+
+ for (;;) {
+ // If this thread is runnable, try to schedule a checkpoint. Do some gymnastics to not hold the
+ // suspend-count lock for too long.
+ if (GetState() == ThreadState::kRunnable) {
+ BarrierClosure barrier_closure(function);
+ bool installed = false;
+ {
+ MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+ installed = RequestCheckpoint(&barrier_closure);
+ }
+ if (installed) {
+ barrier_closure.Wait(self);
+ return;
+ }
+ // Fall-through.
+ }
+
+ // This thread is not runnable, make sure we stay suspended, then run the checkpoint.
+ // Note: ModifySuspendCountInternal also expects the thread_list_lock to be held in
+ // certain situations.
+ {
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+
+ if (!ModifySuspendCount(self, +1, nullptr, false)) {
+ // Just retry the loop.
+ sched_yield();
+ continue;
+ }
+ }
+
+ while (GetState() == ThreadState::kRunnable) {
+ // We became runnable again. Wait till the suspend triggered in ModifySuspendCount
+ // moves us to suspended.
+ sched_yield();
+ }
+
+ function->Run(this);
+
+ {
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+
+ DCHECK_NE(GetState(), ThreadState::kRunnable);
+ CHECK(ModifySuspendCount(self, -1, nullptr, false));
+ }
+
+ return; // We're done, break out of the loop.
+ }
+}
+
Closure* Thread::GetFlipFunction() {
Atomic<Closure*>* atomic_func = reinterpret_cast<Atomic<Closure*>*>(&tlsPtr_.flip_function);
Closure* func;
@@ -1827,14 +1906,6 @@
}
}
-size_t Thread::NumHandleReferences() {
- size_t count = 0;
- for (BaseHandleScope* cur = tlsPtr_.top_handle_scope; cur != nullptr; cur = cur->GetLink()) {
- count += cur->NumberOfReferences();
- }
- return count;
-}
-
bool Thread::HandleScopeContains(jobject obj) const {
StackReference<mirror::Object>* hs_entry =
reinterpret_cast<StackReference<mirror::Object>*>(obj);
diff --git a/runtime/thread.h b/runtime/thread.h
index 24038f5..75b5b12 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -237,6 +237,8 @@
bool RequestCheckpoint(Closure* function)
REQUIRES(Locks::thread_suspend_count_lock_);
+ void RequestSynchronousCheckpoint(Closure* function)
+ REQUIRES(!Locks::thread_suspend_count_lock_, !Locks::thread_list_lock_);
void SetFlipFunction(Closure* function);
Closure* GetFlipFunction();
@@ -731,9 +733,6 @@
tlsPtr_.stack_end = tlsPtr_.stack_begin + GetStackOverflowReservedBytes(kRuntimeISA);
}
- // Install the protected region for implicit stack checks.
- void InstallImplicitProtection();
-
bool IsHandlingStackOverflow() const {
return tlsPtr_.stack_end == tlsPtr_.stack_begin;
}
@@ -784,19 +783,6 @@
ManagedStack::TopShadowFrameOffset());
}
- // Number of references allocated in JNI ShadowFrames on this thread.
- size_t NumJniShadowFrameReferences() const REQUIRES_SHARED(Locks::mutator_lock_) {
- return tlsPtr_.managed_stack.NumJniShadowFrameReferences();
- }
-
- // Number of references in handle scope on this thread.
- size_t NumHandleReferences();
-
- // Number of references allocated in handle scopes & JNI shadow frames on this thread.
- size_t NumStackReferences() REQUIRES_SHARED(Locks::mutator_lock_) {
- return NumHandleReferences() + NumJniShadowFrameReferences();
- }
-
// Is the given obj in this thread's stack indirect reference table?
bool HandleScopeContains(jobject obj) const;
@@ -982,11 +968,6 @@
tlsPtr_.held_mutexes[level] = mutex;
}
- void RunCheckpointFunction();
-
- bool PassActiveSuspendBarriers(Thread* self)
- REQUIRES(!Locks::thread_suspend_count_lock_);
-
void ClearSuspendBarrier(AtomicInteger* target)
REQUIRES(Locks::thread_suspend_count_lock_);
@@ -1236,6 +1217,14 @@
bool for_debugger)
REQUIRES(Locks::thread_suspend_count_lock_);
+ void RunCheckpointFunction();
+
+ bool PassActiveSuspendBarriers(Thread* self)
+ REQUIRES(!Locks::thread_suspend_count_lock_);
+
+ // Install the protected region for implicit stack checks.
+ void InstallImplicitProtection();
+
// 32 bits of atomically changed state and flags. Keeping as 32 bits allows and atomic CAS to
// change from being Suspended to Runnable without a suspend request occurring.
union PACKED(4) StateAndFlags {
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 9f8d981..c5da5d2 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -167,29 +167,29 @@
array_log.LogValue(index, value);
}
-void Transaction::RecordResolveString(mirror::DexCache* dex_cache, uint32_t string_idx) {
+void Transaction::RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx) {
DCHECK(dex_cache != nullptr);
DCHECK_LT(string_idx, dex_cache->GetDexFile()->NumStringIds());
MutexLock mu(Thread::Current(), log_lock_);
resolve_string_logs_.push_back(ResolveStringLog(dex_cache, string_idx));
}
-void Transaction::RecordStrongStringInsertion(mirror::String* s) {
+void Transaction::RecordStrongStringInsertion(ObjPtr<mirror::String> s) {
InternStringLog log(s, InternStringLog::kStrongString, InternStringLog::kInsert);
LogInternedString(log);
}
-void Transaction::RecordWeakStringInsertion(mirror::String* s) {
+void Transaction::RecordWeakStringInsertion(ObjPtr<mirror::String> s) {
InternStringLog log(s, InternStringLog::kWeakString, InternStringLog::kInsert);
LogInternedString(log);
}
-void Transaction::RecordStrongStringRemoval(mirror::String* s) {
+void Transaction::RecordStrongStringRemoval(ObjPtr<mirror::String> s) {
InternStringLog log(s, InternStringLog::kStrongString, InternStringLog::kRemove);
LogInternedString(log);
}
-void Transaction::RecordWeakStringRemoval(mirror::String* s) {
+void Transaction::RecordWeakStringRemoval(ObjPtr<mirror::String> s) {
InternStringLog log(s, InternStringLog::kWeakString, InternStringLog::kRemove);
LogInternedString(log);
}
@@ -470,10 +470,10 @@
case InternStringLog::kInsert: {
switch (string_kind_) {
case InternStringLog::kStrongString:
- intern_table->RemoveStrongFromTransaction(str_);
+ intern_table->RemoveStrongFromTransaction(str_.Read());
break;
case InternStringLog::kWeakString:
- intern_table->RemoveWeakFromTransaction(str_);
+ intern_table->RemoveWeakFromTransaction(str_.Read());
break;
default:
LOG(FATAL) << "Unknown interned string kind";
@@ -484,10 +484,10 @@
case InternStringLog::kRemove: {
switch (string_kind_) {
case InternStringLog::kStrongString:
- intern_table->InsertStrongFromTransaction(str_);
+ intern_table->InsertStrongFromTransaction(str_.Read());
break;
case InternStringLog::kWeakString:
- intern_table->InsertWeakFromTransaction(str_);
+ intern_table->InsertWeakFromTransaction(str_.Read());
break;
default:
LOG(FATAL) << "Unknown interned string kind";
@@ -502,14 +502,15 @@
}
void Transaction::InternStringLog::VisitRoots(RootVisitor* visitor) {
- visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&str_), RootInfo(kRootInternedString));
+ str_.VisitRoot(visitor, RootInfo(kRootInternedString));
}
void Transaction::ResolveStringLog::Undo() {
dex_cache_.Read()->ClearString(string_idx_);
}
-Transaction::ResolveStringLog::ResolveStringLog(mirror::DexCache* dex_cache, uint32_t string_idx)
+Transaction::ResolveStringLog::ResolveStringLog(ObjPtr<mirror::DexCache> dex_cache,
+ uint32_t string_idx)
: dex_cache_(dex_cache),
string_idx_(string_idx) {
DCHECK(dex_cache != nullptr);
@@ -520,6 +521,15 @@
dex_cache_.VisitRoot(visitor, RootInfo(kRootVMInternal));
}
+Transaction::InternStringLog::InternStringLog(ObjPtr<mirror::String> s,
+ StringKind kind,
+ StringOp op)
+ : str_(s),
+ string_kind_(kind),
+ string_op_(op) {
+ DCHECK(s != nullptr);
+}
+
void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
auto it = array_values_.find(index);
if (it == array_values_.end()) {
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 584dfb8..2ec2f50 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -83,21 +83,21 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Record intern string table changes.
- void RecordStrongStringInsertion(mirror::String* s)
+ void RecordStrongStringInsertion(ObjPtr<mirror::String> s)
REQUIRES(Locks::intern_table_lock_)
REQUIRES(!log_lock_);
- void RecordWeakStringInsertion(mirror::String* s)
+ void RecordWeakStringInsertion(ObjPtr<mirror::String> s)
REQUIRES(Locks::intern_table_lock_)
REQUIRES(!log_lock_);
- void RecordStrongStringRemoval(mirror::String* s)
+ void RecordStrongStringRemoval(ObjPtr<mirror::String> s)
REQUIRES(Locks::intern_table_lock_)
REQUIRES(!log_lock_);
- void RecordWeakStringRemoval(mirror::String* s)
+ void RecordWeakStringRemoval(ObjPtr<mirror::String> s)
REQUIRES(Locks::intern_table_lock_)
REQUIRES(!log_lock_);
// Record resolve string.
- void RecordResolveString(mirror::DexCache* dex_cache, uint32_t string_idx)
+ void RecordResolveString(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!log_lock_);
@@ -182,10 +182,7 @@
kInsert,
kRemove
};
- InternStringLog(mirror::String* s, StringKind kind, StringOp op)
- : str_(s), string_kind_(kind), string_op_(op) {
- DCHECK(s != nullptr);
- }
+ InternStringLog(ObjPtr<mirror::String> s, StringKind kind, StringOp op);
void Undo(InternTable* intern_table)
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -193,14 +190,14 @@
void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
private:
- mirror::String* str_;
+ GcRoot<mirror::String> str_;
const StringKind string_kind_;
const StringOp string_op_;
};
class ResolveStringLog : public ValueObject {
public:
- ResolveStringLog(mirror::DexCache* dex_cache, uint32_t string_idx);
+ ResolveStringLog(ObjPtr<mirror::DexCache> dex_cache, uint32_t string_idx);
void Undo() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/utils.h b/runtime/utils.h
index 94738d2..21df839 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -30,10 +30,8 @@
#include "arch/instruction_set.h"
#include "base/casts.h"
#include "base/logging.h"
-#include "base/mutex.h"
#include "base/stringpiece.h"
#include "globals.h"
-#include "obj_ptr.h"
#include "primitive.h"
namespace art {
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 28f9bb3..edd6ffe 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <string>
+#include "base/array_ref.h"
#include "base/macros.h"
#include "mem_map.h"
#include "os.h"
@@ -44,8 +45,11 @@
public:
Header(uint32_t dex_size, uint32_t verifier_deps_size, uint32_t quickening_info_size);
+ const char* GetMagic() const { return reinterpret_cast<const char*>(magic_); }
+ const char* GetVersion() const { return reinterpret_cast<const char*>(version_); }
bool IsMagicValid() const;
bool IsVersionValid() const;
+ bool IsValid() const { return IsMagicValid() && IsVersionValid(); }
uint32_t GetDexSize() const { return dex_size_; }
uint32_t GetVerifierDepsSize() const { return verifier_deps_size_; }
@@ -71,6 +75,15 @@
const uint8_t* End() const { return mmap_->End(); }
size_t Size() const { return mmap_->Size(); }
+ const Header& GetHeader() const {
+ return *reinterpret_cast<const Header*>(Begin());
+ }
+
+ ArrayRef<const uint8_t> GetVerifierDepsData() const {
+ return ArrayRef<const uint8_t>(
+ Begin() + sizeof(Header) + GetHeader().GetDexSize(), GetHeader().GetVerifierDepsSize());
+ }
+
private:
explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index 837ee2d..52be2df 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -23,6 +23,7 @@
#include "common_runtime_test.h"
#include "dex_file.h"
#include "scoped_thread_state_change-inl.h"
+#include "utils.h"
#include "verifier_log_mode.h"
namespace art {
@@ -40,7 +41,14 @@
std::string error_msg;
MethodVerifier::FailureKind failure = MethodVerifier::VerifyClass(
self, klass, nullptr, true, HardFailLogMode::kLogWarning, &error_msg);
- ASSERT_TRUE(failure == MethodVerifier::kNoFailure) << error_msg;
+
+ if (StartsWith(descriptor, "Ljava/lang/invoke")) {
+ ASSERT_TRUE(failure == MethodVerifier::kSoftFailure ||
+ failure == MethodVerifier::kNoFailure) << error_msg;
+
+ } else {
+ ASSERT_TRUE(failure == MethodVerifier::kNoFailure) << error_msg;
+ }
}
void VerifyDexFile(const DexFile& dex)
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 4d1e337..bdf63cb 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -39,6 +39,11 @@
return (it == dex_deps_.end()) ? nullptr : it->second.get();
}
+const VerifierDeps::DexFileDeps* VerifierDeps::GetDexFileDeps(const DexFile& dex_file) const {
+ auto it = dex_deps_.find(&dex_file);
+ return (it == dex_deps_.end()) ? nullptr : it->second.get();
+}
+
template <typename T>
uint16_t VerifierDeps::GetAccessFlags(T* element) {
static_assert(kAccJavaFlagsMask == 0xFFFF, "Unexpected value of a constant");
@@ -95,12 +100,12 @@
return new_id;
}
-std::string VerifierDeps::GetStringFromId(const DexFile& dex_file, uint32_t string_id) {
+std::string VerifierDeps::GetStringFromId(const DexFile& dex_file, uint32_t string_id) const {
uint32_t num_ids_in_dex = dex_file.NumStringIds();
if (string_id < num_ids_in_dex) {
return std::string(dex_file.StringDataByIdx(string_id));
} else {
- DexFileDeps* deps = GetDexFileDeps(dex_file);
+ const DexFileDeps* deps = GetDexFileDeps(dex_file);
DCHECK(deps != nullptr);
string_id -= num_ids_in_dex;
CHECK_LT(string_id, deps->strings_.size());
@@ -108,7 +113,7 @@
}
}
-bool VerifierDeps::IsInClassPath(ObjPtr<mirror::Class> klass) {
+bool VerifierDeps::IsInClassPath(ObjPtr<mirror::Class> klass) const {
DCHECK(klass != nullptr);
ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache();
@@ -431,35 +436,45 @@
}
}
-void VerifierDeps::Encode(std::vector<uint8_t>* buffer) const {
+void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files,
+ std::vector<uint8_t>* buffer) const {
MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
- for (auto& entry : dex_deps_) {
- EncodeStringVector(buffer, entry.second->strings_);
- EncodeSet(buffer, entry.second->assignable_types_);
- EncodeSet(buffer, entry.second->unassignable_types_);
- EncodeSet(buffer, entry.second->classes_);
- EncodeSet(buffer, entry.second->fields_);
- EncodeSet(buffer, entry.second->direct_methods_);
- EncodeSet(buffer, entry.second->virtual_methods_);
- EncodeSet(buffer, entry.second->interface_methods_);
- EncodeUint16Vector(buffer, entry.second->unverified_classes_);
+ for (const DexFile* dex_file : dex_files) {
+ const DexFileDeps& deps = *GetDexFileDeps(*dex_file);
+ EncodeStringVector(buffer, deps.strings_);
+ EncodeSet(buffer, deps.assignable_types_);
+ EncodeSet(buffer, deps.unassignable_types_);
+ EncodeSet(buffer, deps.classes_);
+ EncodeSet(buffer, deps.fields_);
+ EncodeSet(buffer, deps.direct_methods_);
+ EncodeSet(buffer, deps.virtual_methods_);
+ EncodeSet(buffer, deps.interface_methods_);
+ EncodeUint16Vector(buffer, deps.unverified_classes_);
}
}
-VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<uint8_t> data)
+VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files,
+ ArrayRef<const uint8_t> data)
: VerifierDeps(dex_files) {
+ if (data.empty()) {
+ // Return eagerly, as the first thing we expect from VerifierDeps data is
+ // the number of created strings, even if there is no dependency.
+ // Currently, only the boot image does not have any VerifierDeps data.
+ return;
+ }
const uint8_t* data_start = data.data();
const uint8_t* data_end = data_start + data.size();
- for (auto& entry : dex_deps_) {
- DecodeStringVector(&data_start, data_end, &entry.second->strings_);
- DecodeSet(&data_start, data_end, &entry.second->assignable_types_);
- DecodeSet(&data_start, data_end, &entry.second->unassignable_types_);
- DecodeSet(&data_start, data_end, &entry.second->classes_);
- DecodeSet(&data_start, data_end, &entry.second->fields_);
- DecodeSet(&data_start, data_end, &entry.second->direct_methods_);
- DecodeSet(&data_start, data_end, &entry.second->virtual_methods_);
- DecodeSet(&data_start, data_end, &entry.second->interface_methods_);
- DecodeUint16Vector(&data_start, data_end, &entry.second->unverified_classes_);
+ for (const DexFile* dex_file : dex_files) {
+ DexFileDeps* deps = GetDexFileDeps(*dex_file);
+ DecodeStringVector(&data_start, data_end, &deps->strings_);
+ DecodeSet(&data_start, data_end, &deps->assignable_types_);
+ DecodeSet(&data_start, data_end, &deps->unassignable_types_);
+ DecodeSet(&data_start, data_end, &deps->classes_);
+ DecodeSet(&data_start, data_end, &deps->fields_);
+ DecodeSet(&data_start, data_end, &deps->direct_methods_);
+ DecodeSet(&data_start, data_end, &deps->virtual_methods_);
+ DecodeSet(&data_start, data_end, &deps->interface_methods_);
+ DecodeUint16Vector(&data_start, data_end, &deps->unverified_classes_);
}
CHECK_LE(data_start, data_end);
}
@@ -504,5 +519,354 @@
(unverified_classes_ == rhs.unverified_classes_);
}
+void VerifierDeps::Dump(VariableIndentationOutputStream* vios) const {
+ for (const auto& dep : dex_deps_) {
+ const DexFile& dex_file = *dep.first;
+ vios->Stream()
+ << "Dependencies of "
+ << dex_file.GetLocation()
+ << ":\n";
+
+ ScopedIndentation indent(vios);
+
+ for (const std::string& str : dep.second->strings_) {
+ vios->Stream() << "Extra string: " << str << "\n";
+ }
+
+ for (const TypeAssignability& entry : dep.second->assignable_types_) {
+ vios->Stream()
+ << GetStringFromId(dex_file, entry.GetSource())
+ << " must be assignable to "
+ << GetStringFromId(dex_file, entry.GetDestination())
+ << "\n";
+ }
+
+ for (const TypeAssignability& entry : dep.second->unassignable_types_) {
+ vios->Stream()
+ << GetStringFromId(dex_file, entry.GetSource())
+ << " must not be assignable to "
+ << GetStringFromId(dex_file, entry.GetDestination())
+ << "\n";
+ }
+
+ for (const ClassResolution& entry : dep.second->classes_) {
+ vios->Stream()
+ << dex_file.StringByTypeIdx(entry.GetDexTypeIndex())
+ << (entry.IsResolved() ? " must be resolved " : "must not be resolved ")
+ << " with access flags " << std::hex << entry.GetAccessFlags() << std::dec
+ << "\n";
+ }
+
+ for (const FieldResolution& entry : dep.second->fields_) {
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(entry.GetDexFieldIndex());
+ vios->Stream()
+ << dex_file.GetFieldDeclaringClassDescriptor(field_id) << "->"
+ << dex_file.GetFieldName(field_id) << ":"
+ << dex_file.GetFieldTypeDescriptor(field_id)
+ << " is expected to be ";
+ if (!entry.IsResolved()) {
+ vios->Stream() << "unresolved\n";
+ } else {
+ vios->Stream()
+ << "in class "
+ << GetStringFromId(dex_file, entry.GetDeclaringClassIndex())
+ << ", and have the access flags " << std::hex << entry.GetAccessFlags() << std::dec
+ << "\n";
+ }
+ }
+
+ for (const auto& entry :
+ { std::make_pair(kDirectMethodResolution, dep.second->direct_methods_),
+ std::make_pair(kVirtualMethodResolution, dep.second->virtual_methods_),
+ std::make_pair(kInterfaceMethodResolution, dep.second->interface_methods_) }) {
+ for (const MethodResolution& method : entry.second) {
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(method.GetDexMethodIndex());
+ vios->Stream()
+ << dex_file.GetMethodDeclaringClassDescriptor(method_id) << "->"
+ << dex_file.GetMethodName(method_id)
+ << dex_file.GetMethodSignature(method_id).ToString()
+ << " is expected to be ";
+ if (!method.IsResolved()) {
+ vios->Stream() << "unresolved\n";
+ } else {
+ vios->Stream()
+ << "in class "
+ << GetStringFromId(dex_file, method.GetDeclaringClassIndex())
+ << ", have the access flags " << std::hex << method.GetAccessFlags() << std::dec
+ << ", and be of kind " << entry.first
+ << "\n";
+ }
+ }
+ }
+
+ for (uint16_t type_index : dep.second->unverified_classes_) {
+ vios->Stream()
+ << dex_file.StringByTypeIdx(type_index)
+ << " is expected to be verified at runtime\n";
+ }
+ }
+}
+
+bool VerifierDeps::Verify(Handle<mirror::ClassLoader> class_loader, Thread* self) const {
+ for (const auto& entry : dex_deps_) {
+ if (!VerifyDexFile(class_loader, *entry.first, *entry.second, self)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// TODO: share that helper with other parts of the compiler that have
+// the same lookup pattern.
+static mirror::Class* FindClassAndClearException(ClassLinker* class_linker,
+ Thread* self,
+ const char* name,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* result = class_linker->FindClass(self, name, class_loader);
+ if (result == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+ return result;
+}
+
+bool VerifierDeps::VerifyAssignability(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<TypeAssignability>& assignables,
+ bool expected_assignability,
+ Thread* self) const {
+ StackHandleScope<2> hs(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ MutableHandle<mirror::Class> source(hs.NewHandle<mirror::Class>(nullptr));
+ MutableHandle<mirror::Class> destination(hs.NewHandle<mirror::Class>(nullptr));
+
+ for (const auto& entry : assignables) {
+ const std::string& destination_desc = GetStringFromId(dex_file, entry.GetDestination());
+ destination.Assign(
+ FindClassAndClearException(class_linker, self, destination_desc.c_str(), class_loader));
+ const std::string& source_desc = GetStringFromId(dex_file, entry.GetSource());
+ source.Assign(
+ FindClassAndClearException(class_linker, self, source_desc.c_str(), class_loader));
+
+ if (destination.Get() == nullptr) {
+ LOG(INFO) << "VerifiersDeps: Could not resolve class " << destination_desc;
+ return false;
+ }
+
+ if (source.Get() == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << source_desc;
+ return false;
+ }
+
+ DCHECK(destination->IsResolved() && source->IsResolved());
+ if (destination->IsAssignableFrom(source.Get()) != expected_assignability) {
+ LOG(INFO) << "VerifierDeps: Class "
+ << destination_desc
+ << (expected_assignability ? " not " : " ")
+ << "assignable from "
+ << source_desc;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VerifierDeps::VerifyClasses(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<ClassResolution>& classes,
+ Thread* self) const {
+ StackHandleScope<1> hs(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
+ for (const auto& entry : classes) {
+ const char* descriptor = dex_file.StringByTypeIdx(entry.GetDexTypeIndex());
+ cls.Assign(FindClassAndClearException(class_linker, self, descriptor, class_loader));
+
+ if (entry.IsResolved()) {
+ if (cls.Get() == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
+ return false;
+ } else if (entry.GetAccessFlags() != GetAccessFlags(cls.Get())) {
+ LOG(INFO) << "VerifierDeps: Unexpected access flags on class "
+ << descriptor
+ << std::hex
+ << " (expected="
+ << entry.GetAccessFlags()
+ << ", actual="
+ << GetAccessFlags(cls.Get()) << ")"
+ << std::dec;
+ return false;
+ }
+ } else if (cls.Get() != nullptr) {
+ LOG(INFO) << "VerifierDeps: Unexpected successful resolution of class " << descriptor;
+ return false;
+ }
+ }
+ return true;
+}
+
+static std::string GetFieldDescription(const DexFile& dex_file, uint32_t index) {
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(index);
+ return std::string(dex_file.GetFieldDeclaringClassDescriptor(field_id))
+ + "->"
+ + dex_file.GetFieldName(field_id)
+ + ":"
+ + dex_file.GetFieldTypeDescriptor(field_id);
+}
+
+bool VerifierDeps::VerifyFields(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<FieldResolution>& fields,
+ Thread* self) const {
+ // Check recorded fields are resolved the same way, have the same recorded class,
+ // and have the same recorded flags.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::DexCache> dex_cache(
+ hs.NewHandle(class_linker->FindDexCache(self, dex_file, /* allow_failure */ false)));
+ for (const auto& entry : fields) {
+ ArtField* field = class_linker->ResolveFieldJLS(
+ dex_file, entry.GetDexFieldIndex(), dex_cache, class_loader);
+
+ if (field == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+
+ if (entry.IsResolved()) {
+ std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex());
+ std::string temp;
+ if (field == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve field "
+ << GetFieldDescription(dex_file, entry.GetDexFieldIndex());
+ return false;
+ } else if (expected_decl_klass != field->GetDeclaringClass()->GetDescriptor(&temp)) {
+ LOG(INFO) << "VerifierDeps: Unexpected declaring class for field resolution "
+ << GetFieldDescription(dex_file, entry.GetDexFieldIndex())
+ << " (expected=" << expected_decl_klass
+ << ", actual=" << field->GetDeclaringClass()->GetDescriptor(&temp) << ")";
+ return false;
+ } else if (entry.GetAccessFlags() != GetAccessFlags(field)) {
+ LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved field "
+ << GetFieldDescription(dex_file, entry.GetDexFieldIndex())
+ << std::hex << " (expected=" << entry.GetAccessFlags()
+ << ", actual=" << GetAccessFlags(field) << ")" << std::dec;
+ return false;
+ }
+ } else if (field != nullptr) {
+ LOG(INFO) << "VerifierDeps: Unexpected successful resolution of field "
+ << GetFieldDescription(dex_file, entry.GetDexFieldIndex());
+ return false;
+ }
+ }
+ return true;
+}
+
+static std::string GetMethodDescription(const DexFile& dex_file, uint32_t index) {
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(index);
+ return std::string(dex_file.GetMethodDeclaringClassDescriptor(method_id))
+ + "->"
+ + dex_file.GetMethodName(method_id)
+ + dex_file.GetMethodSignature(method_id).ToString();
+}
+
+bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<MethodResolution>& methods,
+ MethodResolutionKind kind,
+ Thread* self) const {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ PointerSize pointer_size = class_linker->GetImagePointerSize();
+
+ for (const auto& entry : methods) {
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(entry.GetDexMethodIndex());
+
+ const char* name = dex_file.GetMethodName(method_id);
+ const Signature signature = dex_file.GetMethodSignature(method_id);
+ const char* descriptor = dex_file.GetMethodDeclaringClassDescriptor(method_id);
+
+ mirror::Class* cls = FindClassAndClearException(class_linker, self, descriptor, class_loader);
+ if (cls == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
+ return false;
+ }
+ DCHECK(cls->IsResolved());
+ ArtMethod* method = nullptr;
+ if (kind == kDirectMethodResolution) {
+ method = cls->FindDirectMethod(name, signature, pointer_size);
+ } else if (kind == kVirtualMethodResolution) {
+ method = cls->FindVirtualMethod(name, signature, pointer_size);
+ } else {
+ DCHECK_EQ(kind, kInterfaceMethodResolution);
+ method = cls->FindInterfaceMethod(name, signature, pointer_size);
+ }
+
+ if (entry.IsResolved()) {
+ std::string temp;
+ std::string expected_decl_klass = GetStringFromId(dex_file, entry.GetDeclaringClassIndex());
+ if (method == nullptr) {
+ LOG(INFO) << "VerifierDeps: Could not resolve "
+ << kind
+ << " method "
+ << GetMethodDescription(dex_file, entry.GetDexMethodIndex());
+ return false;
+ } else if (expected_decl_klass != method->GetDeclaringClass()->GetDescriptor(&temp)) {
+ LOG(INFO) << "VerifierDeps: Unexpected declaring class for "
+ << kind
+ << " method resolution "
+ << GetMethodDescription(dex_file, entry.GetDexMethodIndex())
+ << " (expected="
+ << expected_decl_klass
+ << ", actual="
+ << method->GetDeclaringClass()->GetDescriptor(&temp)
+ << ")";
+ return false;
+ } else if (entry.GetAccessFlags() != GetAccessFlags(method)) {
+ LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved "
+ << kind
+ << " method resolution "
+ << GetMethodDescription(dex_file, entry.GetDexMethodIndex())
+ << std::hex
+ << " (expected="
+ << entry.GetAccessFlags()
+ << ", actual="
+ << GetAccessFlags(method) << ")"
+ << std::dec;
+ return false;
+ }
+ } else if (method != nullptr) {
+ LOG(INFO) << "VerifierDeps: Unexpected successful resolution of "
+ << kind
+ << " method "
+ << GetMethodDescription(dex_file, entry.GetDexMethodIndex());
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VerifierDeps::VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const DexFileDeps& deps,
+ Thread* self) const {
+ bool result = VerifyAssignability(
+ class_loader, dex_file, deps.assignable_types_, /* expected_assignability */ true, self);
+ result = result && VerifyAssignability(
+ class_loader, dex_file, deps.unassignable_types_, /* expected_assignability */ false, self);
+
+ result = result && VerifyClasses(class_loader, dex_file, deps.classes_, self);
+ result = result && VerifyFields(class_loader, dex_file, deps.fields_, self);
+
+ result = result && VerifyMethods(
+ class_loader, dex_file, deps.direct_methods_, kDirectMethodResolution, self);
+ result = result && VerifyMethods(
+ class_loader, dex_file, deps.virtual_methods_, kVirtualMethodResolution, self);
+ result = result && VerifyMethods(
+ class_loader, dex_file, deps.interface_methods_, kInterfaceMethodResolution, self);
+
+ return result;
+}
+
} // namespace verifier
} // namespace art
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 9d2622d..eea0299 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -25,6 +25,7 @@
#include "art_method.h"
#include "base/array_ref.h"
#include "base/mutex.h"
+#include "indenter.h"
#include "method_resolution_kind.h"
#include "method_verifier.h" // For MethodVerifier::FailureKind.
#include "obj_ptr.h"
@@ -50,6 +51,10 @@
explicit VerifierDeps(const std::vector<const DexFile*>& dex_files)
REQUIRES(!Locks::verifier_deps_lock_);
+ VerifierDeps(const std::vector<const DexFile*>& dex_files,
+ ArrayRef<const uint8_t> data)
+ REQUIRES(!Locks::verifier_deps_lock_);
+
// Record the verification status of the class at `type_idx`.
static void MaybeRecordVerificationStatus(const DexFile& dex_file,
uint16_t type_idx,
@@ -94,16 +99,24 @@
REQUIRES(!Locks::verifier_deps_lock_);
// Serialize the recorded dependencies and store the data into `buffer`.
- void Encode(std::vector<uint8_t>* buffer) const
+ // `dex_files` provides the order of the dex files in which the dependencies
+ // should be emitted.
+ void Encode(const std::vector<const DexFile*>& dex_files, std::vector<uint8_t>* buffer) const
REQUIRES(!Locks::verifier_deps_lock_);
+ // NO_THREAD_SAFETY_ANALYSIS as Dump iterates over dex_deps_, which is guarded by
+ // verifier_deps_lock_, but we expect Dump to be called once the deps collection is done.
+ void Dump(VariableIndentationOutputStream* vios) const
+ NO_THREAD_SAFETY_ANALYSIS;
+
+ // Verify the encoded dependencies of this `VerifierDeps`.
+ // NO_THREAD_SAFETY_ANALYSIS, as this must be called on a read-only `VerifierDeps`.
+ bool Verify(Handle<mirror::ClassLoader> class_loader, Thread* self) const
+ NO_THREAD_SAFETY_ANALYSIS;
+
private:
static constexpr uint16_t kUnresolvedMarker = static_cast<uint16_t>(-1);
- // Only used in tests to reconstruct the data structure from serialized data.
- VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<uint8_t> data)
- REQUIRES(!Locks::verifier_deps_lock_);
-
using ClassResolutionBase = std::tuple<uint32_t, uint16_t>;
struct ClassResolution : public ClassResolutionBase {
ClassResolution() = default;
@@ -185,9 +198,12 @@
DexFileDeps* GetDexFileDeps(const DexFile& dex_file)
NO_THREAD_SAFETY_ANALYSIS;
+ const DexFileDeps* GetDexFileDeps(const DexFile& dex_file) const
+ NO_THREAD_SAFETY_ANALYSIS;
+
// Returns true if `klass` is null or not defined in any of dex files which
// were reported as being compiled.
- bool IsInClassPath(ObjPtr<mirror::Class> klass)
+ bool IsInClassPath(ObjPtr<mirror::Class> klass) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns the index of `str`. If it is defined in `dex_file_`, this is the dex
@@ -198,13 +214,13 @@
REQUIRES(Locks::verifier_deps_lock_);
// Returns the string represented by `id`.
- std::string GetStringFromId(const DexFile& dex_file, uint32_t string_id)
+ std::string GetStringFromId(const DexFile& dex_file, uint32_t string_id) const
REQUIRES(Locks::verifier_deps_lock_);
// Returns the bytecode access flags of `element` (bottom 16 bits), or
// `kUnresolvedMarker` if `element` is null.
template <typename T>
- uint16_t GetAccessFlags(T* element)
+ static uint16_t GetAccessFlags(T* element)
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns a string ID of the descriptor of the declaring class of `element`,
@@ -244,6 +260,54 @@
bool Equals(const VerifierDeps& rhs) const
REQUIRES(!Locks::verifier_deps_lock_);
+ // Verify `dex_file` according to the `deps`, that is going over each
+ // `DexFileDeps` field, and checking that the recorded information still
+ // holds.
+ bool VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const DexFileDeps& deps,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
+ bool VerifyAssignability(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<TypeAssignability>& assignables,
+ bool expected_assignability,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
+ // Verify that the set of resolved classes at the point of creation
+ // of this `VerifierDeps` is still the same.
+ bool VerifyClasses(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<ClassResolution>& classes,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
+ // Verify that the set of resolved fields at the point of creation
+ // of this `VerifierDeps` is still the same, and each field resolves to the
+ // same field holder and access flags.
+ bool VerifyFields(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<FieldResolution>& classes,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
+ // Verify that the set of resolved methods at the point of creation
+ // of this `VerifierDeps` is still the same, and each method resolves to the
+ // same method holder, access flags, and invocation kind.
+ bool VerifyMethods(Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const std::set<MethodResolution>& methods,
+ MethodResolutionKind kind,
+ Thread* self) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::verifier_deps_lock_);
+
// Map from DexFiles into dependencies collected from verification of their methods.
std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_
GUARDED_BY(Locks::verifier_deps_lock_);
@@ -251,6 +315,8 @@
friend class VerifierDepsTest;
ART_FRIEND_TEST(VerifierDepsTest, StringToId);
ART_FRIEND_TEST(VerifierDepsTest, EncodeDecode);
+ ART_FRIEND_TEST(VerifierDepsTest, EncodeDecodeMulti);
+ ART_FRIEND_TEST(VerifierDepsTest, VerifyDeps);
};
} // namespace verifier
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 153c7ef..0f4ae52 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -34,6 +34,7 @@
jclass WellKnownClasses::com_android_dex_Dex;
jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative;
jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
+jclass WellKnownClasses::dalvik_system_BaseDexClassLoader;
jclass WellKnownClasses::dalvik_system_DexFile;
jclass WellKnownClasses::dalvik_system_DexPathList;
jclass WellKnownClasses::dalvik_system_DexPathList__Element;
@@ -108,7 +109,7 @@
jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
jfieldID WellKnownClasses::dalvik_system_DexFile_fileName;
-jfieldID WellKnownClasses::dalvik_system_PathClassLoader_pathList;
+jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
jfieldID WellKnownClasses::java_lang_Thread_daemon;
@@ -264,6 +265,7 @@
dalvik_annotation_optimization_CriticalNative =
CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
+ dalvik_system_BaseDexClassLoader = CacheClass(env, "dalvik/system/BaseDexClassLoader");
dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
dalvik_system_DexPathList__Element = CacheClass(env, "dalvik/system/DexPathList$Element");
@@ -332,9 +334,9 @@
org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V");
org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
+ dalvik_system_BaseDexClassLoader_pathList = CacheField(env, dalvik_system_BaseDexClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "Ljava/lang/Object;");
dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;");
- dalvik_system_PathClassLoader_pathList = CacheField(env, dalvik_system_PathClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;");
java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 2fb5bb4..d07977a 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -47,6 +47,7 @@
static jclass com_android_dex_Dex;
static jclass dalvik_annotation_optimization_CriticalNative;
static jclass dalvik_annotation_optimization_FastNative;
+ static jclass dalvik_system_BaseDexClassLoader;
static jclass dalvik_system_DexFile;
static jclass dalvik_system_DexPathList;
static jclass dalvik_system_DexPathList__Element;
@@ -119,11 +120,11 @@
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
+ static jfieldID dalvik_system_BaseDexClassLoader_pathList;
static jfieldID dalvik_system_DexFile_cookie;
static jfieldID dalvik_system_DexFile_fileName;
static jfieldID dalvik_system_DexPathList_dexElements;
static jfieldID dalvik_system_DexPathList__Element_dexFile;
- static jfieldID dalvik_system_PathClassLoader_pathList;
static jfieldID java_lang_reflect_Executable_artMethod;
static jfieldID java_lang_reflect_Proxy_h;
static jfieldID java_lang_Thread_daemon;
diff --git a/test/903-hello-tagging/expected.txt b/test/903-hello-tagging/expected.txt
index e69de29..872b79b 100644
--- a/test/903-hello-tagging/expected.txt
+++ b/test/903-hello-tagging/expected.txt
@@ -0,0 +1,10 @@
+18
+<nothing>
+18
+[<1;1>, <11;1>, <2;2>, <12;2>, <3;3>, <13;3>, <4;4>, <14;4>, <5;5>, <15;5>, <6;6>, <16;6>, <7;7>, <17;7>, <8;8>, <18;8>, <9;9>, <19;9>]
+4
+[<2;2>, <12;2>, <5;5>, <15;5>]
+18
+[<null;1>, <null;1>, <null;2>, <null;2>, <null;3>, <null;3>, <null;4>, <null;4>, <null;5>, <null;5>, <null;6>, <null;6>, <null;7>, <null;7>, <null;8>, <null;8>, <null;9>, <null;9>]
+18
+[<1;0>, <2;0>, <3;0>, <4;0>, <5;0>, <6;0>, <7;0>, <8;0>, <9;0>, <11;0>, <12;0>, <13;0>, <14;0>, <15;0>, <16;0>, <17;0>, <18;0>, <19;0>]
diff --git a/test/903-hello-tagging/src/Main.java b/test/903-hello-tagging/src/Main.java
index 2856a39..a8aedb4 100644
--- a/test/903-hello-tagging/src/Main.java
+++ b/test/903-hello-tagging/src/Main.java
@@ -15,11 +15,14 @@
*/
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
public class Main {
public static void main(String[] args) {
System.loadLibrary(args[1]);
doTest();
+ testGetTaggedObjects();
}
public static void doTest() {
@@ -68,6 +71,100 @@
}
}
+ private static void testGetTaggedObjects() {
+ // Use an array list to ensure that the objects stay live for a bit. Also gives us a source
+ // to compare to. We use index % 10 as the tag.
+ ArrayList<Object> l = new ArrayList<>();
+
+ for (int i = 0; i < 20; i++) {
+ Integer o = new Integer(i);
+ l.add(o);
+ if (i % 10 != 0) {
+ setTag(o, i % 10);
+ }
+ }
+
+ testGetTaggedObjectsRun(l, null, false, false);
+ testGetTaggedObjectsRun(l, null, true, true);
+ testGetTaggedObjectsRun(l, new long[] { 2, 5 }, true, true);
+ testGetTaggedObjectsRun(l, null, false, true);
+ testGetTaggedObjectsRun(l, null, true, false);
+ }
+
+ private static void testGetTaggedObjectsRun(ArrayList<Object> l, long[] searchTags,
+ boolean returnObjects, boolean returnTags) {
+ Object[] result = getTaggedObjects(searchTags, returnObjects, returnTags);
+
+ Object[] objects = (Object[])result[0];
+ long[] tags = (long[])result[1];
+ int count = (int)result[2];
+
+ System.out.println(count);
+ printArraysSorted(objects, tags);
+ }
+
+ private static void printArraysSorted(Object[] objects, long[] tags) {
+ if (objects == null && tags == null) {
+ System.out.println("<nothing>");
+ return;
+ }
+
+ int l1 = objects == null ? 0 : objects.length;
+ int l2 = tags == null ? 0 : tags.length;
+ int l = Math.max(l1, l2);
+ Pair[] tmp = new Pair[l];
+ for (int i = 0; i < l; i++) {
+ tmp[i] = new Pair(objects == null ? null : objects[i], tags == null ? 0 : tags[i]);
+ }
+
+ Arrays.sort(tmp);
+
+ System.out.println(Arrays.toString(tmp));
+ }
+
+ private static class Pair implements Comparable<Pair> {
+ Object obj;
+ long tag;
+ public Pair(Object o, long t) {
+ obj = o;
+ tag = t;
+ }
+
+ public int compareTo(Pair p) {
+ if (tag != p.tag) {
+ return Long.compare(tag, p.tag);
+ }
+
+ if ((obj instanceof Comparable) && (p.obj instanceof Comparable)) {
+ // It's not really correct, but w/e, best effort.
+ int result = ((Comparable<Object>)obj).compareTo(p.obj);
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ if (obj != null && p.obj != null) {
+ return obj.hashCode() - p.obj.hashCode();
+ }
+
+ if (obj != null) {
+ return 1;
+ }
+
+ if (p.obj != null) {
+ return -1;
+ }
+
+ return hashCode() - p.hashCode();
+ }
+
+ public String toString() {
+ return "<" + obj + ";" + tag + ">";
+ }
+ }
+
private static native void setTag(Object o, long tag);
private static native long getTag(Object o);
+ private static native Object[] getTaggedObjects(long[] searchTags, boolean returnObjects,
+ boolean returnTags);
}
diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc
index 7d692fb..bed4e5d 100644
--- a/test/903-hello-tagging/tagging.cc
+++ b/test/903-hello-tagging/tagging.cc
@@ -21,9 +21,12 @@
#include <stdio.h>
#include <vector>
+#include "jni.h"
+#include "ScopedLocalRef.h"
+#include "ScopedPrimitiveArray.h"
+
#include "art_method-inl.h"
#include "base/logging.h"
-#include "jni.h"
#include "openjdkjvmti/jvmti.h"
#include "ti-agent/common_load.h"
#include "utils.h"
@@ -56,6 +59,84 @@
return tag;
}
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTaggedObjects(JNIEnv* env,
+ jclass,
+ jlongArray searchTags,
+ jboolean returnObjects,
+ jboolean returnTags) {
+ ScopedLongArrayRO scoped_array(env);
+ if (searchTags != nullptr) {
+ scoped_array.reset(searchTags);
+ }
+ const jlong* tag_ptr = scoped_array.get();
+ if (tag_ptr == nullptr) {
+ // Can never pass null.
+ tag_ptr = reinterpret_cast<const jlong*>(1);
+ }
+
+ jint result_count;
+ jobject* result_object_array;
+ jobject** result_object_array_ptr = returnObjects == JNI_TRUE ? &result_object_array : nullptr;
+ jlong* result_tag_array;
+ jlong** result_tag_array_ptr = returnTags == JNI_TRUE ? &result_tag_array : nullptr;
+
+ jvmtiError ret = jvmti_env->GetObjectsWithTags(scoped_array.size(),
+ tag_ptr,
+ &result_count,
+ result_object_array_ptr,
+ result_tag_array_ptr);
+ if (ret != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(ret, &err);
+ printf("Failure running GetLoadedClasses: %s\n", err);
+ return nullptr;
+ }
+
+ CHECK_GE(result_count, 0);
+
+ ScopedLocalRef<jclass> obj_class(env, env->FindClass("java/lang/Object"));
+ if (obj_class.get() == nullptr) {
+ return nullptr;
+ }
+
+ jobjectArray resultObjectArray = nullptr;
+ if (returnObjects == JNI_TRUE) {
+ resultObjectArray = env->NewObjectArray(result_count, obj_class.get(), nullptr);
+ if (resultObjectArray == nullptr) {
+ return nullptr;
+ }
+ for (jint i = 0; i < result_count; ++i) {
+ env->SetObjectArrayElement(resultObjectArray, i, result_object_array[i]);
+ }
+ }
+
+ jlongArray resultTagArray = nullptr;
+ if (returnTags == JNI_TRUE) {
+ resultTagArray = env->NewLongArray(result_count);
+ env->SetLongArrayRegion(resultTagArray, 0, result_count, result_tag_array);
+ }
+
+ jobject count_integer;
+ {
+ ScopedLocalRef<jclass> integer_class(env, env->FindClass("java/lang/Integer"));
+ jmethodID methodID = env->GetMethodID(integer_class.get(), "<init>", "(I)V");
+ count_integer = env->NewObject(integer_class.get(), methodID, result_count);
+ if (count_integer == nullptr) {
+ return nullptr;
+ }
+ }
+
+ jobjectArray resultArray = env->NewObjectArray(3, obj_class.get(), nullptr);
+ if (resultArray == nullptr) {
+ return nullptr;
+ }
+ env->SetObjectArrayElement(resultArray, 0, resultObjectArray);
+ env->SetObjectArrayElement(resultArray, 1, resultTagArray);
+ env->SetObjectArrayElement(resultArray, 2, count_integer);
+
+ return resultArray;
+}
+
// Don't do anything
jint OnLoad(JavaVM* vm,
char* options ATTRIBUTE_UNUSED,
diff --git a/test/910-methods/build b/test/910-methods/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/910-methods/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-build "$@" --experimental agents
diff --git a/test/910-methods/expected.txt b/test/910-methods/expected.txt
new file mode 100644
index 0000000..9a74799
--- /dev/null
+++ b/test/910-methods/expected.txt
@@ -0,0 +1,15 @@
+[toString, ()Ljava/lang/String;, null]
+class java.lang.Object
+1
+[charAt, (I)C, null]
+class java.lang.String
+257
+[sqrt, (D)D, null]
+class java.lang.Math
+265
+[add, (Ljava/lang/Object;)Z, null]
+interface java.util.List
+1025
+[run, ()V, null]
+class $Proxy0
+17
diff --git a/test/910-methods/info.txt b/test/910-methods/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/910-methods/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc
new file mode 100644
index 0000000..cc6ad67
--- /dev/null
+++ b/test/910-methods/methods.cc
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2013 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 "methods.h"
+
+#include <stdio.h>
+
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test910Methods {
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getMethodName(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+ jmethodID id = env->FromReflectedMethod(method);
+
+ char* name;
+ char* sig;
+ char* gen;
+ jvmtiError result = jvmti_env->GetMethodName(id, &name, &sig, &gen);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetMethodName: %s\n", err);
+ return nullptr;
+ }
+
+ ScopedLocalRef<jclass> obj_class(env, env->FindClass("java/lang/String"));
+ if (obj_class.get() == nullptr) {
+ return nullptr;
+ }
+
+ jobjectArray ret = env->NewObjectArray(3, obj_class.get(), nullptr);
+ if (ret == nullptr) {
+ return ret;
+ }
+
+ ScopedLocalRef<jstring> name_str(env, name == nullptr ? nullptr : env->NewStringUTF(name));
+ ScopedLocalRef<jstring> sig_str(env, sig == nullptr ? nullptr : env->NewStringUTF(sig));
+ ScopedLocalRef<jstring> gen_str(env, gen == nullptr ? nullptr : env->NewStringUTF(gen));
+
+ env->SetObjectArrayElement(ret, 0, name_str.get());
+ env->SetObjectArrayElement(ret, 1, sig_str.get());
+ env->SetObjectArrayElement(ret, 2, gen_str.get());
+
+ // Need to deallocate the strings.
+ if (name != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name));
+ }
+ if (sig != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
+ }
+ if (gen != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
+ }
+
+ return ret;
+}
+
+extern "C" JNIEXPORT jclass JNICALL Java_Main_getMethodDeclaringClass(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+ jmethodID id = env->FromReflectedMethod(method);
+
+ jclass declaring_class;
+ jvmtiError result = jvmti_env->GetMethodDeclaringClass(id, &declaring_class);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetMethodDeclaringClass: %s\n", err);
+ return nullptr;
+ }
+
+ return declaring_class;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getMethodModifiers(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+ jmethodID id = env->FromReflectedMethod(method);
+
+ jint modifiers;
+ jvmtiError result = jvmti_env->GetMethodModifiers(id, &modifiers);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetMethodModifiers: %s\n", err);
+ return 0;
+ }
+
+ return modifiers;
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ return 0;
+}
+
+} // namespace Test910Methods
+} // namespace art
diff --git a/test/910-methods/methods.h b/test/910-methods/methods.h
new file mode 100644
index 0000000..93d1874
--- /dev/null
+++ b/test/910-methods/methods.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_910_METHODS_METHODS_H_
+#define ART_TEST_910_METHODS_METHODS_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test910Methods {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+} // namespace Test910Methods
+} // namespace art
+
+#endif // ART_TEST_910_METHODS_METHODS_H_
diff --git a/test/910-methods/run b/test/910-methods/run
new file mode 100755
index 0000000..4dd2555
--- /dev/null
+++ b/test/910-methods/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+ lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+ arg="jvm"
+else
+ arg="art"
+fi
+
+if [[ "$@" != *"--debuggable"* ]]; then
+ other_args=" -Xcompiler-option --debuggable "
+else
+ other_args=""
+fi
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:${agent}=910-methods,${arg} \
+ --android-runtime-option -Xplugin:${plugin} \
+ ${other_args} \
+ --args ${lib}
diff --git a/test/910-methods/src/Main.java b/test/910-methods/src/Main.java
new file mode 100644
index 0000000..3459134
--- /dev/null
+++ b/test/910-methods/src/Main.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ testMethod("java.lang.Object", "toString");
+ testMethod("java.lang.String", "charAt", int.class);
+ testMethod("java.lang.Math", "sqrt", double.class);
+ testMethod("java.util.List", "add", Object.class);
+
+ testMethod(getProxyClass(), "run");
+ }
+
+ private static Class<?> proxyClass = null;
+
+ private static Class<?> getProxyClass() throws Exception {
+ if (proxyClass != null) {
+ return proxyClass;
+ }
+
+ proxyClass = Proxy.getProxyClass(Main.class.getClassLoader(), new Class[] { Runnable.class });
+ return proxyClass;
+ }
+
+ private static void testMethod(String className, String methodName, Class<?>... types)
+ throws Exception {
+ Class<?> base = Class.forName(className);
+ testMethod(base, methodName, types);
+ }
+
+ private static void testMethod(Class<?> base, String methodName, Class<?>... types)
+ throws Exception {
+ Method m = base.getDeclaredMethod(methodName, types);
+ String[] result = getMethodName(m);
+ System.out.println(Arrays.toString(result));
+
+ Class<?> declClass = getMethodDeclaringClass(m);
+ if (base != declClass) {
+ throw new RuntimeException("Declaring class not equal: " + base + " vs " + declClass);
+ }
+ System.out.println(declClass);
+
+ int modifiers = getMethodModifiers(m);
+ if (modifiers != m.getModifiers()) {
+ throw new RuntimeException("Modifiers not equal: " + m.getModifiers() + " vs " + modifiers);
+ }
+ System.out.println(modifiers);
+ }
+
+ private static native String[] getMethodName(Method m);
+ private static native Class<?> getMethodDeclaringClass(Method m);
+ private static native int getMethodModifiers(Method m);
+}
diff --git a/test/911-get-stack-trace/build b/test/911-get-stack-trace/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/911-get-stack-trace/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-build "$@" --experimental agents
diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
new file mode 100644
index 0000000..20bab78
--- /dev/null
+++ b/test/911-get-stack-trace/expected.txt
@@ -0,0 +1,208 @@
+###################
+### Same thread ###
+###################
+From top
+---------
+ getStackTrace (Ljava/lang/Thread;II)[Ljava/lang/String;
+ print (Ljava/lang/Thread;II)V
+ printOrWait (IILMain$ControlData;)V
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ doTest ()V
+ main ([Ljava/lang/String;)V
+---------
+ print (Ljava/lang/Thread;II)V
+ printOrWait (IILMain$ControlData;)V
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ doTest ()V
+ main ([Ljava/lang/String;)V
+---------
+ getStackTrace (Ljava/lang/Thread;II)[Ljava/lang/String;
+ print (Ljava/lang/Thread;II)V
+ printOrWait (IILMain$ControlData;)V
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+---------
+ printOrWait (IILMain$ControlData;)V
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+From bottom
+---------
+ main ([Ljava/lang/String;)V
+---------
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ doTest ()V
+ main ([Ljava/lang/String;)V
+---------
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+
+################################
+### Other thread (suspended) ###
+################################
+From top
+---------
+ wait ()V
+ printOrWait (IILMain$ControlData;)V
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ run ()V
+---------
+ printOrWait (IILMain$ControlData;)V
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ run ()V
+---------
+ wait ()V
+ printOrWait (IILMain$ControlData;)V
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+---------
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+From bottom
+---------
+ run ()V
+---------
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ run ()V
+---------
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+
+###########################
+### Other thread (live) ###
+###########################
+From top
+---------
+ printOrWait (IILMain$ControlData;)V
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ run ()V
+---------
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ run ()V
+---------
+ printOrWait (IILMain$ControlData;)V
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+---------
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+From bottom
+---------
+ run ()V
+---------
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ run ()V
+---------
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
+ foo (IIILMain$ControlData;)I
+ baz (IIILMain$ControlData;)Ljava/lang/Object;
+ bar (IIILMain$ControlData;)J
diff --git a/test/911-get-stack-trace/info.txt b/test/911-get-stack-trace/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/911-get-stack-trace/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/911-get-stack-trace/run b/test/911-get-stack-trace/run
new file mode 100755
index 0000000..43fc325
--- /dev/null
+++ b/test/911-get-stack-trace/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+ lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+ arg="jvm"
+else
+ arg="art"
+fi
+
+if [[ "$@" != *"--debuggable"* ]]; then
+ other_args=" -Xcompiler-option --debuggable "
+else
+ other_args=""
+fi
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:${agent}=911-get-stack-trace,${arg} \
+ --android-runtime-option -Xplugin:${plugin} \
+ ${other_args} \
+ --args ${lib}
diff --git a/test/911-get-stack-trace/src/Main.java b/test/911-get-stack-trace/src/Main.java
new file mode 100644
index 0000000..df4501d
--- /dev/null
+++ b/test/911-get-stack-trace/src/Main.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ doTestOtherThreadWait();
+ doTestOtherThreadBusyLoop();
+ }
+
+ public static void doTest() throws Exception {
+ System.out.println("###################");
+ System.out.println("### Same thread ###");
+ System.out.println("###################");
+ System.out.println("From top");
+ Recurse.foo(4, 0, 25, null);
+ Recurse.foo(4, 1, 25, null);
+ Recurse.foo(4, 0, 5, null);
+ Recurse.foo(4, 2, 5, null);
+
+ System.out.println("From bottom");
+ Recurse.foo(4, -1, 25, null);
+ Recurse.foo(4, -5, 5, null);
+ Recurse.foo(4, -7, 5, null);
+ }
+
+ public static void doTestOtherThreadWait() throws Exception {
+ System.out.println();
+ System.out.println("################################");
+ System.out.println("### Other thread (suspended) ###");
+ System.out.println("################################");
+ final ControlData data = new ControlData();
+ data.waitFor = new Object();
+ Thread t = new Thread() {
+ public void run() {
+ Recurse.foo(4, 0, 0, data);
+ }
+ };
+ t.start();
+ data.reached.await();
+ Thread.yield();
+ Thread.sleep(500); // A little bit of time...
+
+ System.out.println("From top");
+ print(t, 0, 25);
+ print(t, 1, 25);
+ print(t, 0, 5);
+ print(t, 2, 5);
+
+ System.out.println("From bottom");
+ print(t, -1, 25);
+ print(t, -5, 5);
+ print(t, -7, 5);
+
+ // Let the thread make progress and die.
+ synchronized(data.waitFor) {
+ data.waitFor.notifyAll();
+ }
+ t.join();
+ }
+
+ public static void doTestOtherThreadBusyLoop() throws Exception {
+ System.out.println();
+ System.out.println("###########################");
+ System.out.println("### Other thread (live) ###");
+ System.out.println("###########################");
+ final ControlData data = new ControlData();
+ Thread t = new Thread() {
+ public void run() {
+ Recurse.foo(4, 0, 0, data);
+ }
+ };
+ t.start();
+ data.reached.await();
+ Thread.yield();
+ Thread.sleep(500); // A little bit of time...
+
+ System.out.println("From top");
+ print(t, 0, 25);
+ print(t, 1, 25);
+ print(t, 0, 5);
+ print(t, 2, 5);
+
+ System.out.println("From bottom");
+ print(t, -1, 25);
+ print(t, -5, 5);
+ print(t, -7, 5);
+
+ // Let the thread stop looping and die.
+ data.stop = true;
+ t.join();
+ }
+
+ public static void print(String[] stack) {
+ System.out.println("---------");
+ for (int i = 0; i < stack.length; i += 2) {
+ System.out.print(' ');
+ System.out.print(stack[i]);
+ System.out.print(' ');
+ System.out.println(stack[i + 1]);
+ }
+ }
+
+ public static void print(Thread t, int start, int max) {
+ print(getStackTrace(t, start, max));
+ }
+
+ // Wrap generated stack traces into a class to separate them nicely.
+ public static class Recurse {
+
+ public static int foo(int x, int start, int max, ControlData data) {
+ bar(x, start, max, data);
+ return 0;
+ }
+
+ private static long bar(int x, int start, int max, ControlData data) {
+ baz(x, start, max, data);
+ return 0;
+ }
+
+ private static Object baz(int x, int start, int max, ControlData data) {
+ if (x == 0) {
+ printOrWait(start, max, data);
+ } else {
+ foo(x - 1, start, max, data);
+ }
+ return null;
+ }
+
+ private static void printOrWait(int start, int max, ControlData data) {
+ if (data == null) {
+ print(Thread.currentThread(), start, max);
+ } else {
+ if (data.waitFor != null) {
+ synchronized (data.waitFor) {
+ data.reached.countDown();
+ try {
+ data.waitFor.wait(); // Use wait() as it doesn't have a "hidden" Java call-graph.
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+ } else {
+ data.reached.countDown();
+ while (!data.stop) {
+ // Busy-loop.
+ }
+ }
+ }
+ }
+ }
+
+ public static class ControlData {
+ CountDownLatch reached = new CountDownLatch(1);
+ Object waitFor = null;
+ volatile boolean stop = false;
+ }
+
+ public static native String[] getStackTrace(Thread thread, int start, int max);
+}
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
new file mode 100644
index 0000000..da649cf
--- /dev/null
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013 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 "stack_trace.h"
+
+#include <memory>
+#include <stdio.h>
+
+#include "base/logging.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test911GetStackTrace {
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint start, jint max) {
+ std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]);
+
+ jint count;
+ jvmtiError result = jvmti_env->GetStackTrace(thread, start, max, frames.get(), &count);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetStackTrace: %s\n", err);
+ return nullptr;
+ }
+
+ ScopedLocalRef<jclass> obj_class(env, env->FindClass("java/lang/String"));
+ if (obj_class.get() == nullptr) {
+ return nullptr;
+ }
+
+ jobjectArray ret = env->NewObjectArray(2 * count, obj_class.get(), nullptr);
+ if (ret == nullptr) {
+ return ret;
+ }
+
+ for (size_t i = 0; i < static_cast<size_t>(count); ++i) {
+ char* name;
+ char* sig;
+ char* gen;
+ jvmtiError result2 = jvmti_env->GetMethodName(frames[i].method, &name, &sig, &gen);
+ if (result2 != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetMethodName: %s\n", err);
+ return nullptr;
+ }
+ ScopedLocalRef<jstring> trace_name(env, name == nullptr ? nullptr : env->NewStringUTF(name));
+ ScopedLocalRef<jstring> trace_sig(env, sig == nullptr ? nullptr : env->NewStringUTF(sig));
+ env->SetObjectArrayElement(ret, static_cast<jint>(2 * i), trace_name.get());
+ env->SetObjectArrayElement(ret, static_cast<jint>(2 * i + 1), trace_sig.get());
+
+ if (name != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name));
+ }
+ if (sig != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
+ }
+ if (gen != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
+ }
+ }
+
+ return ret;
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ return 0;
+}
+
+} // namespace Test911GetStackTrace
+} // namespace art
diff --git a/test/911-get-stack-trace/stack_trace.h b/test/911-get-stack-trace/stack_trace.h
new file mode 100644
index 0000000..eba2a91
--- /dev/null
+++ b/test/911-get-stack-trace/stack_trace.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_911_GET_STACK_TRACE_STACK_TRACE_H_
+#define ART_TEST_911_GET_STACK_TRACE_STACK_TRACE_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test911GetStackTrace {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+} // namespace Test911GetStackTrace
+} // namespace art
+
+#endif // ART_TEST_911_GET_STACK_TRACE_STACK_TRACE_H_
diff --git a/test/912-classes/build b/test/912-classes/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/912-classes/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-build "$@" --experimental agents
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
new file mode 100644
index 0000000..4bf329c
--- /dev/null
+++ b/test/912-classes/classes.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013 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 "classes.h"
+
+#include <stdio.h>
+
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test912Classes {
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassSignature(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ char* sig;
+ char* gen;
+ jvmtiError result = jvmti_env->GetClassSignature(klass, &sig, &gen);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetClassSignature: %s\n", err);
+ return nullptr;
+ }
+
+ ScopedLocalRef<jclass> obj_class(env, env->FindClass("java/lang/String"));
+ if (obj_class.get() == nullptr) {
+ return nullptr;
+ }
+
+ jobjectArray ret = env->NewObjectArray(2, obj_class.get(), nullptr);
+ if (ret == nullptr) {
+ return ret;
+ }
+
+ ScopedLocalRef<jstring> sig_str(env, sig == nullptr ? nullptr : env->NewStringUTF(sig));
+ ScopedLocalRef<jstring> gen_str(env, gen == nullptr ? nullptr : env->NewStringUTF(gen));
+
+ env->SetObjectArrayElement(ret, 0, sig_str.get());
+ env->SetObjectArrayElement(ret, 1, gen_str.get());
+
+ // Need to deallocate the strings.
+ if (sig != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
+ }
+ if (gen != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
+ }
+
+ return ret;
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ return 0;
+}
+
+} // namespace Test912Classes
+} // namespace art
diff --git a/test/912-classes/classes.h b/test/912-classes/classes.h
new file mode 100644
index 0000000..62fb203
--- /dev/null
+++ b/test/912-classes/classes.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_912_CLASSES_CLASSES_H_
+#define ART_TEST_912_CLASSES_CLASSES_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test912Classes {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+} // namespace Test912Classes
+} // namespace art
+
+#endif // ART_TEST_912_CLASSES_CLASSES_H_
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
new file mode 100644
index 0000000..71b22f4
--- /dev/null
+++ b/test/912-classes/expected.txt
@@ -0,0 +1,7 @@
+[Ljava/lang/Object;, null]
+[Ljava/lang/String;, null]
+[Ljava/lang/Math;, null]
+[Ljava/util/List;, null]
+[L$Proxy0;, null]
+[I, null]
+[[D, null]
diff --git a/test/912-classes/info.txt b/test/912-classes/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/912-classes/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/912-classes/run b/test/912-classes/run
new file mode 100755
index 0000000..64bbb98
--- /dev/null
+++ b/test/912-classes/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+ lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+ arg="jvm"
+else
+ arg="art"
+fi
+
+if [[ "$@" != *"--debuggable"* ]]; then
+ other_args=" -Xcompiler-option --debuggable "
+else
+ other_args=""
+fi
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:${agent}=912-classes,${arg} \
+ --android-runtime-option -Xplugin:${plugin} \
+ ${other_args} \
+ --args ${lib}
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
new file mode 100644
index 0000000..025584e
--- /dev/null
+++ b/test/912-classes/src/Main.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ testClass("java.lang.Object");
+ testClass("java.lang.String");
+ testClass("java.lang.Math");
+ testClass("java.util.List");
+
+ testClass(getProxyClass());
+
+ testClass(int.class);
+ testClass(double[].class);
+ }
+
+ private static Class<?> proxyClass = null;
+
+ private static Class<?> getProxyClass() throws Exception {
+ if (proxyClass != null) {
+ return proxyClass;
+ }
+
+ proxyClass = Proxy.getProxyClass(Main.class.getClassLoader(), new Class[] { Runnable.class });
+ return proxyClass;
+ }
+
+ private static void testClass(String className) throws Exception {
+ Class<?> base = Class.forName(className);
+ testClass(base);
+ }
+
+ private static void testClass(Class<?> base) throws Exception {
+ String[] result = getClassSignature(base);
+ System.out.println(Arrays.toString(result));
+ }
+
+ private static native String[] getClassSignature(Class<?> c);
+}
diff --git a/test/913-heaps/build b/test/913-heaps/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/913-heaps/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-build "$@" --experimental agents
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
new file mode 100644
index 0000000..77791a4
--- /dev/null
+++ b/test/913-heaps/expected.txt
@@ -0,0 +1,2 @@
+---
+true true
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
new file mode 100644
index 0000000..437779a
--- /dev/null
+++ b/test/913-heaps/heaps.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 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 "heaps.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test913Heaps {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_forceGarbageCollection(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass klass ATTRIBUTE_UNUSED) {
+ jvmtiError ret = jvmti_env->ForceGarbageCollection();
+ if (ret != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(ret, &err);
+ printf("Error forcing a garbage collection: %s\n", err);
+ }
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ return 0;
+}
+
+} // namespace Test913Heaps
+} // namespace art
diff --git a/test/913-heaps/heaps.h b/test/913-heaps/heaps.h
new file mode 100644
index 0000000..bd828ac
--- /dev/null
+++ b/test/913-heaps/heaps.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_913_HEAPS_HEAPS_H_
+#define ART_TEST_913_HEAPS_HEAPS_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test913Heaps {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+} // namespace Test913Heaps
+} // namespace art
+
+#endif // ART_TEST_913_HEAPS_HEAPS_H_
diff --git a/test/913-heaps/info.txt b/test/913-heaps/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/913-heaps/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/913-heaps/run b/test/913-heaps/run
new file mode 100755
index 0000000..7bd8cbd
--- /dev/null
+++ b/test/913-heaps/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+ lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+ arg="jvm"
+else
+ arg="art"
+fi
+
+if [[ "$@" != *"--debuggable"* ]]; then
+ other_args=" -Xcompiler-option --debuggable "
+else
+ other_args=""
+fi
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:${agent}=913-heaps,${arg} \
+ --android-runtime-option -Xplugin:${plugin} \
+ ${other_args} \
+ --args ${lib}
diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java
new file mode 100644
index 0000000..4d77a48
--- /dev/null
+++ b/test/913-heaps/src/Main.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ setupGcCallback();
+
+ enableGcTracking(true);
+ run();
+ enableGcTracking(false);
+ }
+
+ private static void run() {
+ clearStats();
+ forceGarbageCollection();
+ printStats();
+ }
+
+ private static void clearStats() {
+ getGcStarts();
+ getGcFinishes();
+ }
+
+ private static void printStats() {
+ System.out.println("---");
+ int s = getGcStarts();
+ int f = getGcFinishes();
+ System.out.println((s > 0) + " " + (f > 0));
+ }
+
+ private static native void setupGcCallback();
+ private static native void enableGcTracking(boolean enable);
+ private static native int getGcStarts();
+ private static native int getGcFinishes();
+ private static native void forceGarbageCollection();
+}
diff --git a/test/956-methodhandles/build b/test/956-methodhandles/build
index 613e97c..a423ca6 100755
--- a/test/956-methodhandles/build
+++ b/test/956-methodhandles/build
@@ -20,9 +20,6 @@
if [[ $@ != *"--jvm"* ]]; then
# Don't do anything with jvm.
export USE_JACK=true
- export JACK_SERVER=false
- export JACK_REPOSITORY="${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/jacks"
- export JACK_VERSION=4.11.BETA
fi
./default-build "$@" --experimental method-handles
diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt
index ddc1cb0..ad1c43c 100644
--- a/test/956-methodhandles/expected.txt
+++ b/test/956-methodhandles/expected.txt
@@ -3,3 +3,4 @@
foo_A
foo_B
privateRyan_D
+Received exception: Expected (java.lang.String, java.lang.String)java.lang.String but was (java.lang.String, java.lang.Object)void
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index badea53..44c0447 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -57,8 +57,8 @@
public static void main(String[] args) throws Throwable {
testfindSpecial_invokeSuperBehaviour();
testfindSpecial_invokeDirectBehaviour();
-
- testThrowException();
+ testExceptionDetailMessages();
+ testfindVirtual();
}
public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
@@ -87,10 +87,6 @@
mh1.invokeExact(aInstance);
System.out.println("mh1.invoke(aInstance) should not succeeed");
} catch (WrongMethodTypeException expected) {
- } catch (ClassCastException workaround) {
- // TODO(narayan): ART treats all invokes as if they were non-exact. We
- // should throw a WMTE if we execute an invoke-polymorphic instruction whose
- // target method is MethodHandle.invokeExact.
}
// This should *still* be as if an invoke-super was called from one of C's
@@ -134,18 +130,136 @@
}
}
- public static void testThrowException() throws Throwable {
- MethodHandle handle = MethodHandles.throwException(String.class,
- IllegalArgumentException.class);
- if (handle.type().returnType() != String.class) {
- System.out.println("Unexpected return type for handle: " + handle
- + " [ " + handle.type() + "]");
- }
+ public static void testExceptionDetailMessages() throws Throwable {
+ MethodHandle handle = MethodHandles.lookup().findVirtual(String.class, "concat",
+ MethodType.methodType(String.class, String.class));
try {
- handle.invoke();
- System.out.println("Expected an exception of type: java.lang.IllegalArgumentException");
- } catch (IllegalArgumentException expected) {
+ handle.invokeExact("a", new Object());
+ System.out.println("invokeExact(\"a\", new Object()) unexpectedly succeeded.");
+ } catch (WrongMethodTypeException ex) {
+ System.out.println("Received exception: " + ex.getMessage());
+ }
+ }
+
+ public interface Foo {
+ public String foo();
+ }
+
+ public interface Bar extends Foo {
+ public String bar();
+ }
+
+ public static class BarSuper {
+ public String superPublicMethod() {
+ return "superPublicMethod";
+ }
+
+ public String superProtectedMethod() {
+ return "superProtectedMethod";
+ }
+
+ String superPackageMethod() {
+ return "superPackageMethod";
+ }
+ }
+
+ public static class BarImpl extends BarSuper implements Bar {
+ public BarImpl() {
+ }
+
+ @Override
+ public String foo() {
+ return "foo";
+ }
+
+ @Override
+ public String bar() {
+ return "bar";
+ }
+
+ private String privateMethod() { return "privateMethod"; }
+
+ public static String staticMethod() { return null; }
+
+ static final MethodHandles.Lookup lookup = MethodHandles.lookup();
+ }
+
+ public static void testfindVirtual() throws Throwable {
+ // Virtual lookups on static methods should not succeed.
+ try {
+ MethodHandles.lookup().findVirtual(
+ BarImpl.class, "staticMethod", MethodType.methodType(String.class));
+ System.out.println("findVirtual(staticMethod) unexpectedly succeeded");
+ } catch (IllegalAccessException expected) {
+ }
+
+ // Virtual lookups on private methods should not succeed, unless the Lookup
+ // context had sufficient privileges.
+ try {
+ MethodHandles.lookup().findVirtual(
+ BarImpl.class, "privateMethod", MethodType.methodType(String.class));
+ System.out.println("findVirtual(privateMethod) unexpectedly succeeded");
+ } catch (IllegalAccessException expected) {
+ }
+
+ // Virtual lookup on a private method with a context that *does* have sufficient
+ // privileges.
+ MethodHandle mh = BarImpl.lookup.findVirtual(
+ BarImpl.class, "privateMethod", MethodType.methodType(String.class));
+ String str = (String) mh.invoke(new BarImpl());
+ if (!"privateMethod".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#privateMethod: " + str);
+ }
+
+ // Find virtual must find interface methods defined by interfaces implemented
+ // by the class.
+ mh = MethodHandles.lookup().findVirtual(BarImpl.class, "foo",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ if (!"foo".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#foo: " + str);
+ }
+
+ // .. and their super-interfaces.
+ mh = MethodHandles.lookup().findVirtual(BarImpl.class, "bar",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ if (!"bar".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#bar: " + str);
+ }
+
+ // TODO(narayan): Fix this case, we're using the wrong ArtMethod for the
+ // invoke resulting in a failing check in the interpreter.
+ //
+ // mh = MethodHandles.lookup().findVirtual(Bar.class, "bar",
+ // MethodType.methodType(String.class));
+ // str = (String) mh.invoke(new BarImpl());
+ // if (!"bar".equals(str)) {
+ // System.out.println("Unexpected return value for BarImpl#bar: " + str);
+ // }
+
+ // We should also be able to lookup public / protected / package methods in
+ // the super class, given sufficient access privileges.
+ mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPublicMethod",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ if (!"superPublicMethod".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#superPublicMethod: " + str);
+ }
+
+ mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superProtectedMethod",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ if (!"superProtectedMethod".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#superProtectedMethod: " + str);
+ }
+
+ mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPackageMethod",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ if (!"superPackageMethod".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#superPackageMethod: " + str);
}
}
}
diff --git a/test/957-methodhandle-transforms/build b/test/957-methodhandle-transforms/build
new file mode 100755
index 0000000..a423ca6
--- /dev/null
+++ b/test/957-methodhandle-transforms/build
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+ # Don't do anything with jvm.
+ export USE_JACK=true
+fi
+
+./default-build "$@" --experimental method-handles
diff --git a/test/957-methodhandle-transforms/expected.txt b/test/957-methodhandle-transforms/expected.txt
new file mode 100644
index 0000000..fc4fdd6
--- /dev/null
+++ b/test/957-methodhandle-transforms/expected.txt
@@ -0,0 +1,16 @@
+Message: foo, Message2: 42
+Message: foo, Message2: 42
+Message: foo, Message2: 42
+Message: foo, Message2: 42
+Message: foo, Message2: 42
+Message: foo, Message2: 42
+Target: Arg1: foo, Arg2: 42
+Target: Arg1: foo, Arg2: 42
+Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo, Arg2: 42, ExMsg: exceptionMessage
+Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo, Arg2: 42, ExMsg: exceptionMessage
+Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo
+Handler: java.lang.IllegalArgumentException: exceptionMessage, Arg1: foo
+target: target, 42, 56
+target: target, 42, 56
+fallback: fallback, 42, 56
+target: target, 42, 56
diff --git a/test/957-methodhandle-transforms/info.txt b/test/957-methodhandle-transforms/info.txt
new file mode 100644
index 0000000..bc50e85
--- /dev/null
+++ b/test/957-methodhandle-transforms/info.txt
@@ -0,0 +1,3 @@
+Tests for method handle transformations.
+
+NOTE: needs to run under ART or a Java 8 Language runtime and compiler.
diff --git a/test/957-methodhandle-transforms/run b/test/957-methodhandle-transforms/run
new file mode 100755
index 0000000..a9f1822
--- /dev/null
+++ b/test/957-methodhandle-transforms/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+# make us exit on a failure
+set -e
+
+./default-run "$@" --experimental method-handles
diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java
new file mode 100644
index 0000000..4a27086
--- /dev/null
+++ b/test/957-methodhandle-transforms/src/Main.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+
+public class Main {
+ public static void main(String[] args) throws Throwable {
+ testThrowException();
+ testDropArguments();
+ testCatchException();
+ testGuardWithTest();
+ }
+
+ public static void testThrowException() throws Throwable {
+ MethodHandle handle = MethodHandles.throwException(String.class,
+ IllegalArgumentException.class);
+
+ if (handle.type().returnType() != String.class) {
+ System.out.println("Unexpected return type for handle: " + handle +
+ " [ " + handle.type() + "]");
+ }
+
+ final IllegalArgumentException iae = new IllegalArgumentException("boo!");
+ try {
+ handle.invoke(iae);
+ System.out.println("Expected an exception of type: java.lang.IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ if (expected != iae) {
+ System.out.println("Wrong exception: expected " + iae + " but was " + expected);
+ }
+ }
+ }
+
+ public static void dropArguments_delegate(String message, long message2) {
+ System.out.println("Message: " + message + ", Message2: " + message2);
+ }
+
+ public static void testDropArguments() throws Throwable {
+ MethodHandle delegate = MethodHandles.lookup().findStatic(Main.class,
+ "dropArguments_delegate",
+ MethodType.methodType(void.class, new Class<?>[] { String.class, long.class }));
+
+ MethodHandle transform = MethodHandles.dropArguments(delegate, 0, int.class, Object.class);
+
+ // The transformer will accept two additional arguments at position zero.
+ try {
+ transform.invokeExact("foo", 42l);
+ fail();
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ transform.invokeExact(45, new Object(), "foo", 42l);
+ transform.invoke(45, new Object(), "foo", 42l);
+
+ // Additional arguments at position 1.
+ transform = MethodHandles.dropArguments(delegate, 1, int.class, Object.class);
+ transform.invokeExact("foo", 45, new Object(), 42l);
+ transform.invoke("foo", 45, new Object(), 42l);
+
+ // Additional arguments at position 2.
+ transform = MethodHandles.dropArguments(delegate, 2, int.class, Object.class);
+ transform.invokeExact("foo", 42l, 45, new Object());
+ transform.invoke("foo", 42l, 45, new Object());
+
+ // Note that we still perform argument conversions even for the arguments that
+ // are subsequently dropped.
+ try {
+ transform.invoke("foo", 42l, 45l, new Object());
+ fail();
+ } catch (WrongMethodTypeException expected) {
+ } catch (IllegalArgumentException expected) {
+ // TODO(narayan): We currently throw the wrong type of exception here,
+ // it's IAE and should be WMTE instead.
+ }
+
+ // Invalid argument location, should not be allowed.
+ try {
+ MethodHandles.dropArguments(delegate, -1, int.class, Object.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // Invalid argument location, should not be allowed.
+ try {
+ MethodHandles.dropArguments(delegate, 3, int.class, Object.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ MethodHandles.dropArguments(delegate, 1, void.class);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public static String testCatchException_target(String arg1, long arg2, String exceptionMessage)
+ throws Throwable {
+ if (exceptionMessage != null) {
+ throw new IllegalArgumentException(exceptionMessage);
+ }
+
+ System.out.println("Target: Arg1: " + arg1 + ", Arg2: " + arg2);
+ return "target";
+ }
+
+ public static String testCatchException_handler(IllegalArgumentException iae, String arg1, long arg2,
+ String exMsg) {
+ System.out.println("Handler: " + iae + ", Arg1: " + arg1 + ", Arg2: " + arg2 + ", ExMsg: " + exMsg);
+ return "handler1";
+ }
+
+ public static String testCatchException_handler2(IllegalArgumentException iae, String arg1) {
+ System.out.println("Handler: " + iae + ", Arg1: " + arg1);
+ return "handler2";
+ }
+
+ public static void testCatchException() throws Throwable {
+ MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
+ "testCatchException_target",
+ MethodType.methodType(String.class, new Class<?>[] { String.class, long.class, String.class }));
+
+ MethodHandle handler = MethodHandles.lookup().findStatic(Main.class,
+ "testCatchException_handler",
+ MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class,
+ String.class, long.class, String.class }));
+
+ MethodHandle adapter = MethodHandles.catchException(target, IllegalArgumentException.class,
+ handler);
+
+ String returnVal = null;
+
+ // These two should end up calling the target always. We're passing a null exception
+ // message here, which means the target will not throw.
+ returnVal = (String) adapter.invoke("foo", 42, null);
+ assertEquals("target", returnVal);
+ returnVal = (String) adapter.invokeExact("foo", 42l, (String) null);
+ assertEquals("target", returnVal);
+
+ // We're passing a non-null exception message here, which means the target will throw,
+ // which in turn means that the handler must be called for the next two invokes.
+ returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
+ assertEquals("handler1", returnVal);
+ returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
+ assertEquals("handler1", returnVal);
+
+ handler = MethodHandles.lookup().findStatic(Main.class,
+ "testCatchException_handler2",
+ MethodType.methodType(String.class, new Class<?>[] { IllegalArgumentException.class,
+ String.class }));
+ adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler);
+
+ returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
+ assertEquals("handler2", returnVal);
+ returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage");
+ assertEquals("handler2", returnVal);
+
+ // Test that the type of the invoke doesn't matter. Here we call
+ // IllegalArgumentException.toString() on the exception that was thrown by
+ // the target.
+ handler = MethodHandles.lookup().findVirtual(IllegalArgumentException.class,
+ "toString", MethodType.methodType(String.class));
+ adapter = MethodHandles.catchException(target, IllegalArgumentException.class, handler);
+
+ returnVal = (String) adapter.invoke("foo", 42, "exceptionMessage");
+ assertEquals("java.lang.IllegalArgumentException: exceptionMessage", returnVal);
+ returnVal = (String) adapter.invokeExact("foo", 42l, "exceptionMessage2");
+ assertEquals("java.lang.IllegalArgumentException: exceptionMessage2", returnVal);
+ }
+
+ public static boolean testGuardWithTest_test(String arg1, long arg2) {
+ return "target".equals(arg1) && 42 == arg2;
+ }
+
+ public static String testGuardWithTest_target(String arg1, long arg2, int arg3) {
+ System.out.println("target: " + arg1 + ", " + arg2 + ", " + arg3);
+ return "target";
+ }
+
+ public static String testGuardWithTest_fallback(String arg1, long arg2, int arg3) {
+ System.out.println("fallback: " + arg1 + ", " + arg2 + ", " + arg3);
+ return "fallback";
+ }
+
+ public static void testGuardWithTest() throws Throwable {
+ MethodHandle test = MethodHandles.lookup().findStatic(Main.class,
+ "testGuardWithTest_test",
+ MethodType.methodType(boolean.class, new Class<?>[] { String.class, long.class }));
+
+ final MethodType type = MethodType.methodType(String.class,
+ new Class<?>[] { String.class, long.class, int.class });
+
+ final MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
+ "testGuardWithTest_target", type);
+ final MethodHandle fallback = MethodHandles.lookup().findStatic(Main.class,
+ "testGuardWithTest_fallback", type);
+
+ MethodHandle adapter = MethodHandles.guardWithTest(test, target, fallback);
+
+ String returnVal = null;
+
+ returnVal = (String) adapter.invoke("target", 42, 56);
+ assertEquals("target", returnVal);
+ returnVal = (String) adapter.invokeExact("target", 42l, 56);
+ assertEquals("target", returnVal);
+
+ returnVal = (String) adapter.invoke("fallback", 42l, 56);
+ assertEquals("fallback", returnVal);
+ returnVal = (String) adapter.invokeExact("target", 42l, 56);
+ assertEquals("target", returnVal);
+ }
+
+ public static void fail() {
+ System.out.println("FAIL");
+ Thread.dumpStack();
+ }
+
+ public static void assertEquals(String s1, String s2) {
+ if (s1 == s2) {
+ return;
+ }
+
+ if (s1 != null && s2 != null && s1.equals(s2)) {
+ return;
+ }
+
+ throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+ }
+}
+
+
diff --git a/test/958-methodhandle-emulated-stackframe/build b/test/958-methodhandle-emulated-stackframe/build
new file mode 100755
index 0000000..a423ca6
--- /dev/null
+++ b/test/958-methodhandle-emulated-stackframe/build
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+ # Don't do anything with jvm.
+ export USE_JACK=true
+fi
+
+./default-build "$@" --experimental method-handles
diff --git a/test/958-methodhandle-emulated-stackframe/expected.txt b/test/958-methodhandle-emulated-stackframe/expected.txt
new file mode 100644
index 0000000..5f38259
--- /dev/null
+++ b/test/958-methodhandle-emulated-stackframe/expected.txt
@@ -0,0 +1,32 @@
+boolean: false
+char: h
+short: 56
+int: 72
+long: 2147483689
+float: 0.56
+double: 100.0
+String: hello
+Object: goodbye
+boolean: false
+char: h
+short: 56
+int: 72
+long: 73
+float: 0.56
+double: 100.0
+String: hello
+Object: goodbye
+true
+true
+a
+a
+42
+42
+43
+43
+43.0
+43.0
+43.0
+43.0
+plank
+plank
diff --git a/test/958-methodhandle-emulated-stackframe/info.txt b/test/958-methodhandle-emulated-stackframe/info.txt
new file mode 100644
index 0000000..bec2324
--- /dev/null
+++ b/test/958-methodhandle-emulated-stackframe/info.txt
@@ -0,0 +1,5 @@
+Tests for dalvik.system.EmulatedStackFrame, which is used to implement
+MethodHandle transformations. This is a separate test because it tests
+an implementation detail and hence cannot be used with --mode=jvm.
+
+NOTE: needs to run under ART or a Java 8 Language runtime and compiler.
diff --git a/test/958-methodhandle-emulated-stackframe/run b/test/958-methodhandle-emulated-stackframe/run
new file mode 100755
index 0000000..a9f1822
--- /dev/null
+++ b/test/958-methodhandle-emulated-stackframe/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+# make us exit on a failure
+set -e
+
+./default-run "$@" --experimental method-handles
diff --git a/test/958-methodhandle-emulated-stackframe/src/Main.java b/test/958-methodhandle-emulated-stackframe/src/Main.java
new file mode 100644
index 0000000..f739d47
--- /dev/null
+++ b/test/958-methodhandle-emulated-stackframe/src/Main.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+import java.lang.invoke.Transformers.Transformer;
+
+import dalvik.system.EmulatedStackFrame;
+
+public class Main {
+
+ public static void testDelegate_allTypes(boolean z, char a, short b, int c, long d,
+ float e, double f, String g, Object h) {
+ System.out.println("boolean: " + z);
+ System.out.println("char: " + a);
+ System.out.println("short: " + b);
+ System.out.println("int: " + c);
+ System.out.println("long: " + d);
+ System.out.println("float: " + e);
+ System.out.println("double: " + f);
+ System.out.println("String: " + g);
+ System.out.println("Object: " + h);
+ }
+
+ public static boolean testDelegate_returnBoolean() {
+ return true;
+ }
+
+ public static char testDelegate_returnChar() {
+ return 'a';
+ }
+
+ public static int testDelegate_returnInt() {
+ return 42;
+ }
+
+ public static long testDelegate_returnLong() {
+ return 43;
+ }
+
+ public static float testDelegate_returnFloat() {
+ return 43.0f;
+ }
+
+ public static double testDelegate_returnDouble() {
+ return 43.0;
+ }
+
+ public static String testDelegate_returnString() {
+ return "plank";
+ }
+
+ public static class DelegatingTransformer extends Transformer {
+ private final MethodHandle delegate;
+
+ public DelegatingTransformer(MethodHandle delegate) {
+ super(delegate.type());
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void transform(EmulatedStackFrame stackFrame) throws Throwable {
+ delegate.invoke(stackFrame);
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ MethodHandle specialFunctionHandle = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_allTypes", MethodType.methodType(void.class,
+ new Class<?>[] { boolean.class, char.class, short.class, int.class, long.class,
+ float.class, double.class, String.class, Object.class }));
+
+ DelegatingTransformer delegate = new DelegatingTransformer(specialFunctionHandle);
+
+ // Test an exact invoke.
+ //
+ // Note that the shorter form below doesn't work and must be
+ // investigated on the jack side : b/32536744
+ //
+ // delegate.invokeExact(false, 'h', (short) 56, 72, Integer.MAX_VALUE + 42l,
+ // 0.56f, 100.0d, "hello", (Object) "goodbye");
+
+ Object obj = "goodbye";
+ delegate.invokeExact(false, 'h', (short) 56, 72, Integer.MAX_VALUE + 42l,
+ 0.56f, 100.0d, "hello", obj);
+
+ // Test a non exact invoke with one int -> long conversion and a float -> double
+ // conversion.
+ delegate.invoke(false, 'h', (short) 56, 72, 73,
+ 0.56f, 100.0f, "hello", "goodbye");
+
+ // Should throw a WrongMethodTypeException if the types don't align.
+ try {
+ delegate.invoke(false);
+ throw new AssertionError("Call to invoke unexpectedly succeeded");
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ // Test return values.
+
+ // boolean.
+ MethodHandle returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnBoolean", MethodType.methodType(boolean.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((boolean) delegate.invoke());
+ System.out.println((boolean) delegate.invokeExact());
+
+ // char.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnChar", MethodType.methodType(char.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((char) delegate.invoke());
+ System.out.println((char) delegate.invokeExact());
+
+ // int.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnInt", MethodType.methodType(int.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((int) delegate.invoke());
+ System.out.println((int) delegate.invokeExact());
+
+ // long.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnLong", MethodType.methodType(long.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((long) delegate.invoke());
+ System.out.println((long) delegate.invokeExact());
+
+ // float.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnFloat", MethodType.methodType(float.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((float) delegate.invoke());
+ System.out.println((float) delegate.invokeExact());
+
+ // double.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnDouble", MethodType.methodType(double.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((double) delegate.invoke());
+ System.out.println((double) delegate.invokeExact());
+
+ // references.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnString", MethodType.methodType(String.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((String) delegate.invoke());
+ System.out.println((String) delegate.invokeExact());
+ }
+}
+
+
diff --git a/test/979-invoke-polymorphic-accessors/build b/test/979-invoke-polymorphic-accessors/build
new file mode 100644
index 0000000..a423ca6
--- /dev/null
+++ b/test/979-invoke-polymorphic-accessors/build
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+ # Don't do anything with jvm.
+ export USE_JACK=true
+fi
+
+./default-build "$@" --experimental method-handles
diff --git a/test/979-invoke-polymorphic-accessors/expected.txt b/test/979-invoke-polymorphic-accessors/expected.txt
new file mode 100644
index 0000000..2987b6c
--- /dev/null
+++ b/test/979-invoke-polymorphic-accessors/expected.txt
@@ -0,0 +1 @@
+Passed InvokeExact tests for accessors.
diff --git a/test/979-invoke-polymorphic-accessors/info.txt b/test/979-invoke-polymorphic-accessors/info.txt
new file mode 100644
index 0000000..b2f55f0
--- /dev/null
+++ b/test/979-invoke-polymorphic-accessors/info.txt
@@ -0,0 +1 @@
+This test requires Jack with invoke-polymorphic support.
diff --git a/test/979-invoke-polymorphic-accessors/run b/test/979-invoke-polymorphic-accessors/run
new file mode 100644
index 0000000..a9f1822
--- /dev/null
+++ b/test/979-invoke-polymorphic-accessors/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+# make us exit on a failure
+set -e
+
+./default-run "$@" --experimental method-handles
diff --git a/test/979-invoke-polymorphic-accessors/src/Main.java b/test/979-invoke-polymorphic-accessors/src/Main.java
new file mode 100644
index 0000000..6cdcd10
--- /dev/null
+++ b/test/979-invoke-polymorphic-accessors/src/Main.java
@@ -0,0 +1,727 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.WrongMethodTypeException;
+
+public class Main {
+
+ public static class ValueHolder {
+ public boolean m_z = false;
+ public byte m_b = 0;
+ public char m_c = 'a';
+ public short m_s = 0;
+ public int m_i = 0;
+ public float m_f = 0.0f;
+ public double m_d = 0.0;
+ public long m_j = 0;
+ public String m_l = "a";
+
+ public static boolean s_z;
+ public static byte s_b;
+ public static char s_c;
+ public static short s_s;
+ public static int s_i;
+ public static float s_f;
+ public static double s_d;
+ public static long s_j;
+ public static String s_l;
+
+ public final int m_fi = 0xa5a5a5a5;
+ public static final int s_fi = 0x5a5a5a5a;
+ }
+
+ public static class InvokeExactTester {
+ private enum PrimitiveType {
+ Boolean,
+ Byte,
+ Char,
+ Short,
+ Int,
+ Long,
+ Float,
+ Double,
+ String,
+ }
+
+ private enum AccessorType {
+ IPUT,
+ SPUT,
+ IGET,
+ SGET,
+ }
+
+ private static void assertActualAndExpectedMatch(boolean actual, boolean expected)
+ throws AssertionError {
+ if (actual != expected) {
+ throw new AssertionError("Actual != Expected (" + actual + " != " + expected + ")");
+ }
+ }
+
+ private static void assertTrue(boolean value) throws AssertionError {
+ if (!value) {
+ throw new AssertionError("Value is not true");
+ }
+ }
+
+ static void setByte(MethodHandle m, ValueHolder v, byte value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setByte(MethodHandle m, byte value, boolean expectFailure) throws Throwable {
+ setByte(m, null, value, expectFailure);
+ }
+
+ static void getByte(MethodHandle m, ValueHolder v, byte value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final byte got;
+ if (v == null) {
+ got = (byte)m.invokeExact();
+ } else {
+ got = (byte)m.invokeExact(v);
+ }
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getByte(MethodHandle m, byte value, boolean expectFailure) throws Throwable {
+ getByte(m, null, value, expectFailure);
+ }
+
+ static void setChar(MethodHandle m, ValueHolder v, char value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setChar(MethodHandle m, char value, boolean expectFailure) throws Throwable {
+ setChar(m, null, value, expectFailure);
+ }
+
+ static void getChar(MethodHandle m, ValueHolder v, char value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final char got;
+ if (v == null) {
+ got = (char)m.invokeExact();
+ } else {
+ got = (char)m.invokeExact(v);
+ }
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getChar(MethodHandle m, char value, boolean expectFailure) throws Throwable {
+ getChar(m, null, value, expectFailure);
+ }
+
+ static void setShort(MethodHandle m, ValueHolder v, short value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setShort(MethodHandle m, short value, boolean expectFailure) throws Throwable {
+ setShort(m, null, value, expectFailure);
+ }
+
+ static void getShort(MethodHandle m, ValueHolder v, short value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final short got = (v == null) ? (short)m.invokeExact() : (short)m.invokeExact(v);
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getShort(MethodHandle m, short value, boolean expectFailure) throws Throwable {
+ getShort(m, null, value, expectFailure);
+ }
+
+ static void setInt(MethodHandle m, ValueHolder v, int value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setInt(MethodHandle m, int value, boolean expectFailure) throws Throwable {
+ setInt(m, null, value, expectFailure);
+ }
+
+ static void getInt(MethodHandle m, ValueHolder v, int value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final int got = (v == null) ? (int)m.invokeExact() : (int)m.invokeExact(v);
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getInt(MethodHandle m, int value, boolean expectFailure) throws Throwable {
+ getInt(m, null, value, expectFailure);
+ }
+
+ static void setLong(MethodHandle m, ValueHolder v, long value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setLong(MethodHandle m, long value, boolean expectFailure) throws Throwable {
+ setLong(m, null, value, expectFailure);
+ }
+
+ static void getLong(MethodHandle m, ValueHolder v, long value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final long got = (v == null) ? (long)m.invokeExact() : (long)m.invokeExact(v);
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getLong(MethodHandle m, long value, boolean expectFailure) throws Throwable {
+ getLong(m, null, value, expectFailure);
+ }
+
+ static void setFloat(MethodHandle m, ValueHolder v, float value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setFloat(MethodHandle m, float value, boolean expectFailure) throws Throwable {
+ setFloat(m, null, value, expectFailure);
+ }
+
+ static void getFloat(MethodHandle m, ValueHolder v, float value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final float got = (v == null) ? (float)m.invokeExact() : (float)m.invokeExact(v);
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getFloat(MethodHandle m, float value, boolean expectFailure) throws Throwable {
+ getFloat(m, null, value, expectFailure);
+ }
+
+ static void setDouble(MethodHandle m, ValueHolder v, double value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setDouble(MethodHandle m, double value, boolean expectFailure)
+ throws Throwable {
+ setDouble(m, null, value, expectFailure);
+ }
+
+ static void getDouble(MethodHandle m, ValueHolder v, double value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final double got = (v == null) ? (double)m.invokeExact() : (double)m.invokeExact(v);
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getDouble(MethodHandle m, double value, boolean expectFailure)
+ throws Throwable {
+ getDouble(m, null, value, expectFailure);
+ }
+
+ static void setString(MethodHandle m, ValueHolder v, String value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setString(MethodHandle m, String value, boolean expectFailure)
+ throws Throwable {
+ setString(m, null, value, expectFailure);
+ }
+
+ static void getString(MethodHandle m, ValueHolder v, String value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final String got = (v == null) ? (String)m.invokeExact() : (String)m.invokeExact(v);
+ assertTrue(got.equals(value));
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getString(MethodHandle m, String value, boolean expectFailure)
+ throws Throwable {
+ getString(m, null, value, expectFailure);
+ }
+
+ static void setBoolean(MethodHandle m, ValueHolder v, boolean value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setBoolean(MethodHandle m, boolean value, boolean expectFailure)
+ throws Throwable {
+ setBoolean(m, null, value, expectFailure);
+ }
+
+ static void getBoolean(MethodHandle m, ValueHolder v, boolean value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final boolean got =
+ (v == null) ? (boolean)m.invokeExact() : (boolean)m.invokeExact(v);
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getBoolean(MethodHandle m, boolean value, boolean expectFailure)
+ throws Throwable {
+ getBoolean(m, null, value, expectFailure);
+ }
+
+ static boolean resultFor(PrimitiveType actualType, PrimitiveType expectedType,
+ AccessorType actualAccessor,
+ AccessorType expectedAccessor) {
+ return (actualType != expectedType) || (actualAccessor != expectedAccessor);
+ }
+
+ static void tryAccessor(MethodHandle methodHandle,
+ ValueHolder valueHolder,
+ PrimitiveType primitive,
+ Object value,
+ AccessorType accessor) throws Throwable {
+ boolean booleanValue =
+ value instanceof Boolean ? ((Boolean)value).booleanValue() : false;
+ setBoolean(methodHandle, valueHolder, booleanValue,
+ resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.IPUT));
+ setBoolean(methodHandle, booleanValue,
+ resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.SPUT));
+ getBoolean(methodHandle, valueHolder, booleanValue,
+ resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.IGET));
+ getBoolean(methodHandle, booleanValue,
+ resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.SGET));
+
+ byte byteValue = value instanceof Byte ? ((Byte)value).byteValue() : (byte)0;
+ setByte(methodHandle, valueHolder, byteValue,
+ resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.IPUT));
+ setByte(methodHandle, byteValue,
+ resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.SPUT));
+ getByte(methodHandle, valueHolder, byteValue,
+ resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.IGET));
+ getByte(methodHandle, byteValue,
+ resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.SGET));
+
+ char charValue = value instanceof Character ? ((Character)value).charValue() : 'z';
+ setChar(methodHandle, valueHolder, charValue,
+ resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.IPUT));
+ setChar(methodHandle, charValue,
+ resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.SPUT));
+ getChar(methodHandle, valueHolder, charValue,
+ resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.IGET));
+ getChar(methodHandle, charValue,
+ resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.SGET));
+
+ short shortValue = value instanceof Short ? ((Short)value).shortValue() : (short)0;
+ setShort(methodHandle, valueHolder, shortValue,
+ resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.IPUT));
+ setShort(methodHandle, shortValue,
+ resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.SPUT));
+ getShort(methodHandle, valueHolder, shortValue,
+ resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.IGET));
+ getShort(methodHandle, shortValue,
+ resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.SGET));
+
+ int intValue = value instanceof Integer ? ((Integer)value).intValue() : -1;
+ setInt(methodHandle, valueHolder, intValue,
+ resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.IPUT));
+ setInt(methodHandle, intValue,
+ resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.SPUT));
+ getInt(methodHandle, valueHolder, intValue,
+ resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.IGET));
+ getInt(methodHandle, intValue,
+ resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.SGET));
+
+ long longValue = value instanceof Long ? ((Long)value).longValue() : (long)-1;
+ setLong(methodHandle, valueHolder, longValue,
+ resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.IPUT));
+ setLong(methodHandle, longValue,
+ resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.SPUT));
+ getLong(methodHandle, valueHolder, longValue,
+ resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.IGET));
+ getLong(methodHandle, longValue,
+ resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.SGET));
+
+ float floatValue = value instanceof Float ? ((Float)value).floatValue() : -1.0f;
+ setFloat(methodHandle, valueHolder, floatValue,
+ resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.IPUT));
+ setFloat(methodHandle, floatValue,
+ resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.SPUT));
+ getFloat(methodHandle, valueHolder, floatValue,
+ resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.IGET));
+ getFloat(methodHandle, floatValue,
+ resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.SGET));
+
+ double doubleValue = value instanceof Double ? ((Double)value).doubleValue() : -1.0;
+ setDouble(methodHandle, valueHolder, doubleValue,
+ resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.IPUT));
+ setDouble(methodHandle, doubleValue,
+ resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.SPUT));
+ getDouble(methodHandle, valueHolder, doubleValue,
+ resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.IGET));
+ getDouble(methodHandle, doubleValue,
+ resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.SGET));
+
+ String stringValue = value instanceof String ? ((String) value) : "No Spock, no";
+ setString(methodHandle, valueHolder, stringValue,
+ resultFor(primitive, PrimitiveType.String, accessor, AccessorType.IPUT));
+ setString(methodHandle, stringValue,
+ resultFor(primitive, PrimitiveType.String, accessor, AccessorType.SPUT));
+ getString(methodHandle, valueHolder, stringValue,
+ resultFor(primitive, PrimitiveType.String, accessor, AccessorType.IGET));
+ getString(methodHandle, stringValue,
+ resultFor(primitive, PrimitiveType.String, accessor, AccessorType.SGET));
+ }
+
+ public static void main() throws Throwable {
+ ValueHolder valueHolder = new ValueHolder();
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+ boolean [] booleans = { false, true, false };
+ for (boolean b : booleans) {
+ Boolean boxed = new Boolean(b);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_z", boolean.class),
+ valueHolder, PrimitiveType.Boolean, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_z", boolean.class),
+ valueHolder, PrimitiveType.Boolean, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_z == b);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_z", boolean.class),
+ valueHolder, PrimitiveType.Boolean, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_z", boolean.class),
+ valueHolder, PrimitiveType.Boolean, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_z == b);
+ }
+
+ byte [] bytes = { (byte)0x73, (byte)0xfe };
+ for (byte b : bytes) {
+ Byte boxed = new Byte(b);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_b", byte.class),
+ valueHolder, PrimitiveType.Byte, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_b", byte.class),
+ valueHolder, PrimitiveType.Byte, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_b == b);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_b", byte.class),
+ valueHolder, PrimitiveType.Byte, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_b", byte.class),
+ valueHolder, PrimitiveType.Byte, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_b == b);
+ }
+
+ char [] chars = { 'a', 'b', 'c' };
+ for (char c : chars) {
+ Character boxed = new Character(c);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_c", char.class),
+ valueHolder, PrimitiveType.Char, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_c", char.class),
+ valueHolder, PrimitiveType.Char, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_c == c);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_c", char.class),
+ valueHolder, PrimitiveType.Char, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_c", char.class),
+ valueHolder, PrimitiveType.Char, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_c == c);
+ }
+
+ short [] shorts = { (short)0x1234, (short)0x4321 };
+ for (short s : shorts) {
+ Short boxed = new Short(s);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_s", short.class),
+ valueHolder, PrimitiveType.Short, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_s", short.class),
+ valueHolder, PrimitiveType.Short, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_s == s);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_s", short.class),
+ valueHolder, PrimitiveType.Short, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_s", short.class),
+ valueHolder, PrimitiveType.Short, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_s == s);
+ }
+
+ int [] ints = { -100000000, 10000000 };
+ for (int i : ints) {
+ Integer boxed = new Integer(i);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_i", int.class),
+ valueHolder, PrimitiveType.Int, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_i", int.class),
+ valueHolder, PrimitiveType.Int, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_i == i);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_i", int.class),
+ valueHolder, PrimitiveType.Int, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_i", int.class),
+ valueHolder, PrimitiveType.Int, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_i == i);
+ }
+
+ float [] floats = { 0.99f, -1.23e-17f };
+ for (float f : floats) {
+ Float boxed = new Float(f);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_f", float.class),
+ valueHolder, PrimitiveType.Float, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_f", float.class),
+ valueHolder, PrimitiveType.Float, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_f == f);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_f", float.class),
+ valueHolder, PrimitiveType.Float, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_f", float.class),
+ valueHolder, PrimitiveType.Float, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_f == f);
+ }
+
+ double [] doubles = { 0.44444444444e37, -0.555555555e-37 };
+ for (double d : doubles) {
+ Double boxed = new Double(d);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_d", double.class),
+ valueHolder, PrimitiveType.Double, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_d", double.class),
+ valueHolder, PrimitiveType.Double, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_d == d);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_d", double.class),
+ valueHolder, PrimitiveType.Double, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_d", double.class),
+ valueHolder, PrimitiveType.Double, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_d == d);
+ }
+
+ long [] longs = { 0x0123456789abcdefl, 0xfedcba9876543210l };
+ for (long j : longs) {
+ Long boxed = new Long(j);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_j", long.class),
+ valueHolder, PrimitiveType.Long, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_j", long.class),
+ valueHolder, PrimitiveType.Long, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_j == j);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_j", long.class),
+ valueHolder, PrimitiveType.Long, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_j", long.class),
+ valueHolder, PrimitiveType.Long, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_j == j);
+ }
+
+ String [] strings = { "octopus", "crab" };
+ for (String s : strings) {
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_l", String.class),
+ valueHolder, PrimitiveType.String, s, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_l", String.class),
+ valueHolder, PrimitiveType.String, s, AccessorType.IGET);
+ assertTrue(s.equals(valueHolder.m_l));
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_l", String.class),
+ valueHolder, PrimitiveType.String, s, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_l", String.class),
+ valueHolder, PrimitiveType.String, s, AccessorType.SGET);
+ assertTrue(s.equals(ValueHolder.s_l));
+ }
+
+ System.out.println("Passed InvokeExact tests for accessors.");
+ }
+ }
+
+ public static class FindAccessorTester {
+ public static void main() throws Throwable {
+ ValueHolder valueHolder = new ValueHolder();
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+ lookup.findStaticGetter(ValueHolder.class, "s_fi", int.class);
+ try {
+ lookup.findStaticGetter(ValueHolder.class, "s_fi", byte.class);
+ unreachable();
+ } catch (NoSuchFieldException e) {}
+ try {
+ lookup.findGetter(ValueHolder.class, "s_fi", byte.class);
+ unreachable();
+ } catch (NoSuchFieldException e) {}
+ try {
+ lookup.findStaticSetter(ValueHolder.class, "s_fi", int.class);
+ unreachable();
+ } catch (IllegalAccessException e) {}
+
+ lookup.findGetter(ValueHolder.class, "m_fi", int.class);
+ try {
+ lookup.findGetter(ValueHolder.class, "m_fi", byte.class);
+ unreachable();
+ } catch (NoSuchFieldException e) {}
+ try {
+ lookup.findStaticGetter(ValueHolder.class, "m_fi", byte.class);
+ unreachable();
+ } catch (NoSuchFieldException e) {}
+ try {
+ lookup.findSetter(ValueHolder.class, "m_fi", int.class);
+ unreachable();
+ } catch (IllegalAccessException e) {}
+ }
+
+ public static void unreachable() throws Throwable{
+ throw new Error("unreachable");
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ FindAccessorTester.main();
+ InvokeExactTester.main();
+ }
+}
diff --git a/test/Android.arm_vixl.mk b/test/Android.arm_vixl.mk
index 5e1b953..2d9708d 100644
--- a/test/Android.arm_vixl.mk
+++ b/test/Android.arm_vixl.mk
@@ -21,49 +21,36 @@
004-InterfaceTest \
004-JniTest \
004-NativeAllocations \
- 004-ReferenceMap \
- 004-SignalTest \
- 004-StackWalk \
004-ThreadStress \
004-UnsafeTest \
004-checker-UnsafeTest18 \
005-annotations \
- 006-args \
- 008-exceptions \
009-instanceof \
- 011-array-copy \
012-math \
015-switch \
- 017-float \
- 018-stack-overflow \
- 019-wrong-array-type \
- 020-string \
021-string2 \
022-interface \
023-many-interfaces \
024-illegal-access \
025-access-controller \
- 027-arithmetic \
028-array-write \
031-class-attributes \
- 032-concrete-sub \
035-enum \
036-finalizer \
037-inherit \
- 041-narrowing \
042-new-instance \
044-proxy \
045-reflect-array \
046-reflect \
047-returns \
048-reflect-v8 \
- 049-show-object \
050-sync-test \
051-thread \
052-verifier-fun \
+ 053-wait-some \
054-uncaught \
+ 055-enum-performance \
058-enum-order \
- 059-finalizer-throw \
061-out-of-memory \
062-character-encodings \
063-process-manager \
@@ -73,42 +60,29 @@
067-preemptive-unpark \
068-classloader \
069-field-type \
- 070-nio-buffer \
071-dexfile \
- 072-precise-gc \
074-gc-thrash \
075-verification-error \
- 076-boolean-put \
079-phantom \
080-oom-throw \
- 080-oom-throw-with-finalizer \
- 081-hot-exceptions \
082-inline-execute \
083-compiler-regressions \
086-null-super \
087-gc-after-link \
088-monitor-verification \
- 090-loop-formation \
091-override-package-private-method \
093-serialization \
- 094-pattern \
096-array-copy-concurrent-gc \
098-ddmc \
099-vmdebug \
- 100-reflect2 \
- 101-fibonacci \
- 102-concurrent-gc \
103-string-append \
104-growth-limit \
106-exceptions2 \
107-int-math2 \
108-check-cast \
109-suspend-check \
- 112-double-math \
113-multidex \
114-ParallelGC \
- 117-nopatchoat \
- 119-noimage-patchoat \
120-hashcode \
121-modifiers \
122-npe \
@@ -116,7 +90,6 @@
123-inline-execute2 \
127-checker-secondarydex \
129-ThreadGetId \
- 131-structural-change \
132-daemon-locks-shutdown \
133-static-invoke-super \
134-reg-promotion \
@@ -124,53 +97,32 @@
136-daemon-jni-shutdown \
137-cfi \
138-duplicate-classes-check2 \
- 139-register-natives \
140-field-packing \
141-class-unload \
142-classloader2 \
144-static-field-sigquit \
- 145-alloc-tracking-stress \
146-bad-interface \
150-loadlibrary \
+ 151-OpenFileLimit \
201-built-in-except-detail-messages \
- 302-float-conversion \
304-method-tracing \
- 406-fields \
- 407-arrays \
- 410-floats \
- 411-optimizing-arith-mul \
412-new-array \
- 413-regalloc-regression \
- 414-optimizing-arith-sub \
- 414-static-fields \
- 415-optimizing-arith-neg \
416-optimizing-arith-not \
417-optimizing-arith-div \
- 419-long-parameter \
- 421-exceptions \
422-instanceof \
422-type-conversion \
423-invoke-interface \
424-checkcast \
425-invoke-super \
426-monitor \
- 427-bitwise \
- 427-bounds \
428-optimizing-arith-rem \
- 429-ssa-builder \
430-live-register-slow-path \
- 431-optimizing-arith-shifts \
431-type-propagation \
432-optimizing-cmp \
434-invoke-direct \
436-rem-float \
- 436-shift-constant \
437-inline \
- 438-volatile \
439-npe \
- 439-swap-double \
- 440-stmp \
- 441-checker-inliner \
442-checker-constant-folding \
444-checker-nce \
445-checker-licm \
@@ -178,91 +130,59 @@
448-multiple-returns \
449-checker-bce \
450-checker-types \
- 451-regression-add-float \
- 451-spill-splot \
452-multiple-returns2 \
453-not-byte \
- 454-get-vreg \
- 456-baseline-array-set \
- 457-regs \
458-checker-instruct-simplification \
458-long-to-fpu \
- 459-dead-phi \
460-multiple-returns3 \
- 461-get-reference-vreg \
463-checker-boolean-simplifier \
- 466-get-live-vreg \
467-regalloc-pair \
468-checker-bool-simplif-regression \
- 469-condition-materialization \
- 471-deopt-environment \
- 472-type-propagation \
- 474-checker-boolean-input \
- 474-fp-sub-neg \
475-regression-inliner-ids \
477-checker-bound-type \
- 477-long-2-float-convers-precision \
478-checker-clinit-check-pruning \
483-dce-block \
- 484-checker-register-hints \
- 485-checker-dce-loop-update \
485-checker-dce-switch \
486-checker-must-do-null-check \
488-checker-inline-recursive-calls \
490-checker-inline \
- 491-current-method \
492-checker-inline-invoke-interface \
493-checker-inline-invoke-interface \
494-checker-instanceof-tests \
495-checker-checkcast-tests \
496-checker-inlining-class-loader \
497-inlining-and-class-loader \
- 498-type-propagation \
- 499-bce-phi-array-length \
500-instanceof \
- 501-null-constant-dce \
501-regression-packed-switch \
- 503-dead-instructions \
504-regression-baseline-entry \
508-checker-disassembly \
510-checker-try-catch \
- 513-array-deopt \
- 514-shifts \
515-dce-dominator \
517-checker-builder-fallthrough \
518-null-array-get \
519-bound-load-class \
520-equivalent-phi \
- 521-checker-array-set-null \
- 521-regression-integer-field-set \
522-checker-regression-monitor-exit \
523-checker-can-throw-regression \
525-checker-arrays-fields1 \
525-checker-arrays-fields2 \
526-checker-caller-callee-regs \
- 526-long-regalloc \
527-checker-array-access-split \
528-long-hint \
529-checker-unresolved \
- 529-long-split \
530-checker-loops1 \
530-checker-loops2 \
- 530-checker-loops3 \
530-checker-lse \
530-checker-regression-reftyp-final \
530-instanceof-checkcast \
- 532-checker-nonnull-arrayset \
534-checker-bce-deoptimization \
- 535-deopt-and-inlining \
535-regression-const-val \
536-checker-intrinsic-optimization \
536-checker-needs-access-check \
537-checker-inline-and-unverified \
- 537-checker-jump-over-jump \
538-checker-embed-constants \
540-checker-rtp-bug \
541-regression-inlined-deopt \
- 542-bitfield-rotates \
542-unresolved-access-check \
543-checker-dce-trycatch \
543-env-long-ref \
@@ -270,104 +190,60 @@
546-regression-simplify-catch \
550-checker-multiply-accumulate \
550-checker-regression-wide-store \
- 551-checker-shifter-operand \
- 551-implicit-null-checks \
+ 552-checker-sharpening \
551-invoke-super \
552-checker-primitive-typeprop \
- 552-checker-sharpening \
552-invoke-non-existent-super \
553-invoke-super \
554-checker-rtp-checkcast \
555-UnsafeGetLong-regression \
556-invoke-super \
- 557-checker-instruct-simplifier-ror \
558-switch \
- 559-bce-ssa \
- 559-checker-irreducible-loop \
- 559-checker-rtp-ifnotnull \
560-packed-switch \
561-divrem \
- 561-shared-slowpaths \
562-bce-preheader \
- 562-no-intermediate \
563-checker-fakestring \
- 564-checker-bitcount \
- 564-checker-irreducible-loop \
564-checker-negbitwise \
- 565-checker-condition-liveness \
- 565-checker-doublenegbitwise \
- 565-checker-irreducible-loop \
- 565-checker-rotate \
566-polymorphic-inlining \
- 568-checker-onebit \
570-checker-osr \
570-checker-select \
- 571-irreducible-loop \
- 572-checker-array-get-regression \
573-checker-checkcast-regression \
- 574-irreducible-and-constant-area \
- 575-checker-isnan \
575-checker-string-init-alias \
- 577-checker-fp2int \
- 578-bce-visit \
580-checker-round \
- 580-checker-string-fact-intrinsics \
- 581-rtp \
- 582-checker-bce-length \
- 583-checker-zero \
584-checker-div-bool \
586-checker-null-array-get \
587-inline-class-error \
588-checker-irreducib-lifetime-hole \
589-super-imt \
- 590-checker-arr-set-null-regression \
- 591-new-instance-string \
592-checker-regression-bool-input \
- 593-checker-long-2-float-regression \
- 593-checker-shift-and-simplifier \
594-checker-array-alias \
594-invoke-super \
594-load-string-regression \
- 595-error-class \
- 596-checker-dead-phi \
597-deopt-new-string \
- 599-checker-irreducible-loop \
600-verifier-fails \
601-method-access \
602-deoptimizeable \
603-checker-instanceof \
- 604-hot-static-interface \
605-new-string-from-bytes \
608-checker-unresolved-lse \
609-checker-inline-interface \
- 609-checker-x86-bounds-check \
610-arraycopy \
- 611-checker-simplify-if \
612-jit-dex-cache \
- 613-inlining-dex-cache \
- 614-checker-dump-constant-location \
- 615-checker-arm64-store-zero \
- 617-clinit-oome \
- 618-checker-induction \
700-LoadArgRegs \
701-easy-div-rem \
702-LargeBranchOffset \
- 703-floating-point-div \
- 704-multiply-accumulate \
- 705-register-conflict \
800-smali \
802-deoptimization \
960-default-smali \
- 961-default-iface-resolution-gen \
963-default-range-smali \
965-default-verify \
966-default-conflict \
967-default-ame \
- 968-default-partial-compile-gen \
969-iface-super \
971-iface-super \
972-default-imt-collision \
972-iface-super-multidex \
973-default-multidex \
974-verify-interface-super \
- 975-iface-private
+ 975-iface-private \
+ 979-invoke-polymorphic-accessors
diff --git a/test/Android.bp b/test/Android.bp
index be1864c..bdb7f80 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -253,6 +253,10 @@
"907-get-loaded-classes/get_loaded_classes.cc",
"908-gc-start-finish/gc_callbacks.cc",
"909-attach-agent/attach.cc",
+ "910-methods/methods.cc",
+ "911-get-stack-trace/stack_trace.cc",
+ "912-classes/classes.cc",
+ "913-heaps/heaps.cc",
],
shared_libs: [
"libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index a3c16e6..ae569f9 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -276,6 +276,10 @@
907-get-loaded-classes \
908-gc-start-finish \
909-attach-agent \
+ 910-methods \
+ 911-get-stack-trace \
+ 912-classes \
+ 913-heaps \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -370,13 +374,17 @@
# Tests that are broken with GC stress.
# * 137-cfi needs to unwind a second forked process. We're using a primitive sleep to wait till we
# hope the second process got into the expected state. The slowness of gcstress makes this bad.
+# * 908-gc-start-finish expects GCs only to be run at clear points. The reduced heap size makes
+# this non-deterministic. Same for 913.
# * 961-default-iface-resolution-gen and 964-default-iface-init-genare very long tests that often
# will take more than the timeout to run when gcstress is enabled. This is because gcstress
# slows down allocations significantly which these tests do a lot.
TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
137-cfi \
+ 908-gc-start-finish \
+ 913-heaps \
961-default-iface-resolution-gen \
- 964-default-iface-init-gen
+ 964-default-iface-init-gen \
ifneq (,$(filter gcstress,$(GC_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 4248148..a2eb370 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -128,9 +128,10 @@
return;
}
+ Thread* self = Thread::Current();
ArtMethod* method = nullptr;
{
- ScopedObjectAccess soa(Thread::Current());
+ ScopedObjectAccess soa(self);
ScopedUtfChars chars(env, method_name);
CHECK(chars.c_str() != nullptr);
@@ -147,11 +148,11 @@
} else {
// Sleep to yield to the compiler thread.
usleep(1000);
- ScopedObjectAccess soa(Thread::Current());
+ ScopedObjectAccess soa(self);
// Make sure there is a profiling info, required by the compiler.
- ProfilingInfo::Create(soa.Self(), method, /* retry_allocation */ true);
+ ProfilingInfo::Create(self, method, /* retry_allocation */ true);
// Will either ensure it's compiled or do the compilation itself.
- jit->CompileMethod(method, soa.Self(), /* osr */ false);
+ jit->CompileMethod(method, self, /* osr */ false);
}
}
}
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 3535f32..e769812 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -148,7 +148,7 @@
SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
# Enable cfg-append to make sure we get the dump for both dex files.
# (otherwise the runtime compilation of the secondary dex will overwrite
- # the dump of the first one)
+ # the dump of the first one).
FLAGS="${FLAGS} -Xcompiler-option --dump-cfg-append"
COMPILE_FLAGS="${COMPILE_FLAGS} --dump-cfg-append"
shift
@@ -364,8 +364,8 @@
if [ "$JIT" = "y" ]; then
INT_OPTS="-Xusejit:true"
if [ "$VERIFY" = "y" ] ; then
- INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify-at-runtime"
- COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-at-runtime"
+ INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=interpret-only"
+ COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
else
INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify-none"
COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-none"
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 90d0a66..a959482 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -33,6 +33,10 @@
#include "907-get-loaded-classes/get_loaded_classes.h"
#include "908-gc-start-finish/gc_callbacks.h"
#include "909-attach-agent/attach.h"
+#include "910-methods/methods.h"
+#include "911-get-stack-trace/stack_trace.h"
+#include "912-classes/classes.h"
+#include "913-heaps/heaps.h"
namespace art {
@@ -58,6 +62,10 @@
{ "907-get-loaded-classes", Test907GetLoadedClasses::OnLoad, nullptr },
{ "908-gc-start-finish", Test908GcStartFinish::OnLoad, nullptr },
{ "909-attach-agent", nullptr, Test909AttachAgent::OnAttach },
+ { "910-methods", Test910Methods::OnLoad, nullptr },
+ { "911-get-stack-trace", Test911GetStackTrace::OnLoad, nullptr },
+ { "912-classes", Test912Classes::OnLoad, nullptr },
+ { "913-heaps", Test913Heaps::OnLoad, nullptr },
};
static AgentLib* FindAgent(char* name) {
diff --git a/tools/cpp-define-generator/constant_class.def b/tools/cpp-define-generator/constant_class.def
index 58372f9..f46cd33 100644
--- a/tools/cpp-define-generator/constant_class.def
+++ b/tools/cpp-define-generator/constant_class.def
@@ -25,6 +25,7 @@
DEFINE_FLAG_OFFSET(MIRROR_CLASS, STATUS_INITIALIZED, art::mirror::Class::kStatusInitialized)
DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_FINALIZABLE, art::kAccClassIsFinalizable)
+DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_INTERFACE, art::kAccInterface)
// TODO: We should really have a BitPosition which also checks it's a power of 2.
DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_FINALIZABLE_BIT, art::MostSignificantBit(art::kAccClassIsFinalizable))
diff --git a/tools/cpp-define-generator/generate-asm-support b/tools/cpp-define-generator/generate-asm-support
index f95648b..fcdf72f 100755
--- a/tools/cpp-define-generator/generate-asm-support
+++ b/tools/cpp-define-generator/generate-asm-support
@@ -5,4 +5,4 @@
[[ -z ${ANDROID_BUILD_TOP+x} ]] && (echo "Run source build/envsetup.sh first" >&2 && exit 1)
-cpp-define-generator-datad > ${ANDROID_BUILD_TOP}/art/runtime/generated/asm_support_gen.h
+cpp-define-generator-data > ${ANDROID_BUILD_TOP}/art/runtime/generated/asm_support_gen.h