Add JIT
Currently disabled by default unless -Xjit is passed in.
The proposed JIT is a method JIT which works by utilizing interpreter
instrumentation to request compilation of hot methods async during
runtime.
JIT options:
-Xjit / -Xnojit
-Xjitcodecachesize:N
-Xjitthreshold:integervalue
The JIT has a shared copy of a compiler driver which is accessed
by worker threads to compile individual methods.
Added JIT code cache and data cache, currently sized at 2 MB
capacity by default. Most apps will only fill a small fraction of
this cache however.
Added support to the compiler for compiling interpreter quickened
byte codes.
Added test target ART_TEST_JIT=TRUE and --jit for run-test.
TODO:
Clean up code cache.
Delete compiled methods after they are added to code cache.
Add more optimizations related to runtime checks e.g. direct pointers
for invokes.
Add method recompilation.
Move instrumentation to DexFile to improve performance and reduce
memory usage.
Bug: 17950037
Change-Id: Ifa5b2684a2d5059ec5a5210733900aafa3c51bca
diff --git a/runtime/Android.mk b/runtime/Android.mk
index c647cc2..ab346e3 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -88,6 +88,9 @@
jdwp/jdwp_socket.cc \
jdwp/object_registry.cc \
jni_env_ext.cc \
+ jit/jit.cc \
+ jit/jit_code_cache.cc \
+ jit/jit_instrumentation.cc \
jni_internal.cc \
jobject_comparator.cc \
mem_map.cc \
@@ -298,6 +301,7 @@
base/unix_file/fd_file.h \
dex_file.h \
dex_instruction.h \
+ dex_instruction_utils.h \
gc_root.h \
gc/allocator/rosalloc.h \
gc/collector/gc_type.h \
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index cc1a4a1..3d007ba 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -42,6 +42,7 @@
bool gc;
bool heap;
bool jdwp;
+ bool jit;
bool jni;
bool monitor;
bool profiler;
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 745b209..45d2347 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -67,6 +67,7 @@
kReferenceQueueWeakReferencesLock,
kReferenceQueueClearedReferencesLock,
kReferenceProcessorLock,
+ kJitCodeCacheLock,
kRosAllocGlobalLock,
kRosAllocBracketLock,
kRosAllocBulkFreeLock,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index ee66b49..8fe2ec8 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -43,6 +43,8 @@
#include "handle_scope.h"
#include "intern_table.h"
#include "interpreter/interpreter.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
#include "leb128.h"
#include "oat.h"
#include "oat_file.h"
@@ -91,15 +93,14 @@
// a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we
// failed in verification, in which case v2 5.4.1 says we need to re-throw
// the previous error.
- Runtime* runtime = Runtime::Current();
- bool is_compiler = runtime->IsCompiler();
- if (!is_compiler) { // Give info if this occurs at runtime.
+ Runtime* const runtime = Runtime::Current();
+ if (!runtime->IsAotCompiler()) { // Give info if this occurs at runtime.
LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c);
}
CHECK(c->IsErroneous()) << PrettyClass(c) << " " << c->GetStatus();
Thread* self = Thread::Current();
- if (is_compiler) {
+ if (runtime->IsAotCompiler()) {
// At compile time, accurate errors and NCDFE are disabled to speed compilation.
mirror::Throwable* pre_allocated = runtime->GetPreAllocatedNoClassDefFoundError();
self->SetException(ThrowLocation(), pre_allocated);
@@ -428,7 +429,7 @@
// Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that
// we do not need friend classes or a publicly exposed setter.
quick_generic_jni_trampoline_ = GetQuickGenericJniStub();
- if (!runtime->IsCompiler()) {
+ if (!runtime->IsAotCompiler()) {
// We need to set up the generic trampolines since we don't have an image.
quick_resolution_trampoline_ = GetQuickResolutionStub();
quick_imt_conflict_trampoline_ = GetQuickImtConflictStub();
@@ -1032,8 +1033,7 @@
const char* oat_location,
std::string* error_msg) {
std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
- !Runtime::Current()->IsCompiler(),
- error_msg));
+ !Runtime::Current()->IsAotCompiler(), error_msg));
if (oat_file.get() == nullptr) {
*error_msg = StringPrintf("Failed to find existing oat file at %s: %s", oat_location,
error_msg->c_str());
@@ -1104,8 +1104,8 @@
return nullptr;
}
std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
- !Runtime::Current()->IsCompiler(),
- &error_msg));
+ !Runtime::Current()->IsAotCompiler(),
+ &error_msg));
if (oat_file.get() == nullptr) {
std::string compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s",
oat_location, error_msg.c_str());
@@ -1345,7 +1345,7 @@
*already_opened = false;
const Runtime* runtime = Runtime::Current();
CHECK(runtime != nullptr);
- bool executable = !runtime->IsCompiler();
+ bool executable = !runtime->IsAotCompiler();
std::string odex_error_msg;
bool should_patch_system = false;
@@ -1513,7 +1513,7 @@
bool success = Exec(argv, error_msg);
if (success) {
std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr, nullptr,
- !runtime->IsCompiler(), error_msg));
+ !runtime->IsAotCompiler(), error_msg));
bool checksum_verified = false;
if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified,
error_msg)) {
@@ -1527,7 +1527,7 @@
"but was unable to open output file '%s': %s",
input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
}
- } else if (!runtime->IsCompiler()) {
+ } else if (!runtime->IsAotCompiler()) {
// patchoat failed which means we probably don't have enough room to place the output oat file,
// instead of failing we should just run the interpreter from the dex files in the input oat.
LOG(WARNING) << "Patching of oat file '" << input_oat << "' failed. Attempting to use oat file "
@@ -1614,22 +1614,20 @@
if (oat_file != nullptr) {
return oat_file;
}
-
- return OatFile::Open(oat_location, oat_location, nullptr, nullptr, !Runtime::Current()->IsCompiler(),
- error_msg);
+ return OatFile::Open(oat_location, oat_location, nullptr, nullptr,
+ !Runtime::Current()->IsAotCompiler(), error_msg);
}
void ClassLinker::InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg) {
ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
DCHECK(obj != nullptr);
DCHECK(class_linker != nullptr);
- size_t pointer_size = class_linker->image_pointer_size_;
-
if (obj->IsArtMethod()) {
mirror::ArtMethod* method = obj->AsArtMethod();
if (!method->IsNative()) {
+ const size_t pointer_size = class_linker->image_pointer_size_;
method->SetEntryPointFromInterpreterPtrSize(artInterpreterToInterpreterBridge, pointer_size);
- if (method != Runtime::Current()->GetResolutionMethod()) {
+ if (!method->IsRuntimeMethod() && method != Runtime::Current()->GetResolutionMethod()) {
method->SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(),
pointer_size);
}
@@ -1698,8 +1696,8 @@
// bitmap walk.
mirror::ArtMethod::SetClass(GetClassRoot(kJavaLangReflectArtMethod));
size_t art_method_object_size = mirror::ArtMethod::GetJavaLangReflectArtMethod()->GetObjectSize();
- if (!Runtime::Current()->IsCompiler()) {
- // Compiler supports having an image with a different pointer size than the runtime. This
+ if (!Runtime::Current()->IsAotCompiler()) {
+ // Aot compiler supports having an image with a different pointer size than the runtime. This
// happens on the host for compile 32 bit tests since we use a 64 bit libart compiler. We may
// also use 32 bit dex2oat on a system with 64 bit apps.
CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(sizeof(void*)))
@@ -1714,7 +1712,7 @@
// Set entry point to interpreter if in InterpretOnly mode.
Runtime* runtime = Runtime::Current();
- if (!runtime->IsCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
+ if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
heap->VisitObjects(InitFromImageInterpretOnlyCallback, this);
}
@@ -2517,31 +2515,44 @@
return GetQuickProxyInvokeHandler();
}
bool found;
- OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
- const void* result = nullptr;
- if (found) {
- result = oat_method.GetQuickCode();
- }
-
- if (result == nullptr) {
- if (method->IsNative()) {
- // No code and native? Use generic trampoline.
- result = GetQuickGenericJniStub();
- } else {
- // No code? You must mean to go into the interpreter.
- result = GetQuickToInterpreterBridge();
+ jit::Jit* const jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ auto* code = jit->GetCodeCache()->GetCodeFor(method);
+ if (code != nullptr) {
+ return code;
}
}
- return result;
+ OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
+ if (found) {
+ auto* code = oat_method.GetQuickCode();
+ if (code != nullptr) {
+ return code;
+ }
+ }
+ if (method->IsNative()) {
+ // No code and native? Use generic trampoline.
+ return GetQuickGenericJniStub();
+ }
+ return GetQuickToInterpreterBridge();
}
const void* ClassLinker::GetOatMethodQuickCodeFor(mirror::ArtMethod* method) {
if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) {
return nullptr;
}
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ auto* code = jit->GetCodeCache()->GetCodeFor(method);
+ if (code != nullptr) {
+ return code;
+ }
+ }
bool found;
OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
- return found ? oat_method.GetQuickCode() : nullptr;
+ if (found) {
+ return oat_method.GetQuickCode();
+ }
+ return nullptr;
}
const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
@@ -2577,7 +2588,7 @@
}
Runtime* runtime = Runtime::Current();
if (!runtime->IsStarted() || runtime->UseCompileTimeClassPath()) {
- if (runtime->IsCompiler() || runtime->GetHeap()->HasImageSpace()) {
+ if (runtime->IsAotCompiler() || runtime->GetHeap()->HasImageSpace()) {
return; // OAT file unavailable.
}
}
@@ -2630,7 +2641,7 @@
const OatFile::OatClass* oat_class,
uint32_t class_def_method_index) {
Runtime* runtime = Runtime::Current();
- if (runtime->IsCompiler()) {
+ if (runtime->IsAotCompiler()) {
// The following code only applies to a non-compiler runtime.
return;
}
@@ -3469,7 +3480,7 @@
EnsurePreverifiedMethods(klass);
return;
}
- if (klass->IsCompileTimeVerified() && Runtime::Current()->IsCompiler()) {
+ if (klass->IsCompileTimeVerified() && Runtime::Current()->IsAotCompiler()) {
return;
}
@@ -3485,7 +3496,7 @@
} else {
CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime)
<< PrettyClass(klass.Get());
- CHECK(!Runtime::Current()->IsCompiler());
+ CHECK(!Runtime::Current()->IsAotCompiler());
klass->SetStatus(mirror::Class::kStatusVerifyingAtRuntime, self);
}
@@ -3521,7 +3532,7 @@
self->GetException(nullptr)->SetCause(cause.Get());
}
ClassReference ref(klass->GetDexCache()->GetDexFile(), klass->GetDexClassDefIndex());
- if (Runtime::Current()->IsCompiler()) {
+ if (Runtime::Current()->IsAotCompiler()) {
Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
}
klass->SetStatus(mirror::Class::kStatusError, self);
@@ -3546,7 +3557,7 @@
std::string error_msg;
if (!preverified) {
verifier_failure = verifier::MethodVerifier::VerifyClass(self, klass.Get(),
- Runtime::Current()->IsCompiler(),
+ Runtime::Current()->IsAotCompiler(),
&error_msg);
}
if (preverified || verifier_failure != verifier::MethodVerifier::kHardFailure) {
@@ -3574,7 +3585,7 @@
// Soft failures at compile time should be retried at runtime. Soft
// failures at runtime will be handled by slow paths in the generated
// code. Set status accordingly.
- if (Runtime::Current()->IsCompiler()) {
+ if (Runtime::Current()->IsAotCompiler()) {
klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime, self);
} else {
klass->SetStatus(mirror::Class::kStatusVerified, self);
@@ -3615,7 +3626,7 @@
// we are not compiling the image or if the class we're verifying is not part of
// the app. In other words, we will only check for preverification of bootclasspath
// classes.
- if (Runtime::Current()->IsCompiler()) {
+ if (Runtime::Current()->IsAotCompiler()) {
// Are we compiling the bootclasspath?
if (!Runtime::Current()->UseCompileTimeClassPath()) {
return false;
@@ -3641,7 +3652,7 @@
// image (that we just failed loading), and the verifier can't be run on quickened opcodes when
// the runtime isn't started. On the other hand, app classes can be re-verified even if they are
// already pre-opted, as then the runtime is started.
- if (!Runtime::Current()->IsCompiler() &&
+ if (!Runtime::Current()->IsAotCompiler() &&
!Runtime::Current()->GetHeap()->HasImageSpace() &&
klass->GetClassLoader() != nullptr) {
return false;
@@ -4089,7 +4100,7 @@
CHECK(self->IsExceptionPending());
VlogClassInitializationFailure(klass);
} else {
- CHECK(Runtime::Current()->IsCompiler());
+ CHECK(Runtime::Current()->IsAotCompiler());
CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
}
return false;
@@ -4270,7 +4281,8 @@
if (klass->GetStatus() == mirror::Class::kStatusInitializing) {
continue;
}
- if (klass->GetStatus() == mirror::Class::kStatusVerified && Runtime::Current()->IsCompiler()) {
+ if (klass->GetStatus() == mirror::Class::kStatusVerified &&
+ Runtime::Current()->IsAotCompiler()) {
// Compile time initialization failed.
return false;
}
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index a3d3b47..f33c0f8 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -287,6 +287,13 @@
Dbg::PostException(throw_location, catch_method, catch_dex_pc, exception_object);
}
+ // We only care about how many backward branches were executed in the Jit.
+ void BackwardBranch(Thread* /*thread*/, mirror::ArtMethod* method, int32_t dex_pc_offset)
+ OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ LOG(ERROR) << "Unexpected backward branch event in debugger " << PrettyMethod(method)
+ << " " << dex_pc_offset;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(DebugInstrumentationListener);
} gDebugInstrumentationListener;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index e121a08..c8ede48 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -1028,6 +1028,13 @@
// from an oat file, e.g., directly from an apk.
const OatFile* oat_file_;
};
+
+struct DexFileReference {
+ DexFileReference(const DexFile* file, uint32_t idx) : dex_file(file), index(idx) { }
+ const DexFile* dex_file;
+ uint32_t index;
+};
+
std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
// Iterate over a dex file's ProtoId's paramters
diff --git a/runtime/dex_instruction_utils.h b/runtime/dex_instruction_utils.h
new file mode 100644
index 0000000..1a671c5
--- /dev/null
+++ b/runtime/dex_instruction_utils.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_DEX_INSTRUCTION_UTILS_H_
+#define ART_RUNTIME_DEX_INSTRUCTION_UTILS_H_
+
+#include "dex_instruction.h"
+
+namespace art {
+
+// Dex invoke type corresponds to the ordering of INVOKE instructions;
+// this order is the same for range and non-range invokes.
+enum DexInvokeType : uint8_t {
+ kDexInvokeVirtual = 0, // invoke-virtual, invoke-virtual-range
+ kDexInvokeSuper, // invoke-super, invoke-super-range
+ kDexInvokeDirect, // invoke-direct, invoke-direct-range
+ kDexInvokeStatic, // invoke-static, invoke-static-range
+ kDexInvokeInterface, // invoke-interface, invoke-interface-range
+ kDexInvokeTypeCount
+};
+
+// Dex instruction memory access types correspond to the ordering of GET/PUT instructions;
+// this order is the same for IGET, IPUT, SGET, SPUT, AGET and APUT.
+enum DexMemAccessType : uint8_t {
+ kDexMemAccessWord = 0, // op 0; int or float, the actual type is not encoded.
+ kDexMemAccessWide, // op_WIDE 1; long or double, the actual type is not encoded.
+ kDexMemAccessObject, // op_OBJECT 2; the actual reference type is not encoded.
+ kDexMemAccessBoolean, // op_BOOLEAN 3
+ kDexMemAccessByte, // op_BYTE 4
+ kDexMemAccessChar, // op_CHAR 5
+ kDexMemAccessShort, // op_SHORT 6
+ kDexMemAccessTypeCount
+};
+
+std::ostream& operator<<(std::ostream& os, const DexMemAccessType& type);
+
+// NOTE: The following functions disregard quickened instructions.
+
+constexpr bool IsInstructionReturn(Instruction::Code opcode) {
+ return Instruction::RETURN_VOID <= opcode && opcode <= Instruction::RETURN_OBJECT;
+}
+
+constexpr bool IsInstructionInvoke(Instruction::Code opcode) {
+ return Instruction::INVOKE_VIRTUAL <= opcode && opcode <= Instruction::INVOKE_INTERFACE_RANGE &&
+ opcode != Instruction::RETURN_VOID_BARRIER;
+}
+
+constexpr bool IsInstructionQuickInvoke(Instruction::Code opcode) {
+ return opcode == Instruction::INVOKE_VIRTUAL_QUICK ||
+ opcode == Instruction::INVOKE_VIRTUAL_RANGE_QUICK;
+}
+
+constexpr bool IsInstructionInvokeStatic(Instruction::Code opcode) {
+ return opcode == Instruction::INVOKE_STATIC || opcode == Instruction::INVOKE_STATIC_RANGE;
+}
+
+constexpr bool IsInstructionGoto(Instruction::Code opcode) {
+ return Instruction::GOTO <= opcode && opcode <= Instruction::GOTO_32;
+}
+
+constexpr bool IsInstructionIfCc(Instruction::Code opcode) {
+ return Instruction::IF_EQ <= opcode && opcode <= Instruction::IF_LE;
+}
+
+constexpr bool IsInstructionIfCcZ(Instruction::Code opcode) {
+ return Instruction::IF_EQZ <= opcode && opcode <= Instruction::IF_LEZ;
+}
+
+constexpr bool IsInstructionIGet(Instruction::Code code) {
+ return Instruction::IGET <= code && code <= Instruction::IGET_SHORT;
+}
+
+constexpr bool IsInstructionIPut(Instruction::Code code) {
+ return Instruction::IPUT <= code && code <= Instruction::IPUT_SHORT;
+}
+
+constexpr bool IsInstructionSGet(Instruction::Code code) {
+ return Instruction::SGET <= code && code <= Instruction::SGET_SHORT;
+}
+
+constexpr bool IsInstructionSPut(Instruction::Code code) {
+ return Instruction::SPUT <= code && code <= Instruction::SPUT_SHORT;
+}
+
+constexpr bool IsInstructionAGet(Instruction::Code code) {
+ return Instruction::AGET <= code && code <= Instruction::AGET_SHORT;
+}
+
+constexpr bool IsInstructionAPut(Instruction::Code code) {
+ return Instruction::APUT <= code && code <= Instruction::APUT_SHORT;
+}
+
+constexpr bool IsInstructionIGetOrIPut(Instruction::Code code) {
+ return Instruction::IGET <= code && code <= Instruction::IPUT_SHORT;
+}
+
+constexpr bool IsInstructionIGetQuickOrIPutQuick(Instruction::Code code) {
+ return (code >= Instruction::IGET_QUICK && code <= Instruction::IPUT_OBJECT_QUICK) ||
+ (code >= Instruction::IPUT_BOOLEAN_QUICK && code <= Instruction::IGET_SHORT_QUICK);
+}
+
+constexpr bool IsInstructionSGetOrSPut(Instruction::Code code) {
+ return Instruction::SGET <= code && code <= Instruction::SPUT_SHORT;
+}
+
+constexpr bool IsInstructionAGetOrAPut(Instruction::Code code) {
+ return Instruction::AGET <= code && code <= Instruction::APUT_SHORT;
+}
+
+constexpr bool IsInstructionBinOp2Addr(Instruction::Code code) {
+ return Instruction::ADD_INT_2ADDR <= code && code <= Instruction::REM_DOUBLE_2ADDR;
+}
+
+// TODO: Remove the #if guards below when we fully migrate to C++14.
+
+constexpr bool IsInvokeInstructionRange(Instruction::Code opcode) {
+#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions.
+ DCHECK(IsInstructionInvoke(opcode));
+#endif
+ return opcode >= Instruction::INVOKE_VIRTUAL_RANGE;
+}
+
+constexpr DexInvokeType InvokeInstructionType(Instruction::Code opcode) {
+#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions.
+ DCHECK(IsInstructionInvoke(opcode));
+#endif
+ return static_cast<DexInvokeType>(IsInvokeInstructionRange(opcode)
+ ? (opcode - Instruction::INVOKE_VIRTUAL_RANGE)
+ : (opcode - Instruction::INVOKE_VIRTUAL));
+}
+
+constexpr DexMemAccessType IGetMemAccessType(Instruction::Code code) {
+#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions.
+ DCHECK(IsInstructionIGet(opcode));
+#endif
+ return static_cast<DexMemAccessType>(code - Instruction::IGET);
+}
+
+constexpr DexMemAccessType IPutMemAccessType(Instruction::Code code) {
+#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions.
+ DCHECK(IsInstructionIPut(opcode));
+#endif
+ return static_cast<DexMemAccessType>(code - Instruction::IPUT);
+}
+
+constexpr DexMemAccessType SGetMemAccessType(Instruction::Code code) {
+#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions.
+ DCHECK(IsInstructionSGet(opcode));
+#endif
+ return static_cast<DexMemAccessType>(code - Instruction::SGET);
+}
+
+constexpr DexMemAccessType SPutMemAccessType(Instruction::Code code) {
+#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions.
+ DCHECK(IsInstructionSPut(opcode));
+#endif
+ return static_cast<DexMemAccessType>(code - Instruction::SPUT);
+}
+
+constexpr DexMemAccessType AGetMemAccessType(Instruction::Code code) {
+#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions.
+ DCHECK(IsInstructionAGet(opcode));
+#endif
+ return static_cast<DexMemAccessType>(code - Instruction::AGET);
+}
+
+constexpr DexMemAccessType APutMemAccessType(Instruction::Code code) {
+#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions.
+ DCHECK(IsInstructionAPut(opcode));
+#endif
+ return static_cast<DexMemAccessType>(code - Instruction::APUT);
+}
+
+constexpr DexMemAccessType IGetOrIPutMemAccessType(Instruction::Code code) {
+#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions.
+ DCHECK(IsInstructionIGetOrIPut(opcode));
+#endif
+ return (code >= Instruction::IPUT) ? IPutMemAccessType(code) : IGetMemAccessType(code);
+}
+
+static inline DexMemAccessType IGetQuickOrIPutQuickMemAccessType(Instruction::Code code) {
+ DCHECK(IsInstructionIGetQuickOrIPutQuick(code));
+ switch (code) {
+ case Instruction::IGET_QUICK: case Instruction::IPUT_QUICK:
+ return kDexMemAccessWord;
+ case Instruction::IGET_WIDE_QUICK: case Instruction::IPUT_WIDE_QUICK:
+ return kDexMemAccessWide;
+ case Instruction::IGET_OBJECT_QUICK: case Instruction::IPUT_OBJECT_QUICK:
+ return kDexMemAccessObject;
+ case Instruction::IGET_BOOLEAN_QUICK: case Instruction::IPUT_BOOLEAN_QUICK:
+ return kDexMemAccessBoolean;
+ case Instruction::IGET_BYTE_QUICK: case Instruction::IPUT_BYTE_QUICK:
+ return kDexMemAccessByte;
+ case Instruction::IGET_CHAR_QUICK: case Instruction::IPUT_CHAR_QUICK:
+ return kDexMemAccessChar;
+ case Instruction::IGET_SHORT_QUICK: case Instruction::IPUT_SHORT_QUICK:
+ return kDexMemAccessShort;
+ default:
+ LOG(FATAL) << code;
+ UNREACHABLE();
+ }
+}
+
+constexpr DexMemAccessType SGetOrSPutMemAccessType(Instruction::Code code) {
+#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions.
+ DCHECK(IsInstructionSGetOrSPut(opcode));
+#endif
+ return (code >= Instruction::SPUT) ? SPutMemAccessType(code) : SGetMemAccessType(code);
+}
+
+constexpr DexMemAccessType AGetOrAPutMemAccessType(Instruction::Code code) {
+#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions.
+ DCHECK(IsInstructionAGetOrAPut(opcode));
+#endif
+ return (code >= Instruction::APUT) ? APutMemAccessType(code) : AGetMemAccessType(code);
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_DEX_INSTRUCTION_UTILS_H_
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 419d555..9e159c2 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -655,7 +655,7 @@
}
bool Heap::IsCompilingBoot() const {
- if (!Runtime::Current()->IsCompiler()) {
+ if (!Runtime::Current()->IsAotCompiler()) {
return false;
}
for (const auto& space : continuous_spaces_) {
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index d873e6d..14f770d 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -788,7 +788,7 @@
OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),
image_header.GetOatFileBegin(),
- !Runtime::Current()->IsCompiler(), error_msg);
+ !Runtime::Current()->IsAotCompiler(), error_msg);
if (oat_file == NULL) {
*error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
oat_filename.c_str(), GetName(), error_msg->c_str());
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 90115c3..a054462 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -31,6 +31,8 @@
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc_root-inl.h"
#include "interpreter/interpreter.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache.h"
@@ -92,6 +94,16 @@
static void UpdateEntrypoints(mirror::ArtMethod* method, const void* quick_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Runtime* const runtime = Runtime::Current();
+ jit::Jit* jit = runtime->GetJit();
+ if (jit != nullptr) {
+ const void* old_code_ptr = method->GetEntryPointFromQuickCompiledCode();
+ jit::JitCodeCache* code_cache = jit->GetCodeCache();
+ if (code_cache->ContainsCodePtr(old_code_ptr)) {
+ // Save the old compiled code since we need it to implement ClassLinker::GetQuickOatCodeFor.
+ code_cache->SaveCompiledCode(method, old_code_ptr);
+ }
+ }
method->SetEntryPointFromQuickCompiledCode(quick_code);
if (!method->IsResolutionMethod()) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -120,7 +132,8 @@
}
const void* new_quick_code;
bool uninstall = !entry_exit_stubs_installed_ && !interpreter_stubs_installed_;
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Runtime* const runtime = Runtime::Current();
+ ClassLinker* const class_linker = runtime->GetClassLinker();
bool is_class_initialized = method->GetDeclaringClass()->IsInitialized();
if (uninstall) {
if ((forced_interpret_only_ || IsDeoptimized(method)) && !method->IsNative()) {
@@ -143,7 +156,6 @@
new_quick_code = GetQuickInstrumentationEntryPoint();
} else {
new_quick_code = class_linker->GetQuickOatCodeFor(method);
- DCHECK(!class_linker->IsQuickToInterpreterBridge(new_quick_code));
}
} else {
new_quick_code = GetQuickResolutionStub();
@@ -397,6 +409,10 @@
method_unwind_listeners_.push_back(listener);
have_method_unwind_listeners_ = true;
}
+ if ((events & kBackwardBranch) != 0) {
+ backward_branch_listeners_.push_back(listener);
+ have_backward_branch_listeners_ = true;
+ }
if ((events & kDexPcMoved) != 0) {
std::list<InstrumentationListener*>* modified;
if (have_dex_pc_listeners_) {
@@ -904,6 +920,13 @@
}
}
+void Instrumentation::BackwardBranchImpl(Thread* thread, mirror::ArtMethod* method,
+ int32_t offset) const {
+ for (InstrumentationListener* listener : backward_branch_listeners_) {
+ listener->BackwardBranch(thread, method, offset);
+ }
+}
+
void Instrumentation::FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
mirror::ArtMethod* method, uint32_t dex_pc,
mirror::ArtField* field) const {
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index cea0388..b667a40 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -94,6 +94,10 @@
mirror::ArtMethod* catch_method, uint32_t catch_dex_pc,
mirror::Throwable* exception_object)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+
+ // Call-back for when we get a backward branch.
+ virtual void BackwardBranch(Thread* thread, mirror::ArtMethod* method, int32_t dex_pc_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
};
// Instrumentation is a catch-all for when extra information is required from the runtime. The
@@ -103,13 +107,14 @@
class Instrumentation {
public:
enum InstrumentationEvent {
- kMethodEntered = 1, // 1 << 0
- kMethodExited = 2, // 1 << 1
- kMethodUnwind = 4, // 1 << 2
- kDexPcMoved = 8, // 1 << 3
- kFieldRead = 16, // 1 << 4,
- kFieldWritten = 32, // 1 << 5
- kExceptionCaught = 64, // 1 << 6
+ kMethodEntered = 0x1,
+ kMethodExited = 0x2,
+ kMethodUnwind = 0x4,
+ kDexPcMoved = 0x8,
+ kFieldRead = 0x10,
+ kFieldWritten = 0x20,
+ kExceptionCaught = 0x40,
+ kBackwardBranch = 0x80,
};
Instrumentation();
@@ -244,6 +249,10 @@
return have_exception_caught_listeners_;
}
+ bool HasBackwardBranchListeners() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return have_backward_branch_listeners_;
+ }
+
bool IsActive() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ ||
have_field_read_listeners_ || have_field_write_listeners_ ||
@@ -284,6 +293,14 @@
}
}
+ // Inform listeners that a backward branch has been taken (only supported by the interpreter).
+ void BackwardBranch(Thread* thread, mirror::ArtMethod* method, int32_t offset) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (UNLIKELY(HasBackwardBranchListeners())) {
+ BackwardBranchImpl(thread, method, offset);
+ }
+ }
+
// Inform listeners that we read a field (only supported by the interpreter).
void FieldReadEvent(Thread* thread, mirror::Object* this_object,
mirror::ArtMethod* method, uint32_t dex_pc,
@@ -361,6 +378,8 @@
void DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object,
mirror::ArtMethod* method, uint32_t dex_pc) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void BackwardBranchImpl(Thread* thread, mirror::ArtMethod* method, int32_t offset) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void FieldReadEventImpl(Thread* thread, mirror::Object* this_object,
mirror::ArtMethod* method, uint32_t dex_pc,
mirror::ArtField* field) const
@@ -429,10 +448,14 @@
// Do we have any exception caught listeners? Short-cut to avoid taking the instrumentation_lock_.
bool have_exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_);
+ // Do we have any backward branch listeners? Short-cut to avoid taking the instrumentation_lock_.
+ bool have_backward_branch_listeners_ GUARDED_BY(Locks::mutator_lock_);
+
// The event listeners, written to with the mutator_lock_ exclusively held.
std::list<InstrumentationListener*> method_entry_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_);
+ std::list<InstrumentationListener*> backward_branch_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::shared_ptr<std::list<InstrumentationListener*>> dex_pc_listeners_
GUARDED_BY(Locks::mutator_lock_);
std::shared_ptr<std::list<InstrumentationListener*>> field_read_listeners_
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index e4b3247..37324ea 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -54,6 +54,12 @@
#define UPDATE_HANDLER_TABLE() \
currentHandlersTable = handlersTable[Runtime::Current()->GetInstrumentation()->GetInterpreterHandlerTable()]
+#define BACKWARD_BRANCH_INSTRUMENTATION(offset) \
+ do { \
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); \
+ instrumentation->BackwardBranch(self, shadow_frame.GetMethod(), offset); \
+ } while (false)
+
#define UNREACHABLE_CODE_CHECK() \
do { \
if (kIsDebugBuild) { \
@@ -135,7 +141,7 @@
}
};
- const bool do_assignability_check = do_access_check;
+ constexpr bool do_assignability_check = do_access_check;
if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
LOG(FATAL) << "Invalid shadow frame for interpreter use";
return JValue();
@@ -608,6 +614,7 @@
HANDLE_INSTRUCTION_START(GOTO) {
int8_t offset = inst->VRegA_10t(inst_data);
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -620,6 +627,7 @@
HANDLE_INSTRUCTION_START(GOTO_16) {
int16_t offset = inst->VRegA_20t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -632,6 +640,7 @@
HANDLE_INSTRUCTION_START(GOTO_32) {
int32_t offset = inst->VRegA_30t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -644,6 +653,7 @@
HANDLE_INSTRUCTION_START(PACKED_SWITCH) {
int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -656,6 +666,7 @@
HANDLE_INSTRUCTION_START(SPARSE_SWITCH) {
int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -758,6 +769,7 @@
if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) == shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -774,6 +786,7 @@
if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) != shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -790,6 +803,7 @@
if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) < shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -806,6 +820,7 @@
if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -822,6 +837,7 @@
if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) > shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -838,6 +854,7 @@
if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -854,6 +871,7 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) {
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -870,6 +888,7 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) {
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -886,6 +905,7 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) {
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -902,6 +922,7 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) {
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -918,6 +939,7 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) {
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
@@ -934,6 +956,7 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) {
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
+ BACKWARD_BRANCH_INSTRUMENTATION(offset);
if (UNLIKELY(self->TestAllFlags())) {
self->CheckSuspend();
UPDATE_HANDLER_TABLE();
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index ea7c192..08332d3 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -337,7 +337,8 @@
thread_group = args->group;
}
- if (!runtime->AttachCurrentThread(thread_name, as_daemon, thread_group, !runtime->IsCompiler())) {
+ if (!runtime->AttachCurrentThread(thread_name, as_daemon, thread_group,
+ !runtime->IsAotCompiler())) {
*p_env = nullptr;
return JNI_ERR;
} else {
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
new file mode 100644
index 0000000..539c181
--- /dev/null
+++ b/runtime/jit/jit.cc
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jit.h"
+
+#include <dlfcn.h>
+
+#include "entrypoints/runtime_asm_entrypoints.h"
+#include "interpreter/interpreter.h"
+#include "jit_code_cache.h"
+#include "jit_instrumentation.h"
+#include "mirror/art_method-inl.h"
+#include "runtime.h"
+#include "runtime_options.h"
+#include "thread_list.h"
+#include "utils.h"
+
+namespace art {
+namespace jit {
+
+JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& options) {
+ if (!options.GetOrDefault(RuntimeArgumentMap::UseJIT)) {
+ return nullptr;
+ }
+ auto* jit_options = new JitOptions;
+ jit_options->code_cache_capacity_ =
+ options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheCapacity);
+ jit_options->compile_threshold_ =
+ options.GetOrDefault(RuntimeArgumentMap::JITCompileThreshold);
+ return jit_options;
+}
+
+Jit::Jit()
+ : jit_library_handle_(nullptr), jit_compiler_handle_(nullptr), jit_load_(nullptr),
+ jit_compile_method_(nullptr) {
+}
+
+Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
+ std::unique_ptr<Jit> jit(new Jit);
+ if (!jit->LoadCompiler(error_msg)) {
+ return nullptr;
+ }
+ jit->code_cache_.reset(JitCodeCache::Create(options->GetCodeCacheCapacity(), error_msg));
+ if (jit->GetCodeCache() == nullptr) {
+ return nullptr;
+ }
+ LOG(INFO) << "JIT created with code_cache_capacity="
+ << PrettySize(options->GetCodeCacheCapacity())
+ << " compile_threshold=" << options->GetCompileThreshold();
+ return jit.release();
+}
+
+bool Jit::LoadCompiler(std::string* error_msg) {
+ jit_library_handle_ = dlopen(
+ kIsDebugBuild ? "libartd-compiler.so" : "libart-compiler.so", RTLD_NOW);
+ if (jit_library_handle_ == nullptr) {
+ std::ostringstream oss;
+ oss << "JIT could not load libart-compiler.so: " << dlerror();
+ *error_msg = oss.str();
+ return false;
+ }
+ jit_load_ = reinterpret_cast<void* (*)(CompilerCallbacks**)>(
+ dlsym(jit_library_handle_, "jit_load"));
+ if (jit_load_ == nullptr) {
+ dlclose(jit_library_handle_);
+ *error_msg = "JIT couldn't find jit_load entry point";
+ return false;
+ }
+ jit_unload_ = reinterpret_cast<void (*)(void*)>(
+ dlsym(jit_library_handle_, "jit_unload"));
+ if (jit_unload_ == nullptr) {
+ dlclose(jit_library_handle_);
+ *error_msg = "JIT couldn't find jit_unload entry point";
+ return false;
+ }
+ jit_compile_method_ = reinterpret_cast<bool (*)(void*, mirror::ArtMethod*, Thread*)>(
+ dlsym(jit_library_handle_, "jit_compile_method"));
+ if (jit_compile_method_ == nullptr) {
+ dlclose(jit_library_handle_);
+ *error_msg = "JIT couldn't find jit_compile_method entry point";
+ return false;
+ }
+ CompilerCallbacks* callbacks = nullptr;
+ VLOG(jit) << "Calling JitLoad interpreter_only="
+ << Runtime::Current()->GetInstrumentation()->InterpretOnly();
+ jit_compiler_handle_ = (jit_load_)(&callbacks);
+ if (jit_compiler_handle_ == nullptr) {
+ dlclose(jit_library_handle_);
+ *error_msg = "JIT couldn't load compiler";
+ return false;
+ }
+ if (callbacks == nullptr) {
+ dlclose(jit_library_handle_);
+ *error_msg = "JIT compiler callbacks were not set";
+ jit_compiler_handle_ = nullptr;
+ return false;
+ }
+ compiler_callbacks_ = callbacks;
+ return true;
+}
+
+bool Jit::CompileMethod(mirror::ArtMethod* method, Thread* self) {
+ DCHECK(!method->IsRuntimeMethod());
+ const bool result = jit_compile_method_(jit_compiler_handle_, method, self);
+ if (result) {
+ method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
+ }
+ return result;
+}
+
+void Jit::CreateThreadPool() {
+ CHECK(instrumentation_cache_.get() != nullptr);
+ instrumentation_cache_->CreateThreadPool();
+}
+
+void Jit::DeleteThreadPool() {
+ if (instrumentation_cache_.get() != nullptr) {
+ instrumentation_cache_->DeleteThreadPool();
+ }
+}
+
+Jit::~Jit() {
+ DeleteThreadPool();
+ if (jit_compiler_handle_ != nullptr) {
+ jit_unload_(jit_compiler_handle_);
+ }
+ if (jit_library_handle_ != nullptr) {
+ dlclose(jit_library_handle_);
+ }
+}
+
+void Jit::CreateInstrumentationCache(size_t compile_threshold) {
+ CHECK_GT(compile_threshold, 0U);
+ Runtime* const runtime = Runtime::Current();
+ runtime->GetThreadList()->SuspendAll();
+ // Add Jit interpreter instrumentation, tells the interpreter when to notify the jit to compile
+ // something.
+ instrumentation_cache_.reset(new jit::JitInstrumentationCache(compile_threshold));
+ runtime->GetInstrumentation()->AddListener(
+ new jit::JitInstrumentationListener(instrumentation_cache_.get()),
+ instrumentation::Instrumentation::kMethodEntered |
+ instrumentation::Instrumentation::kBackwardBranch);
+ runtime->GetThreadList()->ResumeAll();
+}
+
+} // namespace jit
+} // namespace art
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
new file mode 100644
index 0000000..b80015f
--- /dev/null
+++ b/runtime/jit/jit.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_JIT_JIT_H_
+#define ART_RUNTIME_JIT_JIT_H_
+
+#include <unordered_map>
+
+#include "instrumentation.h"
+
+#include "atomic.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "gc_root.h"
+#include "jni.h"
+#include "object_callbacks.h"
+#include "thread_pool.h"
+
+namespace art {
+
+class CompilerCallbacks;
+struct RuntimeArgumentMap;
+
+namespace jit {
+
+class JitCodeCache;
+class JitInstrumentationCache;
+class JitOptions;
+
+class Jit {
+ public:
+ static constexpr bool kStressMode = kIsDebugBuild;
+ static constexpr size_t kDefaultCompileThreshold = kStressMode ? 1 : 1000;
+
+ virtual ~Jit();
+ static Jit* Create(JitOptions* options, std::string* error_msg);
+ bool CompileMethod(mirror::ArtMethod* method, Thread* self)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CreateInstrumentationCache(size_t compile_threshold);
+ void CreateThreadPool();
+ CompilerCallbacks* GetCompilerCallbacks() {
+ return compiler_callbacks_;
+ }
+ const JitCodeCache* GetCodeCache() const {
+ return code_cache_.get();
+ }
+ JitCodeCache* GetCodeCache() {
+ return code_cache_.get();
+ }
+ void DeleteThreadPool();
+
+ private:
+ Jit();
+ bool LoadCompiler(std::string* error_msg);
+
+ // JIT compiler
+ void* jit_library_handle_;
+ void* jit_compiler_handle_;
+ void* (*jit_load_)(CompilerCallbacks**);
+ void (*jit_unload_)(void*);
+ bool (*jit_compile_method_)(void*, mirror::ArtMethod*, Thread*);
+
+ std::unique_ptr<jit::JitInstrumentationCache> instrumentation_cache_;
+ std::unique_ptr<jit::JitCodeCache> code_cache_;
+ CompilerCallbacks* compiler_callbacks_; // Owned by the jit compiler.
+};
+
+class JitOptions {
+ public:
+ static JitOptions* CreateFromRuntimeArguments(const RuntimeArgumentMap& options);
+ size_t GetCompileThreshold() const {
+ return compile_threshold_;
+ }
+ size_t GetCodeCacheCapacity() const {
+ return code_cache_capacity_;
+ }
+
+ private:
+ size_t code_cache_capacity_;
+ size_t compile_threshold_;
+
+ JitOptions() : code_cache_capacity_(0), compile_threshold_(0) {
+ }
+};
+
+} // namespace jit
+} // namespace art
+
+#endif // ART_RUNTIME_JIT_JIT_H_
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
new file mode 100644
index 0000000..8d4965e
--- /dev/null
+++ b/runtime/jit/jit_code_cache.cc
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jit_code_cache.h"
+
+#include <sstream>
+
+#include "mem_map.h"
+#include "mirror/art_method-inl.h"
+#include "oat_file-inl.h"
+
+namespace art {
+namespace jit {
+
+JitCodeCache* JitCodeCache::Create(size_t capacity, std::string* error_msg) {
+ CHECK_GT(capacity, 0U);
+ CHECK_LT(capacity, kMaxCapacity);
+ std::string error_str;
+ // Map name specific for android_os_Debug.cpp accounting.
+ MemMap* map = MemMap::MapAnonymous("jit-code-cache", nullptr, capacity,
+ PROT_READ | PROT_WRITE | PROT_EXEC, false, &error_str);
+ if (map == nullptr) {
+ std::ostringstream oss;
+ oss << "Failed to create read write execute cache: " << error_str << " size=" << capacity;
+ *error_msg = oss.str();
+ return nullptr;
+ }
+ return new JitCodeCache(map);
+}
+
+JitCodeCache::JitCodeCache(MemMap* mem_map)
+ : lock_("Jit code cache", kJitCodeCacheLock), num_methods_(0) {
+ VLOG(jit) << "Created jit code cache size=" << PrettySize(mem_map->Size());
+ mem_map_.reset(mem_map);
+ uint8_t* divider = mem_map->Begin() + RoundUp(mem_map->Size() / 4, kPageSize);
+ // Data cache is 1 / 4 of the map. TODO: Make this variable?
+ // Put data at the start.
+ data_cache_ptr_ = mem_map->Begin();
+ data_cache_end_ = divider;
+ data_cache_begin_ = data_cache_ptr_;
+ mprotect(data_cache_ptr_, data_cache_end_ - data_cache_begin_, PROT_READ | PROT_WRITE);
+ // Code cache after.
+ code_cache_begin_ = divider;
+ code_cache_ptr_ = divider;
+ code_cache_end_ = mem_map->End();
+}
+
+bool JitCodeCache::ContainsMethod(mirror::ArtMethod* method) const {
+ return ContainsCodePtr(method->GetEntryPointFromQuickCompiledCode());
+}
+
+bool JitCodeCache::ContainsCodePtr(const void* ptr) const {
+ return ptr >= code_cache_begin_ && ptr < code_cache_end_;
+}
+
+void JitCodeCache::FlushInstructionCache() {
+ UNIMPLEMENTED(FATAL);
+ // TODO: Investigate if we need to do this.
+ // __clear_cache(reinterpret_cast<char*>(code_cache_begin_), static_cast<int>(CodeCacheSize()));
+}
+
+uint8_t* JitCodeCache::ReserveCode(Thread* self, size_t size) {
+ MutexLock mu(self, lock_);
+ if (size > CodeCacheRemain()) {
+ return nullptr;
+ }
+ code_cache_ptr_ += size;
+ return code_cache_ptr_ - size;
+}
+
+uint8_t* JitCodeCache::AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end) {
+ MutexLock mu(self, lock_);
+ const size_t size = end - begin;
+ if (size > DataCacheRemain()) {
+ return nullptr; // Out of space in the data cache.
+ }
+ std::copy(begin, end, data_cache_ptr_);
+ data_cache_ptr_ += size;
+ return data_cache_ptr_ - size;
+}
+
+const void* JitCodeCache::GetCodeFor(mirror::ArtMethod* method) {
+ const void* code = method->GetEntryPointFromQuickCompiledCode();
+ if (ContainsCodePtr(code)) {
+ return code;
+ }
+ MutexLock mu(Thread::Current(), lock_);
+ auto it = method_code_map_.find(method);
+ if (it != method_code_map_.end()) {
+ return it->second;
+ }
+ return nullptr;
+}
+
+void JitCodeCache::SaveCompiledCode(mirror::ArtMethod* method, const void* old_code_ptr) {
+ DCHECK_EQ(method->GetEntryPointFromQuickCompiledCode(), old_code_ptr);
+ DCHECK(ContainsCodePtr(old_code_ptr)) << PrettyMethod(method) << " old_code_ptr="
+ << old_code_ptr;
+ MutexLock mu(Thread::Current(), lock_);
+ auto it = method_code_map_.find(method);
+ if (it != method_code_map_.end()) {
+ return;
+ }
+ method_code_map_.Put(method, old_code_ptr);
+}
+
+} // namespace jit
+} // namespace art
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
new file mode 100644
index 0000000..aa8c717
--- /dev/null
+++ b/runtime/jit/jit_code_cache.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_JIT_JIT_CODE_CACHE_H_
+#define ART_RUNTIME_JIT_JIT_CODE_CACHE_H_
+
+#include "instrumentation.h"
+
+#include "atomic.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "gc_root.h"
+#include "jni.h"
+#include "oat_file.h"
+#include "object_callbacks.h"
+#include "safe_map.h"
+#include "thread_pool.h"
+
+namespace art {
+
+class CompiledMethod;
+class CompilerCallbacks;
+
+namespace mirror {
+class ArtMethod;
+} // namespcae mirror
+
+namespace jit {
+
+class JitInstrumentationCache;
+
+class JitCodeCache {
+ public:
+ static constexpr size_t kMaxCapacity = 1 * GB;
+ static constexpr size_t kDefaultCapacity = 2 * MB;
+
+ static JitCodeCache* Create(size_t capacity, std::string* error_msg);
+
+ const uint8_t* CodeCachePtr() const {
+ return code_cache_ptr_;
+ }
+ size_t CodeCacheSize() const {
+ return code_cache_ptr_ - code_cache_begin_;
+ }
+ size_t CodeCacheRemain() const {
+ return code_cache_end_ - code_cache_ptr_;
+ }
+ size_t DataCacheSize() const {
+ return data_cache_ptr_ - data_cache_begin_;
+ }
+ size_t DataCacheRemain() const {
+ return data_cache_end_ - data_cache_ptr_;
+ }
+ size_t NumMethods() const {
+ return num_methods_;
+ }
+
+ bool ContainsMethod(mirror::ArtMethod* method) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool ContainsCodePtr(const void* ptr) const;
+
+ uint8_t* ReserveCode(Thread* self, size_t size) LOCKS_EXCLUDED(lock_);
+
+ uint8_t* AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end)
+ LOCKS_EXCLUDED(lock_);
+
+ // Get code for a method, returns null if it is not in the jit cache.
+ const void* GetCodeFor(mirror::ArtMethod* method)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_);
+
+ void SaveCompiledCode(mirror::ArtMethod* method, const void* old_code_ptr)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_);
+
+ private:
+ // Takes ownership of code_mem_map.
+ explicit JitCodeCache(MemMap* code_mem_map);
+ void FlushInstructionCache();
+
+ Mutex lock_;
+ // Mem map which holds code and data. We do this since we need to have 32 bit offsets from method
+ // headers in code cache which point to things in the data cache. If the maps are more than 4GB
+ // apart, having multiple maps wouldn't work.
+ std::unique_ptr<MemMap> mem_map_;
+ // Code cache section.
+ uint8_t* code_cache_ptr_;
+ const uint8_t* code_cache_begin_;
+ const uint8_t* code_cache_end_;
+ // Data cache section.
+ uint8_t* data_cache_ptr_;
+ const uint8_t* data_cache_begin_;
+ const uint8_t* data_cache_end_;
+ size_t num_methods_;
+ // TODO: This relies on methods not moving.
+ // This map holds code for methods if they were deoptimized by the instrumentation stubs. This is
+ // required since we have to implement ClassLinker::GetQuickOatCodeFor for walking stacks.
+ SafeMap<mirror::ArtMethod*, const void*> method_code_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(JitCodeCache);
+};
+
+
+} // namespace jit
+} // namespace art
+
+#endif // ART_RUNTIME_JIT_JIT_CODE_CACHE_H_
diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc
new file mode 100644
index 0000000..160e678
--- /dev/null
+++ b/runtime/jit/jit_instrumentation.cc
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jit_instrumentation.h"
+
+#include "jit.h"
+#include "jit_code_cache.h"
+#include "mirror/art_method-inl.h"
+#include "scoped_thread_state_change.h"
+
+namespace art {
+namespace jit {
+
+class JitCompileTask : public Task {
+ public:
+ explicit JitCompileTask(mirror::ArtMethod* method, JitInstrumentationCache* cache)
+ : method_(method), cache_(cache) {
+ }
+
+ virtual void Run(Thread* self) OVERRIDE {
+ ScopedObjectAccess soa(self);
+ VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_);
+ if (Runtime::Current()->GetJit()->CompileMethod(method_, self)) {
+ cache_->SignalCompiled(self, method_);
+ } else {
+ VLOG(jit) << "Failed to compile method " << PrettyMethod(method_);
+ }
+ }
+
+ virtual void Finalize() OVERRIDE {
+ delete this;
+ }
+
+ private:
+ mirror::ArtMethod* const method_;
+ JitInstrumentationCache* const cache_;
+};
+
+JitInstrumentationCache::JitInstrumentationCache(size_t hot_method_threshold)
+ : lock_("jit instrumentation lock"), hot_method_threshold_(hot_method_threshold) {
+}
+
+void JitInstrumentationCache::CreateThreadPool() {
+ thread_pool_.reset(new ThreadPool("Jit thread pool", 1));
+}
+
+void JitInstrumentationCache::DeleteThreadPool() {
+ thread_pool_.reset();
+}
+
+void JitInstrumentationCache::SignalCompiled(Thread* self, mirror::ArtMethod* method) {
+ ScopedObjectAccessUnchecked soa(self);
+ jmethodID method_id = soa.EncodeMethod(method);
+ MutexLock mu(self, lock_);
+ auto it = samples_.find(method_id);
+ if (it != samples_.end()) {
+ samples_.erase(it);
+ }
+}
+
+void JitInstrumentationCache::AddSamples(Thread* self, mirror::ArtMethod* method, size_t count) {
+ ScopedObjectAccessUnchecked soa(self);
+ // Since we don't have on-stack replacement, some methods can remain in the interpreter longer
+ // than we want resulting in samples even after the method is compiled.
+ if (method->IsClassInitializer() ||
+ Runtime::Current()->GetJit()->GetCodeCache()->ContainsMethod(method)) {
+ return;
+ }
+ jmethodID method_id = soa.EncodeMethod(method);
+ bool is_hot = false;
+ {
+ MutexLock mu(self, lock_);
+ size_t sample_count = 0;
+ auto it = samples_.find(method_id);
+ if (it != samples_.end()) {
+ it->second += count;
+ sample_count = it->second;
+ } else {
+ sample_count = count;
+ samples_.insert(std::make_pair(method_id, count));
+ }
+ // If we have enough samples, mark as hot and request Jit compilation.
+ if (sample_count >= hot_method_threshold_ && sample_count - count < hot_method_threshold_) {
+ is_hot = true;
+ }
+ }
+ if (is_hot) {
+ if (thread_pool_.get() != nullptr) {
+ thread_pool_->AddTask(self, new JitCompileTask(method->GetInterfaceMethodIfProxy(), this));
+ thread_pool_->StartWorkers(self);
+ } else {
+ VLOG(jit) << "Compiling hot method " << PrettyMethod(method);
+ Runtime::Current()->GetJit()->CompileMethod(method->GetInterfaceMethodIfProxy(), self);
+ }
+ }
+}
+
+JitInstrumentationListener::JitInstrumentationListener(JitInstrumentationCache* cache)
+ : instrumentation_cache_(cache) {
+ CHECK(instrumentation_cache_ != nullptr);
+}
+
+} // namespace jit
+} // namespace art
diff --git a/runtime/jit/jit_instrumentation.h b/runtime/jit/jit_instrumentation.h
new file mode 100644
index 0000000..9576f4b
--- /dev/null
+++ b/runtime/jit/jit_instrumentation.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_
+#define ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_
+
+#include <unordered_map>
+
+#include "instrumentation.h"
+
+#include "atomic.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "gc_root.h"
+#include "jni.h"
+#include "object_callbacks.h"
+#include "thread_pool.h"
+
+namespace art {
+namespace mirror {
+ class ArtField;
+ class ArtMethod;
+ class Class;
+ class Object;
+ class Throwable;
+} // namespace mirror
+union JValue;
+class Thread;
+class ThrowLocation;
+
+namespace jit {
+
+// Keeps track of which methods are hot.
+class JitInstrumentationCache {
+ public:
+ explicit JitInstrumentationCache(size_t hot_method_threshold);
+ void AddSamples(Thread* self, mirror::ArtMethod* method, size_t samples)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SignalCompiled(Thread* self, mirror::ArtMethod* method)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CreateThreadPool();
+ void DeleteThreadPool();
+
+ private:
+ Mutex lock_;
+ std::unordered_map<jmethodID, size_t> samples_;
+ size_t hot_method_threshold_;
+ std::unique_ptr<ThreadPool> thread_pool_;
+};
+
+class JitInstrumentationListener : public instrumentation::InstrumentationListener {
+ public:
+ explicit JitInstrumentationListener(JitInstrumentationCache* cache);
+
+ virtual void MethodEntered(Thread* thread, mirror::Object* /*this_object*/,
+ mirror::ArtMethod* method, uint32_t /*dex_pc*/)
+ OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ instrumentation_cache_->AddSamples(thread, method, 1);
+ }
+ virtual void MethodExited(Thread* /*thread*/, mirror::Object* /*this_object*/,
+ mirror::ArtMethod* /*method*/, uint32_t /*dex_pc*/,
+ const JValue& /*return_value*/)
+ OVERRIDE { }
+ virtual void MethodUnwind(Thread* /*thread*/, mirror::Object* /*this_object*/,
+ mirror::ArtMethod* /*method*/, uint32_t /*dex_pc*/) OVERRIDE { }
+ virtual void FieldRead(Thread* /*thread*/, mirror::Object* /*this_object*/,
+ mirror::ArtMethod* /*method*/, uint32_t /*dex_pc*/,
+ mirror::ArtField* /*field*/) OVERRIDE { }
+ virtual void FieldWritten(Thread* /*thread*/, mirror::Object* /*this_object*/,
+ mirror::ArtMethod* /*method*/, uint32_t /*dex_pc*/,
+ mirror::ArtField* /*field*/, const JValue& /*field_value*/)
+ OVERRIDE { }
+ virtual void ExceptionCaught(Thread* /*thread*/, const ThrowLocation& /*throw_location*/,
+ mirror::ArtMethod* /*catch_method*/, uint32_t /*catch_dex_pc*/,
+ mirror::Throwable* /*exception_object*/) OVERRIDE { }
+
+ virtual void DexPcMoved(Thread* /*self*/, mirror::Object* /*this_object*/,
+ mirror::ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { }
+
+ // We only care about how many dex instructions were executed in the Jit.
+ virtual void BackwardBranch(Thread* thread, mirror::ArtMethod* method, int32_t dex_pc_offset)
+ OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ CHECK_LE(dex_pc_offset, 0);
+ instrumentation_cache_->AddSamples(thread, method, 1);
+ }
+
+ private:
+ JitInstrumentationCache* const instrumentation_cache_;
+};
+
+} // namespace jit
+} // namespace art
+
+#endif // ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_
diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc
index 5a4ebd1..3cea4a1 100644
--- a/runtime/mirror/art_field.cc
+++ b/runtime/mirror/art_field.cc
@@ -44,7 +44,7 @@
void ArtField::SetOffset(MemberOffset num_bytes) {
DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous());
- if (kIsDebugBuild && Runtime::Current()->IsCompiler() &&
+ if (kIsDebugBuild && Runtime::Current()->IsAotCompiler() &&
!Runtime::Current()->UseCompileTimeClassPath()) {
Primitive::Type type = GetTypeAsPrimitiveType();
if (type == Primitive::kPrimDouble || type == Primitive::kPrimLong) {
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index 7d31148..c27c6e9 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -147,7 +147,10 @@
inline uint32_t ArtMethod::GetCodeSize() {
DCHECK(!IsRuntimeMethod() && !IsProxyMethod()) << PrettyMethod(this);
- const void* code = EntryPointToCodePointer(GetEntryPointFromQuickCompiledCode());
+ return GetCodeSize(EntryPointToCodePointer(GetEntryPointFromQuickCompiledCode()));
+}
+
+inline uint32_t ArtMethod::GetCodeSize(const void* code) {
if (code == nullptr) {
return 0u;
}
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index b2016dc..6259745 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -27,6 +27,8 @@
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc/accounting/card_table-inl.h"
#include "interpreter/interpreter.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
#include "jni_internal.h"
#include "mapping_table.h"
#include "object_array-inl.h"
@@ -229,6 +231,7 @@
if (abort_on_failure) {
LOG(FATAL) << "Failed to find Dex offset for PC offset " << reinterpret_cast<void*>(sought_offset)
<< "(PC " << reinterpret_cast<void*>(pc) << ", entry_point=" << entry_point
+ << " current entry_point=" << GetQuickOatEntryPoint(sizeof(void*))
<< ") in " << PrettyMethod(this);
}
return DexFile::kDexNoIndex;
@@ -329,6 +332,13 @@
class_linker->IsQuickResolutionStub(code)) {
return;
}
+ // If we are the JIT then we may have just compiled the method after the
+ // IsQuickToInterpreterBridge check.
+ jit::Jit* const jit = Runtime::Current()->GetJit();
+ if (jit != nullptr &&
+ jit->GetCodeCache()->ContainsCodePtr(reinterpret_cast<const void*>(code))) {
+ return;
+ }
/*
* During a stack walk, a return PC may point past-the-end of the code
* in the case that the last instruction is a call that isn't expected to
@@ -336,11 +346,11 @@
*
* NOTE: For Thumb both pc and code are offset by 1 indicating the Thumb state.
*/
- CHECK(PcIsWithinQuickCode(pc))
+ CHECK(PcIsWithinQuickCode(reinterpret_cast<uintptr_t>(code), pc))
<< PrettyMethod(this)
<< " pc=" << std::hex << pc
<< " code=" << code
- << " size=" << GetCodeSize();
+ << " size=" << GetCodeSize(reinterpret_cast<const void*>(code));
}
bool ArtMethod::IsEntrypointInterpreter() {
@@ -410,7 +420,8 @@
}
// Ensure that we won't be accidentally calling quick compiled code when -Xint.
- if (kIsDebugBuild && Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly()) {
+ if (kIsDebugBuild && runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
+ DCHECK(!runtime->UseJit());
CHECK(IsEntrypointInterpreter())
<< "Don't call compiled code when -Xint " << PrettyMethod(this);
}
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index f33ca94..019fdcd 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -305,18 +305,8 @@
// quick entrypoint. This code isn't robust for instrumentation, etc. and is only used for
// debug purposes.
bool PcIsWithinQuickCode(uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- uintptr_t code = reinterpret_cast<uintptr_t>(GetEntryPointFromQuickCompiledCode());
- if (code == 0) {
- return pc == 0;
- }
- /*
- * During a stack walk, a return PC may point past-the-end of the code
- * in the case that the last instruction is a call that isn't expected to
- * return. Thus, we check <= code + GetCodeSize().
- *
- * NOTE: For Thumb both pc and code are offset by 1 indicating the Thumb state.
- */
- return code <= pc && pc <= code + GetCodeSize();
+ return PcIsWithinQuickCode(
+ reinterpret_cast<uintptr_t>(GetEntryPointFromQuickCompiledCode()), pc);
}
void AssertPcIsWithinQuickCode(uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -622,6 +612,23 @@
return offset;
}
+ // Code points to the start of the quick code.
+ static uint32_t GetCodeSize(const void* code);
+
+ static bool PcIsWithinQuickCode(uintptr_t code, uintptr_t pc) {
+ if (code == 0) {
+ return pc == 0;
+ }
+ /*
+ * During a stack walk, a return PC may point past-the-end of the code
+ * in the case that the last instruction is a call that isn't expected to
+ * return. Thus, we check <= code + GetCodeSize().
+ *
+ * NOTE: For Thumb both pc and code are offset by 1 indicating the Thumb state.
+ */
+ return code <= pc && pc <= code + GetCodeSize(reinterpret_cast<const void*>(code));
+ }
+
friend struct art::ArtMethodOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(ArtMethod);
};
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 9061bb3..3192e03 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -577,12 +577,12 @@
}
if (oat_file_->IsExecutable() ||
Runtime::Current() == nullptr || // This case applies for oatdump.
- Runtime::Current()->IsCompiler()) {
+ Runtime::Current()->IsAotCompiler()) {
return OatMethod(oat_file_->Begin(), oat_method_offsets->code_offset_);
- } else {
- // We aren't allowed to use the compiled code. We just force it down the interpreted version.
- return OatMethod(oat_file_->Begin(), 0);
}
+ // We aren't allowed to use the compiled code. We just force it down the interpreted / jit
+ // version.
+ return OatMethod(oat_file_->Begin(), 0);
}
void OatFile::OatMethod::LinkMethod(mirror::ArtMethod* method) const {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 6ae3c3e..5e68439 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -147,8 +147,8 @@
return reinterpret_cast<T>(begin_ + offset);
}
- const uint8_t* const begin_;
- const uint32_t code_offset_;
+ const uint8_t* begin_;
+ uint32_t code_offset_;
friend class OatClass;
};
diff --git a/runtime/object_lock.cc b/runtime/object_lock.cc
index f7accc0..749fb5d 100644
--- a/runtime/object_lock.cc
+++ b/runtime/object_lock.cc
@@ -47,6 +47,7 @@
obj_->NotifyAll(self_);
}
+template class ObjectLock<mirror::ArtMethod>;
template class ObjectLock<mirror::Class>;
template class ObjectLock<mirror::Object>;
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 99369ca..7bdd996 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -146,6 +146,15 @@
.Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"})
.WithValues({true, false})
.IntoKey(M::EnableHSpaceCompactForOOM)
+ .Define({"-Xjit", "-Xnojit"})
+ .WithValues({true, false})
+ .IntoKey(M::UseJIT)
+ .Define("-Xjitcodecachesize:_")
+ .WithType<MemoryKiB>()
+ .IntoKey(M::JITCodeCacheCapacity)
+ .Define("-Xjitthreshold:_")
+ .WithType<unsigned int>()
+ .IntoKey(M::JITCompileThreshold)
.Define("-XX:HspaceCompactForOOMMinIntervalMs=_") // in ms
.WithType<MillisecondsToNanoseconds>() // store as ns
.IntoKey(M::HSpaceCompactForOOMMinIntervalsMs)
@@ -246,7 +255,7 @@
"-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
"-Xdexopt:_", "-Xnoquithandler", "-Xjnigreflimit:_", "-Xgenregmap", "-Xnogenregmap",
"-Xverifyopt:_", "-Xcheckdexsum", "-Xincludeselectedop", "-Xjitop:_",
- "-Xincludeselectedmethod", "-Xjitthreshold:_", "-Xjitcodecachesize:_",
+ "-Xincludeselectedmethod", "-Xjitthreshold:_",
"-Xjitblocking", "-Xjitmethod:_", "-Xjitclass:_", "-Xjitoffset:_",
"-Xjitconfig:_", "-Xjitcheckcg", "-Xjitverbose", "-Xjitprofile",
"-Xjitdisableopt", "-Xjitsuspendpoll", "-XX:mainThreadStackSize=_"})
@@ -351,19 +360,20 @@
bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognized,
RuntimeArgumentMap* runtime_options) {
-// gLogVerbosity.class_linker = true; // TODO: don't check this in!
-// gLogVerbosity.compiler = true; // TODO: don't check this in!
-// gLogVerbosity.gc = true; // TODO: don't check this in!
-// gLogVerbosity.heap = true; // TODO: don't check this in!
-// gLogVerbosity.jdwp = true; // TODO: don't check this in!
-// gLogVerbosity.jni = true; // TODO: don't check this in!
-// gLogVerbosity.monitor = true; // TODO: don't check this in!
-// gLogVerbosity.profiler = true; // TODO: don't check this in!
-// gLogVerbosity.signals = true; // TODO: don't check this in!
-// gLogVerbosity.startup = true; // TODO: don't check this in!
-// gLogVerbosity.third_party_jni = true; // TODO: don't check this in!
-// gLogVerbosity.threads = true; // TODO: don't check this in!
-// gLogVerbosity.verifier = true; // TODO: don't check this in!
+ // gLogVerbosity.class_linker = true; // TODO: don't check this in!
+ // gLogVerbosity.compiler = true; // TODO: don't check this in!
+ // gLogVerbosity.gc = true; // TODO: don't check this in!
+ // gLogVerbosity.heap = true; // TODO: don't check this in!
+ // gLogVerbosity.jdwp = true; // TODO: don't check this in!
+ // gLogVerbosity.jit = true; // TODO: don't check this in!
+ // gLogVerbosity.jni = true; // TODO: don't check this in!
+ // gLogVerbosity.monitor = true; // TODO: don't check this in!
+ // gLogVerbosity.profiler = true; // TODO: don't check this in!
+ // gLogVerbosity.signals = true; // TODO: don't check this in!
+ // gLogVerbosity.startup = true; // TODO: don't check this in!
+ // gLogVerbosity.third_party_jni = true; // TODO: don't check this in!
+ // gLogVerbosity.threads = true; // TODO: don't check this in!
+ // gLogVerbosity.verifier = true; // TODO: don't check this in!
for (size_t i = 0; i < options.size(); ++i) {
if (true && options[0].first == "-Xzygote") {
@@ -558,7 +568,7 @@
UsageMessage(stream, "The following standard options are supported:\n");
UsageMessage(stream, " -classpath classpath (-cp classpath)\n");
UsageMessage(stream, " -Dproperty=value\n");
- UsageMessage(stream, " -verbose:tag ('gc', 'jni', or 'class')\n");
+ UsageMessage(stream, " -verbose:tag ('gc', 'jit', 'jni', or 'class')\n");
UsageMessage(stream, " -showversion\n");
UsageMessage(stream, " -help\n");
UsageMessage(stream, " -agentlib:jdwp=options\n");
@@ -588,6 +598,8 @@
UsageMessage(stream, " -XX:ForegroundHeapGrowthMultiplier=doublevalue\n");
UsageMessage(stream, " -XX:LowMemoryMode\n");
UsageMessage(stream, " -Xprofile:{threadcpuclock,wallclock,dualclock}\n");
+ UsageMessage(stream, " -Xjitcodecachesize:N\n");
+ UsageMessage(stream, " -Xjitthreshold:integervalue\n");
UsageMessage(stream, "\n");
UsageMessage(stream, "The following unique to ART options are supported:\n");
@@ -628,6 +640,8 @@
UsageMessage(stream, " -Xcompiler-option dex2oat-option\n");
UsageMessage(stream, " -Ximage-compiler-option dex2oat-option\n");
UsageMessage(stream, " -Xpatchoat:filename\n");
+ UsageMessage(stream, " -Xjit\n");
+ UsageMessage(stream, " -Xnojit\n");
UsageMessage(stream, " -X[no]relocate\n");
UsageMessage(stream, " -X[no]dex2oat (Whether to invoke dex2oat on the application)\n");
UsageMessage(stream, " -X[no]image-dex2oat (Whether to create and use a boot image)\n");
@@ -655,8 +669,6 @@
UsageMessage(stream, " -Xincludeselectedop\n");
UsageMessage(stream, " -Xjitop:hexopvalue[-endvalue][,hexopvalue[-endvalue]]*\n");
UsageMessage(stream, " -Xincludeselectedmethod\n");
- UsageMessage(stream, " -Xjitthreshold:integervalue\n");
- UsageMessage(stream, " -Xjitcodecachesize:decimalvalueofkbytes\n");
UsageMessage(stream, " -Xjitblocking\n");
UsageMessage(stream, " -Xjitmethod:signature[,signature]* (eg Ljava/lang/String\\;replace)\n");
UsageMessage(stream, " -Xjitclass:classname[,classname]*\n");
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
index c3bdcb1..db372c3 100644
--- a/runtime/profiler.cc
+++ b/runtime/profiler.cc
@@ -161,7 +161,7 @@
CHECK(runtime->AttachCurrentThread("Profiler", true, runtime->GetSystemThreadGroup(),
- !runtime->IsCompiler()));
+ !runtime->IsAotCompiler()));
Thread* self = Thread::Current();
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index d65b2d5..44e2844 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -77,7 +77,9 @@
InlineMethod* method) {
DCHECK(verifier != nullptr);
DCHECK_EQ(Runtime::Current()->IsCompiler(), method != nullptr);
- DCHECK_EQ(verifier->CanLoadClasses(), method != nullptr);
+ if (!Runtime::Current()->UseJit()) {
+ DCHECK_EQ(verifier->CanLoadClasses(), method != nullptr);
+ }
// We currently support only plain return or 2-instruction methods.
const DexFile::CodeItem* code_item = verifier->CodeItem();
@@ -110,6 +112,10 @@
case Instruction::IGET_CHAR:
case Instruction::IGET_SHORT:
case Instruction::IGET_WIDE:
+ // TODO: Add handling for JIT.
+ // case Instruction::IGET_QUICK:
+ // case Instruction::IGET_WIDE_QUICK:
+ // case Instruction::IGET_OBJECT_QUICK:
return AnalyseIGetMethod(verifier, method);
case Instruction::IPUT:
case Instruction::IPUT_OBJECT:
@@ -118,6 +124,10 @@
case Instruction::IPUT_CHAR:
case Instruction::IPUT_SHORT:
case Instruction::IPUT_WIDE:
+ // TODO: Add handling for JIT.
+ // case Instruction::IPUT_QUICK:
+ // case Instruction::IPUT_WIDE_QUICK:
+ // case Instruction::IPUT_OBJECT_QUICK:
return AnalyseIPutMethod(verifier, method);
default:
return false;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index f38f65e..749418d 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -65,6 +65,8 @@
#include "image.h"
#include "instrumentation.h"
#include "intern_table.h"
+#include "interpreter/interpreter.h"
+#include "jit/jit.h"
#include "jni_internal.h"
#include "mirror/array.h"
#include "mirror/art_field-inl.h"
@@ -225,6 +227,12 @@
// Make sure to let the GC complete if it is running.
heap_->WaitForGcToComplete(gc::kGcCauseBackground, self);
heap_->DeleteThreadPool();
+ if (jit_.get() != nullptr) {
+ VLOG(jit) << "Deleting jit thread pool";
+ // Delete thread pool before the thread list since we don't want to wait forever on the
+ // JIT compiler threads.
+ jit_->DeleteThreadPool();
+ }
// Make sure our internal threads are dead before we start tearing down things they're using.
Dbg::StopJdwp();
@@ -233,6 +241,13 @@
// Make sure all other non-daemon threads have terminated, and all daemon threads are suspended.
delete thread_list_;
+ // Delete the JIT after thread list to ensure that there is no remaining threads which could be
+ // accessing the instrumentation when we delete it.
+ if (jit_.get() != nullptr) {
+ VLOG(jit) << "Deleting jit";
+ jit_.reset(nullptr);
+ }
+
// Shutdown the fault manager if it was initialized.
fault_manager.Shutdown();
@@ -455,17 +470,24 @@
started_ = true;
- // Use !IsCompiler so that we get test coverage, tests are never the zygote.
- if (!IsCompiler()) {
+ // Use !IsAotCompiler so that we get test coverage, tests are never the zygote.
+ if (!IsAotCompiler()) {
ScopedObjectAccess soa(self);
gc::space::ImageSpace* image_space = heap_->GetImageSpace();
if (image_space != nullptr) {
- Runtime::Current()->GetInternTable()->AddImageStringsToTable(image_space);
- Runtime::Current()->GetClassLinker()->MoveImageClassesToClassTable();
+ GetInternTable()->AddImageStringsToTable(image_space);
+ GetClassLinker()->MoveImageClassesToClassTable();
}
}
- if (!IsImageDex2OatEnabled() || !Runtime::Current()->GetHeap()->HasImageSpace()) {
+ // If we are the zygote then we need to wait until after forking to create the code cache due to
+ // SELinux restrictions on r/w/x memory regions.
+ if (!IsZygote() && jit_.get() != nullptr) {
+ jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold());
+ jit_->CreateThreadPool();
+ }
+
+ if (!IsImageDex2OatEnabled() || !GetHeap()->HasImageSpace()) {
ScopedObjectAccess soa(self);
StackHandleScope<1> hs(soa.Self());
auto klass(hs.NewHandle<mirror::Class>(mirror::Class::GetJavaLangClass()));
@@ -584,8 +606,14 @@
}
}
- // Create the thread pool.
+ // Create the thread pools.
heap_->CreateThreadPool();
+ if (jit_options_.get() != nullptr && jit_.get() == nullptr) {
+ // Create the JIT if the flag is set and we haven't already create it (happens for run-tests).
+ CreateJit();
+ jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold());
+ jit_->CreateThreadPool();
+ }
StartSignalCatcher();
@@ -806,6 +834,17 @@
Dbg::ConfigureJdwp(runtime_options.GetOrDefault(Opt::JdwpOptions));
}
+ if (!IsCompiler()) {
+ // If we are already the compiler at this point, we must be dex2oat. Don't create the jit in
+ // this case.
+ // If runtime_options doesn't have UseJIT set to true then CreateFromRuntimeArguments returns
+ // nullptr and we don't create the jit.
+ jit_options_.reset(jit::JitOptions::CreateFromRuntimeArguments(runtime_options));
+ }
+ if (!IsZygote() && jit_options_.get() != nullptr) {
+ CreateJit();
+ }
+
BlockSignals();
InitPlatformSignalHandlers();
@@ -1054,26 +1093,26 @@
env->NewGlobalRef(env->GetStaticObjectField(
WellKnownClasses::java_lang_ThreadGroup,
WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup));
- CHECK(main_thread_group_ != NULL || IsCompiler());
+ CHECK(main_thread_group_ != NULL || IsAotCompiler());
system_thread_group_ =
env->NewGlobalRef(env->GetStaticObjectField(
WellKnownClasses::java_lang_ThreadGroup,
WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup));
- CHECK(system_thread_group_ != NULL || IsCompiler());
+ CHECK(system_thread_group_ != NULL || IsAotCompiler());
}
jobject Runtime::GetMainThreadGroup() const {
- CHECK(main_thread_group_ != NULL || IsCompiler());
+ CHECK(main_thread_group_ != NULL || IsAotCompiler());
return main_thread_group_;
}
jobject Runtime::GetSystemThreadGroup() const {
- CHECK(system_thread_group_ != NULL || IsCompiler());
+ CHECK(system_thread_group_ != NULL || IsAotCompiler());
return system_thread_group_;
}
jobject Runtime::GetSystemClassLoader() const {
- CHECK(system_class_loader_ != NULL || IsCompiler());
+ CHECK(system_class_loader_ != NULL || IsAotCompiler());
return system_class_loader_;
}
@@ -1329,7 +1368,7 @@
// TODO: use a special method for imt conflict method saves.
method->SetDexMethodIndex(DexFile::kDexNoIndex);
// When compiling, the code pointer will get set later when the image is loaded.
- if (runtime->IsCompiler()) {
+ if (runtime->IsAotCompiler()) {
size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
} else {
@@ -1338,6 +1377,10 @@
return method.Get();
}
+void Runtime::SetImtConflictMethod(mirror::ArtMethod* method) {
+ imt_conflict_method_ = GcRoot<mirror::ArtMethod>(method);
+}
+
mirror::ArtMethod* Runtime::CreateResolutionMethod() {
Thread* self = Thread::Current();
Runtime* runtime = Runtime::Current();
@@ -1348,7 +1391,7 @@
// TODO: use a special method for resolution method saves
method->SetDexMethodIndex(DexFile::kDexNoIndex);
// When compiling, the code pointer will get set later when the image is loaded.
- if (runtime->IsCompiler()) {
+ if (runtime->IsAotCompiler()) {
size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
} else {
@@ -1479,14 +1522,14 @@
// Transaction support.
void Runtime::EnterTransactionMode(Transaction* transaction) {
- DCHECK(IsCompiler());
+ DCHECK(IsAotCompiler());
DCHECK(transaction != nullptr);
DCHECK(!IsActiveTransaction());
preinitialization_transaction_ = transaction;
}
void Runtime::ExitTransactionMode() {
- DCHECK(IsCompiler());
+ DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_ = nullptr;
}
@@ -1546,51 +1589,51 @@
void Runtime::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset,
uint32_t value, bool is_volatile) const {
- DCHECK(IsCompiler());
+ DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordWriteField32(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteField64(mirror::Object* obj, MemberOffset field_offset,
uint64_t value, bool is_volatile) const {
- DCHECK(IsCompiler());
+ DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordWriteField64(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset,
mirror::Object* value, bool is_volatile) const {
- DCHECK(IsCompiler());
+ DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordWriteFieldReference(obj, field_offset, value, is_volatile);
}
void Runtime::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const {
- DCHECK(IsCompiler());
+ DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordWriteArray(array, index, value);
}
void Runtime::RecordStrongStringInsertion(mirror::String* s) const {
- DCHECK(IsCompiler());
+ DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordStrongStringInsertion(s);
}
void Runtime::RecordWeakStringInsertion(mirror::String* s) const {
- DCHECK(IsCompiler());
+ DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordWeakStringInsertion(s);
}
void Runtime::RecordStrongStringRemoval(mirror::String* s) const {
- DCHECK(IsCompiler());
+ DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordStrongStringRemoval(s);
}
void Runtime::RecordWeakStringRemoval(mirror::String* s) const {
- DCHECK(IsCompiler());
+ DCHECK(IsAotCompiler());
DCHECK(IsActiveTransaction());
preinitialization_transaction_->RecordWeakStringRemoval(s);
}
@@ -1622,4 +1665,16 @@
void Runtime::UpdateProfilerState(int state) {
VLOG(profiler) << "Profiler state updated to " << state;
}
+
+void Runtime::CreateJit() {
+ CHECK(jit_options_.get() != nullptr);
+ std::string error_msg;
+ jit_.reset(jit::Jit::Create(jit_options_.get(), &error_msg));
+ if (jit_.get() != nullptr) {
+ compiler_callbacks_ = jit_->GetCompilerCallbacks();
+ } else {
+ LOG(WARNING) << "Failed to create JIT " << error_msg;
+ }
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index fb9ca40..3b6df51 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -48,6 +48,12 @@
class GarbageCollector;
} // namespace collector
} // namespace gc
+
+namespace jit {
+ class Jit;
+ class JitOptions;
+} // namespace jit
+
namespace mirror {
class ArtMethod;
class ClassLoader;
@@ -95,12 +101,18 @@
static bool Create(const RuntimeOptions& options, bool ignore_unrecognized)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_);
+ // IsAotCompiler for compilers that don't have a running runtime. Only dex2oat currently.
+ bool IsAotCompiler() const {
+ return !UseJit() && IsCompiler();
+ }
+
+ // IsCompiler is any runtime which has a running compiler, either dex2oat or JIT.
bool IsCompiler() const {
return compiler_callbacks_ != nullptr;
}
bool CanRelocate() const {
- return !IsCompiler() || compiler_callbacks_->IsRelocationPossible();
+ return !IsAotCompiler() || compiler_callbacks_->IsRelocationPossible();
}
bool ShouldRelocate() const {
@@ -339,9 +351,7 @@
return !imt_conflict_method_.IsNull();
}
- void SetImtConflictMethod(mirror::ArtMethod* method) {
- imt_conflict_method_ = GcRoot<mirror::ArtMethod>(method);
- }
+ void SetImtConflictMethod(mirror::ArtMethod* method);
void SetImtUnimplementedMethod(mirror::ArtMethod* method) {
imt_unimplemented_method_ = GcRoot<mirror::ArtMethod>(method);
}
@@ -421,6 +431,14 @@
kUnload,
kInitialize
};
+
+ jit::Jit* GetJit() {
+ return jit_.get();
+ }
+ bool UseJit() const {
+ return jit_.get() != nullptr;
+ }
+
void PreZygoteFork();
bool InitZygote();
void DidForkFromZygote(JNIEnv* env, NativeBridgeAction action, const char* isa);
@@ -521,6 +539,8 @@
return zygote_max_failed_boots_;
}
+ void CreateJit();
+
private:
static void InitPlatformSignalHandlers();
@@ -600,6 +620,9 @@
JavaVMExt* java_vm_;
+ std::unique_ptr<jit::Jit> jit_;
+ std::unique_ptr<jit::JitOptions> jit_options_;
+
// Fault message, printed when we get a SIGSEGV.
Mutex fault_message_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
std::string fault_message_ GUARDED_BY(fault_message_lock_);
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 71a0152..4d74d61 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -63,6 +63,9 @@
RUNTIME_OPTIONS_KEY (Unit, LowMemoryMode)
RUNTIME_OPTIONS_KEY (bool, UseTLAB, false)
RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true)
+RUNTIME_OPTIONS_KEY (bool, UseJIT, false)
+RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold, jit::Jit::kDefaultCompileThreshold)
+RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheCapacity, jit::JitCodeCache::kDefaultCapacity)
RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
HSpaceCompactForOOMMinIntervalsMs,\
MsToNs(100 * 1000)) // 100s
diff --git a/runtime/runtime_options.h b/runtime/runtime_options.h
index ebd52d7..7e59000 100644
--- a/runtime/runtime_options.h
+++ b/runtime/runtime_options.h
@@ -26,6 +26,8 @@
#include "runtime/base/logging.h"
#include "cmdline/unit.h"
#include "jdwp/jdwp.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
#include "profiler_options.h"
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index e377542..26bf655 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -180,7 +180,7 @@
Runtime* runtime = Runtime::Current();
CHECK(runtime->AttachCurrentThread("Signal Catcher", true, runtime->GetSystemThreadGroup(),
- !runtime->IsCompiler()));
+ !runtime->IsAotCompiler()));
Thread* self = Thread::Current();
DCHECK_NE(self->GetState(), kRunnable);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 3b48f49..79d0066 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -541,7 +541,7 @@
// Set stack_end_ to the bottom of the stack saving space of stack overflows
Runtime* runtime = Runtime::Current();
- bool implicit_stack_check = !runtime->ExplicitStackOverflowChecks() && !runtime->IsCompiler();
+ bool implicit_stack_check = !runtime->ExplicitStackOverflowChecks() && !runtime->IsAotCompiler();
ResetDefaultStackEnd();
// Install the protected region if we are doing implicit overflow checks.
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 0950abe..93b3877 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -298,7 +298,7 @@
intptr_t interval_us = reinterpret_cast<intptr_t>(arg);
CHECK_GE(interval_us, 0);
CHECK(runtime->AttachCurrentThread("Sampling Profiler", true, runtime->GetSystemThreadGroup(),
- !runtime->IsCompiler()));
+ !runtime->IsAotCompiler()));
while (true) {
usleep(interval_us);
@@ -627,6 +627,12 @@
LOG(ERROR) << "Unexpected exception caught event in tracing";
}
+void Trace::BackwardBranch(Thread* /*thread*/, mirror::ArtMethod* method,
+ int32_t /*dex_pc_offset*/)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ LOG(ERROR) << "Unexpected backward branch event in tracing" << PrettyMethod(method);
+}
+
void Trace::ReadClocks(Thread* thread, uint32_t* thread_clock_diff, uint32_t* wall_clock_diff) {
if (UseThreadCpuClock()) {
uint64_t clock_base = thread->GetTraceClockBase();
diff --git a/runtime/trace.h b/runtime/trace.h
index ead1c29..9ba30d5 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -99,7 +99,8 @@
mirror::ArtMethod* catch_method, uint32_t catch_dex_pc,
mirror::Throwable* exception_object)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE;
-
+ void BackwardBranch(Thread* thread, mirror::ArtMethod* method, int32_t dex_pc_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE;
// Reuse an old stack trace if it exists, otherwise allocate a new one.
static std::vector<mirror::ArtMethod*>* AllocStackTrace();
// Clear and store an old stack trace for later use.
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 7e2e0a6..e26f955 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -32,7 +32,7 @@
Transaction::Transaction()
: log_lock_("transaction log lock", kTransactionLogLock), aborted_(false) {
- CHECK(Runtime::Current()->IsCompiler());
+ CHECK(Runtime::Current()->IsAotCompiler());
}
Transaction::~Transaction() {
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 85c9340..dea8f79 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1262,9 +1262,9 @@
os << "+" << it->func_offset;
}
try_addr2line = true;
- } else if (current_method != nullptr &&
- Locks::mutator_lock_->IsSharedHeld(Thread::Current()) &&
- current_method->PcIsWithinQuickCode(it->pc)) {
+ } else if (
+ current_method != nullptr && Locks::mutator_lock_->IsSharedHeld(Thread::Current()) &&
+ current_method->PcIsWithinQuickCode(it->pc)) {
const void* start_of_code = current_method->GetEntryPointFromQuickCompiledCode();
os << JniLongName(current_method) << "+"
<< (it->pc - reinterpret_cast<uintptr_t>(start_of_code));
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 474a066..87a29ed 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -24,6 +24,7 @@
#include "compiler_callbacks.h"
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
+#include "dex_instruction_utils.h"
#include "dex_instruction_visitor.h"
#include "gc/accounting/card_table-inl.h"
#include "indenter.h"
@@ -111,6 +112,20 @@
reg_line->MarkAllRegistersAsConflicts(verifier);
}
+MethodVerifier::FailureKind MethodVerifier::VerifyMethod(
+ mirror::ArtMethod* method, bool allow_soft_failures, std::string* error ATTRIBUTE_UNUSED) {
+ Thread* self = Thread::Current();
+ StackHandleScope<3> hs(self);
+ mirror::Class* klass = method->GetDeclaringClass();
+ auto h_dex_cache(hs.NewHandle(klass->GetDexCache()));
+ auto h_class_loader(hs.NewHandle(klass->GetClassLoader()));
+ auto h_method = hs.NewHandle(method);
+ return VerifyMethod(self, method->GetDexMethodIndex(), method->GetDexFile(), h_dex_cache,
+ h_class_loader, klass->GetClassDef(), method->GetCodeItem(), h_method,
+ method->GetAccessFlags(), allow_soft_failures, false);
+}
+
+
MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self,
mirror::Class* klass,
bool allow_soft_failures,
@@ -136,7 +151,7 @@
}
if (early_failure) {
*error = "Verifier rejected class " + PrettyDescriptor(klass) + failure_message;
- if (Runtime::Current()->IsCompiler()) {
+ if (Runtime::Current()->IsAotCompiler()) {
ClassReference ref(&dex_file, klass->GetDexClassDefIndex());
Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
}
@@ -544,7 +559,7 @@
case VERIFY_ERROR_ACCESS_METHOD:
case VERIFY_ERROR_INSTANTIATION:
case VERIFY_ERROR_CLASS_CHANGE:
- if (Runtime::Current()->IsCompiler() || !can_load_classes_) {
+ if (Runtime::Current()->IsAotCompiler() || !can_load_classes_) {
// If we're optimistically running verification at compile time, turn NO_xxx, ACCESS_xxx,
// class change and instantiation errors into soft verification errors so that we re-verify
// at runtime. We may fail to find or to agree on access because of not yet available class
@@ -568,7 +583,7 @@
// Hard verification failures at compile time will still fail at runtime, so the class is
// marked as rejected to prevent it from being compiled.
case VERIFY_ERROR_BAD_CLASS_HARD: {
- if (Runtime::Current()->IsCompiler()) {
+ if (Runtime::Current()->IsAotCompiler()) {
ClassReference ref(dex_file_, dex_file_->GetIndexForClassDef(*class_def_));
Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
}
@@ -844,7 +859,7 @@
result = false;
break;
}
- if (inst->GetVerifyIsRuntimeOnly() && Runtime::Current()->IsCompiler() && !verify_to_dump_) {
+ if (inst->GetVerifyIsRuntimeOnly() && Runtime::Current()->IsAotCompiler() && !verify_to_dump_) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "opcode only expected at runtime " << inst->Name();
result = false;
}
@@ -2812,8 +2827,8 @@
}
if (have_pending_hard_failure_) {
- if (Runtime::Current()->IsCompiler()) {
- /* When compiling, check that the last failure is a hard failure */
+ if (Runtime::Current()->IsAotCompiler()) {
+ /* When AOT compiling, check that the last failure is a hard failure */
CHECK_EQ(failures_[failures_.size() - 1], VERIFY_ERROR_BAD_CLASS_HARD);
}
/* immediate failure, reject class */
@@ -3941,28 +3956,16 @@
mirror::ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst,
RegisterLine* reg_line) {
- DCHECK(inst->Opcode() == Instruction::IGET_QUICK ||
- inst->Opcode() == Instruction::IGET_WIDE_QUICK ||
- inst->Opcode() == Instruction::IGET_OBJECT_QUICK ||
- inst->Opcode() == Instruction::IGET_BOOLEAN_QUICK ||
- inst->Opcode() == Instruction::IGET_BYTE_QUICK ||
- inst->Opcode() == Instruction::IGET_CHAR_QUICK ||
- inst->Opcode() == Instruction::IGET_SHORT_QUICK ||
- inst->Opcode() == Instruction::IPUT_QUICK ||
- inst->Opcode() == Instruction::IPUT_WIDE_QUICK ||
- inst->Opcode() == Instruction::IPUT_OBJECT_QUICK ||
- inst->Opcode() == Instruction::IPUT_BOOLEAN_QUICK ||
- inst->Opcode() == Instruction::IPUT_BYTE_QUICK ||
- inst->Opcode() == Instruction::IPUT_CHAR_QUICK ||
- inst->Opcode() == Instruction::IPUT_SHORT_QUICK);
+ DCHECK(IsInstructionIGetQuickOrIPutQuick(inst->Opcode())) << inst->Opcode();
const RegType& object_type = reg_line->GetRegisterType(this, inst->VRegB_22c());
if (!object_type.HasClass()) {
VLOG(verifier) << "Failed to get mirror::Class* from '" << object_type << "'";
return nullptr;
}
uint32_t field_offset = static_cast<uint32_t>(inst->VRegC_22c());
- mirror::ArtField* f = mirror::ArtField::FindInstanceFieldWithOffset(object_type.GetClass(),
- field_offset);
+ mirror::ArtField* const f = mirror::ArtField::FindInstanceFieldWithOffset(object_type.GetClass(),
+ field_offset);
+ DCHECK_EQ(f->GetOffset().Uint32Value(), field_offset);
if (f == nullptr) {
VLOG(verifier) << "Failed to find instance field at offset '" << field_offset
<< "' from '" << PrettyDescriptor(object_type.GetClass()) << "'";
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index b83e647..bdd6259 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -156,6 +156,9 @@
uint32_t method_access_flags)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static FailureKind VerifyMethod(mirror::ArtMethod* method, bool allow_soft_failures,
+ std::string* error) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
uint8_t EncodePcToReferenceMapData() const;
uint32_t DexFileVersion() const {
@@ -239,10 +242,14 @@
bool HasFailures() const;
const RegType& ResolveCheckedClass(uint32_t class_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::ArtMethod* GetQuickInvokedMethod(const Instruction* inst,
- RegisterLine* reg_line,
+ // Returns the method of a quick invoke or nullptr if it cannot be found.
+ mirror::ArtMethod* GetQuickInvokedMethod(const Instruction* inst, RegisterLine* reg_line,
bool is_range)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Returns the access field of a quick field access (iget/iput-quick) or nullptr
+ // if it cannot be found.
+ mirror::ArtField* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Is the method being verified a constructor?
bool IsConstructor() const {
@@ -532,11 +539,6 @@
bool is_primitive, bool is_static)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Returns the access field of a quick field access (iget/iput-quick) or nullptr
- // if it cannot be found.
- mirror::ArtField* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
template <FieldAccessType kAccType>
void VerifyQuickFieldAccess(const Instruction* inst, const RegType& insn_type, bool is_primitive)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);