Merge "Remove obsolete ANDROID_COMPILE_WITH_JACK"
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index cde41e0..df7df26 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -201,7 +201,6 @@
LOCAL_MODULE_PATH := $(3)
LOCAL_DEX_PREOPT_IMAGE_LOCATION := $(TARGET_CORE_IMG_OUT)
ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
- LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp
endif
include $(BUILD_JAVA_LIBRARY)
@@ -217,7 +216,6 @@
LOCAL_JAVA_LIBRARIES := $(HOST_CORE_JARS)
LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_LOCATION)
ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
- LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp
endif
include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index b5c99e8..70161eb 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -161,7 +161,14 @@
class LinkerPatch {
public:
- enum class Type {
+ // Note: We explicitly specify the underlying type of the enum because GCC
+ // would otherwise select a bigger underlying type and then complain that
+ // 'art::LinkerPatch::patch_type_' is too small to hold all
+ // values of 'enum class art::LinkerPatch::Type'
+ // which is ridiculous given we have only a handful of values here. If we
+ // choose to squeeze the Type into fewer than 8 bits, we'll have to declare
+ // patch_type_ as an uintN_t and do explicit static_cast<>s.
+ enum class Type : uint8_t {
kRecordPosition, // Just record patch position for patchoat.
kMethod,
kCall,
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index fe2abe4..cc46a07 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -2834,7 +2834,7 @@
} else if (in.IsConstant()) {
int64_t v = in.GetConstant()->AsLongConstant()->GetValue();
XmmRegister dest = out.AsFpuRegister<XmmRegister>();
- codegen_->Load64BitValue(dest, static_cast<double>(v));
+ codegen_->Load32BitValue(dest, static_cast<float>(v));
} else {
__ cvtsi2ss(out.AsFpuRegister<XmmRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()), true);
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index f2e77e2..c790d01 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -1005,4 +1005,19 @@
}
}
+void GraphChecker::VisitTypeConversion(HTypeConversion* instruction) {
+ VisitInstruction(instruction);
+ Primitive::Type result_type = instruction->GetResultType();
+ Primitive::Type input_type = instruction->GetInputType();
+ // Invariant: We should never generate a conversion to a Boolean value.
+ if (result_type == Primitive::kPrimBoolean) {
+ AddError(StringPrintf(
+ "%s %d converts to a %s (from a %s).",
+ instruction->DebugName(),
+ instruction->GetId(),
+ Primitive::PrettyDescriptor(result_type),
+ Primitive::PrettyDescriptor(input_type)));
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index a536b30..83b1984 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -67,6 +67,7 @@
void VisitReturnVoid(HReturnVoid* ret) OVERRIDE;
void VisitSelect(HSelect* instruction) OVERRIDE;
void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE;
+ void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
void HandleLoop(HBasicBlock* loop_header);
void HandleBooleanInput(HInstruction* instruction, size_t input_index);
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 1249b48..1f66db7 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -786,14 +786,21 @@
}
static bool IsTypeConversionImplicit(Primitive::Type input_type, Primitive::Type result_type) {
+ // Invariant: We should never generate a conversion to a Boolean value.
+ DCHECK_NE(Primitive::kPrimBoolean, result_type);
+
// Besides conversion to the same type, widening integral conversions are implicit,
// excluding conversions to long and the byte->char conversion where we need to
// clear the high 16 bits of the 32-bit sign-extended representation of byte.
return result_type == input_type ||
- (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimByte) ||
- (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimShort) ||
- (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimChar) ||
- (result_type == Primitive::kPrimShort && input_type == Primitive::kPrimByte);
+ (result_type == Primitive::kPrimInt && (input_type == Primitive::kPrimBoolean ||
+ input_type == Primitive::kPrimByte ||
+ input_type == Primitive::kPrimShort ||
+ input_type == Primitive::kPrimChar)) ||
+ (result_type == Primitive::kPrimChar && input_type == Primitive::kPrimBoolean) ||
+ (result_type == Primitive::kPrimShort && (input_type == Primitive::kPrimBoolean ||
+ input_type == Primitive::kPrimByte)) ||
+ (result_type == Primitive::kPrimByte && input_type == Primitive::kPrimBoolean);
}
static bool IsTypeConversionLossless(Primitive::Type input_type, Primitive::Type result_type) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index ba42421..9425ef3 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -4904,6 +4904,8 @@
dex_pc) {
SetRawInputAt(0, input);
DCHECK_NE(input->GetType(), result_type);
+ // Invariant: We should never generate a conversion to a Boolean value.
+ DCHECK_NE(Primitive::kPrimBoolean, result_type);
}
HInstruction* GetInput() const { return InputAt(0); }
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 63ae342..a1b3c9e 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -320,11 +320,11 @@
PatchOat& p = space_to_patchoat_map.find(space)->second;
- if (!p.WriteImage(output_image_file.get())) {
- LOG(ERROR) << "Failed to write image file " << output_image_file->GetPath();
+ bool success = p.WriteImage(output_image_file.get());
+ success = FinishFile(output_image_file.get(), success);
+ if (!success) {
return false;
}
- FinishFile(output_image_file.get(), true);
bool skip_patching_oat = space_to_skip_patching_map.find(space)->second;
if (!skip_patching_oat) {
@@ -336,11 +336,11 @@
LOG(ERROR) << "Failed to open output oat file at " << output_oat_filename;
return false;
}
- if (!p.WriteElf(output_oat_file.get())) {
- LOG(ERROR) << "Failed to write oat file " << output_oat_file->GetPath();
+ success = p.WriteElf(output_oat_file.get());
+ success = FinishFile(output_oat_file.get(), success);
+ if (!success) {
return false;
}
- FinishFile(output_oat_file.get(), true);
}
}
return true;
diff --git a/runtime/base/time_utils.cc b/runtime/base/time_utils.cc
index b7cf207..3e5bac8 100644
--- a/runtime/base/time_utils.cc
+++ b/runtime/base/time_utils.cc
@@ -15,6 +15,7 @@
*/
#include <inttypes.h>
+#include <limits>
#include <sstream>
#include "time_utils.h"
@@ -190,9 +191,16 @@
}
int64_t end_sec = ts->tv_sec + ms / 1000;
- if (UNLIKELY(end_sec >= 0x7fffffff)) {
- LOG(INFO) << "Note: end time exceeds INT32_MAX: " << end_sec;
- end_sec = 0x7ffffffe;
+ constexpr int32_t int32_max = std::numeric_limits<int32_t>::max();
+ if (UNLIKELY(end_sec >= int32_max)) {
+ // Either ms was intended to denote an infinite timeout, or we have a
+ // problem. The former generally uses the largest possible millisecond
+ // or nanosecond value. Log only in the latter case.
+ constexpr int64_t int64_max = std::numeric_limits<int64_t>::max();
+ if (ms != int64_max && ms != int64_max / (1000 * 1000)) {
+ LOG(INFO) << "Note: end time exceeds INT32_MAX: " << end_sec;
+ }
+ end_sec = int32_max - 1; // Allow for increment below.
}
ts->tv_sec = end_sec;
ts->tv_nsec = (ts->tv_nsec + (ms % 1000) * 1000000) + ns;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 109e03d..d832552 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -4818,7 +4818,7 @@
LOG(INFO) << "Tracked allocations, (count=" << count << ")";
for (auto it = records->RBegin(), end = records->REnd();
count > 0 && it != end; count--, it++) {
- const gc::AllocRecord* record = it->second;
+ const gc::AllocRecord* record = &it->second;
LOG(INFO) << StringPrintf(" Thread %-2d %6zd bytes ", record->GetTid(), record->ByteCount())
<< PrettyClass(record->GetClass());
@@ -4957,7 +4957,7 @@
uint16_t count = capped_count;
for (auto it = records->RBegin(), end = records->REnd();
count > 0 && it != end; count--, it++) {
- const gc::AllocRecord* record = it->second;
+ const gc::AllocRecord* record = &it->second;
std::string temp;
class_names.Add(record->GetClassDescriptor(&temp));
for (size_t i = 0, depth = record->GetDepth(); i < depth; i++) {
@@ -5008,7 +5008,7 @@
// (2b) thread id
// (2b) allocated object's class name index
// (1b) stack depth
- const gc::AllocRecord* record = it->second;
+ const gc::AllocRecord* record = &it->second;
size_t stack_depth = record->GetDepth();
size_t allocated_object_class_name_index =
class_names.IndexOf(record->GetClassDescriptor(&temp));
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 5344cdd..116261b 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -39,6 +39,7 @@
namespace art {
+template <bool kResolve = true>
inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method,
const InlineInfo& inline_info,
uint8_t inlining_depth)
@@ -50,6 +51,9 @@
if (!caller->IsRuntimeMethod()) {
return caller;
}
+ if (!kResolve) {
+ return nullptr;
+ }
// The method in the dex cache can be the runtime method responsible for invoking
// the stub that will then update the dex cache. Therefore, we need to do the
@@ -64,7 +68,7 @@
if (inlining_depth == 0) {
class_loader.Assign(outer_method->GetClassLoader());
} else {
- caller = GetResolvedMethod(outer_method, inline_info, inlining_depth - 1);
+ caller = GetResolvedMethod<kResolve>(outer_method, inline_info, inlining_depth - 1);
class_loader.Assign(caller->GetClassLoader());
}
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index e3714bb..bd023b3 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -102,15 +102,15 @@
// Only visit the last recent_record_max_ number of allocation records in entries_ and mark the
// klass_ fields as strong roots.
for (auto it = entries_.rbegin(), end = entries_.rend(); it != end; ++it) {
- AllocRecord* record = it->second;
+ AllocRecord& record = it->second;
if (count > 0) {
- buffered_visitor.VisitRootIfNonNull(record->GetClassGcRoot());
+ buffered_visitor.VisitRootIfNonNull(record.GetClassGcRoot());
--count;
}
// Visit all of the stack frames to make sure no methods in the stack traces get unloaded by
// class unloading.
- for (size_t i = 0, depth = record->GetDepth(); i < depth; ++i) {
- const AllocRecordStackTraceElement& element = record->StackElement(i);
+ for (size_t i = 0, depth = record.GetDepth(); i < depth; ++i) {
+ const AllocRecordStackTraceElement& element = record.StackElement(i);
DCHECK(element.GetMethod() != nullptr);
element.GetMethod()->VisitRoots(buffered_visitor, sizeof(void*));
}
@@ -143,15 +143,14 @@
++count;
// This does not need a read barrier because this is called by GC.
mirror::Object* old_object = it->first.Read<kWithoutReadBarrier>();
- AllocRecord* record = it->second;
+ AllocRecord& record = it->second;
mirror::Object* new_object = old_object == nullptr ? nullptr : visitor->IsMarked(old_object);
if (new_object == nullptr) {
if (count > delete_bound) {
it->first = GcRoot<mirror::Object>(nullptr);
- SweepClassObject(record, visitor);
+ SweepClassObject(&record, visitor);
++it;
} else {
- delete record;
it = entries_.erase(it);
++count_deleted;
}
@@ -160,7 +159,7 @@
it->first = GcRoot<mirror::Object>(new_object);
++count_moved;
}
- SweepClassObject(record, visitor);
+ SweepClassObject(&record, visitor);
++it;
}
}
@@ -184,34 +183,32 @@
new_record_condition_.Broadcast(Thread::Current());
}
-struct AllocRecordStackVisitor : public StackVisitor {
- AllocRecordStackVisitor(Thread* thread, AllocRecordStackTrace* trace_in, size_t max)
+class AllocRecordStackVisitor : public StackVisitor {
+ public:
+ AllocRecordStackVisitor(Thread* thread, size_t max_depth, AllocRecordStackTrace* trace_out)
SHARED_REQUIRES(Locks::mutator_lock_)
- : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
- trace(trace_in),
- max_depth(max) {}
+ : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFramesNoResolve),
+ max_depth_(max_depth),
+ trace_(trace_out) {}
// TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
// annotalysis.
bool VisitFrame() OVERRIDE NO_THREAD_SAFETY_ANALYSIS {
- if (depth >= max_depth) {
+ if (trace_->GetDepth() >= max_depth_) {
return false;
}
ArtMethod* m = GetMethod();
- if (!m->IsRuntimeMethod()) {
- trace->SetStackElementAt(depth, m, GetDexPc());
- ++depth;
+ // m may be null if we have inlined methods of unresolved classes. b/27858645
+ if (m != nullptr && !m->IsRuntimeMethod()) {
+ m = m->GetInterfaceMethodIfProxy(sizeof(void*));
+ trace_->AddStackElement(AllocRecordStackTraceElement(m, GetDexPc()));
}
return true;
}
- ~AllocRecordStackVisitor() {
- trace->SetDepth(depth);
- }
-
- AllocRecordStackTrace* trace;
- size_t depth = 0u;
- const size_t max_depth;
+ private:
+ const size_t max_depth_;
+ AllocRecordStackTrace* const trace_;
};
void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) {
@@ -235,7 +232,6 @@
if (self_name == "JDWP") {
records->alloc_ddm_thread_id_ = self->GetTid();
}
- records->scratch_trace_.SetDepth(records->max_stack_depth_);
size_t sz = sizeof(AllocRecordStackTraceElement) * records->max_stack_depth_ +
sizeof(AllocRecord) + sizeof(AllocRecordStackTrace);
LOG(INFO) << "Enabling alloc tracker (" << records->alloc_record_max_ << " entries of "
@@ -265,27 +261,35 @@
}
}
-void AllocRecordObjectMap::RecordAllocation(Thread* self, mirror::Object* obj, mirror::Class* klass,
+void AllocRecordObjectMap::RecordAllocation(Thread* self,
+ mirror::Object** obj,
size_t byte_count) {
+ // Get stack trace outside of lock in case there are allocations during the stack walk.
+ // b/27858645.
+ AllocRecordStackTrace trace;
+ AllocRecordStackVisitor visitor(self, max_stack_depth_, /*out*/ &trace);
+ {
+ StackHandleScope<1> hs(self);
+ auto obj_wrapper = hs.NewHandleWrapper(obj);
+ visitor.WalkStack();
+ }
+
MutexLock mu(self, *Locks::alloc_tracker_lock_);
- Heap* heap = Runtime::Current()->GetHeap();
+ Heap* const heap = Runtime::Current()->GetHeap();
if (!heap->IsAllocTrackingEnabled()) {
// In the process of shutting down recording, bail.
return;
}
- AllocRecordObjectMap* records = heap->GetAllocationRecords();
- DCHECK(records != nullptr);
-
- // Do not record for DDM thread
- if (records->alloc_ddm_thread_id_ == self->GetTid()) {
+ // Do not record for DDM thread.
+ if (alloc_ddm_thread_id_ == self->GetTid()) {
return;
}
// Wait for GC's sweeping to complete and allow new records
- while (UNLIKELY((!kUseReadBarrier && !records->allow_new_record_) ||
+ while (UNLIKELY((!kUseReadBarrier && !allow_new_record_) ||
(kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) {
- records->new_record_condition_.WaitHoldingLocks(self);
+ new_record_condition_.WaitHoldingLocks(self);
}
if (!heap->IsAllocTrackingEnabled()) {
@@ -294,28 +298,22 @@
return;
}
- DCHECK_LE(records->Size(), records->alloc_record_max_);
+ DCHECK_LE(Size(), alloc_record_max_);
- // Get stack trace.
- // add scope to make "visitor" destroyed promptly, in order to set the scratch_trace_->depth_
- {
- AllocRecordStackVisitor visitor(self, &records->scratch_trace_, records->max_stack_depth_);
- visitor.WalkStack();
- }
- records->scratch_trace_.SetTid(self->GetTid());
- AllocRecordStackTrace* trace = new AllocRecordStackTrace(records->scratch_trace_);
+ // Erase extra unfilled elements.
+ trace.SetTid(self->GetTid());
- // Fill in the basics.
- AllocRecord* record = new AllocRecord(byte_count, klass, trace);
-
- records->Put(obj, record);
- DCHECK_LE(records->Size(), records->alloc_record_max_);
+ // Add the record.
+ Put(*obj, AllocRecord(byte_count, (*obj)->GetClass(), std::move(trace)));
+ DCHECK_LE(Size(), alloc_record_max_);
}
void AllocRecordObjectMap::Clear() {
- STLDeleteValues(&entries_);
entries_.clear();
}
+AllocRecordObjectMap::AllocRecordObjectMap()
+ : new_record_condition_("New allocation record condition", *Locks::alloc_tracker_lock_) {}
+
} // namespace gc
} // namespace art
diff --git a/runtime/gc/allocation_record.h b/runtime/gc/allocation_record.h
index 18cce4d..a2d86cc 100644
--- a/runtime/gc/allocation_record.h
+++ b/runtime/gc/allocation_record.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_GC_ALLOCATION_RECORD_H_
#include <list>
+#include <memory>
#include "base/mutex.h"
#include "object_callbacks.h"
@@ -37,10 +38,13 @@
class AllocRecordStackTraceElement {
public:
- AllocRecordStackTraceElement() : method_(nullptr), dex_pc_(0) {}
-
int32_t ComputeLineNumber() const SHARED_REQUIRES(Locks::mutator_lock_);
+ AllocRecordStackTraceElement() = default;
+ AllocRecordStackTraceElement(ArtMethod* method, uint32_t dex_pc)
+ : method_(method),
+ dex_pc_(dex_pc) {}
+
ArtMethod* GetMethod() const {
return method_;
}
@@ -58,32 +62,27 @@
}
bool operator==(const AllocRecordStackTraceElement& other) const {
- if (this == &other) return true;
return method_ == other.method_ && dex_pc_ == other.dex_pc_;
}
private:
- ArtMethod* method_;
- uint32_t dex_pc_;
+ ArtMethod* method_ = nullptr;
+ uint32_t dex_pc_ = 0;
};
class AllocRecordStackTrace {
public:
static constexpr size_t kHashMultiplier = 17;
- explicit AllocRecordStackTrace(size_t max_depth)
- : tid_(0), depth_(0), stack_(new AllocRecordStackTraceElement[max_depth]) {}
+ AllocRecordStackTrace() = default;
+
+ AllocRecordStackTrace(AllocRecordStackTrace&& r)
+ : tid_(r.tid_),
+ stack_(std::move(r.stack_)) {}
AllocRecordStackTrace(const AllocRecordStackTrace& r)
- : tid_(r.tid_), depth_(r.depth_), stack_(new AllocRecordStackTraceElement[r.depth_]) {
- for (size_t i = 0; i < depth_; ++i) {
- stack_[i] = r.stack_[i];
- }
- }
-
- ~AllocRecordStackTrace() {
- delete[] stack_;
- }
+ : tid_(r.tid_),
+ stack_(r.stack_) {}
pid_t GetTid() const {
return tid_;
@@ -94,37 +93,32 @@
}
size_t GetDepth() const {
- return depth_;
- }
-
- void SetDepth(size_t depth) {
- depth_ = depth;
+ return stack_.size();
}
const AllocRecordStackTraceElement& GetStackElement(size_t index) const {
- DCHECK_LT(index, depth_);
+ DCHECK_LT(index, GetDepth());
return stack_[index];
}
+ void AddStackElement(const AllocRecordStackTraceElement& element) {
+ stack_.push_back(element);
+ }
+
void SetStackElementAt(size_t index, ArtMethod* m, uint32_t dex_pc) {
+ DCHECK_LT(index, stack_.size());
stack_[index].SetMethod(m);
stack_[index].SetDexPc(dex_pc);
}
bool operator==(const AllocRecordStackTrace& other) const {
if (this == &other) return true;
- if (tid_ != other.tid_) return false;
- if (depth_ != other.depth_) return false;
- for (size_t i = 0; i < depth_; ++i) {
- if (!(stack_[i] == other.stack_[i])) return false;
- }
- return true;
+ return tid_ == other.tid_ && stack_ == other.stack_;
}
private:
- pid_t tid_;
- size_t depth_;
- AllocRecordStackTraceElement* const stack_;
+ pid_t tid_ = 0;
+ std::vector<AllocRecordStackTraceElement> stack_;
};
struct HashAllocRecordTypes {
@@ -161,19 +155,15 @@
class AllocRecord {
public:
// All instances of AllocRecord should be managed by an instance of AllocRecordObjectMap.
- AllocRecord(size_t count, mirror::Class* klass, AllocRecordStackTrace* trace)
- : byte_count_(count), klass_(klass), trace_(trace) {}
-
- ~AllocRecord() {
- delete trace_;
- }
+ AllocRecord(size_t count, mirror::Class* klass, AllocRecordStackTrace&& trace)
+ : byte_count_(count), klass_(klass), trace_(std::move(trace)) {}
size_t GetDepth() const {
- return trace_->GetDepth();
+ return trace_.GetDepth();
}
const AllocRecordStackTrace* GetStackTrace() const {
- return trace_;
+ return &trace_;
}
size_t ByteCount() const {
@@ -181,7 +171,7 @@
}
pid_t GetTid() const {
- return trace_->GetTid();
+ return trace_.GetTid();
}
mirror::Class* GetClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -196,16 +186,15 @@
}
const AllocRecordStackTraceElement& StackElement(size_t index) const {
- return trace_->GetStackElement(index);
+ return trace_.GetStackElement(index);
}
private:
const size_t byte_count_;
// The klass_ could be a strong or weak root for GC
GcRoot<mirror::Class> klass_;
- // TODO: Currently trace_ is like a std::unique_ptr,
- // but in future with deduplication it could be a std::shared_ptr.
- const AllocRecordStackTrace* const trace_;
+ // TODO: Share between alloc records with identical stack traces.
+ AllocRecordStackTrace trace_;
};
class AllocRecordObjectMap {
@@ -215,36 +204,29 @@
// weak roots). The last recent_record_max_ number of pairs in the list are always kept for DDMS's
// recent allocation tracking, but GcRoot<mirror::Object> pointers in these pairs can become null.
// Both types of pointers need read barriers, do not directly access them.
- typedef std::list<std::pair<GcRoot<mirror::Object>, AllocRecord*>> EntryList;
+ using EntryPair = std::pair<GcRoot<mirror::Object>, AllocRecord>;
+ typedef std::list<EntryPair> EntryList;
- // "static" because it is part of double-checked locking. It needs to check a bool first,
- // in order to make sure the AllocRecordObjectMap object is not null.
- static void RecordAllocation(Thread* self, mirror::Object* obj, mirror::Class* klass,
- size_t byte_count)
+ // Caller needs to check that it is enabled before calling since we read the stack trace before
+ // checking the enabled boolean.
+ void RecordAllocation(Thread* self,
+ mirror::Object** obj,
+ size_t byte_count)
REQUIRES(!Locks::alloc_tracker_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
static void SetAllocTrackingEnabled(bool enabled) REQUIRES(!Locks::alloc_tracker_lock_);
- AllocRecordObjectMap() REQUIRES(Locks::alloc_tracker_lock_)
- : alloc_record_max_(kDefaultNumAllocRecords),
- recent_record_max_(kDefaultNumRecentRecords),
- max_stack_depth_(kDefaultAllocStackDepth),
- scratch_trace_(kMaxSupportedStackDepth),
- alloc_ddm_thread_id_(0),
- allow_new_record_(true),
- new_record_condition_("New allocation record condition", *Locks::alloc_tracker_lock_) {}
-
+ AllocRecordObjectMap() REQUIRES(Locks::alloc_tracker_lock_);
~AllocRecordObjectMap();
- void Put(mirror::Object* obj, AllocRecord* record)
+ void Put(mirror::Object* obj, AllocRecord&& record)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(Locks::alloc_tracker_lock_) {
if (entries_.size() == alloc_record_max_) {
- delete entries_.front().second;
entries_.pop_front();
}
- entries_.emplace_back(GcRoot<mirror::Object>(obj), record);
+ entries_.push_back(EntryPair(GcRoot<mirror::Object>(obj), std::move(record)));
}
size_t Size() const SHARED_REQUIRES(Locks::alloc_tracker_lock_) {
@@ -313,12 +295,11 @@
static constexpr size_t kDefaultNumRecentRecords = 64 * 1024 - 1;
static constexpr size_t kDefaultAllocStackDepth = 16;
static constexpr size_t kMaxSupportedStackDepth = 128;
- size_t alloc_record_max_ GUARDED_BY(Locks::alloc_tracker_lock_);
- size_t recent_record_max_ GUARDED_BY(Locks::alloc_tracker_lock_);
- size_t max_stack_depth_ GUARDED_BY(Locks::alloc_tracker_lock_);
- AllocRecordStackTrace scratch_trace_ GUARDED_BY(Locks::alloc_tracker_lock_);
- pid_t alloc_ddm_thread_id_ GUARDED_BY(Locks::alloc_tracker_lock_);
- bool allow_new_record_ GUARDED_BY(Locks::alloc_tracker_lock_);
+ size_t alloc_record_max_ GUARDED_BY(Locks::alloc_tracker_lock_) = kDefaultNumAllocRecords;
+ size_t recent_record_max_ GUARDED_BY(Locks::alloc_tracker_lock_) = kDefaultNumRecentRecords;
+ size_t max_stack_depth_ = kDefaultAllocStackDepth;
+ pid_t alloc_ddm_thread_id_ GUARDED_BY(Locks::alloc_tracker_lock_) = 0;
+ bool allow_new_record_ GUARDED_BY(Locks::alloc_tracker_lock_) = true;
ConditionVariable new_record_condition_ GUARDED_BY(Locks::alloc_tracker_lock_);
// see the comment in typedef of EntryList
EntryList entries_ GUARDED_BY(Locks::alloc_tracker_lock_);
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 59fd4a6..6aed61a 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -176,8 +176,10 @@
}
if (kInstrumented) {
if (IsAllocTrackingEnabled()) {
- // Use obj->GetClass() instead of klass, because PushOnAllocationStack() could move klass
- AllocRecordObjectMap::RecordAllocation(self, obj, obj->GetClass(), bytes_allocated);
+ // allocation_records_ is not null since it never becomes null after allocation tracking is
+ // enabled.
+ DCHECK(allocation_records_ != nullptr);
+ allocation_records_->RecordAllocation(self, &obj, bytes_allocated);
}
} else {
DCHECK(!IsAllocTrackingEnabled());
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 2925591..fada1a2 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1326,8 +1326,7 @@
// Allocation tracking support
Atomic<bool> alloc_tracking_enabled_;
- std::unique_ptr<AllocRecordObjectMap> allocation_records_
- GUARDED_BY(Locks::alloc_tracker_lock_);
+ std::unique_ptr<AllocRecordObjectMap> allocation_records_;
// GC stress related data structures.
Mutex* backtrace_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index bb35ec7..3885c60 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -828,7 +828,7 @@
continue;
}
++count;
- const gc::AllocRecordStackTrace* trace = it->second->GetStackTrace();
+ const gc::AllocRecordStackTrace* trace = it->second.GetStackTrace();
// Copy the pair into a real hash map to speed up look up.
auto records_result = allocation_records_.emplace(obj, trace);
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index d70a7c4..f365fd0 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -290,7 +290,6 @@
JValue result;
self->AllowThreadSuspension();
HANDLE_MONITOR_CHECKS();
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
shadow_frame.GetMethod(), dex_pc,
@@ -305,7 +304,6 @@
JValue result;
self->AllowThreadSuspension();
HANDLE_MONITOR_CHECKS();
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
shadow_frame.GetMethod(), dex_pc,
@@ -321,7 +319,6 @@
result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
self->AllowThreadSuspension();
HANDLE_MONITOR_CHECKS();
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
shadow_frame.GetMethod(), dex_pc,
@@ -336,7 +333,6 @@
result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
self->AllowThreadSuspension();
HANDLE_MONITOR_CHECKS();
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
shadow_frame.GetMethod(), dex_pc,
@@ -372,7 +368,6 @@
}
}
result.SetL(obj_result);
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
shadow_frame.GetMethod(), dex_pc,
@@ -2588,7 +2583,6 @@
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, dex_pc,
instrumentation);
if (found_dex_pc == DexFile::kDexNoIndex) {
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index de9041b..cbfdcc3 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -693,7 +693,7 @@
return MterpSetUpHotnessCountdown(method, shadow_frame);
}
-// TUNING: Unused by arm/arm64. Remove when x86/x86_64/mips/mips64 mterps support batch updates.
+// TUNING: Unused by arm/arm64/x86/x86_64. Remove when mips/mips64 mterps support batch updates.
extern "C" bool MterpProfileBranch(Thread* self, ShadowFrame* shadow_frame, int32_t offset)
SHARED_REQUIRES(Locks::mutator_lock_) {
ArtMethod* method = shadow_frame->GetMethod();
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index ebac5fc..685b9b6 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -124,6 +124,21 @@
.cfi_restore \_reg
.endm
+/*
+ * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
+ * to access other shadow frame fields, we need to use a backwards offset. Define those here.
+ */
+#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
+#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
+#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
+#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
+#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
+#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
+#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
+#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_COUNTDOWN_OFFSET OFF_FP(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET)
+#define OFF_FP_SHADOWFRAME OFF_FP(0)
+
/* Frame size must be 16-byte aligned.
* Remember about 4 bytes for return address + 4 * 4 for spills
*/
@@ -155,43 +170,11 @@
#define rINSTbl %bl
#define rIBASE %edx
#define rREFS %ebp
+#define rPROFILE OFF_FP_COUNTDOWN_OFFSET(rFP)
-/*
- * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
- * to access other shadow frame fields, we need to use a backwards offset. Define those here.
- */
-#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
-#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
-#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
-#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
-#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
-#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
-#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
-#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
-
-#define MTERP_PROFILE_BRANCHES 1
#define MTERP_LOGGING 0
/*
- * Profile branch. rINST should contain the offset. %eax is scratch.
- */
-.macro MTERP_PROFILE_BRANCH
-#ifdef MTERP_PROFILE_BRANCHES
- EXPORT_PC
- movl rSELF, %eax
- movl %eax, OUT_ARG0(%esp)
- leal OFF_FP_SHADOWFRAME(rFP), %eax
- movl %eax, OUT_ARG1(%esp)
- movl rINST, OUT_ARG2(%esp)
- call SYMBOL(MterpProfileBranch)
- testb %al, %al
- jnz MterpOnStackReplacement
- RESTORE_IBASE
-#endif
-.endm
-
-/*
* "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must
* be done *before* something throws.
*
@@ -399,6 +382,13 @@
lea (rPC, %eax, 2), rPC
EXPORT_PC
+ /* Set up for backwards branches & osr profiling */
+ movl OFF_FP_METHOD(rFP), %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ call SYMBOL(MterpSetUpHotnessCountdown)
+
/* Starting ibase */
REFRESH_IBASE
@@ -1099,12 +1089,8 @@
*/
/* goto +AA */
movsbl rINSTbl, rINST # rINST <- ssssssAA
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # rINST <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
.balign 128
@@ -1118,12 +1104,8 @@
*/
/* goto/16 +AAAA */
movswl 2(rPC), rINST # rINST <- ssssAAAA
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # rINST <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
.balign 128
@@ -1142,12 +1124,8 @@
*/
/* goto/32 +AAAAAAAA */
movl 2(rPC), rINST # rINST <- AAAAAAAA
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # rINST <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
.balign 128
@@ -1169,14 +1147,10 @@
movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA
movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData
call SYMBOL(MterpDoPackedSwitch)
- movl %eax, rINST
- MTERP_PROFILE_BRANCH
- addl rINST, rINST
- leal (rPC, rINST), rPC
- FETCH_INST
REFRESH_IBASE
- jle MterpCheckSuspendAndContinue
- GOTO_NEXT
+ testl %eax, %eax
+ movl %eax, rINST
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
.balign 128
@@ -1199,14 +1173,10 @@
movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA
movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData
call SYMBOL(MterpDoSparseSwitch)
- movl %eax, rINST
- MTERP_PROFILE_BRANCH
- addl rINST, rINST
- leal (rPC, rINST), rPC
- FETCH_INST
REFRESH_IBASE
- jle MterpCheckSuspendAndContinue
- GOTO_NEXT
+ testl %eax, %eax
+ movl %eax, rINST
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
@@ -1423,16 +1393,14 @@
GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $2, rINST
jne 1f
movswl 2(rPC), rINST # Get signed branch offset
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1453,16 +1421,14 @@
GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $2, rINST
je 1f
movswl 2(rPC), rINST # Get signed branch offset
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1483,16 +1449,14 @@
GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $2, rINST
jge 1f
movswl 2(rPC), rINST # Get signed branch offset
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1513,16 +1477,14 @@
GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $2, rINST
jl 1f
movswl 2(rPC), rINST # Get signed branch offset
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1543,16 +1505,14 @@
GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $2, rINST
jle 1f
movswl 2(rPC), rINST # Get signed branch offset
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1573,16 +1533,14 @@
GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $2, rINST
jg 1f
movswl 2(rPC), rINST # Get signed branch offset
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1599,16 +1557,14 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $2, rINST
jne 1f
movswl 2(rPC), rINST # fetch signed displacement
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1625,16 +1581,14 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $2, rINST
je 1f
movswl 2(rPC), rINST # fetch signed displacement
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1651,16 +1605,14 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $2, rINST
jge 1f
movswl 2(rPC), rINST # fetch signed displacement
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1677,16 +1629,14 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $2, rINST
jl 1f
movswl 2(rPC), rINST # fetch signed displacement
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1703,16 +1653,14 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $2, rINST
jle 1f
movswl 2(rPC), rINST # fetch signed displacement
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1729,16 +1677,14 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $2, rINST
jg 1f
movswl 2(rPC), rINST # fetch signed displacement
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -12936,20 +12882,120 @@
/* NOTE: no fallthrough */
/*
- * Check for suspend check request. Assumes rINST already loaded, rPC advanced and
- * still needs to get the opcode and branch to it, and flags are in lr.
+ * Common handling for branches with support for Jit profiling.
+ * On entry:
+ * rINST <= signed offset
+ * condition bits <= set to establish sign of offset (use "NoFlags" entry if not)
+ *
+ * We have quite a few different cases for branch profiling, OSR detection and
+ * suspend check support here.
+ *
+ * Taken backward branches:
+ * If profiling active, do hotness countdown and report if we hit zero.
+ * If in osr check mode, see if our target is a compiled loop header entry and do OSR if so.
+ * Is there a pending suspend request? If so, suspend.
+ *
+ * Taken forward branches and not-taken backward branches:
+ * If in osr check mode, see if our target is a compiled loop header entry and do OSR if so.
+ *
+ * Our most common case is expected to be a taken backward branch with active jit profiling,
+ * but no full OSR check and no pending suspend request.
+ * Next most common case is not-taken branch with no full OSR check.
+ *
*/
-MterpCheckSuspendAndContinue:
+MterpCommonTakenBranch:
+ jg .L_forward_branch # don't add forward branches to hotness
+/*
+ * We need to subtract 1 from positive values and we should not see 0 here,
+ * so we may use the result of the comparison with -1.
+ */
+#if JIT_CHECK_OSR != -1
+# error "JIT_CHECK_OSR must be -1."
+#endif
+ cmpw $JIT_CHECK_OSR, rPROFILE
+ je .L_osr_check
+ decw rPROFILE
+ je .L_add_batch # counted down to zero - report
+.L_resume_backward_branch:
movl rSELF, %eax
- EXPORT_PC
testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
- jz 1f
- movl %eax, OUT_ARG0(%esp)
- call SYMBOL(MterpSuspendCheck)
+ leal (rPC, rINST, 2), rPC
+ FETCH_INST
+ jnz .L_suspend_request_pending
REFRESH_IBASE
-1:
GOTO_NEXT
+.L_suspend_request_pending:
+ EXPORT_PC
+ movl %eax, OUT_ARG0(%esp) # rSELF in eax
+ call SYMBOL(MterpSuspendCheck) # (self)
+ testb %al, %al
+ jnz MterpFallback
+ REFRESH_IBASE # might have changed during suspend
+ GOTO_NEXT
+
+.L_no_count_backwards:
+ cmpw $JIT_CHECK_OSR, rPROFILE # possible OSR re-entry?
+ jne .L_resume_backward_branch
+.L_osr_check:
+ EXPORT_PC
+ movl rSELF, %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ movl rINST, OUT_ARG2(%esp)
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jz .L_resume_backward_branch
+ jmp MterpOnStackReplacement
+
+.L_forward_branch:
+ cmpw $JIT_CHECK_OSR, rPROFILE # possible OSR re-entry?
+ je .L_check_osr_forward
+.L_resume_forward_branch:
+ leal (rPC, rINST, 2), rPC
+ FETCH_INST
+ GOTO_NEXT
+
+.L_check_osr_forward:
+ EXPORT_PC
+ movl rSELF, %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ movl rINST, OUT_ARG2(%esp)
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ REFRESH_IBASE
+ jz .L_resume_forward_branch
+ jmp MterpOnStackReplacement
+
+.L_add_batch:
+ movl OFF_FP_METHOD(rFP), %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ movl rSELF, %eax
+ movl %eax, OUT_ARG2(%esp)
+ call SYMBOL(MterpAddHotnessBatch) # (method, shadow_frame, self)
+ jmp .L_no_count_backwards
+
+/*
+ * Entered from the conditional branch handlers when OSR check request active on
+ * not-taken path. All Dalvik not-taken conditional branch offsets are 2.
+ */
+.L_check_not_taken_osr:
+ movl rSELF, %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ movl rINST, OUT_ARG3(%esp)
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ REFRESH_IBASE
+ jnz MterpOnStackReplacement
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
+
/*
* On-stack replacement has happened, and now we've returned from the compiled method.
*/
@@ -12994,7 +13040,29 @@
movl %ecx, 4(%edx)
mov $1, %eax
MterpDone:
+/*
+ * At this point, we expect rPROFILE to be non-zero. If negative, hotness is disabled or we're
+ * checking for OSR. If greater than zero, we might have unreported hotness to register
+ * (the difference between the ending rPROFILE and the cached hotness counter). rPROFILE
+ * should only reach zero immediately after a hotness decrement, and is then reset to either
+ * a negative special state or the new non-zero countdown value.
+ */
+ cmpw $0, rPROFILE
+ jle MRestoreFrame # if > 0, we may have some counts to report.
+
+ movl %eax, rINST # stash return value
+ /* Report cached hotness counts */
+ movl OFF_FP_METHOD(rFP), %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ movl rSELF, %eax
+ movl %eax, OUT_ARG2(%esp)
+ call SYMBOL(MterpAddHotnessBatch) # (method, shadow_frame, self)
+ movl rINST, %eax # restore return value
+
/* pop up frame */
+MRestoreFrame:
addl $FRAME_SIZE, %esp
.cfi_adjust_cfa_offset -FRAME_SIZE
diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S
index a1360e0..f78bcf0 100644
--- a/runtime/interpreter/mterp/out/mterp_x86_64.S
+++ b/runtime/interpreter/mterp/out/mterp_x86_64.S
@@ -120,6 +120,21 @@
.cfi_restore \_reg
.endm
+/*
+ * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
+ * to access other shadow frame fields, we need to use a backwards offset. Define those here.
+ */
+#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
+#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
+#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
+#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
+#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
+#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
+#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
+#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_COUNTDOWN_OFFSET OFF_FP(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET)
+#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
+
/* Frame size must be 16-byte aligned.
* Remember about 8 bytes for return address + 6 * 8 for spills.
*/
@@ -130,6 +145,8 @@
#define IN_ARG2 %rdx
#define IN_ARG1 %rsi
#define IN_ARG0 %rdi
+/* Spill offsets relative to %esp */
+#define SELF_SPILL (FRAME_SIZE - 8)
/* Out Args */
#define OUT_ARG3 %rcx
#define OUT_ARG2 %rdx
@@ -144,7 +161,7 @@
/* During bringup, we'll use the shadow frame model instead of rFP */
/* single-purpose registers, given names for clarity */
-#define rSELF %rbp
+#define rSELF SELF_SPILL(%rsp)
#define rPC %r12
#define rFP %r13
#define rINST %ebx
@@ -154,40 +171,11 @@
#define rINSTbl %bl
#define rIBASE %r14
#define rREFS %r15
+#define rPROFILE %ebp
-/*
- * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
- * to access other shadow frame fields, we need to use a backwards offset. Define those here.
- */
-#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
-#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
-#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
-#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
-#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
-#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
-#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
-#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
-
-#define MTERP_PROFILE_BRANCHES 1
#define MTERP_LOGGING 0
/*
- * Profile branch. rINST should contain the offset. %eax is scratch.
- */
-.macro MTERP_PROFILE_BRANCH
-#ifdef MTERP_PROFILE_BRANCHES
- EXPORT_PC
- movq rSELF, OUT_ARG0
- leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
- movl rINST, OUT_32_ARG2
- call SYMBOL(MterpProfileBranch)
- testb %al, %al
- jnz MterpOnStackReplacement
-#endif
-.endm
-
-/*
* "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must
* be done *before* something throws.
*
@@ -211,7 +199,8 @@
*
*/
.macro REFRESH_IBASE
- movq THREAD_CURRENT_IBASE_OFFSET(rSELF), rIBASE
+ movq rSELF, rIBASE
+ movq THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE
.endm
/*
@@ -377,6 +366,12 @@
movq IN_ARG0, rSELF
REFRESH_IBASE
+ /* Set up for backwards branches & osr profiling */
+ movq OFF_FP_METHOD(rFP), OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ call SYMBOL(MterpSetUpHotnessCountdown)
+ movswl %ax, rPROFILE
+
/* start executing the instruction at rPC */
FETCH_INST
GOTO_NEXT
@@ -579,9 +574,10 @@
.L_op_move_exception: /* 0x0d */
/* File: x86_64/op_move_exception.S */
/* move-exception vAA */
- movl THREAD_EXCEPTION_OFFSET(rSELF), %eax
+ movq rSELF, %rcx
+ movl THREAD_EXCEPTION_OFFSET(%rcx), %eax
SET_VREG_OBJECT %eax, rINSTq # fp[AA] <- exception object
- movl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movl $0, THREAD_EXCEPTION_OFFSET(%rcx)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
/* ------------------------------ */
@@ -590,9 +586,9 @@
/* File: x86_64/op_return_void.S */
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
xorq %rax, %rax
@@ -610,9 +606,9 @@
/* op vAA */
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
GET_VREG %eax, rINSTq # eax <- vAA
@@ -628,9 +624,9 @@
/* return-wide vAA */
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
GET_WIDE_VREG %rax, rINSTq # eax <- v[AA]
@@ -649,9 +645,9 @@
/* op vAA */
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
GET_VREG %eax, rINSTq # eax <- vAA
@@ -854,7 +850,8 @@
movq rSELF, OUT_ARG3
call SYMBOL(MterpInstanceOf) # (index, &obj, method, self)
movsbl %al, %eax
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
andb $0xf, rINSTbl # rINSTbl <- A
SET_VREG %eax, rINSTq
@@ -988,7 +985,8 @@
GET_VREG %eax, rINSTq # eax<- vAA (exception object)
testb %al, %al
jz common_errNullObject
- movq %rax, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ movq %rax, THREAD_EXCEPTION_OFFSET(%rcx)
jmp MterpException
/* ------------------------------ */
@@ -1003,12 +1001,8 @@
*/
/* goto +AA */
movsbq rINSTbl, rINSTq # rINSTq <- ssssssAA
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
.balign 128
@@ -1022,12 +1016,8 @@
*/
/* goto/16 +AAAA */
movswq 2(rPC), rINSTq # rINSTq <- ssssAAAA
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
.balign 128
@@ -1044,12 +1034,8 @@
*/
/* goto/32 +AAAAAAAA */
movslq 2(rPC), rINSTq # rINSTq <- AAAAAAAA
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
.balign 128
@@ -1069,13 +1055,9 @@
leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2
GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA
call SYMBOL(MterpDoPackedSwitch)
+ testl %eax, %eax
movslq %eax, rINSTq
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue
- GOTO_NEXT
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
.balign 128
@@ -1096,13 +1078,9 @@
leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2
GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA
call SYMBOL(MterpDoSparseSwitch)
+ testl %eax, %eax
movslq %eax, rINSTq
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue
- GOTO_NEXT
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
@@ -1309,16 +1287,14 @@
andb $0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $2, rINST # assume not taken
jne 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1339,16 +1315,14 @@
andb $0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $2, rINST # assume not taken
je 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1369,16 +1343,14 @@
andb $0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $2, rINST # assume not taken
jge 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1399,16 +1371,14 @@
andb $0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $2, rINST # assume not taken
jl 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1429,16 +1399,14 @@
andb $0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $2, rINST # assume not taken
jle 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1459,16 +1427,14 @@
andb $0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $2, rINST # assume not taken
jg 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1485,16 +1451,14 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $2, rINST # assume branch not taken
jne 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1511,16 +1475,14 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $2, rINST # assume branch not taken
je 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1537,16 +1499,14 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $2, rINST # assume branch not taken
jge 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1563,16 +1523,14 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $2, rINST # assume branch not taken
jl 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1589,16 +1547,14 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $2, rINST # assume branch not taken
jle 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1615,16 +1571,14 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $2, rINST # assume branch not taken
jg 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1767,7 +1721,8 @@
GET_VREG OUT_32_ARG1, %rcx # ecx <- vCC (requested index)
EXPORT_PC
call SYMBOL(artAGetObjectFromMterp) # (array, index)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
SET_VREG_OBJECT %eax, rINSTq
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2099,7 +2054,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGet32InstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
@@ -2131,7 +2087,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGet64InstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
@@ -2164,7 +2121,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGetObjInstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 1
@@ -2197,7 +2155,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGetBooleanInstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
@@ -2230,7 +2189,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGetByteInstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
@@ -2263,7 +2223,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGetCharInstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
@@ -2296,7 +2257,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGetShortInstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
@@ -2489,7 +2451,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGet32StaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 0
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -2519,7 +2482,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGet64StaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 0
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -2550,7 +2514,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGetObjStaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 1
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -2581,7 +2546,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGetBooleanStaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 0
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -2612,7 +2578,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGetByteStaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 0
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -2643,7 +2610,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGetCharStaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 0
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -2674,7 +2642,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGetShortStaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 0
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -3002,9 +2971,9 @@
.balign 128
.L_op_return_void_no_barrier: /* 0x73 */
/* File: x86_64/op_return_void_no_barrier.S */
- testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
xorq %rax, %rax
@@ -5712,7 +5681,8 @@
movzwl 2(rPC), OUT_32_ARG1 # eax <- field byte offset
EXPORT_PC
callq SYMBOL(artIGetObjectFromMterp) # (obj, offset)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -11849,7 +11819,7 @@
#if MTERP_LOGGING
movq rSELF, OUT_ARG0
leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
- movl THREAD_FLAGS_OFFSET(rSELF), OUT_32_ARG2
+ movl THREAD_FLAGS_OFFSET(OUT_ARG0), OUT_32_ARG2
call SYMBOL(MterpLogSuspendFallback)
#endif
jmp MterpCommonFallback
@@ -11860,7 +11830,8 @@
* interpreter.
*/
MterpPossibleException:
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jz MterpFallback
/* intentional fallthrough - handle pending exception. */
@@ -11891,19 +11862,113 @@
/* NOTE: no fallthrough */
/*
- * Check for suspend check request. Assumes rINST already loaded, rPC advanced and
- * still needs to get the opcode and branch to it, and flags are in lr.
+ * Common handling for branches with support for Jit profiling.
+ * On entry:
+ * rINST <= signed offset
+ * rPROFILE <= signed hotness countdown (expanded to 32 bits)
+ * condition bits <= set to establish sign of offset (use "NoFlags" entry if not)
+ *
+ * We have quite a few different cases for branch profiling, OSR detection and
+ * suspend check support here.
+ *
+ * Taken backward branches:
+ * If profiling active, do hotness countdown and report if we hit zero.
+ * If in osr check mode, see if our target is a compiled loop header entry and do OSR if so.
+ * Is there a pending suspend request? If so, suspend.
+ *
+ * Taken forward branches and not-taken backward branches:
+ * If in osr check mode, see if our target is a compiled loop header entry and do OSR if so.
+ *
+ * Our most common case is expected to be a taken backward branch with active jit profiling,
+ * but no full OSR check and no pending suspend request.
+ * Next most common case is not-taken branch with no full OSR check.
+ *
*/
-MterpCheckSuspendAndContinue:
+MterpCommonTakenBranch:
+ jg .L_forward_branch # don't add forward branches to hotness
+/*
+ * We need to subtract 1 from positive values and we should not see 0 here,
+ * so we may use the result of the comparison with -1.
+ */
+#if JIT_CHECK_OSR != -1
+# error "JIT_CHECK_OSR must be -1."
+#endif
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_osr_check
+ decl rPROFILE
+ je .L_add_batch # counted down to zero - report
+.L_resume_backward_branch:
+ movq rSELF, %rax
+ testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%rax)
REFRESH_IBASE
- testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
+ leaq (rPC, rINSTq, 2), rPC
+ FETCH_INST
+ jnz .L_suspend_request_pending
+ GOTO_NEXT
+
+.L_suspend_request_pending:
EXPORT_PC
movq rSELF, OUT_ARG0
- call SYMBOL(MterpSuspendCheck)
-1:
+ call SYMBOL(MterpSuspendCheck) # (self)
+ testb %al, %al
+ jnz MterpFallback
+ REFRESH_IBASE # might have changed during suspend
GOTO_NEXT
+.L_no_count_backwards:
+ cmpl $JIT_CHECK_OSR, rPROFILE # possible OSR re-entry?
+ jne .L_resume_backward_branch
+.L_osr_check:
+ EXPORT_PC
+ movq rSELF, OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movq rINSTq, OUT_ARG2
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jz .L_resume_backward_branch
+ jmp MterpOnStackReplacement
+
+.L_forward_branch:
+ cmpl $JIT_CHECK_OSR, rPROFILE # possible OSR re-entry?
+ je .L_check_osr_forward
+.L_resume_forward_branch:
+ leaq (rPC, rINSTq, 2), rPC
+ FETCH_INST
+ GOTO_NEXT
+
+.L_check_osr_forward:
+ EXPORT_PC
+ movq rSELF, OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movq rINSTq, OUT_ARG2
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jz .L_resume_forward_branch
+ jmp MterpOnStackReplacement
+
+.L_add_batch:
+ movl rPROFILE, %eax
+ movq OFF_FP_METHOD(rFP), OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movw %ax, OFF_FP_COUNTDOWN_OFFSET(rFP)
+ movq rSELF, OUT_ARG2
+ call SYMBOL(MterpAddHotnessBatch) # (method, shadow_frame, self)
+ movswl %ax, rPROFILE
+ jmp .L_no_count_backwards
+
+/*
+ * Entered from the conditional branch handlers when OSR check request active on
+ * not-taken path. All Dalvik not-taken conditional branch offsets are 2.
+ */
+.L_check_not_taken_osr:
+ movq rSELF, OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movq rINSTq, OUT_ARG3
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jnz MterpOnStackReplacement
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
+
/*
* On-stack replacement has happened, and now we've returned from the compiled method.
*/
@@ -11943,7 +12008,28 @@
movq %rax, (%rdx)
movl $1, %eax
MterpDone:
+/*
+ * At this point, we expect rPROFILE to be non-zero. If negative, hotness is disabled or we're
+ * checking for OSR. If greater than zero, we might have unreported hotness to register
+ * (the difference between the ending rPROFILE and the cached hotness counter). rPROFILE
+ * should only reach zero immediately after a hotness decrement, and is then reset to either
+ * a negative special state or the new non-zero countdown value.
+ */
+ testl rPROFILE, rPROFILE
+ jle MRestoreFrame # if > 0, we may have some counts to report.
+
+ movl %eax, rINST # stash return value
+ /* Report cached hotness counts */
+ movl rPROFILE, %eax
+ movq OFF_FP_METHOD(rFP), OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movw %ax, OFF_FP_COUNTDOWN_OFFSET(rFP)
+ movq rSELF, OUT_ARG2
+ call SYMBOL(MterpAddHotnessBatch) # (method, shadow_frame, self)
+ movl rINST, %eax # restore return value
+
/* pop up frame */
+MRestoreFrame:
addq $FRAME_SIZE, %rsp
.cfi_adjust_cfa_offset -FRAME_SIZE
diff --git a/runtime/interpreter/mterp/x86/bincmp.S b/runtime/interpreter/mterp/x86/bincmp.S
index c72a5cf..ee32278 100644
--- a/runtime/interpreter/mterp/x86/bincmp.S
+++ b/runtime/interpreter/mterp/x86/bincmp.S
@@ -11,13 +11,11 @@
GET_VREG %eax, %ecx # eax <- vA
sarl $$4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $$2, rINST
j${revcmp} 1f
movswl 2(rPC), rINST # Get signed branch offset
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $$JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S
index 785efdc..384dd9a 100644
--- a/runtime/interpreter/mterp/x86/entry.S
+++ b/runtime/interpreter/mterp/x86/entry.S
@@ -64,6 +64,13 @@
lea (rPC, %eax, 2), rPC
EXPORT_PC
+ /* Set up for backwards branches & osr profiling */
+ movl OFF_FP_METHOD(rFP), %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ call SYMBOL(MterpSetUpHotnessCountdown)
+
/* Starting ibase */
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S
index 3965ecd..df10ff0 100644
--- a/runtime/interpreter/mterp/x86/footer.S
+++ b/runtime/interpreter/mterp/x86/footer.S
@@ -131,20 +131,120 @@
/* NOTE: no fallthrough */
/*
- * Check for suspend check request. Assumes rINST already loaded, rPC advanced and
- * still needs to get the opcode and branch to it, and flags are in lr.
+ * Common handling for branches with support for Jit profiling.
+ * On entry:
+ * rINST <= signed offset
+ * condition bits <= set to establish sign of offset (use "NoFlags" entry if not)
+ *
+ * We have quite a few different cases for branch profiling, OSR detection and
+ * suspend check support here.
+ *
+ * Taken backward branches:
+ * If profiling active, do hotness countdown and report if we hit zero.
+ * If in osr check mode, see if our target is a compiled loop header entry and do OSR if so.
+ * Is there a pending suspend request? If so, suspend.
+ *
+ * Taken forward branches and not-taken backward branches:
+ * If in osr check mode, see if our target is a compiled loop header entry and do OSR if so.
+ *
+ * Our most common case is expected to be a taken backward branch with active jit profiling,
+ * but no full OSR check and no pending suspend request.
+ * Next most common case is not-taken branch with no full OSR check.
+ *
*/
-MterpCheckSuspendAndContinue:
+MterpCommonTakenBranch:
+ jg .L_forward_branch # don't add forward branches to hotness
+/*
+ * We need to subtract 1 from positive values and we should not see 0 here,
+ * so we may use the result of the comparison with -1.
+ */
+#if JIT_CHECK_OSR != -1
+# error "JIT_CHECK_OSR must be -1."
+#endif
+ cmpw $$JIT_CHECK_OSR, rPROFILE
+ je .L_osr_check
+ decw rPROFILE
+ je .L_add_batch # counted down to zero - report
+.L_resume_backward_branch:
movl rSELF, %eax
- EXPORT_PC
testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
- jz 1f
- movl %eax, OUT_ARG0(%esp)
- call SYMBOL(MterpSuspendCheck)
+ leal (rPC, rINST, 2), rPC
+ FETCH_INST
+ jnz .L_suspend_request_pending
REFRESH_IBASE
-1:
GOTO_NEXT
+.L_suspend_request_pending:
+ EXPORT_PC
+ movl %eax, OUT_ARG0(%esp) # rSELF in eax
+ call SYMBOL(MterpSuspendCheck) # (self)
+ testb %al, %al
+ jnz MterpFallback
+ REFRESH_IBASE # might have changed during suspend
+ GOTO_NEXT
+
+.L_no_count_backwards:
+ cmpw $$JIT_CHECK_OSR, rPROFILE # possible OSR re-entry?
+ jne .L_resume_backward_branch
+.L_osr_check:
+ EXPORT_PC
+ movl rSELF, %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ movl rINST, OUT_ARG2(%esp)
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jz .L_resume_backward_branch
+ jmp MterpOnStackReplacement
+
+.L_forward_branch:
+ cmpw $$JIT_CHECK_OSR, rPROFILE # possible OSR re-entry?
+ je .L_check_osr_forward
+.L_resume_forward_branch:
+ leal (rPC, rINST, 2), rPC
+ FETCH_INST
+ GOTO_NEXT
+
+.L_check_osr_forward:
+ EXPORT_PC
+ movl rSELF, %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ movl rINST, OUT_ARG2(%esp)
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ REFRESH_IBASE
+ jz .L_resume_forward_branch
+ jmp MterpOnStackReplacement
+
+.L_add_batch:
+ movl OFF_FP_METHOD(rFP), %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ movl rSELF, %eax
+ movl %eax, OUT_ARG2(%esp)
+ call SYMBOL(MterpAddHotnessBatch) # (method, shadow_frame, self)
+ jmp .L_no_count_backwards
+
+/*
+ * Entered from the conditional branch handlers when OSR check request active on
+ * not-taken path. All Dalvik not-taken conditional branch offsets are 2.
+ */
+.L_check_not_taken_osr:
+ movl rSELF, %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ movl rINST, OUT_ARG3(%esp)
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ REFRESH_IBASE
+ jnz MterpOnStackReplacement
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
+
/*
* On-stack replacement has happened, and now we've returned from the compiled method.
*/
@@ -189,7 +289,29 @@
movl %ecx, 4(%edx)
mov $$1, %eax
MterpDone:
+/*
+ * At this point, we expect rPROFILE to be non-zero. If negative, hotness is disabled or we're
+ * checking for OSR. If greater than zero, we might have unreported hotness to register
+ * (the difference between the ending rPROFILE and the cached hotness counter). rPROFILE
+ * should only reach zero immediately after a hotness decrement, and is then reset to either
+ * a negative special state or the new non-zero countdown value.
+ */
+ cmpw $$0, rPROFILE
+ jle MRestoreFrame # if > 0, we may have some counts to report.
+
+ movl %eax, rINST # stash return value
+ /* Report cached hotness counts */
+ movl OFF_FP_METHOD(rFP), %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ movl rSELF, %eax
+ movl %eax, OUT_ARG2(%esp)
+ call SYMBOL(MterpAddHotnessBatch) # (method, shadow_frame, self)
+ movl rINST, %eax # restore return value
+
/* pop up frame */
+MRestoreFrame:
addl $$FRAME_SIZE, %esp
.cfi_adjust_cfa_offset -FRAME_SIZE
diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S
index 5729b90..3a2dcb7 100644
--- a/runtime/interpreter/mterp/x86/header.S
+++ b/runtime/interpreter/mterp/x86/header.S
@@ -117,6 +117,21 @@
.cfi_restore \_reg
.endm
+/*
+ * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
+ * to access other shadow frame fields, we need to use a backwards offset. Define those here.
+ */
+#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
+#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
+#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
+#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
+#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
+#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
+#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
+#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_COUNTDOWN_OFFSET OFF_FP(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET)
+#define OFF_FP_SHADOWFRAME OFF_FP(0)
+
/* Frame size must be 16-byte aligned.
* Remember about 4 bytes for return address + 4 * 4 for spills
*/
@@ -148,43 +163,11 @@
#define rINSTbl %bl
#define rIBASE %edx
#define rREFS %ebp
+#define rPROFILE OFF_FP_COUNTDOWN_OFFSET(rFP)
-/*
- * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
- * to access other shadow frame fields, we need to use a backwards offset. Define those here.
- */
-#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
-#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
-#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
-#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
-#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
-#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
-#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
-#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
-
-#define MTERP_PROFILE_BRANCHES 1
#define MTERP_LOGGING 0
/*
- * Profile branch. rINST should contain the offset. %eax is scratch.
- */
-.macro MTERP_PROFILE_BRANCH
-#ifdef MTERP_PROFILE_BRANCHES
- EXPORT_PC
- movl rSELF, %eax
- movl %eax, OUT_ARG0(%esp)
- leal OFF_FP_SHADOWFRAME(rFP), %eax
- movl %eax, OUT_ARG1(%esp)
- movl rINST, OUT_ARG2(%esp)
- call SYMBOL(MterpProfileBranch)
- testb %al, %al
- jnz MterpOnStackReplacement
- RESTORE_IBASE
-#endif
-.endm
-
-/*
* "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must
* be done *before* something throws.
*
diff --git a/runtime/interpreter/mterp/x86/op_goto.S b/runtime/interpreter/mterp/x86/op_goto.S
index 9a87361..1827d68 100644
--- a/runtime/interpreter/mterp/x86/op_goto.S
+++ b/runtime/interpreter/mterp/x86/op_goto.S
@@ -6,9 +6,5 @@
*/
/* goto +AA */
movsbl rINSTbl, rINST # rINST <- ssssssAA
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # rINST <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
diff --git a/runtime/interpreter/mterp/x86/op_goto_16.S b/runtime/interpreter/mterp/x86/op_goto_16.S
index a25c31b..ea5ea90 100644
--- a/runtime/interpreter/mterp/x86/op_goto_16.S
+++ b/runtime/interpreter/mterp/x86/op_goto_16.S
@@ -6,9 +6,5 @@
*/
/* goto/16 +AAAA */
movswl 2(rPC), rINST # rINST <- ssssAAAA
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # rINST <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
diff --git a/runtime/interpreter/mterp/x86/op_goto_32.S b/runtime/interpreter/mterp/x86/op_goto_32.S
index 159128b..4becaf3 100644
--- a/runtime/interpreter/mterp/x86/op_goto_32.S
+++ b/runtime/interpreter/mterp/x86/op_goto_32.S
@@ -11,9 +11,5 @@
*/
/* goto/32 +AAAAAAAA */
movl 2(rPC), rINST # rINST <- AAAAAAAA
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # rINST <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
diff --git a/runtime/interpreter/mterp/x86/op_packed_switch.S b/runtime/interpreter/mterp/x86/op_packed_switch.S
index e33cf75..fcb7509 100644
--- a/runtime/interpreter/mterp/x86/op_packed_switch.S
+++ b/runtime/interpreter/mterp/x86/op_packed_switch.S
@@ -15,11 +15,7 @@
movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA
movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData
call SYMBOL($func)
- movl %eax, rINST
- MTERP_PROFILE_BRANCH
- addl rINST, rINST
- leal (rPC, rINST), rPC
- FETCH_INST
REFRESH_IBASE
- jle MterpCheckSuspendAndContinue
- GOTO_NEXT
+ testl %eax, %eax
+ movl %eax, rINST
+ jmp MterpCommonTakenBranch
diff --git a/runtime/interpreter/mterp/x86/zcmp.S b/runtime/interpreter/mterp/x86/zcmp.S
index 0f28d1a..c116159 100644
--- a/runtime/interpreter/mterp/x86/zcmp.S
+++ b/runtime/interpreter/mterp/x86/zcmp.S
@@ -7,13 +7,11 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $$0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $$2, rINST
j${revcmp} 1f
movswl 2(rPC), rINST # fetch signed displacement
+ testl rINST, rINST
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addl rINST, rINST # eax <- AA * 2
- leal (rPC, rINST), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpw $$JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86_64/bincmp.S b/runtime/interpreter/mterp/x86_64/bincmp.S
index a16050b..6601483 100644
--- a/runtime/interpreter/mterp/x86_64/bincmp.S
+++ b/runtime/interpreter/mterp/x86_64/bincmp.S
@@ -11,13 +11,11 @@
andb $$0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $$2, rINST # assume not taken
j${revcmp} 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $$JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S
index 69b2371..d992956 100644
--- a/runtime/interpreter/mterp/x86_64/entry.S
+++ b/runtime/interpreter/mterp/x86_64/entry.S
@@ -65,6 +65,12 @@
movq IN_ARG0, rSELF
REFRESH_IBASE
+ /* Set up for backwards branches & osr profiling */
+ movq OFF_FP_METHOD(rFP), OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ call SYMBOL(MterpSetUpHotnessCountdown)
+ movswl %ax, rPROFILE
+
/* start executing the instruction at rPC */
FETCH_INST
GOTO_NEXT
diff --git a/runtime/interpreter/mterp/x86_64/footer.S b/runtime/interpreter/mterp/x86_64/footer.S
index 573256b..71130d1 100644
--- a/runtime/interpreter/mterp/x86_64/footer.S
+++ b/runtime/interpreter/mterp/x86_64/footer.S
@@ -71,7 +71,7 @@
#if MTERP_LOGGING
movq rSELF, OUT_ARG0
leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
- movl THREAD_FLAGS_OFFSET(rSELF), OUT_32_ARG2
+ movl THREAD_FLAGS_OFFSET(OUT_ARG0), OUT_32_ARG2
call SYMBOL(MterpLogSuspendFallback)
#endif
jmp MterpCommonFallback
@@ -82,7 +82,8 @@
* interpreter.
*/
MterpPossibleException:
- cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $$0, THREAD_EXCEPTION_OFFSET(%rcx)
jz MterpFallback
/* intentional fallthrough - handle pending exception. */
@@ -113,19 +114,113 @@
/* NOTE: no fallthrough */
/*
- * Check for suspend check request. Assumes rINST already loaded, rPC advanced and
- * still needs to get the opcode and branch to it, and flags are in lr.
+ * Common handling for branches with support for Jit profiling.
+ * On entry:
+ * rINST <= signed offset
+ * rPROFILE <= signed hotness countdown (expanded to 32 bits)
+ * condition bits <= set to establish sign of offset (use "NoFlags" entry if not)
+ *
+ * We have quite a few different cases for branch profiling, OSR detection and
+ * suspend check support here.
+ *
+ * Taken backward branches:
+ * If profiling active, do hotness countdown and report if we hit zero.
+ * If in osr check mode, see if our target is a compiled loop header entry and do OSR if so.
+ * Is there a pending suspend request? If so, suspend.
+ *
+ * Taken forward branches and not-taken backward branches:
+ * If in osr check mode, see if our target is a compiled loop header entry and do OSR if so.
+ *
+ * Our most common case is expected to be a taken backward branch with active jit profiling,
+ * but no full OSR check and no pending suspend request.
+ * Next most common case is not-taken branch with no full OSR check.
+ *
*/
-MterpCheckSuspendAndContinue:
+MterpCommonTakenBranch:
+ jg .L_forward_branch # don't add forward branches to hotness
+/*
+ * We need to subtract 1 from positive values and we should not see 0 here,
+ * so we may use the result of the comparison with -1.
+ */
+#if JIT_CHECK_OSR != -1
+# error "JIT_CHECK_OSR must be -1."
+#endif
+ cmpl $$JIT_CHECK_OSR, rPROFILE
+ je .L_osr_check
+ decl rPROFILE
+ je .L_add_batch # counted down to zero - report
+.L_resume_backward_branch:
+ movq rSELF, %rax
+ testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%rax)
REFRESH_IBASE
- testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
+ leaq (rPC, rINSTq, 2), rPC
+ FETCH_INST
+ jnz .L_suspend_request_pending
+ GOTO_NEXT
+
+.L_suspend_request_pending:
EXPORT_PC
movq rSELF, OUT_ARG0
- call SYMBOL(MterpSuspendCheck)
-1:
+ call SYMBOL(MterpSuspendCheck) # (self)
+ testb %al, %al
+ jnz MterpFallback
+ REFRESH_IBASE # might have changed during suspend
GOTO_NEXT
+.L_no_count_backwards:
+ cmpl $$JIT_CHECK_OSR, rPROFILE # possible OSR re-entry?
+ jne .L_resume_backward_branch
+.L_osr_check:
+ EXPORT_PC
+ movq rSELF, OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movq rINSTq, OUT_ARG2
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jz .L_resume_backward_branch
+ jmp MterpOnStackReplacement
+
+.L_forward_branch:
+ cmpl $$JIT_CHECK_OSR, rPROFILE # possible OSR re-entry?
+ je .L_check_osr_forward
+.L_resume_forward_branch:
+ leaq (rPC, rINSTq, 2), rPC
+ FETCH_INST
+ GOTO_NEXT
+
+.L_check_osr_forward:
+ EXPORT_PC
+ movq rSELF, OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movq rINSTq, OUT_ARG2
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jz .L_resume_forward_branch
+ jmp MterpOnStackReplacement
+
+.L_add_batch:
+ movl rPROFILE, %eax
+ movq OFF_FP_METHOD(rFP), OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movw %ax, OFF_FP_COUNTDOWN_OFFSET(rFP)
+ movq rSELF, OUT_ARG2
+ call SYMBOL(MterpAddHotnessBatch) # (method, shadow_frame, self)
+ movswl %ax, rPROFILE
+ jmp .L_no_count_backwards
+
+/*
+ * Entered from the conditional branch handlers when OSR check request active on
+ * not-taken path. All Dalvik not-taken conditional branch offsets are 2.
+ */
+.L_check_not_taken_osr:
+ movq rSELF, OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movq rINSTq, OUT_ARG3
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jnz MterpOnStackReplacement
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
+
/*
* On-stack replacement has happened, and now we've returned from the compiled method.
*/
@@ -165,7 +260,28 @@
movq %rax, (%rdx)
movl $$1, %eax
MterpDone:
+/*
+ * At this point, we expect rPROFILE to be non-zero. If negative, hotness is disabled or we're
+ * checking for OSR. If greater than zero, we might have unreported hotness to register
+ * (the difference between the ending rPROFILE and the cached hotness counter). rPROFILE
+ * should only reach zero immediately after a hotness decrement, and is then reset to either
+ * a negative special state or the new non-zero countdown value.
+ */
+ testl rPROFILE, rPROFILE
+ jle MRestoreFrame # if > 0, we may have some counts to report.
+
+ movl %eax, rINST # stash return value
+ /* Report cached hotness counts */
+ movl rPROFILE, %eax
+ movq OFF_FP_METHOD(rFP), OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movw %ax, OFF_FP_COUNTDOWN_OFFSET(rFP)
+ movq rSELF, OUT_ARG2
+ call SYMBOL(MterpAddHotnessBatch) # (method, shadow_frame, self)
+ movl rINST, %eax # restore return value
+
/* pop up frame */
+MRestoreFrame:
addq $$FRAME_SIZE, %rsp
.cfi_adjust_cfa_offset -FRAME_SIZE
diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S
index eb84ea1..47d30ec 100644
--- a/runtime/interpreter/mterp/x86_64/header.S
+++ b/runtime/interpreter/mterp/x86_64/header.S
@@ -113,6 +113,21 @@
.cfi_restore \_reg
.endm
+/*
+ * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
+ * to access other shadow frame fields, we need to use a backwards offset. Define those here.
+ */
+#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
+#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
+#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
+#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
+#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
+#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
+#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
+#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_COUNTDOWN_OFFSET OFF_FP(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET)
+#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
+
/* Frame size must be 16-byte aligned.
* Remember about 8 bytes for return address + 6 * 8 for spills.
*/
@@ -123,6 +138,8 @@
#define IN_ARG2 %rdx
#define IN_ARG1 %rsi
#define IN_ARG0 %rdi
+/* Spill offsets relative to %esp */
+#define SELF_SPILL (FRAME_SIZE - 8)
/* Out Args */
#define OUT_ARG3 %rcx
#define OUT_ARG2 %rdx
@@ -137,7 +154,7 @@
/* During bringup, we'll use the shadow frame model instead of rFP */
/* single-purpose registers, given names for clarity */
-#define rSELF %rbp
+#define rSELF SELF_SPILL(%rsp)
#define rPC %r12
#define rFP %r13
#define rINST %ebx
@@ -147,40 +164,11 @@
#define rINSTbl %bl
#define rIBASE %r14
#define rREFS %r15
+#define rPROFILE %ebp
-/*
- * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
- * to access other shadow frame fields, we need to use a backwards offset. Define those here.
- */
-#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
-#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
-#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
-#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
-#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
-#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
-#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
-#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
-
-#define MTERP_PROFILE_BRANCHES 1
#define MTERP_LOGGING 0
/*
- * Profile branch. rINST should contain the offset. %eax is scratch.
- */
-.macro MTERP_PROFILE_BRANCH
-#ifdef MTERP_PROFILE_BRANCHES
- EXPORT_PC
- movq rSELF, OUT_ARG0
- leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
- movl rINST, OUT_32_ARG2
- call SYMBOL(MterpProfileBranch)
- testb %al, %al
- jnz MterpOnStackReplacement
-#endif
-.endm
-
-/*
* "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must
* be done *before* something throws.
*
@@ -204,7 +192,8 @@
*
*/
.macro REFRESH_IBASE
- movq THREAD_CURRENT_IBASE_OFFSET(rSELF), rIBASE
+ movq rSELF, rIBASE
+ movq THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE
.endm
/*
diff --git a/runtime/interpreter/mterp/x86_64/op_aget_object.S b/runtime/interpreter/mterp/x86_64/op_aget_object.S
index 8baedea..5f77a97 100644
--- a/runtime/interpreter/mterp/x86_64/op_aget_object.S
+++ b/runtime/interpreter/mterp/x86_64/op_aget_object.S
@@ -10,7 +10,8 @@
GET_VREG OUT_32_ARG1, %rcx # ecx <- vCC (requested index)
EXPORT_PC
call SYMBOL(artAGetObjectFromMterp) # (array, index)
- cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $$0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
SET_VREG_OBJECT %eax, rINSTq
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86_64/op_goto.S b/runtime/interpreter/mterp/x86_64/op_goto.S
index c4fc976..9749901 100644
--- a/runtime/interpreter/mterp/x86_64/op_goto.S
+++ b/runtime/interpreter/mterp/x86_64/op_goto.S
@@ -6,9 +6,5 @@
*/
/* goto +AA */
movsbq rINSTbl, rINSTq # rINSTq <- ssssssAA
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
diff --git a/runtime/interpreter/mterp/x86_64/op_goto_16.S b/runtime/interpreter/mterp/x86_64/op_goto_16.S
index 8cb9a5c..77688e0 100644
--- a/runtime/interpreter/mterp/x86_64/op_goto_16.S
+++ b/runtime/interpreter/mterp/x86_64/op_goto_16.S
@@ -6,9 +6,5 @@
*/
/* goto/16 +AAAA */
movswq 2(rPC), rINSTq # rINSTq <- ssssAAAA
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
diff --git a/runtime/interpreter/mterp/x86_64/op_goto_32.S b/runtime/interpreter/mterp/x86_64/op_goto_32.S
index 4ecdacd..29d777b 100644
--- a/runtime/interpreter/mterp/x86_64/op_goto_32.S
+++ b/runtime/interpreter/mterp/x86_64/op_goto_32.S
@@ -9,9 +9,5 @@
*/
/* goto/32 +AAAAAAAA */
movslq 2(rPC), rINSTq # rINSTq <- AAAAAAAA
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
diff --git a/runtime/interpreter/mterp/x86_64/op_iget.S b/runtime/interpreter/mterp/x86_64/op_iget.S
index a0d0faf..df43efe 100644
--- a/runtime/interpreter/mterp/x86_64/op_iget.S
+++ b/runtime/interpreter/mterp/x86_64/op_iget.S
@@ -12,7 +12,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL($helper)
- cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $$0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $$0xf, rINSTbl # rINST <- A
.if $is_object
diff --git a/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S
index 964d20a..176c954 100644
--- a/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S
+++ b/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S
@@ -7,7 +7,8 @@
movzwl 2(rPC), OUT_32_ARG1 # eax <- field byte offset
EXPORT_PC
callq SYMBOL(artIGetObjectFromMterp) # (obj, offset)
- cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $$0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $$0xf, rINSTbl # rINST <- A
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
diff --git a/runtime/interpreter/mterp/x86_64/op_instance_of.S b/runtime/interpreter/mterp/x86_64/op_instance_of.S
index 6be37f9..4819833 100644
--- a/runtime/interpreter/mterp/x86_64/op_instance_of.S
+++ b/runtime/interpreter/mterp/x86_64/op_instance_of.S
@@ -14,7 +14,8 @@
movq rSELF, OUT_ARG3
call SYMBOL(MterpInstanceOf) # (index, &obj, method, self)
movsbl %al, %eax
- cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $$0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
andb $$0xf, rINSTbl # rINSTbl <- A
SET_VREG %eax, rINSTq
diff --git a/runtime/interpreter/mterp/x86_64/op_move_exception.S b/runtime/interpreter/mterp/x86_64/op_move_exception.S
index d0a14fd..33db878 100644
--- a/runtime/interpreter/mterp/x86_64/op_move_exception.S
+++ b/runtime/interpreter/mterp/x86_64/op_move_exception.S
@@ -1,5 +1,6 @@
/* move-exception vAA */
- movl THREAD_EXCEPTION_OFFSET(rSELF), %eax
+ movq rSELF, %rcx
+ movl THREAD_EXCEPTION_OFFSET(%rcx), %eax
SET_VREG_OBJECT %eax, rINSTq # fp[AA] <- exception object
- movl $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movl $$0, THREAD_EXCEPTION_OFFSET(%rcx)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86_64/op_packed_switch.S b/runtime/interpreter/mterp/x86_64/op_packed_switch.S
index cb0acb7..fdf5a50 100644
--- a/runtime/interpreter/mterp/x86_64/op_packed_switch.S
+++ b/runtime/interpreter/mterp/x86_64/op_packed_switch.S
@@ -13,10 +13,6 @@
leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2
GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA
call SYMBOL($func)
+ testl %eax, %eax
movslq %eax, rINSTq
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue
- GOTO_NEXT
+ jmp MterpCommonTakenBranch
diff --git a/runtime/interpreter/mterp/x86_64/op_return.S b/runtime/interpreter/mterp/x86_64/op_return.S
index 14f4f8a..07e0e53 100644
--- a/runtime/interpreter/mterp/x86_64/op_return.S
+++ b/runtime/interpreter/mterp/x86_64/op_return.S
@@ -6,9 +6,9 @@
/* op vAA */
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
GET_VREG %eax, rINSTq # eax <- vAA
diff --git a/runtime/interpreter/mterp/x86_64/op_return_void.S b/runtime/interpreter/mterp/x86_64/op_return_void.S
index 46a5753..6a12df3 100644
--- a/runtime/interpreter/mterp/x86_64/op_return_void.S
+++ b/runtime/interpreter/mterp/x86_64/op_return_void.S
@@ -1,8 +1,8 @@
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
xorq %rax, %rax
diff --git a/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S
index 92e3506..822b2e8 100644
--- a/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S
+++ b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S
@@ -1,6 +1,6 @@
- testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
xorq %rax, %rax
diff --git a/runtime/interpreter/mterp/x86_64/op_return_wide.S b/runtime/interpreter/mterp/x86_64/op_return_wide.S
index f2d6e04..288eb96 100644
--- a/runtime/interpreter/mterp/x86_64/op_return_wide.S
+++ b/runtime/interpreter/mterp/x86_64/op_return_wide.S
@@ -4,9 +4,9 @@
/* return-wide vAA */
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
GET_WIDE_VREG %rax, rINSTq # eax <- v[AA]
diff --git a/runtime/interpreter/mterp/x86_64/op_sget.S b/runtime/interpreter/mterp/x86_64/op_sget.S
index 38d9a5e..d39e6c4 100644
--- a/runtime/interpreter/mterp/x86_64/op_sget.S
+++ b/runtime/interpreter/mterp/x86_64/op_sget.S
@@ -11,7 +11,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL($helper)
- cmpl $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $$0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if $is_object
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
diff --git a/runtime/interpreter/mterp/x86_64/op_throw.S b/runtime/interpreter/mterp/x86_64/op_throw.S
index 22ed990..8095c25 100644
--- a/runtime/interpreter/mterp/x86_64/op_throw.S
+++ b/runtime/interpreter/mterp/x86_64/op_throw.S
@@ -6,5 +6,6 @@
GET_VREG %eax, rINSTq # eax<- vAA (exception object)
testb %al, %al
jz common_errNullObject
- movq %rax, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ movq %rax, THREAD_EXCEPTION_OFFSET(%rcx)
jmp MterpException
diff --git a/runtime/interpreter/mterp/x86_64/zcmp.S b/runtime/interpreter/mterp/x86_64/zcmp.S
index 0051407..fb8ae6a 100644
--- a/runtime/interpreter/mterp/x86_64/zcmp.S
+++ b/runtime/interpreter/mterp/x86_64/zcmp.S
@@ -7,13 +7,11 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $$0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $$2, rINST # assume branch not taken
j${revcmp} 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $$JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index bb90d46..ce892f3 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -179,9 +179,10 @@
return HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded;
}
-bool OatFileAssistant::MakeUpToDate(CompilerFilter::Filter target, std::string* error_msg) {
+OatFileAssistant::ResultOfAttemptToUpdate
+OatFileAssistant::MakeUpToDate(CompilerFilter::Filter target, std::string* error_msg) {
switch (GetDexOptNeeded(target)) {
- case kNoDexOptNeeded: return true;
+ case kNoDexOptNeeded: return kUpdateSucceeded;
case kDex2OatNeeded: return GenerateOatFile(target, error_msg);
case kPatchOatNeeded: return RelocateOatFile(OdexFileName(), error_msg);
case kSelfPatchOatNeeded: return RelocateOatFile(OatFileName(), error_msg);
@@ -569,21 +570,21 @@
return true;
}
-bool OatFileAssistant::RelocateOatFile(const std::string* input_file,
- std::string* error_msg) {
+OatFileAssistant::ResultOfAttemptToUpdate
+OatFileAssistant::RelocateOatFile(const std::string* input_file, std::string* error_msg) {
CHECK(error_msg != nullptr);
if (input_file == nullptr) {
*error_msg = "Patching of oat file for dex location " + dex_location_
+ " not attempted because the input file name could not be determined.";
- return false;
+ return kUpdateNotAttempted;
}
const std::string& input_file_name = *input_file;
if (OatFileName() == nullptr) {
*error_msg = "Patching of oat file for dex location " + dex_location_
+ " not attempted because the oat file name could not be determined.";
- return false;
+ return kUpdateNotAttempted;
}
const std::string& oat_file_name = *OatFileName();
@@ -592,13 +593,13 @@
if (image_info == nullptr) {
*error_msg = "Patching of oat file " + oat_file_name
+ " not attempted because no image location was found.";
- return false;
+ return kUpdateNotAttempted;
}
if (!runtime->IsDex2OatEnabled()) {
*error_msg = "Patching of oat file " + oat_file_name
+ " not attempted because dex2oat is disabled";
- return false;
+ return kUpdateNotAttempted;
}
std::vector<std::string> argv;
@@ -613,28 +614,29 @@
// Manually delete the file. This ensures there is no garbage left over if
// the process unexpectedly died.
TEMP_FAILURE_RETRY(unlink(oat_file_name.c_str()));
- return false;
+ return kUpdateFailed;
}
// Mark that the oat file has changed and we should try to reload.
ClearOatFileCache();
- return true;
+ return kUpdateSucceeded;
}
-bool OatFileAssistant::GenerateOatFile(CompilerFilter::Filter target, std::string* error_msg) {
+OatFileAssistant::ResultOfAttemptToUpdate
+OatFileAssistant::GenerateOatFile(CompilerFilter::Filter target, std::string* error_msg) {
CHECK(error_msg != nullptr);
Runtime* runtime = Runtime::Current();
if (!runtime->IsDex2OatEnabled()) {
*error_msg = "Generation of oat file for dex location " + dex_location_
+ " not attempted because dex2oat is disabled.";
- return false;
+ return kUpdateNotAttempted;
}
if (OatFileName() == nullptr) {
*error_msg = "Generation of oat file for dex location " + dex_location_
+ " not attempted because the oat file name could not be determined.";
- return false;
+ return kUpdateNotAttempted;
}
const std::string& oat_file_name = *OatFileName();
@@ -643,7 +645,7 @@
// TODO: Why does dex2oat behave that way?
if (!OS::FileExists(dex_location_.c_str())) {
*error_msg = "Dex location " + dex_location_ + " does not exists.";
- return false;
+ return kUpdateNotAttempted;
}
std::unique_ptr<File> oat_file;
@@ -651,14 +653,14 @@
if (oat_file.get() == nullptr) {
*error_msg = "Generation of oat file " + oat_file_name
+ " not attempted because the oat file could not be created.";
- return false;
+ return kUpdateNotAttempted;
}
if (fchmod(oat_file->Fd(), 0644) != 0) {
*error_msg = "Generation of oat file " + oat_file_name
+ " not attempted because the oat file could not be made world readable.";
oat_file->Erase();
- return false;
+ return kUpdateNotAttempted;
}
std::vector<std::string> args;
@@ -672,18 +674,18 @@
// the process unexpectedly died.
oat_file->Erase();
TEMP_FAILURE_RETRY(unlink(oat_file_name.c_str()));
- return false;
+ return kUpdateFailed;
}
if (oat_file->FlushCloseOrErase() != 0) {
*error_msg = "Unable to close oat file " + oat_file_name;
TEMP_FAILURE_RETRY(unlink(oat_file_name.c_str()));
- return false;
+ return kUpdateFailed;
}
// Mark that the oat file has changed and we should try to reload.
ClearOatFileCache();
- return true;
+ return kUpdateSucceeded;
}
bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args,
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index db754b9..17f72fe 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -148,14 +148,26 @@
// given compiler filter.
DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter);
+ // Return code used when attempting to generate updated code.
+ enum ResultOfAttemptToUpdate {
+ kUpdateFailed, // We tried making the code up to date, but
+ // encountered an unexpected failure.
+ kUpdateNotAttempted, // We wanted to update the code, but determined we
+ // should not make the attempt.
+ kUpdateSucceeded // We successfully made the code up to date
+ // (possibly by doing nothing).
+ };
+
// Attempts to generate or relocate the oat file as needed to make it up to
// date with in a way that is at least as good as an oat file generated with
// the given compiler filter.
- // Returns true on success.
+ // Returns the result of attempting to update the code.
//
- // If there is a failure, the value of error_msg will be set to a string
- // describing why there was failure. error_msg must not be null.
- bool MakeUpToDate(CompilerFilter::Filter target_compiler_filter, std::string* error_msg);
+ // If the result is not kUpdateSucceeded, the value of error_msg will be set
+ // to a string describing why there was a failure or the update was not
+ // attempted. error_msg must not be null.
+ ResultOfAttemptToUpdate MakeUpToDate(CompilerFilter::Filter target_compiler_filter,
+ std::string* error_msg);
// Returns an oat file that can be used for loading dex files.
// Returns null if no suitable oat file was found.
@@ -232,22 +244,20 @@
// Generates the oat file by relocation from the named input file.
// This does not check the current status before attempting to relocate the
// oat file.
- // Returns true on success.
- // This will fail if dex2oat is not enabled in the current runtime.
//
- // If there is a failure, the value of error_msg will be set to a string
- // describing why there was failure. error_msg must not be null.
- bool RelocateOatFile(const std::string* input_file, std::string* error_msg);
+ // If the result is not kUpdateSucceeded, the value of error_msg will be set
+ // to a string describing why there was a failure or the update was not
+ // attempted. error_msg must not be null.
+ ResultOfAttemptToUpdate RelocateOatFile(const std::string* input_file, std::string* error_msg);
// Generate the oat file from the dex file using the given compiler filter.
// This does not check the current status before attempting to generate the
// oat file.
- // Returns true on success.
- // This will fail if dex2oat is not enabled in the current runtime.
//
- // If there is a failure, the value of error_msg will be set to a string
- // describing why there was failure. error_msg must not be null.
- bool GenerateOatFile(CompilerFilter::Filter filter, std::string* error_msg);
+ // If the result is not kUpdateSucceeded, the value of error_msg will be set
+ // to a string describing why there was a failure or the update was not
+ // attempted. error_msg must not be null.
+ ResultOfAttemptToUpdate GenerateOatFile(CompilerFilter::Filter filter, std::string* error_msg);
// Executes dex2oat using the current runtime configuration overridden with
// the given arguments. This does not check to see if dex2oat is enabled in
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index c247812..bddfa4f 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -452,7 +452,8 @@
// Trying to make the oat file up to date should not fail or crash.
std::string error_msg;
- EXPECT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg));
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
+ oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg));
// Trying to get the best oat file should fail, but not crash.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -703,7 +704,8 @@
// Make the oat file up to date.
std::string error_msg;
- ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
+ ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
+ oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
@@ -765,7 +767,8 @@
// Make the oat file up to date.
std::string error_msg;
- ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
+ ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
+ oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
@@ -821,7 +824,8 @@
// Make the oat file up to date. This should have no effect.
std::string error_msg;
- EXPECT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
+ oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
@@ -871,7 +875,8 @@
// Make the oat file up to date.
std::string error_msg;
- ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
+ ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
+ oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
@@ -914,7 +919,8 @@
// Make the oat file up to date.
std::string error_msg;
- ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
+ ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
+ oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
@@ -1093,7 +1099,8 @@
OatFileAssistant oat_file_assistant(
dex_location.c_str(), oat_location.c_str(), kRuntimeISA, false, true);
std::string error_msg;
- ASSERT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
+ ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
+ oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg)) << error_msg;
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
@@ -1123,7 +1130,8 @@
OatFileAssistant oat_file_assistant(
dex_location.c_str(), oat_location.c_str(), kRuntimeISA, false, true);
std::string error_msg;
- ASSERT_FALSE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg));
+ ASSERT_EQ(OatFileAssistant::kUpdateNotAttempted,
+ oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg));
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() == nullptr);
@@ -1138,7 +1146,8 @@
OatFileAssistant oat_file_assistant(
dex_location.c_str(), oat_location.c_str(), kRuntimeISA, false, true);
std::string error_msg;
- ASSERT_FALSE(oat_file_assistant.GenerateOatFile(CompilerFilter::kSpeed, &error_msg));
+ EXPECT_EQ(OatFileAssistant::kUpdateNotAttempted,
+ oat_file_assistant.GenerateOatFile(CompilerFilter::kSpeed, &error_msg));
}
// Turn an absolute path into a path relative to the current working
@@ -1217,7 +1226,8 @@
// Trying to make it up to date should have no effect.
std::string error_msg;
- EXPECT_TRUE(oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg));
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
+ oat_file_assistant.MakeUpToDate(CompilerFilter::kSpeed, &error_msg));
EXPECT_TRUE(error_msg.empty());
}
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 2f13f55..94f6345 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -329,8 +329,20 @@
// Update the oat file on disk if we can. This may fail, but that's okay.
// Best effort is all that matters here.
- if (!oat_file_assistant.MakeUpToDate(filter_, /*out*/ &error_msg)) {
- LOG(INFO) << error_msg;
+ switch (oat_file_assistant.MakeUpToDate(filter_, /*out*/ &error_msg)) {
+ case OatFileAssistant::kUpdateFailed:
+ LOG(WARNING) << error_msg;
+ break;
+
+ case OatFileAssistant::kUpdateNotAttempted:
+ // Avoid spamming the logs if we decided not to attempt making the oat
+ // file up to date.
+ VLOG(oat) << error_msg;
+ break;
+
+ case OatFileAssistant::kUpdateSucceeded:
+ // Nothing to do.
+ break;
}
// Get the oat file on disk.
diff --git a/runtime/stack.cc b/runtime/stack.cc
index ee5da8e..57ce07d 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -130,7 +130,11 @@
if (IsInInlinedFrame()) {
size_t depth_in_stack_map = current_inlining_depth_ - 1;
InlineInfo inline_info = GetCurrentInlineInfo();
- return GetResolvedMethod(*GetCurrentQuickFrame(), inline_info, depth_in_stack_map);
+ DCHECK(walk_kind_ != StackWalkKind::kSkipInlinedFrames);
+ bool allow_resolve = walk_kind_ != StackWalkKind::kIncludeInlinedFramesNoResolve;
+ return allow_resolve
+ ? GetResolvedMethod<true>(*GetCurrentQuickFrame(), inline_info, depth_in_stack_map)
+ : GetResolvedMethod<false>(*GetCurrentQuickFrame(), inline_info, depth_in_stack_map);
} else {
return *cur_quick_frame_;
}
@@ -859,7 +863,8 @@
cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_);
SanityCheckFrame();
- if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames)
+ if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames ||
+ walk_kind_ == StackWalkKind::kIncludeInlinedFramesNoResolve)
&& (cur_oat_quick_method_header_ != nullptr)
&& cur_oat_quick_method_header_->IsOptimized()) {
CodeInfo code_info = cur_oat_quick_method_header_->GetOptimizedCodeInfo();
diff --git a/runtime/stack.h b/runtime/stack.h
index ec653e7..a25874e 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -595,6 +595,7 @@
// when walking the stack.
enum class StackWalkKind {
kIncludeInlinedFrames,
+ kIncludeInlinedFramesNoResolve,
kSkipInlinedFrames,
};
diff --git a/test/593-checker-boolean-to-integral-conv/expected.txt b/test/593-checker-boolean-to-integral-conv/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/593-checker-boolean-to-integral-conv/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/593-checker-boolean-to-integral-conv/info.txt b/test/593-checker-boolean-to-integral-conv/info.txt
new file mode 100644
index 0000000..2d883c7
--- /dev/null
+++ b/test/593-checker-boolean-to-integral-conv/info.txt
@@ -0,0 +1 @@
+Regression test for Boolean to integral types conversions.
diff --git a/test/593-checker-boolean-to-integral-conv/src/Main.java b/test/593-checker-boolean-to-integral-conv/src/Main.java
new file mode 100644
index 0000000..ba65839
--- /dev/null
+++ b/test/593-checker-boolean-to-integral-conv/src/Main.java
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+public class Main {
+
+ public static void main(String args[]) {
+ expectEqualsByte((byte)1, booleanToByte(true));
+ expectEqualsShort((short)1, booleanToShort(true));
+ expectEqualsChar((char)1, booleanToChar(true));
+ expectEqualsInt(1, booleanToInt(true));
+ expectEqualsLong(1L, booleanToLong(true));
+
+ expectEqualsInt(1, longToIntOfBoolean());
+
+ System.out.println("passed");
+ }
+
+ /// CHECK-START: byte Main.booleanToByte(boolean) builder (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<IToS:b\d+>> TypeConversion [<<Phi>>]
+ /// CHECK-DAG: Return [<<IToS>>]
+
+ /// CHECK-START: byte Main.booleanToByte(boolean) select_generator (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+ /// CHECK-DAG: <<IToS:b\d+>> TypeConversion [<<Sel>>]
+ /// CHECK-DAG: Return [<<IToS>>]
+
+ /// CHECK-START: byte Main.booleanToByte(boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ static byte booleanToByte(boolean b) {
+ return (byte)(b ? 1 : 0);
+ }
+
+ /// CHECK-START: short Main.booleanToShort(boolean) builder (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<IToS:s\d+>> TypeConversion [<<Phi>>]
+ /// CHECK-DAG: Return [<<IToS>>]
+
+ /// CHECK-START: short Main.booleanToShort(boolean) select_generator (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+ /// CHECK-DAG: <<IToS:s\d+>> TypeConversion [<<Sel>>]
+ /// CHECK-DAG: Return [<<IToS>>]
+
+ /// CHECK-START: short Main.booleanToShort(boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ static short booleanToShort(boolean b) {
+ return (short)(b ? 1 : 0);
+ }
+
+ /// CHECK-START: char Main.booleanToChar(boolean) builder (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<IToC:c\d+>> TypeConversion [<<Phi>>]
+ /// CHECK-DAG: Return [<<IToC>>]
+
+ /// CHECK-START: char Main.booleanToChar(boolean) select_generator (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+ /// CHECK-DAG: <<IToC:c\d+>> TypeConversion [<<Sel>>]
+ /// CHECK-DAG: Return [<<IToC>>]
+
+ /// CHECK-START: char Main.booleanToChar(boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ static char booleanToChar(boolean b) {
+ return (char)(b ? 1 : 0);
+ }
+
+ /// CHECK-START: int Main.booleanToInt(boolean) builder (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: Return [<<Phi>>]
+
+ /// CHECK-START: int Main.booleanToInt(boolean) select_generator (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Sel>>]
+
+ /// CHECK-START: int Main.booleanToInt(boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ static int booleanToInt(boolean b) {
+ return b ? 1 : 0;
+ }
+
+ /// CHECK-START: long Main.booleanToLong(boolean) builder (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Phi>>]
+ /// CHECK-DAG: Return [<<IToJ>>]
+
+ /// CHECK-START: long Main.booleanToLong(boolean) select_generator (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+ /// CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Sel>>]
+ /// CHECK-DAG: Return [<<IToJ>>]
+
+ /// CHECK-START: long Main.booleanToLong(boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<ZToJ:j\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: Return [<<ZToJ>>]
+
+ static long booleanToLong(boolean b) {
+ return b ? 1 : 0;
+ }
+
+ /// CHECK-START: int Main.longToIntOfBoolean() builder (after)
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+ /// CHECK-DAG: <<ZToJ:j\d+>> InvokeStaticOrDirect [<<Sget>>,<<Method>>]
+ /// CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<ZToJ>>]
+ /// CHECK-DAG: Return [<<JToI>>]
+
+ /// CHECK-START: int Main.longToIntOfBoolean() inliner (after)
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+ /// CHECK-DAG: If [<<Sget>>]
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Phi>>]
+ /// CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<IToJ>>]
+ /// CHECK-DAG: Return [<<JToI>>]
+
+ /// CHECK-START: int Main.longToIntOfBoolean() select_generator (after)
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+ /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Sget>>]
+ /// CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Sel>>]
+ /// CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<IToJ>>]
+ /// CHECK-DAG: Return [<<JToI>>]
+
+ /// CHECK-START: int Main.longToIntOfBoolean() instruction_simplifier_after_bce (after)
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+ /// CHECK-DAG: Return [<<Sget>>]
+
+ static int longToIntOfBoolean() {
+ long l = booleanToLong(booleanField);
+ return (int) l;
+ }
+
+
+ private static void expectEqualsByte(byte expected, byte result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEqualsShort(short expected, short result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEqualsChar(char expected, char result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEqualsInt(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEqualsLong(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+
+ public static boolean booleanField = true;
+
+}
diff --git a/test/593-checker-long-to-float-regression/expected.txt b/test/593-checker-long-to-float-regression/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/593-checker-long-to-float-regression/expected.txt
diff --git a/test/593-checker-long-to-float-regression/info.txt b/test/593-checker-long-to-float-regression/info.txt
new file mode 100644
index 0000000..39402e9
--- /dev/null
+++ b/test/593-checker-long-to-float-regression/info.txt
@@ -0,0 +1,3 @@
+Regression test for x86_64's code generator, which had a bug in
+the long-to-float implementation loading a constant as 64-bit double
+instead of 32-bit float.
diff --git a/test/593-checker-long-to-float-regression/src/Main.java b/test/593-checker-long-to-float-regression/src/Main.java
new file mode 100644
index 0000000..9c07f3d
--- /dev/null
+++ b/test/593-checker-long-to-float-regression/src/Main.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+public class Main {
+
+ static boolean doThrow = false;
+ static long longValue;
+
+ public static void assertEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void main(String[] args) {
+ assertEquals(1.0F, $noinline$longToFloat());
+ }
+
+ /// CHECK-START: float Main.$noinline$longToFloat() register (after)
+ /// CHECK-DAG: <<Const1:j\d+>> LongConstant 1
+ /// CHECK-DAG: <<Convert:f\d+>> TypeConversion [<<Const1>>]
+ /// CHECK-DAG: Return [<<Convert>>]
+
+ static float $noinline$longToFloat() {
+ if (doThrow) { throw new Error(); }
+ longValue = $inline$returnConst();
+ return (float) longValue;
+ }
+
+ static long $inline$returnConst() {
+ return 1L;
+ }
+}