Revert^2 "VIXL simulator for ART (Stage1)"

This reverts commit 3060bb919cd2f37c6a97e87c1581ac5294af72b3.

Reason for revert: relanding original change. The fix is setting
`device_supported: false` for libart(d)-simulator module in the .bp
file (`m checkbuild` attempted to build it for arm32 and failed).
Original commit message:

VIXL simulator for ART (Stage1)

Quick User Guide: test/README.simulator.md

This CL enables running ART run-tests in a simulator on host machine.
Some benefits of using this simulator approach:
- No need to use a target device at all.
  Save developers from solving the device troubles: build, flash, usb,
  adb, etc.
- Speed up development/debug/test cycle.
- Allows easy debugging/testing new instruction features without real
  hardware.
- Allows using a smaller AOSP Android manifest master-art.

The Stage1 CL provides support for running 30% of current run-tests.
The rest unsupported test cases are kept in knownfailures.json.

Future work will be supporting proper stack frame layout between
simulator and quick entrypoints, so that stack walk,
QuickArgumentVisitor, deoptimization, etc can be supported.

This CL adds libart(d)-simulator-container library to the ART APEX. It
has caused the following increase of the APEX size (small, about 0.13%
for release APEX, measured for target aosp_arm64-userdebug):
 Before:
   88992 com.android.art.debug.apex
   51612 com.android.art.release.apex
  112352 com.android.art.testing.apex
 After:
   89124 com.android.art.debug.apex
   51680 com.android.art.release.apex
  112468 com.android.art.testing.apex

Change-Id: I461c80aa9c4ce0673eef1c0254d2c539f2b6a8d5
Test: art/test.py --run-test --optimizing --simulate-arm64
Test: art/test.py --run-test --optimizing --host
Test: m test-art-host-gtest
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index 1a337cb..021fd5a 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -546,6 +546,8 @@
     self._checker.check_native_library('libopenjdkjvmti')
     self._checker.check_native_library('libprofile')
     self._checker.check_native_library('libsigchain')
+    # Only on ARM/ARM64
+    self._checker.check_optional_native_library('libart-simulator-container')
 
     # Check java libraries for Managed Core Library.
     self._checker.check_java_library('apache-xml')
@@ -688,6 +690,8 @@
     self._checker.check_native_library('libopenjdkjvmd')
     self._checker.check_native_library('libopenjdkjvmtid')
     self._checker.check_native_library('libprofiled')
+    # Only on ARM/ARM64
+    self._checker.check_optional_native_library('libartd-simulator-container')
 
     # Check internal libraries for Managed Core Library.
     self._checker.check_native_library('libopenjdkd')
@@ -759,7 +763,6 @@
 
     # Check ART test (internal) libraries.
     self._checker.check_native_library('libart-gtest')
-    self._checker.check_native_library('libartd-simulator-container')
 
     # Check ART test tools.
     self._checker.check_executable('signal_dumper')
diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h
index 9d15f1f..f873e75 100644
--- a/compiler/optimizing/codegen_test_utils.h
+++ b/compiler/optimizing/codegen_test_utils.h
@@ -223,12 +223,15 @@
                                 Expected expected) {
   ASSERT_TRUE(CanExecute(target_isa)) << "Target isa is not executable.";
 
-  // Verify on simulator.
-  CodeSimulatorContainer simulator(target_isa);
-  if (simulator.CanSimulate()) {
-    Expected result = SimulatorExecute<Expected>(simulator.Get(), f);
-    if (has_result) {
-      ASSERT_EQ(expected, result);
+  // Simulator cannot run without runtime, because it needs quick entrypoints.
+  if (Runtime::Current() != nullptr) {
+    // Verify on simulator.
+    CodeSimulatorContainer simulator(target_isa);
+    if (simulator.CanSimulate()) {
+      Expected result = SimulatorExecute<Expected>(simulator.Get(), f);
+      if (has_result) {
+        ASSERT_EQ(expected, result);
+      }
     }
   }
 
diff --git a/runtime/Android.bp b/runtime/Android.bp
index e3200c4..f82a623 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -410,6 +410,7 @@
     export_generated_headers: ["cpp-define-generator-asm-support"],
     header_libs: [
         "art_cmdlineparser_headers",
+        "libart_simulator_headers",
         "cpp-define-generator-definitions",
         "jni_platform_headers",
         "libnativehelper_header_only",
@@ -436,6 +437,7 @@
     name: "libart_static_base_defaults",
     static_libs: [
         "libartpalette",
+        "libart-simulator-container",
         "libbacktrace",
         "libbase",
         "liblog",
@@ -529,6 +531,7 @@
     ],
     shared_libs: [
         "libartbase",
+        "libart-simulator-container",
         "libdexfile",
         // We need to eagerly load it so libdexfile_support used from libunwindstack can find it.
         "libdexfile_external",
@@ -563,6 +566,7 @@
     ],
     shared_libs: [
         "libartbased",
+        "libartd-simulator-container",
         "libdexfiled",
         // We need to eagerly preload it, so that libunwindstack can find it.
         // Otherwise, it would try to load the non-debug version with dlopen.
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 2db2faa..3c70a92 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -20,13 +20,14 @@
 #include <cstddef>
 
 #include "android-base/stringprintf.h"
-
 #include "arch/context.h"
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "base/stl_util.h"
 #include "class_linker-inl.h"
 #include "class_root-inl.h"
+#include "code_simulator.h"
+#include "code_simulator_container.h"
 #include "debugger.h"
 #include "dex/class_accessor-inl.h"
 #include "dex/descriptors_names.h"
@@ -101,6 +102,11 @@
   return reinterpret_cast<ArtMethod*>(GetDataPtrSize(pointer_size));
 }
 
+bool ArtMethod::CanBeSimulated() REQUIRES_SHARED(Locks::mutator_lock_) {
+  CodeSimulatorContainer* simulator = Thread::Current()->GetSimulator();
+  return simulator->Get()->CanSimulate(this);
+}
+
 ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa,
                                           jobject jlr_method) {
   ObjPtr<mirror::Executable> executable = soa.Decode<mirror::Executable>(jlr_method);
@@ -355,7 +361,9 @@
       }
 
       // Ensure that we won't be accidentally calling quick compiled code when -Xint.
-      if (kIsDebugBuild && runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
+      if (kIsDebugBuild &&
+          runtime->GetInstrumentation()->IsForcedInterpretOnly() &&
+          !Runtime::SimulatorMode()) {
         CHECK(!runtime->UseJitCompilation());
         const void* oat_quick_code =
             (IsNative() || !IsInvokable() || IsProxyMethod() || IsObsolete())
@@ -365,7 +373,10 @@
             << "Don't call compiled code when -Xint " << PrettyMethod();
       }
 
-      if (!IsStatic()) {
+      if (Runtime::SimulatorMode() && CanBeSimulated()) {
+        CodeSimulatorContainer* simulator = Thread::Current()->GetSimulator();
+        simulator->Get()->Invoke(this, args, args_size, self, result, shorty, IsStatic());
+      } else if (!IsStatic()) {
         (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
       } else {
         (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
@@ -570,6 +581,10 @@
     return nullptr;
   }
 
+  if (Runtime::SimulatorMode()) {
+    return nullptr;
+  }
+
   Runtime* runtime = Runtime::Current();
   const void* existing_entry_point = GetEntryPointFromQuickCompiledCode();
   CHECK(existing_entry_point != nullptr) << PrettyMethod() << "@" << this;
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 16b4648..7f6004a 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -384,6 +384,8 @@
     ClearAccessFlags(kAccSkipAccessChecks);
   }
 
+  bool CanBeSimulated() REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Returns true if this method could be overridden by a default method.
   bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 8bf38d3..3ea4a8d 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2185,9 +2185,14 @@
     header.VisitPackedArtMethods([&](ArtMethod& method) REQUIRES_SHARED(Locks::mutator_lock_) {
       if (!method.IsRuntimeMethod()) {
         DCHECK(method.GetDeclaringClass() != nullptr);
-        if (!method.IsNative() && !method.IsResolutionMethod()) {
-          method.SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(),
-                                                            image_pointer_size_);
+        if (!method.IsResolutionMethod()) {
+          if (!method.IsNative()) {
+            method.SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(),
+                                                             image_pointer_size_);
+          } else if (Runtime::SimulatorMode()) {
+            method.SetEntryPointFromQuickCompiledCodePtrSize(GetQuickGenericJniStub(),
+                                                             image_pointer_size_);
+          }
         }
       }
     }, space->Begin(), image_pointer_size_);
@@ -3554,6 +3559,10 @@
     return true;
   }
 
+  if (Runtime::SimulatorMode()) {
+    return !method->CanBeSimulated();
+  }
+
   Runtime* runtime = Runtime::Current();
   instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
   if (instr->InterpretOnly()) {
@@ -3671,7 +3680,7 @@
     }
 
     // Check whether the method is native, in which case it's generic JNI.
-    if (quick_code == nullptr && method->IsNative()) {
+    if ((Runtime::SimulatorMode() || quick_code == nullptr) && method->IsNative()) {
       quick_code = GetQuickGenericJniStub();
     } else if (ShouldUseInterpreterEntrypoint(method, quick_code)) {
       // Use interpreter entry point.
@@ -3731,7 +3740,7 @@
   // Note: this mimics the logic in image_writer.cc that installs the resolution
   // stub only if we have compiled code and the method needs a class initialization
   // check.
-  if (quick_code == nullptr) {
+  if (quick_code == nullptr || Runtime::SimulatorMode()) {
     method->SetEntryPointFromQuickCompiledCode(
         method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge());
   } else if (enter_interpreter) {
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 6bd1c8f..88992f1 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -31,6 +31,7 @@
 #include "base/utils.h"
 #include "elf/elf_utils.h"
 #include "elf_file_impl.h"
+#include "runtime.h"
 
 namespace art {
 
@@ -1101,9 +1102,9 @@
 
   if (executable) {
     InstructionSet elf_ISA = GetInstructionSetFromELF(GetHeader().e_machine, GetHeader().e_flags);
-    if (elf_ISA != kRuntimeISA) {
+    if (elf_ISA != Runtime::GetQuickCodeISA()) {
       std::ostringstream oss;
-      oss << "Expected ISA " << kRuntimeISA << " but found " << elf_ISA;
+      oss << "Expected ISA " << Runtime::GetQuickCodeISA() << " but found " << elf_ISA;
       *error_msg = oss.str();
       return false;
     }
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 52c4142..e3f0d00 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -136,12 +136,12 @@
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_mark_stack, async_exception, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, async_exception, top_reflective_handle_scope,
                         sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, top_reflective_handle_scope, simulator, sizeof(void*));
     // The first field after tlsPtr_ is forced to a 16 byte alignment so it might have some space.
     auto offset_tlsptr_end = OFFSETOF_MEMBER(Thread, tlsPtr_) +
         sizeof(decltype(reinterpret_cast<Thread*>(16)->tlsPtr_));
-    CHECKED(offset_tlsptr_end - OFFSETOF_MEMBER(Thread, tlsPtr_.top_reflective_handle_scope) ==
-                sizeof(void*),
-            "async_exception last field");
+    CHECKED(offset_tlsptr_end - OFFSETOF_MEMBER(Thread, tlsPtr_.simulator) == sizeof(void*),
+            "simulator last field");
   }
 
   void CheckJniEntryPoints() {
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index c6d3258..01a6213 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -150,6 +150,9 @@
       runtime->IsStarted() &&
       !runtime->IsAotCompiler() &&
       !runtime->GetInstrumentation()->IsActive() &&
+      // In simulator mode, mterp and its fast path are avoided to ensure every
+      // called method can go through ArtMethod::Invoke().
+      !Runtime::SimulatorMode() &&
       // mterp only knows how to deal with the normal exits. It cannot handle any of the
       // non-standard force-returns.
       !runtime->AreNonStandardExitsEnabled() &&
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 11e02a2..27dd970 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -143,8 +143,10 @@
     const DexFile* dex_file = path[i];
 
     // For multidex locations, e.g., x.jar!classes2.dex, we want to look into x.jar.
-    const std::string location(DexFileLoader::GetBaseLocation(dex_file->GetLocation()));
-
+    std::string location(DexFileLoader::GetBaseLocation(dex_file->GetLocation()));
+    if (Runtime::SimulatorMode()) {
+      location = getenv("ANDROID_PRODUCT_OUT") + location;
+    }
     ScopedLocalRef<jstring> javaPath(env, env->NewStringUTF(location.c_str()));
     if (javaPath.get() == nullptr) {
       DCHECK(env->ExceptionCheck());
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 9c169e6..de3d878 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -111,9 +111,9 @@
 
   dex_location_.assign(dex_location);
 
-  if (load_executable_ && isa != kRuntimeISA) {
+  if (load_executable_ && isa != Runtime::GetQuickCodeISA()) {
     LOG(WARNING) << "OatFileAssistant: Load executable specified, "
-      << "but isa is not kRuntimeISA. Will not attempt to load executable.";
+      << "but isa is not executable isa. Will not attempt to load executable.";
     load_executable_ = false;
   }
 
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 85992ea..1480c1d 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -236,10 +236,10 @@
     std::unique_ptr<ClassLoaderContext> context(
         ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements));
 
-    OatFileAssistant oat_file_assistant(dex_location,
-                                        kRuntimeISA,
-                                        runtime->GetOatFilesExecutable(),
-                                        only_use_system_oat_files_);
+  OatFileAssistant oat_file_assistant(dex_location,
+                                      Runtime::GetQuickCodeISA(),
+                                      runtime->GetOatFilesExecutable(),
+                                      only_use_system_oat_files_);
 
     // Get the oat file on disk.
     std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 0a52e7e..dab0f55 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -422,6 +422,11 @@
           .WithType<bool>()
           .WithValueMap({{"false", false}, {"true", true}})
           .IntoKey(M::PerfettoHprof)
+      .Define("--simulate-isa=_")
+          .WithType<InstructionSet>()
+          .WithValueMap({{"none",  InstructionSet::kNone},
+                         {"arm64", InstructionSet::kArm64}})
+          .IntoKey(M::SimulateInstructionSet)
       .Ignore({
           "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
           "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index 8873eb9..60a0837 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -109,6 +109,7 @@
   EXPECT_FALSE(VLOG_IS_ON(startup));
   EXPECT_FALSE(VLOG_IS_ON(third_party_jni));
   EXPECT_FALSE(VLOG_IS_ON(threads));
+  EXPECT_PARSED_EQ(InstructionSet::kNone, Opt::SimulateInstructionSet);
 
   auto&& properties_list = map.GetOrDefault(Opt::PropertiesList);
   ASSERT_EQ(2U, properties_list.size());
@@ -181,4 +182,18 @@
   }
 }
 
+TEST_F(ParsedOptionsTest, ParsedOptionSimulateInstructionSet) {
+  RuntimeOptions options;
+  options.push_back(std::make_pair("--simulate-isa=arm64", nullptr));
+
+  RuntimeArgumentMap map;
+  bool parsed = ParsedOptions::Parse(options, false, &map);
+  ASSERT_TRUE(parsed);
+  ASSERT_NE(0u, map.Size());
+
+  using Opt = RuntimeArgumentMap;
+
+  EXPECT_PARSED_EQ(InstructionSet::kArm64, Opt::SimulateInstructionSet);
+}
+
 }  // namespace art
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index fc1a3c8..79b51da 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -69,6 +69,7 @@
 #include "base/utils.h"
 #include "class_linker-inl.h"
 #include "class_root-inl.h"
+#include "code_simulator_container.h"
 #include "compiler_callbacks.h"
 #include "debugger.h"
 #include "dex/art_dex_file_loader.h"
@@ -230,6 +231,7 @@
       imt_conflict_method_(nullptr),
       imt_unimplemented_method_(nullptr),
       instruction_set_(InstructionSet::kNone),
+      simulate_isa_(InstructionSet::kNone),
       compiler_callbacks_(nullptr),
       is_zygote_(false),
       is_primary_zygote_(false),
@@ -1340,7 +1342,9 @@
 
   fingerprint_ = runtime_options.ReleaseOrDefault(Opt::Fingerprint);
 
-  if (runtime_options.GetOrDefault(Opt::Interpret)) {
+  if (runtime_options.GetOrDefault(Opt::Interpret) ||
+      runtime_options.GetOrDefault(Opt::SimulateInstructionSet) != InstructionSet::kNone) {
+    // Both -Xint and --simulate-isa options force interpreter only.
     GetInstrumentation()->ForceInterpretOnly();
   }
 
@@ -1376,6 +1380,12 @@
 
   image_space_loading_order_ = runtime_options.GetOrDefault(Opt::ImageSpaceLoadingOrder);
 
+  instruction_set_ = runtime_options.GetOrDefault(Opt::ImageInstructionSet);
+  SetInstructionSet(instruction_set_);
+
+  InstructionSet simulate_isa = runtime_options.GetOrDefault(Opt::SimulateInstructionSet);
+  SetSimulateISA(simulate_isa);
+
   heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
                        runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
                        runtime_options.GetOrDefault(Opt::HeapMinFree),
@@ -1388,7 +1398,7 @@
                        GetBootClassPath(),
                        GetBootClassPathLocations(),
                        image_location_,
-                       instruction_set_,
+                       GetQuickCodeISA(),
                        // Override the collector type to CC if the read barrier config.
                        kUseReadBarrier ? gc::kCollectorTypeCC : xgc_option.collector_type_,
                        kUseReadBarrier ? BackgroundGcOption(gc::kCollectorTypeCCBackground)
@@ -1616,7 +1626,6 @@
     }
 
     // TODO: Should we move the following to InitWithoutImage?
-    SetInstructionSet(instruction_set_);
     for (uint32_t i = 0; i < kCalleeSaveSize; i++) {
       CalleeSaveType type = CalleeSaveType(i);
       if (!HasCalleeSaveMethod(type)) {
@@ -2367,6 +2376,24 @@
   }
 }
 
+InstructionSet Runtime::GetQuickCodeISA() {
+  Runtime* runtime = Runtime::Current();
+  if (runtime == nullptr) {
+    return kRuntimeISA;
+  }
+
+  // In simulator mode, image ISA should align with simulator.
+  if (SimulatorMode()) {
+    return runtime->GetSimulateISA();
+  }
+
+  // Otherwise, image ISA should align with runtime.
+  if (runtime->GetInstructionSet() == InstructionSet::kNone) {
+    return kRuntimeISA;
+  }
+  return runtime->GetInstructionSet();
+}
+
 void Runtime::SetInstructionSet(InstructionSet instruction_set) {
   instruction_set_ = instruction_set;
   switch (instruction_set) {
@@ -2389,6 +2416,16 @@
   instruction_set_ = InstructionSet::kNone;
 }
 
+void Runtime::SetSimulateISA(InstructionSet instruction_set) {
+  DCHECK(GetInstructionSet() != InstructionSet::kNone) << "Simulator ISA set before runtime ISA.";
+  if (instruction_set == InstructionSet::kNone) {
+    return;
+  }
+  CodeSimulatorContainer simulator(instruction_set);
+  DCHECK(simulator.CanSimulate()) << "Fail to set simulator isa: " << instruction_set;
+  simulate_isa_ = instruction_set;
+}
+
 void Runtime::SetCalleeSaveMethod(ArtMethod* method, CalleeSaveType type) {
   DCHECK_LT(static_cast<uint32_t>(type), kCalleeSaveSize);
   CHECK(method != nullptr);
@@ -2625,8 +2662,12 @@
   // Make the dex2oat instruction set match that of the launching runtime. If we have multiple
   // architecture support, dex2oat may be compiled as a different instruction-set than that
   // currently being executed.
+  // In simulator mode, the dex2oat instruction set should match the simulator, so that we can
+  // compile for simulating ISA.
+  InstructionSet target_isa =
+      (simulate_isa_ == InstructionSet::kNone) ? kRuntimeISA : simulate_isa_;
   std::string instruction_set("--instruction-set=");
-  instruction_set += GetInstructionSetString(kRuntimeISA);
+  instruction_set += GetInstructionSetString(target_isa);
   argv->push_back(instruction_set);
 
   if (InstructionSetFeatures::IsRuntimeDetectionSupported()) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 594edaa..0ee280c 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -461,6 +461,10 @@
     return OFFSETOF_MEMBER(Runtime, callee_save_methods_[static_cast<size_t>(type)]);
   }
 
+  // There are different kinds of ISAs in runtime:
+  //   1) Runtime ISA: for compiler, frame information
+  //   2) Quick code ISA: for image loading
+  //   3) Simulate ISA: for simulator
   InstructionSet GetInstructionSet() const {
     return instruction_set_;
   }
@@ -468,6 +472,26 @@
   void SetInstructionSet(InstructionSet instruction_set);
   void ClearInstructionSet();
 
+  static InstructionSet GetQuickCodeISA();
+
+  void SetSimulateISA(InstructionSet instruction_set);
+
+  InstructionSet GetSimulateISA() const {
+    return simulate_isa_;
+  }
+
+  static inline bool SimulatorMode() {
+    if (kIsDebugBuild) {
+      Runtime* runtime = Current();
+      // Disable simulator for compiler.
+      if (runtime == nullptr || runtime->IsCompiler()) {
+        return false;
+      }
+      return runtime->GetSimulateISA() != InstructionSet::kNone;
+    }
+    return false;
+  }
+
   void SetCalleeSaveMethod(ArtMethod* method, CalleeSaveType type);
   void ClearCalleeSaveMethods();
 
@@ -1046,6 +1070,10 @@
 
   InstructionSet instruction_set_;
 
+  // The ISA of code we are simulating. If it is not kNone, we will run the code with that ISA and
+  // run with a simulator. The code might come from JIT compiler or a pre-built image.
+  InstructionSet simulate_isa_;
+
   CompilerCallbacks* compiler_callbacks_;
   bool is_zygote_;
   bool is_primary_zygote_;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index a342973..ce5a16b 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -139,6 +139,8 @@
 RUNTIME_OPTIONS_KEY (std::list<ti::AgentSpec>,         AgentLib)  // -agentlib:<libname>=<options>
 RUNTIME_OPTIONS_KEY (std::list<ti::AgentSpec>,         AgentPath)  // -agentpath:<libname>=<options>
 RUNTIME_OPTIONS_KEY (std::vector<Plugin>,            Plugins)  // -Xplugin:<library>
+// Simulator is disabled by default.
+RUNTIME_OPTIONS_KEY (InstructionSet,      SimulateInstructionSet,         InstructionSet::kNone)  // --simulate-isa=_
 
 // Not parse-able from command line, but can be provided explicitly.
 // (Do not add anything here that is defined in ParsedOptions::MakeParser)
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 21b8d05..b71c41e 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -57,6 +57,8 @@
 #include "base/utils.h"
 #include "class_linker-inl.h"
 #include "class_root-inl.h"
+#include "code_simulator.h"
+#include "code_simulator_container.h"
 #include "debugger.h"
 #include "dex/descriptors_names.h"
 #include "dex/dex_file-inl.h"
@@ -164,6 +166,15 @@
   UpdateReadBarrierEntrypoints(&tlsPtr_.quick_entrypoints, /* is_active= */ is_marking);
 }
 
+void Thread::InitSimulator() {
+  tlsPtr_.simulator = new CodeSimulatorContainer(Runtime::Current()->GetSimulateISA());
+}
+
+CodeSimulatorContainer* Thread::GetSimulator() {
+  DCHECK(tlsPtr_.simulator != nullptr);
+  return tlsPtr_.simulator;
+}
+
 void Thread::InitTlsEntryPoints() {
   ScopedTrace trace("InitTlsEntryPoints");
   // Insert a placeholder so we can easily tell if we call an unimplemented entry point.
@@ -174,6 +185,14 @@
     *it = reinterpret_cast<uintptr_t>(UnimplementedEntryPoint);
   }
   InitEntryPoints(&tlsPtr_.jni_entrypoints, &tlsPtr_.quick_entrypoints);
+
+  // Initialize entry points for simulator because some entry points are not needed in normal run,
+  // but required in simulator mode.
+  if (Runtime::SimulatorMode()) {
+    CodeSimulatorContainer *simulator = GetSimulator();
+    DCHECK(simulator->CanSimulate());
+    simulator->Get()->InitEntryPoints(&tlsPtr_.quick_entrypoints);
+  }
 }
 
 void Thread::ResetQuickAllocEntryPointsForThread() {
@@ -934,6 +953,9 @@
     return false;
   }
   InitCpu();
+  if (Runtime::SimulatorMode()) {
+    InitSimulator();
+  }
   InitTlsEntryPoints();
   RemoveSuspendTrigger();
   InitCardTable();
@@ -2488,6 +2510,10 @@
     CleanupCpu();
   }
 
+  if (tlsPtr_.simulator != nullptr) {
+    delete tlsPtr_.simulator;
+  }
+
   delete tlsPtr_.instrumentation_stack;
   delete tlsPtr_.name;
   delete tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample;
diff --git a/runtime/thread.h b/runtime/thread.h
index d2833b0..c2416e6 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -49,6 +49,8 @@
 
 namespace art {
 
+class CodeSimulatorContainer;
+
 namespace gc {
 namespace accounting {
 template<class T> class AtomicStack;
@@ -192,6 +194,8 @@
   // TODO: mark as PURE so the compiler may coalesce and remove?
   static Thread* Current();
 
+  CodeSimulatorContainer* GetSimulator();
+
   // On a runnable thread, check for pending thread suspension request and handle if pending.
   void AllowThreadSuspension() REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -1419,6 +1423,7 @@
       REQUIRES(Locks::runtime_shutdown_lock_);
   void InitCardTable();
   void InitCpu();
+  void InitSimulator();
   void CleanupCpu();
   void InitTlsEntryPoints();
   void InitTid();
@@ -1683,7 +1688,7 @@
       thread_local_objects(0), mterp_current_ibase(nullptr), thread_local_alloc_stack_top(nullptr),
       thread_local_alloc_stack_end(nullptr),
       flip_function(nullptr), method_verifier(nullptr), thread_local_mark_stack(nullptr),
-      async_exception(nullptr), top_reflective_handle_scope(nullptr) {
+      async_exception(nullptr), top_reflective_handle_scope(nullptr), simulator(nullptr) {
       std::fill(held_mutexes, held_mutexes + kLockLevelCount, nullptr);
     }
 
@@ -1842,6 +1847,9 @@
 
     // Top of the linked-list for reflective-handle scopes or null if none.
     BaseReflectiveHandleScope* top_reflective_handle_scope;
+
+    // A pointer to the simulator container.
+    CodeSimulatorContainer* simulator;
   } tlsPtr_;
 
   // Small thread-local cache to be used from the interpreter.
diff --git a/simulator/Android.bp b/simulator/Android.bp
index 1410444..d71e565 100644
--- a/simulator/Android.bp
+++ b/simulator/Android.bp
@@ -18,12 +18,15 @@
     name: "libart_simulator_headers",
     host_supported: true,
     export_include_dirs: ["include"],
+    apex_available: [
+        "com.android.art.release",
+        "com.android.art.debug",
+    ],
 }
 
 cc_defaults {
     name: "libart_simulator_defaults",
     host_supported: true,
-    device_supported: false,
 
     defaults: ["art_defaults"],
     srcs: [
@@ -36,7 +39,12 @@
     ],
     cflags: ["-DVIXL_INCLUDE_SIMULATOR_AARCH64"],
 
-    header_libs: ["libart_simulator_headers"],
+    header_libs: [
+        "jni_platform_headers",
+        "libdexfile_all_headers",
+        "libart_runtime_headers_ndk",
+        "libart_simulator_headers",
+    ],
 }
 
 art_cc_library {
@@ -47,6 +55,7 @@
         "libartbase",
         "libvixl",
     ],
+    device_supported: false,
 }
 
 art_cc_library {
@@ -60,6 +69,7 @@
         "libartbased",
         "libvixld",
     ],
+    device_supported: false,
 }
 
 cc_defaults {
@@ -73,8 +83,12 @@
     shared_libs: [
         "libbase",
     ],
-
-    header_libs: ["libart_simulator_headers"],
+    header_libs: [
+        "jni_platform_headers",
+        "libdexfile_all_headers",
+        "libart_runtime_headers_ndk",
+        "libart_simulator_headers",
+    ],
     export_include_dirs: ["."], // TODO: Consider a proper separation.
 }
 
@@ -83,7 +97,10 @@
     defaults: ["libart_simulator_container_defaults"],
     shared_libs: [
         "libartbase",
-        "libart",
+    ],
+    apex_available: [
+        "com.android.art.release",
+        "com.android.art.debug",
     ],
 }
 
@@ -95,7 +112,6 @@
     ],
     shared_libs: [
         "libartbased",
-        "libartd",
     ],
     apex_available: [
         "com.android.art.debug",
diff --git a/simulator/code_simulator_arm64.cc b/simulator/code_simulator_arm64.cc
index a64bd0b..7521d18 100644
--- a/simulator/code_simulator_arm64.cc
+++ b/simulator/code_simulator_arm64.cc
@@ -16,13 +16,178 @@
 
 #include "code_simulator_arm64.h"
 
-#include <android-base/logging.h>
+#include "art_method.h"
+#include "base/logging.h"
+#include "class_linker.h"
+#include "thread.h"
+
+#include <string>
+#include <cstring>
+#include <math.h>
+
+static constexpr bool kEnableSimulateMethodAllowList = false;
+
+static const std::vector<std::string> simulate_method_allow_list = {
+  // Add any run test method you want to simulate here, for example:
+  // test/684-checker-simd-dotprod
+  "other.TestByte.testDotProdComplex",
+  "other.TestByte.testDotProdComplexSignedCastedToUnsigned",
+  "other.TestByte.testDotProdComplexUnsigned",
+  "other.TestByte.testDotProdComplexUnsignedCastedToSigned",
+};
+static const std::vector<std::string> avoid_simulation_method_list = {
+  // For now, we can focus on simulating run test methods called by main().
+  "main",
+  "<clinit>",
+  // Currently, we don't simulate Java library methods.
+  "java.",
+  "sun.",
+  "dalvik.",
+  "android.",
+  "libcore.",
+};
 
 using namespace vixl::aarch64;  // NOLINT(build/namespaces)
 
 namespace art {
 namespace arm64 {
 
+  // Special registers defined in asm_support_arm64.s.
+  // Register holding Thread::current().
+  static const unsigned kSelf = 19;
+  // Marking register.
+  static const unsigned kMR = 20;
+  // Frame Pointer.
+  static const unsigned kFp = 29;
+  // Stack Pointer.
+  static const unsigned kSp = 31;
+
+class CustomSimulator final: public Simulator {
+ public:
+  explicit CustomSimulator(Decoder* decoder) : Simulator(decoder), qpoints_(nullptr) {}
+  virtual ~CustomSimulator() {}
+
+  void SetEntryPoints(QuickEntryPoints* qpoints) {
+    DCHECK(qpoints_ == nullptr);
+    qpoints_ = qpoints;
+  }
+
+  template <typename R, typename... P>
+  struct RuntimeCallHelper {
+    static void Execute(Simulator* simulator, R (*f)(P...)) {
+      simulator->RuntimeCallNonVoid(f);
+    }
+  };
+
+  // Partial specialization when the return type is `void`.
+  template <typename... P>
+  struct RuntimeCallHelper<void, P...> {
+    static void Execute(Simulator* simulator, void (*f)(P...)) {
+      simulator->RuntimeCallVoid(f);
+    }
+  };
+
+  // Override Simulator::VisitUnconditionalBranchToRegister to handle any runtime invokes
+  // which can be simulated.
+  void VisitUnconditionalBranchToRegister(const vixl::aarch64::Instruction* instr) override
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    DCHECK(qpoints_ != nullptr);
+    if (instr->Mask(UnconditionalBranchToRegisterMask) == BR) {
+      // The thunk mechansim code (LDR, BR) is generated by
+      // CodeGeneratorARM64::InvokeRuntime()
+
+      // Conceptually, the control flow works as if:
+      // #########################################################################
+      // Compiled Method (arm64)    |  THUNK (arm64) | Runtime Function (x86_64)
+      // #########################################################################
+      // BL kQuickTestSuspend@thunk -> LDR x16, [...]
+      //                                BR x16 -------> art_quick_test_suspend
+      //     ^                                               (x86 ret)
+      //     |                                                   |
+      //     +---------------------------------------------------+
+
+      // Actual control flow: arm64 code <-> x86_64 runtime, intercepted by simulator.
+      // ##########################################################################
+      //              arm64 code in simulator      |         | ART Runtime (x86_64)
+      // ##########################################################################
+      // BL kQuickTestSuspend@thunk -> LDR x16, [...]
+      //                                BR x16 ---> simulator ---> art_quick_test_suspend
+      //     ^                                      (x86 call)          (x86 ret)
+      //     |                                                              |
+      //     +------------------------------------- simulator <-------------+
+      //                                            (ARM ret)
+      //
+
+      const void* target = reinterpret_cast<const void*>(ReadXRegister(instr->GetRn()));
+      auto lr = vixl::aarch64::Instruction::Cast(get_lr());
+      if (target == reinterpret_cast<const void*>(qpoints_->pTestSuspend)) {
+        RuntimeCallHelper<void>::Execute(this, qpoints_->pTestSuspend);
+      } else {
+        // For branching to fixed addresses or labels, nothing has changed.
+        Simulator::VisitUnconditionalBranchToRegister(instr);
+        return;
+      }
+      WritePc(lr);  // aarch64 return
+      return;
+    } else if (instr->Mask(UnconditionalBranchToRegisterMask) == BLR) {
+      const void* target = reinterpret_cast<const void*>(ReadXRegister(instr->GetRn()));
+      auto lr = instr->GetNextInstruction();
+      if (target == reinterpret_cast<const void*>(qpoints_->pAllocObjectInitialized)) {
+        RuntimeCallHelper<void *, mirror::Class *>::Execute(this, qpoints_->pAllocObjectInitialized);
+      } else if (target == reinterpret_cast<const void*>(qpoints_->pAllocArrayResolved8) ||
+                 target == reinterpret_cast<const void*>(qpoints_->pAllocArrayResolved16) ||
+                 target == reinterpret_cast<const void*>(qpoints_->pAllocArrayResolved32) ||
+                 target == reinterpret_cast<const void*>(qpoints_->pAllocArrayResolved64)) {
+        RuntimeCallHelper<void *, mirror::Class *, int32_t>::Execute(this,
+            reinterpret_cast<void *(*)(art::mirror::Class *, int)>(const_cast<void*>(target)));
+      } else {
+        // For branching to fixed addresses or labels, nothing has changed.
+        Simulator::VisitUnconditionalBranchToRegister(instr);
+        return;
+      }
+      WritePc(lr);  // aarch64 return
+      return;
+    }
+    Simulator::VisitUnconditionalBranchToRegister(instr);
+    return;
+  }
+
+  // TODO(simulator): Maybe integrate these into vixl?
+  int64_t get_sp() const {
+    return ReadRegister<int64_t>(kSp, Reg31IsStackPointer);
+  }
+
+  int64_t get_x(int32_t n) const {
+    return ReadRegister<int64_t>(n, Reg31IsStackPointer);
+  }
+
+  int64_t get_lr() const {
+    return ReadRegister<int64_t>(kLinkRegCode);
+  }
+
+  int64_t get_fp() const {
+    return ReadXRegister(kFp);
+  }
+
+ private:
+  QuickEntryPoints* qpoints_;
+};
+
+static const void* GetQuickCodeFromArtMethod(ArtMethod* method)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK(!method->IsAbstract());
+  DCHECK(!method->IsNative());
+  DCHECK(Runtime::SimulatorMode());
+  DCHECK(method->CanBeSimulated());
+
+  ClassLinker* linker = Runtime::Current()->GetClassLinker();
+  const void* code = method->GetOatMethodQuickCode(linker->GetImagePointerSize());
+  if (code != nullptr) {
+    return code;
+  }
+  return nullptr;
+}
+
 // VIXL has not been tested on 32bit architectures, so Simulator is not always
 // available. To avoid linker error on these architectures, we check if we can simulate
 // in the beginning of following methods, with compile time constant `kCanSimulate`.
@@ -40,7 +205,11 @@
     : CodeSimulator(), decoder_(nullptr), simulator_(nullptr) {
   DCHECK(kCanSimulate);
   decoder_ = new Decoder();
-  simulator_ = new Simulator(decoder_);
+  simulator_ = new CustomSimulator(decoder_);
+  if (VLOG_IS_ON(simulator)) {
+    simulator_->SetColouredTrace(true);
+    simulator_->SetTraceParameters(LOG_DISASM | LOG_WRITE);
+  }
 }
 
 CodeSimulatorArm64::~CodeSimulatorArm64() {
@@ -51,7 +220,7 @@
 
 void CodeSimulatorArm64::RunFrom(intptr_t code_buffer) {
   DCHECK(kCanSimulate);
-  simulator_->RunFrom(reinterpret_cast<const Instruction*>(code_buffer));
+  simulator_->RunFrom(reinterpret_cast<const vixl::aarch64::Instruction*>(code_buffer));
 }
 
 bool CodeSimulatorArm64::GetCReturnBool() const {
@@ -69,5 +238,208 @@
   return simulator_->ReadXRegister(0);
 }
 
+void CodeSimulatorArm64::Invoke(ArtMethod* method, uint32_t* args, uint32_t args_size_in_bytes,
+                                Thread* self, JValue* result, const char* shorty, bool isStatic)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK(kCanSimulate);
+  // ARM64 simulator only supports 64-bit host machines. Because:
+  //   1) vixl simulator is not tested on 32-bit host machines.
+  //   2) Data structures in ART have different representations for 32/64-bit machines.
+  DCHECK(sizeof(args) == sizeof(int64_t));
+
+  if (VLOG_IS_ON(simulator)) {
+    VLOG(simulator) << "\nVIXL_SIMULATOR simulate: " << method->PrettyMethod();
+  }
+
+  InitRegistersForInvokeStub(method, args, args_size_in_bytes, self, result, shorty, isStatic);
+
+  int64_t quick_code = reinterpret_cast<int64_t>(GetQuickCodeFromArtMethod(method));
+  RunFrom(quick_code);
+
+  GetResultFromShorty(result, shorty);
+
+  // Ensure simulation state is not carried over from one method to another.
+  simulator_->ResetState();
+
+  // Reset stack pointer.
+  simulator_->WriteSp(saved_sp_);
+}
+
+void CodeSimulatorArm64::GetResultFromShorty(JValue* result, const char* shorty) {
+  switch (shorty[0]) {
+    case 'V':
+      return;
+    case 'D':
+      result->SetD(simulator_->ReadDRegister(0));
+      return;
+    case 'F':
+      result->SetF(simulator_->ReadSRegister(0));
+      return;
+    default:
+      // Just store x0. Doesn't matter if it is 64 or 32 bits.
+      result->SetJ(simulator_->ReadXRegister(0));
+      return;
+  }
+}
+
+// Init registers for invoking art_quick_invoke_stub:
+//
+//  extern"C" void art_quick_invoke_stub(ArtMethod *method,   x0
+//                                       uint32_t  *args,     x1
+//                                       uint32_t argsize,    w2
+//                                       Thread *self,        x3
+//                                       JValue *result,      x4
+//                                       char   *shorty);     x5
+//
+// See art/runtime/arch/arm64/quick_entrypoints_arm64.S
+//
+//  +----------------------+
+//  |                      |
+//  |  C/C++ frame         |
+//  |       LR''           |
+//  |       FP''           | <- SP'
+//  +----------------------+
+//  +----------------------+
+//  |        X28           |
+//  |        :             |
+//  |        X19 (*self)   |
+//  |        SP'           |        Saved registers
+//  |        X5 (*shorty)  |
+//  |        X4 (*result)  |
+//  |        LR'           |
+//  |        FP'           | <- FP
+//  +----------------------+
+//  | uint32_t out[n-1]    |
+//  |    :      :          |        Outs
+//  | uint32_t out[0]      |
+//  | ArtMethod*           | <- SP  value=null
+//  +----------------------+
+//
+// Outgoing registers:
+//  x0    - Current ArtMethod*
+//  x1-x7 - integer parameters.
+//  d0-d7 - Floating point parameters.
+//  xSELF = self
+//  SP = & of ArtMethod*
+//  x1    - "this" pointer (for non-static method)
+void CodeSimulatorArm64::InitRegistersForInvokeStub(ArtMethod* method, uint32_t* args,
+                                                    uint32_t args_size_in_bytes, Thread* self,
+                                                    JValue* result, const char* shorty,
+                                                    bool isStatic)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK(kCanSimulate);
+
+  // Set registers x0, x4, x5, and x19.
+  simulator_->WriteXRegister(0, reinterpret_cast<int64_t>(method));
+  simulator_->WriteXRegister(kSelf, reinterpret_cast<int64_t>(self));
+  simulator_->WriteXRegister(4, reinterpret_cast<int64_t>(result));
+  simulator_->WriteXRegister(5, reinterpret_cast<int64_t>(shorty));
+
+  // Stack Pointer here is not the real one in hardware. This will break stack overflow check.
+  // Also note that the simulator stack is limited.
+  saved_sp_ = simulator_->get_sp();
+  // x4, x5, x19, x20 .. x28, SP, LR, FP saved (15 in total).
+  const int64_t regs_save_size_in_bytes = kXRegSizeInBytes * 15;
+  const int64_t frame_save_size = regs_save_size_in_bytes +
+                                  kXRegSizeInBytes +  // ArtMethod*
+                                  static_cast<int64_t>(args_size_in_bytes);
+  // Comply with 16-byte alignment requirement for SP.
+  void** new_sp = reinterpret_cast<void**>((saved_sp_ - frame_save_size) & (~0xfUL));
+
+  simulator_->WriteSp(new_sp);
+
+  // Store null into ArtMethod* at bottom of frame.
+  *new_sp++ = nullptr;
+  // Copy arguments into stack frame.
+  std::memcpy(new_sp, args, args_size_in_bytes * sizeof(uint32_t));
+
+  // Callee-saved registers.
+  int64_t* save_registers = reinterpret_cast<int64_t*>(saved_sp_) + 3;
+  save_registers[0] = simulator_->get_fp();
+  save_registers[1] = simulator_->get_lr();
+  save_registers[2] = simulator_->get_x(4);  // X4 (*result)
+  save_registers[3] = simulator_->get_x(5);  // X5 (*shorty)
+  save_registers[4] = saved_sp_;
+  save_registers[5] = simulator_->get_x(kSelf);  // X19 (*self)
+  for (unsigned int i = 6; i < 15; i++) {
+    save_registers[i] = simulator_->get_x(i + 14);  // X20 .. X28
+  }
+
+  // Use xFP (Frame Pointer) now, as it's callee-saved.
+  simulator_->WriteXRegister(kFp, saved_sp_ - regs_save_size_in_bytes);
+
+  // Fill registers from args, according to shorty.
+  static const unsigned kRegisterIndexLimit = 8;
+  unsigned fpr_index = 0;
+  unsigned gpr_index = 1;  // x1 ~ x7 integer parameters.
+  shorty++;  // Skip the return value.
+  // For non-static method, load "this" parameter, and increment args pointer.
+  if (!isStatic) {
+    simulator_->WriteWRegister(gpr_index++, *args++);
+  }
+  // Loop to fill registers.
+  for (const char* s = shorty; *s != '\0'; s++) {
+    switch (*s) {
+      case 'D':
+        simulator_->WriteDRegister(fpr_index++, *reinterpret_cast<double*>(args));
+        args += 2;
+        break;
+      case 'J':
+        simulator_->WriteXRegister(gpr_index++, *reinterpret_cast<int64_t*>(args));
+        args += 2;
+        break;
+      case 'F':
+        simulator_->WriteSRegister(fpr_index++, *reinterpret_cast<float*>(args));
+        args++;
+        break;
+      default:
+        // Everything else takes one vReg.
+        simulator_->WriteWRegister(gpr_index++, *reinterpret_cast<int32_t*>(args));
+        args++;
+        break;
+    }
+    if (gpr_index > kRegisterIndexLimit || fpr_index < kRegisterIndexLimit) {
+      // TODO: Handle register spill.
+      UNREACHABLE();
+    }
+  }
+
+  // REFRESH_MARKING_REGISTER
+  if (kUseReadBarrier) {
+    simulator_->WriteWRegister(kMR, self->GetIsGcMarking());
+  }
+}
+
+void CodeSimulatorArm64::InitEntryPoints(QuickEntryPoints* qpoints) {
+  simulator_->SetEntryPoints(qpoints);
+}
+
+bool CodeSimulatorArm64::CanSimulate(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+  std::string name = method->PrettyMethod();
+
+  // Make sure simulate methods with $simulate$ in their names.
+  if (name.find("$simulate$") != std::string::npos) {
+    return true;
+  }
+  // Simulation allow list mode, only simulate method on the allow list.
+  if (kEnableSimulateMethodAllowList) {
+    for (auto& s : simulate_method_allow_list) {
+      if (name.find(s) != std::string::npos) {
+        return true;
+      }
+    }
+    return false;
+  }
+  // Avoid simulating following methods.
+  for (auto& s : avoid_simulation_method_list) {
+    if (name.find(s) != std::string::npos) {
+      return false;
+    }
+  }
+
+  // Try to simulate as much as we can.
+  return true;
+}
+
 }  // namespace arm64
 }  // namespace art
diff --git a/simulator/code_simulator_arm64.h b/simulator/code_simulator_arm64.h
index e726500..ea5c95a 100644
--- a/simulator/code_simulator_arm64.h
+++ b/simulator/code_simulator_arm64.h
@@ -27,10 +27,13 @@
 
 #include "arch/instruction_set.h"
 #include "code_simulator.h"
+#include "entrypoints/quick/quick_entrypoints.h"
 
 namespace art {
 namespace arm64 {
 
+class CustomSimulator;
+
 class CodeSimulatorArm64 : public CodeSimulator {
  public:
   static CodeSimulatorArm64* CreateCodeSimulatorArm64();
@@ -42,11 +45,24 @@
   int32_t GetCReturnInt32() const override;
   int64_t GetCReturnInt64() const override;
 
+  bool CanSimulate(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) override;
+  void Invoke(ArtMethod* method, uint32_t* args, uint32_t args_size, Thread* self, JValue* result,
+              const char* shorty, bool isStatic) override REQUIRES_SHARED(Locks::mutator_lock_);
+
+  void InitEntryPoints(QuickEntryPoints* qpoints) override;
+
  private:
   CodeSimulatorArm64();
 
+  void InitRegistersForInvokeStub(ArtMethod* method, uint32_t* args, uint32_t args_size,
+                                  Thread* self, JValue* result, const char* shorty, bool isStatic)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  void GetResultFromShorty(JValue* result, const char* shorty);
+
   vixl::aarch64::Decoder* decoder_;
-  vixl::aarch64::Simulator* simulator_;
+  CustomSimulator* simulator_;
+  int64_t saved_sp_;
 
   // TODO: Enable CodeSimulatorArm64 for more host ISAs once Simulator supports them.
   static constexpr bool kCanSimulate = (kRuntimeISA == InstructionSet::kX86_64);
diff --git a/simulator/include/code_simulator.h b/simulator/include/code_simulator.h
index 256ab23..22bac1e 100644
--- a/simulator/include/code_simulator.h
+++ b/simulator/include/code_simulator.h
@@ -18,9 +18,15 @@
 #define ART_SIMULATOR_INCLUDE_CODE_SIMULATOR_H_
 
 #include "arch/instruction_set.h"
+#include "runtime.h"
 
 namespace art {
 
+class ArtMethod;
+union JValue;
+class Thread;
+struct QuickEntryPoints;
+
 class CodeSimulator {
  public:
   CodeSimulator() {}
@@ -35,6 +41,13 @@
   virtual int32_t GetCReturnInt32() const = 0;
   virtual int64_t GetCReturnInt64() const = 0;
 
+  virtual bool CanSimulate(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+  virtual void Invoke(ArtMethod* method, uint32_t* args, uint32_t args_size, Thread* self,
+                      JValue* result, const char* shorty, bool isStatic)
+      REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+  virtual void InitEntryPoints(QuickEntryPoints* qpoints) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(CodeSimulator);
 };
diff --git a/test.py b/test.py
index 9e54363..1cbad72 100755
--- a/test.py
+++ b/test.py
@@ -33,6 +33,7 @@
 parser.add_argument('--gtest', '-g', action='store_true', dest='gtest', help='execute gtest tests')
 parser.add_argument('--target', action='store_true', dest='target', help='test on target system')
 parser.add_argument('--host', action='store_true', dest='host', help='test on build host system')
+parser.add_argument('--simulate-arm64', action='store_true', dest='simulate_arm64', help='test on build host system')
 parser.add_argument('--help-runner', action='store_true', dest='help_runner', help='show help for optional run test arguments')
 options, unknown = parser.parse_known_args()
 
diff --git a/test/README.simulator.md b/test/README.simulator.md
new file mode 100644
index 0000000..c575c35
--- /dev/null
+++ b/test/README.simulator.md
@@ -0,0 +1,98 @@
+# ART VIXL Simulator Integration
+
+This file documents the use of the VIXL Simulator for running tests on ART. The
+simulator enables us to run the ART run-tests without the need for a target
+device. This helps to speed up the development/debug/test cycle. The full AOSP
+source tree, as well as the partial master-art AOSP source tree, are supported.
+
+## Quick User Guide
+1. Set lunch target and setup environment:
+
+    ```bash
+    source build/envsetup.sh; lunch armv8-eng
+    ```
+
+2. Build ART target and host:
+
+    ```bash
+    art/tools/buildbot-build.sh --target
+    art/tools/buildbot-build.sh --host
+    ```
+
+3. Run Tests:
+
+    To enable the simulator we use the `--simulate-arm64` flag. The simulator can
+    be used directly with the dalvikvm or the ART test scripts.
+
+    To run a single test on simulator, use the command:
+    ```bash
+    art/test/run-test --host --simulate-arm64 --64 <TEST_NAME>
+    ```
+
+    To run all ART run-tests on simulator, use the `art/test.py` script with the
+    following command:
+    ```bash
+    ./art/test.py --simulate-arm64 --run-test --optimizing
+    ```
+
+4. Enable simulator tracing
+
+    Simulator provides tracing feature which is useful in debugging. Setting
+    runtime option `-verbose:simulator` will enable instruction trace and register
+    updates.
+    For example,
+    ```bash
+    ./art/test/run-test --host --runtime-option -verbose:simulator --optimizing \
+      --never-clean --simulate-arm64 --64 640-checker-simd
+    ```
+
+5. Debug
+
+    Another useful usecase of the simulator is debugging using the `--gdb` flag.
+    ```bash
+    ./art/test/run-test --gdb --host --simulate-arm64 --64 527-checker-array-access-split
+    ```
+    If developing a compiler optimization which affects the test case
+    `527-checker-array-access-split`, you can use the simulator to run and
+    generate the control flow graph with:
+    ```bash
+    ./art/test/run-test --host --dex2oat-jobs 1 -Xcompiler-option --dump-cfg=oat.cfg \
+      --never-clean --simulate-arm64 --64 527-checker-array-access-split
+    ```
+
+6. Control simulation
+
+    By default, in simulator mode, all methods in `art/test/` run-tests files are
+    simulated. However, within `art/simulator/code_simulator_arm64.cc`, the
+    `CanSimulate()` function provides options for developer to control simulation:
+    - the `kEnableSimulateMethodAllowList` to restrict the methods run in the simulator;
+    - the `$simulate$` tag to force the simulator to run a method.
+
+    #### Allow list to control simulation
+    Sometimes we may wish to restrict the methods run in the simulator, this can
+    be done using the `simulate_method_white_list`. Here a list of methods which
+    we know to be safe to run in the simulator is kept in
+    `art/simulator/code_simulator_arm64.cc`, the simulator can be forced to only
+    run the methods on this list by setting
+    ```
+    kEnableSimulateMethodAllowList = true
+    ```
+    and recompile art and rerun the test cases. For example, if we set the white list to
+   ```
+   static const std::vector<std::string> simulate_method_white_list = {
+     "other.TestByte.testDotProdComplex",
+     "other.TestByte.testDotProdComplexSignedCastedToUnsigned",
+     "other.TestByte.testDotProdComplexUnsigned",
+     "other.TestByte.testDotProdComplexUnsignedCastedToSigned",
+    };
+    ```
+    We only allow these methods to be run in simulator and all the other methods
+    will run in the interpreter.
+
+    #### The `$simulate$` tag to control simulation
+    In the case that we may wish to quickly change a Java method test case and
+    force the simulator to run a method without recompiling art, add the
+    `$simulate$` tag in the method name. For example,
+    ```
+    public void $simulate$foo() {}
+    ```
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 487958f..a7190ce 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -109,6 +109,7 @@
 # The same for dex2oatd, both prebuild and runtime-driven.
 ANDROID_FLAGS="${ANDROID_FLAGS} -Xcompiler-option --runtime-arg -Xcompiler-option -XX:SlowDebug=true"
 COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -XX:SlowDebug=true"
+SIMULATOR="none"
 
 # Let the compiler and runtime know that we are running tests.
 COMPILE_FLAGS="${COMPILE_FLAGS} --compile-art-test"
@@ -468,6 +469,14 @@
     elif [ "x$1" = "x--random-profile" ]; then
         RANDOM_PROFILE="y"
         shift
+    elif [ "x$1" = "x--simulate-isa" ]; then
+        HOST="y"
+        ANDROID_ROOT="${ANDROID_PRODUCT_OUT}/system"
+        ANDROID_RUNTIME_ROOT="${ANDROID_PRODUCT_OUT}/apex/com.android.runtime.debug"
+        shift
+        SIMULATOR=$1
+        FLAGS="${FLAGS} --simulate-isa=${SIMULATOR}"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         exit 1
@@ -713,7 +722,11 @@
 bpath_separator=""
 bpath_prefix=""
 bpath_location_prefix=""
-if [ "${HOST}" = "y" ]; then
+if [ "$SIMULATOR" != "none" ]; then
+  # Simulator mode uses a mix of host and target bootclasspath locations.
+  bpath_prefix="${ANDROID_HOST_OUT}"
+  bpath_location_prefix=""
+elif [ "${HOST}" = "y" ]; then
   bpath_prefix="${ANDROID_HOST_OUT}"
   if [ "${ANDROID_HOST_OUT:0:${#ANDROID_BUILD_TOP}+1}" = "${ANDROID_BUILD_TOP}/" ]; then
     bpath_location_prefix="${ANDROID_HOST_OUT:${#ANDROID_BUILD_TOP}+1}"
@@ -857,13 +870,15 @@
     exit 1
 fi
 
-if [ "$HOST" = "y" ]; then
+if [ "$HOST" = "y" ] && [ "$SIMULATOR" = "none" ]; then
   # On host, run binaries (`dex2oat(d)`, `dalvikvm`, `profman`) from the `bin`
   # directory under the "Android Root" (usually `out/host/linux-x86`).
   #
   # TODO(b/130295968): Adjust this if/when ART host artifacts are installed
   # under the ART root (usually `out/host/linux-x86/com.android.art`).
   ANDROID_ART_BIN_DIR=$ANDROID_ROOT/bin
+elif [ "$SIMULATOR" != "none" ]; then
+  ANDROID_ART_BIN_DIR=$ANDROID_HOST_OUT/bin
 else
   # On target, run binaries (`dex2oat(d)`, `dalvikvm`, `profman`) from the ART
   # APEX's `bin` directory. This means the linker will observe the ART APEX
@@ -873,6 +888,10 @@
 fi
 
 profman_cmdline="true"
+if [ "$SIMULATOR" != "none" ]; then
+  ISA=$SIMULATOR
+fi
+
 dex2oat_cmdline="true"
 vdex_cmdline="true"
 dm_cmdline="true"
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 6143dc7..1eb8e79 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1344,5 +1344,636 @@
         "variant": "jvm",
         "bug": "b/154802847",
         "description": ["Failing on RI. Needs further investigating."]
+    },
+    {
+        "tests": ["004-JniTest",
+                  "003-omnibus-opcodes",
+                  "004-NativeAllocations",
+                  "004-ReferenceMap",
+                  "004-SignalTest",
+                  "004-StackWalk",
+                  "004-ThreadStress",
+                  "004-UnsafeTest",
+                  "004-checker-UnsafeTest18",
+                  "006-args",
+                  "007-count10",
+                  "008-exceptions",
+                  "011-array-copy",
+                  "012-math",
+                  "013-math2",
+                  "014-math3",
+                  "015-switch",
+                  "017-float",
+                  "018-stack-overflow",
+                  "020-string",
+                  "021-string2",
+                  "022-interface",
+                  "023-many-interfaces",
+                  "024-illegal-access",
+                  "027-arithmetic",
+                  "028-array-write",
+                  "030-bad-finalizer",
+                  "031-class-attributes",
+                  "032-concrete-sub",
+                  "033-class-init-deadlock",
+                  "036-finalizer",
+                  "037-inherit",
+                  "038-inner-null",
+                  "039-join-main",
+                  "041-narrowing",
+                  "042-new-instance",
+                  "043-privates",
+                  "044-proxy",
+                  "045-reflect-array",
+                  "046-reflect",
+                  "047-returns",
+                  "048-reflect-v8",
+                  "049-show-object",
+                  "050-sync-test",
+                  "051-thread",
+                  "052-verifier-fun",
+                  "054-uncaught",
+                  "058-enum-order",
+                  "059-finalizer-throw",
+                  "061-out-of-memory",
+                  "062-character-encodings",
+                  "063-process-manager",
+                  "064-field-access",
+                  "067-preemptive-unpark",
+                  "068-classloader",
+                  "070-nio-buffer",
+                  "071-dexfile",
+                  "071-dexfile-get-static-size",
+                  "071-dexfile-map-clean",
+                  "072-precise-gc",
+                  "073-mismatched-field",
+                  "074-gc-thrash",
+                  "075-verification-error",
+                  "076-boolean-put",
+                  "077-method-override",
+                  "078-polymorphic-virtual",
+                  "079-phantom",
+                  "080-oom-throw",
+                  "080-oom-throw-with-finalizer",
+                  "081-hot-exceptions",
+                  "082-inline-execute",
+                  "083-compiler-regressions",
+                  "084-class-init",
+                  "086-null-super",
+                  "087-gc-after-link",
+                  "088-monitor-verification",
+                  "090-loop-formation",
+                  "091-override-package-private-method",
+                  "092-locale",
+                  "093-serialization",
+                  "096-array-copy-concurrent-gc",
+                  "098-ddmc",
+                  "099-vmdebug",
+                  "100-reflect2",
+                  "1002-notify-startup",
+                  "1003-metadata-section-strings",
+                  "1004-checker-volatile-ref-load",
+                  "104-growth-limit",
+                  "105-invoke",
+                  "106-exceptions2",
+                  "107-int-math2",
+                  "109-suspend-check",
+                  "110-field-access",
+                  "111-unresolvable-exception",
+                  "113-multidex",
+                  "114-ParallelGC",
+                  "115-native-bridge",
+                  "121-simple-suspend-check",
+                  "122-npe",
+                  "123-compiler-regressions-mt",
+                  "124-missing-classes",
+                  "125-gc-and-classloading",
+                  "126-miranda-multidex",
+                  "127-checker-secondarydex",
+                  "129-ThreadGetId",
+                  "130-hprof",
+                  "132-daemon-locks-shutdown",
+                  "1336-short-finalizer-timeout",
+                  "1337-gc-coverage",
+                  "1339-dead-reference-safe",
+                  "134-reg-promotion",
+                  "135-MirandaDispatch",
+                  "136-daemon-jni-shutdown",
+                  "137-cfi",
+                  "138-duplicate-classes-check",
+                  "138-duplicate-classes-check2",
+                  "139-register-natives",
+                  "140-dce-regression",
+                  "140-field-packing",
+                  "141-class-unload",
+                  "142-classloader2",
+                  "144-static-field-sigquit",
+                  "145-alloc-tracking-stress",
+                  "146-bad-interface",
+                  "148-multithread-gc-annotations",
+                  "151-OpenFileLimit",
+                  "154-gc-loop",
+                  "155-java-set-resolved-type",
+                  "156-register-dex-file-multi-loader",
+                  "158-app-image-class-table",
+                  "159-app-image-fields",
+                  "160-read-barrier-stress",
+                  "161-final-abstract-class",
+                  "162-method-resolution",
+                  "163-app-image-methods",
+                  "164-resolution-trampoline-dex-cache",
+                  "165-lock-owner-proxy",
+                  "167-visit-locks",
+                  "168-vmstack-annotated",
+                  "170-interface-init",
+                  "172-app-image-twice",
+                  "173-missing-field-type",
+                  "174-escaping-instance-of-bad-class",
+                  "177-visibly-initialized-deadlock",
+                  "178-app-image-native-method",
+                  "201-built-in-except-detail-messages",
+                  "203-multi-checkpoint",
+                  "300-package-override",
+                  "302-float-conversion",
+                  "303-verification-stress",
+                  "304-method-tracing",
+                  "305-other-fault-handler",
+                  "401-optimizing-compiler",
+                  "403-optimizing-long",
+                  "406-fields",
+                  "407-arrays",
+                  "409-materialized-condition",
+                  "410-floats",
+                  "411-checker-instruct-simplifier-hrem",
+                  "411-checker-hdiv-hrem-const",
+                  "411-checker-hdiv-hrem-pow2",
+                  "411-optimizing-arith",
+                  "412-new-array",
+                  "414-static-fields",
+                  "416-optimizing-arith-not",
+                  "420-const-class",
+                  "421-exceptions",
+                  "421-large-frame",
+                  "422-instanceof",
+                  "422-type-conversion",
+                  "423-invoke-interface",
+                  "424-checkcast",
+                  "426-monitor",
+                  "427-bitwise",
+                  "427-bounds",
+                  "430-live-register-slow-path",
+                  "432-optimizing-cmp",
+                  "434-invoke-direct",
+                  "435-try-finally-without-catch",
+                  "436-rem-float",
+                  "438-volatile",
+                  "439-npe",
+                  "439-swap-double",
+                  "440-stmp",
+                  "441-checker-inliner",
+                  "442-checker-constant-folding",
+                  "445-checker-licm",
+                  "449-checker-bce",
+                  "449-checker-bce-rem",
+                  "455-checker-gvn",
+                  "458-checker-instruct-simplification",
+                  "461-get-reference-vreg",
+                  "462-checker-inlining-dex-files",
+                  "464-checker-inline-sharpen-calls",
+                  "465-checker-clinit-gvn",
+                  "466-get-live-vreg",
+                  "467-regalloc-pair",
+                  "468-checker-bool-simplif-regression",
+                  "470-huge-method",
+                  "472-type-propagation",
+                  "472-unreachable-if-regression",
+                  "474-fp-sub-neg",
+                  "476-checker-ctor-fence-redun-elim",
+                  "476-clinit-inline-static-invoke",
+                  "477-long-2-float-convers-precision",
+                  "478-checker-clinit-check-pruning",
+                  "478-checker-inline-noreturn",
+                  "479-regression-implicit-null-check",
+                  "480-checker-dead-blocks",
+                  "487-checker-inline-calls",
+                  "488-checker-inline-recursive-calls",
+                  "491-current-method",
+                  "492-checker-inline-invoke-interface",
+                  "493-checker-inline-invoke-interface",
+                  "494-checker-instanceof-tests",
+                  "495-checker-checkcast-tests",
+                  "496-checker-inlining-class-loader",
+                  "508-referrer-method",
+                  "510-checker-try-catch",
+                  "517-checker-builder-fallthrough",
+                  "518-null-array-get",
+                  "519-bound-load-class",
+                  "524-boolean-simplifier-regression",
+                  "525-checker-arrays-fields1",
+                  "525-checker-arrays-fields2",
+                  "526-checker-caller-callee-regs",
+                  "526-long-regalloc",
+                  "529-checker-unresolved",
+                  "529-long-split",
+                  "530-checker-loops1",
+                  "530-checker-loops2",
+                  "530-checker-loops3",
+                  "530-checker-lse",
+                  "530-checker-lse-ctor-fences",
+                  "530-checker-lse-simd",
+                  "530-checker-lse2",
+                  "530-checker-peel-unroll",
+                  "530-checker-regression-reftyp-final",
+                  "530-instanceof-checkcast",
+                  "530-regression-lse",
+                  "534-checker-bce-deoptimization",
+                  "535-deopt-and-inlining",
+                  "536-checker-intrinsic-optimization",
+                  "536-checker-needs-access-check",
+                  "537-checker-inline-and-unverified",
+                  "541-regression-inlined-deopt",
+                  "542-bitfield-rotates",
+                  "542-inline-trycatch",
+                  "543-env-long-ref",
+                  "545-tracing-and-jit",
+                  "550-checker-multiply-accumulate",
+                  "550-checker-regression-wide-store",
+                  "551-checker-shifter-operand",
+                  "551-implicit-null-checks",
+                  "551-invoke-super",
+                  "552-checker-primitive-typeprop",
+                  "552-checker-sharpening",
+                  "552-invoke-non-existent-super",
+                  "553-invoke-super",
+                  "556-invoke-super",
+                  "559-checker-irreducible-loop",
+                  "561-shared-slowpaths",
+                  "563-checker-fakestring",
+                  "564-checker-bitcount",
+                  "564-checker-irreducible-loop",
+                  "565-checker-doublenegbitwise",
+                  "565-checker-irreducible-loop",
+                  "566-polymorphic-inlining",
+                  "567-checker-builder-intrinsics",
+                  "569-checker-pattern-replacement",
+                  "570-checker-osr",
+                  "570-checker-osr-locals",
+                  "574-irreducible-and-constant-area",
+                  "575-checker-string-init-alias",
+                  "576-polymorphic-inlining",
+                  "578-bce-visit",
+                  "578-polymorphic-inlining",
+                  "579-inline-infinite",
+                  "580-checker-string-fact-intrinsics",
+                  "580-crc32",
+                  "580-fp16",
+                  "582-checker-bce-length",
+                  "584-checker-div-bool",
+                  "585-inline-unresolved",
+                  "586-checker-null-array-get",
+                  "587-inline-class-error",
+                  "588-checker-irreducib-lifetime-hole",
+                  "589-super-imt",
+                  "590-checker-arr-set-null-regression",
+                  "591-new-instance-string",
+                  "592-checker-regression-bool-input",
+                  "593-checker-shift-and-simplifier",
+                  "594-invoke-super",
+                  "594-load-string-regression",
+                  "595-profile-saving",
+                  "596-app-images",
+                  "596-checker-dead-phi",
+                  "596-monitor-inflation",
+                  "597-app-images-same-classloader",
+                  "597-deopt-busy-loop",
+                  "597-deopt-new-string",
+                  "600-verifier-fails",
+                  "601-method-access",
+                  "603-checker-instanceof",
+                  "607-daemon-stress",
+                  "608-checker-unresolved-lse",
+                  "609-checker-inline-interface",
+                  "612-jit-dex-cache",
+                  "613-inlining-dex-cache",
+                  "615-checker-arm64-store-zero",
+                  "616-cha",
+                  "616-cha-abstract",
+                  "616-cha-interface",
+                  "616-cha-interface-default",
+                  "616-cha-miranda",
+                  "616-cha-proxy-method-inline",
+                  "616-cha-regression-proxy-method",
+                  "616-cha-unloading",
+                  "618-checker-induction",
+                  "622-simplifyifs-exception-edges",
+                  "623-checker-loop-regressions",
+                  "624-checker-stringops",
+                  "625-checker-licm-regressions",
+                  "626-checker-arm64-scratch-register",
+                  "626-const-class-linking",
+                  "626-set-resolved-string",
+                  "633-checker-rtp-getclass",
+                  "635-checker-arm64-volatile-load-cc",
+                  "636-wrong-static-access",
+                  "638-checker-inline-cache-intrinsic",
+                  "638-checker-inline-caches",
+                  "639-checker-code-sinking",
+                  "641-irreducible-inline",
+                  "641-iterations",
+                  "642-fp-callees",
+                  "643-checker-bogus-ic",
+                  "647-jni-get-field-id",
+                  "647-sinking-catch",
+                  "650-checker-inline-access-thunks",
+                  "652-deopt-intrinsic",
+                  "655-jit-clinit",
+                  "656-annotation-lookup-generic-jni",
+                  "656-checker-simd-opt",
+                  "656-loop-deopt",
+                  "657-branches",
+                  "658-fp-read-barrier",
+                  "660-clinit",
+                  "661-classloader-allocator",
+                  "661-oat-writer-layout",
+                  "662-regression-alias",
+                  "666-dex-cache-itf",
+                  "667-checker-simd-alignment",
+                  "667-jit-jni-stub",
+                  "667-out-of-bounds",
+                  "668-aiobe",
+                  "670-bitstring-type-check",
+                  "671-npe-field-opts",
+                  "672-checker-throw-method",
+                  "673-checker-throw-vmethod",
+                  "674-hiddenapi",
+                  "674-vdex-uncompress",
+                  "676-proxy-jit-at-first-use",
+                  "676-resolve-field-type",
+                  "677-fsi",
+                  "677-fsi2",
+                  "678-quickening",
+                  "679-locks",
+                  "680-checker-deopt-dex-pc-0",
+                  "680-sink-regression",
+                  "683-clinit-inline-static-invoke",
+                  "684-checker-simd-dotprod",
+                  "684-select-condition",
+                  "686-get-this",
+                  "687-deopt",
+                  "688-shared-library",
+                  "689-multi-catch",
+                  "689-zygote-jit-deopt",
+                  "690-hiddenapi-same-name-methods",
+                  "691-hiddenapi-proxy",
+                  "692-vdex-inmem-loader",
+                  "693-vdex-inmem-loader-evict",
+                  "694-clinit-jit",
+                  "695-simplify-throws",
+                  "697-checker-string-append",
+                  "700-LoadArgRegs",
+                  "701-easy-div-rem",
+                  "702-LargeBranchOffset",
+                  "703-floating-point-div",
+                  "704-multiply-accumulate",
+                  "706-checker-scheduler",
+                  "707-checker-invalid-profile",
+                  "708-jit-cache-churn",
+                  "710-varhandle-creation",
+                  "711-checker-type-conversion",
+                  "712-varhandle-invocations",
+                  "713-varhandle-invokers",
+                  "716-jli-jit-samples",
+                  "717-integer-value-of",
+                  "718-zipfile-finalizer",
+                  "179-nonvirtual-jni",
+                  "720-thread-priority",
+                  "721-osr",
+                  "724-invoke-super-npe",
+                  "800-smali",
+                  "802-deoptimization",
+                  "804-class-extends-itself",
+                  "807-method-handle-and-mr",
+                  "900-hello-plugin",
+                  "901-hello-ti-agent",
+                  "902-hello-transformation",
+                  "903-hello-tagging",
+                  "904-object-allocation",
+                  "905-object-free",
+                  "906-iterate-heap",
+                  "907-get-loaded-classes",
+                  "908-gc-start-finish",
+                  "909-attach-agent",
+                  "910-methods",
+                  "911-get-stack-trace",
+                  "912-classes",
+                  "913-heaps",
+                  "914-hello-obsolescence",
+                  "915-obsolete-2",
+                  "916-obsolete-jit",
+                  "917-fields-transformation",
+                  "918-fields",
+                  "919-obsolete-fields",
+                  "920-objects",
+                  "921-hello-failure",
+                  "922-properties",
+                  "923-monitors",
+                  "924-threads",
+                  "925-threadgroups",
+                  "926-multi-obsolescence",
+                  "927-timers",
+                  "928-jni-table",
+                  "929-search",
+                  "930-hello-retransform",
+                  "931-agent-thread",
+                  "932-transform-saves",
+                  "933-misc-events",
+                  "934-load-transform",
+                  "935-non-retransformable",
+                  "936-search-onload",
+                  "937-hello-retransform-package",
+                  "938-load-transform-bcp",
+                  "939-hello-transformation-bcp",
+                  "940-recursive-obsolete",
+                  "941-recursive-obsolete-jit",
+                  "942-private-recursive",
+                  "943-private-recursive-jit",
+                  "944-transform-classloaders",
+                  "945-obsolete-native",
+                  "946-obsolete-throw",
+                  "947-reflect-method",
+                  "948-change-annotations",
+                  "949-in-memory-transform",
+                  "950-redefine-intrinsic",
+                  "951-threaded-obsolete",
+                  "952-invoke-custom",
+                  "953-invoke-polymorphic-compiler",
+                  "954-invoke-polymorphic-verifier",
+                  "956-methodhandles",
+                  "957-methodhandle-transforms",
+                  "959-invoke-polymorphic-accessors",
+                  "960-default-smali",
+                  "961-default-iface-resolution-gen",
+                  "962-iface-static",
+                  "964-default-iface-init-gen",
+                  "965-default-verify",
+                  "966-default-conflict",
+                  "967-default-ame",
+                  "968-default-partial-compile-gen",
+                  "969-iface-super",
+                  "970-iface-super-resolution-gen",
+                  "971-iface-super",
+                  "972-default-imt-collision",
+                  "975-iface-private",
+                  "978-virtual-interface",
+                  "979-const-method-handle",
+                  "980-redefine-object",
+                  "981-dedup-original-dex",
+                  "982-ok-no-retransform",
+                  "983-source-transform-verify",
+                  "984-obsolete-invoke",
+                  "985-re-obsolete",
+                  "986-native-method-bind",
+                  "987-agent-bind",
+                  "988-method-trace",
+                  "989-method-trace-throw",
+                  "990-field-trace",
+                  "991-field-trace-2",
+                  "992-source-data",
+                  "993-breakpoints",
+                  "994-breakpoint-line",
+                  "995-breakpoints-throw",
+                  "996-breakpoint-obsolete",
+                  "997-single-step",
+                  "998-redefine-use-after-free",
+                  "999-redefine-hiddenapi",
+                  "1900-track-alloc",
+                  "1901-get-bytecodes",
+                  "1902-suspend",
+                  "1903-suspend-self",
+                  "1904-double-suspend",
+                  "1905-suspend-native",
+                  "1906-suspend-list-me-first",
+                  "1907-suspend-list-self-twice",
+                  "1908-suspend-native-resume-self",
+                  "1909-per-agent-tls",
+                  "1910-transform-with-default",
+                  "1911-get-local-var-table",
+                  "1912-get-set-local-primitive",
+                  "1913-get-set-local-objects",
+                  "1914-get-local-instance",
+                  "1915-get-set-local-current-thread",
+                  "1916-get-set-current-frame",
+                  "1917-get-stack-frame",
+                  "1919-vminit-thread-start-timing",
+                  "1920-suspend-native-monitor",
+                  "1921-suspend-native-recursive-monitor",
+                  "1922-owned-monitors-info",
+                  "1923-frame-pop",
+                  "1924-frame-pop-toggle",
+                  "1925-self-frame-pop",
+                  "1926-missed-frame-pop",
+                  "1927-exception-event",
+                  "1928-exception-event-exception",
+                  "1929-exception-catch-exception",
+                  "1930-monitor-info",
+                  "1931-monitor-events",
+                  "1932-monitor-events-misc",
+                  "1933-monitor-current-contended",
+                  "1934-jvmti-signal-thread",
+                  "1935-get-set-current-frame-jit",
+                  "1936-thread-end-events",
+                  "1937-transform-soft-fail",
+                  "1938-transform-abstract-single-impl",
+                  "1939-proxy-frames",
+                  "1940-ddms-ext",
+                  "1941-dispose-stress",
+                  "1942-suspend-raw-monitor-exit",
+                  "1943-suspend-raw-monitor-wait",
+                  "1945-proxy-method-arguments",
+                  "1946-list-descriptors",
+                  "1947-breakpoint-redefine-deopt",
+                  "1948-obsolete-const-method-handle",
+                  "1949-short-dex-file",
+                  "1950-unprepared-transform",
+                  "1951-monitor-enter-no-suspend",
+                  "1953-pop-frame",
+                  "1954-pop-frame-jit",
+                  "1955-pop-frame-jit-called",
+                  "1956-pop-frame-jit-calling",
+                  "1957-error-ext",
+                  "1958-transform-try-jit",
+                  "1959-redefine-object-instrument",
+                  "1960-checker-bounds-codegen",
+                  "1960-obsolete-jit-multithread-native",
+                  "1961-checker-loop-vectorizer",
+                  "1961-obsolete-jit-multithread",
+                  "1962-multi-thread-events",
+                  "1963-add-to-dex-classloader-in-memory",
+                  "1964-add-to-dex-classloader-file",
+                  "1965-get-set-local-primitive-no-tables",
+                  "1966-get-set-local-objects-no-table",
+                  "1967-get-set-local-bad-slot",
+                  "1968-force-early-return",
+                  "1969-force-early-return-void",
+                  "1970-force-early-return-long",
+                  "1971-multi-force-early-return",
+                  "1972-jni-id-swap-indices",
+                  "1973-jni-id-swap-pointer",
+                  "1974-resize-array",
+                  "1975-hello-structural-transformation",
+                  "1976-hello-structural-static-methods",
+                  "1977-hello-structural-obsolescence",
+                  "1978-regular-obsolete-then-structural-obsolescence",
+                  "1979-threaded-structural-transformation",
+                  "1980-obsolete-object-cleared",
+                  "1981-structural-redef-private-method-handles",
+                  "1982-no-virtuals-structural-redefinition",
+                  "1983-structural-redefinition-failures",
+                  "1984-structural-redefine-field-trace",
+                  "1985-structural-redefine-stack-scope",
+                  "1986-structural-redefine-multi-thread-stack-scope",
+                  "1988-multi-structural-redefine",
+                  "1989-transform-bad-monitor",
+                  "1990-structural-bad-verify",
+                  "1991-hello-structural-retransform",
+                  "1992-retransform-no-such-field",
+                  "1993-fallback-non-structural",
+                  "1994-final-virtual-structural",
+                  "1995-final-virtual-structural-multithread",
+                  "1996-final-override-virtual-structural",
+                  "1997-structural-shadow-method",
+                  "1998-structural-shadow-field",
+                  "1999-virtual-structural",
+                  "2000-virtual-list-structural",
+                  "2001-virtual-structural-multithread",
+                  "2002-virtual-structural-initializing",
+                  "2003-double-virtual-structural",
+                  "2004-double-virtual-structural-abstract",
+                  "2005-pause-all-redefine-multithreaded",
+                  "2006-virtual-structural-finalizing",
+                  "2007-virtual-structural-finalizable",
+                  "2008-redefine-then-old-reflect-field",
+                  "2009-structural-local-ref",
+                  "2011-stack-walk-concurrent-instrument",
+                  "2012-structural-redefinition-failures-jni-id",
+                  "2019-constantcalculationsinking",
+                  "2020-InvokeVirtual-Inlining",
+                  "2022-Invariantloops",
+                  "2023-InvariantLoops_typecast",
+                  "2024-InvariantNegativeLoop",
+                  "2025-ChangedArrayValue",
+                  "2026-DifferentMemoryLSCouples",
+                  "2027-TwiceTheSameMemoryCouple",
+                  "2028-MultiBackward",
+                  "2029-contended-monitors",
+                  "2030-long-running-child",
+                  "2032-default-method-private-override",
+                  "2033-shutdown-mechanics",
+                  "2035-structural-native-method",
+                  "2036-jni-filechannel",
+                  "1987-structural-redefine-recursive-stack-scope"
+                 ],
+        "variant": "simulate-arm64",
+        "description": ["TODO: Support more quick entry points in ART-VIXL simulator."]
     }
 ]
diff --git a/test/run-test b/test/run-test
index 86d30d5..cfe8111 100755
--- a/test/run-test
+++ b/test/run-test
@@ -141,6 +141,7 @@
 create_runner="no"
 update_mode="no"
 debug_mode="no"
+simulator_mode="no"
 relocate="no"
 runtime="art"
 usage="no"
@@ -181,6 +182,9 @@
         DEX_LOCATION=$tmp_dir
         run_args+=(--host)
         shift
+    elif [ "x$1" = "x--simulate-arm64" ]; then
+        simulator_mode="yes"
+        shift
     elif [ "x$1" = "x--quiet" ]; then
         quiet="yes"
         shift
@@ -674,10 +678,14 @@
         true # defaults to using target BOOTCLASSPATH
     fi
 elif [ "$runtime" = "art" ]; then
-    if [ "$target_mode" = "no" ]; then
+    if [ "$target_mode" = "no" ] && [ "$simulator_mode" = "no" ]; then
         guess_host_arch_name
         run_args+=(--boot "${ANDROID_HOST_OUT}/apex/com.android.art/javalib/boot.art")
         run_args+=(--runtime-option "-Djava.library.path=${host_lib_root}/lib${suffix64}:${host_lib_root}/nativetest${suffix64}")
+    elif [ "$simulator_mode" = "yes" ]; then
+        run_args+=(--simulate-isa "arm64")
+        run_args+=(--boot "${ANDROID_PRODUCT_OUT}/system/apex/com.android.art.testing/javalib/boot.art")
+        run_args+=(--runtime-option "-Djava.library.path=${host_lib_root}/lib${suffix64}:${host_lib_root}/nativetest${suffix64}")
     else
         guess_target_arch_name
         run_args+=(--runtime-option "-Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}")
@@ -915,7 +923,10 @@
     if [ "$prebuild_mode" = "yes" -a "$have_image" = "yes" ]; then
       run_checker="yes"
 
-      if [ "$target_mode" = "no" ]; then
+      if [ "$simulator_mode" = "yes" ]; then
+        cfg_output_dir="$tmp_dir"
+        checker_args="--arch=ARM64"
+      elif [ "$target_mode" = "no" ]; then
         cfg_output_dir="$tmp_dir"
         checker_args="--arch=${host_arch_name^^}"
       else
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index b8e8cb7..f547681 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -182,7 +182,7 @@
   global TOTAL_VARIANTS_SET
   # TODO: Avoid duplication of the variant names in different lists.
   VARIANT_TYPE_DICT['run'] = {'ndebug', 'debug'}
-  VARIANT_TYPE_DICT['target'] = {'target', 'host', 'jvm'}
+  VARIANT_TYPE_DICT['target'] = {'target', 'host', 'jvm', 'simulate-arm64'}
   VARIANT_TYPE_DICT['trace'] = {'trace', 'ntrace', 'stream'}
   VARIANT_TYPE_DICT['image'] = {'picimage', 'no-image'}
   VARIANT_TYPE_DICT['debuggable'] = {'ndebuggable', 'debuggable'}
@@ -254,6 +254,7 @@
 
   _user_input_variants['address_sizes_target'] = collections.defaultdict(set)
   if not _user_input_variants['address_sizes']:
+    _user_input_variants['address_sizes_target']['simulate-arm64'].add('64')
     _user_input_variants['address_sizes_target']['target'].add(
         env.ART_PHONY_TEST_TARGET_SUFFIX)
     _user_input_variants['address_sizes_target']['host'].add(
@@ -423,6 +424,8 @@
 
       if target == 'host':
         options_test += ' --host'
+      elif target == 'simulate-arm64':
+        options_test += ' --host --simulate-arm64'
       elif target == 'jvm':
         options_test += ' --jvm'