diff options
29 files changed, 1395 insertions, 48 deletions
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py index 1a337cbe3e..021fd5af81 100755 --- a/build/apex/art_apex_test.py +++ b/build/apex/art_apex_test.py @@ -546,6 +546,8 @@ class ReleaseChecker: 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 @@ class DebugChecker: 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 @@ class TestingTargetChecker: # 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 9d15f1f294..f873e7514e 100644 --- a/compiler/optimizing/codegen_test_utils.h +++ b/compiler/optimizing/codegen_test_utils.h @@ -223,12 +223,15 @@ static void VerifyGeneratedCode(InstructionSet target_isa, 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 e3200c4394..f82a623c71 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -410,6 +410,7 @@ libart_cc_defaults { 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 @@ libart_static_cc_defaults { name: "libart_static_base_defaults", static_libs: [ "libartpalette", + "libart-simulator-container", "libbacktrace", "libbase", "liblog", @@ -529,6 +531,7 @@ art_cc_library { ], 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 @@ art_cc_library { ], 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 2db2faa408..3c70a92612 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 @@ ArtMethod* ArtMethod::GetSingleImplementation(PointerSize pointer_size) { 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 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* } // 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 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* << "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 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { 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 16b4648821..7f6004ace7 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -384,6 +384,8 @@ class ArtMethod final { 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 8bf38d35b2..3ea4a8d06f 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2185,9 +2185,14 @@ bool ClassLinker::AddImageSpace( 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 @@ bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* return true; } + if (Runtime::SimulatorMode()) { + return !method->CanBeSimulated(); + } + Runtime* runtime = Runtime::Current(); instrumentation::Instrumentation* instr = runtime->GetInstrumentation(); if (instr->InterpretOnly()) { @@ -3671,7 +3680,7 @@ void ClassLinker::FixupStaticTrampolines(Thread* self, ObjPtr<mirror::Class> kla } // 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 @@ static void LinkCode(ClassLinker* class_linker, // 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 6bd1c8f933..88992f126a 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 @@ bool ElfFileImpl<ElfTypes>::Load(File* file, 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 52c4142712..e3f0d00440 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -136,12 +136,12 @@ class EntrypointsOrderTest : public CommonRuntimeTest { 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 c6d3258766..01a6213a26 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -150,6 +150,9 @@ bool CanUseMterp() 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 11e02a2ce4..27dd9709bd 100644 --- a/runtime/native/java_lang_VMClassLoader.cc +++ b/runtime/native/java_lang_VMClassLoader.cc @@ -143,8 +143,10 @@ static jobjectArray VMClassLoader_getBootClassPathEntries(JNIEnv* env, jclass) { 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 9c169e6c63..de3d878302 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -111,9 +111,9 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, 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 85992ea191..1480c1db6f 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -236,10 +236,10 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( 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 0a52e7e861..dab0f55df8 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -422,6 +422,11 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .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 8873eb9c60..60a0837819 100644 --- a/runtime/parsed_options_test.cc +++ b/runtime/parsed_options_test.cc @@ -109,6 +109,7 @@ TEST_F(ParsedOptionsTest, ParsedOptions) { 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, ParsedOptionsInstructionSet) { } } +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 fc1a3c8031..79b51daff7 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 @@ Runtime::Runtime() 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 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { 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 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { 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 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { 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 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { } // 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 @@ void Runtime::BroadcastForNewSystemWeaks(bool broadcast_for_checkpoint) { } } +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 @@ void Runtime::ClearInstructionSet() { 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 @@ void Runtime::AddCurrentRuntimeFeaturesAsDex2OatArguments(std::vector<std::strin // 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 594edaae39..0ee280c359 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -461,6 +461,10 @@ class Runtime { 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 @@ class Runtime { 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 @@ class Runtime { 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 a3429732f3..ce5a16b125 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -139,6 +139,8 @@ RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::k 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 21b8d05087..b71c41e48e 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 @@ void Thread::SetIsGcMarkingAndUpdateEntrypoints(bool is_marking) { 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 @@ void Thread::InitTlsEntryPoints() { *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 @@ bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_en return false; } InitCpu(); + if (Runtime::SimulatorMode()) { + InitSimulator(); + } InitTlsEntryPoints(); RemoveSuspendTrigger(); InitCardTable(); @@ -2488,6 +2510,10 @@ Thread::~Thread() { 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 d2833b0372..c2416e61f9 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -49,6 +49,8 @@ class BacktraceMap; namespace art { +class CodeSimulatorContainer; + namespace gc { namespace accounting { template<class T> class AtomicStack; @@ -192,6 +194,8 @@ class Thread { // 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 @@ class Thread { REQUIRES(Locks::runtime_shutdown_lock_); void InitCardTable(); void InitCpu(); + void InitSimulator(); void CleanupCpu(); void InitTlsEntryPoints(); void InitTid(); @@ -1683,7 +1688,7 @@ class Thread { 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 @@ class Thread { // 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 1410444a3d..d71e56578c 100644 --- a/simulator/Android.bp +++ b/simulator/Android.bp @@ -18,12 +18,15 @@ cc_library_headers { 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 @@ cc_defaults { ], 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 @@ art_cc_library { "libartbase", "libvixl", ], + device_supported: false, } art_cc_library { @@ -60,6 +69,7 @@ art_cc_library { "libartbased", "libvixld", ], + device_supported: false, } cc_defaults { @@ -73,8 +83,12 @@ cc_defaults { 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 @@ art_cc_library { defaults: ["libart_simulator_container_defaults"], shared_libs: [ "libartbase", - "libart", + ], + apex_available: [ + "com.android.art.release", + "com.android.art.debug", ], } @@ -95,7 +112,6 @@ art_cc_library { ], 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 a64bd0bc0b..7521d183b8 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 @@ CodeSimulatorArm64::CodeSimulatorArm64() : 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 @@ CodeSimulatorArm64::~CodeSimulatorArm64() { 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 @@ int64_t CodeSimulatorArm64::GetCReturnInt64() const { 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 e726500452..ea5c95a3cc 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 @@ class CodeSimulatorArm64 : public CodeSimulator { 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 256ab23aa4..22bac1e83f 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 @@ class CodeSimulator { 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); }; @@ -33,6 +33,7 @@ parser.add_argument('--run-test', '-r', action='store_true', dest='run_test', he 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 0000000000..c575c35f09 --- /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 487958f40a..a7190ce6a9 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -109,6 +109,7 @@ ANDROID_FLAGS="${ANDROID_FLAGS} -XX:SlowDebug=true" # 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 @@ while true; do 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_locations="" 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 @@ if [ ${#VDEX_NAME} -gt $max_filename_size ]; then 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 @@ else 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 6143dc7e90..1eb8e79b58 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 86d30d526f..cfe8111476 100755 --- a/test/run-test +++ b/test/run-test @@ -141,6 +141,7 @@ dev_mode="no" create_runner="no" update_mode="no" debug_mode="no" +simulator_mode="no" relocate="no" runtime="art" usage="no" @@ -181,6 +182,9 @@ while true; do 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 @@ if [ "$runtime" = "dalvik" ]; then 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 [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]]; then 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 b8e8cb7808..f547681e62 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -182,7 +182,7 @@ def gather_test_info(): 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 @@ def setup_test_env(): _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 @@ def run_tests(tests): if target == 'host': options_test += ' --host' + elif target == 'simulate-arm64': + options_test += ' --host --simulate-arm64' elif target == 'jvm': options_test += ' --jvm' |