diff options
292 files changed, 7442 insertions, 3575 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 1691dbb3bc..bd7f900965 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -26,6 +26,8 @@ GTEST_DEX_DIRECTORIES := \ AbstractMethod \ AllFields \ DexToDexDecompiler \ + ErroneousA \ + ErroneousB \ ExceptionHandle \ GetMethodSignature \ ImageLayoutA \ @@ -85,7 +87,8 @@ $(ART_TEST_TARGET_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $( ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces -ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode +ART_GTEST_class_linker_test_DEX_DEPS := ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode +ART_GTEST_class_table_test_DEX_DEPS := XandY ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested @@ -598,6 +601,7 @@ ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES := ART_TEST_TARGET_VALGRIND_GTEST_RULES := ART_GTEST_TARGET_ANDROID_ROOT := ART_GTEST_class_linker_test_DEX_DEPS := +ART_GTEST_class_table_test_DEX_DEPS := ART_GTEST_compiler_driver_test_DEX_DEPS := ART_GTEST_dex_file_test_DEX_DEPS := ART_GTEST_exception_test_DEX_DEPS := diff --git a/build/art.go b/build/art.go index ccaa11dfe7..e6e0544e4d 100644 --- a/build/art.go +++ b/build/art.go @@ -58,7 +58,7 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) { asflags = append(asflags, "-DART_HEAP_POISONING=1") } - if envTrue(ctx, "ART_USE_READ_BARRIER") { + if envTrue(ctx, "ART_USE_READ_BARRIER") || ctx.AConfig().ArtUseReadBarrier() { // Used to change the read barrier type. Valid values are BAKER, BROOKS, TABLELOOKUP. // The default is BAKER. barrierType := envDefault(ctx, "ART_READ_BARRIER_TYPE", "BAKER") @@ -259,7 +259,7 @@ func artDefaultsFactory() (blueprint.Module, []interface{}) { } func artLibrary() (blueprint.Module, []interface{}) { - library, _ := cc.NewLibrary(android.HostAndDeviceSupported, true, true) + library, _ := cc.NewLibrary(android.HostAndDeviceSupported) module, props := library.Init() props = installCodegenCustomizer(module, props, true) diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h index 6e042c3c27..f4540ff655 100644 --- a/cmdline/cmdline.h +++ b/cmdline/cmdline.h @@ -24,10 +24,12 @@ #include <iostream> #include <string> -#include "runtime.h" +#include "android-base/stringprintf.h" + +#include "base/logging.h" #include "base/stringpiece.h" #include "noop_compiler_callbacks.h" -#include "base/logging.h" +#include "runtime.h" #if !defined(NDEBUG) #define DBG_LOG LOG(INFO) @@ -197,7 +199,7 @@ struct CmdlineArgs { " Example: --boot-image=/system/framework/boot.art\n" " (specifies /system/framework/<arch>/boot.art as the image file)\n" "\n"; - usage += StringPrintf( // Optional. + usage += android::base::StringPrintf( // Optional. " --instruction-set=(arm|arm64|mips|mips64|x86|x86_64): for locating the image\n" " file based on the image location set.\n" " Example: --instruction-set=x86\n" @@ -264,8 +266,8 @@ struct CmdlineArgs { // Check that the boot image location points to a valid file name. std::string file_name; if (!LocationToFilename(boot_image_location, instruction_set_, &file_name)) { - *error_msg = StringPrintf("No corresponding file for location '%s' exists", - file_name.c_str()); + *error_msg = android::base::StringPrintf("No corresponding file for location '%s' exists", + file_name.c_str()); return false; } diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 156ca9ef3e..e41d9bde59 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -22,6 +22,8 @@ #include "detail/cmdline_debug_detail.h" #include "cmdline_type_parser.h" +#include "android-base/strings.h" + // Includes for the types that are being specialized #include <string> #include "base/logging.h" @@ -447,7 +449,7 @@ struct ParseStringList { } std::string Join() const { - return art::Join(list_, Separator); + return android::base::Join(list_, Separator); } static ParseStringList<Separator> Split(const std::string& str) { @@ -709,43 +711,43 @@ struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions> // The rest of these options are always the wildcard from '-Xps-*' std::string suffix = RemovePrefix(option); - if (StartsWith(option, "min-save-period-ms:")) { + if (android::base::StartsWith(option, "min-save-period-ms:")) { CmdlineType<unsigned int> type_parser; return ParseInto(existing, &ProfileSaverOptions::min_save_period_ms_, type_parser.Parse(suffix)); } - if (StartsWith(option, "save-resolved-classes-delay-ms:")) { + if (android::base::StartsWith(option, "save-resolved-classes-delay-ms:")) { CmdlineType<unsigned int> type_parser; return ParseInto(existing, &ProfileSaverOptions::save_resolved_classes_delay_ms_, type_parser.Parse(suffix)); } - if (StartsWith(option, "startup-method-samples:")) { + if (android::base::StartsWith(option, "startup-method-samples:")) { CmdlineType<unsigned int> type_parser; return ParseInto(existing, &ProfileSaverOptions::startup_method_samples_, type_parser.Parse(suffix)); } - if (StartsWith(option, "min-methods-to-save:")) { + if (android::base::StartsWith(option, "min-methods-to-save:")) { CmdlineType<unsigned int> type_parser; return ParseInto(existing, &ProfileSaverOptions::min_methods_to_save_, type_parser.Parse(suffix)); } - if (StartsWith(option, "min-classes-to-save:")) { + if (android::base::StartsWith(option, "min-classes-to-save:")) { CmdlineType<unsigned int> type_parser; return ParseInto(existing, &ProfileSaverOptions::min_classes_to_save_, type_parser.Parse(suffix)); } - if (StartsWith(option, "min-notification-before-wake:")) { + if (android::base::StartsWith(option, "min-notification-before-wake:")) { CmdlineType<unsigned int> type_parser; return ParseInto(existing, &ProfileSaverOptions::min_notification_before_wake_, type_parser.Parse(suffix)); } - if (StartsWith(option, "max-notification-before-wake:")) { + if (android::base::StartsWith(option, "max-notification-before-wake:")) { CmdlineType<unsigned int> type_parser; return ParseInto(existing, &ProfileSaverOptions::max_notification_before_wake_, diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h index 14eac30aa1..da03c2198f 100644 --- a/cmdline/detail/cmdline_parse_argument_detail.h +++ b/cmdline/detail/cmdline_parse_argument_detail.h @@ -25,6 +25,8 @@ #include <numeric> #include <memory> +#include "android-base/strings.h" + #include "cmdline_parse_result.h" #include "cmdline_types.h" #include "token_range.h" @@ -399,7 +401,7 @@ namespace art { allowed_values.push_back(name); } - std::string allowed_values_flat = Join(allowed_values, ','); + std::string allowed_values_flat = android::base::Join(allowed_values, ','); return CmdlineResult(CmdlineResult::kFailure, "Argument value '" + argument + "' does not match any of known valid" "values: {" + allowed_values_flat + "}"); @@ -426,7 +428,7 @@ namespace art { allowed_values.push_back(arg_name); } - std::string allowed_values_flat = Join(allowed_values, ','); + std::string allowed_values_flat = android::base::Join(allowed_values, ','); return CmdlineResult(CmdlineResult::kFailure, "Argument value '" + argument + "' does not match any of known valid" "values: {" + allowed_values_flat + "}"); diff --git a/cmdline/token_range.h b/cmdline/token_range.h index 335806795a..c22d6c8959 100644 --- a/cmdline/token_range.h +++ b/cmdline/token_range.h @@ -23,6 +23,8 @@ #include <algorithm> #include <memory> +#include "android-base/strings.h" + namespace art { // A range of tokens to make token matching algorithms easier. // @@ -374,7 +376,7 @@ struct TokenRange { // e.g. ["hello", "world"].join('$') == "hello$world" std::string Join(char separator) const { TokenList tmp(begin(), end()); - return art::Join(tmp, separator); + return android::base::Join(tmp, separator); // TODO: Join should probably take an offset or iterators } diff --git a/compiler/Android.bp b/compiler/Android.bp index db55ea0ef7..46f3358af1 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -49,6 +49,7 @@ art_cc_defaults { "optimizing/block_builder.cc", "optimizing/bounds_check_elimination.cc", "optimizing/builder.cc", + "optimizing/cha_guard_optimization.cc", "optimizing/code_generator.cc", "optimizing/code_generator_utils.cc", "optimizing/constant_folding.cc", @@ -145,6 +146,7 @@ art_cc_defaults { mips64: { srcs: [ "jni/quick/mips64/calling_convention_mips64.cc", + "linker/mips64/relative_patcher_mips64.cc", "optimizing/code_generator_mips64.cc", "optimizing/intrinsics_mips64.cc", "utils/mips64/assembler_mips64.cc", @@ -381,6 +383,11 @@ art_cc_test { "linker/mips/relative_patcher_mips32r6_test.cc", ], }, + mips64: { + srcs: [ + "linker/mips64/relative_patcher_mips64_test.cc", + ], + }, x86: { srcs: [ "linker/x86/relative_patcher_x86_test.cc", diff --git a/compiler/compiled_class.h b/compiler/compiled_class.h index b88d613ad5..06ce946942 100644 --- a/compiler/compiled_class.h +++ b/compiler/compiled_class.h @@ -28,8 +28,11 @@ class CompiledClass { mirror::Class::Status GetStatus() const { return status_; } + void SetStatus(mirror::Class::Status status) { + status_ = status; + } private: - const mirror::Class::Status status_; + mirror::Class::Status status_; }; } // namespace art diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index cf69f469a0..d4f6545c59 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -16,6 +16,8 @@ #include "dex_to_dex_compiler.h" +#include "android-base/stringprintf.h" + #include "art_field-inl.h" #include "art_method-inl.h" #include "base/logging.h" @@ -32,6 +34,8 @@ namespace art { namespace optimizer { +using android::base::StringPrintf; + // Controls quickening activation. const bool kEnableQuickening = true; // Control check-cast elision. diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc index 051125eeaa..bfd485d126 100644 --- a/compiler/dex/dex_to_dex_decompiler.cc +++ b/compiler/dex/dex_to_dex_decompiler.cc @@ -27,10 +27,13 @@ namespace optimizer { class DexDecompiler { public: - DexDecompiler(const DexFile::CodeItem& code_item, const ArrayRef<const uint8_t>& quickened_info) + DexDecompiler(const DexFile::CodeItem& code_item, + const ArrayRef<const uint8_t>& quickened_info, + bool decompile_return_instruction) : code_item_(code_item), quickened_info_ptr_(quickened_info.data()), - quickened_info_end_(quickened_info.data() + quickened_info.size()) {} + quickened_info_end_(quickened_info.data() + quickened_info.size()), + decompile_return_instruction_(decompile_return_instruction) {} bool Decompile(); @@ -87,6 +90,7 @@ class DexDecompiler { const DexFile::CodeItem& code_item_; const uint8_t* quickened_info_ptr_; const uint8_t* const quickened_info_end_; + const bool decompile_return_instruction_; DISALLOW_COPY_AND_ASSIGN(DexDecompiler); }; @@ -102,7 +106,9 @@ bool DexDecompiler::Decompile() { switch (inst->Opcode()) { case Instruction::RETURN_VOID_NO_BARRIER: - inst->SetOpcode(Instruction::RETURN_VOID); + if (decompile_return_instruction_) { + inst->SetOpcode(Instruction::RETURN_VOID); + } break; case Instruction::NOP: @@ -189,8 +195,12 @@ bool DexDecompiler::Decompile() { } bool ArtDecompileDEX(const DexFile::CodeItem& code_item, - const ArrayRef<const uint8_t>& quickened_info) { - DexDecompiler decompiler(code_item, quickened_info); + const ArrayRef<const uint8_t>& quickened_info, + bool decompile_return_instruction) { + if (quickened_info.size() == 0 && !decompile_return_instruction) { + return true; + } + DexDecompiler decompiler(code_item, quickened_info, decompile_return_instruction); return decompiler.Decompile(); } diff --git a/compiler/dex/dex_to_dex_decompiler.h b/compiler/dex/dex_to_dex_decompiler.h index 5502ca2d92..b5d5b91915 100644 --- a/compiler/dex/dex_to_dex_decompiler.h +++ b/compiler/dex/dex_to_dex_decompiler.h @@ -30,7 +30,8 @@ namespace optimizer { // consistent with DexToDexCompiler, but we should really change it to // DexFile::CodeItem*. bool ArtDecompileDEX(const DexFile::CodeItem& code_item, - const ArrayRef<const uint8_t>& quickened_data); + const ArrayRef<const uint8_t>& quickened_data, + bool decompile_return_instruction); } // namespace optimizer } // namespace art diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc index ea6c7a2e09..9a8d27cd03 100644 --- a/compiler/dex/dex_to_dex_decompiler_test.cc +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -102,7 +102,8 @@ class DexToDexDecompilerTest : public CommonCompilerTest { if (compiled_method != nullptr) { table = compiled_method->GetVmapTable(); } - optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table); + optimizer::ArtDecompileDEX( + *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true); it.Next(); } while (it.HasNextVirtualMethod()) { @@ -113,7 +114,8 @@ class DexToDexDecompilerTest : public CommonCompilerTest { if (compiled_method != nullptr) { table = compiled_method->GetVmapTable(); } - optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table); + optimizer::ArtDecompileDEX( + *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true); it.Next(); } DCHECK(!it.HasNext()); diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index 9d39bf2c7a..00a7d44bac 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -46,9 +46,7 @@ VerificationResults::~VerificationResults() { void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) { DCHECK(method_verifier != nullptr); MethodReference ref = method_verifier->GetMethodReference(); - bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags()); - std::unique_ptr<const VerifiedMethod> verified_method( - VerifiedMethod::Create(method_verifier, compile)); + std::unique_ptr<const VerifiedMethod> verified_method(VerifiedMethod::Create(method_verifier)); if (verified_method == nullptr) { // We'll punt this later. return; @@ -84,7 +82,6 @@ void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method // TODO: Investigate why are we doing the work again for this method and try to avoid it. LOG(WARNING) << "Method processed more than once: " << ref.PrettyMethod(); if (!Runtime::Current()->UseJitCompilation()) { - DCHECK_EQ(existing->GetDevirtMap().size(), verified_method->GetDevirtMap().size()); DCHECK_EQ(existing->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size()); } // Let the unique_ptr delete the new verified method since there was already an existing one diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc index 1bdace9284..cbca33320d 100644 --- a/compiler/dex/verified_method.cc +++ b/compiler/dex/verified_method.cc @@ -42,25 +42,12 @@ VerifiedMethod::VerifiedMethod(uint32_t encountered_error_types, bool has_runtim has_runtime_throw_(has_runtime_throw) { } -const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier, - bool compile) { +const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier) { + DCHECK(Runtime::Current()->IsAotCompiler()); std::unique_ptr<VerifiedMethod> verified_method( new VerifiedMethod(method_verifier->GetEncounteredFailureTypes(), method_verifier->HasInstructionThatWillThrow())); - if (compile) { - // TODO: move this out when DEX-to-DEX supports devirtualization. - if (method_verifier->HasVirtualOrInterfaceInvokes()) { - verified_method->GenerateDevirtMap(method_verifier); - } - - // Only need dequicken info for JIT so far. - if (Runtime::Current()->UseJitCompilation() && - !verified_method->GenerateDequickenMap(method_verifier)) { - return nullptr; - } - } - if (method_verifier->HasCheckCasts()) { verified_method->GenerateSafeCastSet(method_verifier); } @@ -68,140 +55,10 @@ const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_ve return verified_method.release(); } -const MethodReference* VerifiedMethod::GetDevirtTarget(uint32_t dex_pc) const { - auto it = devirt_map_.find(dex_pc); - return (it != devirt_map_.end()) ? &it->second : nullptr; -} - -const DexFileReference* VerifiedMethod::GetDequickenIndex(uint32_t dex_pc) const { - DCHECK(Runtime::Current()->UseJitCompilation()); - auto it = dequicken_map_.find(dex_pc); - return (it != dequicken_map_.end()) ? &it->second : nullptr; -} - bool VerifiedMethod::IsSafeCast(uint32_t pc) const { return std::binary_search(safe_cast_set_.begin(), safe_cast_set_.end(), pc); } -bool VerifiedMethod::GenerateDequickenMap(verifier::MethodVerifier* method_verifier) { - if (method_verifier->HasFailures()) { - return false; - } - const DexFile::CodeItem* code_item = method_verifier->CodeItem(); - const uint16_t* insns = code_item->insns_; - const Instruction* inst = Instruction::At(insns); - const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_); - for (; inst < end; inst = inst->Next()) { - const bool is_virtual_quick = inst->Opcode() == Instruction::INVOKE_VIRTUAL_QUICK; - const bool is_range_quick = inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK; - if (is_virtual_quick || is_range_quick) { - uint32_t dex_pc = inst->GetDexPc(insns); - verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc); - ArtMethod* method = - method_verifier->GetQuickInvokedMethod(inst, line, is_range_quick, true); - if (method == nullptr) { - // It can be null if the line wasn't verified since it was unreachable. - return false; - } - // The verifier must know what the type of the object was or else we would have gotten a - // failure. Put the dex method index in the dequicken map since we need this to get number of - // arguments in the compiler. - dequicken_map_.Put(dex_pc, DexFileReference(method->GetDexFile(), - method->GetDexMethodIndex())); - } else if (IsInstructionIGetQuickOrIPutQuick(inst->Opcode())) { - uint32_t dex_pc = inst->GetDexPc(insns); - verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc); - ArtField* field = method_verifier->GetQuickFieldAccess(inst, line); - if (field == nullptr) { - // It can be null if the line wasn't verified since it was unreachable. - return false; - } - // The verifier must know what the type of the field was or else we would have gotten a - // failure. Put the dex field index in the dequicken map since we need this for lowering - // in the compiler. - // TODO: Putting a field index in a method reference is gross. - dequicken_map_.Put(dex_pc, DexFileReference(field->GetDexFile(), field->GetDexFieldIndex())); - } - } - return true; -} - -void VerifiedMethod::GenerateDevirtMap(verifier::MethodVerifier* method_verifier) { - // It is risky to rely on reg_types for sharpening in cases of soft - // verification, we might end up sharpening to a wrong implementation. Just abort. - if (method_verifier->HasFailures()) { - return; - } - - const DexFile::CodeItem* code_item = method_verifier->CodeItem(); - const uint16_t* insns = code_item->insns_; - const Instruction* inst = Instruction::At(insns); - const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_); - - for (; inst < end; inst = inst->Next()) { - const bool is_virtual = inst->Opcode() == Instruction::INVOKE_VIRTUAL || - inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE; - const bool is_interface = inst->Opcode() == Instruction::INVOKE_INTERFACE || - inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE; - - if (!is_interface && !is_virtual) { - continue; - } - // Get reg type for register holding the reference to the object that will be dispatched upon. - uint32_t dex_pc = inst->GetDexPc(insns); - verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc); - const bool is_range = inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE || - inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE; - const verifier::RegType& - reg_type(line->GetRegisterType(method_verifier, - is_range ? inst->VRegC_3rc() : inst->VRegC_35c())); - - if (!reg_type.HasClass()) { - // We will compute devirtualization information only when we know the Class of the reg type. - continue; - } - mirror::Class* reg_class = reg_type.GetClass(); - if (reg_class->IsInterface()) { - // We can't devirtualize when the known type of the register is an interface. - continue; - } - if (reg_class->IsAbstract() && !reg_class->IsArrayClass()) { - // We can't devirtualize abstract classes except on arrays of abstract classes. - continue; - } - auto* cl = Runtime::Current()->GetClassLinker(); - PointerSize pointer_size = cl->GetImagePointerSize(); - ArtMethod* abstract_method = method_verifier->GetDexCache()->GetResolvedMethod( - is_range ? inst->VRegB_3rc() : inst->VRegB_35c(), pointer_size); - if (abstract_method == nullptr) { - // If the method is not found in the cache this means that it was never found - // by ResolveMethodAndCheckAccess() called when verifying invoke_*. - continue; - } - // Find the concrete method. - ArtMethod* concrete_method = nullptr; - if (is_interface) { - concrete_method = reg_type.GetClass()->FindVirtualMethodForInterface( - abstract_method, pointer_size); - } - if (is_virtual) { - concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual( - abstract_method, pointer_size); - } - if (concrete_method == nullptr || !concrete_method->IsInvokable()) { - // In cases where concrete_method is not found, or is not invokable, continue to the next - // invoke. - continue; - } - if (reg_type.IsPreciseReference() || concrete_method->IsFinal() || - concrete_method->GetDeclaringClass()->IsFinal()) { - // If we knew exactly the class being dispatched upon, or if the target method cannot be - // overridden record the target to be used in the compiler driver. - devirt_map_.Put(dex_pc, concrete_method->ToMethodReference()); - } - } -} - void VerifiedMethod::GenerateSafeCastSet(verifier::MethodVerifier* method_verifier) { /* * Walks over the method code and adds any cast instructions in which @@ -218,35 +75,32 @@ void VerifiedMethod::GenerateSafeCastSet(verifier::MethodVerifier* method_verifi for (; inst < end; inst = inst->Next()) { Instruction::Code code = inst->Opcode(); - if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) { + if (code == Instruction::CHECK_CAST) { uint32_t dex_pc = inst->GetDexPc(code_item->insns_); if (!method_verifier->GetInstructionFlags(dex_pc).IsVisited()) { // Do not attempt to quicken this instruction, it's unreachable anyway. continue; } const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc); - bool is_safe_cast = false; - if (code == Instruction::CHECK_CAST) { - const verifier::RegType& reg_type(line->GetRegisterType(method_verifier, - inst->VRegA_21c())); - const verifier::RegType& cast_type = - method_verifier->ResolveCheckedClass(dex::TypeIndex(inst->VRegB_21c())); - is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type, method_verifier); - } else { - const verifier::RegType& array_type(line->GetRegisterType(method_verifier, - inst->VRegB_23x())); - // We only know its safe to assign to an array if the array type is precise. For example, - // an Object[] can have any type of object stored in it, but it may also be assigned a - // String[] in which case the stores need to be of Strings. - if (array_type.IsPreciseReference()) { - const verifier::RegType& value_type(line->GetRegisterType(method_verifier, - inst->VRegA_23x())); - const verifier::RegType& component_type = method_verifier->GetRegTypeCache() - ->GetComponentType(array_type, method_verifier->GetClassLoader()); - is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type, method_verifier); + const verifier::RegType& reg_type(line->GetRegisterType(method_verifier, + inst->VRegA_21c())); + const verifier::RegType& cast_type = + method_verifier->ResolveCheckedClass(dex::TypeIndex(inst->VRegB_21c())); + // Pass null for the method verifier to not record the VerifierDeps dependency + // if the types are not assignable. + if (cast_type.IsStrictlyAssignableFrom(reg_type, /* method_verifier */ nullptr)) { + // The types are assignable, we record that dependency in the VerifierDeps so + // that if this changes after OTA, we will re-verify again. + // We check if reg_type has a class, as the verifier may have inferred it's + // 'null'. + if (reg_type.HasClass()) { + DCHECK(cast_type.HasClass()); + verifier::VerifierDeps::MaybeRecordAssignability(method_verifier->GetDexFile(), + cast_type.GetClass(), + reg_type.GetClass(), + /* strict */ true, + /* assignable */ true); } - } - if (is_safe_cast) { // Verify ordering for push_back() to the sorted vector. DCHECK(safe_cast_set_.empty() || safe_cast_set_.back() < dex_pc); safe_cast_set_.push_back(dex_pc); diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h index ce53417185..439e69ece9 100644 --- a/compiler/dex/verified_method.h +++ b/compiler/dex/verified_method.h @@ -39,31 +39,14 @@ class VerifiedMethod { // is better for performance (not just memory usage), especially for large sets. typedef std::vector<uint32_t> SafeCastSet; - // Devirtualization map type maps dex offset to concrete method reference. - typedef SafeMap<uint32_t, MethodReference> DevirtualizationMap; - - // Devirtualization map type maps dex offset to field / method idx. - typedef SafeMap<uint32_t, DexFileReference> DequickenMap; - - static const VerifiedMethod* Create(verifier::MethodVerifier* method_verifier, bool compile) + static const VerifiedMethod* Create(verifier::MethodVerifier* method_verifier) REQUIRES_SHARED(Locks::mutator_lock_); ~VerifiedMethod() = default; - const DevirtualizationMap& GetDevirtMap() const { - return devirt_map_; - } - const SafeCastSet& GetSafeCastSet() const { return safe_cast_set_; } - // Returns the devirtualization target method, or null if none. - const MethodReference* GetDevirtTarget(uint32_t dex_pc) const; - - // Returns the dequicken field / method for a quick invoke / field get. Returns null if there is - // no entry for that dex pc. - const DexFileReference* GetDequickenIndex(uint32_t dex_pc) const; - // Returns true if the cast can statically be verified to be redundant // by using the check-cast elision peephole optimization in the verifier. bool IsSafeCast(uint32_t pc) const; @@ -82,38 +65,10 @@ class VerifiedMethod { } private: - /* - * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of - * verification). For type-precise determination we have all the data we need, so we just need to - * encode it in some clever fashion. - * Stores the data in dex_gc_map_, returns true on success and false on failure. - */ - bool GenerateGcMap(verifier::MethodVerifier* method_verifier); - - // Verify that the GC map associated with method_ is well formed. - static void VerifyGcMap(verifier::MethodVerifier* method_verifier, - const std::vector<uint8_t>& data); - - // Compute sizes for GC map data. - static void ComputeGcMapSizes(verifier::MethodVerifier* method_verifier, - size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc); - - // Generate devirtualizaion map into devirt_map_. - void GenerateDevirtMap(verifier::MethodVerifier* method_verifier) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Generate dequickening map into dequicken_map_. Returns false if there is an error. - bool GenerateDequickenMap(verifier::MethodVerifier* method_verifier) - REQUIRES_SHARED(Locks::mutator_lock_); - // Generate safe case set into safe_cast_set_. void GenerateSafeCastSet(verifier::MethodVerifier* method_verifier) REQUIRES_SHARED(Locks::mutator_lock_); - DevirtualizationMap devirt_map_; - // Dequicken map is required for compiling quickened byte codes. The quicken maps from - // dex PC to dex method index or dex field index based on the instruction. - DequickenMap dequicken_map_; SafeCastSet safe_cast_set_; const uint32_t encountered_error_types_; diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index e62bdb5530..29502666ad 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -299,7 +299,7 @@ CompilerDriver::CompilerDriver( dump_passes_(dump_passes), timings_logger_(timer), compiler_context_(nullptr), - support_boot_image_fixup_(instruction_set != kMips64), + support_boot_image_fixup_(true), dex_files_for_oat_file_(nullptr), compiled_method_storage_(swap_fd), profile_compilation_info_(profile_compilation_info), @@ -423,7 +423,7 @@ INTRINSICS_LIST(SETUP_INTRINSICS) // Compile: // 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex // compilation. - if (!GetCompilerOptions().VerifyAtRuntime()) { + if (GetCompilerOptions().IsAnyMethodCompilationEnabled()) { Compile(class_loader, dex_files, timings); } if (dump_stats_) { @@ -435,7 +435,8 @@ INTRINSICS_LIST(SETUP_INTRINSICS) // In-place unquicken the given `dex_files` based on `quickening_info`. static void Unquicken(const std::vector<const DexFile*>& dex_files, - const ArrayRef<const uint8_t>& quickening_info) { + const ArrayRef<const uint8_t>& quickening_info, + bool decompile_return_instruction) { const uint8_t* quickening_info_ptr = quickening_info.data(); const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size(); for (const DexFile* dex_file : dex_files) { @@ -454,14 +455,14 @@ static void Unquicken(const std::vector<const DexFile*>& dex_files, it.Next(); } - // Unquicken each method. while (it.HasNextDirectMethod()) { const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); if (code_item != nullptr) { uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr); quickening_info_ptr += sizeof(uint32_t); - optimizer::ArtDecompileDEX( - *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size)); + optimizer::ArtDecompileDEX(*code_item, + ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size), + decompile_return_instruction); quickening_info_ptr += quickening_size; } it.Next(); @@ -472,8 +473,9 @@ static void Unquicken(const std::vector<const DexFile*>& dex_files, if (code_item != nullptr) { uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr); quickening_info_ptr += sizeof(uint32_t); - optimizer::ArtDecompileDEX( - *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size)); + optimizer::ArtDecompileDEX(*code_item, + ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size), + decompile_return_instruction); quickening_info_ptr += quickening_size; } it.Next(); @@ -492,7 +494,15 @@ void CompilerDriver::CompileAll(jobject class_loader, // TODO: we unquicken unconditionnally, as we don't know // if the boot image has changed. How exactly we'll know is under // experimentation. - Unquicken(dex_files, vdex_file->GetQuickeningInfo()); + if (vdex_file->GetQuickeningInfo().size() != 0) { + TimingLogger::ScopedTiming t("Unquicken", timings); + // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening + // optimization does not depend on the boot image (the optimization relies on not + // having final fields in a class, which does not change for an app). + Unquicken(dex_files, + vdex_file->GetQuickeningInfo(), + /* decompile_return_instruction */ false); + } Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps( new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData())); } @@ -504,10 +514,7 @@ static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel( const DexFile& dex_file, const DexFile::ClassDef& class_def) REQUIRES_SHARED(Locks::mutator_lock_) { auto* const runtime = Runtime::Current(); - if (runtime->UseJitCompilation() || driver.GetCompilerOptions().VerifyAtRuntime()) { - // Verify at runtime shouldn't dex to dex since we didn't resolve of verify. - return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; - } + DCHECK(driver.GetCompilerOptions().IsAnyMethodCompilationEnabled()); const char* descriptor = dex_file.GetClassDescriptor(class_def); ClassLinker* class_linker = runtime->GetClassLinker(); mirror::Class* klass = class_linker->FindClass(self, descriptor, class_loader); @@ -948,24 +955,17 @@ void CompilerDriver::PreCompile(jobject class_loader, LoadImageClasses(timings); VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false); - const bool verification_enabled = compiler_options_->IsVerificationEnabled(); - const bool never_verify = compiler_options_->NeverVerify(); - const bool verify_only_profile = compiler_options_->VerifyOnlyProfile(); - - // We need to resolve for never_verify since it needs to run dex to dex to add the - // RETURN_VOID_NO_BARRIER. - // Let the verifier resolve as needed for the verify_only_profile case. - if ((never_verify || verification_enabled) && !verify_only_profile) { + if (compiler_options_->IsAnyMethodCompilationEnabled()) { Resolve(class_loader, dex_files, timings); VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false); } - if (never_verify) { + if (compiler_options_->AssumeClassesAreVerified()) { VLOG(compiler) << "Verify none mode specified, skipping verification."; SetVerified(class_loader, dex_files, timings); } - if (!verification_enabled) { + if (!compiler_options_->IsVerificationEnabled()) { return; } @@ -983,8 +983,10 @@ void CompilerDriver::PreCompile(jobject class_loader, << "situations. Please check the log."; } - InitializeClasses(class_loader, dex_files, timings); - VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false); + if (compiler_options_->IsAnyMethodCompilationEnabled()) { + InitializeClasses(class_loader, dex_files, timings); + VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false); + } UpdateImageClasses(timings); VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false); @@ -1529,90 +1531,6 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi } } -void CompilerDriver::GetCodeAndMethodForDirectCall(const mirror::Class* referrer_class, - ArtMethod* method, - uintptr_t* direct_code, - uintptr_t* direct_method) { - // For direct and static methods compute possible direct_code and direct_method values, ie - // an address for the Method* being invoked and an address of the code for that Method*. - // For interface calls compute a value for direct_method that is the interface method being - // invoked, so this can be passed to the out-of-line runtime support code. - *direct_code = 0; - *direct_method = 0; - Runtime* const runtime = Runtime::Current(); - gc::Heap* const heap = runtime->GetHeap(); - auto* cl = runtime->GetClassLinker(); - bool use_dex_cache = GetCompilerOptions().GetCompilePic(); // Off by default - const bool compiling_boot = heap->IsCompilingBoot(); - // TODO This is somewhat hacky. We should refactor all of this invoke codepath. - const bool force_relocations = (compiling_boot || - GetCompilerOptions().GetIncludePatchInformation()); - // TODO: support patching on all architectures. - use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_); - mirror::Class* declaring_class = method->GetDeclaringClass(); - bool method_code_in_boot = declaring_class->GetClassLoader() == nullptr; - if (!use_dex_cache) { - if (!method_code_in_boot) { - use_dex_cache = true; - } else if (method->IsStatic() && - declaring_class != referrer_class && - !declaring_class->IsInitialized()) { - // Ensure we run the clinit trampoline unless we are invoking a static method in the same - // class. - use_dex_cache = true; - } - } - if (runtime->UseJitCompilation()) { - // If we are the JIT, then don't allow a direct call to the interpreter bridge since this will - // never be updated even after we compile the method. - if (cl->IsQuickToInterpreterBridge( - reinterpret_cast<const void*>(compiler_->GetEntryPointOf(method)))) { - use_dex_cache = true; - } - } - - if (!use_dex_cache && force_relocations) { - bool is_in_image; - if (GetCompilerOptions().IsBootImage()) { - is_in_image = IsImageClass(method->GetDeclaringClassDescriptor()); - } else { - is_in_image = instruction_set_ != kX86 && instruction_set_ != kX86_64 && - heap->FindSpaceFromObject(method->GetDeclaringClass(), false)->IsImageSpace() && - !cl->IsQuickToInterpreterBridge( - reinterpret_cast<const void*>(compiler_->GetEntryPointOf(method))); - } - if (!is_in_image) { - // We can only branch directly to Methods that are resolved in the DexCache. - // Otherwise we won't invoke the resolution trampoline. - use_dex_cache = true; - } - } - - if (!use_dex_cache) { - bool method_in_image = false; - const std::vector<gc::space::ImageSpace*>& image_spaces = heap->GetBootImageSpaces(); - for (gc::space::ImageSpace* image_space : image_spaces) { - const auto& method_section = image_space->GetImageHeader().GetMethodsSection(); - if (method_section.Contains(reinterpret_cast<uint8_t*>(method) - image_space->Begin())) { - method_in_image = true; - break; - } - } - if (method_in_image || compiling_boot || runtime->UseJitCompilation()) { - // We know we must be able to get to the method in the image, so use that pointer. - // In the case where we are the JIT, we can always use direct pointers since we know where - // the method and its code are / will be. We don't sharpen to interpreter bridge since we - // check IsQuickToInterpreterBridge above. - CHECK(!method->IsAbstract()); - *direct_method = force_relocations ? -1 : reinterpret_cast<uintptr_t>(method); - *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method); - } else { - // Set the code and rely on the dex cache for the method. - *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method); - } - } -} - const VerifiedMethod* CompilerDriver::GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const { MethodReference ref(dex_file, method_idx); @@ -2060,21 +1978,32 @@ void CompilerDriver::Verify(jobject jclass_loader, std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end()); for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - const char* descriptor = dex_file->GetClassDescriptor(class_def); - cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader)); - if (cls.Get() == nullptr) { - CHECK(soa.Self()->IsExceptionPending()); - soa.Self()->ClearException(); - } else if (set.find(class_def.class_idx_) == set.end()) { - ObjectLock<mirror::Class> lock(soa.Self(), cls); - mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self()); - // Create `VerifiedMethod`s for each methods, the compiler expects one for - // quickening or compiling. - // Note that this means: - // - We're only going to compile methods that did verify. - // - Quickening will not do checkcast ellision. - // TODO(ngeoffray): Reconsider this once we refactor compiler filters. - PopulateVerifiedMethods(*dex_file, i, verification_results_); + if (set.find(class_def.class_idx_) == set.end()) { + if (!GetCompilerOptions().IsAnyMethodCompilationEnabled()) { + // Just update the compiled_classes_ map. The compiler doesn't need to resolve + // the type. + compiled_classes_.Overwrite( + ClassReference(dex_file, i), new CompiledClass(mirror::Class::kStatusVerified)); + } else { + // Resolve the type, so later compilation stages know they don't need to verify + // the class. + const char* descriptor = dex_file->GetClassDescriptor(class_def); + cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader)); + if (cls.Get() != nullptr) { + ObjectLock<mirror::Class> lock(soa.Self(), cls); + mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self()); + } else { + DCHECK(soa.Self()->IsExceptionPending()); + soa.Self()->ClearException(); + } + // Create `VerifiedMethod`s for each methods, the compiler expects one for + // quickening or compiling. + // Note that this means: + // - We're only going to compile methods that did verify. + // - Quickening will not do checkcast ellision. + // TODO(ngeoffray): Reconsider this once we refactor compiler filters. + PopulateVerifiedMethods(*dex_file, i, verification_results_); + } } } } @@ -2675,29 +2604,29 @@ CompiledClass* CompilerDriver::GetCompiledClass(ClassReference ref) const { } void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status status) { + switch (status) { + case mirror::Class::kStatusNotReady: + case mirror::Class::kStatusError: + case mirror::Class::kStatusRetryVerificationAtRuntime: + case mirror::Class::kStatusVerified: + case mirror::Class::kStatusInitialized: + case mirror::Class::kStatusResolved: + break; // Expected states. + default: + LOG(FATAL) << "Unexpected class status for class " + << PrettyDescriptor(ref.first->GetClassDescriptor(ref.first->GetClassDef(ref.second))) + << " of " << status; + } + MutexLock mu(Thread::Current(), compiled_classes_lock_); auto it = compiled_classes_.find(ref); - if (it == compiled_classes_.end() || it->second->GetStatus() != status) { - // An entry doesn't exist or the status is lower than the new status. - if (it != compiled_classes_.end()) { - CHECK_GT(status, it->second->GetStatus()); - delete it->second; - } - switch (status) { - case mirror::Class::kStatusNotReady: - case mirror::Class::kStatusError: - case mirror::Class::kStatusRetryVerificationAtRuntime: - case mirror::Class::kStatusVerified: - case mirror::Class::kStatusInitialized: - case mirror::Class::kStatusResolved: - break; // Expected states. - default: - LOG(FATAL) << "Unexpected class status for class " - << PrettyDescriptor(ref.first->GetClassDescriptor(ref.first->GetClassDef(ref.second))) - << " of " << status; - } + if (it == compiled_classes_.end()) { CompiledClass* compiled_class = new CompiledClass(status); compiled_classes_.Overwrite(ref, compiled_class); + } else if (status > it->second->GetStatus()) { + // Update the status if we now have a greater one. This happens with vdex, + // which records a class is verified, but does not resolve it. + it->second->SetStatus(status); } } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index cc50197140..2e3b7c8eb5 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -378,13 +378,6 @@ class CompilerDriver { return current_dex_to_dex_methods_; } - // Compute constant code and method pointers when possible. - void GetCodeAndMethodForDirectCall(const mirror::Class* referrer_class, - ArtMethod* method, - /* out */ uintptr_t* direct_code, - /* out */ uintptr_t* direct_method) - REQUIRES_SHARED(Locks::mutator_lock_); - private: // Return whether the declaring class of `resolved_member` is // available to `referrer_class` for read or write access using two @@ -440,9 +433,12 @@ class CompilerDriver { TimingLogger* timings) REQUIRES(!Locks::mutator_lock_); + // NO_THREAD_SAFETY_ANALYSIS as the method accesses a guarded value in a + // single-threaded way. void Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files, - TimingLogger* timings); + TimingLogger* timings) + NO_THREAD_SAFETY_ANALYSIS; void VerifyDexFile(jobject class_loader, const DexFile& dex_file, diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 9c62f80339..6894cd5028 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -109,7 +109,7 @@ class CompilerOptions FINAL { return CompilerFilter::IsVerificationEnabled(compiler_filter_); } - bool NeverVerify() const { + bool AssumeClassesAreVerified() const { return compiler_filter_ == CompilerFilter::kVerifyNone; } @@ -117,6 +117,10 @@ class CompilerOptions FINAL { return compiler_filter_ == CompilerFilter::kVerifyProfile; } + bool IsAnyMethodCompilationEnabled() const { + return CompilerFilter::IsAnyMethodCompilationEnabled(compiler_filter_); + } + size_t GetHugeMethodThreshold() const { return huge_method_threshold_; } diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc index 64fd9e7f0f..47b19297e5 100644 --- a/compiler/driver/dex_compilation_unit.cc +++ b/compiler/driver/dex_compilation_unit.cc @@ -16,7 +16,6 @@ #include "dex_compilation_unit.h" -#include "base/stringprintf.h" #include "mirror/dex_cache.h" #include "utils.h" diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc index 6f48779258..9669c4a9d3 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -16,7 +16,6 @@ #include "elf_file.h" -#include "base/stringprintf.h" #include "base/unix_file/fd_file.h" #include "common_compiler_test.h" #include "elf_file.h" diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 9bbe595fa9..b0225a3625 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -20,10 +20,14 @@ #include <string> #include <vector> +#include "android-base/stringprintf.h" + #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" +#include "compiler_callbacks.h" #include "common_compiler_test.h" #include "debug/method_debug_info.h" +#include "dex/quick_compiler_callbacks.h" #include "driver/compiler_options.h" #include "elf_writer.h" #include "elf_writer_quick.h" @@ -74,6 +78,14 @@ class ImageTest : public CommonCompilerTest { const std::string& extra_dex = "", const std::string& image_class = ""); + void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { + CommonCompilerTest::SetUpRuntimeOptions(options); + callbacks_.reset(new QuickCompilerCallbacks( + verification_results_.get(), + CompilerCallbacks::CallbackMode::kCompileBootImage)); + options->push_back(std::make_pair("compilercallbacks", callbacks_.get())); + } + std::unordered_set<std::string>* GetImageClasses() OVERRIDE { return new std::unordered_set<std::string>(image_classes_); } @@ -134,7 +146,8 @@ void CompilationHelper::Compile(CompilerDriver* driver, // Create a generic tmp file, to be the base of the .art and .oat temporary files. ScratchFile location; for (int i = 0; i < static_cast<int>(class_path.size()); ++i) { - std::string cur_location(StringPrintf("%s-%d.art", location.GetFilename().c_str(), i)); + std::string cur_location = + android::base::StringPrintf("%s-%d.art", location.GetFilename().c_str(), i); image_locations.push_back(ScratchFile(cur_location)); } } @@ -241,6 +254,7 @@ void CompilationHelper::Compile(CompilerDriver* driver, driver->GetInstructionSetFeatures(), &key_value_store, /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify. + /* update_input_vdex */ false, &cur_opened_dex_files_map, &cur_opened_dex_files); ASSERT_TRUE(dex_files_ok); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index a47e711c28..9c38445276 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -803,6 +803,13 @@ bool ImageWriter::PruneAppImageClassInternal( result = result || PruneAppImageClassInternal(klass->GetSuperClass(), &my_early_exit, visited); + // Remove the class if the dex file is not in the set of dex files. This happens for classes that + // are from uses library if there is no profile. b/30688277 + mirror::DexCache* dex_cache = klass->GetDexCache(); + if (dex_cache != nullptr) { + result = result || + dex_file_oat_index_map_.find(dex_cache->GetDexFile()) == dex_file_oat_index_map_.end(); + } // Erase the element we stored earlier since we are exiting the function. auto it = visited->find(klass); DCHECK(it != visited->end()); @@ -1106,11 +1113,15 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const { } // build an Object[] of the roots needed to restore the runtime + int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_); auto image_roots(hs.NewHandle( - ObjectArray<Object>::Alloc(self, object_array_class.Get(), ImageHeader::kImageRootsMax))); + ObjectArray<Object>::Alloc(self, object_array_class.Get(), image_roots_size))); image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get()); image_roots->Set<false>(ImageHeader::kClassRoots, class_linker->GetClassRoots()); - for (int i = 0; i < ImageHeader::kImageRootsMax; i++) { + // image_roots[ImageHeader::kClassLoader] will be set later for app image. + static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax, + "Class loader should be the last image root."); + for (int32_t i = 0; i < ImageHeader::kImageRootsMax - 1; ++i) { CHECK(image_roots->Get(i) != nullptr); } return image_roots.Get(); @@ -1532,6 +1543,12 @@ void ImageWriter::CalculateNewObjectOffsets() { } // Process the work stack in case anything was added by TryAssignBinSlot. ProcessWorkStack(&work_stack); + + // Store the class loader in the class roots. + CHECK_EQ(class_loaders_.size(), 1u); + CHECK_EQ(image_roots.size(), 1u); + CHECK(*class_loaders_.begin() != nullptr); + image_roots[0]->Set<false>(ImageHeader::kClassLoader, *class_loaders_.begin()); } // Verify that all objects have assigned image bin slots. diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 9dfb434b10..148ce4f9ee 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -16,6 +16,8 @@ #include "jit_compiler.h" +#include "android-base/stringprintf.h" + #include "arch/instruction_set.h" #include "arch/instruction_set_features.h" #include "art_method-inl.h" @@ -81,7 +83,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { va_list ap; va_start(ap, fmt); std::string error; - StringAppendV(&error, fmt, ap); + android::base::StringAppendV(&error, fmt, ap); LOG(FATAL) << error; va_end(ap); exit(EXIT_FAILURE); diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/compiler/linker/mips/relative_patcher_mips32r6_test.cc index a16aaca545..4f9a3a050a 100644 --- a/compiler/linker/mips/relative_patcher_mips32r6_test.cc +++ b/compiler/linker/mips/relative_patcher_mips32r6_test.cc @@ -29,10 +29,10 @@ class Mips32r6RelativePatcherTest : public RelativePatcherTest { Mips32r6RelativePatcherTest() : RelativePatcherTest(kMips, "mips32r6") {} protected: - static const uint8_t UnpatchedPcRelativeRawCode[]; - static const uint32_t LiteralOffset; - static const uint32_t AnchorOffset; - static const ArrayRef<const uint8_t> UnpatchedPcRelativeCode; + static const uint8_t kUnpatchedPcRelativeRawCode[]; + static const uint32_t kLiteralOffset; + static const uint32_t kAnchorOffset; + static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode; uint32_t GetMethodOffset(uint32_t method_idx) { auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx)); @@ -45,24 +45,25 @@ class Mips32r6RelativePatcherTest : public RelativePatcherTest { void TestStringReference(uint32_t string_offset); }; -const uint8_t Mips32r6RelativePatcherTest::UnpatchedPcRelativeRawCode[] = { +const uint8_t Mips32r6RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = { 0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234 0x78, 0x56, 0x52, 0x26, // addiu s2, s2, low(diff); placeholder = 0x5678 }; -const uint32_t Mips32r6RelativePatcherTest::LiteralOffset = 0; // At auipc (where patching starts). -const uint32_t Mips32r6RelativePatcherTest::AnchorOffset = 0; // At auipc (where PC+0 points). -const ArrayRef<const uint8_t> Mips32r6RelativePatcherTest::UnpatchedPcRelativeCode( - UnpatchedPcRelativeRawCode); +const uint32_t Mips32r6RelativePatcherTest::kLiteralOffset = 0; // At auipc (where + // patching starts). +const uint32_t Mips32r6RelativePatcherTest::kAnchorOffset = 0; // At auipc (where PC+0 points). +const ArrayRef<const uint8_t> Mips32r6RelativePatcherTest::kUnpatchedPcRelativeCode( + kUnpatchedPcRelativeRawCode); void Mips32r6RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset) { - AddCompiledMethod(MethodRef(1u), UnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); + AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); Link(); auto result = method_offset_map_.FindMethodOffset(MethodRef(1u)); ASSERT_TRUE(result.first); - uint32_t diff = target_offset - (result.second + AnchorOffset); + uint32_t diff = target_offset - (result.second + kAnchorOffset); if (patches[0].GetType() == LinkerPatch::Type::kDexCacheArray) { diff += kDexCacheArrayLwOffset; } @@ -79,7 +80,7 @@ void Mips32r6RelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_array uint32_t element_offset) { dex_cache_arrays_begin_ = dex_cache_arrays_begin; LinkerPatch patches[] = { - LinkerPatch::DexCacheArrayPatch(LiteralOffset, nullptr, AnchorOffset, element_offset) + LinkerPatch::DexCacheArrayPatch(kLiteralOffset, nullptr, kAnchorOffset, element_offset) }; CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), dex_cache_arrays_begin_ + element_offset); @@ -89,7 +90,7 @@ void Mips32r6RelativePatcherTest::TestStringReference(uint32_t string_offset) { constexpr uint32_t kStringIndex = 1u; string_index_to_offset_map_.Put(kStringIndex, string_offset); LinkerPatch patches[] = { - LinkerPatch::RelativeStringPatch(LiteralOffset, nullptr, AnchorOffset, kStringIndex) + LinkerPatch::RelativeStringPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex) }; CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset); } diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/compiler/linker/mips/relative_patcher_mips_test.cc index 335ce2e476..faeb92a315 100644 --- a/compiler/linker/mips/relative_patcher_mips_test.cc +++ b/compiler/linker/mips/relative_patcher_mips_test.cc @@ -29,10 +29,10 @@ class MipsRelativePatcherTest : public RelativePatcherTest { MipsRelativePatcherTest() : RelativePatcherTest(kMips, "mips32r2") {} protected: - static const uint8_t UnpatchedPcRelativeRawCode[]; - static const uint32_t LiteralOffset; - static const uint32_t AnchorOffset; - static const ArrayRef<const uint8_t> UnpatchedPcRelativeCode; + static const uint8_t kUnpatchedPcRelativeRawCode[]; + static const uint32_t kLiteralOffset; + static const uint32_t kAnchorOffset; + static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode; uint32_t GetMethodOffset(uint32_t method_idx) { auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx)); @@ -45,26 +45,26 @@ class MipsRelativePatcherTest : public RelativePatcherTest { void TestStringReference(uint32_t string_offset); }; -const uint8_t MipsRelativePatcherTest::UnpatchedPcRelativeRawCode[] = { +const uint8_t MipsRelativePatcherTest::kUnpatchedPcRelativeRawCode[] = { 0x00, 0x00, 0x10, 0x04, // nal 0x34, 0x12, 0x12, 0x3C, // lui s2, high(diff); placeholder = 0x1234 0x78, 0x56, 0x52, 0x36, // ori s2, s2, low(diff); placeholder = 0x5678 0x21, 0x90, 0x5F, 0x02, // addu s2, s2, ra }; -const uint32_t MipsRelativePatcherTest::LiteralOffset = 4; // At lui (where patching starts). -const uint32_t MipsRelativePatcherTest::AnchorOffset = 8; // At ori (where PC+0 points). -const ArrayRef<const uint8_t> MipsRelativePatcherTest::UnpatchedPcRelativeCode( - UnpatchedPcRelativeRawCode); +const uint32_t MipsRelativePatcherTest::kLiteralOffset = 4; // At lui (where patching starts). +const uint32_t MipsRelativePatcherTest::kAnchorOffset = 8; // At ori (where PC+0 points). +const ArrayRef<const uint8_t> MipsRelativePatcherTest::kUnpatchedPcRelativeCode( + kUnpatchedPcRelativeRawCode); void MipsRelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset) { - AddCompiledMethod(MethodRef(1u), UnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); + AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); Link(); auto result = method_offset_map_.FindMethodOffset(MethodRef(1u)); ASSERT_TRUE(result.first); - uint32_t diff = target_offset - (result.second + AnchorOffset); + uint32_t diff = target_offset - (result.second + kAnchorOffset); if (patches[0].GetType() == LinkerPatch::Type::kDexCacheArray) { diff += kDexCacheArrayLwOffset; } @@ -82,7 +82,7 @@ void MipsRelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_arrays_be uint32_t element_offset) { dex_cache_arrays_begin_ = dex_cache_arrays_begin; LinkerPatch patches[] = { - LinkerPatch::DexCacheArrayPatch(LiteralOffset, nullptr, AnchorOffset, element_offset) + LinkerPatch::DexCacheArrayPatch(kLiteralOffset, nullptr, kAnchorOffset, element_offset) }; CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), dex_cache_arrays_begin_ + element_offset); @@ -92,7 +92,7 @@ void MipsRelativePatcherTest::TestStringReference(uint32_t string_offset) { constexpr uint32_t kStringIndex = 1u; string_index_to_offset_map_.Put(kStringIndex, string_offset); LinkerPatch patches[] = { - LinkerPatch::RelativeStringPatch(LiteralOffset, nullptr, AnchorOffset, kStringIndex) + LinkerPatch::RelativeStringPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex) }; CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset); } diff --git a/compiler/linker/mips64/relative_patcher_mips64.cc b/compiler/linker/mips64/relative_patcher_mips64.cc new file mode 100644 index 0000000000..c47971635b --- /dev/null +++ b/compiler/linker/mips64/relative_patcher_mips64.cc @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker/mips64/relative_patcher_mips64.h" + +#include "compiled_method.h" + +namespace art { +namespace linker { + +uint32_t Mips64RelativePatcher::ReserveSpace( + uint32_t offset, + const CompiledMethod* compiled_method ATTRIBUTE_UNUSED, + MethodReference method_ref ATTRIBUTE_UNUSED) { + return offset; // No space reserved; no limit on relative call distance. +} + +uint32_t Mips64RelativePatcher::ReserveSpaceEnd(uint32_t offset) { + return offset; // No space reserved; no limit on relative call distance. +} + +uint32_t Mips64RelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, uint32_t offset) { + return offset; // No thunks added; no limit on relative call distance. +} + +void Mips64RelativePatcher::PatchCall(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) { + // Basic sanity checks. + DCHECK_GE(code->size(), 8u); + DCHECK_LE(literal_offset, code->size() - 8u); + // auipc reg, offset_high + DCHECK_EQ((*code)[literal_offset + 0], 0x34); + DCHECK_EQ((*code)[literal_offset + 1], 0x12); + DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E); + DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC); + // jialc reg, offset_low + DCHECK_EQ((*code)[literal_offset + 4], 0x78); + DCHECK_EQ((*code)[literal_offset + 5], 0x56); + DCHECK_EQ(((*code)[literal_offset + 6] & 0xE0), 0x00); + DCHECK_EQ((*code)[literal_offset + 7], 0xF8); + + // Apply patch. + uint32_t diff = target_offset - patch_offset; + // Note that a combination of auipc with an instruction that adds a sign-extended + // 16-bit immediate operand (e.g. jialc) provides a PC-relative range of + // PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end + // by 32KB. + diff += (diff & 0x8000) << 1; // Account for sign extension in jialc. + + // auipc reg, offset_high + (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16); + (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24); + // jialc reg, offset_low + (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0); + (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8); +} + +void Mips64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) { + uint32_t anchor_literal_offset = patch.PcInsnOffset(); + uint32_t literal_offset = patch.LiteralOffset(); + + // Basic sanity checks. + DCHECK_GE(code->size(), 8u); + DCHECK_LE(literal_offset, code->size() - 8u); + DCHECK_EQ(literal_offset, anchor_literal_offset); + // auipc reg, offset_high + DCHECK_EQ((*code)[literal_offset + 0], 0x34); + DCHECK_EQ((*code)[literal_offset + 1], 0x12); + DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E); + DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC); + // instr reg(s), offset_low + DCHECK_EQ((*code)[literal_offset + 4], 0x78); + DCHECK_EQ((*code)[literal_offset + 5], 0x56); + + // Apply patch. + uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset; + uint32_t diff = target_offset - anchor_offset; + // Note that a combination of auipc with an instruction that adds a sign-extended + // 16-bit immediate operand (e.g. ld) provides a PC-relative range of + // PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end + // by 32KB. + diff += (diff & 0x8000) << 1; // Account for sign extension in instruction following auipc. + + // auipc reg, offset_high + (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16); + (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24); + // instr reg(s), offset_low + (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0); + (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8); +} + +} // namespace linker +} // namespace art diff --git a/compiler/linker/mips64/relative_patcher_mips64.h b/compiler/linker/mips64/relative_patcher_mips64.h new file mode 100644 index 0000000000..8ef8cebe2f --- /dev/null +++ b/compiler/linker/mips64/relative_patcher_mips64.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ +#define ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ + +#include "linker/relative_patcher.h" + +namespace art { +namespace linker { + +class Mips64RelativePatcher FINAL : public RelativePatcher { + public: + explicit Mips64RelativePatcher() {} + + uint32_t ReserveSpace(uint32_t offset, + const CompiledMethod* compiled_method, + MethodReference method_ref) OVERRIDE; + uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE; + uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE; + void PatchCall(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; + void PatchPcRelativeReference(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(Mips64RelativePatcher); +}; + +} // namespace linker +} // namespace art + +#endif // ART_COMPILER_LINKER_MIPS64_RELATIVE_PATCHER_MIPS64_H_ diff --git a/compiler/linker/mips64/relative_patcher_mips64_test.cc b/compiler/linker/mips64/relative_patcher_mips64_test.cc new file mode 100644 index 0000000000..c3170584e4 --- /dev/null +++ b/compiler/linker/mips64/relative_patcher_mips64_test.cc @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "linker/relative_patcher_test.h" +#include "linker/mips64/relative_patcher_mips64.h" + +namespace art { +namespace linker { + +class Mips64RelativePatcherTest : public RelativePatcherTest { + public: + Mips64RelativePatcherTest() : RelativePatcherTest(kMips64, "default") {} + + protected: + static const uint8_t kUnpatchedPcRelativeRawCode[]; + static const uint8_t kUnpatchedPcRelativeCallRawCode[]; + static const uint32_t kLiteralOffset; + static const uint32_t kAnchorOffset; + static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode; + static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCallCode; + + uint32_t GetMethodOffset(uint32_t method_idx) { + auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx)); + CHECK(result.first); + return result.second; + } + + void CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, uint32_t target_offset); + void TestDexCacheReference(uint32_t dex_cache_arrays_begin, uint32_t element_offset); + void TestStringReference(uint32_t string_offset); +}; + +const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = { + 0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234 + 0x78, 0x56, 0x52, 0x66, // daddiu s2, s2, low(diff); placeholder = 0x5678 +}; +const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeCallRawCode[] = { + 0x34, 0x12, 0x3E, 0xEC, // auipc at, high(diff); placeholder = 0x1234 + 0x78, 0x56, 0x01, 0xF8, // jialc at, low(diff); placeholder = 0x5678 +}; +const uint32_t Mips64RelativePatcherTest::kLiteralOffset = 0; // At auipc (where patching starts). +const uint32_t Mips64RelativePatcherTest::kAnchorOffset = 0; // At auipc (where PC+0 points). +const ArrayRef<const uint8_t> Mips64RelativePatcherTest::kUnpatchedPcRelativeCode( + kUnpatchedPcRelativeRawCode); +const ArrayRef<const uint8_t> Mips64RelativePatcherTest::kUnpatchedPcRelativeCallCode( + kUnpatchedPcRelativeCallRawCode); + +void Mips64RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches, + uint32_t target_offset) { + AddCompiledMethod(MethodRef(1u), kUnpatchedPcRelativeCode, ArrayRef<const LinkerPatch>(patches)); + Link(); + + auto result = method_offset_map_.FindMethodOffset(MethodRef(1u)); + ASSERT_TRUE(result.first); + + uint32_t diff = target_offset - (result.second + kAnchorOffset); + diff += (diff & 0x8000) << 1; // Account for sign extension in instruction following auipc. + + const uint8_t expected_code[] = { + static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x5E, 0xEE, + static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x66, + }; + EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code))); +} + +void Mips64RelativePatcherTest::TestDexCacheReference(uint32_t dex_cache_arrays_begin, + uint32_t element_offset) { + dex_cache_arrays_begin_ = dex_cache_arrays_begin; + LinkerPatch patches[] = { + LinkerPatch::DexCacheArrayPatch(kLiteralOffset, nullptr, kAnchorOffset, element_offset) + }; + CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), + dex_cache_arrays_begin_ + element_offset); +} + +TEST_F(Mips64RelativePatcherTest, DexCacheReference) { + TestDexCacheReference(/* dex_cache_arrays_begin */ 0x12345678, /* element_offset */ 0x1234); +} + +TEST_F(Mips64RelativePatcherTest, CallOther) { + LinkerPatch method1_patches[] = { + LinkerPatch::RelativeCodePatch(kLiteralOffset, nullptr, 2u), + }; + AddCompiledMethod(MethodRef(1u), + kUnpatchedPcRelativeCallCode, + ArrayRef<const LinkerPatch>(method1_patches)); + LinkerPatch method2_patches[] = { + LinkerPatch::RelativeCodePatch(kLiteralOffset, nullptr, 1u), + }; + AddCompiledMethod(MethodRef(2u), + kUnpatchedPcRelativeCallCode, + ArrayRef<const LinkerPatch>(method2_patches)); + Link(); + + uint32_t method1_offset = GetMethodOffset(1u); + uint32_t method2_offset = GetMethodOffset(2u); + uint32_t diff_after = method2_offset - (method1_offset + kAnchorOffset /* PC adjustment */); + diff_after += (diff_after & 0x8000) << 1; // Account for sign extension in jialc. + static const uint8_t method1_expected_code[] = { + static_cast<uint8_t>(diff_after >> 16), static_cast<uint8_t>(diff_after >> 24), 0x3E, 0xEC, + static_cast<uint8_t>(diff_after), static_cast<uint8_t>(diff_after >> 8), 0x01, 0xF8, + }; + EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code))); + uint32_t diff_before = method1_offset - (method2_offset + kAnchorOffset /* PC adjustment */); + diff_before += (diff_before & 0x8000) << 1; // Account for sign extension in jialc. + static const uint8_t method2_expected_code[] = { + static_cast<uint8_t>(diff_before >> 16), static_cast<uint8_t>(diff_before >> 24), 0x3E, 0xEC, + static_cast<uint8_t>(diff_before), static_cast<uint8_t>(diff_before >> 8), 0x01, 0xF8, + }; + EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code))); +} + +} // namespace linker +} // namespace art diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc index 77655947fd..f1538b10cc 100644 --- a/compiler/linker/relative_patcher.cc +++ b/compiler/linker/relative_patcher.cc @@ -25,6 +25,9 @@ #ifdef ART_ENABLE_CODEGEN_mips #include "linker/mips/relative_patcher_mips.h" #endif +#ifdef ART_ENABLE_CODEGEN_mips64 +#include "linker/mips64/relative_patcher_mips64.h" +#endif #ifdef ART_ENABLE_CODEGEN_x86 #include "linker/x86/relative_patcher_x86.h" #endif @@ -103,6 +106,10 @@ std::unique_ptr<RelativePatcher> RelativePatcher::Create( return std::unique_ptr<RelativePatcher>( new MipsRelativePatcher(features->AsMipsInstructionSetFeatures())); #endif +#ifdef ART_ENABLE_CODEGEN_mips64 + case kMips64: + return std::unique_ptr<RelativePatcher>(new Mips64RelativePatcher()); +#endif default: return std::unique_ptr<RelativePatcher>(new RelativePatcherNone); } diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 0a778b0954..86d92ff0b5 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "android-base/stringprintf.h" + #include "arch/instruction_set_features.h" #include "art_method-inl.h" #include "base/enums.h" @@ -48,7 +50,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { va_list ap; va_start(ap, fmt); std::string error; - StringAppendV(&error, fmt, ap); + android::base::StringAppendV(&error, fmt, ap); LOG(FATAL) << error; va_end(ap); UNREACHABLE(); @@ -196,6 +198,7 @@ class OatTest : public CommonCompilerTest { compiler_driver_->GetInstructionSetFeatures(), &key_value_store, verify, + /* update_input_vdex */ false, &opened_dex_files_map, &opened_dex_files)) { return false; diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index bebd5f5ae2..a9da09c82c 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -483,6 +483,7 @@ bool OatWriter::WriteAndOpenDexFiles( const InstructionSetFeatures* instruction_set_features, SafeMap<std::string, std::string>* key_value_store, bool verify, + bool update_input_vdex, /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { CHECK(write_state_ == WriteState::kAddingDexFileSources); @@ -511,15 +512,15 @@ bool OatWriter::WriteAndOpenDexFiles( if (kIsVdexEnabled) { std::unique_ptr<BufferedOutputStream> vdex_out( MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(vdex_file))); - // Write DEX files into VDEX, mmap and open them. - if (!WriteDexFiles(vdex_out.get(), vdex_file) || + if (!WriteDexFiles(vdex_out.get(), vdex_file, update_input_vdex) || !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { return false; } } else { + DCHECK(!update_input_vdex); // Write DEX files into OAT, mmap and open them. - if (!WriteDexFiles(oat_rodata, vdex_file) || + if (!WriteDexFiles(oat_rodata, vdex_file, update_input_vdex) || !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { return false; } @@ -1468,30 +1469,32 @@ bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) { if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) { return false; } - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data != nullptr) { // ie not an empty class, such as a marker interface - ClassDataItemIterator it(*dex_file, class_data); - while (it.HasNextStaticField()) { - it.Next(); - } - while (it.HasNextInstanceField()) { - it.Next(); - } - size_t class_def_method_index = 0u; - while (it.HasNextDirectMethod()) { - if (!visitor->VisitMethod(class_def_method_index, it)) { - return false; + if (compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + const uint8_t* class_data = dex_file->GetClassData(class_def); + if (class_data != nullptr) { // ie not an empty class, such as a marker interface + ClassDataItemIterator it(*dex_file, class_data); + while (it.HasNextStaticField()) { + it.Next(); } - ++class_def_method_index; - it.Next(); - } - while (it.HasNextVirtualMethod()) { - if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) { - return false; + while (it.HasNextInstanceField()) { + it.Next(); + } + size_t class_def_method_index = 0u; + while (it.HasNextDirectMethod()) { + if (!visitor->VisitMethod(class_def_method_index, it)) { + return false; + } + ++class_def_method_index; + it.Next(); + } + while (it.HasNextVirtualMethod()) { + if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) { + return false; + } + ++class_def_method_index; + it.Next(); } - ++class_def_method_index; - it.Next(); } } if (UNLIKELY(!visitor->EndClass())) { @@ -1548,6 +1551,9 @@ size_t OatWriter::InitOatClasses(size_t offset) { } size_t OatWriter::InitOatMaps(size_t offset) { + if (!compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) { + return offset; + } InitMapMethodVisitor visitor(this, offset); bool success = VisitDexMethods(&visitor); DCHECK(success); @@ -1594,6 +1600,9 @@ size_t OatWriter::InitOatCode(size_t offset) { } size_t OatWriter::InitOatCodeDexFiles(size_t offset) { + if (!compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) { + return offset; + } InitCodeMethodVisitor code_visitor(this, offset, vdex_quickening_info_offset_); bool success = VisitDexMethods(&code_visitor); DCHECK(success); @@ -1745,19 +1754,24 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { return false; } - WriteQuickeningInfoMethodVisitor visitor(this, vdex_out, start_offset); - if (!VisitDexMethods(&visitor)) { - PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation(); - return false; - } + if (compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) { + WriteQuickeningInfoMethodVisitor visitor(this, vdex_out, start_offset); + if (!VisitDexMethods(&visitor)) { + PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation(); + return false; + } - if (!vdex_out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing quickening info." - << " File: " << vdex_out->GetLocation(); - return false; + if (!vdex_out->Flush()) { + PLOG(ERROR) << "Failed to flush stream after writing quickening info." + << " File: " << vdex_out->GetLocation(); + return false; + } + size_quickening_info_ = visitor.GetNumberOfWrittenBytes(); + } else { + // We know we did not quicken. + size_quickening_info_ = 0; } - size_quickening_info_ = visitor.GetNumberOfWrittenBytes(); vdex_size_ += size_quickening_info_; return true; } @@ -2096,47 +2110,56 @@ bool OatWriter::ValidateDexFileHeader(const uint8_t* raw_header, const char* loc return true; } -bool OatWriter::WriteDexFiles(OutputStream* out, File* file) { +bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_vdex) { TimingLogger::ScopedTiming split("Write Dex files", timings_); vdex_dex_files_offset_ = vdex_size_; // Write dex files. for (OatDexFile& oat_dex_file : oat_dex_files_) { - if (!WriteDexFile(out, file, &oat_dex_file)) { + if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) { return false; } } - // Close sources. + CloseSources(); + return true; +} + +void OatWriter::CloseSources() { for (OatDexFile& oat_dex_file : oat_dex_files_) { oat_dex_file.source_.Clear(); // Get rid of the reference, it's about to be invalidated. } zipped_dex_files_.clear(); zip_archives_.clear(); raw_dex_files_.clear(); - return true; } -bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) { +bool OatWriter::WriteDexFile(OutputStream* out, + File* file, + OatDexFile* oat_dex_file, + bool update_input_vdex) { if (!SeekToDexFile(out, file, oat_dex_file)) { return false; } if (profile_compilation_info_ != nullptr) { + DCHECK(!update_input_vdex); if (!LayoutAndWriteDexFile(out, oat_dex_file)) { return false; } } else if (oat_dex_file->source_.IsZipEntry()) { + DCHECK(!update_input_vdex); if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) { return false; } } else if (oat_dex_file->source_.IsRawFile()) { + DCHECK(!update_input_vdex); if (!WriteDexFile(out, file, oat_dex_file, oat_dex_file->source_.GetRawFile())) { return false; } } else { DCHECK(oat_dex_file->source_.IsRawData()); - if (!WriteDexFile(out, oat_dex_file, oat_dex_file->source_.GetRawData())) { + if (!WriteDexFile(out, oat_dex_file, oat_dex_file->source_.GetRawData(), update_input_vdex)) { return false; } } @@ -2146,6 +2169,7 @@ bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_ DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_); vdex_size_ += oat_dex_file->dex_file_size_; } else { + DCHECK(!update_input_vdex); DCHECK_EQ(oat_size_, oat_dex_file->dex_file_offset_); oat_size_ += oat_dex_file->dex_file_size_; } @@ -2216,7 +2240,7 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil DexLayout dex_layout(options, profile_compilation_info_, nullptr); dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0); std::unique_ptr<MemMap> mem_map(dex_layout.GetAndReleaseMemMap()); - if (!WriteDexFile(out, oat_dex_file, mem_map->Begin())) { + if (!WriteDexFile(out, oat_dex_file, mem_map->Begin(), /* update_input_vdex */ false)) { return false; } // Set the checksum of the new oat dex file to be the original file's checksum. @@ -2373,22 +2397,27 @@ bool OatWriter::WriteDexFile(OutputStream* out, bool OatWriter::WriteDexFile(OutputStream* out, OatDexFile* oat_dex_file, - const uint8_t* dex_file) { + const uint8_t* dex_file, + bool update_input_vdex) { // Note: The raw data has already been checked to contain the header // and all the data that the header specifies as the file size. DCHECK(dex_file != nullptr); DCHECK(ValidateDexFileHeader(dex_file, oat_dex_file->GetLocation())); const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(dex_file); - if (!out->WriteFully(dex_file, header->file_size_)) { - PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation() - << " to " << out->GetLocation(); - return false; - } - if (!out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing dex file." - << " File: " << oat_dex_file->GetLocation(); - return false; + if (update_input_vdex) { + // The vdex already contains the dex code, no need to write it again. + } else { + if (!out->WriteFully(dex_file, header->file_size_)) { + PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation() + << " to " << out->GetLocation(); + return false; + } + if (!out->Flush()) { + PLOG(ERROR) << "Failed to flush stream after writing dex file." + << " File: " << oat_dex_file->GetLocation(); + return false; + } } // Update dex file size and resize class offsets in the OatDexFile. diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index da221d6029..8d087f4f91 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -158,12 +158,15 @@ class OatWriter { // Supporting data structures are written into the .rodata section of the oat file. // The `verify` setting dictates whether the dex file verifier should check the dex files. // This is generally the case, and should only be false for tests. + // If `update_input_vdex` is true, then this method won't actually write the dex files, + // and the compiler will just re-use the existing vdex file. bool WriteAndOpenDexFiles(File* vdex_file, OutputStream* oat_rodata, InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, SafeMap<std::string, std::string>* key_value_store, bool verify, + bool update_input_vdex, /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files); bool WriteQuickeningInfo(OutputStream* vdex_out); @@ -263,8 +266,13 @@ class OatWriter { // with a given DexMethodVisitor. bool VisitDexMethods(DexMethodVisitor* visitor); - bool WriteDexFiles(OutputStream* out, File* file); - bool WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file); + // If `update_input_vdex` is true, then this method won't actually write the dex files, + // and the compiler will just re-use the existing vdex file. + bool WriteDexFiles(OutputStream* out, File* file, bool update_input_vdex); + bool WriteDexFile(OutputStream* out, + File* file, + OatDexFile* oat_dex_file, + bool update_input_vdex); bool SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file); bool LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_file); bool WriteDexFile(OutputStream* out, @@ -275,7 +283,10 @@ class OatWriter { File* file, OatDexFile* oat_dex_file, File* dex_file); - bool WriteDexFile(OutputStream* out, OatDexFile* oat_dex_file, const uint8_t* dex_file); + bool WriteDexFile(OutputStream* out, + OatDexFile* oat_dex_file, + const uint8_t* dex_file, + bool update_input_vdex); bool OpenDexFiles(File* file, bool verify, /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, @@ -306,6 +317,7 @@ class OatWriter { const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files); bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta); void SetMultiOatRelativePatcherAdjustment(); + void CloseSources(); enum class WriteState { kAddingDexFileSources, diff --git a/compiler/optimizing/cha_guard_optimization.cc b/compiler/optimizing/cha_guard_optimization.cc new file mode 100644 index 0000000000..fe423012ca --- /dev/null +++ b/compiler/optimizing/cha_guard_optimization.cc @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cha_guard_optimization.h" + +namespace art { + +// Note we can only do CHA guard elimination/motion in a single pass, since +// if a guard is not removed, another guard might be removed due to +// the existence of the first guard. The first guard should not be further +// removed in another pass. For example, due to further optimizations, +// a receiver of a guard might turn out to be a parameter value, or defined at +// a different site, which makes the guard removable as a result. However +// it's not safe to remove the guard in another pass since another guard might +// have been removed due to the existence of this guard. +// +// As a consequence, we decided not to rely on other passes to remove them +// (such as GVN or instruction simplifier). + +class CHAGuardVisitor : HGraphVisitor { + public: + explicit CHAGuardVisitor(HGraph* graph) + : HGraphVisitor(graph), + block_has_cha_guard_(GetGraph()->GetBlocks().size(), + 0, + graph->GetArena()->Adapter(kArenaAllocCHA)) { + number_of_guards_to_visit_ = GetGraph()->GetNumberOfCHAGuards(); + DCHECK_NE(number_of_guards_to_visit_, 0u); + // Will recount number of guards during guard optimization. + GetGraph()->SetNumberOfCHAGuards(0); + } + + void VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) OVERRIDE; + + void VisitBasicBlock(HBasicBlock* block) OVERRIDE; + + private: + void RemoveGuard(HShouldDeoptimizeFlag* flag); + // Return true if `flag` is removed. + bool OptimizeForParameter(HShouldDeoptimizeFlag* flag, HInstruction* receiver); + // Return true if `flag` is removed. + bool OptimizeWithDominatingGuard(HShouldDeoptimizeFlag* flag, HInstruction* receiver); + // Return true if `flag` is hoisted. + bool HoistGuard(HShouldDeoptimizeFlag* flag, HInstruction* receiver); + + // Record if each block has any CHA guard. It's updated during the + // reverse post order visit. Use int instead of bool since ArenaVector + // does not support bool. + ArenaVector<int> block_has_cha_guard_; + + // The iterator that's being used for this visitor. Need it to manually + // advance the iterator due to removing/moving more than one instruction. + HInstructionIterator* instruction_iterator_; + + // Used to short-circuit the pass when there is no more guards left to visit. + uint32_t number_of_guards_to_visit_; + + DISALLOW_COPY_AND_ASSIGN(CHAGuardVisitor); +}; + +void CHAGuardVisitor::VisitBasicBlock(HBasicBlock* block) { + if (number_of_guards_to_visit_ == 0) { + return; + } + // Skip phis, just iterate through instructions. + HInstructionIterator it(block->GetInstructions()); + instruction_iterator_ = ⁢ + for (; !it.Done(); it.Advance()) { + DCHECK(it.Current()->IsInBlock()); + it.Current()->Accept(this); + } +} + +void CHAGuardVisitor::RemoveGuard(HShouldDeoptimizeFlag* flag) { + HBasicBlock* block = flag->GetBlock(); + HInstruction* compare = flag->GetNext(); + DCHECK(compare->IsNotEqual()); + HInstruction* deopt = compare->GetNext(); + DCHECK(deopt->IsDeoptimize()); + + // Advance instruction iterator first before we remove the guard. + // We need to do it twice since we remove three instructions and the + // visitor is responsible for advancing it once. + instruction_iterator_->Advance(); + instruction_iterator_->Advance(); + block->RemoveInstruction(deopt); + block->RemoveInstruction(compare); + block->RemoveInstruction(flag); +} + +bool CHAGuardVisitor::OptimizeForParameter(HShouldDeoptimizeFlag* flag, + HInstruction* receiver) { + // If some compiled code is invalidated by CHA due to class loading, the + // compiled code will not be entered anymore. So the very fact that the + // compiled code is invoked guarantees that a parameter receiver conforms + // to all the CHA devirtualization assumptions made by the compiled code, + // since all parameter receivers pre-exist any (potential) invalidation of + // the compiled code. + // + // TODO: allow more cases such as a phi whose inputs are all parameters. + if (receiver->IsParameterValue()) { + RemoveGuard(flag); + return true; + } + return false; +} + +bool CHAGuardVisitor::OptimizeWithDominatingGuard(HShouldDeoptimizeFlag* flag, + HInstruction* receiver) { + // If there is another guard that dominates the current guard, and + // that guard is dominated by receiver's definition, then the current + // guard can be eliminated, since receiver must pre-exist that other + // guard, and passing that guard guarantees that receiver conforms to + // all the CHA devirtualization assumptions. + HBasicBlock* dominator = flag->GetBlock(); + HBasicBlock* receiver_def_block = receiver->GetBlock(); + + // Complexity of the following algorithm: + // We potentially need to traverse the full dominator chain to receiver_def_block, + // plus a (partial) linear search within one block for each guard. + // So the worst case for each guard is bounded by the size of the + // biggest block plus the depth of the dominating tree. + + while (dominator != receiver_def_block) { + if (block_has_cha_guard_[dominator->GetBlockId()] == 1) { + RemoveGuard(flag); + return true; + } + dominator = dominator->GetDominator(); + } + + // At this point dominator is the block where receiver is defined. + // We do a linear search within dominator to see if there is a guard after + // receiver's definition. + HInstruction* instruction; + if (dominator == flag->GetBlock()) { + // Flag and receiver are defined in the same block. Search backward from + // the current guard. + instruction = flag->GetPrevious(); + } else { + // Search backward from the last instruction of that dominator. + instruction = dominator->GetLastInstruction(); + } + while (instruction != receiver) { + if (instruction == nullptr) { + // receiver must be defined in this block, we didn't find it + // in the instruction list, so it must be a Phi. + DCHECK(receiver->IsPhi()); + break; + } + if (instruction->IsShouldDeoptimizeFlag()) { + RemoveGuard(flag); + return true; + } + instruction = instruction->GetPrevious(); + } + return false; +} + +bool CHAGuardVisitor::HoistGuard(HShouldDeoptimizeFlag* flag, + HInstruction* receiver) { + // If receiver is loop invariant, we can hoist the guard out of the + // loop since passing a guard before entering the loop guarantees that + // receiver conforms to all the CHA devirtualization assumptions. + // We only hoist guards out of the inner loop since that offers most of the + // benefit and it might help remove other guards in the inner loop. + HBasicBlock* block = flag->GetBlock(); + HLoopInformation* loop_info = block->GetLoopInformation(); + if (loop_info != nullptr && + !loop_info->IsIrreducible() && + loop_info->IsDefinedOutOfTheLoop(receiver)) { + HInstruction* compare = flag->GetNext(); + DCHECK(compare->IsNotEqual()); + HInstruction* deopt = compare->GetNext(); + DCHECK(deopt->IsDeoptimize()); + + // Advance instruction iterator first before we move the guard. + // We need to do it twice since we move three instructions and the + // visitor is responsible for advancing it once. + instruction_iterator_->Advance(); + instruction_iterator_->Advance(); + + HBasicBlock* pre_header = loop_info->GetPreHeader(); + flag->MoveBefore(pre_header->GetLastInstruction()); + compare->MoveBefore(pre_header->GetLastInstruction()); + + block->RemoveInstruction(deopt); + HInstruction* suspend = loop_info->GetSuspendCheck(); + // Need a new deoptimize instruction that copies the environment + // of the suspend instruction for the loop. + HDeoptimize* deoptimize = + new (GetGraph()->GetArena()) HDeoptimize(compare, suspend->GetDexPc()); + pre_header->InsertInstructionBefore(deoptimize, pre_header->GetLastInstruction()); + deoptimize->CopyEnvironmentFromWithLoopPhiAdjustment( + suspend->GetEnvironment(), loop_info->GetHeader()); + block_has_cha_guard_[pre_header->GetBlockId()] = 1; + GetGraph()->IncrementNumberOfCHAGuards(); + return true; + } + return false; +} + +void CHAGuardVisitor::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) { + number_of_guards_to_visit_--; + HInstruction* receiver = flag->InputAt(0); + // Don't need the receiver anymore. + flag->RemoveInputAt(0); + if (receiver->IsNullCheck()) { + receiver = receiver->InputAt(0); + } + + if (OptimizeForParameter(flag, receiver)) { + DCHECK(!flag->IsInBlock()); + return; + } + if (OptimizeWithDominatingGuard(flag, receiver)) { + DCHECK(!flag->IsInBlock()); + return; + } + if (HoistGuard(flag, receiver)) { + DCHECK(flag->IsInBlock()); + return; + } + + // Need to keep the CHA guard in place. + block_has_cha_guard_[flag->GetBlock()->GetBlockId()] = 1; + GetGraph()->IncrementNumberOfCHAGuards(); +} + +void CHAGuardOptimization::Run() { + if (graph_->GetNumberOfCHAGuards() == 0) { + return; + } + CHAGuardVisitor visitor(graph_); + for (HBasicBlock* block : graph_->GetReversePostOrder()) { + visitor.VisitBasicBlock(block); + } +} + +} // namespace art diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h new file mode 100644 index 0000000000..ba0cdb81fd --- /dev/null +++ b/compiler/optimizing/cha_guard_optimization.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_ +#define ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_ + +#include "optimization.h" + +namespace art { + +/** + * Optimize CHA guards by removing/moving them. + */ +class CHAGuardOptimization : public HOptimization { + public: + explicit CHAGuardOptimization(HGraph* graph) + : HOptimization(graph, kCHAGuardOptimizationPassName) {} + + void Run() OVERRIDE; + + static constexpr const char* kCHAGuardOptimizationPassName = "cha_guard_optimization"; + + private: + DISALLOW_COPY_AND_ASSIGN(CHAGuardOptimization); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_ diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index fa6a5225e7..402eeee65f 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -1402,6 +1402,14 @@ void CodeGenerator::EmitJitRoots(uint8_t* code, entry.second = index; ++index; } + for (auto& entry : jit_class_roots_) { + // Update the `roots` with the class, and replace the address temporarily + // stored to the index in the table. + uint64_t address = entry.second; + roots->Set(index, reinterpret_cast<StackReference<mirror::Class>*>(address)->AsMirrorPtr()); + entry.second = index; + ++index; + } EmitJitRootPatches(code, roots_data); } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 4b11e7c699..2e2c3c00af 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -34,6 +34,7 @@ #include "stack_map_stream.h" #include "string_reference.h" #include "utils/label.h" +#include "utils/type_reference.h" namespace art { @@ -343,7 +344,7 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { void BuildStackMaps(MemoryRegion region, const DexFile::CodeItem& code_item); size_t ComputeStackMapsSize(); size_t GetNumberOfJitRoots() const { - return jit_string_roots_.size(); + return jit_string_roots_.size() + jit_class_roots_.size(); } // Fills the `literals` array with literals collected during code generation. @@ -611,6 +612,8 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { block_order_(nullptr), jit_string_roots_(StringReferenceValueComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_class_roots_(TypeReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), disasm_info_(nullptr), stats_(stats), graph_(graph), @@ -681,6 +684,7 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { virtual void EmitJitRootPatches(uint8_t* code ATTRIBUTE_UNUSED, const uint8_t* roots_data ATTRIBUTE_UNUSED) { DCHECK_EQ(jit_string_roots_.size(), 0u); + DCHECK_EQ(jit_class_roots_.size(), 0u); } // Frame size required for this method. @@ -711,7 +715,12 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { // Maps a StringReference (dex_file, string_index) to the index in the literal table. // Entries are intially added with a 0 index, and `EmitJitRoots` will compute all the // indices. - ArenaSafeMap<StringReference, size_t, StringReferenceValueComparator> jit_string_roots_; + ArenaSafeMap<StringReference, uint32_t, StringReferenceValueComparator> jit_string_roots_; + + // Maps a ClassReference (dex_file, type_index) to the index in the literal table. + // Entries are intially added with a pointer in the handle zone, and `EmitJitRoots` + // will compute all the indices. + ArenaSafeMap<TypeReference, uint64_t, TypeReferenceValueComparator> jit_class_roots_; DisassemblyInformation* disasm_info_; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index ed6eef1b55..8a7f6d3a33 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1201,11 +1201,6 @@ CodeGeneratorARM::CodeGeneratorARM(HGraph* graph, isa_features_(isa_features), uint32_literals_(std::less<uint32_t>(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - method_patches_(MethodReferenceComparator(), - graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - call_patches_(MethodReferenceComparator(), - graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_string_patches_(StringReferenceValueComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), @@ -1216,7 +1211,9 @@ CodeGeneratorARM::CodeGeneratorARM(HGraph* graph, boot_image_address_patches_(std::less<uint32_t>(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(StringReferenceValueComparator(), - graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_class_patches_(TypeReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Always save the LR register to mimic Quick. AddAllocatedRegister(Location::RegisterLocation(LR)); } @@ -5712,8 +5709,7 @@ HLoadClass::LoadKind CodeGeneratorARM::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageAddress: break; - case HLoadClass::LoadKind::kDexCacheAddress: - DCHECK(Runtime::Current()->UseJitCompilation()); + case HLoadClass::LoadKind::kJitTableAddress: break; case HLoadClass::LoadKind::kDexCachePcRelative: DCHECK(!Runtime::Current()->UseJitCompilation()); @@ -5814,22 +5810,12 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address)); break; } - case HLoadClass::LoadKind::kDexCacheAddress: { - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); - // 16-bit LDR immediate has a 5-bit offset multiplied by the size and that gives - // a 128B range. To try and reduce the number of literals if we load multiple types, - // simply split the dex cache address to a 128B aligned base loaded from a literal - // and the remaining offset embedded in the load. - static_assert(sizeof(GcRoot<mirror::Class>) == 4u, "Expected GC root to be 4 bytes."); - DCHECK_ALIGNED(cls->GetAddress(), 4u); - constexpr size_t offset_bits = /* encoded bits */ 5 + /* scale */ 2; - uint32_t base_address = address & ~MaxInt<uint32_t>(offset_bits); - uint32_t offset = address & MaxInt<uint32_t>(offset_bits); - __ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address)); - // /* GcRoot<mirror::Class> */ out = *(base_address + offset) - GenerateGcRootFieldLoad(cls, out_loc, out, offset, read_barrier_option); - generate_null_check = !cls->IsInDexCache(); + case HLoadClass::LoadKind::kJitTableAddress: { + __ LoadLiteral(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), + cls->GetTypeIndex(), + cls->GetAddress())); + // /* GcRoot<mirror::Class> */ out = *out + GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); break; } case HLoadClass::LoadKind::kDexCachePcRelative: { @@ -7148,7 +7134,7 @@ void CodeGeneratorARM::GenerateReadBarrierForRootSlow(HInstruction* instruction, HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch( const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, - HInvokeStaticOrDirect* invoke) { + HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) { HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info; // We disable pc-relative load when there is an irreducible loop, as the optimization // is incompatible with it. @@ -7160,24 +7146,6 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOr dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; } - if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) { - const DexFile& outer_dex_file = GetGraph()->GetDexFile(); - if (&outer_dex_file != invoke->GetTargetMethod().dex_file) { - // Calls across dex files are more likely to exceed the available BL range, - // so use absolute patch with fixup if available and kCallArtMethod otherwise. - HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = - (desired_dispatch_info.method_load_kind == - HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) - ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup - : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; - return HInvokeStaticOrDirect::DispatchInfo { - dispatch_info.method_load_kind, - code_ptr_location, - dispatch_info.method_load_data, - 0u - }; - } - } return dispatch_info; } @@ -7208,20 +7176,6 @@ Register CodeGeneratorARM::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOr } void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { - // For better instruction scheduling we load the direct code pointer before the method pointer. - switch (invoke->GetCodePtrLocation()) { - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - // LR = code address from literal pool with link-time patch. - __ LoadLiteral(LR, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod())); - break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // LR = invoke->GetDirectCodePtr(); - __ LoadImmediate(LR, invoke->GetDirectCodePtr()); - break; - default: - break; - } - Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. switch (invoke->GetMethodLoadKind()) { case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: { @@ -7237,10 +7191,6 @@ void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: __ LoadImmediate(temp.AsRegister<Register>(), invoke->GetMethodAddress()); break; - case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: - __ LoadLiteral(temp.AsRegister<Register>(), - DeduplicateMethodAddressLiteral(invoke->GetTargetMethod())); - break; case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { HArmDexCacheArraysBase* base = invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase(); @@ -7279,19 +7229,6 @@ void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: __ bl(GetFrameEntryLabel()); break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: - relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file, - invoke->GetTargetMethod().dex_method_index); - __ BindTrackedLabel(&relative_call_patches_.back().label); - // Arbitrarily branch to the BL itself, override at link time. - __ bl(&relative_call_patches_.back().label); - break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // LR prepared above for better instruction scheduling. - // LR() - __ blx(LR); - break; case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: // LR = callee_method->entry_point_from_quick_compiled_code_ __ LoadFromOffset( @@ -7379,10 +7316,6 @@ Literal* CodeGeneratorARM::DeduplicateBootImageAddressLiteral(uint32_t address) return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map); } -Literal* CodeGeneratorARM::DeduplicateDexCacheAddressLiteral(uint32_t address) { - return DeduplicateUint32Literal(address, &uint32_literals_); -} - Literal* CodeGeneratorARM::DeduplicateJitStringLiteral(const DexFile& dex_file, dex::StringIndex string_index) { jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u); @@ -7391,6 +7324,15 @@ Literal* CodeGeneratorARM::DeduplicateJitStringLiteral(const DexFile& dex_file, [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); } +Literal* CodeGeneratorARM::DeduplicateJitClassLiteral(const DexFile& dex_file, + dex::TypeIndex type_index, + uint64_t address) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address); + return jit_class_patches_.GetOrCreate( + TypeReference(&dex_file, type_index), + [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); +} + template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> inline void CodeGeneratorARM::EmitPcRelativeLinkerPatches( const ArenaDeque<PcRelativePatchInfo>& infos, @@ -7414,9 +7356,6 @@ inline void CodeGeneratorARM::EmitPcRelativeLinkerPatches( void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { DCHECK(linker_patches->empty()); size_t size = - method_patches_.size() + - call_patches_.size() + - relative_call_patches_.size() + /* MOVW+MOVT for each entry */ 2u * pc_relative_dex_cache_patches_.size() + boot_image_string_patches_.size() + /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() + @@ -7424,29 +7363,6 @@ void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() + boot_image_address_patches_.size(); linker_patches->reserve(size); - for (const auto& entry : method_patches_) { - const MethodReference& target_method = entry.first; - Literal* literal = entry.second; - DCHECK(literal->GetLabel()->IsBound()); - uint32_t literal_offset = literal->GetLabel()->Position(); - linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, - target_method.dex_file, - target_method.dex_method_index)); - } - for (const auto& entry : call_patches_) { - const MethodReference& target_method = entry.first; - Literal* literal = entry.second; - DCHECK(literal->GetLabel()->IsBound()); - uint32_t literal_offset = literal->GetLabel()->Position(); - linker_patches->push_back(LinkerPatch::CodePatch(literal_offset, - target_method.dex_file, - target_method.dex_method_index)); - } - for (const PatchInfo<Label>& info : relative_call_patches_) { - uint32_t literal_offset = info.label.Position(); - linker_patches->push_back( - LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index)); - } EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, linker_patches); for (const auto& entry : boot_image_string_patches_) { @@ -7498,14 +7414,6 @@ Literal* CodeGeneratorARM::DeduplicateMethodLiteral(MethodReference target_metho [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); } -Literal* CodeGeneratorARM::DeduplicateMethodAddressLiteral(MethodReference target_method) { - return DeduplicateMethodLiteral(target_method, &method_patches_); -} - -Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_method) { - return DeduplicateMethodLiteral(target_method, &call_patches_); -} - void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); @@ -7707,18 +7615,28 @@ void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction } } +static void PatchJitRootUse(uint8_t* code, + const uint8_t* roots_data, + Literal* literal, + uint64_t index_in_table) { + DCHECK(literal->GetLabel()->IsBound()); + uint32_t literal_offset = literal->GetLabel()->Position(); + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + uint8_t* data = code + literal_offset; + reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address); +} + void CodeGeneratorARM::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { for (const auto& entry : jit_string_patches_) { const auto& it = jit_string_roots_.find(entry.first); DCHECK(it != jit_string_roots_.end()); - size_t index_in_table = it->second; - Literal* literal = entry.second; - DCHECK(literal->GetLabel()->IsBound()); - uint32_t literal_offset = literal->GetLabel()->Position(); - uintptr_t address = - reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); - uint8_t* data = code + literal_offset; - reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address); + PatchJitRootUse(code, roots_data, entry.second, it->second); + } + for (const auto& entry : jit_class_patches_) { + const auto& it = jit_class_roots_.find(entry.first); + DCHECK(it != jit_class_roots_.end()); + PatchJitRootUse(code, roots_data, entry.second, it->second); } } diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 8230512825..6435851320 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -489,8 +489,10 @@ class CodeGeneratorARM : public CodeGenerator { dex::StringIndex string_index); Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index); Literal* DeduplicateBootImageAddressLiteral(uint32_t address); - Literal* DeduplicateDexCacheAddressLiteral(uint32_t address); Literal* DeduplicateJitStringLiteral(const DexFile& dex_file, dex::StringIndex string_index); + Literal* DeduplicateJitClassLiteral(const DexFile& dex_file, + dex::TypeIndex type_index, + uint64_t address); void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; @@ -599,14 +601,12 @@ class CodeGeneratorARM : public CodeGenerator { using StringToLiteralMap = ArenaSafeMap<StringReference, Literal*, StringReferenceValueComparator>; - using BootTypeToLiteralMap = ArenaSafeMap<TypeReference, - Literal*, - TypeReferenceValueComparator>; + using TypeToLiteralMap = ArenaSafeMap<TypeReference, + Literal*, + TypeReferenceValueComparator>; Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map); Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map); - Literal* DeduplicateMethodAddressLiteral(MethodReference target_method); - Literal* DeduplicateMethodCodeLiteral(MethodReference target_method); PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches); @@ -625,12 +625,6 @@ class CodeGeneratorARM : public CodeGenerator { // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. Uint32ToLiteralMap uint32_literals_; - // Method patch info, map MethodReference to a literal for method address and method code. - MethodToLiteralMap method_patches_; - MethodToLiteralMap call_patches_; - // Relative call patch info. - // Using ArenaDeque<> which retains element addresses on push/emplace_back(). - ArenaDeque<PatchInfo<Label>> relative_call_patches_; // PC-relative patch info for each HArmDexCacheArraysBase. ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_; // Deduplication map for boot string literals for kBootImageLinkTimeAddress. @@ -638,7 +632,7 @@ class CodeGeneratorARM : public CodeGenerator { // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC). ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; // Deduplication map for boot type literals for kBootImageLinkTimeAddress. - BootTypeToLiteralMap boot_image_type_patches_; + TypeToLiteralMap boot_image_type_patches_; // PC-relative type patch info. ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_; // Deduplication map for patchable boot image addresses. @@ -646,6 +640,8 @@ class CodeGeneratorARM : public CodeGenerator { // Patches for string literals in JIT compiled code. StringToLiteralMap jit_string_patches_; + // Patches for class literals in JIT compiled code. + TypeToLiteralMap jit_class_patches_; DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM); }; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 6eebd69a04..5c33fe1a7d 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1147,11 +1147,6 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), uint64_literals_(std::less<uint64_t>(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - method_patches_(MethodReferenceComparator(), - graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - call_patches_(MethodReferenceComparator(), - graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_string_patches_(StringReferenceValueComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), @@ -1162,7 +1157,9 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, boot_image_address_patches_(std::less<uint32_t>(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(StringReferenceValueComparator(), - graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_class_patches_(TypeReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Save the link register (containing the return address) to mimic Quick. AddAllocatedRegister(LocationFrom(lr)); } @@ -1536,8 +1533,17 @@ void CodeGeneratorARM64::MoveLocation(Location destination, DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot()); DCHECK(source.IsDoubleStackSlot() == destination.IsDoubleStackSlot()); UseScratchRegisterScope temps(GetVIXLAssembler()); - // There is generally less pressure on FP registers. - FPRegister temp = destination.IsDoubleStackSlot() ? temps.AcquireD() : temps.AcquireS(); + // Use any scratch register (a core or a floating-point one) + // from VIXL scratch register pools as a temporary. + // + // We used to only use the FP scratch register pool, but in some + // rare cases the only register from this pool (D31) would + // already be used (e.g. within a ParallelMove instruction, when + // a move is blocked by a another move requiring a scratch FP + // register, which would reserve D31). To prevent this issue, we + // ask for a scratch register of any type (core or FP). + CPURegister temp = + temps.AcquireCPURegisterOfSize(destination.IsDoubleStackSlot() ? kXRegSize : kWRegSize); __ Ldr(temp, StackOperandFrom(source)); __ Str(temp, StackOperandFrom(destination)); } @@ -3969,23 +3975,6 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM64::GetSupportedInvokeStatic } void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { - // For better instruction scheduling we load the direct code pointer before the method pointer. - bool direct_code_loaded = false; - switch (invoke->GetCodePtrLocation()) { - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - // LR = code address from literal pool with link-time patch. - __ Ldr(lr, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod())); - direct_code_loaded = true; - break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // LR = invoke->GetDirectCodePtr(); - __ Ldr(lr, DeduplicateUint64Literal(invoke->GetDirectCodePtr())); - direct_code_loaded = true; - break; - default: - break; - } - // Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention. Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. switch (invoke->GetMethodLoadKind()) { @@ -4003,11 +3992,6 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok // Load method address from literal pool. __ Ldr(XRegisterFrom(temp), DeduplicateUint64Literal(invoke->GetMethodAddress())); break; - case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: - // Load method address from literal pool with a link-time patch. - __ Ldr(XRegisterFrom(temp), - DeduplicateMethodAddressLiteral(invoke->GetTargetMethod())); - break; case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { // Add ADRP with its PC-relative DexCache access patch. const DexFile& dex_file = invoke->GetDexFile(); @@ -4049,23 +4033,6 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: __ Bl(&frame_entry_label_); break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: { - relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file, - invoke->GetTargetMethod().dex_method_index); - vixl::aarch64::Label* label = &relative_call_patches_.back().label; - SingleEmissionCheckScope guard(GetVIXLAssembler()); - __ Bind(label); - // Branch and link to itself. This will be overriden at link time. - __ bl(static_cast<int64_t>(0)); - break; - } - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // LR prepared above for better instruction scheduling. - DCHECK(direct_code_loaded); - // lr() - __ Blr(lr); - break; case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: // LR = callee_method->entry_point_from_quick_compiled_code_; __ Ldr(lr, MemOperand( @@ -4169,11 +4136,6 @@ vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageAddres return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map); } -vixl::aarch64::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateDexCacheAddressLiteral( - uint64_t address) { - return DeduplicateUint64Literal(address); -} - vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitStringLiteral( const DexFile& dex_file, dex::StringIndex string_index) { jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u); @@ -4182,6 +4144,14 @@ vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitStringLitera [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); }); } +vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitClassLiteral( + const DexFile& dex_file, dex::TypeIndex type_index, uint64_t address) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address); + return jit_class_patches_.GetOrCreate( + TypeReference(&dex_file, type_index), + [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); }); +} + void CodeGeneratorARM64::EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label, vixl::aarch64::Register reg) { DCHECK(reg.IsX()); @@ -4224,9 +4194,6 @@ inline void CodeGeneratorARM64::EmitPcRelativeLinkerPatches( void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { DCHECK(linker_patches->empty()); size_t size = - method_patches_.size() + - call_patches_.size() + - relative_call_patches_.size() + pc_relative_dex_cache_patches_.size() + boot_image_string_patches_.size() + pc_relative_string_patches_.size() + @@ -4234,24 +4201,6 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patc pc_relative_type_patches_.size() + boot_image_address_patches_.size(); linker_patches->reserve(size); - for (const auto& entry : method_patches_) { - const MethodReference& target_method = entry.first; - vixl::aarch64::Literal<uint64_t>* literal = entry.second; - linker_patches->push_back(LinkerPatch::MethodPatch(literal->GetOffset(), - target_method.dex_file, - target_method.dex_method_index)); - } - for (const auto& entry : call_patches_) { - const MethodReference& target_method = entry.first; - vixl::aarch64::Literal<uint64_t>* literal = entry.second; - linker_patches->push_back(LinkerPatch::CodePatch(literal->GetOffset(), - target_method.dex_file, - target_method.dex_method_index)); - } - for (const PatchInfo<vixl::aarch64::Label>& info : relative_call_patches_) { - linker_patches->push_back( - LinkerPatch::RelativeCodePatch(info.label.GetLocation(), &info.dex_file, info.index)); - } for (const PcRelativePatchInfo& info : pc_relative_dex_cache_patches_) { linker_patches->push_back(LinkerPatch::DexCacheArrayPatch(info.label.GetLocation(), &info.target_dex_file, @@ -4309,17 +4258,6 @@ vixl::aarch64::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodLiteral( [this]() { return __ CreateLiteralDestroyedWithPool<uint64_t>(/* placeholder */ 0u); }); } -vixl::aarch64::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodAddressLiteral( - MethodReference target_method) { - return DeduplicateMethodLiteral(target_method, &method_patches_); -} - -vixl::aarch64::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateMethodCodeLiteral( - MethodReference target_method) { - return DeduplicateMethodLiteral(target_method, &call_patches_); -} - - void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { // Explicit clinit checks triggered by static invokes must have been pruned by // art::PrepareForRegisterAllocation. @@ -4359,7 +4297,7 @@ HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageAddress: break; - case HLoadClass::LoadKind::kDexCacheAddress: + case HLoadClass::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); break; case HLoadClass::LoadKind::kDexCachePcRelative: @@ -4452,26 +4390,16 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(cls->GetAddress())); break; } - case HLoadClass::LoadKind::kDexCacheAddress: { - DCHECK_NE(cls->GetAddress(), 0u); - // LDR immediate has a 12-bit offset multiplied by the size and for 32-bit loads - // that gives a 16KiB range. To try and reduce the number of literals if we load - // multiple types, simply split the dex cache address to a 16KiB aligned base - // loaded from a literal and the remaining offset embedded in the load. - static_assert(sizeof(GcRoot<mirror::Class>) == 4u, "Expected GC root to be 4 bytes."); - DCHECK_ALIGNED(cls->GetAddress(), 4u); - constexpr size_t offset_bits = /* encoded bits */ 12 + /* scale */ 2; - uint64_t base_address = cls->GetAddress() & ~MaxInt<uint64_t>(offset_bits); - uint32_t offset = cls->GetAddress() & MaxInt<uint64_t>(offset_bits); - __ Ldr(out.X(), codegen_->DeduplicateDexCacheAddressLiteral(base_address)); - // /* GcRoot<mirror::Class> */ out = *(base_address + offset) + case HLoadClass::LoadKind::kJitTableAddress: { + __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), + cls->GetTypeIndex(), + cls->GetAddress())); GenerateGcRootFieldLoad(cls, out_loc, out.X(), - offset, + /* offset */ 0, /* fixup_label */ nullptr, - read_barrier_option); - generate_null_check = !cls->IsInDexCache(); + kCompilerReadBarrierOption); break; } case HLoadClass::LoadKind::kDexCachePcRelative: { @@ -5782,17 +5710,27 @@ void InstructionCodeGeneratorARM64::VisitClassTableGet(HClassTableGet* instructi } } +static void PatchJitRootUse(uint8_t* code, + const uint8_t* roots_data, + vixl::aarch64::Literal<uint32_t>* literal, + uint64_t index_in_table) { + uint32_t literal_offset = literal->GetOffset(); + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + uint8_t* data = code + literal_offset; + reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address); +} + void CodeGeneratorARM64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { for (const auto& entry : jit_string_patches_) { const auto& it = jit_string_roots_.find(entry.first); DCHECK(it != jit_string_roots_.end()); - size_t index_in_table = it->second; - vixl::aarch64::Literal<uint32_t>* literal = entry.second; - uint32_t literal_offset = literal->GetOffset(); - uintptr_t address = - reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); - uint8_t* data = code + literal_offset; - reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address); + PatchJitRootUse(code, roots_data, entry.second, it->second); + } + for (const auto& entry : jit_class_patches_) { + const auto& it = jit_class_roots_.find(entry.first); + DCHECK(it != jit_class_roots_.end()); + PatchJitRootUse(code, roots_data, entry.second, it->second); } } diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 868c8b07ed..8f33b6becf 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -566,9 +566,11 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index); vixl::aarch64::Literal<uint32_t>* DeduplicateBootImageAddressLiteral(uint64_t address); - vixl::aarch64::Literal<uint64_t>* DeduplicateDexCacheAddressLiteral(uint64_t address); vixl::aarch64::Literal<uint32_t>* DeduplicateJitStringLiteral(const DexFile& dex_file, dex::StringIndex string_index); + vixl::aarch64::Literal<uint32_t>* DeduplicateJitClassLiteral(const DexFile& dex_file, + dex::TypeIndex string_index, + uint64_t address); void EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label, vixl::aarch64::Register reg); void EmitAddPlaceholder(vixl::aarch64::Label* fixup_label, @@ -682,17 +684,15 @@ class CodeGeneratorARM64 : public CodeGenerator { using StringToLiteralMap = ArenaSafeMap<StringReference, vixl::aarch64::Literal<uint32_t>*, StringReferenceValueComparator>; - using BootTypeToLiteralMap = ArenaSafeMap<TypeReference, - vixl::aarch64::Literal<uint32_t>*, - TypeReferenceValueComparator>; + using TypeToLiteralMap = ArenaSafeMap<TypeReference, + vixl::aarch64::Literal<uint32_t>*, + TypeReferenceValueComparator>; vixl::aarch64::Literal<uint32_t>* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map); vixl::aarch64::Literal<uint64_t>* DeduplicateUint64Literal(uint64_t value); vixl::aarch64::Literal<uint64_t>* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map); - vixl::aarch64::Literal<uint64_t>* DeduplicateMethodAddressLiteral(MethodReference target_method); - vixl::aarch64::Literal<uint64_t>* DeduplicateMethodCodeLiteral(MethodReference target_method); // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays // and boot image strings/types. The only difference is the interpretation of the @@ -733,15 +733,8 @@ class CodeGeneratorARM64 : public CodeGenerator { // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. Uint32ToLiteralMap uint32_literals_; - // Deduplication map for 64-bit literals, used for non-patchable method address, method code - // or string dex cache address. + // Deduplication map for 64-bit literals, used for non-patchable method address or method code. Uint64ToLiteralMap uint64_literals_; - // Method patch info, map MethodReference to a literal for method address and method code. - MethodToLiteralMap method_patches_; - MethodToLiteralMap call_patches_; - // Relative call patch info. - // Using ArenaDeque<> which retains element addresses on push/emplace_back(). - ArenaDeque<PatchInfo<vixl::aarch64::Label>> relative_call_patches_; // PC-relative DexCache access info. ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_; // Deduplication map for boot string literals for kBootImageLinkTimeAddress. @@ -749,7 +742,7 @@ class CodeGeneratorARM64 : public CodeGenerator { // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC). ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; // Deduplication map for boot type literals for kBootImageLinkTimeAddress. - BootTypeToLiteralMap boot_image_type_patches_; + TypeToLiteralMap boot_image_type_patches_; // PC-relative type patch info. ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_; // Deduplication map for patchable boot image addresses. @@ -757,6 +750,8 @@ class CodeGeneratorARM64 : public CodeGenerator { // Patches for string literals in JIT compiled code. StringToLiteralMap jit_string_patches_; + // Patches for class literals in JIT compiled code. + TypeToLiteralMap jit_class_patches_; DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM64); }; diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 3a3d2a9db1..f108595a00 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -46,8 +46,10 @@ using helpers::InputOperandAt; using helpers::InputRegister; using helpers::InputRegisterAt; using helpers::InputSRegisterAt; +using helpers::InputVRegister; using helpers::InputVRegisterAt; using helpers::Int32ConstantFrom; +using helpers::Int64ConstantFrom; using helpers::LocationFrom; using helpers::LowRegisterFrom; using helpers::LowSRegisterFrom; @@ -56,6 +58,10 @@ using helpers::OutputSRegister; using helpers::OutputVRegister; using helpers::RegisterFrom; using helpers::SRegisterFrom; +using helpers::Uint64ConstantFrom; + +using vixl::ExactAssemblyScope; +using vixl::CodeBufferCheckScope; using RegisterList = vixl32::RegisterList; @@ -799,7 +805,7 @@ class ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL : public SlowPathCodeARMVIXL // as-is. vixl32::Label done; __ Cmp(temp1_, ref_reg); - __ B(eq, &done); + __ B(eq, &done, /* far_target */ false); // Update the the holder's field atomically. This may fail if // mutator updates before us, but it's OK. This is achieved @@ -843,19 +849,19 @@ class ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL : public SlowPathCodeARMVIXL __ Subs(tmp, tmp, expected); { - AssemblerAccurateScope aas(arm_codegen->GetVIXLAssembler(), - 2 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(arm_codegen->GetVIXLAssembler(), + 2 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ it(ne); __ clrex(ne); } - __ B(ne, &exit_loop); + __ B(ne, &exit_loop, /* far_target */ false); __ Strex(tmp, value, MemOperand(tmp_ptr)); __ Cmp(tmp, 1); - __ B(eq, &loop_head); + __ B(eq, &loop_head, /* far_target */ false); __ Bind(&exit_loop); @@ -1237,10 +1243,21 @@ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, move_resolver_(graph->GetArena(), this), assembler_(graph->GetArena()), isa_features_(isa_features), - relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + uint32_literals_(std::less<uint32_t>(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + boot_image_string_patches_(StringReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + boot_image_type_patches_(TypeReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + boot_image_address_patches_(std::less<uint32_t>(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_string_patches_(StringReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_class_patches_(TypeReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Always save the LR register to mimic Quick. AddAllocatedRegister(Location::RegisterLocation(LR)); // Give d14 and d15 as scratch registers to VIXL. @@ -1261,9 +1278,9 @@ void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) { // We are about to use the assembler to place literals directly. Make sure we have enough // underlying code buffer and we have generated a jump table of the right size, using // codegen->GetVIXLAssembler()->GetBuffer().Align(); - AssemblerAccurateScope aas(codegen->GetVIXLAssembler(), - num_entries * sizeof(int32_t), - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(codegen->GetVIXLAssembler(), + num_entries * sizeof(int32_t), + CodeBufferCheckScope::kMaximumSize); // TODO(VIXL): Check that using lower case bind is fine here. codegen->GetVIXLAssembler()->bind(&table_start_); for (uint32_t i = 0; i < num_entries; i++) { @@ -1375,11 +1392,11 @@ void CodeGeneratorARMVIXL::GenerateFrameEntry() { if (!skip_overflow_check) { UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); - __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm))); + __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(kArm))); // The load must immediately precede RecordPcInfo. - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ ldr(temp, MemOperand(temp)); RecordPcInfo(nullptr, 0); } @@ -1637,9 +1654,9 @@ void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint, __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value())); // Ensure the pc position is recorded immediately after the `blx` instruction. // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used. - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::k16BitT32InstructionSizeInBytes, - CodeBufferCheckScope::kExactSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::k16BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kExactSize); __ blx(lr); if (EntrypointRequiresStackMap(entrypoint)) { RecordPcInfo(instruction, dex_pc, slow_path); @@ -1792,7 +1809,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateLongComparesAndJumps(HCondition* c break; } if (right.IsConstant()) { - int64_t value = right.GetConstant()->AsLongConstant()->GetValue(); + int64_t value = Int64ConstantFrom(right); int32_t val_low = Low32Bits(value); int32_t val_high = High32Bits(value); @@ -1877,7 +1894,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instru __ B(true_target); } } else { - DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue(); + DCHECK(cond->AsIntConstant()->IsFalse()) << Int32ConstantFrom(cond); if (false_target != nullptr) { __ B(false_target); } @@ -2082,9 +2099,9 @@ void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) { __ Cmp(InputRegisterAt(cond, 0), CodeGenerator::GetInt32ValueOf(right.GetConstant())); } - AssemblerAccurateScope aas(GetVIXLAssembler(), - 3 * vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + 3 * vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ ite(ARMCondition(cond->GetCondition())); __ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1); __ mov(ARMCondition(cond->GetOppositeCondition()), OutputRegister(cond), 0); @@ -2370,9 +2387,9 @@ void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* inv // Ensure the pc position is recorded immediately after the `ldr` instruction. { - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); // /* HeapReference<Class> */ temp = receiver->klass_ __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset)); codegen_->MaybeRecordImplicitNullCheck(invoke); @@ -2418,7 +2435,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* inv { // Ensure the pc position is recorded immediately after the `blx` instruction. // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used. - AssemblerAccurateScope aas(GetVIXLAssembler(), + ExactAssemblyScope aas(GetVIXLAssembler(), vixl32::k16BitT32InstructionSizeInBytes, CodeBufferCheckScope::kExactSize); // LR(); @@ -2479,9 +2496,7 @@ void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) { case Primitive::kPrimFloat: case Primitive::kPrimDouble: - // TODO(VIXL): Consider introducing an InputVRegister() - // helper function (equivalent to InputRegister()). - __ Vneg(OutputVRegister(neg), InputVRegisterAt(neg, 0)); + __ Vneg(OutputVRegister(neg), InputVRegister(neg)); break; default: @@ -2771,8 +2786,8 @@ void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conve } else { DCHECK(in.IsConstant()); DCHECK(in.GetConstant()->IsLongConstant()); - int64_t value = in.GetConstant()->AsLongConstant()->GetValue(); - __ Mov(OutputRegister(conversion), static_cast<int32_t>(value)); + int32_t value = Int32ConstantFrom(in); + __ Mov(OutputRegister(conversion), value); } break; @@ -3111,8 +3126,8 @@ void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) { // Extra checks to protect caused by the existence of R1_R2. // The algorithm is wrong if out.hi is either in1.lo or in2.lo: // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2); - DCHECK_NE(out_hi.GetCode(), in1_lo.GetCode()); - DCHECK_NE(out_hi.GetCode(), in2_lo.GetCode()); + DCHECK(!out_hi.Is(in1_lo)); + DCHECK(!out_hi.Is(in2_lo)); // input: in1 - 64 bits, in2 - 64 bits // output: out @@ -3152,7 +3167,7 @@ void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* inst vixl32::Register out = OutputRegister(instruction); vixl32::Register dividend = InputRegisterAt(instruction, 0); - int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); + int32_t imm = Int32ConstantFrom(second); DCHECK(imm == 1 || imm == -1); if (instruction->IsRem()) { @@ -3177,7 +3192,7 @@ void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instr vixl32::Register out = OutputRegister(instruction); vixl32::Register dividend = InputRegisterAt(instruction, 0); vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); - int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); + int32_t imm = Int32ConstantFrom(second); uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm)); int ctz_imm = CTZ(abs_imm); @@ -3250,7 +3265,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral( Location second = instruction->GetLocations()->InAt(1); DCHECK(second.IsConstant()); - int32_t imm = second.GetConstant()->AsIntConstant()->GetValue(); + int32_t imm = Int32ConstantFrom(second); if (imm == 0) { // Do not generate anything. DivZeroCheck would prevent any code to be executed. } else if (imm == 1 || imm == -1) { @@ -3284,7 +3299,7 @@ void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant())); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); - int32_t value = div->InputAt(1)->AsIntConstant()->GetValue(); + int32_t value = Int32ConstantFrom(div->InputAt(1)); if (value == 1 || value == 0 || value == -1) { // No temp register required. } else { @@ -3397,7 +3412,7 @@ void LocationsBuilderARMVIXL::VisitRem(HRem* rem) { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant())); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); - int32_t value = rem->InputAt(1)->AsIntConstant()->GetValue(); + int32_t value = Int32ConstantFrom(rem->InputAt(1)); if (value == 1 || value == 0 || value == -1) { // No temp register required. } else { @@ -3532,7 +3547,7 @@ void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instructi __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel()); } else { DCHECK(value.IsConstant()) << value; - if (value.GetConstant()->AsIntConstant()->GetValue() == 0) { + if (Int32ConstantFrom(value) == 0) { __ B(slow_path->GetEntryLabel()); } } @@ -3546,7 +3561,7 @@ void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instructi __ B(eq, slow_path->GetEntryLabel()); } else { DCHECK(value.IsConstant()) << value; - if (value.GetConstant()->AsLongConstant()->GetValue() == 0) { + if (Int64ConstantFrom(value) == 0) { __ B(slow_path->GetEntryLabel()); } } @@ -3622,7 +3637,7 @@ void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) { __ And(shift_right, RegisterFrom(rhs), 0x1F); __ Lsrs(shift_left, RegisterFrom(rhs), 6); __ Rsb(LeaveFlags, shift_left, shift_right, Operand::From(kArmBitsPerWord)); - __ B(cc, &shift_by_32_plus_shift_right); + __ B(cc, &shift_by_32_plus_shift_right, /* far_target */ false); // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right). // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right). @@ -3756,7 +3771,7 @@ void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) { __ Lsr(out_reg, first_reg, out_reg); } } else { - int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); + int32_t cst = Int32ConstantFrom(second); uint32_t shift_value = cst & kMaxIntShiftDistance; if (shift_value == 0) { // ARM does not support shifting with 0 immediate. __ Mov(out_reg, first_reg); @@ -3793,9 +3808,9 @@ void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) { // If the shift is > 32 bits, override the high part __ Subs(temp, o_l, Operand::From(kArmBitsPerWord)); { - AssemblerAccurateScope guard(GetVIXLAssembler(), - 2 * vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope guard(GetVIXLAssembler(), + 2 * vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ it(pl); __ lsl(pl, o_h, low, temp); } @@ -3812,9 +3827,9 @@ void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) { // If the shift is > 32 bits, override the low part __ Subs(temp, o_h, Operand::From(kArmBitsPerWord)); { - AssemblerAccurateScope guard(GetVIXLAssembler(), - 2 * vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope guard(GetVIXLAssembler(), + 2 * vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ it(pl); __ asr(pl, o_l, high, temp); } @@ -3829,9 +3844,9 @@ void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) { __ Orr(o_l, o_l, temp); __ Subs(temp, o_h, Operand::From(kArmBitsPerWord)); { - AssemblerAccurateScope guard(GetVIXLAssembler(), - 2 * vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope guard(GetVIXLAssembler(), + 2 * vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ it(pl); __ lsr(pl, o_l, high, temp); } @@ -3841,7 +3856,7 @@ void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) { // Register allocator doesn't create partial overlap. DCHECK(!o_l.Is(high)); DCHECK(!o_h.Is(low)); - int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); + int32_t cst = Int32ConstantFrom(second); uint32_t shift_value = cst & kMaxLongShiftDistance; if (shift_value > 32) { if (op->IsShl()) { @@ -3948,9 +3963,9 @@ void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString)); GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value()); // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used. - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::k16BitT32InstructionSizeInBytes, - CodeBufferCheckScope::kExactSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::k16BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kExactSize); __ blx(lr); codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } else { @@ -4094,8 +4109,8 @@ void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) { } case Primitive::kPrimLong: { __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare. - __ B(lt, &less); - __ B(gt, &greater); + __ B(lt, &less, /* far_target */ false); + __ B(gt, &greater, /* far_target */ false); // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags. __ Mov(out, 0); __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare. @@ -4116,8 +4131,8 @@ void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) { UNREACHABLE(); } - __ B(eq, &done); - __ B(less_cond, &less); + __ B(eq, &done, /* far_target */ false); + __ B(less_cond, &less, /* far_target */ false); __ Bind(&greater); __ Mov(out, 1); @@ -4192,9 +4207,9 @@ void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register a __ Bind(&fail); { // Ensure the pc position is recorded immediately after the `ldrexd` instruction. - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); // We need a load followed by store. (The address used in a STREX instruction must // be the same as the address in the most recently executed LDREX instruction.) __ ldrexd(temp1, temp2, MemOperand(addr)); @@ -4411,7 +4426,7 @@ void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction, locations->AddTemp(Location::RequiresRegister()); } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) { // We need a temporary register for the read barrier marking slow - // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier. + // path in CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier. locations->AddTemp(Location::RequiresRegister()); } } @@ -4715,9 +4730,9 @@ void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) { UseScratchRegisterScope temps(GetVIXLAssembler()); // Ensure the pc position is recorded immediately after the `ldr` instruction. - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0))); RecordPcInfo(instruction, instruction->GetDexPc()); } @@ -4873,7 +4888,7 @@ void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) { object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap); } // We need a temporary register for the read barrier marking slow - // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier. + // path in CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier. // Also need for String compression feature. if ((object_array_get_with_read_barrier && kUseBakerReadBarrier) || (mirror::kUseStringCompression && instruction->IsStringCharAt())) { @@ -4908,13 +4923,13 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { codegen_->MaybeRecordImplicitNullCheck(instruction); } if (index.IsConstant()) { - int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue(); + int32_t const_index = Int32ConstantFrom(index); if (maybe_compressed_char_at) { vixl32::Label uncompressed_load, done; __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not. static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, "Expecting 0=compressed, 1=uncompressed"); - __ B(cs, &uncompressed_load); + __ B(cs, &uncompressed_load, /* far_target */ false); GetAssembler()->LoadFromOffset(kLoadUnsignedByte, RegisterFrom(out_loc), obj, @@ -4942,7 +4957,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { // `TryExtractArrayAccessAddress()`. if (kIsDebugBuild) { HIntermediateAddress* tmp = array_instr->AsIntermediateAddress(); - DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset); + DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset); } temp = obj; } else { @@ -4953,7 +4968,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not. static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, "Expecting 0=compressed, 1=uncompressed"); - __ B(cs, &uncompressed_load); + __ B(cs, &uncompressed_load, /* far_target */ false); __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0)); __ B(&done); __ Bind(&uncompressed_load); @@ -4987,7 +5002,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { vixl32::Register out = OutputRegister(instruction); if (index.IsConstant()) { size_t offset = - (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + (Int32ConstantFrom(index) << TIMES_4) + data_offset; GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset); // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, // we should use a scope and the assembler to emit the load instruction to guarantee that @@ -5009,7 +5024,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { // `TryExtractArrayAccessAddress()`. if (kIsDebugBuild) { HIntermediateAddress* tmp = array_instr->AsIntermediateAddress(); - DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset); + DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset); } temp = obj; } else { @@ -5034,7 +5049,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { case Primitive::kPrimLong: { if (index.IsConstant()) { size_t offset = - (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + (Int32ConstantFrom(index) << TIMES_8) + data_offset; GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset); } else { UseScratchRegisterScope temps(GetVIXLAssembler()); @@ -5048,7 +5063,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { case Primitive::kPrimFloat: { vixl32::SRegister out = SRegisterFrom(out_loc); if (index.IsConstant()) { - size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset; GetAssembler()->LoadSFromOffset(out, obj, offset); } else { UseScratchRegisterScope temps(GetVIXLAssembler()); @@ -5061,7 +5076,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { case Primitive::kPrimDouble: { if (index.IsConstant()) { - size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset; GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset); } else { UseScratchRegisterScope temps(GetVIXLAssembler()); @@ -5135,7 +5150,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { case Primitive::kPrimChar: case Primitive::kPrimInt: { if (index.IsConstant()) { - int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue(); + int32_t const_index = Int32ConstantFrom(index); uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(value_type)); StoreOperandType store_type = GetStoreOperandType(value_type); @@ -5150,7 +5165,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { // `TryExtractArrayAccessAddress()`. if (kIsDebugBuild) { HIntermediateAddress* tmp = array_instr->AsIntermediateAddress(); - DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset); + DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset); } temp = array; } else { @@ -5171,7 +5186,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { // Just setting null. if (index.IsConstant()) { size_t offset = - (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + (Int32ConstantFrom(index) << TIMES_4) + data_offset; GetAssembler()->StoreToOffset(kStoreWord, value, array, offset); } else { DCHECK(index.IsRegister()) << index; @@ -5207,7 +5222,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { __ CompareAndBranchIfNonZero(value, &non_zero); if (index.IsConstant()) { size_t offset = - (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + (Int32ConstantFrom(index) << TIMES_4) + data_offset; GetAssembler()->StoreToOffset(kStoreWord, value, array, offset); } else { DCHECK(index.IsRegister()) << index; @@ -5233,9 +5248,9 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { { // Ensure we record the pc position immediately after the `ldr` instruction. - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); // /* HeapReference<Class> */ temp1 = array->klass_ __ ldr(temp1, MemOperand(array, class_offset)); codegen_->MaybeRecordImplicitNullCheck(instruction); @@ -5252,7 +5267,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { if (instruction->StaticTypeOfArrayIsObjectArray()) { vixl32::Label do_put; - __ B(eq, &do_put); + __ B(eq, &do_put, /* far_target */ false); // If heap poisoning is enabled, the `temp1` reference has // not been unpoisoned yet; unpoison it now. GetAssembler()->MaybeUnpoisonHeapReference(temp1); @@ -5281,7 +5296,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { if (index.IsConstant()) { size_t offset = - (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + (Int32ConstantFrom(index) << TIMES_4) + data_offset; GetAssembler()->StoreToOffset(kStoreWord, source, array, offset); } else { DCHECK(index.IsRegister()) << index; @@ -5318,7 +5333,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { Location value = locations->InAt(2); if (index.IsConstant()) { size_t offset = - (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + (Int32ConstantFrom(index) << TIMES_8) + data_offset; GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset); } else { UseScratchRegisterScope temps(GetVIXLAssembler()); @@ -5333,7 +5348,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { Location value = locations->InAt(2); DCHECK(value.IsFpuRegister()); if (index.IsConstant()) { - size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset; GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset); } else { UseScratchRegisterScope temps(GetVIXLAssembler()); @@ -5348,7 +5363,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { Location value = locations->InAt(2); DCHECK(value.IsFpuRegisterPair()); if (index.IsConstant()) { - size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; + size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset; GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset); } else { UseScratchRegisterScope temps(GetVIXLAssembler()); @@ -5384,9 +5399,9 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction vixl32::Register obj = InputRegisterAt(instruction, 0); vixl32::Register out = OutputRegister(instruction); { - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ ldr(out, MemOperand(obj, offset)); codegen_->MaybeRecordImplicitNullCheck(instruction); } @@ -5413,7 +5428,7 @@ void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddr if (second.IsRegister()) { __ Add(out, first, RegisterFrom(second)); } else { - __ Add(out, first, second.GetConstant()->AsIntConstant()->GetValue()); + __ Add(out, first, Int32ConstantFrom(second)); } } @@ -5609,7 +5624,7 @@ void ParallelMoveResolverARMVIXL::EmitMove(size_t index) { GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex()); } } else if (constant->IsLongConstant()) { - int64_t value = constant->AsLongConstant()->GetValue(); + int64_t value = Int64ConstantFrom(source); if (destination.IsRegisterPair()) { __ Mov(LowRegisterFrom(destination), Low32Bits(value)); __ Mov(HighRegisterFrom(destination), High32Bits(value)); @@ -5768,17 +5783,15 @@ HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind( case HLoadClass::LoadKind::kReferrersClass: break; case HLoadClass::LoadKind::kBootImageLinkTimeAddress: - // TODO(VIXL): Enable it back when literal pools are fixed in VIXL. - return HLoadClass::LoadKind::kDexCacheViaMethod; + DCHECK(!GetCompilerOptions().GetCompilePic()); + break; case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: DCHECK(GetCompilerOptions().GetCompilePic()); break; case HLoadClass::LoadKind::kBootImageAddress: - // TODO(VIXL): Enable it back when literal pools are fixed in VIXL. - return HLoadClass::LoadKind::kDexCacheViaMethod; - case HLoadClass::LoadKind::kDexCacheAddress: - // TODO(VIXL): Enable it back when literal pools are fixed in VIXL. - return HLoadClass::LoadKind::kDexCacheViaMethod; + break; + case HLoadClass::LoadKind::kJitTableAddress: + break; case HLoadClass::LoadKind::kDexCachePcRelative: DCHECK(!Runtime::Current()->UseJitCompilation()); // We disable pc-relative load when there is an irreducible loop, as the optimization @@ -5854,7 +5867,9 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { break; } case HLoadClass::LoadKind::kBootImageLinkTimeAddress: { - TODO_VIXL32(FATAL); + DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); + __ Ldr(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(), + cls->GetTypeIndex())); break; } case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { @@ -5865,11 +5880,18 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) { break; } case HLoadClass::LoadKind::kBootImageAddress: { - TODO_VIXL32(FATAL); + DCHECK_EQ(read_barrier_option, kWithoutReadBarrier); + DCHECK_NE(cls->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address)); break; } - case HLoadClass::LoadKind::kDexCacheAddress: { - TODO_VIXL32(FATAL); + case HLoadClass::LoadKind::kJitTableAddress: { + __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(), + cls->GetTypeIndex(), + cls->GetAddress())); + // /* GcRoot<mirror::Class> */ out = *out + GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); break; } case HLoadClass::LoadKind::kDexCachePcRelative: { @@ -5954,21 +5976,19 @@ HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) { switch (desired_string_load_kind) { case HLoadString::LoadKind::kBootImageLinkTimeAddress: - // TODO(VIXL): Implement missing optimization. - return HLoadString::LoadKind::kDexCacheViaMethod; + DCHECK(!GetCompilerOptions().GetCompilePic()); + break; case HLoadString::LoadKind::kBootImageLinkTimePcRelative: DCHECK(GetCompilerOptions().GetCompilePic()); break; case HLoadString::LoadKind::kBootImageAddress: - // TODO(VIXL): Implement missing optimization. - return HLoadString::LoadKind::kDexCacheViaMethod; + break; case HLoadString::LoadKind::kBssEntry: DCHECK(!Runtime::Current()->UseJitCompilation()); break; case HLoadString::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); - // TODO(VIXL): Implement missing optimization. - return HLoadString::LoadKind::kDexCacheViaMethod; + break; case HLoadString::LoadKind::kDexCacheViaMethod: break; } @@ -6010,8 +6030,9 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) { switch (load_kind) { case HLoadString::LoadKind::kBootImageLinkTimeAddress: { - TODO_VIXL32(FATAL); - break; + __ Ldr(out, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(), + load->GetStringIndex())); + return; // No dex cache slow path. } case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { DCHECK(codegen_->GetCompilerOptions().IsBootImage()); @@ -6021,8 +6042,10 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) { return; // No dex cache slow path. } case HLoadString::LoadKind::kBootImageAddress: { - TODO_VIXL32(FATAL); - break; + DCHECK_NE(load->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress()); + __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address)); + return; // No dex cache slow path. } case HLoadString::LoadKind::kBssEntry: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); @@ -6039,8 +6062,11 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) { return; } case HLoadString::LoadKind::kJitTableAddress: { - TODO_VIXL32(FATAL); - break; + __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(), + load->GetStringIndex())); + // /* GcRoot<mirror::String> */ out = *out + GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + return; } default: break; @@ -6182,7 +6208,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) kCompilerReadBarrierOption); __ Cmp(out, cls); // Classes must be equal for the instanceof to succeed. - __ B(ne, &zero); + __ B(ne, &zero, /* far_target */ false); __ Mov(out, 1); __ B(&done); break; @@ -6209,7 +6235,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) // If `out` is null, we use it for the result, and jump to `done`. __ CompareAndBranchIfZero(out, &done, /* far_target */ false); __ Cmp(out, cls); - __ B(ne, &loop); + __ B(ne, &loop, /* far_target */ false); __ Mov(out, 1); if (zero.IsReferenced()) { __ B(&done); @@ -6229,7 +6255,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) vixl32::Label loop, success; __ Bind(&loop); __ Cmp(out, cls); - __ B(eq, &success); + __ B(eq, &success, /* far_target */ false); // /* HeapReference<Class> */ out = out->super_class_ GenerateReferenceLoadOneRegister(instruction, out_loc, @@ -6258,7 +6284,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) // Do an exact check. vixl32::Label exact_check; __ Cmp(out, cls); - __ B(eq, &exact_check); + __ B(eq, &exact_check, /* far_target */ false); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ out = out->component_type_ GenerateReferenceLoadOneRegister(instruction, @@ -6460,7 +6486,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { // Otherwise, compare the classes. __ Cmp(temp, cls); - __ B(ne, &loop); + __ B(ne, &loop, /* far_target */ false); break; } @@ -6477,7 +6503,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { vixl32::Label loop; __ Bind(&loop); __ Cmp(temp, cls); - __ B(eq, &done); + __ B(eq, &done, /* far_target */ false); // /* HeapReference<Class> */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, @@ -6505,7 +6531,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { // Do an exact check. __ Cmp(temp, cls); - __ B(eq, &done); + __ B(eq, &done, /* far_target */ false); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ temp = temp->component_type_ @@ -6569,7 +6595,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2); // Compare the classes and continue the loop if they do not match. __ Cmp(cls, RegisterFrom(maybe_temp3_loc)); - __ B(ne, &start_loop); + __ B(ne, &start_loop, /* far_target */ false); break; } } @@ -7202,20 +7228,7 @@ void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruct // otherwise return a fall-back info that should be used instead. HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch( const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, - HInvokeStaticOrDirect* invoke) { - // TODO(VIXL): Implement optimized code paths. - if (desired_dispatch_info.method_load_kind == - HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup || - desired_dispatch_info.code_ptr_location == - HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup) { - return { - HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod, - HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, - 0u, - 0u - }; - } - + HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) { HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info; // We disable pc-relative load when there is an irreducible loop, as the optimization // is incompatible with it. @@ -7227,24 +7240,6 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStat dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; } - if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) { - const DexFile& outer_dex_file = GetGraph()->GetDexFile(); - if (&outer_dex_file != invoke->GetTargetMethod().dex_file) { - // Calls across dex files are more likely to exceed the available BL range, - // so use absolute patch with fixup if available and kCallArtMethod otherwise. - HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = - (desired_dispatch_info.method_load_kind == - HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) - ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup - : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; - return HInvokeStaticOrDirect::DispatchInfo { - dispatch_info.method_load_kind, - code_ptr_location, - dispatch_info.method_load_data, - 0u - }; - } - } return dispatch_info; } @@ -7276,20 +7271,6 @@ vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter( void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( HInvokeStaticOrDirect* invoke, Location temp) { - // For better instruction scheduling we load the direct code pointer before the method pointer. - switch (invoke->GetCodePtrLocation()) { - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - // LR = code address from literal pool with link-time patch. - TODO_VIXL32(FATAL); - break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // LR = invoke->GetDirectCodePtr(); - __ Mov(lr, Operand::From(invoke->GetDirectCodePtr())); - break; - default: - break; - } - Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. switch (invoke->GetMethodLoadKind()) { case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: { @@ -7305,9 +7286,6 @@ void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress())); break; - case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: - TODO_VIXL32(FATAL); - break; case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { HArmDexCacheArraysBase* base = invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase(); @@ -7347,30 +7325,6 @@ void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: __ Bl(GetFrameEntryLabel()); break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: - relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file, - invoke->GetTargetMethod().dex_method_index); - { - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); - __ bind(&relative_call_patches_.back().label); - // Arbitrarily branch to the BL itself, override at link time. - __ bl(&relative_call_patches_.back().label); - } - break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // LR prepared above for better instruction scheduling. - // LR() - { - // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used. - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::k16BitT32InstructionSizeInBytes, - CodeBufferCheckScope::kExactSize); - __ blx(lr); - } - break; case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: // LR = callee_method->entry_point_from_quick_compiled_code_ GetAssembler()->LoadFromOffset( @@ -7380,9 +7334,9 @@ void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall( ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value()); { // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used. - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::k16BitT32InstructionSizeInBytes, - CodeBufferCheckScope::kExactSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::k16BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kExactSize); // LR() __ blx(lr); } @@ -7406,9 +7360,9 @@ void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); { // Make sure the pc is recorded immediately after the `ldr` instruction. - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); // /* HeapReference<Class> */ temp = receiver->klass_ __ ldr(temp, MemOperand(receiver, class_offset)); MaybeRecordImplicitNullCheck(invoke); @@ -7433,9 +7387,9 @@ void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location // `RecordPcInfo()` immediately following record the correct pc. Use a scope to help guarantee // that. // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used. - AssemblerAccurateScope aas(GetVIXLAssembler(), - vixl32::k16BitT32InstructionSizeInBytes, - CodeBufferCheckScope::kExactSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + vixl32::k16BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kExactSize); __ blx(lr); } @@ -7460,6 +7414,57 @@ CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePa return &patches->back(); } +VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageStringLiteral( + const DexFile& dex_file, + dex::StringIndex string_index) { + return boot_image_string_patches_.GetOrCreate( + StringReference(&dex_file, string_index), + [this]() { + return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); + }); +} + +VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageTypeLiteral( + const DexFile& dex_file, + dex::TypeIndex type_index) { + return boot_image_type_patches_.GetOrCreate( + TypeReference(&dex_file, type_index), + [this]() { + return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); + }); +} + +VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageAddressLiteral(uint32_t address) { + bool needs_patch = GetCompilerOptions().GetIncludePatchInformation(); + Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_; + return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map); +} + +VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateDexCacheAddressLiteral(uint32_t address) { + return DeduplicateUint32Literal(address, &uint32_literals_); +} + +VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(const DexFile& dex_file, + dex::StringIndex string_index) { + jit_string_roots_.Overwrite(StringReference(&dex_file, string_index), /* placeholder */ 0u); + return jit_string_patches_.GetOrCreate( + StringReference(&dex_file, string_index), + [this]() { + return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); + }); +} + +VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file, + dex::TypeIndex type_index, + uint64_t address) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, type_index), address); + return jit_class_patches_.GetOrCreate( + TypeReference(&dex_file, type_index), + [this]() { + return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); + }); +} + template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches( const ArenaDeque<PcRelativePatchInfo>& infos, @@ -7483,18 +7488,24 @@ inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches( void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { DCHECK(linker_patches->empty()); size_t size = - relative_call_patches_.size() + /* MOVW+MOVT for each entry */ 2u * pc_relative_dex_cache_patches_.size() + + boot_image_string_patches_.size() + /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() + - /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size(); + boot_image_type_patches_.size() + + /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size() + + boot_image_address_patches_.size(); linker_patches->reserve(size); - for (const PatchInfo<vixl32::Label>& info : relative_call_patches_) { - uint32_t literal_offset = info.label.GetLocation(); - linker_patches->push_back( - LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index)); - } EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, linker_patches); + for (const auto& entry : boot_image_string_patches_) { + const StringReference& target_string = entry.first; + VIXLUInt32Literal* literal = entry.second; + DCHECK(literal->IsBound()); + uint32_t literal_offset = literal->GetLocation(); + linker_patches->push_back(LinkerPatch::StringPatch(literal_offset, + target_string.dex_file, + target_string.string_index.index_)); + } if (!GetCompilerOptions().IsBootImage()) { EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_, linker_patches); @@ -7502,8 +7513,44 @@ void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pa EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_, linker_patches); } + for (const auto& entry : boot_image_type_patches_) { + const TypeReference& target_type = entry.first; + VIXLUInt32Literal* literal = entry.second; + DCHECK(literal->IsBound()); + uint32_t literal_offset = literal->GetLocation(); + linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, + target_type.dex_file, + target_type.type_index.index_)); + } EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, linker_patches); + for (const auto& entry : boot_image_address_patches_) { + DCHECK(GetCompilerOptions().GetIncludePatchInformation()); + VIXLUInt32Literal* literal = entry.second; + DCHECK(literal->IsBound()); + uint32_t literal_offset = literal->GetLocation(); + linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset)); + } +} + +VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal( + uint32_t value, + Uint32ToLiteralMap* map) { + return map->GetOrCreate( + value, + [this, value]() { + return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ value); + }); +} + +VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateMethodLiteral( + MethodReference target_method, + MethodToLiteralMap* map) { + return map->GetOrCreate( + target_method, + [this]() { + return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); + }); } void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { @@ -7699,12 +7746,37 @@ void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(HClassTableGet* instruc } } +static void PatchJitRootUse(uint8_t* code, + const uint8_t* roots_data, + VIXLUInt32Literal* literal, + uint64_t index_in_table) { + DCHECK(literal->IsBound()); + uint32_t literal_offset = literal->GetLocation(); + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + uint8_t* data = code + literal_offset; + reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address); +} + +void CodeGeneratorARMVIXL::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { + for (const auto& entry : jit_string_patches_) { + const auto& it = jit_string_roots_.find(entry.first); + DCHECK(it != jit_string_roots_.end()); + PatchJitRootUse(code, roots_data, entry.second, it->second); + } + for (const auto& entry : jit_class_patches_) { + const auto& it = jit_class_roots_.find(entry.first); + DCHECK(it != jit_class_roots_.end()); + PatchJitRootUse(code, roots_data, entry.second, it->second); + } +} + void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder( CodeGeneratorARMVIXL::PcRelativePatchInfo* labels, vixl32::Register out) { - AssemblerAccurateScope aas(GetVIXLAssembler(), - 3 * vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(GetVIXLAssembler(), + 3 * vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); // TODO(VIXL): Think about using mov instead of movw. __ bind(&labels->movw_label); __ movw(out, /* placeholder */ 0u); diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 5ec3da4652..297d63cefd 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -107,20 +107,20 @@ static const size_t kRuntimeParameterFpuRegistersLengthVIXL = arraysize(kRuntimeParameterFpuRegistersVIXL); class LoadClassSlowPathARMVIXL; - class CodeGeneratorARMVIXL; +using VIXLInt32Literal = vixl::aarch32::Literal<int32_t>; +using VIXLUInt32Literal = vixl::aarch32::Literal<uint32_t>; + class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> { public: - typedef vixl::aarch32::Literal<int32_t> IntLiteral; - explicit JumpTableARMVIXL(HPackedSwitch* switch_instr) : switch_instr_(switch_instr), table_start_(), bb_addresses_(switch_instr->GetArena()->Adapter(kArenaAllocCodeGenerator)) { uint32_t num_entries = switch_instr_->GetNumEntries(); for (uint32_t i = 0; i < num_entries; i++) { - IntLiteral *lit = new IntLiteral(0); + VIXLInt32Literal *lit = new VIXLInt32Literal(0, vixl32::RawLiteral::kManuallyPlaced); bb_addresses_.emplace_back(lit); } } @@ -133,7 +133,7 @@ class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> { private: HPackedSwitch* const switch_instr_; vixl::aarch32::Label table_start_; - ArenaVector<std::unique_ptr<IntLiteral>> bb_addresses_; + ArenaVector<std::unique_ptr<VIXLInt32Literal>> bb_addresses_; DISALLOW_COPY_AND_ASSIGN(JumpTableARMVIXL); }; @@ -566,8 +566,22 @@ class CodeGeneratorARMVIXL : public CodeGenerator { PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); + VIXLUInt32Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, + dex::StringIndex string_index); + VIXLUInt32Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, + dex::TypeIndex type_index); + VIXLUInt32Literal* DeduplicateBootImageAddressLiteral(uint32_t address); + VIXLUInt32Literal* DeduplicateDexCacheAddressLiteral(uint32_t address); + VIXLUInt32Literal* DeduplicateJitStringLiteral(const DexFile& dex_file, + dex::StringIndex string_index); + VIXLUInt32Literal* DeduplicateJitClassLiteral(const DexFile& dex_file, + dex::TypeIndex type_index, + uint64_t address); + void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; + // Fast path implementation of ReadBarrier::Barrier for a heap // reference field load when Baker's read barriers are used. void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, @@ -673,10 +687,19 @@ class CodeGeneratorARMVIXL : public CodeGenerator { vixl::aarch32::Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, vixl::aarch32::Register temp); - using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, vixl::aarch32::Literal<uint32_t>*>; + using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, VIXLUInt32Literal*>; using MethodToLiteralMap = - ArenaSafeMap<MethodReference, vixl::aarch32::Literal<uint32_t>*, MethodReferenceComparator>; - + ArenaSafeMap<MethodReference, VIXLUInt32Literal*, MethodReferenceComparator>; + using StringToLiteralMap = ArenaSafeMap<StringReference, + VIXLUInt32Literal*, + StringReferenceValueComparator>; + using TypeToLiteralMap = ArenaSafeMap<TypeReference, + VIXLUInt32Literal*, + TypeReferenceValueComparator>; + + VIXLUInt32Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map); + VIXLUInt32Literal* DeduplicateMethodLiteral(MethodReference target_method, + MethodToLiteralMap* map); PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches); @@ -697,15 +720,25 @@ class CodeGeneratorARMVIXL : public CodeGenerator { ArmVIXLAssembler assembler_; const ArmInstructionSetFeatures& isa_features_; - // Relative call patch info. - // Using ArenaDeque<> which retains element addresses on push/emplace_back(). - ArenaDeque<PatchInfo<vixl::aarch32::Label>> relative_call_patches_; + // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. + Uint32ToLiteralMap uint32_literals_; // PC-relative patch info for each HArmDexCacheArraysBase. ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_; + // Deduplication map for boot string literals for kBootImageLinkTimeAddress. + StringToLiteralMap boot_image_string_patches_; // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC). ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; + // Deduplication map for boot type literals for kBootImageLinkTimeAddress. + TypeToLiteralMap boot_image_type_patches_; // PC-relative type patch info. ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_; + // Deduplication map for patchable boot image addresses. + Uint32ToLiteralMap boot_image_address_patches_; + + // Patches for string literals in JIT compiled code. + StringToLiteralMap jit_string_patches_; + // Patches for class literals in JIT compiled code. + TypeToLiteralMap jit_class_patches_; DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARMVIXL); }; diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index ff48f6642d..01e0dac33e 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -458,10 +458,6 @@ CodeGeneratorMIPS::CodeGeneratorMIPS(HGraph* graph, isa_features_(isa_features), uint32_literals_(std::less<uint32_t>(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - method_patches_(MethodReferenceComparator(), - graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - call_patches_(MethodReferenceComparator(), - graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_string_patches_(StringReferenceValueComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), @@ -757,6 +753,11 @@ void CodeGeneratorMIPS::GenerateFrameEntry() { if (RequiresCurrentMethod()) { __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, kCurrentMethodStackOffset); } + + if (GetGraph()->HasShouldDeoptimizeFlag()) { + // Initialize should deoptimize flag to 0. + __ StoreToOffset(kStoreWord, ZERO, SP, GetStackOffsetOfShouldDeoptimizeFlag()); + } } void CodeGeneratorMIPS::GenerateFrameExit() { @@ -1003,8 +1004,6 @@ inline void CodeGeneratorMIPS::EmitPcRelativeLinkerPatches( void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { DCHECK(linker_patches->empty()); size_t size = - method_patches_.size() + - call_patches_.size() + pc_relative_dex_cache_patches_.size() + pc_relative_string_patches_.size() + pc_relative_type_patches_.size() + @@ -1012,24 +1011,6 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patch boot_image_type_patches_.size() + boot_image_address_patches_.size(); linker_patches->reserve(size); - for (const auto& entry : method_patches_) { - const MethodReference& target_method = entry.first; - Literal* literal = entry.second; - DCHECK(literal->GetLabel()->IsBound()); - uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); - linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, - target_method.dex_file, - target_method.dex_method_index)); - } - for (const auto& entry : call_patches_) { - const MethodReference& target_method = entry.first; - Literal* literal = entry.second; - DCHECK(literal->GetLabel()->IsBound()); - uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); - linker_patches->push_back(LinkerPatch::CodePatch(literal_offset, - target_method.dex_file, - target_method.dex_method_index)); - } EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, linker_patches); if (!GetCompilerOptions().IsBootImage()) { @@ -1102,14 +1083,6 @@ Literal* CodeGeneratorMIPS::DeduplicateMethodLiteral(MethodReference target_meth [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); } -Literal* CodeGeneratorMIPS::DeduplicateMethodAddressLiteral(MethodReference target_method) { - return DeduplicateMethodLiteral(target_method, &method_patches_); -} - -Literal* CodeGeneratorMIPS::DeduplicateMethodCodeLiteral(MethodReference target_method) { - return DeduplicateMethodLiteral(target_method, &call_patches_); -} - Literal* CodeGeneratorMIPS::DeduplicateBootImageStringLiteral(const DexFile& dex_file, dex::StringIndex string_index) { return boot_image_string_patches_.GetOrCreate( @@ -1160,11 +1133,15 @@ void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholder( __ SetReorder(reordering); } -void CodeGeneratorMIPS::MarkGCCard(Register object, Register value) { +void CodeGeneratorMIPS::MarkGCCard(Register object, + Register value, + bool value_can_be_null) { MipsLabel done; Register card = AT; Register temp = TMP; - __ Beqz(value, &done); + if (value_can_be_null) { + __ Beqz(value, &done); + } __ LoadFromOffset(kLoadWord, card, TR, @@ -1172,7 +1149,9 @@ void CodeGeneratorMIPS::MarkGCCard(Register object, Register value) { __ Srl(temp, object, gc::accounting::CardTable::kCardShift); __ Addu(temp, card, temp); __ Sb(card, temp, 0); - __ Bind(&done); + if (value_can_be_null) { + __ Bind(&done); + } } void CodeGeneratorMIPS::SetupBlockedRegisters() const { @@ -2091,7 +2070,7 @@ void InstructionCodeGeneratorMIPS::VisitArraySet(HArraySet* instruction) { __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker); if (needs_write_barrier) { DCHECK_EQ(value_type, Primitive::kPrimNot); - codegen_->MarkGCCard(obj, value); + codegen_->MarkGCCard(obj, value, instruction->GetValueCanBeNull()); } } } else { @@ -4689,14 +4668,17 @@ void InstructionCodeGeneratorMIPS::GenConditionalMoveR6(HSelect* select) { } } -void LocationsBuilderMIPS::VisitShouldDeoptimizeFlag( - HShouldDeoptimizeFlag* flag ATTRIBUTE_UNUSED) { - // TODO: to be implemented. +void LocationsBuilderMIPS::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) { + LocationSummary* locations = new (GetGraph()->GetArena()) + LocationSummary(flag, LocationSummary::kNoCall); + locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorMIPS::VisitShouldDeoptimizeFlag( - HShouldDeoptimizeFlag* flag ATTRIBUTE_UNUSED) { - // TODO: to be implemented. +void InstructionCodeGeneratorMIPS::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) { + __ LoadFromOffset(kLoadWord, + flag->GetLocations()->Out().AsRegister<Register>(), + SP, + codegen_->GetStackOffsetOfShouldDeoptimizeFlag()); } void LocationsBuilderMIPS::VisitSelect(HSelect* select) { @@ -4892,7 +4874,8 @@ void LocationsBuilderMIPS::HandleFieldSet(HInstruction* instruction, const Field void InstructionCodeGeneratorMIPS::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, - uint32_t dex_pc) { + uint32_t dex_pc, + bool value_can_be_null) { Primitive::Type type = field_info.GetFieldType(); LocationSummary* locations = instruction->GetLocations(); Register obj = locations->InAt(0).AsRegister<Register>(); @@ -4987,7 +4970,7 @@ void InstructionCodeGeneratorMIPS::HandleFieldSet(HInstruction* instruction, // TODO: memory barriers? if (CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1))) { Register src = value_location.AsRegister<Register>(); - codegen_->MarkGCCard(obj, src); + codegen_->MarkGCCard(obj, src, value_can_be_null); } if (is_volatile) { @@ -5008,7 +4991,10 @@ void LocationsBuilderMIPS::VisitInstanceFieldSet(HInstanceFieldSet* instruction) } void InstructionCodeGeneratorMIPS::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { - HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetDexPc()); + HandleFieldSet(instruction, + instruction->GetFieldInfo(), + instruction->GetDexPc(), + instruction->GetValueCanBeNull()); } void InstructionCodeGeneratorMIPS::GenerateGcRootFieldLoad( @@ -5149,22 +5135,7 @@ void LocationsBuilderMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invo // art::PrepareForRegisterAllocation. DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); - HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind(); - HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation(); - bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); - - // kDirectAddressWithFixup and kCallDirectWithFixup need no extra input on R6 because - // R6 has PC-relative addressing. - bool has_extra_input = !isR6 && - ((method_load_kind == HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) || - (code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup)); - - if (invoke->HasPcRelativeDexCache()) { - // kDexCachePcRelative is mutually exclusive with - // kDirectAddressWithFixup/kCallDirectWithFixup. - CHECK(!has_extra_input); - has_extra_input = true; - } + bool has_extra_input = invoke->HasPcRelativeDexCache(); IntrinsicLocationsBuilderMIPS intrinsic(codegen_); if (intrinsic.TryDispatch(invoke)) { @@ -5251,9 +5222,9 @@ HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageAddress: break; - case HLoadClass::LoadKind::kDexCacheAddress: + case HLoadClass::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); - fallback_load = false; + fallback_load = true; break; case HLoadClass::LoadKind::kDexCachePcRelative: DCHECK(!Runtime::Current()->UseJitCompilation()); @@ -5304,9 +5275,7 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticO // is incompatible with it. bool has_irreducible_loops = GetGraph()->HasIrreducibleLoops(); bool fallback_load = true; - bool fallback_call = true; switch (dispatch_info.method_load_kind) { - case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: fallback_load = has_irreducible_loops; break; @@ -5314,25 +5283,10 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticO fallback_load = false; break; } - switch (dispatch_info.code_ptr_location) { - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - fallback_call = has_irreducible_loops; - break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: - // TODO: Implement this type. - break; - default: - fallback_call = false; - break; - } if (fallback_load) { dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; dispatch_info.method_load_data = 0; } - if (fallback_call) { - dispatch_info.code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; - dispatch_info.direct_code_ptr = 0; - } return dispatch_info; } @@ -5341,31 +5295,10 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind(); HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation(); - bool isR6 = isa_features_.IsR6(); - // kDirectAddressWithFixup and kCallDirectWithFixup have no extra input on R6 because - // R6 has PC-relative addressing. - bool has_extra_input = invoke->HasPcRelativeDexCache() || - (!isR6 && - ((method_load_kind == HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) || - (code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup))); - Register base_reg = has_extra_input + Register base_reg = invoke->HasPcRelativeDexCache() ? GetInvokeStaticOrDirectExtraParameter(invoke, temp.AsRegister<Register>()) : ZERO; - // For better instruction scheduling we load the direct code pointer before the method pointer. - switch (code_ptr_location) { - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // T9 = invoke->GetDirectCodePtr(); - __ LoadConst32(T9, invoke->GetDirectCodePtr()); - break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - // T9 = code address from literal pool with link-time patch. - __ LoadLiteral(T9, base_reg, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod())); - break; - default: - break; - } - switch (method_load_kind) { case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: { // temp = thread->string_init_entrypoint @@ -5383,11 +5316,6 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: __ LoadConst32(temp.AsRegister<Register>(), invoke->GetMethodAddress()); break; - case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: - __ LoadLiteral(temp.AsRegister<Register>(), - base_reg, - DeduplicateMethodAddressLiteral(invoke->GetTargetMethod())); - break; case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { HMipsDexCacheArraysBase* base = invoke->InputAt(invoke->GetSpecialInputIndex())->AsMipsDexCacheArraysBase(); @@ -5430,18 +5358,6 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: __ Bal(&frame_entry_label_); break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - // T9 prepared above for better instruction scheduling. - // T9() - __ Jalr(T9); - __ NopIfNoReordering(); - break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: - // TODO: Implement this type. - // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch(). - LOG(FATAL) << "Unsupported"; - UNREACHABLE(); case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: // T9 = callee_method->entry_point_from_quick_compiled_code_; __ LoadFromOffset(kLoadWord, @@ -5614,17 +5530,8 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) { codegen_->DeduplicateBootImageAddressLiteral(address)); break; } - case HLoadClass::LoadKind::kDexCacheAddress: { - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); - static_assert(sizeof(GcRoot<mirror::Class>) == 4u, "Expected GC root to be 4 bytes."); - DCHECK_ALIGNED(cls->GetAddress(), 4u); - int16_t offset = Low16Bits(address); - uint32_t base_address = address - offset; // This accounts for offset sign extension. - __ Lui(out, High16Bits(base_address)); - // /* GcRoot<mirror::Class> */ out = *(base_address + offset) - GenerateGcRootFieldLoad(cls, out_loc, out, offset); - generate_null_check = !cls->IsInDexCache(); + case HLoadClass::LoadKind::kJitTableAddress: { + LOG(FATAL) << "Unimplemented"; break; } case HLoadClass::LoadKind::kDexCachePcRelative: { @@ -5690,11 +5597,7 @@ void InstructionCodeGeneratorMIPS::VisitClearException(HClearException* clear AT } void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) { - LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier) - ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod) - ? LocationSummary::kCallOnMainOnly - : LocationSummary::kCallOnSlowPath) - : LocationSummary::kNoCall; + LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); HLoadString::LoadKind load_kind = load->GetLoadKind(); switch (load_kind) { @@ -5744,14 +5647,12 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) { switch (load_kind) { case HLoadString::LoadKind::kBootImageLinkTimeAddress: - DCHECK(!kEmitCompilerReadBarrier); __ LoadLiteral(out, base_or_current_method_reg, codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(), load->GetStringIndex())); return; // No dex cache slow path. case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { - DCHECK(!kEmitCompilerReadBarrier); DCHECK(codegen_->GetCompilerOptions().IsBootImage()); CodeGeneratorMIPS::PcRelativePatchInfo* info = codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); @@ -5759,7 +5660,6 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) { return; // No dex cache slow path. } case HLoadString::LoadKind::kBootImageAddress: { - DCHECK(!kEmitCompilerReadBarrier); DCHECK_NE(load->GetAddress(), 0u); uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress()); __ LoadLiteral(out, @@ -6285,7 +6185,10 @@ void LocationsBuilderMIPS::VisitStaticFieldSet(HStaticFieldSet* instruction) { } void InstructionCodeGeneratorMIPS::VisitStaticFieldSet(HStaticFieldSet* instruction) { - HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetDexPc()); + HandleFieldSet(instruction, + instruction->GetFieldInfo(), + instruction->GetDexPc(), + instruction->GetValueCanBeNull()); } void LocationsBuilderMIPS::VisitUnresolvedInstanceFieldGet( diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index f03f29c5d4..7b0812cb7b 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -236,7 +236,10 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { void HandleBinaryOp(HBinaryOperation* operation); void HandleCondition(HCondition* instruction); void HandleShift(HBinaryOperation* operation); - void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc); + void HandleFieldSet(HInstruction* instruction, + const FieldInfo& field_info, + uint32_t dex_pc, + bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc); // Generate a GC root reference load: // @@ -350,7 +353,7 @@ class CodeGeneratorMIPS : public CodeGenerator { // Emit linker patches. void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; - void MarkGCCard(Register object, Register value); + void MarkGCCard(Register object, Register value, bool value_can_be_null); // Register allocation. @@ -474,8 +477,6 @@ class CodeGeneratorMIPS : public CodeGenerator { Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map); Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map); - Literal* DeduplicateMethodAddressLiteral(MethodReference target_method); - Literal* DeduplicateMethodCodeLiteral(MethodReference target_method); PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches); @@ -495,9 +496,6 @@ class CodeGeneratorMIPS : public CodeGenerator { // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. Uint32ToLiteralMap uint32_literals_; - // Method patch info, map MethodReference to a literal for method address and method code. - MethodToLiteralMap method_patches_; - MethodToLiteralMap call_patches_; // PC-relative patch info for each HMipsDexCacheArraysBase. ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_; // Deduplication map for boot string literals for kBootImageLinkTimeAddress. diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index b1f9b1db53..5cf3c246cf 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -18,6 +18,7 @@ #include "art_method.h" #include "code_generator_utils.h" +#include "compiled_method.h" #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gc/accounting/card_table.h" @@ -90,8 +91,6 @@ Location InvokeDexCallingConventionVisitorMIPS64::GetNextLocation(Primitive::Typ // Space on the stack is reserved for all arguments. stack_index_ += Primitive::Is64BitType(type) ? 2 : 1; - // TODO: review - // TODO: shouldn't we use a whole machine word per argument on the stack? // Implicit 4-byte method pointer (and such) will cause misalignment. @@ -234,6 +233,7 @@ class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; + HLoadString* load = instruction_->AsLoadString(); const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex().index_; __ LoadConst32(calling_convention.GetRegisterAt(0), string_index); mips64_codegen->InvokeRuntime(kQuickResolveString, @@ -247,6 +247,17 @@ class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { type); RestoreLiveRegisters(codegen, locations); + + // Store the resolved String to the BSS entry. + // TODO: Change art_quick_resolve_string to kSaveEverything and use a temporary for the + // .bss entry address in the fast path, so that we can avoid another calculation here. + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + DCHECK_NE(out, AT); + CodeGeneratorMIPS64::PcRelativePatchInfo* info = + mips64_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index); + mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info, AT); + __ Sw(out, AT, /* placeholder */ 0x5678); + __ Bc(GetExitLabel()); } @@ -399,7 +410,20 @@ CodeGeneratorMIPS64::CodeGeneratorMIPS64(HGraph* graph, instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this), assembler_(graph->GetArena()), - isa_features_(isa_features) { + isa_features_(isa_features), + uint32_literals_(std::less<uint32_t>(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + uint64_literals_(std::less<uint64_t>(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + boot_image_string_patches_(StringReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + boot_image_type_patches_(TypeReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + boot_image_address_patches_(std::less<uint32_t>(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Save RA (containing the return address) to mimic Quick. AddAllocatedRegister(Location::RegisterLocation(RA)); } @@ -510,8 +534,6 @@ void CodeGeneratorMIPS64::GenerateFrameEntry() { RecordPcInfo(nullptr, 0); } - // TODO: anything related to T9/GP/GOT/PIC/.so's? - if (HasEmptyFrame()) { return; } @@ -562,13 +584,16 @@ void CodeGeneratorMIPS64::GenerateFrameEntry() { "kCurrentMethodStackOffset must fit into int16_t"); __ Sd(kMethodRegisterArgument, SP, kCurrentMethodStackOffset); } + + if (GetGraph()->HasShouldDeoptimizeFlag()) { + // Initialize should_deoptimize flag to 0. + __ StoreToOffset(kStoreWord, ZERO, SP, GetStackOffsetOfShouldDeoptimizeFlag()); + } } void CodeGeneratorMIPS64::GenerateFrameExit() { __ cfi().RememberState(); - // TODO: anything related to T9/GP/GOT/PIC/.so's? - if (!HasEmptyFrame()) { // Deallocate the rest of the frame. @@ -878,6 +903,136 @@ void CodeGeneratorMIPS64::MarkGCCard(GpuRegister object, } } +template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> +inline void CodeGeneratorMIPS64::EmitPcRelativeLinkerPatches( + const ArenaDeque<PcRelativePatchInfo>& infos, + ArenaVector<LinkerPatch>* linker_patches) { + for (const PcRelativePatchInfo& info : infos) { + const DexFile& dex_file = info.target_dex_file; + size_t offset_or_index = info.offset_or_index; + DCHECK(info.pc_rel_label.IsBound()); + uint32_t pc_rel_offset = __ GetLabelLocation(&info.pc_rel_label); + linker_patches->push_back(Factory(pc_rel_offset, &dex_file, pc_rel_offset, offset_or_index)); + } +} + +void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { + DCHECK(linker_patches->empty()); + size_t size = + pc_relative_dex_cache_patches_.size() + + pc_relative_string_patches_.size() + + pc_relative_type_patches_.size() + + boot_image_string_patches_.size() + + boot_image_type_patches_.size() + + boot_image_address_patches_.size(); + linker_patches->reserve(size); + EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, + linker_patches); + if (!GetCompilerOptions().IsBootImage()) { + EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_, + linker_patches); + } else { + EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_, + linker_patches); + } + EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_, + linker_patches); + for (const auto& entry : boot_image_string_patches_) { + const StringReference& target_string = entry.first; + Literal* literal = entry.second; + DCHECK(literal->GetLabel()->IsBound()); + uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); + linker_patches->push_back(LinkerPatch::StringPatch(literal_offset, + target_string.dex_file, + target_string.string_index.index_)); + } + for (const auto& entry : boot_image_type_patches_) { + const TypeReference& target_type = entry.first; + Literal* literal = entry.second; + DCHECK(literal->GetLabel()->IsBound()); + uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); + linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, + target_type.dex_file, + target_type.type_index.index_)); + } + for (const auto& entry : boot_image_address_patches_) { + DCHECK(GetCompilerOptions().GetIncludePatchInformation()); + Literal* literal = entry.second; + DCHECK(literal->GetLabel()->IsBound()); + uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel()); + linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset)); + } +} + +CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeStringPatch( + const DexFile& dex_file, uint32_t string_index) { + return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_); +} + +CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeTypePatch( + const DexFile& dex_file, dex::TypeIndex type_index) { + return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_); +} + +CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeDexCacheArrayPatch( + const DexFile& dex_file, uint32_t element_offset) { + return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_); +} + +CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativePatch( + const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) { + patches->emplace_back(dex_file, offset_or_index); + return &patches->back(); +} + +Literal* CodeGeneratorMIPS64::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) { + return map->GetOrCreate( + value, + [this, value]() { return __ NewLiteral<uint32_t>(value); }); +} + +Literal* CodeGeneratorMIPS64::DeduplicateUint64Literal(uint64_t value) { + return uint64_literals_.GetOrCreate( + value, + [this, value]() { return __ NewLiteral<uint64_t>(value); }); +} + +Literal* CodeGeneratorMIPS64::DeduplicateMethodLiteral(MethodReference target_method, + MethodToLiteralMap* map) { + return map->GetOrCreate( + target_method, + [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); +} + +Literal* CodeGeneratorMIPS64::DeduplicateBootImageStringLiteral(const DexFile& dex_file, + dex::StringIndex string_index) { + return boot_image_string_patches_.GetOrCreate( + StringReference(&dex_file, string_index), + [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); +} + +Literal* CodeGeneratorMIPS64::DeduplicateBootImageTypeLiteral(const DexFile& dex_file, + dex::TypeIndex type_index) { + return boot_image_type_patches_.GetOrCreate( + TypeReference(&dex_file, type_index), + [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); +} + +Literal* CodeGeneratorMIPS64::DeduplicateBootImageAddressLiteral(uint64_t address) { + bool needs_patch = GetCompilerOptions().GetIncludePatchInformation(); + Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_; + return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map); +} + +void CodeGeneratorMIPS64::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info, + GpuRegister out) { + __ Bind(&info->pc_rel_label); + // Add the high half of a 32-bit offset to PC. + __ Auipc(out, /* placeholder */ 0x1234); + // The immediately following instruction will add the sign-extended low half of the 32-bit + // offset to `out` (e.g. ld, jialc, daddiu). +} + void CodeGeneratorMIPS64::SetupBlockedRegisters() const { // ZERO, K0, K1, GP, SP, RA are always reserved and can't be allocated. blocked_core_registers_[ZERO] = true; @@ -901,8 +1056,6 @@ void CodeGeneratorMIPS64::SetupBlockedRegisters() const { // Reserve T9 for function calls blocked_core_registers_[T9] = true; - // TODO: review; anything else? - if (GetGraph()->IsDebuggable()) { // Stubs do not save callee-save floating point registers. If the graph // is debuggable, we need to deal with these registers differently. For @@ -946,7 +1099,6 @@ void CodeGeneratorMIPS64::InvokeRuntime(QuickEntrypointEnum entrypoint, uint32_t dex_pc, SlowPathCode* slow_path) { ValidateInvokeRuntime(entrypoint, instruction, slow_path); - // TODO: anything related to T9/GP/GOT/PIC/.so's? __ LoadFromOffset(kLoadDoubleword, T9, TR, @@ -1777,9 +1929,6 @@ void InstructionCodeGeneratorMIPS64::HandleCondition(HCondition* instruction) { Primitive::Type type = instruction->InputAt(0)->GetType(); LocationSummary* locations = instruction->GetLocations(); - GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); - Mips64Label true_label; - switch (type) { default: // Integer case. @@ -1788,29 +1937,11 @@ void InstructionCodeGeneratorMIPS64::HandleCondition(HCondition* instruction) { case Primitive::kPrimLong: GenerateIntLongCompare(instruction->GetCondition(), /* is64bit */ true, locations); return; - case Primitive::kPrimFloat: case Primitive::kPrimDouble: - // TODO: don't use branches. - GenerateFpCompareAndBranch(instruction->GetCondition(), - instruction->IsGtBias(), - type, - locations, - &true_label); - break; + GenerateFpCompare(instruction->GetCondition(), instruction->IsGtBias(), type, locations); + return; } - - // Convert the branches into the result. - Mips64Label done; - - // False case: result = 0. - __ LoadConst32(dst, 0); - __ Bc(&done); - - // True case: result = 1. - __ Bind(&true_label); - __ LoadConst32(dst, 1); - __ Bind(&done); } void InstructionCodeGeneratorMIPS64::DivRemOneOrMinusOne(HBinaryOperation* instruction) { @@ -2225,19 +2356,40 @@ void InstructionCodeGeneratorMIPS64::GenerateIntLongCompare(IfCondition cond, switch (cond) { case kCondEQ: case kCondNE: - if (use_imm && IsUint<16>(rhs_imm)) { - __ Xori(dst, lhs, rhs_imm); - } else { - if (use_imm) { - rhs_reg = TMP; - __ LoadConst64(rhs_reg, rhs_imm); + if (use_imm && IsInt<16>(-rhs_imm)) { + if (rhs_imm == 0) { + if (cond == kCondEQ) { + __ Sltiu(dst, lhs, 1); + } else { + __ Sltu(dst, ZERO, lhs); + } + } else { + if (is64bit) { + __ Daddiu(dst, lhs, -rhs_imm); + } else { + __ Addiu(dst, lhs, -rhs_imm); + } + if (cond == kCondEQ) { + __ Sltiu(dst, dst, 1); + } else { + __ Sltu(dst, ZERO, dst); + } } - __ Xor(dst, lhs, rhs_reg); - } - if (cond == kCondEQ) { - __ Sltiu(dst, dst, 1); } else { - __ Sltu(dst, ZERO, dst); + if (use_imm && IsUint<16>(rhs_imm)) { + __ Xori(dst, lhs, rhs_imm); + } else { + if (use_imm) { + rhs_reg = TMP; + __ LoadConst64(rhs_reg, rhs_imm); + } + __ Xor(dst, lhs, rhs_reg); + } + if (cond == kCondEQ) { + __ Sltiu(dst, dst, 1); + } else { + __ Sltu(dst, ZERO, dst); + } } break; @@ -2424,6 +2576,121 @@ void InstructionCodeGeneratorMIPS64::GenerateIntLongCompareAndBranch(IfCondition } } +void InstructionCodeGeneratorMIPS64::GenerateFpCompare(IfCondition cond, + bool gt_bias, + Primitive::Type type, + LocationSummary* locations) { + GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); + FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>(); + FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>(); + if (type == Primitive::kPrimFloat) { + switch (cond) { + case kCondEQ: + __ CmpEqS(FTMP, lhs, rhs); + __ Mfc1(dst, FTMP); + __ Andi(dst, dst, 1); + break; + case kCondNE: + __ CmpEqS(FTMP, lhs, rhs); + __ Mfc1(dst, FTMP); + __ Addiu(dst, dst, 1); + break; + case kCondLT: + if (gt_bias) { + __ CmpLtS(FTMP, lhs, rhs); + } else { + __ CmpUltS(FTMP, lhs, rhs); + } + __ Mfc1(dst, FTMP); + __ Andi(dst, dst, 1); + break; + case kCondLE: + if (gt_bias) { + __ CmpLeS(FTMP, lhs, rhs); + } else { + __ CmpUleS(FTMP, lhs, rhs); + } + __ Mfc1(dst, FTMP); + __ Andi(dst, dst, 1); + break; + case kCondGT: + if (gt_bias) { + __ CmpUltS(FTMP, rhs, lhs); + } else { + __ CmpLtS(FTMP, rhs, lhs); + } + __ Mfc1(dst, FTMP); + __ Andi(dst, dst, 1); + break; + case kCondGE: + if (gt_bias) { + __ CmpUleS(FTMP, rhs, lhs); + } else { + __ CmpLeS(FTMP, rhs, lhs); + } + __ Mfc1(dst, FTMP); + __ Andi(dst, dst, 1); + break; + default: + LOG(FATAL) << "Unexpected non-floating-point condition " << cond; + UNREACHABLE(); + } + } else { + DCHECK_EQ(type, Primitive::kPrimDouble); + switch (cond) { + case kCondEQ: + __ CmpEqD(FTMP, lhs, rhs); + __ Mfc1(dst, FTMP); + __ Andi(dst, dst, 1); + break; + case kCondNE: + __ CmpEqD(FTMP, lhs, rhs); + __ Mfc1(dst, FTMP); + __ Addiu(dst, dst, 1); + break; + case kCondLT: + if (gt_bias) { + __ CmpLtD(FTMP, lhs, rhs); + } else { + __ CmpUltD(FTMP, lhs, rhs); + } + __ Mfc1(dst, FTMP); + __ Andi(dst, dst, 1); + break; + case kCondLE: + if (gt_bias) { + __ CmpLeD(FTMP, lhs, rhs); + } else { + __ CmpUleD(FTMP, lhs, rhs); + } + __ Mfc1(dst, FTMP); + __ Andi(dst, dst, 1); + break; + case kCondGT: + if (gt_bias) { + __ CmpUltD(FTMP, rhs, lhs); + } else { + __ CmpLtD(FTMP, rhs, lhs); + } + __ Mfc1(dst, FTMP); + __ Andi(dst, dst, 1); + break; + case kCondGE: + if (gt_bias) { + __ CmpUleD(FTMP, rhs, lhs); + } else { + __ CmpLeD(FTMP, rhs, lhs); + } + __ Mfc1(dst, FTMP); + __ Andi(dst, dst, 1); + break; + default: + LOG(FATAL) << "Unexpected non-floating-point condition " << cond; + UNREACHABLE(); + } + } +} + void InstructionCodeGeneratorMIPS64::GenerateFpCompareAndBranch(IfCondition cond, bool gt_bias, Primitive::Type type, @@ -2636,14 +2903,17 @@ void InstructionCodeGeneratorMIPS64::VisitDeoptimize(HDeoptimize* deoptimize) { /* false_target */ nullptr); } -void LocationsBuilderMIPS64::VisitShouldDeoptimizeFlag( - HShouldDeoptimizeFlag* flag ATTRIBUTE_UNUSED) { - // TODO: to be implemented. +void LocationsBuilderMIPS64::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) { + LocationSummary* locations = new (GetGraph()->GetArena()) + LocationSummary(flag, LocationSummary::kNoCall); + locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorMIPS64::VisitShouldDeoptimizeFlag( - HShouldDeoptimizeFlag* flag ATTRIBUTE_UNUSED) { - // TODO: to be implemented. +void InstructionCodeGeneratorMIPS64::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) { + __ LoadFromOffset(kLoadWord, + flag->GetLocations()->Out().AsRegister<GpuRegister>(), + SP, + codegen_->GetStackOffsetOfShouldDeoptimizeFlag()); } void LocationsBuilderMIPS64::VisitSelect(HSelect* select) { @@ -2820,6 +3090,31 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceFieldSet(HInstanceFieldSet* in HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); } +void InstructionCodeGeneratorMIPS64::GenerateGcRootFieldLoad( + HInstruction* instruction ATTRIBUTE_UNUSED, + Location root, + GpuRegister obj, + uint32_t offset) { + // When handling HLoadClass::LoadKind::kDexCachePcRelative, the caller calls + // EmitPcRelativeAddressPlaceholderHigh() and then GenerateGcRootFieldLoad(). + // The relative patcher expects the two methods to emit the following patchable + // sequence of instructions in this case: + // auipc reg1, 0x1234 // 0x1234 is a placeholder for offset_high. + // lwu reg2, 0x5678(reg1) // 0x5678 is a placeholder for offset_low. + // TODO: Adjust GenerateGcRootFieldLoad() and its caller when this method is + // extended (e.g. for read barriers) so as not to break the relative patcher. + GpuRegister root_reg = root.AsRegister<GpuRegister>(); + if (kEmitCompilerReadBarrier) { + UNIMPLEMENTED(FATAL) << "for read barrier"; + } else { + // Plain GC root load with no read barrier. + // /* GcRoot<mirror::Object> */ root = *(obj + offset) + __ LoadFromOffset(kLoadUnsignedWord, root_reg, obj, offset); + // Note that GC roots are not affected by heap poisoning, thus we + // do not have to unpoison `root_reg` here. + } +} + void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary::CallKind call_kind = instruction->IsExactCheck() ? LocationSummary::kNoCall : LocationSummary::kCallOnSlowPath; @@ -2971,54 +3266,85 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS64* codeg } HLoadString::LoadKind CodeGeneratorMIPS64::GetSupportedLoadStringKind( - HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) { - // TODO: Implement other kinds. - return HLoadString::LoadKind::kDexCacheViaMethod; + HLoadString::LoadKind desired_string_load_kind) { + if (kEmitCompilerReadBarrier) { + UNIMPLEMENTED(FATAL) << "for read barrier"; + } + bool fallback_load = false; + switch (desired_string_load_kind) { + case HLoadString::LoadKind::kBootImageLinkTimeAddress: + DCHECK(!GetCompilerOptions().GetCompilePic()); + break; + case HLoadString::LoadKind::kBootImageLinkTimePcRelative: + DCHECK(GetCompilerOptions().GetCompilePic()); + break; + case HLoadString::LoadKind::kBootImageAddress: + break; + case HLoadString::LoadKind::kBssEntry: + DCHECK(!Runtime::Current()->UseJitCompilation()); + break; + case HLoadString::LoadKind::kDexCacheViaMethod: + break; + case HLoadString::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + // TODO: implement. + fallback_load = true; + break; + } + if (fallback_load) { + desired_string_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; + } + return desired_string_load_kind; } HLoadClass::LoadKind CodeGeneratorMIPS64::GetSupportedLoadClassKind( HLoadClass::LoadKind desired_class_load_kind) { - DCHECK_NE(desired_class_load_kind, HLoadClass::LoadKind::kReferrersClass); - // TODO: Implement other kinds. - return HLoadClass::LoadKind::kDexCacheViaMethod; + if (kEmitCompilerReadBarrier) { + UNIMPLEMENTED(FATAL) << "for read barrier"; + } + bool fallback_load = false; + switch (desired_class_load_kind) { + case HLoadClass::LoadKind::kReferrersClass: + break; + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + DCHECK(!GetCompilerOptions().GetCompilePic()); + break; + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + DCHECK(GetCompilerOptions().GetCompilePic()); + break; + case HLoadClass::LoadKind::kBootImageAddress: + break; + case HLoadClass::LoadKind::kJitTableAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + // TODO: implement. + fallback_load = true; + break; + case HLoadClass::LoadKind::kDexCachePcRelative: + DCHECK(!Runtime::Current()->UseJitCompilation()); + break; + case HLoadClass::LoadKind::kDexCacheViaMethod: + break; + } + if (fallback_load) { + desired_class_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; + } + return desired_class_load_kind; } HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch( const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) { - switch (desired_dispatch_info.method_load_kind) { - case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: - case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: - // TODO: Implement these types. For the moment, we fall back to kDexCacheViaMethod. - return HInvokeStaticOrDirect::DispatchInfo { - HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod, - HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, - 0u, - 0u - }; - default: - break; - } - switch (desired_dispatch_info.code_ptr_location) { - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: - // TODO: Implement these types. For the moment, we fall back to kCallArtMethod. - return HInvokeStaticOrDirect::DispatchInfo { - desired_dispatch_info.method_load_kind, - HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, - desired_dispatch_info.method_load_data, - 0u - }; - default: - return desired_dispatch_info; - } + // On MIPS64 we support all dispatch types. + return desired_dispatch_info; } void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) { // All registers are assumed to be correctly set up per the calling convention. - Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp. - switch (invoke->GetMethodLoadKind()) { + HInvokeStaticOrDirect::MethodLoadKind method_load_kind = invoke->GetMethodLoadKind(); + HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = invoke->GetCodePtrLocation(); + + switch (method_load_kind) { case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: { // temp = thread->string_init_entrypoint uint32_t offset = @@ -3033,14 +3359,18 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); break; case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: - __ LoadConst64(temp.AsRegister<GpuRegister>(), invoke->GetMethodAddress()); + __ LoadLiteral(temp.AsRegister<GpuRegister>(), + kLoadDoubleword, + DeduplicateUint64Literal(invoke->GetMethodAddress())); break; - case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: - case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: - // TODO: Implement these types. - // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch(). - LOG(FATAL) << "Unsupported"; - UNREACHABLE(); + case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { + uint32_t offset = invoke->GetDexCacheArrayOffset(); + CodeGeneratorMIPS64::PcRelativePatchInfo* info = + NewPcRelativeDexCacheArrayPatch(invoke->GetDexFile(), offset); + EmitPcRelativeAddressPlaceholderHigh(info, AT); + __ Ld(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678); + break; + } case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: { Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex()); GpuRegister reg = temp.AsRegister<GpuRegister>(); @@ -3071,23 +3401,10 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo } } - switch (invoke->GetCodePtrLocation()) { + switch (code_ptr_location) { case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: - __ Jialc(&frame_entry_label_, T9); - break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // LR = invoke->GetDirectCodePtr(); - __ LoadConst64(T9, invoke->GetDirectCodePtr()); - // LR() - __ Jalr(T9); - __ Nop(); + __ Balc(&frame_entry_label_); break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: - // TODO: Implement these types. - // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch(). - LOG(FATAL) << "Unsupported"; - UNREACHABLE(); case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: // T9 = callee_method->entry_point_from_quick_compiled_code_; __ LoadFromOffset(kLoadDoubleword, @@ -3157,11 +3474,26 @@ void InstructionCodeGeneratorMIPS64::VisitInvokeVirtual(HInvokeVirtual* invoke) } void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) { - InvokeRuntimeCallingConvention calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( - cls, - Location::RegisterLocation(calling_convention.GetRegisterAt(0)), - calling_convention.GetReturnLocation(cls->GetType())); + if (cls->NeedsAccessCheck()) { + InvokeRuntimeCallingConvention calling_convention; + CodeGenerator::CreateLoadClassLocationSummary( + cls, + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), + calling_convention.GetReturnLocation(Primitive::kPrimNot), + /* code_generator_supports_read_barrier */ false); + return; + } + + LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier) + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kReferrersClass || + load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + locations->SetInAt(0, Location::RequiresRegister()); + } + locations->SetOut(Location::RequiresRegister()); } void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { @@ -3173,35 +3505,90 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) { return; } - GpuRegister out = locations->Out().AsRegister<GpuRegister>(); - GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>(); - if (cls->IsReferrersClass()) { - DCHECK(!cls->CanCallRuntime()); - DCHECK(!cls->MustGenerateClinitCheck()); - __ LoadFromOffset(kLoadUnsignedWord, out, current_method, - ArtMethod::DeclaringClassOffset().Int32Value()); - } else { - __ LoadFromOffset(kLoadDoubleword, out, current_method, - ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value()); - __ LoadFromOffset( - kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_)); - // TODO: We will need a read barrier here. - if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) { - DCHECK(cls->CanCallRuntime()); - SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64( - cls, - cls, - cls->GetDexPc(), - cls->MustGenerateClinitCheck()); - codegen_->AddSlowPath(slow_path); - if (!cls->IsInDexCache()) { - __ Beqzc(out, slow_path->GetEntryLabel()); - } - if (cls->MustGenerateClinitCheck()) { - GenerateClassInitializationCheck(slow_path, out); - } else { - __ Bind(slow_path->GetExitLabel()); - } + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + Location out_loc = locations->Out(); + GpuRegister out = out_loc.AsRegister<GpuRegister>(); + GpuRegister current_method_reg = ZERO; + if (load_kind == HLoadClass::LoadKind::kReferrersClass || + load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + current_method_reg = locations->InAt(0).AsRegister<GpuRegister>(); + } + + bool generate_null_check = false; + switch (load_kind) { + case HLoadClass::LoadKind::kReferrersClass: + DCHECK(!cls->CanCallRuntime()); + DCHECK(!cls->MustGenerateClinitCheck()); + // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ + GenerateGcRootFieldLoad(cls, + out_loc, + current_method_reg, + ArtMethod::DeclaringClassOffset().Int32Value()); + break; + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + DCHECK(!kEmitCompilerReadBarrier); + __ LoadLiteral(out, + kLoadUnsignedWord, + codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(), + cls->GetTypeIndex())); + break; + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { + DCHECK(!kEmitCompilerReadBarrier); + CodeGeneratorMIPS64::PcRelativePatchInfo* info = + codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); + codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT); + __ Daddiu(out, AT, /* placeholder */ 0x5678); + break; + } + case HLoadClass::LoadKind::kBootImageAddress: { + DCHECK(!kEmitCompilerReadBarrier); + DCHECK_NE(cls->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + __ LoadLiteral(out, + kLoadUnsignedWord, + codegen_->DeduplicateBootImageAddressLiteral(address)); + break; + } + case HLoadClass::LoadKind::kJitTableAddress: { + LOG(FATAL) << "Unimplemented"; + break; + } + case HLoadClass::LoadKind::kDexCachePcRelative: { + uint32_t element_offset = cls->GetDexCacheElementOffset(); + CodeGeneratorMIPS64::PcRelativePatchInfo* info = + codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), element_offset); + codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT); + // /* GcRoot<mirror::Class> */ out = *address /* PC-relative */ + GenerateGcRootFieldLoad(cls, out_loc, AT, /* placeholder */ 0x5678); + generate_null_check = !cls->IsInDexCache(); + break; + } + case HLoadClass::LoadKind::kDexCacheViaMethod: { + // /* GcRoot<mirror::Class>[] */ out = + // current_method.ptr_sized_fields_->dex_cache_resolved_types_ + __ LoadFromOffset(kLoadDoubleword, + out, + current_method_reg, + ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value()); + // /* GcRoot<mirror::Class> */ out = out[type_index] + size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_); + GenerateGcRootFieldLoad(cls, out_loc, out, offset); + generate_null_check = !cls->IsInDexCache(); + } + } + + if (generate_null_check || cls->MustGenerateClinitCheck()) { + DCHECK(cls->CanCallRuntime()); + SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64( + cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); + codegen_->AddSlowPath(slow_path); + if (generate_null_check) { + __ Beqzc(out, slow_path->GetEntryLabel()); + } + if (cls->MustGenerateClinitCheck()) { + GenerateClassInitializationCheck(slow_path, out); + } else { + __ Bind(slow_path->GetExitLabel()); } } } @@ -3230,20 +3617,68 @@ void InstructionCodeGeneratorMIPS64::VisitClearException(HClearException* clear } void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) { - LocationSummary::CallKind call_kind = load->NeedsEnvironment() - ? LocationSummary::kCallOnSlowPath - : LocationSummary::kNoCall; + HLoadString::LoadKind load_kind = load->GetLoadKind(); + LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister()); + if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) { + InvokeRuntimeCallingConvention calling_convention; + locations->SetOut(calling_convention.GetReturnLocation(load->GetType())); + } else { + locations->SetOut(Location::RequiresRegister()); + } } void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) { + HLoadString::LoadKind load_kind = load->GetLoadKind(); + LocationSummary* locations = load->GetLocations(); + Location out_loc = locations->Out(); + GpuRegister out = out_loc.AsRegister<GpuRegister>(); + + switch (load_kind) { + case HLoadString::LoadKind::kBootImageLinkTimeAddress: + __ LoadLiteral(out, + kLoadUnsignedWord, + codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(), + load->GetStringIndex())); + return; // No dex cache slow path. + case HLoadString::LoadKind::kBootImageLinkTimePcRelative: { + DCHECK(codegen_->GetCompilerOptions().IsBootImage()); + CodeGeneratorMIPS64::PcRelativePatchInfo* info = + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); + codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT); + __ Daddiu(out, AT, /* placeholder */ 0x5678); + return; // No dex cache slow path. + } + case HLoadString::LoadKind::kBootImageAddress: { + DCHECK_NE(load->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress()); + __ LoadLiteral(out, + kLoadUnsignedWord, + codegen_->DeduplicateBootImageAddressLiteral(address)); + return; // No dex cache slow path. + } + case HLoadString::LoadKind::kBssEntry: { + DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); + CodeGeneratorMIPS64::PcRelativePatchInfo* info = + codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_); + codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT); + __ Lwu(out, AT, /* placeholder */ 0x5678); + SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load); + codegen_->AddSlowPath(slow_path); + __ Beqzc(out, slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + return; + } + default: + break; + } + // TODO: Re-add the compiler code to do string dex cache lookup again. - SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load); - codegen_->AddSlowPath(slow_path); - __ Bc(slow_path->GetEntryLabel()); - __ Bind(slow_path->GetExitLabel()); + DCHECK(load_kind == HLoadString::LoadKind::kDexCacheViaMethod); + InvokeRuntimeCallingConvention calling_convention; + __ LoadConst32(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_); + codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc()); + CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); } void LocationsBuilderMIPS64::VisitLongConstant(HLongConstant* constant) { @@ -3840,9 +4275,12 @@ void InstructionCodeGeneratorMIPS64::VisitTypeConversion(HTypeConversion* conver break; case Primitive::kPrimInt: case Primitive::kPrimLong: - // Sign-extend 32-bit int into bits 32 through 63 for - // int-to-long and long-to-int conversions - __ Sll(dst, src, 0); + // Sign-extend 32-bit int into bits 32 through 63 for int-to-long and long-to-int + // conversions, except when the input and output registers are the same and we are not + // converting longs to shorter types. In these cases, do nothing. + if ((input_type == Primitive::kPrimLong) || (dst != src)) { + __ Sll(dst, src, 0); + } break; default: diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 690eccb7d8..d5811c20e3 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -22,6 +22,7 @@ #include "nodes.h" #include "parallel_move_resolver.h" #include "utils/mips64/assembler_mips64.h" +#include "utils/type_reference.h" namespace art { namespace mips64 { @@ -227,6 +228,15 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { const FieldInfo& field_info, bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + // Generate a GC root reference load: + // + // root <- *(obj + offset) + // + // while honoring read barriers (if any). + void GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + GpuRegister obj, + uint32_t offset); void GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, Mips64Label* true_target, @@ -240,6 +250,10 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { bool is64bit, LocationSummary* locations, Mips64Label* label); + void GenerateFpCompare(IfCondition cond, + bool gt_bias, + Primitive::Type type, + LocationSummary* locations); void GenerateFpCompareAndBranch(IfCondition cond, bool gt_bias, Primitive::Type type, @@ -279,6 +293,9 @@ class CodeGeneratorMIPS64 : public CodeGenerator { Mips64Assembler* GetAssembler() OVERRIDE { return &assembler_; } const Mips64Assembler& GetAssembler() const OVERRIDE { return assembler_; } + // Emit linker patches. + void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void MarkGCCard(GpuRegister object, GpuRegister value, bool value_can_be_null); // Register allocation. @@ -307,6 +324,10 @@ class CodeGeneratorMIPS64 : public CodeGenerator { block_labels_ = CommonInitializeLabels<Mips64Label>(); } + // We prefer aligned loads and stores (less code), so spill and restore registers in slow paths + // at aligned locations. + uint32_t GetPreferredSlotsAlignment() const OVERRIDE { return kMips64DoublewordSize; } + void Finalize(CodeAllocator* allocator) OVERRIDE; // Code generation helpers. @@ -357,7 +378,57 @@ class CodeGeneratorMIPS64 : public CodeGenerator { void GenerateImplicitNullCheck(HNullCheck* instruction) OVERRIDE; void GenerateExplicitNullCheck(HNullCheck* instruction) OVERRIDE; + // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays, + // boot image strings and method calls. The only difference is the interpretation of + // the offset_or_index. + struct PcRelativePatchInfo { + PcRelativePatchInfo(const DexFile& dex_file, uint32_t off_or_idx) + : target_dex_file(dex_file), offset_or_index(off_or_idx) { } + PcRelativePatchInfo(PcRelativePatchInfo&& other) = default; + + const DexFile& target_dex_file; + // Either the dex cache array element offset or the string/type/method index. + uint32_t offset_or_index; + // Label for the auipc instruction. + Mips64Label pc_rel_label; + }; + + PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index); + PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index); + PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, + uint32_t element_offset); + PcRelativePatchInfo* NewPcRelativeCallPatch(const DexFile& dex_file, + uint32_t method_index); + Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, + dex::StringIndex string_index); + Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index); + Literal* DeduplicateBootImageAddressLiteral(uint64_t address); + + void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info, GpuRegister out); + private: + using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>; + using Uint64ToLiteralMap = ArenaSafeMap<uint64_t, Literal*>; + using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>; + using BootStringToLiteralMap = ArenaSafeMap<StringReference, + Literal*, + StringReferenceValueComparator>; + using BootTypeToLiteralMap = ArenaSafeMap<TypeReference, + Literal*, + TypeReferenceValueComparator>; + + Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map); + Literal* DeduplicateUint64Literal(uint64_t value); + Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map); + + PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file, + uint32_t offset_or_index, + ArenaDeque<PcRelativePatchInfo>* patches); + + template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)> + void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos, + ArenaVector<LinkerPatch>* linker_patches); + // Labels for each block that will be compiled. Mips64Label* block_labels_; // Indexed by block id. Mips64Label frame_entry_label_; @@ -367,6 +438,24 @@ class CodeGeneratorMIPS64 : public CodeGenerator { Mips64Assembler assembler_; const Mips64InstructionSetFeatures& isa_features_; + // Deduplication map for 32-bit literals, used for non-patchable boot image addresses. + Uint32ToLiteralMap uint32_literals_; + // Deduplication map for 64-bit literals, used for non-patchable method address or method code + // address. + Uint64ToLiteralMap uint64_literals_; + // PC-relative patch info. + ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_; + // Deduplication map for boot string literals for kBootImageLinkTimeAddress. + BootStringToLiteralMap boot_image_string_patches_; + // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC). + ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; + // Deduplication map for boot type literals for kBootImageLinkTimeAddress. + BootTypeToLiteralMap boot_image_type_patches_; + // PC-relative type patch info. + ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_; + // Deduplication map for patchable boot image addresses. + Uint32ToLiteralMap boot_image_address_patches_; + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorMIPS64); }; diff --git a/compiler/optimizing/code_generator_utils.h b/compiler/optimizing/code_generator_utils.h index 7efed8c9ec..a6b41c0588 100644 --- a/compiler/optimizing/code_generator_utils.h +++ b/compiler/optimizing/code_generator_utils.h @@ -18,6 +18,8 @@ #define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_UTILS_H_ #include <cstdint> +#include <cstdlib> +#include <limits> namespace art { @@ -32,6 +34,12 @@ void CalculateMagicAndShiftForDivRem(int64_t divisor, bool is_long, int64_t* mag // that it has been previously visited by the InstructionCodeGenerator. bool IsBooleanValueOrMaterializedCondition(HInstruction* cond_input); +template <typename T> T AbsOrMin(T value) { + return (value == std::numeric_limits<T>::min()) + ? value + : std::abs(value); +} + } // namespace art #endif // ART_COMPILER_OPTIMIZING_CODE_GENERATOR_UTILS_H_ diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index d6e92ccb81..0abe85540c 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1006,13 +1006,12 @@ CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, move_resolver_(graph->GetArena(), this), assembler_(graph->GetArena()), isa_features_(isa_features), - method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), jit_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_class_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), constant_area_start_(-1), fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), method_address_offset_(-1) { @@ -4453,20 +4452,7 @@ HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86::GetSupportedInvokeStaticOr HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) { dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; } - switch (dispatch_info.code_ptr_location) { - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // For direct code, we actually prefer to call via the code pointer from ArtMethod*. - // (Though the direct CALL ptr16:32 is available for consideration). - return HInvokeStaticOrDirect::DispatchInfo { - dispatch_info.method_load_kind, - HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, - dispatch_info.method_load_data, - 0u - }; - default: - return dispatch_info; - } + return dispatch_info; } Register CodeGeneratorX86::GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, @@ -4513,12 +4499,6 @@ Location CodeGeneratorX86::GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticO case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: __ movl(temp.AsRegister<Register>(), Immediate(invoke->GetMethodAddress())); break; - case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: - __ movl(temp.AsRegister<Register>(), Immediate(/* placeholder */ 0)); - method_patches_.emplace_back(*invoke->GetTargetMethod().dex_file, - invoke->GetTargetMethod().dex_method_index); - __ Bind(&method_patches_.back().label); // Bind the label at the end of the "movl" insn. - break; case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, temp.AsRegister<Register>()); @@ -4560,19 +4540,6 @@ void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: __ call(GetFrameEntryLabel()); break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: { - relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file, - invoke->GetTargetMethod().dex_method_index); - Label* label = &relative_call_patches_.back().label; - __ call(label); // Bind to the patch label, override at link time. - __ Bind(label); // Bind the label at the end of the "call" insn. - break; - } - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // Filtered out by GetSupportedInvokeStaticOrDirectDispatch(). - LOG(FATAL) << "Unsupported"; - UNREACHABLE(); case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: // (callee_method + offset_of_quick_compiled_code)() __ call(Address(callee_method.AsRegister<Register>(), @@ -4663,22 +4630,11 @@ inline void CodeGeneratorX86::EmitPcRelativeLinkerPatches( void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { DCHECK(linker_patches->empty()); size_t size = - method_patches_.size() + - relative_call_patches_.size() + pc_relative_dex_cache_patches_.size() + simple_patches_.size() + string_patches_.size() + type_patches_.size(); linker_patches->reserve(size); - for (const PatchInfo<Label>& info : method_patches_) { - uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; - linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, &info.dex_file, info.index)); - } - for (const PatchInfo<Label>& info : relative_call_patches_) { - uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; - linker_patches->push_back( - LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index)); - } EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, linker_patches); for (const Label& label : simple_patches_) { @@ -6034,7 +5990,7 @@ HLoadClass::LoadKind CodeGeneratorX86::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageAddress: break; - case HLoadClass::LoadKind::kDexCacheAddress: + case HLoadClass::LoadKind::kJitTableAddress: DCHECK(Runtime::Current()->UseJitCompilation()); break; case HLoadClass::LoadKind::kDexCacheViaMethod: @@ -6073,6 +6029,16 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { locations->SetOut(Location::RequiresRegister()); } +Label* CodeGeneratorX86::NewJitRootClassPatch(const DexFile& dex_file, + dex::TypeIndex dex_index, + uint64_t address) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, dex_index), address); + // Add a patch entry and return the label. + jit_class_patches_.emplace_back(dex_file, dex_index.index_); + PatchInfo<Label>* info = &jit_class_patches_.back(); + return &info->label; +} + void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { @@ -6124,16 +6090,12 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { codegen_->RecordSimplePatch(); break; } - case HLoadClass::LoadKind::kDexCacheAddress: { - DCHECK_NE(cls->GetAddress(), 0u); - uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + case HLoadClass::LoadKind::kJitTableAddress: { + Address address = Address::Absolute(CodeGeneratorX86::kDummy32BitOffset); + Label* fixup_label = codegen_->NewJitRootClassPatch( + cls->GetDexFile(), cls->GetTypeIndex(), cls->GetAddress()); // /* GcRoot<mirror::Class> */ out = *address - GenerateGcRootFieldLoad(cls, - out_loc, - Address::Absolute(address), - /* fixup_label */ nullptr, - read_barrier_option); - generate_null_check = !cls->IsInDexCache(); + GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption); break; } case HLoadClass::LoadKind::kDexCachePcRelative: { @@ -7770,18 +7732,31 @@ void CodeGeneratorX86::MoveFromReturnRegister(Location target, Primitive::Type t } } +void CodeGeneratorX86::PatchJitRootUse(uint8_t* code, + const uint8_t* roots_data, + const PatchInfo<Label>& info, + uint64_t index_in_table) const { + uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; + reinterpret_cast<unaligned_uint32_t*>(code + code_offset)[0] = + dchecked_integral_cast<uint32_t>(address); +} + void CodeGeneratorX86::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { for (const PatchInfo<Label>& info : jit_string_patches_) { - const auto& it = jit_string_roots_.find(StringReference(&info.dex_file, - dex::StringIndex(info.index))); + const auto& it = jit_string_roots_.find( + StringReference(&info.dex_file, dex::StringIndex(info.index))); DCHECK(it != jit_string_roots_.end()); - size_t index_in_table = it->second; - uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; - uintptr_t address = - reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); - typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; - reinterpret_cast<unaligned_uint32_t*>(code + code_offset)[0] = - dchecked_integral_cast<uint32_t>(address); + PatchJitRootUse(code, roots_data, info, it->second); + } + + for (const PatchInfo<Label>& info : jit_class_patches_) { + const auto& it = jit_class_roots_.find( + TypeReference(&info.dex_file, dex::TypeIndex(info.index))); + DCHECK(it != jit_class_roots_.end()); + PatchJitRootUse(code, roots_data, info, it->second); } } diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 2ae3670bed..1af685087c 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -416,12 +416,17 @@ class CodeGeneratorX86 : public CodeGenerator { Label* NewStringBssEntryPatch(HLoadString* load_string); Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); Label* NewJitRootStringPatch(const DexFile& dex_file, dex::StringIndex dex_index); + Label* NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, uint64_t address); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; // Emit linker patches. void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void PatchJitRootUse(uint8_t* code, + const uint8_t* roots_data, + const PatchInfo<Label>& info, + uint64_t index_in_table) const; void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; // Emit a write barrier. @@ -608,9 +613,6 @@ class CodeGeneratorX86 : public CodeGenerator { X86Assembler assembler_; const X86InstructionSetFeatures& isa_features_; - // Method patch info. Using ArenaDeque<> which retains element addresses on push/emplace_back(). - ArenaDeque<PatchInfo<Label>> method_patches_; - ArenaDeque<PatchInfo<Label>> relative_call_patches_; // PC-relative DexCache access info. ArenaDeque<PatchInfo<Label>> pc_relative_dex_cache_patches_; // Patch locations for patchoat where the linker doesn't do any other work. @@ -623,6 +625,9 @@ class CodeGeneratorX86 : public CodeGenerator { // Patches for string root accesses in JIT compiled code. ArenaDeque<PatchInfo<Label>> jit_string_patches_; + // Patches for class root accesses in JIT compiled code. + ArenaDeque<PatchInfo<Label>> jit_class_patches_; + // Offset to the start of the constant area in the assembled code. // Used for fixups to the constant area. int32_t constant_area_start_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 4474decf59..903844fcdb 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -960,19 +960,7 @@ inline Condition X86_64FPCondition(IfCondition cond) { HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86_64::GetSupportedInvokeStaticOrDirectDispatch( const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) { - switch (desired_dispatch_info.code_ptr_location) { - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // For direct code, we actually prefer to call via the code pointer from ArtMethod*. - return HInvokeStaticOrDirect::DispatchInfo { - desired_dispatch_info.method_load_kind, - HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, - desired_dispatch_info.method_load_data, - 0u - }; - default: - return desired_dispatch_info; - } + return desired_dispatch_info; } Location CodeGeneratorX86_64::GenerateCalleeMethodStaticOrDirectCall(HInvokeStaticOrDirect* invoke, @@ -993,12 +981,6 @@ Location CodeGeneratorX86_64::GenerateCalleeMethodStaticOrDirectCall(HInvokeStat case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: __ movq(temp.AsRegister<CpuRegister>(), Immediate(invoke->GetMethodAddress())); break; - case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: - __ movl(temp.AsRegister<CpuRegister>(), Immediate(0)); // Placeholder. - method_patches_.emplace_back(*invoke->GetTargetMethod().dex_file, - invoke->GetTargetMethod().dex_method_index); - __ Bind(&method_patches_.back().label); // Bind the label at the end of the "movl" insn. - break; case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: { __ movq(temp.AsRegister<CpuRegister>(), Address::Absolute(kDummy32BitOffset, /* no_rip */ false)); @@ -1042,19 +1024,6 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf: __ call(&frame_entry_label_); break; - case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative: { - relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file, - invoke->GetTargetMethod().dex_method_index); - Label* label = &relative_call_patches_.back().label; - __ call(label); // Bind to the patch label, override at link time. - __ Bind(label); // Bind the label at the end of the "call" insn. - break; - } - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup: - case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect: - // Filtered out by GetSupportedInvokeStaticOrDirectDispatch(). - LOG(FATAL) << "Unsupported"; - UNREACHABLE(); case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod: // (callee_method + offset_of_quick_compiled_code)() __ call(Address(callee_method.AsRegister<CpuRegister>(), @@ -1146,22 +1115,11 @@ inline void CodeGeneratorX86_64::EmitPcRelativeLinkerPatches( void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) { DCHECK(linker_patches->empty()); size_t size = - method_patches_.size() + - relative_call_patches_.size() + pc_relative_dex_cache_patches_.size() + simple_patches_.size() + string_patches_.size() + type_patches_.size(); linker_patches->reserve(size); - for (const PatchInfo<Label>& info : method_patches_) { - uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; - linker_patches->push_back(LinkerPatch::MethodPatch(literal_offset, &info.dex_file, info.index)); - } - for (const PatchInfo<Label>& info : relative_call_patches_) { - uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; - linker_patches->push_back( - LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index)); - } EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_, linker_patches); for (const Label& label : simple_patches_) { @@ -1253,14 +1211,13 @@ CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, assembler_(graph->GetArena()), isa_features_(isa_features), constant_area_start_(0), - method_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), - jit_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { + jit_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jit_class_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister)); } @@ -5460,8 +5417,7 @@ HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( break; case HLoadClass::LoadKind::kBootImageAddress: break; - case HLoadClass::LoadKind::kDexCacheAddress: - DCHECK(Runtime::Current()->UseJitCompilation()); + case HLoadClass::LoadKind::kJitTableAddress: break; case HLoadClass::LoadKind::kDexCachePcRelative: DCHECK(!Runtime::Current()->UseJitCompilation()); @@ -5500,6 +5456,16 @@ void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) { locations->SetOut(Location::RequiresRegister()); } +Label* CodeGeneratorX86_64::NewJitRootClassPatch(const DexFile& dex_file, + dex::TypeIndex dex_index, + uint64_t address) { + jit_class_roots_.Overwrite(TypeReference(&dex_file, dex_index), address); + // Add a patch entry and return the label. + jit_class_patches_.emplace_back(dex_file, dex_index.index_); + PatchInfo<Label>* info = &jit_class_patches_.back(); + return &info->label; +} + void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { LocationSummary* locations = cls->GetLocations(); if (cls->NeedsAccessCheck()) { @@ -5543,26 +5509,13 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { codegen_->RecordSimplePatch(); break; } - case HLoadClass::LoadKind::kDexCacheAddress: { - DCHECK_NE(cls->GetAddress(), 0u); + case HLoadClass::LoadKind::kJitTableAddress: { + Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, + /* no_rip */ true); + Label* fixup_label = + codegen_->NewJitRootClassPatch(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetAddress()); // /* GcRoot<mirror::Class> */ out = *address - if (IsUint<32>(cls->GetAddress())) { - Address address = Address::Absolute(cls->GetAddress(), /* no_rip */ true); - GenerateGcRootFieldLoad(cls, - out_loc, - address, - /* fixup_label */ nullptr, - read_barrier_option); - } else { - // TODO: Consider using opcode A1, i.e. movl eax, moff32 (with 64-bit address). - __ movq(out, Immediate(cls->GetAddress())); - GenerateGcRootFieldLoad(cls, - out_loc, - Address(out, 0), - /* fixup_label */ nullptr, - read_barrier_option); - } - generate_null_check = !cls->IsInDexCache(); + GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption); break; } case HLoadClass::LoadKind::kDexCachePcRelative: { @@ -7127,18 +7080,31 @@ void CodeGeneratorX86_64::MoveInt64ToAddress(const Address& addr_low, } } +void CodeGeneratorX86_64::PatchJitRootUse(uint8_t* code, + const uint8_t* roots_data, + const PatchInfo<Label>& info, + uint64_t index_in_table) const { + uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; + uintptr_t address = + reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); + typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; + reinterpret_cast<unaligned_uint32_t*>(code + code_offset)[0] = + dchecked_integral_cast<uint32_t>(address); +} + void CodeGeneratorX86_64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) { for (const PatchInfo<Label>& info : jit_string_patches_) { - const auto& it = jit_string_roots_.find(StringReference(&info.dex_file, - dex::StringIndex(info.index))); + const auto& it = jit_string_roots_.find( + StringReference(&info.dex_file, dex::StringIndex(info.index))); DCHECK(it != jit_string_roots_.end()); - size_t index_in_table = it->second; - uint32_t code_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; - uintptr_t address = - reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>); - typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; - reinterpret_cast<unaligned_uint32_t*>(code + code_offset)[0] = - dchecked_integral_cast<uint32_t>(address); + PatchJitRootUse(code, roots_data, info, it->second); + } + + for (const PatchInfo<Label>& info : jit_class_patches_) { + const auto& it = jit_class_roots_.find( + TypeReference(&info.dex_file, dex::TypeIndex(info.index))); + DCHECK(it != jit_class_roots_.end()); + PatchJitRootUse(code, roots_data, info, it->second); } } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 2f41f73da6..f827e79a94 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -413,11 +413,17 @@ class CodeGeneratorX86_64 : public CodeGenerator { Label* NewStringBssEntryPatch(HLoadString* load_string); Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); Label* NewJitRootStringPatch(const DexFile& dex_file, dex::StringIndex dex_index); + Label* NewJitRootClassPatch(const DexFile& dex_file, dex::TypeIndex dex_index, uint64_t address); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; + void PatchJitRootUse(uint8_t* code, + const uint8_t* roots_data, + const PatchInfo<Label>& info, + uint64_t index_in_table) const; + void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) OVERRIDE; const X86_64InstructionSetFeatures& GetInstructionSetFeatures() const { @@ -590,9 +596,6 @@ class CodeGeneratorX86_64 : public CodeGenerator { // Used for fixups to the constant area. int constant_area_start_; - // Method patch info. Using ArenaDeque<> which retains element addresses on push/emplace_back(). - ArenaDeque<PatchInfo<Label>> method_patches_; - ArenaDeque<PatchInfo<Label>> relative_call_patches_; // PC-relative DexCache access info. ArenaDeque<PatchInfo<Label>> pc_relative_dex_cache_patches_; // Patch locations for patchoat where the linker doesn't do any other work. @@ -608,6 +611,9 @@ class CodeGeneratorX86_64 : public CodeGenerator { // Patches for string literals in JIT compiled code. ArenaDeque<PatchInfo<Label>> jit_string_patches_; + // Patches for class literals in JIT compiled code. + ArenaDeque<PatchInfo<Label>> jit_class_patches_; + DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86_64); }; diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h index eabdbad13c..21c3ae628a 100644 --- a/compiler/optimizing/common_arm.h +++ b/compiler/optimizing/common_arm.h @@ -122,10 +122,16 @@ inline vixl::aarch32::VRegister InputVRegisterAt(HInstruction* instr, int input_ if (type == Primitive::kPrimFloat) { return InputSRegisterAt(instr, input_index); } else { + DCHECK_EQ(type, Primitive::kPrimDouble); return InputDRegisterAt(instr, input_index); } } +inline vixl::aarch32::VRegister InputVRegister(HInstruction* instr) { + DCHECK_EQ(instr->InputCount(), 1u); + return InputVRegisterAt(instr, 0); +} + inline vixl::aarch32::Register OutputRegister(HInstruction* instr) { return RegisterFrom(instr->GetLocations()->Out(), instr->GetType()); } @@ -140,8 +146,7 @@ inline vixl::aarch32::Register InputRegister(HInstruction* instr) { return InputRegisterAt(instr, 0); } -inline int32_t Int32ConstantFrom(Location location) { - HConstant* instr = location.GetConstant(); +inline int32_t Int32ConstantFrom(HInstruction* instr) { if (instr->IsIntConstant()) { return instr->AsIntConstant()->GetValue(); } else if (instr->IsNullConstant()) { @@ -155,6 +160,10 @@ inline int32_t Int32ConstantFrom(Location location) { } } +inline int32_t Int32ConstantFrom(Location location) { + return Int32ConstantFrom(location.GetConstant()); +} + inline int64_t Int64ConstantFrom(Location location) { HConstant* instr = location.GetConstant(); if (instr->IsIntConstant()) { @@ -167,6 +176,11 @@ inline int64_t Int64ConstantFrom(Location location) { } } +inline uint64_t Uint64ConstantFrom(HInstruction* instr) { + DCHECK(instr->IsConstant()) << instr->DebugName(); + return instr->AsConstant()->GetValueAsUint64(); +} + inline vixl::aarch32::Operand OperandFrom(Location location, Primitive::Type type) { if (location.IsRegister()) { return vixl::aarch32::Operand(RegisterFrom(location, type)); diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 188ee3a8d1..34b52a87b5 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -20,12 +20,15 @@ #include <string> #include <sstream> +#include "android-base/stringprintf.h" + #include "base/arena_containers.h" #include "base/bit_vector-inl.h" -#include "base/stringprintf.h" namespace art { +using android::base::StringPrintf; + static bool IsAllowedToJumpToExitBlock(HInstruction* instruction) { return instruction->IsThrow() || instruction->IsReturn() || instruction->IsReturnVoid(); } diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc index d5305646a8..28ee3a5e8b 100644 --- a/compiler/optimizing/graph_test.cc +++ b/compiler/optimizing/graph_test.cc @@ -15,7 +15,6 @@ */ #include "base/arena_allocator.h" -#include "base/stringprintf.h" #include "builder.h" #include "nodes.h" #include "optimizing_unit_test.h" diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index c240c67e79..88473f02e5 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -73,10 +73,18 @@ static bool IsNarrowingIntegralConversion(Primitive::Type from, Primitive::Type } /** - * Returns narrowest data type. + * Returns result of implicit widening type conversion done in HIR. */ -static Primitive::Type Narrowest(Primitive::Type type1, Primitive::Type type2) { - return Primitive::ComponentSize(type1) <= Primitive::ComponentSize(type2) ? type1 : type2; +static Primitive::Type ImplicitConversion(Primitive::Type type) { + switch (type) { + case Primitive::kPrimShort: + case Primitive::kPrimChar: + case Primitive::kPrimByte: + case Primitive::kPrimBoolean: + return Primitive::kPrimInt; + default: + return type; + } } // @@ -211,7 +219,7 @@ uint32_t HInductionVarAnalysis::VisitDescendant(HLoopInformation* loop, HInstruc void HInductionVarAnalysis::ClassifyTrivial(HLoopInformation* loop, HInstruction* instruction) { InductionInfo* info = nullptr; if (instruction->IsPhi()) { - info = TransferPhi(loop, instruction, /* input_index */ 0); + info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 0); } else if (instruction->IsAdd()) { info = TransferAddSub(LookupInfo(loop, instruction->InputAt(0)), LookupInfo(loop, instruction->InputAt(1)), kAdd); @@ -224,15 +232,17 @@ void HInductionVarAnalysis::ClassifyTrivial(HLoopInformation* loop, HInstruction info = TransferMul(LookupInfo(loop, instruction->InputAt(0)), LookupInfo(loop, instruction->InputAt(1))); } else if (instruction->IsShl()) { - HInstruction* mulc = GetMultConstantForShift(loop, instruction); + HInstruction* mulc = GetShiftConstant(loop, instruction, /*initial*/ nullptr); if (mulc != nullptr) { info = TransferMul(LookupInfo(loop, instruction->InputAt(0)), LookupInfo(loop, mulc)); } + } else if (instruction->IsSelect()) { + info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 1); } else if (instruction->IsTypeConversion()) { - info = TransferCnv(LookupInfo(loop, instruction->InputAt(0)), - instruction->AsTypeConversion()->GetInputType(), - instruction->AsTypeConversion()->GetResultType()); + info = TransferConversion(LookupInfo(loop, instruction->InputAt(0)), + instruction->AsTypeConversion()->GetInputType(), + instruction->AsTypeConversion()->GetResultType()); } else if (instruction->IsBoundsCheck()) { info = LookupInfo(loop, instruction->InputAt(0)); // Pass-through. } @@ -265,12 +275,16 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) { return; } - // Store interesting cycle. - AssignCycle(phi->AsPhi()); + // Store interesting cycle in each loop phi. + for (size_t i = 0; i < size; i++) { + if (scc_[i]->IsLoopHeaderPhi()) { + AssignCycle(scc_[i]->AsPhi()); + } + } // Singleton is wrap-around induction if all internal links have the same meaning. if (size == 1) { - InductionInfo* update = TransferPhi(loop, phi, /* input_index */ 1); + InductionInfo* update = TransferPhi(loop, phi, /*input_index*/ 1, /*adjust_input_size*/ 0); if (update != nullptr) { AssignInfo(loop, phi, CreateInduction(kWrapAround, kNop, @@ -305,10 +319,15 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) { update = SolveOp( loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kRem); } else if (instruction->IsShl()) { - HInstruction* mulc = GetMultConstantForShift(loop, instruction); + HInstruction* mulc = GetShiftConstant(loop, instruction, /*initial*/ nullptr); if (mulc != nullptr) { update = SolveOp(loop, phi, instruction, instruction->InputAt(0), mulc, kMul); } + } else if (instruction->IsShr() || instruction->IsUShr()) { + HInstruction* divc = GetShiftConstant(loop, instruction, initial); + if (divc != nullptr) { + update = SolveOp(loop, phi, instruction, instruction->InputAt(0), divc, kDiv); + } } else if (instruction->IsXor()) { update = SolveOp( loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kXor); @@ -316,8 +335,10 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) { update = SolveTest(loop, phi, instruction, 0); } else if (instruction->IsNotEqual()) { update = SolveTest(loop, phi, instruction, 1); + } else if (instruction->IsSelect()) { + update = SolvePhi(instruction, /*input_index*/ 0, /*adjust_input_size*/ 1); // acts like Phi } else if (instruction->IsTypeConversion()) { - update = SolveCnv(instruction->AsTypeConversion()); + update = SolveConversion(loop, phi, instruction->AsTypeConversion()); } if (update == nullptr) { return; @@ -326,7 +347,7 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) { } // Success if all internal links received the same temporary meaning. - InductionInfo* induction = SolvePhi(phi, /* input_index */ 1); + InductionInfo* induction = SolvePhi(phi, /*input_index*/ 1, /*adjust_input_size*/ 0); if (induction != nullptr) { switch (induction->induction_class) { case kInvariant: @@ -385,12 +406,13 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::RotatePeriodicInduc HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(HLoopInformation* loop, HInstruction* phi, - size_t input_index) { + size_t input_index, + size_t adjust_input_size) { // Match all phi inputs from input_index onwards exactly. HInputsRef inputs = phi->GetInputs(); DCHECK_LT(input_index, inputs.size()); InductionInfo* a = LookupInfo(loop, inputs[input_index]); - for (size_t i = input_index + 1; i < inputs.size(); i++) { + for (size_t i = input_index + 1, n = inputs.size() - adjust_input_size; i < n; i++) { InductionInfo* b = LookupInfo(loop, inputs[i]); if (!InductionEqual(a, b)) { return nullptr; @@ -406,18 +428,20 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(Indu // wrap-around, or periodic can be combined with an invariant to yield a similar result. // Two linear or two polynomial inputs can be combined too. Other combinations fail. if (a != nullptr && b != nullptr) { - type_ = Narrowest(type_, Narrowest(a->type, b->type)); - if (a->induction_class == kInvariant && b->induction_class == kInvariant) { - return CreateInvariantOp(op, a, b); + if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) { + return nullptr; // no transfer + } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) { + return CreateInvariantOp(op, a, b); // direct invariant } else if ((a->induction_class == kLinear && b->induction_class == kLinear) || (a->induction_class == kPolynomial && b->induction_class == kPolynomial)) { - return CreateInduction(a->induction_class, - a->operation, - TransferAddSub(a->op_a, b->op_a, op), - TransferAddSub(a->op_b, b->op_b, op), - /*fetch*/ nullptr, - type_); + // Rule induc(a, b) + induc(a', b') -> induc(a + a', b + b'). + InductionInfo* new_a = TransferAddSub(a->op_a, b->op_a, op); + InductionInfo* new_b = TransferAddSub(a->op_b, b->op_b, op); + if (new_a != nullptr && new_b != nullptr) { + return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_); + } } else if (a->induction_class == kInvariant) { + // Rule a + induc(a', b') -> induc(a', a + b') or induc(a + a', a + b'). InductionInfo* new_a = b->op_a; InductionInfo* new_b = TransferAddSub(a, b->op_b, op); if (b->induction_class == kWrapAround || b->induction_class == kPeriodic) { @@ -425,14 +449,19 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(Indu } else if (op == kSub) { // Negation required. new_a = TransferNeg(new_a); } - return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type_); + if (new_a != nullptr && new_b != nullptr) { + return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type_); + } } else if (b->induction_class == kInvariant) { + // Rule induc(a, b) + b' -> induc(a, b + b') or induc(a + b', b + b'). InductionInfo* new_a = a->op_a; InductionInfo* new_b = TransferAddSub(a->op_b, b, op); if (a->induction_class == kWrapAround || a->induction_class == kPeriodic) { new_a = TransferAddSub(new_a, b, op); } - return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_); + if (new_a != nullptr && new_b != nullptr) { + return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_); + } } } return nullptr; @@ -442,16 +471,17 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(Inducti // Transfer over a unary negation: an invariant, linear, polynomial, geometric (mul), // wrap-around, or periodic input yields a similar but negated induction as result. if (a != nullptr) { - type_ = Narrowest(type_, a->type); - if (a->induction_class == kInvariant) { - return CreateInvariantOp(kNeg, nullptr, a); + if (IsNarrowingLinear(a)) { + return nullptr; // no transfer + } else if (a->induction_class == kInvariant) { + return CreateInvariantOp(kNeg, nullptr, a); // direct invariant } else if (a->induction_class != kGeometric || a->operation == kMul) { - return CreateInduction(a->induction_class, - a->operation, - TransferNeg(a->op_a), - TransferNeg(a->op_b), - a->fetch, - type_); + // Rule - induc(a, b) -> induc(-a, -b). + InductionInfo* new_a = TransferNeg(a->op_a); + InductionInfo* new_b = TransferNeg(a->op_b); + if (new_a != nullptr && new_b != nullptr) { + return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_); + } } } return nullptr; @@ -463,54 +493,56 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(Inducti // wrap-around, or periodic can be multiplied with an invariant to yield a similar // but multiplied result. Two non-invariant inputs cannot be multiplied, however. if (a != nullptr && b != nullptr) { - type_ = Narrowest(type_, Narrowest(a->type, b->type)); - if (a->induction_class == kInvariant && b->induction_class == kInvariant) { - return CreateInvariantOp(kMul, a, b); + if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) { + return nullptr; // no transfer + } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) { + return CreateInvariantOp(kMul, a, b); // direct invariant } else if (a->induction_class == kInvariant && (b->induction_class != kGeometric || b->operation == kMul)) { - return CreateInduction(b->induction_class, - b->operation, - TransferMul(a, b->op_a), - TransferMul(a, b->op_b), - b->fetch, - type_); + // Rule a * induc(a', b') -> induc(a * a', b * b'). + InductionInfo* new_a = TransferMul(a, b->op_a); + InductionInfo* new_b = TransferMul(a, b->op_b); + if (new_a != nullptr && new_b != nullptr) { + return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type_); + } } else if (b->induction_class == kInvariant && (a->induction_class != kGeometric || a->operation == kMul)) { - return CreateInduction(a->induction_class, - a->operation, - TransferMul(a->op_a, b), - TransferMul(a->op_b, b), - a->fetch, - type_); + // Rule induc(a, b) * b' -> induc(a * b', b * b'). + InductionInfo* new_a = TransferMul(a->op_a, b); + InductionInfo* new_b = TransferMul(a->op_b, b); + if (new_a != nullptr && new_b != nullptr) { + return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_); + } } } return nullptr; } -HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCnv(InductionInfo* a, - Primitive::Type from, - Primitive::Type to) { +HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferConversion( + InductionInfo* a, + Primitive::Type from, + Primitive::Type to) { if (a != nullptr) { - // Allow narrowing conversion on linear induction in certain cases. - if (IsNarrowingIntegralConversion(from, to)) { - if (a->induction_class == kLinear) { - if (a->type == to || (a->type == from && IsNarrowingIntegralConversion(from, to))) { - return CreateInduction(kLinear, kNop, a->op_a, a->op_b, /*fetch*/ nullptr, to); - } - } + // Allow narrowing conversion on linear induction in certain cases: + // induction is already at narrow type, or can be made narrower. + if (IsNarrowingIntegralConversion(from, to) && + a->induction_class == kLinear && + (a->type == to || IsNarrowingIntegralConversion(a->type, to))) { + return CreateInduction(kLinear, kNop, a->op_a, a->op_b, a->fetch, to); } } return nullptr; } HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhi(HInstruction* phi, - size_t input_index) { + size_t input_index, + size_t adjust_input_size) { // Match all phi inputs from input_index onwards exactly. HInputsRef inputs = phi->GetInputs(); DCHECK_LT(input_index, inputs.size()); auto ita = cycle_.find(inputs[input_index]); if (ita != cycle_.end()) { - for (size_t i = input_index + 1; i < inputs.size(); i++) { + for (size_t i = input_index + 1, n = inputs.size() - adjust_input_size; i < n; i++) { auto itb = cycle_.find(inputs[i]); if (itb == cycle_.end() || !HInductionVarAnalysis::InductionEqual(ita->second, itb->second)) { @@ -527,7 +559,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhiAllInputs( HInstruction* entry_phi, HInstruction* phi) { // Match all phi inputs. - InductionInfo* match = SolvePhi(phi, /* input_index */ 0); + InductionInfo* match = SolvePhi(phi, /*input_index*/ 0, /*adjust_input_size*/ 0); if (match != nullptr) { return match; } @@ -542,7 +574,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhiAllInputs( InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0)); return CreateInduction(kPeriodic, kNop, a, initial, /*fetch*/ nullptr, type_); } - InductionInfo* b = SolvePhi(phi, /* input_index */ 1); + InductionInfo* b = SolvePhi(phi, /*input_index*/ 1, /*adjust_input_size*/ 0); if (b != nullptr && b->induction_class == kPeriodic) { return CreateInduction(kPeriodic, kNop, a, b, /*fetch*/ nullptr, type_); } @@ -574,17 +606,15 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(HLoopIn return CreateInvariantOp(op, a, b); } } - } else if (op == kAdd && b->induction_class == kLinear) { + } else if (b->induction_class == kLinear && b->type == type_) { // Solve within a tight cycle that adds a term that is already classified as a linear // induction for a polynomial induction k = k + i (represented as sum over linear terms). if (x == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) { InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0)); - return CreateInduction(kPolynomial, - kNop, - b, - initial, - /*fetch*/ nullptr, - type_); + InductionInfo* new_a = op == kAdd ? b : TransferNeg(b); + if (new_a != nullptr) { + return CreateInduction(kPolynomial, kNop, new_a, initial, /*fetch*/ nullptr, type_); + } } } } @@ -689,16 +719,29 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveTest(HLoopInfo return nullptr; } -HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveCnv(HTypeConversion* conversion) { +HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveConversion( + HLoopInformation* loop, + HInstruction* entry_phi, + HTypeConversion* conversion) { Primitive::Type from = conversion->GetInputType(); Primitive::Type to = conversion->GetResultType(); - // A narrowing conversion is allowed within the cycle of a linear induction, provided that the - // narrowest encountered type is recorded with the induction to account for the precision loss. - if (IsNarrowingIntegralConversion(from, to)) { - auto it = cycle_.find(conversion->GetInput()); - if (it != cycle_.end() && it->second->induction_class == kInvariant) { - type_ = Narrowest(type_, to); - return it->second; + // A narrowing conversion is allowed as *last* operation of the cycle of a linear induction + // with an initial value that fits the type, provided that the narrowest encountered type is + // recorded with the induction to account for the precision loss. The narrower induction does + // *not* transfer to any wider operations, however, since these may yield out-of-type values + if (entry_phi->InputCount() == 2 && conversion == entry_phi->InputAt(1)) { + int64_t min = Primitive::MinValueOfIntegralType(to); + int64_t max = Primitive::MaxValueOfIntegralType(to); + int64_t value = 0; + InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0)); + if (IsNarrowingIntegralConversion(from, to) && + IsAtLeast(initial, &value) && value >= min && + IsAtMost(initial, &value) && value <= max) { + auto it = cycle_.find(conversion->GetInput()); + if (it != cycle_.end() && it->second->induction_class == kInvariant) { + type_ = to; + return it->second; + } } } return nullptr; @@ -718,7 +761,7 @@ void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) { HCondition* condition = if_expr->AsCondition(); InductionInfo* a = LookupInfo(loop, condition->InputAt(0)); InductionInfo* b = LookupInfo(loop, condition->InputAt(1)); - Primitive::Type type = condition->InputAt(0)->GetType(); + Primitive::Type type = ImplicitConversion(condition->InputAt(0)->GetType()); // Determine if the loop control uses a known sequence on an if-exit (X outside) or on // an if-iterate (X inside), expressed as if-iterate when passed into VisitCondition(). if (a == nullptr || b == nullptr) { @@ -890,8 +933,8 @@ bool HInductionVarAnalysis::IsFinite(InductionInfo* upper_expr, int64_t stride_value, Primitive::Type type, IfCondition cmp) { - const int64_t min = Primitive::MinValueOfIntegralType(type); - const int64_t max = Primitive::MaxValueOfIntegralType(type); + int64_t min = Primitive::MinValueOfIntegralType(type); + int64_t max = Primitive::MaxValueOfIntegralType(type); // Some rules under which it is certain at compile-time that the loop is finite. int64_t value; switch (cmp) { @@ -927,8 +970,6 @@ bool HInductionVarAnalysis::FitsNarrowerControl(InductionInfo* lower_expr, min++; } // Do both bounds fit the range? - // Note: The `value` is initialized to please valgrind - the compiler can reorder - // the return value check with the `value` check, b/27651442 . int64_t value = 0; return IsAtLeast(lower_expr, &value) && value >= min && IsAtMost(lower_expr, &value) && value <= max && @@ -1035,16 +1076,27 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv return CreateSimplifiedInvariant(kSub, b->op_b, b->op_a); } } - return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, b->type); + return new (graph_->GetArena()) InductionInfo( + kInvariant, op, a, b, nullptr, ImplicitConversion(b->type)); } -HInstruction* HInductionVarAnalysis::GetMultConstantForShift(HLoopInformation* loop, - HInstruction* instruction) { - // Obtain the constant needed to treat shift as equivalent multiplication. This yields an - // existing instruction if the constant is already there. Otherwise, this has a side effect - // on the HIR. The restriction on the shift factor avoids generating a negative constant - // (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that generalization for - // shift factors outside [0,32) and [0,64) ranges is done by earlier simplification. +HInstruction* HInductionVarAnalysis::GetShiftConstant(HLoopInformation* loop, + HInstruction* instruction, + InductionInfo* initial) { + DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr()); + // Shift-rights are only the same as division for non-negative initial inputs. + // Otherwise we would round incorrectly. + if (initial != nullptr) { + int64_t value = -1; + if (!IsAtLeast(initial, &value) || value < 0) { + return nullptr; + } + } + // Obtain the constant needed to treat shift as equivalent multiplication or division. + // This yields an existing instruction if the constant is already there. Otherwise, this + // has a side effect on the HIR. The restriction on the shift factor avoids generating a + // negative constant (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that + // generalization for shift factors outside [0,32) and [0,64) ranges is done earlier. InductionInfo* b = LookupInfo(loop, instruction->InputAt(1)); int64_t value = -1; if (IsExact(b, &value)) { @@ -1087,6 +1139,16 @@ bool HInductionVarAnalysis::IsAtLeast(InductionInfo* info, int64_t* value) { return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtLeast, value); } +bool HInductionVarAnalysis::IsNarrowingLinear(InductionInfo* info) { + return info != nullptr && + info->induction_class == kLinear && + (info->type == Primitive::kPrimByte || + info->type == Primitive::kPrimShort || + info->type == Primitive::kPrimChar || + (info->type == Primitive::kPrimInt && (info->op_a->type == Primitive::kPrimLong || + info->op_b->type == Primitive::kPrimLong))); +} + bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1, InductionInfo* info2) { // Test structural equality only, without accounting for simplifications. diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index 4720f2d61c..39b39cdf55 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -115,7 +115,7 @@ class HInductionVarAnalysis : public HOptimization { InductionInfo* op_a; InductionInfo* op_b; HInstruction* fetch; - Primitive::Type type; // precision of induction + Primitive::Type type; // precision of operation }; bool IsVisitedNode(HInstruction* instruction) const { @@ -160,14 +160,17 @@ class HInductionVarAnalysis : public HOptimization { InductionInfo* RotatePeriodicInduction(InductionInfo* induction, InductionInfo* last); // Transfer operations. - InductionInfo* TransferPhi(HLoopInformation* loop, HInstruction* phi, size_t input_index); + InductionInfo* TransferPhi(HLoopInformation* loop, + HInstruction* phi, + size_t input_index, + size_t adjust_input_size); InductionInfo* TransferAddSub(InductionInfo* a, InductionInfo* b, InductionOp op); InductionInfo* TransferNeg(InductionInfo* a); InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b); - InductionInfo* TransferCnv(InductionInfo* a, Primitive::Type from, Primitive::Type to); + InductionInfo* TransferConversion(InductionInfo* a, Primitive::Type from, Primitive::Type to); // Solvers. - InductionInfo* SolvePhi(HInstruction* phi, size_t input_index); + InductionInfo* SolvePhi(HInstruction* phi, size_t input_index, size_t adjust_input_size); InductionInfo* SolvePhiAllInputs(HLoopInformation* loop, HInstruction* entry_phi, HInstruction* phi); @@ -188,7 +191,9 @@ class HInductionVarAnalysis : public HOptimization { HInstruction* entry_phi, HInstruction* instruction, int64_t oppositive_value); - InductionInfo* SolveCnv(HTypeConversion* conversion); + InductionInfo* SolveConversion(HLoopInformation* loop, + HInstruction* entry_phi, + HTypeConversion* conversion); // Trip count information. void VisitControl(HLoopInformation* loop); @@ -220,7 +225,9 @@ class HInductionVarAnalysis : public HOptimization { InductionInfo* LookupInfo(HLoopInformation* loop, HInstruction* instruction); InductionInfo* CreateConstant(int64_t value, Primitive::Type type); InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b); - HInstruction* GetMultConstantForShift(HLoopInformation* loop, HInstruction* instruction); + HInstruction* GetShiftConstant(HLoopInformation* loop, + HInstruction* instruction, + InductionInfo* initial); void AssignCycle(HPhi* phi); ArenaSet<HInstruction*>* LookupCycle(HPhi* phi); @@ -230,6 +237,7 @@ class HInductionVarAnalysis : public HOptimization { bool IsAtLeast(InductionInfo* info, /*out*/ int64_t* value); // Helpers. + static bool IsNarrowingLinear(InductionInfo* info); static bool InductionEqual(InductionInfo* info1, InductionInfo* info2); static std::string FetchToString(HInstruction* fetch); static std::string InductionToString(InductionInfo* info); diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index 2d182f6483..82ee93d5c2 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -87,6 +87,7 @@ class InductionVarAnalysisTest : public CommonCompilerTest { constant2_ = graph_->GetIntConstant(2); constant7_ = graph_->GetIntConstant(7); constant100_ = graph_->GetIntConstant(100); + constantm1_ = graph_->GetIntConstant(-1); float_constant0_ = graph_->GetFloatConstant(0.0f); return_->AddInstruction(new (&allocator_) HReturnVoid()); exit_->AddInstruction(new (&allocator_) HExit()); @@ -173,6 +174,12 @@ class InductionVarAnalysisTest : public CommonCompilerTest { iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2)); } + // Returns true for narrowing linear induction. + bool IsNarrowingLinear(HInstruction* instruction) { + return HInductionVarAnalysis::IsNarrowingLinear( + iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction)); + } + // Performs InductionVarAnalysis (after proper set up). void PerformInductionVarAnalysis() { graph_->BuildDominatorTree(); @@ -196,6 +203,7 @@ class InductionVarAnalysisTest : public CommonCompilerTest { HInstruction* constant2_; HInstruction* constant7_; HInstruction* constant100_; + HInstruction* constantm1_; HInstruction* float_constant0_; // Loop specifics. @@ -612,6 +620,45 @@ TEST_F(InductionVarAnalysisTest, FindGeometricDivInductionAndDerived) { EXPECT_STREQ("", GetInductionInfo(div, 0).c_str()); } +TEST_F(InductionVarAnalysisTest, FindGeometricShrInduction) { + // Setup: + // k = 100; + // for (int i = 0; i < 100; i++) { + // k = k >> 1; // geometric (/ 2) + // } + BuildLoopNest(1); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(constant100_); + + HInstruction* shr = InsertInstruction( + new (&allocator_) HShr(Primitive::kPrimInt, k_header, constant1_), 0); + k_header->AddInput(shr); + PerformInductionVarAnalysis(); + + // Note, only the phi in the cycle is classified. + EXPECT_STREQ("geo((100) * 2 ^ -i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(shr, 0).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindNotGeometricShrInduction) { + // Setup: + // k = -1; + // for (int i = 0; i < 100; i++) { + // k = k >> 1; // initial value is negative + // } + BuildLoopNest(1); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(constantm1_); + + HInstruction* shr = InsertInstruction( + new (&allocator_) HShr(Primitive::kPrimInt, k_header, constant1_), 0); + k_header->AddInput(shr); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(shr, 0).c_str()); +} + TEST_F(InductionVarAnalysisTest, FindRemWrapAroundInductionAndDerived) { // Setup: // k = 100; @@ -1025,16 +1072,20 @@ TEST_F(InductionVarAnalysisTest, ByteInductionIntLoopControl) { // } BuildLoopNest(1); HInstruction* conv = InsertInstruction( - new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0); + new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0); HInstruction* store1 = InsertArrayStore(conv, 0); HInstruction* store2 = InsertArrayStore(basic_[0], 0); PerformInductionVarAnalysis(); - // Regular int induction (i) is "transferred" over conversion into byte induction (k). + // Regular int induction (i) is transferred over conversion into byte induction (k). EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str()); EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str()); EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str()); + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1))); + EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1))); + // Type matters! EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1))); @@ -1052,7 +1103,7 @@ TEST_F(InductionVarAnalysisTest, ByteInductionDerivedIntLoopControl) { // } BuildLoopNest(1); HInstruction* conv = InsertInstruction( - new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0); + new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0); HInstruction* store1 = InsertArrayStore(conv, 0); HInstruction* add = InsertInstruction( new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0); @@ -1060,11 +1111,86 @@ TEST_F(InductionVarAnalysisTest, ByteInductionDerivedIntLoopControl) { PerformInductionVarAnalysis(); - // Byte induction (k) is "transferred" over conversion into addition (k + 1). - // This means only values within byte range can be trusted (even though - // addition can jump out of the range of course). + // Byte induction (k) is detected, but it does not transfer over the addition, + // since this may yield out-of-type values. EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str()); - EXPECT_STREQ("((1) * i + (1)):PrimByte", GetInductionInfo(store2->InputAt(1), 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(store2->InputAt(1), 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1))); + EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1))); // works for null +} + +TEST_F(InductionVarAnalysisTest, ByteInduction) { + // Setup: + // k = -128; + // for (int i = 0; i < 100; i++) { + // k = k + 1; + // k = (byte) k; + // } + BuildLoopNest(1); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(graph_->GetIntConstant(-128)); + + HInstruction* add = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0); + HInstruction* conv = InsertInstruction( + new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0); + k_header->AddInput(conv); + PerformInductionVarAnalysis(); + + // Byte induction (k) is detected, but it does not transfer over the addition, + // since this may yield out-of-type values. + EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(k_header, 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(add, 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(k_header)); + EXPECT_FALSE(IsNarrowingLinear(add)); // works for null +} + +TEST_F(InductionVarAnalysisTest, NoByteInduction1) { + // Setup: + // k = -129; / does not fit! + // for (int i = 0; i < 100; i++) { + // k = k + 1; + // k = (byte) k; + // } + BuildLoopNest(1); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(graph_->GetIntConstant(-129)); + + HInstruction* add = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0); + HInstruction* conv = InsertInstruction( + new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0); + k_header->AddInput(conv); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(add, 0).c_str()); +} + +TEST_F(InductionVarAnalysisTest, NoByteInduction2) { + // Setup: + // k = 0; + // for (int i = 0; i < 100; i++) { + // k = (byte) k; // conversion not done last! + // k = k + 1; + // } + BuildLoopNest(1); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(constant0_); + + HInstruction* conv = InsertInstruction( + new (&allocator_) HTypeConversion(Primitive::kPrimByte, k_header, kNoDexPc), 0); + HInstruction* add = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0); + k_header->AddInput(add); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(add, 0).c_str()); } TEST_F(InductionVarAnalysisTest, ByteLoopControl1) { @@ -1075,12 +1201,20 @@ TEST_F(InductionVarAnalysisTest, ByteLoopControl1) { basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0); HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); ifs->ReplaceInput(graph_->GetIntConstant(127), 1); - HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1); + HInstruction* conv = + new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc); loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); basic_[0]->ReplaceInput(conv, 1); PerformInductionVarAnalysis(); - EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str()); + // Recorded at the phi, but not transferred to increment. + EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(basic_[0])); + EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null + // Trip-count. EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))", GetTripCount(0).c_str()); } @@ -1093,12 +1227,20 @@ TEST_F(InductionVarAnalysisTest, ByteLoopControl2) { basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0); HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); ifs->ReplaceInput(graph_->GetIntConstant(128), 1); - HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1); + HInstruction* conv = + new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc); loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); basic_[0]->ReplaceInput(conv, 1); PerformInductionVarAnalysis(); - EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str()); + // Recorded at the phi, but not transferred to increment. + EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(basic_[0])); + EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null + // Trip-count undefined. EXPECT_STREQ("", GetTripCount(0).c_str()); } @@ -1111,13 +1253,20 @@ TEST_F(InductionVarAnalysisTest, ShortLoopControl1) { basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0); HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); ifs->ReplaceInput(graph_->GetIntConstant(32767), 1); - HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1); + HInstruction* conv = + new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc); loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); basic_[0]->ReplaceInput(conv, 1); PerformInductionVarAnalysis(); - EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort", - GetInductionInfo(increment_[0], 0).c_str()); + // Recorded at the phi, but not transferred to increment. + EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(basic_[0])); + EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null + // Trip-count. EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))", GetTripCount(0).c_str()); } @@ -1130,13 +1279,20 @@ TEST_F(InductionVarAnalysisTest, ShortLoopControl2) { basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0); HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); ifs->ReplaceInput(graph_->GetIntConstant(32768), 1); - HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1); + HInstruction* conv = + new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc); loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); basic_[0]->ReplaceInput(conv, 1); PerformInductionVarAnalysis(); - EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort", - GetInductionInfo(increment_[0], 0).c_str()); + // Recorded at the phi, but not transferred to increment. + EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(basic_[0])); + EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null + // Trip-count undefined. EXPECT_STREQ("", GetTripCount(0).c_str()); } @@ -1148,12 +1304,20 @@ TEST_F(InductionVarAnalysisTest, CharLoopControl1) { BuildLoopNest(1); HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); ifs->ReplaceInput(graph_->GetIntConstant(65535), 1); - HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1); + HInstruction* conv = + new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc); loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); basic_[0]->ReplaceInput(conv, 1); PerformInductionVarAnalysis(); - EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str()); + // Recorded at the phi, but not transferred to increment. + EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(basic_[0])); + EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null + // Trip-count. EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))", GetTripCount(0).c_str()); } @@ -1165,12 +1329,20 @@ TEST_F(InductionVarAnalysisTest, CharLoopControl2) { BuildLoopNest(1); HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); ifs->ReplaceInput(graph_->GetIntConstant(65536), 1); - HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1); + HInstruction* conv = + new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc); loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); basic_[0]->ReplaceInput(conv, 1); PerformInductionVarAnalysis(); - EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str()); + // Recorded at the phi, but not transferred to increment. + EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str()); + + // Narrowing detected. + EXPECT_TRUE(IsNarrowingLinear(basic_[0])); + EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null + // Trip-count undefined. EXPECT_STREQ("", GetTripCount(0).c_str()); } diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index e665551012..d5c4c2fa69 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -169,8 +169,8 @@ static InductionVarRange::Value CorrectForType(InductionVarRange::Value v, Primi case Primitive::kPrimByte: { // Constants within range only. // TODO: maybe some room for improvement, like allowing widening conversions - const int32_t min = Primitive::MinValueOfIntegralType(type); - const int32_t max = Primitive::MaxValueOfIntegralType(type); + int32_t min = Primitive::MinValueOfIntegralType(type); + int32_t max = Primitive::MaxValueOfIntegralType(type); return (IsConstantValue(v) && min <= v.b_constant && v.b_constant <= max) ? v : InductionVarRange::Value(); @@ -551,7 +551,7 @@ InductionVarRange::Value InductionVarRange::GetPolynomial(HInductionVarAnalysis: int64_t b = 0; if (IsConstant(info->op_a->op_a, kExact, &a) && CanLongValueFitIntoInt(a) && a >= 0 && IsConstant(info->op_a->op_b, kExact, &b) && CanLongValueFitIntoInt(b) && b >= 0) { - // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for known + // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for // maximum index value m as a * (m * (m-1)) / 2 + b * m + c. Value c = GetVal(info->op_b, trip, in_body, is_min); if (is_min) { @@ -629,6 +629,7 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction, } } else if (instruction->IsTypeConversion()) { // Since analysis is 32-bit (or narrower), chase beyond widening along the path. + // For example, this discovers the length in: for (long i = 0; i < a.length; i++); if (instruction->AsTypeConversion()->GetInputType() == Primitive::kPrimInt && instruction->AsTypeConversion()->GetResultType() == Primitive::kPrimLong) { return GetFetch(instruction->InputAt(0), trip, in_body, is_min); @@ -843,7 +844,7 @@ InductionVarRange::Value InductionVarRange::DivRangeAndConstant( InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const { if (v1.is_known && v2.is_known && IsSafeAdd(v1.b_constant, v2.b_constant)) { - const int32_t b = v1.b_constant + v2.b_constant; + int32_t b = v1.b_constant + v2.b_constant; if (v1.a_constant == 0) { return Value(v2.instruction, v2.a_constant, b); } else if (v2.a_constant == 0) { @@ -857,7 +858,7 @@ InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const { InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) const { if (v1.is_known && v2.is_known && IsSafeSub(v1.b_constant, v2.b_constant)) { - const int32_t b = v1.b_constant - v2.b_constant; + int32_t b = v1.b_constant - v2.b_constant; if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) { return Value(v2.instruction, -v2.a_constant, b); } else if (v2.a_constant == 0) { @@ -983,18 +984,21 @@ bool InductionVarRange::GenerateLastValuePolynomial(HInductionVarAnalysis::Induc int64_t a = 0; int64_t b = 0; int64_t m = 0; - if (IsConstant(info->op_a->op_a, kExact, &a) && a >= 0 && - IsConstant(info->op_a->op_b, kExact, &b) && b >= 0 && + if (IsConstant(info->op_a->op_a, kExact, &a) && + IsConstant(info->op_a->op_b, kExact, &b) && IsConstant(trip->op_a, kExact, &m) && m >= 1) { - // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for known + // Evaluate bounds on sum_i=0^m-1(a * i + b) + c for known // maximum index value m as a * (m * (m-1)) / 2 + b * m + c. - // TODO: generalize - HInstruction* c_instr = nullptr; - if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c_instr : nullptr, false, false)) { + HInstruction* c = nullptr; + if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c : nullptr, false, false)) { if (graph != nullptr) { + Primitive::Type type = info->type; int64_t sum = a * ((m * (m - 1)) / 2) + b * m; - *result = Insert(block, new (graph->GetArena()) HAdd(info->type, - graph->GetIntConstant(sum), c_instr)); + if (type != Primitive::kPrimLong) { + sum = static_cast<int32_t>(sum); // okay to truncate + } + *result = + Insert(block, new (graph->GetArena()) HAdd(type, graph->GetConstant(type, sum), c)); } return true; } @@ -1011,35 +1015,33 @@ bool InductionVarRange::GenerateLastValueGeometric(HInductionVarAnalysis::Induct DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric); // Detect known base and trip count (always taken). int64_t f = 0; - int64_t t = 0; - if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &t) && t >= 1) { + int64_t m = 0; + if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &m) && m >= 1) { HInstruction* opa = nullptr; HInstruction* opb = nullptr; if (GenerateCode(info->op_a, nullptr, graph, block, &opa, false, false) && GenerateCode(info->op_b, nullptr, graph, block, &opb, false, false)) { - // Compute f ^ t. - int64_t fpowt = IntPow(f, t); + // Compute f ^ m for known maximum index value m. + int64_t fpow = IntPow(f, m); if (graph != nullptr) { - DCHECK(info->type == Primitive::kPrimInt); // due to codegen, generalize? - if (fpowt == 0) { + DCHECK(info->operation == HInductionVarAnalysis::kMul || + info->operation == HInductionVarAnalysis::kDiv); + Primitive::Type type = info->type; + if (fpow == 0) { // Special case: repeated mul/div always yields zero. - *result = graph->GetIntConstant(0); - } else if (info->operation == HInductionVarAnalysis::kMul) { - // Last value multiplication: a * f ^ t + b. - HInstruction* mul = Insert(block, - new (graph->GetArena()) HMul(info->type, - opa, - graph->GetIntConstant(fpowt))); - *result = Insert(block, new (graph->GetArena()) HAdd(info->type, mul, opb)); + *result = graph->GetConstant(type, 0); } else { - // Last value multiplication: a * f ^ -t + b. - DCHECK_EQ(info->operation, HInductionVarAnalysis::kDiv); - HInstruction* div = Insert(block, - new (graph->GetArena()) HDiv(info->type, - opa, - graph->GetIntConstant(fpowt), - kNoDexPc)); - *result = Insert(block, new (graph->GetArena()) HAdd(info->type, div, opb)); + // Last value: a * f ^ m + b or a * f ^ -m + b. + if (type != Primitive::kPrimLong) { + fpow = static_cast<int32_t>(fpow); // okay to truncate + } + HInstruction* e = nullptr; + if (info->operation == HInductionVarAnalysis::kMul) { + e = new (graph->GetArena()) HMul(type, opa, graph->GetConstant(type, fpow)); + } else { + e = new (graph->GetArena()) HDiv(type, opa, graph->GetConstant(type, fpow), kNoDexPc); + } + *result = Insert(block, new (graph->GetArena()) HAdd(type, Insert(block, e), opb)); } } return true; @@ -1060,12 +1062,11 @@ bool InductionVarRange::GenerateLastValueWrapAround(HInductionVarAnalysis::Induc for (; info->induction_class == HInductionVarAnalysis::kWrapAround; info = info->op_b, ++depth) {} // Handle wrap(x, wrap(.., y)) if trip count reaches an invariant at end. - // TODO: generalize - int64_t t = 0; + // TODO: generalize, but be careful to adjust the terminal. + int64_t m = 0; if (info->induction_class == HInductionVarAnalysis::kInvariant && - IsConstant(trip->op_a, kExact, &t) && t >= depth && - GenerateCode(info, nullptr, graph, block, result, false, false)) { - return true; + IsConstant(trip->op_a, kExact, &m) && m >= depth) { + return GenerateCode(info, nullptr, graph, block, result, false, false); } return false; } @@ -1079,43 +1080,49 @@ bool InductionVarRange::GenerateLastValuePeriodic(HInductionVarAnalysis::Inducti DCHECK(info != nullptr); DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPeriodic); // Count period. - int32_t period = 1; + int64_t period = 1; for (HInductionVarAnalysis::InductionInfo* p = info; p->induction_class == HInductionVarAnalysis::kPeriodic; p = p->op_b, ++period) {} - // Handle periodic(x, y) case for restricted types. - // TODO: generalize - if (period != 2 || - trip->op_a->type != Primitive::kPrimInt || - (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean)) { - return false; + // Handle any periodic(x, periodic(.., y)) for known maximum index value m. + int64_t m = 0; + if (IsConstant(trip->op_a, kExact, &m) && m >= 1) { + int64_t li = m % period; + for (int64_t i = 0; i < li; info = info->op_b, i++) {} + if (info->induction_class == HInductionVarAnalysis::kPeriodic) { + info = info->op_a; + } + return GenerateCode(info, nullptr, graph, block, result, false, false); } - HInstruction* x_instr = nullptr; - HInstruction* y_instr = nullptr; - HInstruction* trip_expr = nullptr; - if (GenerateCode(info->op_a, nullptr, graph, block, graph ? &x_instr : nullptr, false, false) && - GenerateCode(info->op_b, nullptr, graph, block, graph ? &y_instr : nullptr, false, false) && - GenerateCode(trip->op_a, nullptr, graph, block, graph ? &trip_expr : nullptr, false, false)) { - // During actual code generation (graph != nullptr), - // generate is_even ? x : y select instruction. + // Handle periodic(x, y) using even/odd-select on trip count. Enter trip count expression + // directly to obtain the maximum index value t even if taken test is needed. + HInstruction* x = nullptr; + HInstruction* y = nullptr; + HInstruction* t = nullptr; + if (period == 2 && + GenerateCode(info->op_a, nullptr, graph, block, graph ? &x : nullptr, false, false) && + GenerateCode(info->op_b, nullptr, graph, block, graph ? &y : nullptr, false, false) && + GenerateCode(trip->op_a, nullptr, graph, block, graph ? &t : nullptr, false, false)) { + // During actual code generation (graph != nullptr), generate is_even ? x : y. if (graph != nullptr) { - HInstruction* is_even = Insert(block, new (graph->GetArena()) HEqual( - Insert(block, new (graph->GetArena()) HAnd( - Primitive::kPrimInt, trip_expr, graph->GetIntConstant(1))), - graph->GetIntConstant(0), kNoDexPc)); - *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x_instr, y_instr, kNoDexPc)); + Primitive::Type type = trip->type; + HInstruction* msk = + Insert(block, new (graph->GetArena()) HAnd(type, t, graph->GetConstant(type, 1))); + HInstruction* is_even = + Insert(block, new (graph->GetArena()) HEqual(msk, graph->GetConstant(type, 0), kNoDexPc)); + *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x, y, kNoDexPc)); } // Guard select with taken test if needed. if (*needs_taken_test) { - HInstruction* taken_test = nullptr; - if (!GenerateCode( - trip->op_b, nullptr, graph, block, graph ? &taken_test : nullptr, false, false)) { + HInstruction* is_taken = nullptr; + if (GenerateCode(trip->op_b, nullptr, graph, block, graph ? &is_taken : nullptr, false, false)) { + if (graph != nullptr) { + *result = Insert(block, new (graph->GetArena()) HSelect(is_taken, *result, x, kNoDexPc)); + } + *needs_taken_test = false; // taken care of + } else { return false; - } else if (graph != nullptr) { - *result = Insert(block, - new (graph->GetArena()) HSelect(taken_test, *result, x_instr, kNoDexPc)); } - *needs_taken_test = false; // taken care of } return true; } @@ -1134,13 +1141,8 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, if (graph != nullptr && result == nullptr) { return true; } - // Verify type safety. - // TODO: generalize - Primitive::Type type = Primitive::kPrimInt; - if (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean) { - return false; - } // Handle current operation. + Primitive::Type type = info->type; HInstruction* opa = nullptr; HInstruction* opb = nullptr; switch (info->induction_class) { @@ -1214,15 +1216,15 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, case HInductionVarAnalysis::kTripCountInBodyUnsafe: if (is_min) { if (graph != nullptr) { - *result = graph->GetIntConstant(0); + *result = graph->GetConstant(type, 0); } return true; } else if (in_body) { if (GenerateCode(info->op_a, trip, graph, block, &opb, in_body, is_min)) { if (graph != nullptr) { - *result = Insert(block, - new (graph->GetArena()) - HSub(type, opb, graph->GetIntConstant(1))); + *result = + Insert(block, + new (graph->GetArena()) HSub(type, opb, graph->GetConstant(type, 1))); } return true; } @@ -1236,26 +1238,31 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, // Linear induction a * i + b, for normalized 0 <= i < TC. For ranges, this should // be restricted to a unit stride to avoid arithmetic wrap-around situations that // are harder to guard against. For a last value, requesting min/max based on any - // stride yields right value. - int64_t stride_value = 0; - if (IsConstant(info->op_a, kExact, &stride_value)) { - const bool is_min_a = stride_value >= 0 ? is_min : !is_min; - if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) && - GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) { - if (graph != nullptr) { - HInstruction* oper; - if (stride_value == 1) { - oper = new (graph->GetArena()) HAdd(type, opa, opb); - } else if (stride_value == -1) { - oper = new (graph->GetArena()) HSub(type, opb, opa); - } else { - HInstruction* mul = new (graph->GetArena()) HMul( - type, graph->GetIntConstant(stride_value), opa); - oper = new (graph->GetArena()) HAdd(type, Insert(block, mul), opb); + // known stride yields right value. Always avoid any narrowing linear induction or + // any type mismatch between the linear induction and the trip count expression. + // TODO: careful runtime type conversions could generalize this latter restriction. + if (!HInductionVarAnalysis::IsNarrowingLinear(info) && trip->type == type) { + int64_t stride_value = 0; + if (IsConstant(info->op_a, kExact, &stride_value) && + CanLongValueFitIntoInt(stride_value)) { + const bool is_min_a = stride_value >= 0 ? is_min : !is_min; + if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) && + GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) { + if (graph != nullptr) { + HInstruction* oper; + if (stride_value == 1) { + oper = new (graph->GetArena()) HAdd(type, opa, opb); + } else if (stride_value == -1) { + oper = new (graph->GetArena()) HSub(type, opb, opa); + } else { + HInstruction* mul = + new (graph->GetArena()) HMul(type, graph->GetConstant(type, stride_value), opa); + oper = new (graph->GetArena()) HAdd(type, Insert(block, mul), opb); + } + *result = Insert(block, oper); } - *result = Insert(block, oper); + return true; } - return true; } } break; @@ -1270,7 +1277,7 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, Value extreme = GetVal(info, trip, in_body, is_min); if (IsConstantValue(extreme)) { if (graph != nullptr) { - *result = graph->GetIntConstant(extreme.b_constant); + *result = graph->GetConstant(type, extreme.b_constant); } return true; } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 8d93867230..d84787984d 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -480,13 +480,11 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, } // We successfully inlined, now add a guard. - bool is_referrer = - (GetMonomorphicType(classes) == outermost_graph_->GetArtMethod()->GetDeclaringClass()); AddTypeGuard(receiver, cursor, bb_cursor, class_index, - is_referrer, + GetMonomorphicType(classes), invoke_instruction, /* with_deoptimization */ true); @@ -506,52 +504,62 @@ void HInliner::AddCHAGuard(HInstruction* invoke_instruction, uint32_t dex_pc, HInstruction* cursor, HBasicBlock* bb_cursor) { - HInstruction* deopt_flag = new (graph_->GetArena()) HShouldDeoptimizeFlag(dex_pc); - HInstruction* should_deopt = new (graph_->GetArena()) HNotEqual( + HShouldDeoptimizeFlag* deopt_flag = new (graph_->GetArena()) + HShouldDeoptimizeFlag(graph_->GetArena(), dex_pc); + HInstruction* compare = new (graph_->GetArena()) HNotEqual( deopt_flag, graph_->GetIntConstant(0, dex_pc)); - HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(should_deopt, dex_pc); + HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(compare, dex_pc); if (cursor != nullptr) { bb_cursor->InsertInstructionAfter(deopt_flag, cursor); } else { bb_cursor->InsertInstructionBefore(deopt_flag, bb_cursor->GetFirstInstruction()); } - bb_cursor->InsertInstructionAfter(should_deopt, deopt_flag); - bb_cursor->InsertInstructionAfter(deopt, should_deopt); + bb_cursor->InsertInstructionAfter(compare, deopt_flag); + bb_cursor->InsertInstructionAfter(deopt, compare); + + // Add receiver as input to aid CHA guard optimization later. + deopt_flag->AddInput(invoke_instruction->InputAt(0)); + DCHECK_EQ(deopt_flag->InputCount(), 1u); deopt->CopyEnvironmentFrom(invoke_instruction->GetEnvironment()); + outermost_graph_->IncrementNumberOfCHAGuards(); } HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, HInstruction* cursor, HBasicBlock* bb_cursor, dex::TypeIndex class_index, - bool is_referrer, + mirror::Class* klass, HInstruction* invoke_instruction, bool with_deoptimization) { + ScopedAssertNoThreadSuspension sants("Adding compiler type guard"); + ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); HInstanceFieldGet* receiver_class = BuildGetReceiverClass( class_linker, receiver, invoke_instruction->GetDexPc()); + if (cursor != nullptr) { + bb_cursor->InsertInstructionAfter(receiver_class, cursor); + } else { + bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction()); + } const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); + bool is_referrer = (klass == outermost_graph_->GetArtMethod()->GetDeclaringClass()); // Note that we will just compare the classes, so we don't need Java semantics access checks. - // Also, the caller of `AddTypeGuard` must have guaranteed that the class is in the dex cache. + // Note that the type index and the dex file are relative to the method this type guard is + // inlined into. HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(), class_index, caller_dex_file, is_referrer, invoke_instruction->GetDexPc(), - /* needs_access_check */ false, - /* is_in_dex_cache */ true, - /* is_in_boot_image */ false); + /* needs_access_check */ false); + bb_cursor->InsertInstructionAfter(load_class, receiver_class); + // Sharpen after adding the instruction, as the sharpening may remove inputs. + HSharpening::SharpenClass(load_class, klass, handles_, codegen_, compiler_driver_); - HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class); // TODO: Extend reference type propagation to understand the guard. - if (cursor != nullptr) { - bb_cursor->InsertInstructionAfter(receiver_class, cursor); - } else { - bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction()); - } - bb_cursor->InsertInstructionAfter(load_class, receiver_class); + HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class); bb_cursor->InsertInstructionAfter(compare, load_class); if (with_deoptimization) { HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize( @@ -604,7 +612,6 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, all_targets_inlined = false; } else { one_target_inlined = true; - bool is_referrer = (classes->Get(i) == outermost_graph_->GetArtMethod()->GetDeclaringClass()); // If we have inlined all targets before, and this receiver is the last seen, // we deoptimize instead of keeping the original invoke instruction. @@ -616,8 +623,13 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, // We do not support HDeoptimize in OSR methods. deoptimize = false; } - HInstruction* compare = AddTypeGuard( - receiver, cursor, bb_cursor, class_index, is_referrer, invoke_instruction, deoptimize); + HInstruction* compare = AddTypeGuard(receiver, + cursor, + bb_cursor, + class_index, + classes->Get(i), + invoke_instruction, + deoptimize); if (deoptimize) { if (return_replacement != nullptr) { invoke_instruction->ReplaceWith(return_replacement); @@ -1444,7 +1456,7 @@ size_t HInliner::RunOptimizations(HGraph* callee_graph, // optimization that could lead to a HDeoptimize. The following optimizations do not. HDeadCodeElimination dce(callee_graph, stats_, "dead_code_elimination$inliner"); HConstantFolding fold(callee_graph, "constant_folding$inliner"); - HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_); + HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_, handles_); InstructionSimplifier simplify(callee_graph, stats_); IntrinsicsRecognizer intrinsics(callee_graph, stats_); diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index ffebd97cb8..0c6436235f 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -167,7 +167,7 @@ class HInliner : public HOptimization { HInstruction* cursor, HBasicBlock* bb_cursor, dex::TypeIndex class_index, - bool is_referrer, + mirror::Class* klass, HInstruction* invoke_instruction, bool with_deoptimization) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index b97581beb3..af8e2c8a7c 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -816,8 +816,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, HInvokeStaticOrDirect::DispatchInfo dispatch_info = { HInvokeStaticOrDirect::MethodLoadKind::kStringInit, HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, - dchecked_integral_cast<uint64_t>(string_init_entry_point), - 0U + dchecked_integral_cast<uint64_t>(string_init_entry_point) }; MethodReference target_method(dex_file_, method_idx); HInvoke* invoke = new (arena_) HInvokeStaticOrDirect( @@ -862,8 +861,7 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, HInvokeStaticOrDirect::DispatchInfo dispatch_info = { HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod, HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, - 0u, - 0U + 0u }; MethodReference target_method(resolved_method->GetDexFile(), resolved_method->GetDexMethodIndex()); @@ -937,9 +935,7 @@ bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t d outer_dex_file, IsOutermostCompilingClass(type_index), dex_pc, - needs_access_check, - /* is_in_dex_cache */ false, - /* is_in_boot_image */ false); + needs_access_check); AppendInstruction(load_class); HInstruction* cls = load_class; @@ -1029,9 +1025,7 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( outer_dex_file, is_outer_class, dex_pc, - /*needs_access_check*/ false, - /* is_in_dex_cache */ false, - /* is_in_boot_image */ false); + /*needs_access_check*/ false); AppendInstruction(load_class); clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); AppendInstruction(clinit_check); @@ -1388,9 +1382,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, outer_dex_file, is_outer_class, dex_pc, - /*needs_access_check*/ false, - /* is_in_dex_cache */ false, - /* is_in_boot_image */ false); + /*needs_access_check*/ false); AppendInstruction(constant); HInstruction* cls = constant; @@ -1664,9 +1656,7 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, dex_file, IsOutermostCompilingClass(type_index), dex_pc, - !can_access, - /* is_in_dex_cache */ false, - /* is_in_boot_image */ false); + !can_access); AppendInstruction(cls); TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class); @@ -2656,9 +2646,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, *dex_file_, IsOutermostCompilingClass(type_index), dex_pc, - !can_access, - /* is_in_dex_cache */ false, - /* is_in_boot_image */ false)); + !can_access)); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); break; } diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 658b80468e..439e3b66db 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -1185,6 +1185,18 @@ void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) { RecordSimplification(); } +// Return whether x / divisor == x * (1.0f / divisor), for every float x. +static constexpr bool CanDivideByReciprocalMultiplyFloat(int32_t divisor) { + // True, if the most significant bits of divisor are 0. + return ((divisor & 0x7fffff) == 0); +} + +// Return whether x / divisor == x * (1.0 / divisor), for every double x. +static constexpr bool CanDivideByReciprocalMultiplyDouble(int64_t divisor) { + // True, if the most significant bits of divisor are 0. + return ((divisor & ((UINT64_C(1) << 52) - 1)) == 0); +} + void InstructionSimplifierVisitor::VisitDiv(HDiv* instruction) { HConstant* input_cst = instruction->GetConstantRight(); HInstruction* input_other = instruction->GetLeastConstantLeft(); @@ -1885,7 +1897,8 @@ void InstructionSimplifierVisitor::SimplifyReturnThis(HInvoke* invoke) { static bool NoEscapeForStringBufferReference(HInstruction* reference, HInstruction* user) { if (user->IsInvokeStaticOrDirect()) { // Any constructor on StringBuffer is okay. - return user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() && + return user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr && + user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() && user->InputAt(0) == reference; } else if (user->IsInvokeVirtual()) { switch (user->AsInvokeVirtual()->GetIntrinsic()) { diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 433dced9d7..85e84d8d2c 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -47,6 +47,9 @@ using helpers::SRegisterFrom; using namespace vixl::aarch32; // NOLINT(build/namespaces) +using vixl::ExactAssemblyScope; +using vixl::CodeBufferCheckScope; + ArmVIXLAssembler* IntrinsicCodeGeneratorARMVIXL::GetAssembler() { return codegen_->GetAssembler(); } @@ -184,7 +187,7 @@ class ReadBarrierSystemArrayCopySlowPathARMVIXL : public SlowPathCodeARMVIXL { assembler->MaybePoisonHeapReference(tmp); __ Str(tmp, MemOperand(dst_curr_addr, element_size, PostIndex)); __ Cmp(src_curr_addr, src_stop_addr); - __ B(ne, &loop); + __ B(ne, &loop, /* far_target */ false); __ B(GetExitLabel()); } @@ -467,9 +470,9 @@ static void GenMinMax(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) __ Cmp(op1, op2); { - AssemblerAccurateScope aas(assembler->GetVIXLAssembler(), - 3 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(assembler->GetVIXLAssembler(), + 3 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ ite(is_min ? lt : gt); __ mov(is_min ? lt : gt, out, op1); @@ -848,7 +851,7 @@ static void GenUnsafePut(LocationSummary* locations, __ Ldrexd(temp_lo, temp_hi, MemOperand(temp_reg)); __ Strexd(temp_lo, value_lo, value_hi, MemOperand(temp_reg)); __ Cmp(temp_lo, 0); - __ B(ne, &loop_head); + __ B(ne, &loop_head, /* far_target */ false); } else { __ Strd(value_lo, value_hi, MemOperand(base, offset)); } @@ -1050,25 +1053,25 @@ static void GenCas(HInvoke* invoke, Primitive::Type type, CodeGeneratorARMVIXL* __ Subs(tmp, tmp, expected); { - AssemblerAccurateScope aas(assembler->GetVIXLAssembler(), - 3 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(assembler->GetVIXLAssembler(), + 3 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ itt(eq); __ strex(eq, tmp, value, MemOperand(tmp_ptr)); __ cmp(eq, tmp, 1); } - __ B(eq, &loop_head); + __ B(eq, &loop_head, /* far_target */ false); __ Dmb(vixl32::ISH); __ Rsbs(out, tmp, 1); { - AssemblerAccurateScope aas(assembler->GetVIXLAssembler(), - 2 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(assembler->GetVIXLAssembler(), + 2 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ it(cc); __ mov(cc, out, 0); @@ -1185,9 +1188,9 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { // temp0 = min(len(str), len(arg)). { - AssemblerAccurateScope aas(assembler->GetVIXLAssembler(), - 2 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(assembler->GetVIXLAssembler(), + 2 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ it(gt); __ mov(gt, temp0, temp1); @@ -1207,9 +1210,9 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { // This could in theory exceed INT32_MAX, so treat temp0 as unsigned. __ Lsls(temp3, temp3, 31u); // Extract purely the compression flag. - AssemblerAccurateScope aas(assembler->GetVIXLAssembler(), - 2 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(assembler->GetVIXLAssembler(), + 2 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ it(ne); __ add(ne, temp0, temp0, temp0); @@ -1235,23 +1238,23 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { __ Ldr(temp_reg, MemOperand(str, temp1)); __ Ldr(temp2, MemOperand(arg, temp1)); __ Cmp(temp_reg, temp2); - __ B(ne, &find_char_diff); + __ B(ne, &find_char_diff, /* far_target */ false); __ Add(temp1, temp1, char_size * 2); __ Ldr(temp_reg, MemOperand(str, temp1)); __ Ldr(temp2, MemOperand(arg, temp1)); __ Cmp(temp_reg, temp2); - __ B(ne, &find_char_diff_2nd_cmp); + __ B(ne, &find_char_diff_2nd_cmp, /* far_target */ false); __ Add(temp1, temp1, char_size * 2); // With string compression, we have compared 8 bytes, otherwise 4 chars. __ Subs(temp0, temp0, (mirror::kUseStringCompression ? 8 : 4)); - __ B(hi, &loop); + __ B(hi, &loop, /* far_target */ false); __ B(&end); __ Bind(&find_char_diff_2nd_cmp); if (mirror::kUseStringCompression) { __ Subs(temp0, temp0, 4); // 4 bytes previously compared. - __ B(ls, &end); // Was the second comparison fully beyond the end? + __ B(ls, &end, /* far_target */ false); // Was the second comparison fully beyond the end? } else { // Without string compression, we can start treating temp0 as signed // and rely on the signed comparison below. @@ -1279,7 +1282,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { // the remaining string data, so just return length diff (out). // The comparison is unsigned for string compression, otherwise signed. __ Cmp(temp0, Operand(temp1, vixl32::LSR, (mirror::kUseStringCompression ? 3 : 4))); - __ B((mirror::kUseStringCompression ? ls : le), &end); + __ B((mirror::kUseStringCompression ? ls : le), &end, /* far_target */ false); // Extract the characters and calculate the difference. if (mirror::kUseStringCompression) { @@ -1324,9 +1327,9 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { __ Mov(temp2, arg); __ Lsrs(temp3, temp3, 1u); // Continue the move of the compression flag. { - AssemblerAccurateScope aas(assembler->GetVIXLAssembler(), - 3 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(assembler->GetVIXLAssembler(), + 3 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ itt(cs); // Interleave with selection of temp1 and temp2. __ mov(cs, temp1, arg); // Preserves flags. __ mov(cs, temp2, str); // Preserves flags. @@ -1346,9 +1349,9 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { __ Ldrb(temp_reg, MemOperand(temp1, c_char_size, PostIndex)); __ Ldrh(temp3, MemOperand(temp2, char_size, PostIndex)); __ Cmp(temp_reg, temp3); - __ B(ne, &different_compression_diff); + __ B(ne, &different_compression_diff, /* far_target */ false); __ Subs(temp0, temp0, 2); - __ B(hi, &different_compression_loop); + __ B(hi, &different_compression_loop, /* far_target */ false); __ B(&end); // Calculate the difference. @@ -1361,9 +1364,9 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringCompareTo(HInvoke* invoke) { static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, "Expecting 0=compressed, 1=uncompressed"); - AssemblerAccurateScope aas(assembler->GetVIXLAssembler(), - 2 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(assembler->GetVIXLAssembler(), + 2 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ it(cc); __ rsb(cc, out, out, 0); } @@ -1424,7 +1427,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { // Reference equality check, return true if same reference. __ Cmp(str, arg); - __ B(eq, &return_true); + __ B(eq, &return_true, /* far_target */ false); if (!optimizations.GetArgumentIsString()) { // Instanceof check for the argument by comparing class fields. @@ -1434,7 +1437,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { __ Ldr(temp, MemOperand(str, class_offset)); __ Ldr(temp1, MemOperand(arg, class_offset)); __ Cmp(temp, temp1); - __ B(ne, &return_false); + __ B(ne, &return_false, /* far_target */ false); } // Load `count` fields of this and argument strings. @@ -1443,7 +1446,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { // Check if `count` fields are equal, return false if they're not. // Also compares the compression style, if differs return false. __ Cmp(temp, temp1); - __ B(ne, &return_false); + __ B(ne, &return_false, /* far_target */ false); // Return true if both strings are empty. Even with string compression `count == 0` means empty. static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, "Expecting 0=compressed, 1=uncompressed"); @@ -1457,9 +1460,9 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { // For string compression, calculate the number of bytes to compare (not chars). // This could in theory exceed INT32_MAX, so treat temp as unsigned. __ Lsrs(temp, temp, 1u); // Extract length and check compression flag. - AssemblerAccurateScope aas(assembler->GetVIXLAssembler(), - 2 * kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope aas(assembler->GetVIXLAssembler(), + 2 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); __ it(cs); // If uncompressed, __ add(cs, temp, temp, temp); // double the byte count. } @@ -1474,10 +1477,10 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { __ Ldr(temp2, MemOperand(arg, temp1)); __ Add(temp1, temp1, Operand::From(sizeof(uint32_t))); __ Cmp(out, temp2); - __ B(ne, &return_false); + __ B(ne, &return_false, /* far_target */ false); // With string compression, we have compared 4 bytes, otherwise 2 chars. __ Subs(temp, temp, mirror::kUseStringCompression ? 4 : 2); - __ B(hi, &loop); + __ B(hi, &loop, /* far_target */ false); // Return true and exit the function. // If loop does not result in returning false, we return true. @@ -1506,7 +1509,7 @@ static void GenerateVisitStringIndexOf(HInvoke* invoke, SlowPathCodeARMVIXL* slow_path = nullptr; HInstruction* code_point = invoke->InputAt(1); if (code_point->IsIntConstant()) { - if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > + if (static_cast<uint32_t>(Int32ConstantFrom(code_point)) > std::numeric_limits<uint16_t>::max()) { // Always needs the slow-path. We could directly dispatch to it, but this case should be // rare, so for simplicity just put the full slow-path down and branch unconditionally. @@ -1797,7 +1800,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { } else { if (!optimizations.GetDestinationIsSource()) { __ Cmp(src, dest); - __ B(ne, &conditions_on_positions_validated); + __ B(ne, &conditions_on_positions_validated, /* far_target */ false); } __ Cmp(RegisterFrom(dest_pos), src_pos_constant); __ B(gt, intrinsic_slow_path->GetEntryLabel()); @@ -1805,7 +1808,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { } else { if (!optimizations.GetDestinationIsSource()) { __ Cmp(src, dest); - __ B(ne, &conditions_on_positions_validated); + __ B(ne, &conditions_on_positions_validated, /* far_target */ false); } if (dest_pos.IsConstant()) { int32_t dest_pos_constant = Int32ConstantFrom(dest_pos); @@ -1913,7 +1916,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { if (optimizations.GetDestinationIsTypedObjectArray()) { vixl32::Label do_copy; - __ B(eq, &do_copy); + __ B(eq, &do_copy, /* far_target */ false); // /* HeapReference<Class> */ temp1 = temp1->component_type_ codegen_->GenerateFieldLoadWithBakerReadBarrier( invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false); @@ -1973,7 +1976,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { if (optimizations.GetDestinationIsTypedObjectArray()) { vixl32::Label do_copy; - __ B(eq, &do_copy); + __ B(eq, &do_copy, /* far_target */ false); if (!did_unpoison) { assembler->MaybeUnpoisonHeapReference(temp1); } @@ -2066,7 +2069,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // Don't enter copy loop if `length == 0`. __ Cmp(temp1, temp3); - __ B(eq, &done); + __ B(eq, &done, /* far_target */ false); // /* int32_t */ monitor = src->monitor_ __ Ldr(temp2, MemOperand(src, monitor_offset)); @@ -2119,7 +2122,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { } __ Cmp(temp1, temp3); - __ B(ne, &loop); + __ B(ne, &loop, /* far_target */ false); __ Bind(read_barrier_slow_path->GetExitLabel()); __ Bind(&done); @@ -2139,7 +2142,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { // poison/unpoison. vixl32::Label loop, done; __ Cmp(temp1, temp3); - __ B(eq, &done); + __ B(eq, &done, /* far_target */ false); __ Bind(&loop); { @@ -2151,7 +2154,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) { } __ Cmp(temp1, temp3); - __ B(ne, &loop); + __ B(ne, &loop, /* far_target */ false); __ Bind(&done); } @@ -2557,7 +2560,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) __ Subs(num_chr, srcEnd, srcBegin); // Early out for valid zero-length retrievals. - __ B(eq, &done); + __ B(eq, &done, /* far_target */ false); // src range to copy. __ Add(src_ptr, srcObj, value_offset); @@ -2573,7 +2576,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) __ Ldr(temp, MemOperand(srcObj, count_offset)); __ Tst(temp, 1); temps.Release(temp); - __ B(eq, &compressed_string_preloop); + __ B(eq, &compressed_string_preloop, /* far_target */ false); } __ Add(src_ptr, src_ptr, Operand(srcBegin, vixl32::LSL, 1)); @@ -2583,7 +2586,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) temp = temps.Acquire(); // Save repairing the value of num_chr on the < 4 character path. __ Subs(temp, num_chr, 4); - __ B(lt, &remainder); + __ B(lt, &remainder, /* far_target */ false); // Keep the result of the earlier subs, we are going to fetch at least 4 characters. __ Mov(num_chr, temp); @@ -2598,10 +2601,10 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) __ Ldr(temp, MemOperand(src_ptr, char_size * 4, PostIndex)); __ Str(temp, MemOperand(dst_ptr, char_size * 4, PostIndex)); temps.Release(temp); - __ B(ge, &loop); + __ B(ge, &loop, /* far_target */ false); __ Adds(num_chr, num_chr, 4); - __ B(eq, &done); + __ B(eq, &done, /* far_target */ false); // Main loop for < 4 character case and remainder handling. Loads and stores one // 16-bit Java character at a time. @@ -2611,7 +2614,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) __ Subs(num_chr, num_chr, 1); __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex)); temps.Release(temp); - __ B(gt, &remainder); + __ B(gt, &remainder, /* far_target */ false); if (mirror::kUseStringCompression) { __ B(&done); @@ -2627,7 +2630,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) __ Strh(temp, MemOperand(dst_ptr, char_size, PostIndex)); temps.Release(temp); __ Subs(num_chr, num_chr, 1); - __ B(gt, &compressed_string_loop); + __ B(gt, &compressed_string_loop, /* far_target */ false); } __ Bind(&done); diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 9b5d7a02dd..1fb90e5113 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1648,7 +1648,8 @@ static void GenUnsafePut(LocationSummary* locations, } if (type == Primitive::kPrimNot) { - codegen->MarkGCCard(base, locations->InAt(3).AsRegister<Register>()); + bool value_can_be_null = true; // TODO: Worth finding out this information? + codegen->MarkGCCard(base, locations->InAt(3).AsRegister<Register>(), value_can_be_null); } } @@ -1806,7 +1807,8 @@ static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGenerat if (type == Primitive::kPrimNot) { // Mark card for object assuming new value is stored. - codegen->MarkGCCard(base, value); + bool value_can_be_null = true; // TODO: Worth finding out this information? + codegen->MarkGCCard(base, value, value_can_be_null); } // do { diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 5a998861eb..3022e975e8 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1846,6 +1846,84 @@ void IntrinsicCodeGeneratorMIPS64::VisitDoubleIsInfinite(HInvoke* invoke) { GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); } +// void java.lang.String.getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) +void IntrinsicLocationsBuilderMIPS64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCallOnMainOnly, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); + + // We will call memcpy() to do the actual work. Allocate the temporary + // registers to use the correct input registers, and output register. + // memcpy() uses the normal MIPS calling conventions. + InvokeRuntimeCallingConvention calling_convention; + + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimLong); + locations->AddTemp(Location::RegisterLocation(outLocation.AsRegister<GpuRegister>())); +} + +void IntrinsicCodeGeneratorMIPS64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + Mips64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Check assumption that sizeof(Char) is 2 (used in scaling below). + const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + DCHECK_EQ(char_size, 2u); + const size_t char_shift = Primitive::ComponentSizeShift(Primitive::kPrimChar); + + GpuRegister srcObj = locations->InAt(0).AsRegister<GpuRegister>(); + GpuRegister srcBegin = locations->InAt(1).AsRegister<GpuRegister>(); + GpuRegister srcEnd = locations->InAt(2).AsRegister<GpuRegister>(); + GpuRegister dstObj = locations->InAt(3).AsRegister<GpuRegister>(); + GpuRegister dstBegin = locations->InAt(4).AsRegister<GpuRegister>(); + + GpuRegister dstPtr = locations->GetTemp(0).AsRegister<GpuRegister>(); + DCHECK_EQ(dstPtr, A0); + GpuRegister srcPtr = locations->GetTemp(1).AsRegister<GpuRegister>(); + DCHECK_EQ(srcPtr, A1); + GpuRegister numChrs = locations->GetTemp(2).AsRegister<GpuRegister>(); + DCHECK_EQ(numChrs, A2); + + GpuRegister dstReturn = locations->GetTemp(3).AsRegister<GpuRegister>(); + DCHECK_EQ(dstReturn, V0); + + Mips64Label done; + + // Location of data in char array buffer. + const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); + + // Get offset of value field within a string object. + const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); + + __ Beqc(srcEnd, srcBegin, &done); // No characters to move. + + // Calculate number of characters to be copied. + __ Dsubu(numChrs, srcEnd, srcBegin); + + // Calculate destination address. + __ Daddiu(dstPtr, dstObj, data_offset); + __ Dlsa(dstPtr, dstBegin, dstPtr, char_shift); + + // Calculate source address. + __ Daddiu(srcPtr, srcObj, value_offset); + __ Dlsa(srcPtr, srcBegin, srcPtr, char_shift); + + // Calculate number of bytes to copy from number of characters. + __ Dsll(numChrs, numChrs, char_shift); + + codegen_->InvokeRuntime(kQuickMemcpy, invoke, invoke->GetDexPc(), nullptr); + + __ Bind(&done); +} + static void GenHighestOneBit(LocationSummary* locations, Primitive::Type type, Mips64Assembler* assembler) { @@ -1925,7 +2003,6 @@ void IntrinsicCodeGeneratorMIPS64::VisitLongLowestOneBit(HInvoke* invoke) { } UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(MIPS64, StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy) diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc index 13e14c53b5..3831aa6c91 100644 --- a/compiler/optimizing/linearize_test.cc +++ b/compiler/optimizing/linearize_test.cc @@ -18,7 +18,6 @@ #include "arch/x86/instruction_set_features_x86.h" #include "base/arena_allocator.h" -#include "base/stringprintf.h" #include "builder.h" #include "code_generator.h" #include "code_generator_x86.h" diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 594255c625..a599c2aa84 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1108,13 +1108,23 @@ size_t HInstruction::EnvironmentSize() const { return HasEnvironment() ? environment_->Size() : 0; } -void HPhi::AddInput(HInstruction* input) { +void HVariableInputSizeInstruction::AddInput(HInstruction* input) { DCHECK(input->GetBlock() != nullptr); inputs_.push_back(HUserRecord<HInstruction*>(input)); input->AddUseAt(this, inputs_.size() - 1); } -void HPhi::RemoveInputAt(size_t index) { +void HVariableInputSizeInstruction::InsertInputAt(size_t index, HInstruction* input) { + inputs_.insert(inputs_.begin() + index, HUserRecord<HInstruction*>(input)); + input->AddUseAt(this, index); + // Update indexes in use nodes of inputs that have been pushed further back by the insert(). + for (size_t i = index + 1u, e = inputs_.size(); i < e; ++i) { + DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i - 1u); + inputs_[i].GetUseNode()->SetIndex(i); + } +} + +void HVariableInputSizeInstruction::RemoveInputAt(size_t index) { RemoveAsUserOfInput(index); inputs_.erase(inputs_.begin() + index); // Update indexes in use nodes of inputs that have been pulled forward by the erase(). @@ -1347,7 +1357,9 @@ std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& void HInstruction::MoveBefore(HInstruction* cursor) { DCHECK(!IsPhi()); DCHECK(!IsControlFlow()); - DCHECK(CanBeMoved()); + DCHECK(CanBeMoved() || + // HShouldDeoptimizeFlag can only be moved by CHAGuardOptimization. + IsShouldDeoptimizeFlag()); DCHECK(!cursor->IsPhi()); next_->previous_ = previous_; @@ -2386,26 +2398,6 @@ bool HInvokeStaticOrDirect::NeedsDexCacheOfDeclaringClass() const { return !opt.GetDoesNotNeedDexCache(); } -void HInvokeStaticOrDirect::InsertInputAt(size_t index, HInstruction* input) { - inputs_.insert(inputs_.begin() + index, HUserRecord<HInstruction*>(input)); - input->AddUseAt(this, index); - // Update indexes in use nodes of inputs that have been pushed further back by the insert(). - for (size_t i = index + 1u, e = inputs_.size(); i < e; ++i) { - DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i - 1u); - inputs_[i].GetUseNode()->SetIndex(i); - } -} - -void HInvokeStaticOrDirect::RemoveInputAt(size_t index) { - RemoveAsUserOfInput(index); - inputs_.erase(inputs_.begin() + index); - // Update indexes in use nodes of inputs that have been pulled forward by the erase(). - for (size_t i = index, e = inputs_.size(); i < e; ++i) { - DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i + 1u); - inputs_[i].GetUseNode()->SetIndex(i); - } -} - std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind rhs) { switch (rhs) { case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: @@ -2414,8 +2406,6 @@ std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind return os << "recursive"; case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress: return os << "direct"; - case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup: - return os << "direct_fixup"; case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: return os << "dex_cache_pc_relative"; case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: @@ -2440,6 +2430,17 @@ std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckReq } } +// Helper for InstructionDataEquals to fetch the mirror Class out +// from a kJitTableAddress LoadClass kind. +// NO_THREAD_SAFETY_ANALYSIS because even though we're accessing +// mirrors, they are stored in a variable size handle scope which is always +// visited during a pause. Also, the only caller of this helper +// only uses the mirror for pointer comparison. +static inline mirror::Class* AsMirrorInternal(uint64_t address) + NO_THREAD_SAFETY_ANALYSIS { + return reinterpret_cast<StackReference<mirror::Class>*>(address)->AsMirrorPtr(); +} + bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { const HLoadClass* other_load_class = other->AsLoadClass(); // TODO: To allow GVN for HLoadClass from different dex files, we should compare the type @@ -2448,16 +2449,14 @@ bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { GetPackedFields() != other_load_class->GetPackedFields()) { return false; } - LoadKind load_kind = GetLoadKind(); - if (HasAddress(load_kind)) { - return GetAddress() == other_load_class->GetAddress(); - } else if (HasTypeReference(load_kind)) { - return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile()); - } else { - DCHECK(HasDexCacheReference(load_kind)) << load_kind; - // If the type indexes and dex files are the same, dex cache element offsets - // must also be the same, so we don't need to compare them. - return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile()); + switch (GetLoadKind()) { + case LoadKind::kBootImageAddress: + return GetAddress() == other_load_class->GetAddress(); + case LoadKind::kJitTableAddress: + return AsMirrorInternal(GetAddress()) == AsMirrorInternal(other_load_class->GetAddress()); + default: + DCHECK(HasTypeReference(GetLoadKind()) || HasDexCacheReference(GetLoadKind())); + return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile()); } } @@ -2487,8 +2486,8 @@ std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs) { return os << "BootImageLinkTimePcRelative"; case HLoadClass::LoadKind::kBootImageAddress: return os << "BootImageAddress"; - case HLoadClass::LoadKind::kDexCacheAddress: - return os << "DexCacheAddress"; + case HLoadClass::LoadKind::kJitTableAddress: + return os << "JitTableAddress"; case HLoadClass::LoadKind::kDexCachePcRelative: return os << "DexCachePcRelative"; case HLoadClass::LoadKind::kDexCacheViaMethod: diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index e3f4d8f035..afa17cefa2 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -125,6 +125,11 @@ enum GraphAnalysisResult { kAnalysisSuccess, }; +template <typename T> +static inline typename std::make_unsigned<T>::type MakeUnsigned(T x) { + return static_cast<typename std::make_unsigned<T>::type>(x); +} + class HInstructionList : public ValueObject { public: HInstructionList() : first_instruction_(nullptr), last_instruction_(nullptr) {} @@ -325,6 +330,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { invoke_type_(invoke_type), in_ssa_form_(false), should_generate_constructor_barrier_(should_generate_constructor_barrier), + number_of_cha_guards_(0), instruction_set_(instruction_set), cached_null_constant_(nullptr), cached_int_constants_(std::less<int32_t>(), arena->Adapter(kArenaAllocConstantsMap)), @@ -546,9 +552,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { } bool HasShouldDeoptimizeFlag() const { - // TODO: if all CHA guards can be eliminated, there is no need for the flag - // even if cha_single_implementation_list_ is not empty. - return !cha_single_implementation_list_.empty(); + return number_of_cha_guards_ != 0; } bool HasTryCatch() const { return has_try_catch_; } @@ -567,6 +571,10 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { ReferenceTypeInfo GetInexactObjectRti() const { return inexact_object_rti_; } + uint32_t GetNumberOfCHAGuards() { return number_of_cha_guards_; } + void SetNumberOfCHAGuards(uint32_t num) { number_of_cha_guards_ = num; } + void IncrementNumberOfCHAGuards() { number_of_cha_guards_++; } + private: void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const; void RemoveDeadBlocks(const ArenaBitVector& visited); @@ -662,6 +670,10 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { const bool should_generate_constructor_barrier_; + // Number of CHA guards in the graph. Used to short-circuit the + // CHA guard optimization pass when there is no CHA guard left. + uint32_t number_of_cha_guards_; + const InstructionSet instruction_set_; // Cached constants. @@ -2342,6 +2354,32 @@ class HBackwardInstructionIterator : public ValueObject { DISALLOW_COPY_AND_ASSIGN(HBackwardInstructionIterator); }; +class HVariableInputSizeInstruction : public HInstruction { + public: + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE { + return ArrayRef<HUserRecord<HInstruction*>>(inputs_); + } + + void AddInput(HInstruction* input); + void InsertInputAt(size_t index, HInstruction* input); + void RemoveInputAt(size_t index); + + protected: + HVariableInputSizeInstruction(SideEffects side_effects, + uint32_t dex_pc, + ArenaAllocator* arena, + size_t number_of_inputs, + ArenaAllocKind kind) + : HInstruction(side_effects, dex_pc), + inputs_(number_of_inputs, arena->Adapter(kind)) {} + + ArenaVector<HUserRecord<HInstruction*>> inputs_; + + private: + DISALLOW_COPY_AND_ASSIGN(HVariableInputSizeInstruction); +}; + template<size_t N> class HTemplateInstruction: public HInstruction { public: @@ -2433,15 +2471,19 @@ class HReturn FINAL : public HTemplateInstruction<1> { DISALLOW_COPY_AND_ASSIGN(HReturn); }; -class HPhi FINAL : public HInstruction { +class HPhi FINAL : public HVariableInputSizeInstruction { public: HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type, uint32_t dex_pc = kNoDexPc) - : HInstruction(SideEffects::None(), dex_pc), - inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)), + : HVariableInputSizeInstruction( + SideEffects::None(), + dex_pc, + arena, + number_of_inputs, + kArenaAllocPhiInputs), reg_number_(reg_number) { SetPackedField<TypeField>(ToPhiType(type)); DCHECK_NE(GetType(), Primitive::kPrimVoid); @@ -2459,14 +2501,6 @@ class HPhi FINAL : public HInstruction { bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); } - using HInstruction::GetInputRecords; // Keep the const version visible. - ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL { - return ArrayRef<HUserRecord<HInstruction*>>(inputs_); - } - - void AddInput(HInstruction* input); - void RemoveInputAt(size_t index); - Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); } void SetType(Primitive::Type new_type) { // Make sure that only valid type changes occur. The following are allowed: @@ -2522,7 +2556,6 @@ class HPhi FINAL : public HInstruction { static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>; - ArenaVector<HUserRecord<HInstruction*>> inputs_; const uint32_t reg_number_; DISALLOW_COPY_AND_ASSIGN(HPhi); @@ -2899,14 +2932,20 @@ class HDeoptimize FINAL : public HTemplateInstruction<1> { // if it's true, starts to do deoptimization. // It has a 4-byte slot on stack. // TODO: allocate a register for this flag. -class HShouldDeoptimizeFlag FINAL : public HExpression<0> { +class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction { public: - // TODO: use SideEffects to aid eliminating some CHA guards. - explicit HShouldDeoptimizeFlag(uint32_t dex_pc) - : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) { + // CHA guards are only optimized in a separate pass and it has no side effects + // with regard to other passes. + HShouldDeoptimizeFlag(ArenaAllocator* arena, uint32_t dex_pc) + : HVariableInputSizeInstruction(SideEffects::None(), dex_pc, arena, 0, kArenaAllocCHA) { } - // We don't eliminate CHA guards yet. + Primitive::Type GetType() const OVERRIDE { return Primitive::kPrimInt; } + + // We do all CHA guard elimination/motion in a single pass, after which there is no + // further guard elimination/motion since a guard might have been used for justification + // of the elimination of another guard. Therefore, we pretend this guard cannot be moved + // to avoid other optimizations trying to move it. bool CanBeMoved() const OVERRIDE { return false; } DECLARE_INSTRUCTION(ShouldDeoptimizeFlag); @@ -3786,15 +3825,10 @@ enum IntrinsicExceptions { kCanThrow // Intrinsic may throw exceptions. }; -class HInvoke : public HInstruction { +class HInvoke : public HVariableInputSizeInstruction { public: bool NeedsEnvironment() const OVERRIDE; - using HInstruction::GetInputRecords; // Keep the const version visible. - ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE { - return ArrayRef<HUserRecord<HInstruction*>>(inputs_); - } - void SetArgumentAt(size_t index, HInstruction* argument) { SetRawInputAt(index, argument); } @@ -3873,12 +3907,14 @@ class HInvoke : public HInstruction { uint32_t dex_method_index, ArtMethod* resolved_method, InvokeType invoke_type) - : HInstruction( - SideEffects::AllExceptGCDependency(), dex_pc), // Assume write/read on all fields/arrays. + : HVariableInputSizeInstruction( + SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. + dex_pc, + arena, + number_of_arguments + number_of_other_inputs, + kArenaAllocInvokeInputs), number_of_arguments_(number_of_arguments), resolved_method_(resolved_method), - inputs_(number_of_arguments + number_of_other_inputs, - arena->Adapter(kArenaAllocInvokeInputs)), dex_method_index_(dex_method_index), intrinsic_(Intrinsics::kNone), intrinsic_optimizations_(0) { @@ -3889,7 +3925,6 @@ class HInvoke : public HInstruction { uint32_t number_of_arguments_; ArtMethod* const resolved_method_; - ArenaVector<HUserRecord<HInstruction*>> inputs_; const uint32_t dex_method_index_; Intrinsics intrinsic_; @@ -3947,12 +3982,6 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { // Used for app->boot calls with non-relocatable image and for JIT-compiled calls. kDirectAddress, - // Use ArtMethod* at an address that will be known at link time, embed the direct - // address in the code. If the image is relocatable, emit .patch_oat entry. - // Used for app->boot calls with relocatable image and boot->boot calls, whether - // the image relocatable or not. - kDirectAddressWithFixup, - // Load from resolved methods array in the dex cache using a PC-relative load. // Used when we need to use the dex cache, for example for invoke-static that // may cause class initialization (the entry may point to a resolution method), @@ -3971,20 +4000,6 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { // Recursive call, use local PC-relative call instruction. kCallSelf, - // Use PC-relative call instruction patched at link time. - // Used for calls within an oat file, boot->boot or app->app. - kCallPCRelative, - - // Call to a known target address, embed the direct address in code. - // Used for app->boot call with non-relocatable image and for JIT-compiled calls. - kCallDirect, - - // Call to a target address that will be known at link time, embed the direct - // address in code. If the image is relocatable, emit .patch_oat entry. - // Used for app->boot calls with relocatable image and boot->boot calls, whether - // the image relocatable or not. - kCallDirectWithFixup, - // Use code pointer from the ArtMethod*. // Used when we don't know the target code. This is also the last-resort-kind used when // other kinds are unimplemented or impractical (i.e. slow) on a particular architecture. @@ -4000,7 +4015,6 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { // - the method address for kDirectAddress // - the dex cache arrays offset for kDexCachePcRel. uint64_t method_load_data; - uint64_t direct_code_ptr; }; HInvokeStaticOrDirect(ArenaAllocator* arena, @@ -4110,7 +4124,6 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { return false; } } - bool HasDirectCodePtr() const { return GetCodePtrLocation() == CodePtrLocation::kCallDirect; } QuickEntrypointEnum GetStringInitEntryPoint() const { DCHECK(IsStringInit()); @@ -4127,11 +4140,6 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { return dispatch_info_.method_load_data; } - uint64_t GetDirectCodePtr() const { - DCHECK(HasDirectCodePtr()); - return dispatch_info_.direct_code_ptr; - } - ClinitCheckRequirement GetClinitCheckRequirement() const { return GetPackedField<ClinitCheckRequirementField>(); } @@ -4179,10 +4187,6 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { DECLARE_INSTRUCTION(InvokeStaticOrDirect); - protected: - void InsertInputAt(size_t index, HInstruction* input); - void RemoveInputAt(size_t index); - private: static constexpr size_t kFieldClinitCheckRequirement = kNumberOfInvokePackedBits; static constexpr size_t kFieldClinitCheckRequirementSize = @@ -5493,9 +5497,8 @@ class HLoadClass FINAL : public HInstruction { // GetIncludePatchInformation(). kBootImageAddress, - // Load from the resolved types array at an absolute address. - // Used for classes outside the boot image referenced by JIT-compiled code. - kDexCacheAddress, + // Load from the root table associated with the JIT compiled method. + kJitTableAddress, // Load from resolved types array in the dex cache using a PC-relative load. // Used for classes outside boot image when we know that we can access @@ -5515,9 +5518,7 @@ class HLoadClass FINAL : public HInstruction { const DexFile& dex_file, bool is_referrers_class, uint32_t dex_pc, - bool needs_access_check, - bool is_in_dex_cache, - bool is_in_boot_image) + bool needs_access_check) : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc), special_input_(HUserRecord<HInstruction*>(current_method)), type_index_(type_index), @@ -5530,8 +5531,8 @@ class HLoadClass FINAL : public HInstruction { SetPackedField<LoadKindField>( is_referrers_class ? LoadKind::kReferrersClass : LoadKind::kDexCacheViaMethod); SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check); - SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache); - SetPackedFlag<kFlagIsInBootImage>(is_in_boot_image); + SetPackedFlag<kFlagIsInDexCache>(false); + SetPackedFlag<kFlagIsInBootImage>(false); SetPackedFlag<kFlagGenerateClInitCheck>(false); } @@ -5588,7 +5589,6 @@ class HLoadClass FINAL : public HInstruction { NeedsAccessCheck(); } - bool CanThrow() const OVERRIDE { return CanCallRuntime(); } @@ -5613,7 +5613,9 @@ class HLoadClass FINAL : public HInstruction { return load_data_.address; } - bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !IsReferrersClass(); } + bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { + return !IsReferrersClass(); + } static SideEffects SideEffectsForArchRuntimeCalls() { return SideEffects::CanTriggerGC(); @@ -5672,7 +5674,8 @@ class HLoadClass FINAL : public HInstruction { } static bool HasAddress(LoadKind load_kind) { - return load_kind == LoadKind::kBootImageAddress || load_kind == LoadKind::kDexCacheAddress; + return load_kind == LoadKind::kBootImageAddress || + load_kind == LoadKind::kJitTableAddress; } static bool HasDexCacheReference(LoadKind load_kind) { @@ -5691,7 +5694,7 @@ class HLoadClass FINAL : public HInstruction { union { uint32_t dex_cache_element_index; // Only for dex cache reference. - uint64_t address; // Up to 64-bit, needed for kDexCacheAddress on 64-bit targets. + uint64_t address; // Up to 64-bit, needed for kJitTableAddress on 64-bit targets. } load_data_; ReferenceTypeInfo loaded_class_rti_; diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc index 013e110b87..0e02311672 100644 --- a/compiler/optimizing/optimizing_cfi_test.cc +++ b/compiler/optimizing/optimizing_cfi_test.cc @@ -24,12 +24,22 @@ #include "optimizing/code_generator.h" #include "optimizing/optimizing_unit_test.h" #include "utils/assembler.h" +#ifdef ART_USE_VIXL_ARM_BACKEND +#include "utils/arm/assembler_arm_vixl.h" +#else #include "utils/arm/assembler_thumb2.h" +#endif #include "utils/mips/assembler_mips.h" #include "utils/mips64/assembler_mips64.h" #include "optimizing/optimizing_cfi_test_expected.inc" +#ifdef ART_USE_VIXL_ARM_BACKEND +namespace vixl32 = vixl::aarch32; + +using vixl32::r0; +#endif + namespace art { // Run the tests only on host. @@ -158,8 +168,7 @@ class OptimizingCFITest : public CFITest { TestImpl(isa, #isa, expected_asm, expected_cfi); \ } -// TODO(VIXL): Support this test for the VIXL backend. -#if defined(ART_ENABLE_CODEGEN_arm) && !defined(ART_USE_VIXL_ARM_BACKEND) +#ifdef ART_ENABLE_CODEGEN_arm TEST_ISA(kThumb2) #endif #ifdef ART_ENABLE_CODEGEN_arm64 @@ -178,8 +187,7 @@ TEST_ISA(kMips) TEST_ISA(kMips64) #endif -// TODO(VIXL): Support this test for the VIXL backend. -#if defined(ART_ENABLE_CODEGEN_arm) && !defined(ART_USE_VIXL_ARM_BACKEND) +#ifdef ART_ENABLE_CODEGEN_arm TEST_F(OptimizingCFITest, kThumb2Adjust) { std::vector<uint8_t> expected_asm( expected_asm_kThumb2_adjust, @@ -188,6 +196,16 @@ TEST_F(OptimizingCFITest, kThumb2Adjust) { expected_cfi_kThumb2_adjust, expected_cfi_kThumb2_adjust + arraysize(expected_cfi_kThumb2_adjust)); SetUpFrame(kThumb2); +#ifdef ART_USE_VIXL_ARM_BACKEND +#define __ down_cast<arm::ArmVIXLAssembler*>(GetCodeGenerator() \ + ->GetAssembler())->GetVIXLAssembler()-> + vixl32::Label target; + __ CompareAndBranchIfZero(r0, &target); + // Push the target out of range of CBZ. + for (size_t i = 0; i != 65; ++i) { + __ Ldr(r0, vixl32::MemOperand(r0)); + } +#else #define __ down_cast<arm::Thumb2Assembler*>(GetCodeGenerator()->GetAssembler())-> Label target; __ CompareAndBranchIfZero(arm::R0, &target); @@ -195,6 +213,7 @@ TEST_F(OptimizingCFITest, kThumb2Adjust) { for (size_t i = 0; i != 65; ++i) { __ ldr(arm::R0, arm::Address(arm::R0)); } +#endif __ Bind(&target); #undef __ Finish(); diff --git a/compiler/optimizing/optimizing_cfi_test_expected.inc b/compiler/optimizing/optimizing_cfi_test_expected.inc index f735dc8cb3..82670c38fe 100644 --- a/compiler/optimizing/optimizing_cfi_test_expected.inc +++ b/compiler/optimizing/optimizing_cfi_test_expected.inc @@ -223,8 +223,16 @@ static constexpr uint8_t expected_cfi_kMips64[] = { // 0x00000040: .cfi_def_cfa_offset: 64 static constexpr uint8_t expected_asm_kThumb2_adjust[] = { +#ifdef ART_USE_VIXL_ARM_BACKEND + // VIXL emits an extra 2 bytes here for a 32-bit beq as there is no + // optimistic 16-bit emit and subsequent fixup for out of reach targets + // as with the current assembler. + 0x60, 0xB5, 0x2D, 0xED, 0x02, 0x8A, 0x8B, 0xB0, 0x00, 0x28, 0x00, 0xF0, + 0x41, 0x80, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, +#else 0x60, 0xB5, 0x2D, 0xED, 0x02, 0x8A, 0x8B, 0xB0, 0x00, 0x28, 0x40, 0xD0, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, +#endif 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, 0x00, 0x68, @@ -239,7 +247,11 @@ static constexpr uint8_t expected_asm_kThumb2_adjust[] = { }; static constexpr uint8_t expected_cfi_kThumb2_adjust[] = { 0x42, 0x0E, 0x0C, 0x85, 0x03, 0x86, 0x02, 0x8E, 0x01, 0x44, 0x0E, 0x14, +#ifdef ART_USE_VIXL_ARM_BACKEND + 0x05, 0x50, 0x05, 0x05, 0x51, 0x04, 0x42, 0x0E, 0x40, 0x02, 0x88, 0x0A, +#else 0x05, 0x50, 0x05, 0x05, 0x51, 0x04, 0x42, 0x0E, 0x40, 0x02, 0x86, 0x0A, +#endif 0x42, 0x0E, 0x14, 0x44, 0x0E, 0x0C, 0x06, 0x50, 0x06, 0x51, 0x42, 0x0B, 0x0E, 0x40, }; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 8ea2b06530..4bf5b080a7 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -22,6 +22,8 @@ #include <stdint.h> +#include "android-base/strings.h" + #ifdef ART_ENABLE_CODEGEN_arm #include "dex_cache_array_fixups_arm.h" #endif @@ -52,6 +54,7 @@ #include "base/timing_logger.h" #include "bounds_check_elimination.h" #include "builder.h" +#include "cha_guard_optimization.h" #include "code_generator.h" #include "compiled_method.h" #include "compiler.h" @@ -375,7 +378,8 @@ class OptimizingCompiler FINAL : public Compiler { const DexFile& dex_file, Handle<mirror::DexCache> dex_cache, ArtMethod* method, - bool osr) const; + bool osr, + VariableSizedHandleScope* handles) const; void MaybeRunInliner(HGraph* graph, CodeGenerator* codegen, @@ -495,7 +499,7 @@ static HOptimization* BuildOptimization( number_of_dex_registers, /* depth */ 0); } else if (opt_name == HSharpening::kSharpeningPassName) { - return new (arena) HSharpening(graph, codegen, dex_compilation_unit, driver); + return new (arena) HSharpening(graph, codegen, dex_compilation_unit, driver, handles); } else if (opt_name == HSelectGenerator::kSelectGeneratorPassName) { return new (arena) HSelectGenerator(graph, stats); } else if (opt_name == HInductionVarAnalysis::kInductionPassName) { @@ -514,6 +518,8 @@ static HOptimization* BuildOptimization( return new (arena) SideEffectsAnalysis(graph); } else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) { return new (arena) HLoopOptimization(graph, most_recent_induction); + } else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) { + return new (arena) CHAGuardOptimization(graph); #ifdef ART_ENABLE_CODEGEN_arm } else if (opt_name == arm::DexCacheArrayFixups::kDexCacheArrayFixupsArmPassName) { return new (arena) arm::DexCacheArrayFixups(graph, codegen, stats); @@ -767,7 +773,8 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, HInductionVarAnalysis* induction = new (arena) HInductionVarAnalysis(graph); BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, *side_effects, induction); HLoopOptimization* loop = new (arena) HLoopOptimization(graph, induction); - HSharpening* sharpening = new (arena) HSharpening(graph, codegen, dex_compilation_unit, driver); + HSharpening* sharpening = new (arena) HSharpening( + graph, codegen, dex_compilation_unit, driver, handles); InstructionSimplifier* simplify2 = new (arena) InstructionSimplifier( graph, stats, "instruction_simplifier$after_inlining"); InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier( @@ -775,6 +782,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, InstructionSimplifier* simplify4 = new (arena) InstructionSimplifier( graph, stats, "instruction_simplifier$before_codegen"); IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, stats); + CHAGuardOptimization* cha_guard = new (arena) CHAGuardOptimization(graph); HOptimization* optimizations1[] = { intrinsics, @@ -803,6 +811,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, fold3, // evaluates code generated by dynamic bce simplify3, lse, + cha_guard, dce3, // The codegen has a few assumptions that only the instruction simplifier // can satisfy. For example, the code generator does not expect to see a @@ -866,7 +875,8 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache, ArtMethod* method, - bool osr) const { + bool osr, + VariableSizedHandleScope* handles) const { MaybeRecordStat(MethodCompilationStat::kAttemptCompilation); CompilerDriver* compiler_driver = GetCompilerDriver(); InstructionSet instruction_set = compiler_driver->GetInstructionSet(); @@ -976,63 +986,55 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, compiler_driver, dump_mutex_); - VLOG(compiler) << "Building " << pass_observer.GetMethodName(); - { - ScopedObjectAccess soa(Thread::Current()); - VariableSizedHandleScope handles(soa.Self()); - // Do not hold `mutator_lock_` between optimizations. - ScopedThreadSuspension sts(soa.Self(), kNative); - - { - PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); - HGraphBuilder builder(graph, - &dex_compilation_unit, - &dex_compilation_unit, - &dex_file, - *code_item, - compiler_driver, - compilation_stats_.get(), - interpreter_metadata, - dex_cache, - &handles); - GraphAnalysisResult result = builder.BuildGraph(); - if (result != kAnalysisSuccess) { - switch (result) { - case kAnalysisSkipped: - MaybeRecordStat(MethodCompilationStat::kNotCompiledSkipped); - break; - case kAnalysisInvalidBytecode: - MaybeRecordStat(MethodCompilationStat::kNotCompiledInvalidBytecode); - break; - case kAnalysisFailThrowCatchLoop: - MaybeRecordStat(MethodCompilationStat::kNotCompiledThrowCatchLoop); - break; - case kAnalysisFailAmbiguousArrayOp: - MaybeRecordStat(MethodCompilationStat::kNotCompiledAmbiguousArrayOp); - break; - case kAnalysisSuccess: - UNREACHABLE(); - } - pass_observer.SetGraphInBadState(); - return nullptr; + VLOG(compiler) << "Building " << pass_observer.GetMethodName(); + PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); + HGraphBuilder builder(graph, + &dex_compilation_unit, + &dex_compilation_unit, + &dex_file, + *code_item, + compiler_driver, + compilation_stats_.get(), + interpreter_metadata, + dex_cache, + handles); + GraphAnalysisResult result = builder.BuildGraph(); + if (result != kAnalysisSuccess) { + switch (result) { + case kAnalysisSkipped: + MaybeRecordStat(MethodCompilationStat::kNotCompiledSkipped); + break; + case kAnalysisInvalidBytecode: + MaybeRecordStat(MethodCompilationStat::kNotCompiledInvalidBytecode); + break; + case kAnalysisFailThrowCatchLoop: + MaybeRecordStat(MethodCompilationStat::kNotCompiledThrowCatchLoop); + break; + case kAnalysisFailAmbiguousArrayOp: + MaybeRecordStat(MethodCompilationStat::kNotCompiledAmbiguousArrayOp); + break; + case kAnalysisSuccess: + UNREACHABLE(); } + pass_observer.SetGraphInBadState(); + return nullptr; } + } - RunOptimizations(graph, - codegen.get(), - compiler_driver, - dex_compilation_unit, - &pass_observer, - &handles); + RunOptimizations(graph, + codegen.get(), + compiler_driver, + dex_compilation_unit, + &pass_observer, + handles); - RegisterAllocator::Strategy regalloc_strategy = - compiler_options.GetRegisterAllocationStrategy(); - AllocateRegisters(graph, codegen.get(), &pass_observer, regalloc_strategy); + RegisterAllocator::Strategy regalloc_strategy = + compiler_options.GetRegisterAllocationStrategy(); + AllocateRegisters(graph, codegen.get(), &pass_observer, regalloc_strategy); - codegen->Compile(code_allocator); - pass_observer.DumpDisassembly(); - } + codegen->Compile(code_allocator); + pass_observer.DumpDisassembly(); return codegen.release(); } @@ -1055,19 +1057,27 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, verified_method->GetEncounteredVerificationFailures())) { ArenaAllocator arena(Runtime::Current()->GetArenaPool()); CodeVectorAllocator code_allocator(&arena); - std::unique_ptr<CodeGenerator> codegen( - TryCompile(&arena, - &code_allocator, - code_item, - access_flags, - invoke_type, - class_def_idx, - method_idx, - jclass_loader, - dex_file, - dex_cache, - nullptr, - /* osr */ false)); + std::unique_ptr<CodeGenerator> codegen; + { + ScopedObjectAccess soa(Thread::Current()); + VariableSizedHandleScope handles(soa.Self()); + // Go to native so that we don't block GC during compilation. + ScopedThreadSuspension sts(soa.Self(), kNative); + codegen.reset( + TryCompile(&arena, + &code_allocator, + code_item, + access_flags, + invoke_type, + class_def_idx, + method_idx, + jclass_loader, + dex_file, + dex_cache, + nullptr, + /* osr */ false, + &handles)); + } if (codegen.get() != nullptr) { MaybeRecordStat(MethodCompilationStat::kCompiled); method = Emit(&arena, &code_allocator, codegen.get(), compiler_driver, code_item); @@ -1112,7 +1122,8 @@ Compiler* CreateOptimizingCompiler(CompilerDriver* driver) { bool IsCompilingWithCoreImage() { const std::string& image = Runtime::Current()->GetImageLocation(); // TODO: This is under-approximating... - if (EndsWith(image, "core.art") || EndsWith(image, "core-optimizing.art")) { + if (android::base::EndsWith(image, "core.art") || + android::base::EndsWith(image, "core-optimizing.art")) { return true; } return false; @@ -1138,6 +1149,8 @@ bool OptimizingCompiler::JitCompile(Thread* self, ArenaAllocator arena(Runtime::Current()->GetJitArenaPool()); CodeVectorAllocator code_allocator(&arena); + VariableSizedHandleScope handles(self); + std::unique_ptr<CodeGenerator> codegen; { // Go to native so that we don't block GC during compilation. @@ -1154,7 +1167,8 @@ bool OptimizingCompiler::JitCompile(Thread* self, *dex_file, dex_cache, method, - osr)); + osr, + &handles)); if (codegen.get() == nullptr) { return false; } @@ -1184,7 +1198,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, } uint8_t* stack_map_data = nullptr; uint8_t* roots_data = nullptr; - code_cache->ReserveData( + uint32_t data_size = code_cache->ReserveData( self, stack_map_size, number_of_roots, method, &stack_map_data, &roots_data); if (stack_map_data == nullptr || roots_data == nullptr) { return false; @@ -1203,6 +1217,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, codegen->GetFpuSpillMask(), code_allocator.GetMemory().data(), code_allocator.GetSize(), + data_size, osr, roots, codegen->GetGraph()->HasShouldDeoptimizeFlag(), diff --git a/compiler/optimizing/pc_relative_fixups_mips.cc b/compiler/optimizing/pc_relative_fixups_mips.cc index 82feb95a2f..e321b9e3aa 100644 --- a/compiler/optimizing/pc_relative_fixups_mips.cc +++ b/compiler/optimizing/pc_relative_fixups_mips.cc @@ -45,10 +45,6 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { } private: - void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE { - HandleInvoke(invoke); - } - void InitializePCRelativeBasePointer() { // Ensure we only initialize the pointer once. if (base_ != nullptr) { @@ -112,38 +108,6 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { block->ReplaceAndRemoveInstructionWith(switch_insn, mips_switch); } - void HandleInvoke(HInvoke* invoke) { - // If this is an invoke-static/-direct with PC-relative dex cache array - // addressing, we need the PC-relative address base. - HInvokeStaticOrDirect* invoke_static_or_direct = invoke->AsInvokeStaticOrDirect(); - if (invoke_static_or_direct != nullptr) { - HInvokeStaticOrDirect::MethodLoadKind method_load_kind = - invoke_static_or_direct->GetMethodLoadKind(); - HInvokeStaticOrDirect::CodePtrLocation code_ptr_location = - invoke_static_or_direct->GetCodePtrLocation(); - - bool has_extra_input = - (method_load_kind == HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup) || - (code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup); - - // We can't add a pointer to the constant area if we already have a current - // method pointer. This may arise when sharpening doesn't remove the current - // method pointer from the invoke. - if (invoke_static_or_direct->HasCurrentMethodInput()) { - DCHECK(!invoke_static_or_direct->HasPcRelativeDexCache()); - CHECK(!has_extra_input); - return; - } - - if (has_extra_input && - !IsCallFreeIntrinsic<IntrinsicLocationsBuilderMIPS>(invoke, codegen_)) { - InitializePCRelativeBasePointer(); - // Add the extra parameter base_. - invoke_static_or_direct->AddSpecialInput(base_); - } - } - } - CodeGeneratorMIPS* codegen_; // The generated HMipsComputeBaseMethodAddress in the entry block needed as an diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h index 5891350894..c6579dc5e0 100644 --- a/compiler/optimizing/pretty_printer.h +++ b/compiler/optimizing/pretty_printer.h @@ -17,7 +17,8 @@ #ifndef ART_COMPILER_OPTIMIZING_PRETTY_PRINTER_H_ #define ART_COMPILER_OPTIMIZING_PRETTY_PRINTER_H_ -#include "base/stringprintf.h" +#include "android-base/stringprintf.h" + #include "nodes.h" namespace art { @@ -108,7 +109,7 @@ class StringPrettyPrinter : public HPrettyPrinter { : HPrettyPrinter(graph), str_(""), current_block_(nullptr) { } void PrintInt(int value) OVERRIDE { - str_ += StringPrintf("%d", value); + str_ += android::base::StringPrintf("%d", value); } void PrintString(const char* value) OVERRIDE { diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc index 951cdfbd8b..1af94f3445 100644 --- a/compiler/optimizing/pretty_printer_test.cc +++ b/compiler/optimizing/pretty_printer_test.cc @@ -15,7 +15,6 @@ */ #include "base/arena_allocator.h" -#include "base/stringprintf.h" #include "builder.h" #include "dex_file.h" #include "dex_instruction.h" diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index c191c6651f..33b3875e3b 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -270,7 +270,7 @@ void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) { ScopedObjectAccess soa(Thread::Current()); HInstruction* insert_point = notNullBlock->GetFirstInstruction(); ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create( - handle_cache_.GetObjectClassHandle(), /* is_exact */ true); + handle_cache_.GetObjectClassHandle(), /* is_exact */ false); if (ShouldCreateBoundType(insert_point, obj, object_rti, nullptr, notNullBlock)) { bound_type = new (graph_->GetArena()) HBoundType(obj); bound_type->SetUpperBound(object_rti, /* bound_can_be_null */ false); @@ -411,7 +411,9 @@ void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) { HInstruction* insert_point = instanceOfTrueBlock->GetFirstInstruction(); if (ShouldCreateBoundType(insert_point, obj, class_rti, nullptr, instanceOfTrueBlock)) { bound_type = new (graph_->GetArena()) HBoundType(obj); - bound_type->SetUpperBound(class_rti, /* InstanceOf fails for null. */ false); + bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes(); + bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact), + /* InstanceOf fails for null. */ false); instanceOfTrueBlock->InsertInstructionBefore(bound_type, insert_point); } else { // We already have a bound type on the position we would need to insert @@ -605,15 +607,17 @@ void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) { // Narrow the type as much as possible. HInstruction* obj = instr->InputAt(0); ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo(); - if (class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) { - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ true)); + if (class_rti.IsExact()) { + instr->SetReferenceTypeInfo(class_rti); } else if (obj_rti.IsValid()) { if (class_rti.IsSupertypeOf(obj_rti)) { // Object type is more specific. instr->SetReferenceTypeInfo(obj_rti); } else { - // Upper bound is more specific. + // Upper bound is more specific, or unrelated to the object's type. + // Note that the object might then be exact, and we know the code dominated by this + // bound type is dead. To not confuse potential other optimizations, we mark + // the bound as non-exact. instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ false)); } @@ -644,8 +648,11 @@ void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast if (class_rti.IsValid()) { DCHECK(is_first_run_); + ScopedObjectAccess soa(Thread::Current()); // This is the first run of RTP and class is resolved. - bound_type->SetUpperBound(class_rti, /* CheckCast succeeds for nulls. */ true); + bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes(); + bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact), + /* CheckCast succeeds for nulls. */ true); } else { // This is the first run of RTP and class is unresolved. Remove the binding. // The instruction itself is removed in VisitBoundType so as to not @@ -795,21 +802,25 @@ void ReferenceTypePropagation::RTPVisitor::VisitArrayGet(HArrayGet* instr) { } void ReferenceTypePropagation::UpdateBoundType(HBoundType* instr) { - ReferenceTypeInfo new_rti = instr->InputAt(0)->GetReferenceTypeInfo(); - if (!new_rti.IsValid()) { + ReferenceTypeInfo input_rti = instr->InputAt(0)->GetReferenceTypeInfo(); + if (!input_rti.IsValid()) { return; // No new info yet. } - // Make sure that we don't go over the bounded type. ReferenceTypeInfo upper_bound_rti = instr->GetUpperBound(); - if (!upper_bound_rti.IsSupertypeOf(new_rti)) { - // Note that the input might be exact, in which case we know the branch leading - // to the bound type is dead. We play it safe by not marking the bound type as - // exact. - bool is_exact = upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes(); - new_rti = ReferenceTypeInfo::Create(upper_bound_rti.GetTypeHandle(), is_exact); - } - instr->SetReferenceTypeInfo(new_rti); + if (upper_bound_rti.IsExact()) { + instr->SetReferenceTypeInfo(upper_bound_rti); + } else if (upper_bound_rti.IsSupertypeOf(input_rti)) { + // input is more specific. + instr->SetReferenceTypeInfo(input_rti); + } else { + // upper_bound is more specific or unrelated. + // Note that the object might then be exact, and we know the code dominated by this + // bound type is dead. To not confuse potential other optimizations, we mark + // the bound as non-exact. + instr->SetReferenceTypeInfo( + ReferenceTypeInfo::Create(upper_bound_rti.GetTypeHandle(), /* is_exact */ false)); + } } // NullConstant inputs are ignored during merging as they do not provide any useful information. diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index daf160a483..9fdeccfa1a 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -54,6 +54,24 @@ void HSharpening::Run() { } } +static bool IsInBootImage(ArtMethod* method) { + const std::vector<gc::space::ImageSpace*>& image_spaces = + Runtime::Current()->GetHeap()->GetBootImageSpaces(); + for (gc::space::ImageSpace* image_space : image_spaces) { + const auto& method_section = image_space->GetImageHeader().GetMethodsSection(); + if (method_section.Contains(reinterpret_cast<uint8_t*>(method) - image_space->Begin())) { + return true; + } + } + return false; +} + +static bool AOTCanEmbedMethod(ArtMethod* method, const CompilerOptions& options) { + // Including patch information means the AOT code will be patched, which we don't + // support in the compiler, and is anyways moving away b/33192586. + return IsInBootImage(method) && !options.GetCompilePic() && !options.GetIncludePatchInformation(); +} + void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { if (invoke->IsStringInit()) { // Not using the dex cache arrays. But we could still try to use a better dispatch... @@ -61,68 +79,42 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { return; } - HGraph* outer_graph = codegen_->GetGraph(); - ArtMethod* compiling_method = graph_->GetArtMethod(); + ArtMethod* callee = invoke->GetResolvedMethod(); + DCHECK(callee != nullptr); HInvokeStaticOrDirect::MethodLoadKind method_load_kind; HInvokeStaticOrDirect::CodePtrLocation code_ptr_location; uint64_t method_load_data = 0u; - uint64_t direct_code_ptr = 0u; - if (invoke->GetResolvedMethod() == outer_graph->GetArtMethod()) { - DCHECK(outer_graph->GetArtMethod() != nullptr); + // Note: we never call an ArtMethod through a known code pointer, as + // we do not want to keep on invoking it if it gets deoptimized. This + // applies to both AOT and JIT. + // This also avoids having to find out if the code pointer of an ArtMethod + // is the resolution trampoline (for ensuring the class is initialized), or + // the interpreter entrypoint. Such code pointers we do not want to call + // directly. + // Only in the case of a recursive call can we call directly, as we know the + // class is initialized already or being initialized, and the call will not + // be invoked once the method is deoptimized. + + if (callee == codegen_->GetGraph()->GetArtMethod()) { + // Recursive call. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive; code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf; + } else if (Runtime::Current()->UseJitCompilation() || + AOTCanEmbedMethod(callee, codegen_->GetCompilerOptions())) { + // JIT or on-device AOT compilation referencing a boot image method. + // Use the method address directly. + method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress; + method_load_data = reinterpret_cast<uintptr_t>(callee); + code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } else { - uintptr_t direct_code, direct_method; - { - ScopedObjectAccess soa(Thread::Current()); - compiler_driver_->GetCodeAndMethodForDirectCall( - (compiling_method == nullptr) ? nullptr : compiling_method->GetDeclaringClass(), - invoke->GetResolvedMethod(), - &direct_code, - &direct_method); - } - if (direct_method != 0u) { // Should we use a direct pointer to the method? - // Note: For JIT, kDirectAddressWithFixup doesn't make sense at all and while - // kDirectAddress would be fine for image methods, we don't support it at the moment. - DCHECK(!Runtime::Current()->UseJitCompilation()); - if (direct_method != static_cast<uintptr_t>(-1)) { // Is the method pointer known now? - method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress; - method_load_data = direct_method; - } else { // The direct pointer will be known at link time. - method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup; - } - } else { // Use dex cache. - if (!Runtime::Current()->UseJitCompilation()) { - // Use PC-relative access to the dex cache arrays. - method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative; - DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()), - &graph_->GetDexFile()); - method_load_data = layout.MethodOffset(invoke->GetDexMethodIndex()); - } else { // We must go through the ArtMethod's pointer to resolved methods. - method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; - } - } - if (direct_code != 0u) { // Should we use a direct pointer to the code? - // Note: For JIT, kCallPCRelative and kCallDirectWithFixup don't make sense at all and - // while kCallDirect would be fine for image methods, we don't support it at the moment. - DCHECK(!Runtime::Current()->UseJitCompilation()); - const DexFile* dex_file_of_callee = invoke->GetTargetMethod().dex_file; - if (direct_code != static_cast<uintptr_t>(-1)) { // Is the code pointer known now? - code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirect; - direct_code_ptr = direct_code; - } else if (ContainsElement(compiler_driver_->GetDexFilesForOatFile(), dex_file_of_callee)) { - // Use PC-relative calls for invokes within a multi-dex oat file. - code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative; - } else { // The direct pointer will be known at link time. - // NOTE: This is used for app->boot calls when compiling an app against - // a relocatable but not yet relocated image. - code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup; - } - } else { // We must use the code pointer from the ArtMethod. - code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; - } + // Use PC-relative access to the dex cache arrays. + method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative; + DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()), + &graph_->GetDexFile()); + method_load_data = layout.MethodOffset(invoke->GetDexMethodIndex()); + code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } if (graph_->IsDebuggable()) { @@ -132,7 +124,7 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { } HInvokeStaticOrDirect::DispatchInfo desired_dispatch_info = { - method_load_kind, code_ptr_location, method_load_data, direct_code_ptr + method_load_kind, code_ptr_location, method_load_data }; HInvokeStaticOrDirect::DispatchInfo dispatch_info = codegen_->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke); @@ -140,6 +132,25 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { } void HSharpening::ProcessLoadClass(HLoadClass* load_class) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + const DexFile& dex_file = load_class->GetDexFile(); + dex::TypeIndex type_index = load_class->GetTypeIndex(); + Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile()) + ? compilation_unit_.GetDexCache() + : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); + mirror::Class* cls = dex_cache->GetResolvedType(type_index); + SharpenClass(load_class, cls, handles_, codegen_, compiler_driver_); +} + +void HSharpening::SharpenClass(HLoadClass* load_class, + mirror::Class* klass, + VariableSizedHandleScope* handles, + CodeGenerator* codegen, + CompilerDriver* compiler_driver) { + ScopedAssertNoThreadSuspension sants("Sharpening class in compiler"); DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCacheViaMethod || load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) << load_class->GetLoadKind(); @@ -151,70 +162,65 @@ void HSharpening::ProcessLoadClass(HLoadClass* load_class) { bool is_in_dex_cache = false; bool is_in_boot_image = false; - HLoadClass::LoadKind desired_load_kind; + HLoadClass::LoadKind desired_load_kind = static_cast<HLoadClass::LoadKind>(-1); uint64_t address = 0u; // Class or dex cache element address. - { - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); - Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile()) - ? compilation_unit_.GetDexCache() - : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); - mirror::Class* klass = dex_cache->GetResolvedType(type_index); - if (codegen_->GetCompilerOptions().IsBootImage()) { - // Compiling boot image. Check if the class is a boot image class. - DCHECK(!runtime->UseJitCompilation()); - if (!compiler_driver_->GetSupportBootImageFixup()) { - // MIPS64 or compiler_driver_test. Do not sharpen. - desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; - } else if ((klass != nullptr) && compiler_driver_->IsImageClass( - dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) { - is_in_boot_image = true; - is_in_dex_cache = true; - desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic() - ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative - : HLoadClass::LoadKind::kBootImageLinkTimeAddress; - } else { - // Not a boot image class. We must go through the dex cache. - DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file)); - desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative; - } + Runtime* runtime = Runtime::Current(); + if (codegen->GetCompilerOptions().IsBootImage()) { + // Compiling boot image. Check if the class is a boot image class. + DCHECK(!runtime->UseJitCompilation()); + if (!compiler_driver->GetSupportBootImageFixup()) { + // MIPS64 or compiler_driver_test. Do not sharpen. + desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; + } else if ((klass != nullptr) && compiler_driver->IsImageClass( + dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) { + is_in_boot_image = true; + is_in_dex_cache = true; + desired_load_kind = codegen->GetCompilerOptions().GetCompilePic() + ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative + : HLoadClass::LoadKind::kBootImageLinkTimeAddress; } else { - is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass); - if (runtime->UseJitCompilation()) { - // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. - // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); - is_in_dex_cache = (klass != nullptr); - if (is_in_boot_image) { - // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787 - desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; - address = reinterpret_cast64<uint64_t>(klass); - } else { - // Note: If the class is not in the dex cache or isn't initialized, the - // instruction needs environment and will not be inlined across dex files. - // Within a dex file, the slow-path helper loads the correct class and - // inlined frames are used correctly for OOM stack trace. - // TODO: Write a test for this. Bug: 29416588 - desired_load_kind = HLoadClass::LoadKind::kDexCacheAddress; - void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index.index_]; - address = reinterpret_cast64<uint64_t>(dex_cache_element_address); - } - // AOT app compilation. Check if the class is in the boot image. - } else if (is_in_boot_image && !codegen_->GetCompilerOptions().GetCompilePic()) { + // Not a boot image class. We must go through the dex cache. + DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file)); + desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative; + } + } else { + is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass); + if (runtime->UseJitCompilation()) { + // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. + // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); + is_in_dex_cache = (klass != nullptr); + if (is_in_boot_image) { + // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787 desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; address = reinterpret_cast64<uint64_t>(klass); + } else if (is_in_dex_cache) { + desired_load_kind = HLoadClass::LoadKind::kJitTableAddress; + // We store in the address field the location of the stack reference maintained + // by the handle. We do this now so that the code generation does not need to figure + // out which class loader to use. + address = reinterpret_cast<uint64_t>(handles->NewHandle(klass).GetReference()); } else { - // Not JIT and either the klass is not in boot image or we are compiling in PIC mode. - // Use PC-relative load from the dex cache if the dex file belongs - // to the oat file that we're currently compiling. - desired_load_kind = - ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &load_class->GetDexFile()) - ? HLoadClass::LoadKind::kDexCachePcRelative - : HLoadClass::LoadKind::kDexCacheViaMethod; + // Class not loaded yet. This happens when the dex code requesting + // this `HLoadClass` hasn't been executed in the interpreter. + // Fallback to the dex cache. + // TODO(ngeoffray): Generate HDeoptimize instead. + desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; } + } else if (is_in_boot_image && !codegen->GetCompilerOptions().GetCompilePic()) { + // AOT app compilation. Check if the class is in the boot image. + desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; + address = reinterpret_cast64<uint64_t>(klass); + } else { + // Not JIT and either the klass is not in boot image or we are compiling in PIC mode. + // Use PC-relative load from the dex cache if the dex file belongs + // to the oat file that we're currently compiling. + desired_load_kind = + ContainsElement(compiler_driver->GetDexFilesForOatFile(), &load_class->GetDexFile()) + ? HLoadClass::LoadKind::kDexCachePcRelative + : HLoadClass::LoadKind::kDexCacheViaMethod; } } + DCHECK_NE(desired_load_kind, static_cast<HLoadClass::LoadKind>(-1)); if (is_in_boot_image) { load_class->MarkInBootImage(); @@ -237,7 +243,7 @@ void HSharpening::ProcessLoadClass(HLoadClass* load_class) { load_class->MarkInDexCache(); } - HLoadClass::LoadKind load_kind = codegen_->GetSupportedLoadClassKind(desired_load_kind); + HLoadClass::LoadKind load_kind = codegen->GetSupportedLoadClassKind(desired_load_kind); switch (load_kind) { case HLoadClass::LoadKind::kBootImageLinkTimeAddress: case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: @@ -245,12 +251,12 @@ void HSharpening::ProcessLoadClass(HLoadClass* load_class) { load_class->SetLoadKindWithTypeReference(load_kind, dex_file, type_index); break; case HLoadClass::LoadKind::kBootImageAddress: - case HLoadClass::LoadKind::kDexCacheAddress: + case HLoadClass::LoadKind::kJitTableAddress: DCHECK_NE(address, 0u); load_class->SetLoadKindWithAddress(load_kind, address); break; case HLoadClass::LoadKind::kDexCachePcRelative: { - PointerSize pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet()); + PointerSize pointer_size = InstructionSetPointerSize(codegen->GetInstructionSet()); DexCacheArraysLayout layout(pointer_size, &dex_file); size_t element_index = layout.TypeOffset(type_index); load_class->SetLoadKindWithDexCacheReference(load_kind, dex_file, element_index); diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index d35ae66e05..ae5ccb33ab 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -35,16 +35,26 @@ class HSharpening : public HOptimization { HSharpening(HGraph* graph, CodeGenerator* codegen, const DexCompilationUnit& compilation_unit, - CompilerDriver* compiler_driver) + CompilerDriver* compiler_driver, + VariableSizedHandleScope* handles) : HOptimization(graph, kSharpeningPassName), codegen_(codegen), compilation_unit_(compilation_unit), - compiler_driver_(compiler_driver) { } + compiler_driver_(compiler_driver), + handles_(handles) { } void Run() OVERRIDE; static constexpr const char* kSharpeningPassName = "sharpening"; + // Used internally but also by the inliner. + static void SharpenClass(HLoadClass* load_class, + mirror::Class* klass, + VariableSizedHandleScope* handles, + CodeGenerator* codegen, + CompilerDriver* compiler_driver) + REQUIRES_SHARED(Locks::mutator_lock_); + private: void ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke); void ProcessLoadClass(HLoadClass* load_class); @@ -53,6 +63,7 @@ class HSharpening : public HOptimization { CodeGenerator* codegen_; const DexCompilationUnit& compilation_unit_; CompilerDriver* compiler_driver_; + VariableSizedHandleScope* handles_; }; } // namespace art diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc index 429763423c..f69f417efc 100644 --- a/compiler/optimizing/ssa_test.cc +++ b/compiler/optimizing/ssa_test.cc @@ -14,8 +14,9 @@ * limitations under the License. */ +#include "android-base/stringprintf.h" + #include "base/arena_allocator.h" -#include "base/stringprintf.h" #include "builder.h" #include "dex_file.h" #include "dex_instruction.h" @@ -35,7 +36,7 @@ class SsaPrettyPrinter : public HPrettyPrinter { explicit SsaPrettyPrinter(HGraph* graph) : HPrettyPrinter(graph), str_("") {} void PrintInt(int value) OVERRIDE { - str_ += StringPrintf("%d", value); + str_ += android::base::StringPrintf("%d", value); } void PrintString(const char* value) OVERRIDE { diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc index 1614d04a95..e5eef37b7b 100644 --- a/compiler/utils/arm/assembler_arm_vixl.cc +++ b/compiler/utils/arm/assembler_arm_vixl.cc @@ -23,6 +23,9 @@ using namespace vixl::aarch32; // NOLINT(build/namespaces) +using vixl::ExactAssemblyScope; +using vixl::CodeBufferCheckScope; + namespace art { namespace arm { @@ -441,7 +444,7 @@ void ArmVIXLMacroAssembler::CompareAndBranchIfZero(vixl32::Register rn, return; } Cmp(rn, 0); - B(eq, label); + B(eq, label, is_far_target); } void ArmVIXLMacroAssembler::CompareAndBranchIfNonZero(vixl32::Register rn, @@ -452,16 +455,16 @@ void ArmVIXLMacroAssembler::CompareAndBranchIfNonZero(vixl32::Register rn, return; } Cmp(rn, 0); - B(ne, label); + B(ne, label, is_far_target); } void ArmVIXLMacroAssembler::B(vixl32::Label* label) { if (!label->IsBound()) { // Try to use 16-bit T2 encoding of B instruction. DCHECK(OutsideITBlock()); - AssemblerAccurateScope ass(this, - kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope guard(this, + k16BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); b(al, Narrow, label); AddBranchLabel(label); return; @@ -469,13 +472,22 @@ void ArmVIXLMacroAssembler::B(vixl32::Label* label) { MacroAssembler::B(label); } -void ArmVIXLMacroAssembler::B(vixl32::Condition cond, vixl32::Label* label) { +void ArmVIXLMacroAssembler::B(vixl32::Condition cond, vixl32::Label* label, bool is_far_target) { + if (!label->IsBound() && !is_far_target) { + // Try to use 16-bit T2 encoding of B instruction. + DCHECK(OutsideITBlock()); + ExactAssemblyScope guard(this, + k16BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); + b(cond, Narrow, label); + AddBranchLabel(label); + return; + } // To further reduce the Bcc encoding size and use 16-bit T1 encoding, // we can provide a hint to this function: i.e. far_target=false. // By default this function uses 'EncodingSizeType::Best' which generates 32-bit T3 encoding. MacroAssembler::B(cond, label); } - } // namespace arm } // namespace art diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h index 17cf1064b0..322f6c4d70 100644 --- a/compiler/utils/arm/assembler_arm_vixl.h +++ b/compiler/utils/arm/assembler_arm_vixl.h @@ -39,6 +39,13 @@ namespace arm { class ArmVIXLMacroAssembler FINAL : public vixl32::MacroAssembler { public: + // Most methods fit in a 1KB code buffer, which results in more optimal alloc/realloc and + // fewer system calls than a larger default capacity. + static constexpr size_t kDefaultCodeBufferCapacity = 1 * KB; + + ArmVIXLMacroAssembler() + : vixl32::MacroAssembler(ArmVIXLMacroAssembler::kDefaultCodeBufferCapacity) {} + // The following interfaces can generate CMP+Bcc or Cbz/Cbnz. // CMP+Bcc are generated by default. // If a hint is given (is_far_target = false) and rn and label can all fit into Cbz/Cbnz, @@ -114,7 +121,7 @@ class ArmVIXLMacroAssembler FINAL : public vixl32::MacroAssembler { // TODO: Remove when MacroAssembler::Add(FlagsUpdate, Condition, Register, Register, Operand) // makes the right decision about 16-bit encodings. void Add(vixl32::Register rd, vixl32::Register rn, const vixl32::Operand& operand) { - if (rd.Is(rn)) { + if (rd.Is(rn) && operand.IsPlainRegister()) { MacroAssembler::Add(rd, rn, operand); } else { MacroAssembler::Add(vixl32::DontCare, rd, rn, operand); @@ -124,7 +131,10 @@ class ArmVIXLMacroAssembler FINAL : public vixl32::MacroAssembler { // These interfaces try to use 16-bit T2 encoding of B instruction. void B(vixl32::Label* label); - void B(vixl32::Condition cond, vixl32::Label* label); + // For B(label), we always try to use Narrow encoding, because 16-bit T2 encoding supports + // jumping within 2KB range. For B(cond, label), because the supported branch range is 256 + // bytes; we use the far_target hint to try to use 16-bit T1 encoding for short range jumps. + void B(vixl32::Condition cond, vixl32::Label* label, bool is_far_target = true); }; class ArmVIXLAssembler FINAL : public Assembler { @@ -205,6 +215,15 @@ class ArmVIXLAssembler FINAL : public Assembler { int32_t value, vixl32::Condition cond = vixl32::al); + template <typename T> + vixl::aarch32::Literal<T>* CreateLiteralDestroyedWithPool(T value) { + vixl::aarch32::Literal<T>* literal = + new vixl::aarch32::Literal<T>(value, + vixl32::RawLiteral::kPlacedWhenUsed, + vixl32::RawLiteral::kDeletedOnPoolDestruction); + return literal; + } + private: // VIXL assembler. ArmVIXLMacroAssembler vixl_masm_; diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc index 30e8f4e604..0147a76744 100644 --- a/compiler/utils/arm/assembler_thumb2_test.cc +++ b/compiler/utils/arm/assembler_thumb2_test.cc @@ -16,12 +16,15 @@ #include "assembler_thumb2.h" +#include "android-base/stringprintf.h" + #include "base/stl_util.h" -#include "base/stringprintf.h" #include "utils/assembler_test.h" namespace art { +using android::base::StringPrintf; + class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler, arm::Register, arm::SRegister, uint32_t> { diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc index 2d026b83f9..d07c047253 100644 --- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc +++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc @@ -24,6 +24,9 @@ using namespace vixl::aarch32; // NOLINT(build/namespaces) namespace vixl32 = vixl::aarch32; +using vixl::ExactAssemblyScope; +using vixl::CodeBufferCheckScope; + namespace art { namespace arm { @@ -455,16 +458,16 @@ void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg, if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) { if (!out_reg.Equals(in_reg)) { - AssemblerAccurateScope guard(asm_.GetVIXLAssembler(), - 3 * vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope guard(asm_.GetVIXLAssembler(), + 3 * vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); ___ it(eq, 0xc); ___ mov(eq, out_reg.AsVIXLRegister(), 0); asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne); } else { - AssemblerAccurateScope guard(asm_.GetVIXLAssembler(), - 2 * vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope guard(asm_.GetVIXLAssembler(), + 2 * vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); ___ it(ne, 0x8); asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne); } @@ -493,9 +496,9 @@ void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(FrameOffset out_off, ___ Cmp(scratch.AsVIXLRegister(), 0); if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) { - AssemblerAccurateScope guard(asm_.GetVIXLAssembler(), - 2 * vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); + ExactAssemblyScope guard(asm_.GetVIXLAssembler(), + 2 * vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); ___ it(ne, 0x8); asm_.AddConstantInIt(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne); } else { @@ -586,10 +589,12 @@ void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister m_scratch, size_t s ___ Cmp(scratch.AsVIXLRegister(), 0); { - AssemblerAccurateScope guard(asm_.GetVIXLAssembler(), - vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); - ___ b(ne, Narrow, exception_blocks_.back()->Entry()); + ExactAssemblyScope guard(asm_.GetVIXLAssembler(), + vixl32::kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); + vixl32::Label* label = exception_blocks_.back()->Entry(); + ___ b(ne, Narrow, label); + ___ AddBranchLabel(label); } // TODO: think about using CBNZ here. } diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index b34e125866..5c4875951b 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -145,7 +145,8 @@ class AssemblerTest : public testing::Test { const std::vector<Reg2*> reg2_registers, std::string (AssemblerTest::*GetName1)(const Reg1&), std::string (AssemblerTest::*GetName2)(const Reg2&), - const std::string& fmt) { + const std::string& fmt, + int bias = 0) { std::string str; std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0)); @@ -153,7 +154,7 @@ class AssemblerTest : public testing::Test { for (auto reg2 : reg2_registers) { for (int64_t imm : imms) { ImmType new_imm = CreateImmediate(imm); - (assembler_.get()->*f)(*reg1, *reg2, new_imm); + (assembler_.get()->*f)(*reg1, *reg2, new_imm + bias); std::string base = fmt; std::string reg1_string = (this->*GetName1)(*reg1); @@ -171,7 +172,7 @@ class AssemblerTest : public testing::Test { size_t imm_index = base.find(IMM_TOKEN); if (imm_index != std::string::npos) { std::ostringstream sreg; - sreg << imm; + sreg << imm + bias; std::string imm_string = sreg.str(); base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string); } @@ -188,6 +189,67 @@ class AssemblerTest : public testing::Test { return str; } + template <typename Reg1, typename Reg2, typename Reg3, typename ImmType> + std::string RepeatTemplatedRegistersImmBits(void (Ass::*f)(Reg1, Reg2, Reg3, ImmType), + int imm_bits, + const std::vector<Reg1*> reg1_registers, + const std::vector<Reg2*> reg2_registers, + const std::vector<Reg3*> reg3_registers, + std::string (AssemblerTest::*GetName1)(const Reg1&), + std::string (AssemblerTest::*GetName2)(const Reg2&), + std::string (AssemblerTest::*GetName3)(const Reg3&), + std::string fmt, + int bias) { + std::string str; + std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0)); + + for (auto reg1 : reg1_registers) { + for (auto reg2 : reg2_registers) { + for (auto reg3 : reg3_registers) { + for (int64_t imm : imms) { + ImmType new_imm = CreateImmediate(imm); + (assembler_.get()->*f)(*reg1, *reg2, *reg3, new_imm + bias); + std::string base = fmt; + + std::string reg1_string = (this->*GetName1)(*reg1); + size_t reg1_index; + while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) { + base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string); + } + + std::string reg2_string = (this->*GetName2)(*reg2); + size_t reg2_index; + while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) { + base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string); + } + + std::string reg3_string = (this->*GetName3)(*reg3); + size_t reg3_index; + while ((reg3_index = base.find(REG3_TOKEN)) != std::string::npos) { + base.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string); + } + + size_t imm_index = base.find(IMM_TOKEN); + if (imm_index != std::string::npos) { + std::ostringstream sreg; + sreg << imm + bias; + std::string imm_string = sreg.str(); + base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string); + } + + if (str.size() > 0) { + str += "\n"; + } + str += base; + } + } + } + } + // Add a newline at the end. + str += "\n"; + return str; + } + template <typename ImmType, typename Reg1, typename Reg2> std::string RepeatTemplatedImmBitsRegisters(void (Ass::*f)(ImmType, Reg1, Reg2), const std::vector<Reg1*> reg1_registers, @@ -245,14 +307,15 @@ class AssemblerTest : public testing::Test { int imm_bits, const std::vector<Reg*> registers, std::string (AssemblerTest::*GetName)(const RegType&), - const std::string& fmt) { + const std::string& fmt, + int bias) { std::string str; std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0)); for (auto reg : registers) { for (int64_t imm : imms) { ImmType new_imm = CreateImmediate(imm); - (assembler_.get()->*f)(*reg, new_imm); + (assembler_.get()->*f)(*reg, new_imm + bias); std::string base = fmt; std::string reg_string = (this->*GetName)(*reg); @@ -264,7 +327,7 @@ class AssemblerTest : public testing::Test { size_t imm_index = base.find(IMM_TOKEN); if (imm_index != std::string::npos) { std::ostringstream sreg; - sreg << imm; + sreg << imm + bias; std::string imm_string = sreg.str(); base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string); } @@ -281,36 +344,60 @@ class AssemblerTest : public testing::Test { } template <typename ImmType> - std::string RepeatRRIb(void (Ass::*f)(Reg, Reg, ImmType), int imm_bits, const std::string& fmt) { + std::string RepeatRRIb(void (Ass::*f)(Reg, Reg, ImmType), + int imm_bits, + const std::string& fmt, + int bias = 0) { return RepeatTemplatedRegistersImmBits<Reg, Reg, ImmType>(f, imm_bits, GetRegisters(), GetRegisters(), &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, - fmt); + fmt, + bias); + } + + template <typename ImmType> + std::string RepeatRRRIb(void (Ass::*f)(Reg, Reg, Reg, ImmType), + int imm_bits, + const std::string& fmt, + int bias = 0) { + return RepeatTemplatedRegistersImmBits<Reg, Reg, Reg, ImmType>(f, + imm_bits, + GetRegisters(), + GetRegisters(), + GetRegisters(), + &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, + fmt, + bias); } template <typename ImmType> - std::string RepeatRIb(void (Ass::*f)(Reg, ImmType), int imm_bits, const std::string& fmt) { + std::string RepeatRIb(void (Ass::*f)(Reg, ImmType), int imm_bits, std::string fmt, int bias = 0) { return RepeatTemplatedRegisterImmBits<Reg, ImmType>(f, imm_bits, GetRegisters(), &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, - fmt); + fmt, + bias); } template <typename ImmType> std::string RepeatFRIb(void (Ass::*f)(FPReg, Reg, ImmType), int imm_bits, - const std::string& fmt) { + const std::string& fmt, + int bias = 0) { return RepeatTemplatedRegistersImmBits<FPReg, Reg, ImmType>(f, imm_bits, GetFPRegisters(), GetRegisters(), &AssemblerTest::GetFPRegName, &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>, - fmt); + fmt, + bias); } std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), const std::string& fmt) { diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h index ac24ee95eb..e7edf96722 100644 --- a/compiler/utils/assembler_test_base.h +++ b/compiler/utils/assembler_test_base.h @@ -23,6 +23,8 @@ #include <iterator> #include <sys/stat.h> +#include "android-base/strings.h" + #include "common_runtime_test.h" // For ScratchFile #include "utils.h" @@ -221,7 +223,7 @@ class AssemblerTestInfrastructure { args.push_back("-o"); args.push_back(to_file); args.push_back(from_file); - std::string cmd = Join(args, ' '); + std::string cmd = android::base::Join(args, ' '); args.clear(); args.push_back("/bin/sh"); @@ -257,7 +259,7 @@ class AssemblerTestInfrastructure { args.push_back(file); args.push_back(">"); args.push_back(file+".dump"); - std::string cmd = Join(args, ' '); + std::string cmd = android::base::Join(args, ' '); args.clear(); args.push_back("/bin/sh"); @@ -338,7 +340,7 @@ class AssemblerTestInfrastructure { args.push_back("| sed -n \'/<.data>/,$p\' | sed -e \'s/.*://\'"); args.push_back(">"); args.push_back(file+".dis"); - std::string cmd = Join(args, ' '); + std::string cmd = android::base::Join(args, ' '); args.clear(); args.push_back("/bin/sh"); @@ -500,7 +502,7 @@ class AssemblerTestInfrastructure { std::string tmp_file = GetTmpnam(); args.push_back(">"); args.push_back(tmp_file); - std::string sh_args = Join(args, ' '); + std::string sh_args = android::base::Join(args, ' '); args.clear(); args.push_back("/bin/sh"); @@ -541,7 +543,7 @@ class AssemblerTestInfrastructure { args.push_back("sort"); args.push_back(">"); args.push_back(tmp_file); - std::string sh_args = Join(args, ' '); + std::string sh_args = android::base::Join(args, ' '); args.clear(); args.push_back("/bin/sh"); diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index 50a1d9fd98..4e9b619979 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -1717,6 +1717,11 @@ TEST_F(ArmVIXLAssemblerTest, VixlJniHelpers) { __ ExceptionPoll(scratch_register, 0); + // Push the target out of range of branch emitted by ExceptionPoll. + for (int i = 0; i < 64; i++) { + __ Store(FrameOffset(2047), scratch_register, 4); + } + __ DecreaseFrameSize(4096); __ DecreaseFrameSize(32); __ RemoveFrame(frame_size, callee_save_regs); diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index 69e1d8f6fa..ab4f9e944c 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -5458,94 +5458,160 @@ const char* const CmpConstantResults[] = { }; const char* const VixlJniHelpersResults[] = { - " 0: e92d 4de0 stmdb sp!, {r5, r6, r7, r8, sl, fp, lr}\n", - " 4: ed2d 8a10 vpush {s16-s31}\n", - " 8: b089 sub sp, #36 ; 0x24\n", - " a: 9000 str r0, [sp, #0]\n", - " c: 9121 str r1, [sp, #132] ; 0x84\n", - " e: ed8d 0a22 vstr s0, [sp, #136] ; 0x88\n", - " 12: 9223 str r2, [sp, #140] ; 0x8c\n", - " 14: 9324 str r3, [sp, #144] ; 0x90\n", - " 16: b088 sub sp, #32\n", - " 18: f5ad 5d80 sub.w sp, sp, #4096 ; 0x1000\n", - " 1c: 9808 ldr r0, [sp, #32]\n", - " 1e: 981f ldr r0, [sp, #124] ; 0x7c\n", - " 20: 9821 ldr r0, [sp, #132] ; 0x84\n", - " 22: 98ff ldr r0, [sp, #1020] ; 0x3fc\n", - " 24: f8dd 0400 ldr.w r0, [sp, #1024] ; 0x400\n", - " 28: f8dd cffc ldr.w ip, [sp, #4092] ; 0xffc\n", - " 2c: f50d 5c80 add.w ip, sp, #4096 ; 0x1000\n", - " 30: f8dc c000 ldr.w ip, [ip]\n", - " 34: f8d9 c200 ldr.w ip, [r9, #512] ; 0x200\n", - " 38: f8dc 0080 ldr.w r0, [ip, #128] ; 0x80\n", - " 3c: 9008 str r0, [sp, #32]\n", - " 3e: 901f str r0, [sp, #124] ; 0x7c\n", - " 40: 9021 str r0, [sp, #132] ; 0x84\n", - " 42: 90ff str r0, [sp, #1020] ; 0x3fc\n", - " 44: f8cd 0400 str.w r0, [sp, #1024] ; 0x400\n", - " 48: f8cd cffc str.w ip, [sp, #4092] ; 0xffc\n", - " 4c: f84d 5d04 str.w r5, [sp, #-4]!\n", - " 50: f50d 5580 add.w r5, sp, #4096 ; 0x1000\n", - " 54: f8c5 c004 str.w ip, [r5, #4]\n", - " 58: f85d 5b04 ldr.w r5, [sp], #4\n", - " 5c: f04f 0cff mov.w ip, #255 ; 0xff\n", - " 60: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", - " 64: f06f 4c7f mvn.w ip, #4278190080 ; 0xff000000\n", - " 68: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", - " 6c: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", - " 70: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", - " 74: 900c str r0, [sp, #48] ; 0x30\n", - " 76: f8dd c030 ldr.w ip, [sp, #48] ; 0x30\n", - " 7a: f8cd c034 str.w ip, [sp, #52] ; 0x34\n", - " 7e: f50d 5c80 add.w ip, sp, #4096 ; 0x1000\n", - " 82: f8c9 c200 str.w ip, [r9, #512] ; 0x200\n", - " 86: f8c9 d200 str.w sp, [r9, #512] ; 0x200\n", - " 8a: f8d0 c030 ldr.w ip, [r0, #48] ; 0x30\n", - " 8e: 47e0 blx ip\n", - " 90: f8dd c02c ldr.w ip, [sp, #44] ; 0x2c\n", - " 94: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", - " 98: f8d9 c200 ldr.w ip, [r9, #512] ; 0x200\n", - " 9c: f8cd c02c str.w ip, [sp, #44] ; 0x2c\n", - " a0: f8dd c02c ldr.w ip, [sp, #44] ; 0x2c\n", - " a4: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", - " a8: 4648 mov r0, r9\n", - " aa: f8cd 9030 str.w r9, [sp, #48] ; 0x30\n", - " ae: 4684 mov ip, r0\n", - " b0: f1bc 0f00 cmp.w ip, #0\n", - " b4: bf18 it ne\n", - " b6: f10d 0c30 addne.w ip, sp, #48 ; 0x30\n", - " ba: f10d 0c30 add.w ip, sp, #48 ; 0x30\n", - " be: f1bc 0f00 cmp.w ip, #0\n", - " c2: bf0c ite eq\n", - " c4: 2000 moveq r0, #0\n", - " c6: a80c addne r0, sp, #48 ; 0x30\n", - " c8: f8dd c040 ldr.w ip, [sp, #64] ; 0x40\n", - " cc: f1bc 0f00 cmp.w ip, #0\n", - " d0: bf18 it ne\n", - " d2: f10d 0c40 addne.w ip, sp, #64 ; 0x40\n", - " d6: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", - " da: f1bc 0f00 cmp.w ip, #0\n", - " de: bf0c ite eq\n", - " e0: 2000 moveq r0, #0\n", - " e2: 4668 movne r0, sp\n", - " e4: f1bc 0f00 cmp.w ip, #0\n", - " e8: bf0c ite eq\n", - " ea: 2000 moveq r0, #0\n", - " ec: f20d 4001 addwne r0, sp, #1025 ; 0x401\n", - " f0: f1bc 0f00 cmp.w ip, #0\n", - " f4: bf18 it ne\n", - " f6: f20d 4c01 addwne ip, sp, #1025 ; 0x401\n", - " fa: f8d9 c084 ldr.w ip, [r9, #132] ; 0x84\n", - " fe: f1bc 0f00 cmp.w ip, #0\n", - " 102: d107 bne.n 114 <VixlJniHelpers+0x114>\n", - " 104: f50d 5d80 add.w sp, sp, #4096 ; 0x1000\n", - " 108: b008 add sp, #32\n", - " 10a: b009 add sp, #36 ; 0x24\n", - " 10c: ecbd 8a10 vpop {s16-s31}\n", - " 110: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n", - " 114: 4660 mov r0, ip\n", - " 116: f8d9 c2b0 ldr.w ip, [r9, #688] ; 0x2b0\n", - " 11a: 47e0 blx ip\n", + " 0: e92d 4de0 stmdb sp!, {r5, r6, r7, r8, sl, fp, lr}\n", + " 4: ed2d 8a10 vpush {s16-s31}\n", + " 8: b089 sub sp, #36 ; 0x24\n", + " a: 9000 str r0, [sp, #0]\n", + " c: 9121 str r1, [sp, #132] ; 0x84\n", + " e: ed8d 0a22 vstr s0, [sp, #136] ; 0x88\n", + " 12: 9223 str r2, [sp, #140] ; 0x8c\n", + " 14: 9324 str r3, [sp, #144] ; 0x90\n", + " 16: b088 sub sp, #32\n", + " 18: f5ad 5d80 sub.w sp, sp, #4096 ; 0x1000\n", + " 1c: 9808 ldr r0, [sp, #32]\n", + " 1e: 981f ldr r0, [sp, #124] ; 0x7c\n", + " 20: 9821 ldr r0, [sp, #132] ; 0x84\n", + " 22: 98ff ldr r0, [sp, #1020] ; 0x3fc\n", + " 24: f8dd 0400 ldr.w r0, [sp, #1024] ; 0x400\n", + " 28: f8dd cffc ldr.w ip, [sp, #4092] ; 0xffc\n", + " 2c: f50d 5c80 add.w ip, sp, #4096 ; 0x1000\n", + " 30: f8dc c000 ldr.w ip, [ip]\n", + " 34: f8d9 c200 ldr.w ip, [r9, #512] ; 0x200\n", + " 38: f8dc 0080 ldr.w r0, [ip, #128] ; 0x80\n", + " 3c: 9008 str r0, [sp, #32]\n", + " 3e: 901f str r0, [sp, #124] ; 0x7c\n", + " 40: 9021 str r0, [sp, #132] ; 0x84\n", + " 42: 90ff str r0, [sp, #1020] ; 0x3fc\n", + " 44: f8cd 0400 str.w r0, [sp, #1024] ; 0x400\n", + " 48: f8cd cffc str.w ip, [sp, #4092] ; 0xffc\n", + " 4c: f84d 5d04 str.w r5, [sp, #-4]!\n", + " 50: f50d 5580 add.w r5, sp, #4096 ; 0x1000\n", + " 54: f8c5 c004 str.w ip, [r5, #4]\n", + " 58: f85d 5b04 ldr.w r5, [sp], #4\n", + " 5c: f04f 0cff mov.w ip, #255 ; 0xff\n", + " 60: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", + " 64: f06f 4c7f mvn.w ip, #4278190080 ; 0xff000000\n", + " 68: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", + " 6c: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", + " 70: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", + " 74: 900c str r0, [sp, #48] ; 0x30\n", + " 76: f8dd c030 ldr.w ip, [sp, #48] ; 0x30\n", + " 7a: f8cd c034 str.w ip, [sp, #52] ; 0x34\n", + " 7e: f50d 5c80 add.w ip, sp, #4096 ; 0x1000\n", + " 82: f8c9 c200 str.w ip, [r9, #512] ; 0x200\n", + " 86: f8c9 d200 str.w sp, [r9, #512] ; 0x200\n", + " 8a: f8d0 c030 ldr.w ip, [r0, #48] ; 0x30\n", + " 8e: 47e0 blx ip\n", + " 90: f8dd c02c ldr.w ip, [sp, #44] ; 0x2c\n", + " 94: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", + " 98: f8d9 c200 ldr.w ip, [r9, #512] ; 0x200\n", + " 9c: f8cd c02c str.w ip, [sp, #44] ; 0x2c\n", + " a0: f8dd c02c ldr.w ip, [sp, #44] ; 0x2c\n", + " a4: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", + " a8: 4648 mov r0, r9\n", + " aa: f8cd 9030 str.w r9, [sp, #48] ; 0x30\n", + " ae: 4684 mov ip, r0\n", + " b0: f1bc 0f00 cmp.w ip, #0\n", + " b4: bf18 it ne\n", + " b6: f10d 0c30 addne.w ip, sp, #48 ; 0x30\n", + " ba: f10d 0c30 add.w ip, sp, #48 ; 0x30\n", + " be: f1bc 0f00 cmp.w ip, #0\n", + " c2: bf0c ite eq\n", + " c4: 2000 moveq r0, #0\n", + " c6: a80c addne r0, sp, #48 ; 0x30\n", + " c8: f8dd c040 ldr.w ip, [sp, #64] ; 0x40\n", + " cc: f1bc 0f00 cmp.w ip, #0\n", + " d0: bf18 it ne\n", + " d2: f10d 0c40 addne.w ip, sp, #64 ; 0x40\n", + " d6: f8cd c030 str.w ip, [sp, #48] ; 0x30\n", + " da: f1bc 0f00 cmp.w ip, #0\n", + " de: bf0c ite eq\n", + " e0: 2000 moveq r0, #0\n", + " e2: 4668 movne r0, sp\n", + " e4: f1bc 0f00 cmp.w ip, #0\n", + " e8: bf0c ite eq\n", + " ea: 2000 moveq r0, #0\n", + " ec: f20d 4001 addwne r0, sp, #1025 ; 0x401\n", + " f0: f1bc 0f00 cmp.w ip, #0\n", + " f4: bf18 it ne\n", + " f6: f20d 4c01 addwne ip, sp, #1025 ; 0x401\n", + " fa: f8d9 c084 ldr.w ip, [r9, #132] ; 0x84\n", + " fe: f1bc 0f00 cmp.w ip, #0\n", + " 102: d171 bne.n 1e8 <VixlJniHelpers+0x1e8>\n", + " 104: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 108: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 10c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 110: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 114: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 118: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 11c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 120: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 124: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 128: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 12c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 130: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 134: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 138: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 13c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 140: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 144: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 148: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 14c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 150: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 154: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 158: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 15c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 160: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 164: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 168: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 16c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 170: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 174: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 178: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 17c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 180: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 184: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 188: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 18c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 190: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 194: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 198: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 19c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1a0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1a4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1a8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1ac: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1b0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1b4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1b8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1bc: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1c0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1c4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1c8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1cc: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1d0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1d4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1d8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1dc: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1e0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1e4: f000 b802 b.w 1ec <VixlJniHelpers+0x1ec>\n", + " 1e8: f000 b818 b.w 21c <VixlJniHelpers+0x21c>\n", + " 1ec: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1f0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1f4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1f8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 1fc: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 200: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 204: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 208: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n", + " 20c: f50d 5d80 add.w sp, sp, #4096 ; 0x1000\n", + " 210: b008 add sp, #32\n", + " 212: b009 add sp, #36 ; 0x24\n", + " 214: ecbd 8a10 vpop {s16-s31}\n", + " 218: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n", + " 21c: 4660 mov r0, ip\n", + " 21e: f8d9 c2b0 ldr.w ip, [r9, #688] ; 0x2b0\n", + " 222: 47e0 blx ip\n", nullptr }; diff --git a/compiler/utils/dedupe_set-inl.h b/compiler/utils/dedupe_set-inl.h index ac5481336b..c06e9cadcc 100644 --- a/compiler/utils/dedupe_set-inl.h +++ b/compiler/utils/dedupe_set-inl.h @@ -23,10 +23,11 @@ #include <inttypes.h> #include <unordered_map> +#include "android-base/stringprintf.h" + #include "base/mutex.h" #include "base/hash_set.h" #include "base/stl_util.h" -#include "base/stringprintf.h" #include "base/time_utils.h" namespace art { @@ -238,13 +239,13 @@ std::string DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::DumpS for (HashType shard = 0; shard < kShard; ++shard) { shards_[shard]->UpdateStats(self, &stats); } - return StringPrintf("%zu collisions, %zu max hash collisions, " - "%zu/%zu probe distance, %" PRIu64 " ns hash time", - stats.collision_sum, - stats.collision_max, - stats.total_probe_distance, - stats.total_size, - hash_time_); + return android::base::StringPrintf("%zu collisions, %zu max hash collisions, " + "%zu/%zu probe distance, %" PRIu64 " ns hash time", + stats.collision_sum, + stats.collision_max, + stats.total_probe_distance, + stats.total_size, + hash_time_); } diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc index 1a21df939e..5906a71b38 100644 --- a/compiler/utils/mips64/assembler_mips64.cc +++ b/compiler/utils/mips64/assembler_mips64.cc @@ -35,6 +35,7 @@ void Mips64Assembler::FinalizeCode() { for (auto& exception_block : exception_blocks_) { EmitExceptionPoll(&exception_block); } + EmitLiterals(); PromoteBranches(); } @@ -318,6 +319,18 @@ void Mips64Assembler::Dinsu(GpuRegister rt, GpuRegister rs, int pos, int size) { EmitR(0x1f, rs, rt, static_cast<GpuRegister>(pos + size - 33), pos - 32, 0x6); } +void Mips64Assembler::Lsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne) { + CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne; + int sa = saPlusOne - 1; + EmitR(0x0, rs, rt, rd, sa, 0x05); +} + +void Mips64Assembler::Dlsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne) { + CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne; + int sa = saPlusOne - 1; + EmitR(0x0, rs, rt, rd, sa, 0x15); +} + void Mips64Assembler::Wsbh(GpuRegister rd, GpuRegister rt) { EmitRtd(0x1f, rt, rd, 2, 0x20); } @@ -450,6 +463,21 @@ void Mips64Assembler::Lwu(GpuRegister rt, GpuRegister rs, uint16_t imm16) { EmitI(0x27, rs, rt, imm16); } +void Mips64Assembler::Lwpc(GpuRegister rs, uint32_t imm19) { + CHECK(IsUint<19>(imm19)) << imm19; + EmitI21(0x3B, rs, (0x01 << 19) | imm19); +} + +void Mips64Assembler::Lwupc(GpuRegister rs, uint32_t imm19) { + CHECK(IsUint<19>(imm19)) << imm19; + EmitI21(0x3B, rs, (0x02 << 19) | imm19); +} + +void Mips64Assembler::Ldpc(GpuRegister rs, uint32_t imm18) { + CHECK(IsUint<18>(imm18)) << imm18; + EmitI21(0x3B, rs, (0x06 << 18) | imm18); +} + void Mips64Assembler::Lui(GpuRegister rt, uint16_t imm16) { EmitI(0xf, static_cast<GpuRegister>(0), rt, imm16); } @@ -548,6 +576,10 @@ void Mips64Assembler::Bc(uint32_t imm26) { EmitI26(0x32, imm26); } +void Mips64Assembler::Balc(uint32_t imm26) { + EmitI26(0x3A, imm26); +} + void Mips64Assembler::Jic(GpuRegister rt, uint16_t imm16) { EmitI(0x36, static_cast<GpuRegister>(0), rt, imm16); } @@ -1064,19 +1096,37 @@ void Mips64Assembler::Branch::InitShortOrLong(Mips64Assembler::Branch::OffsetBit type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type; } -void Mips64Assembler::Branch::InitializeType(bool is_call) { +void Mips64Assembler::Branch::InitializeType(Type initial_type) { OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_); - if (is_call) { - InitShortOrLong(offset_size, kCall, kLongCall); - } else if (condition_ == kUncond) { - InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch); - } else { - if (condition_ == kCondEQZ || condition_ == kCondNEZ) { - // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions. - type_ = (offset_size <= kOffset23) ? kCondBranch : kLongCondBranch; - } else { - InitShortOrLong(offset_size, kCondBranch, kLongCondBranch); - } + switch (initial_type) { + case kLabel: + case kLiteral: + case kLiteralUnsigned: + case kLiteralLong: + CHECK(!IsResolved()); + type_ = initial_type; + break; + case kCall: + InitShortOrLong(offset_size, kCall, kLongCall); + break; + case kCondBranch: + switch (condition_) { + case kUncond: + InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch); + break; + case kCondEQZ: + case kCondNEZ: + // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions. + type_ = (offset_size <= kOffset23) ? kCondBranch : kLongCondBranch; + break; + default: + InitShortOrLong(offset_size, kCondBranch, kLongCondBranch); + break; + } + break; + default: + LOG(FATAL) << "Unexpected branch type " << initial_type; + UNREACHABLE(); } old_type_ = type_; } @@ -1109,14 +1159,14 @@ bool Mips64Assembler::Branch::IsUncond(BranchCondition condition, } } -Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target) +Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target, bool is_call) : old_location_(location), location_(location), target_(target), lhs_reg_(ZERO), rhs_reg_(ZERO), condition_(kUncond) { - InitializeType(false); + InitializeType(is_call ? kCall : kCondBranch); } Mips64Assembler::Branch::Branch(uint32_t location, @@ -1164,19 +1214,18 @@ Mips64Assembler::Branch::Branch(uint32_t location, // Branch condition is always true, make the branch unconditional. condition_ = kUncond; } - InitializeType(false); + InitializeType(kCondBranch); } -Mips64Assembler::Branch::Branch(uint32_t location, uint32_t target, GpuRegister indirect_reg) +Mips64Assembler::Branch::Branch(uint32_t location, GpuRegister dest_reg, Type label_or_literal_type) : old_location_(location), location_(location), - target_(target), - lhs_reg_(indirect_reg), + target_(kUnresolved), + lhs_reg_(dest_reg), rhs_reg_(ZERO), condition_(kUncond) { - CHECK_NE(indirect_reg, ZERO); - CHECK_NE(indirect_reg, AT); - InitializeType(true); + CHECK_NE(dest_reg, ZERO); + InitializeType(label_or_literal_type); } Mips64Assembler::BranchCondition Mips64Assembler::Branch::OppositeCondition( @@ -1278,11 +1327,23 @@ bool Mips64Assembler::Branch::IsLong() const { case kUncondBranch: case kCondBranch: case kCall: + // Near label. + case kLabel: + // Near literals. + case kLiteral: + case kLiteralUnsigned: + case kLiteralLong: return false; // Long branches. case kLongUncondBranch: case kLongCondBranch: case kLongCall: + // Far label. + case kFarLabel: + // Far literals. + case kFarLiteral: + case kFarLiteralUnsigned: + case kFarLiteralLong: return true; } UNREACHABLE(); @@ -1351,6 +1412,20 @@ void Mips64Assembler::Branch::PromoteToLong() { case kCall: type_ = kLongCall; break; + // Near label. + case kLabel: + type_ = kFarLabel; + break; + // Near literals. + case kLiteral: + type_ = kFarLiteral; + break; + case kLiteralUnsigned: + type_ = kFarLiteralUnsigned; + break; + case kLiteralLong: + type_ = kFarLiteralLong; + break; default: // Note: 'type_' is already long. break; @@ -1397,7 +1472,15 @@ uint32_t Mips64Assembler::Branch::GetOffset() const { uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize()); // Calculate the byte distance between instructions and also account for // different PC-relative origins. - uint32_t offset = target_ - GetOffsetLocation() - branch_info_[type_].pc_org * sizeof(uint32_t); + uint32_t offset_location = GetOffsetLocation(); + if (type_ == kLiteralLong) { + // Special case for the ldpc instruction, whose address (PC) is rounded down to + // a multiple of 8 before adding the offset. + // Note, branch promotion has already taken care of aligning `target_` to an + // address that's a multiple of 8. + offset_location = RoundDown(offset_location, sizeof(uint64_t)); + } + uint32_t offset = target_ - offset_location - branch_info_[type_].pc_org * sizeof(uint32_t); // Prepare the offset for encoding into the instruction(s). offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift; return offset; @@ -1444,7 +1527,7 @@ void Mips64Assembler::Bind(Mips64Label* label) { label->BindTo(bound_pc); } -uint32_t Mips64Assembler::GetLabelLocation(Mips64Label* label) const { +uint32_t Mips64Assembler::GetLabelLocation(const Mips64Label* label) const { CHECK(label->IsBound()); uint32_t target = label->Position(); if (label->prev_branch_id_plus_one_) { @@ -1500,7 +1583,7 @@ void Mips64Assembler::FinalizeLabeledBranch(Mips64Label* label) { void Mips64Assembler::Buncond(Mips64Label* label) { uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; - branches_.emplace_back(buffer_.Size(), target); + branches_.emplace_back(buffer_.Size(), target, /* is_call */ false); FinalizeLabeledBranch(label); } @@ -1517,12 +1600,87 @@ void Mips64Assembler::Bcond(Mips64Label* label, FinalizeLabeledBranch(label); } -void Mips64Assembler::Call(Mips64Label* label, GpuRegister indirect_reg) { +void Mips64Assembler::Call(Mips64Label* label) { uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved; - branches_.emplace_back(buffer_.Size(), target, indirect_reg); + branches_.emplace_back(buffer_.Size(), target, /* is_call */ true); + FinalizeLabeledBranch(label); +} + +void Mips64Assembler::LoadLabelAddress(GpuRegister dest_reg, Mips64Label* label) { + // Label address loads are treated as pseudo branches since they require very similar handling. + DCHECK(!label->IsBound()); + branches_.emplace_back(buffer_.Size(), dest_reg, Branch::kLabel); FinalizeLabeledBranch(label); } +Literal* Mips64Assembler::NewLiteral(size_t size, const uint8_t* data) { + // We don't support byte and half-word literals. + if (size == 4u) { + literals_.emplace_back(size, data); + return &literals_.back(); + } else { + DCHECK_EQ(size, 8u); + long_literals_.emplace_back(size, data); + return &long_literals_.back(); + } +} + +void Mips64Assembler::LoadLiteral(GpuRegister dest_reg, + LoadOperandType load_type, + Literal* literal) { + // Literal loads are treated as pseudo branches since they require very similar handling. + Branch::Type literal_type; + switch (load_type) { + case kLoadWord: + DCHECK_EQ(literal->GetSize(), 4u); + literal_type = Branch::kLiteral; + break; + case kLoadUnsignedWord: + DCHECK_EQ(literal->GetSize(), 4u); + literal_type = Branch::kLiteralUnsigned; + break; + case kLoadDoubleword: + DCHECK_EQ(literal->GetSize(), 8u); + literal_type = Branch::kLiteralLong; + break; + default: + LOG(FATAL) << "Unexpected literal load type " << load_type; + UNREACHABLE(); + } + Mips64Label* label = literal->GetLabel(); + DCHECK(!label->IsBound()); + branches_.emplace_back(buffer_.Size(), dest_reg, literal_type); + FinalizeLabeledBranch(label); +} + +void Mips64Assembler::EmitLiterals() { + if (!literals_.empty()) { + for (Literal& literal : literals_) { + Mips64Label* label = literal.GetLabel(); + Bind(label); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + DCHECK_EQ(literal.GetSize(), 4u); + for (size_t i = 0, size = literal.GetSize(); i != size; ++i) { + buffer_.Emit<uint8_t>(literal.GetData()[i]); + } + } + } + if (!long_literals_.empty()) { + // Reserve 4 bytes for potential alignment. If after the branch promotion the 64-bit + // literals don't end up 8-byte-aligned, they will be moved down 4 bytes. + Emit(0); // NOP. + for (Literal& literal : long_literals_) { + Mips64Label* label = literal.GetLabel(); + Bind(label); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + DCHECK_EQ(literal.GetSize(), 8u); + for (size_t i = 0, size = literal.GetSize(); i != size; ++i) { + buffer_.Emit<uint8_t>(literal.GetData()[i]); + } + } + } +} + void Mips64Assembler::PromoteBranches() { // Promote short branches to long as necessary. bool changed; @@ -1561,6 +1719,35 @@ void Mips64Assembler::PromoteBranches() { end = branch.GetOldLocation(); } } + + // Align 64-bit literals by moving them down by 4 bytes if needed. + // This will reduce the PC-relative distance, which should be safe for both near and far literals. + if (!long_literals_.empty()) { + uint32_t first_literal_location = GetLabelLocation(long_literals_.front().GetLabel()); + size_t lit_size = long_literals_.size() * sizeof(uint64_t); + size_t buf_size = buffer_.Size(); + // 64-bit literals must be at the very end of the buffer. + CHECK_EQ(first_literal_location + lit_size, buf_size); + if (!IsAligned<sizeof(uint64_t)>(first_literal_location)) { + buffer_.Move(first_literal_location - sizeof(uint32_t), first_literal_location, lit_size); + // The 4 reserved bytes proved useless, reduce the buffer size. + buffer_.Resize(buf_size - sizeof(uint32_t)); + // Reduce target addresses in literal and address loads by 4 bytes in order for correct + // offsets from PC to be generated. + for (auto& branch : branches_) { + uint32_t target = branch.GetTarget(); + if (target >= first_literal_location) { + branch.Resolve(target - sizeof(uint32_t)); + } + } + // If after this we ever call GetLabelLocation() to get the location of a 64-bit literal, + // we need to adjust the location of the literal's label as well. + for (Literal& literal : long_literals_) { + // Bound label's position is negative, hence incrementing it instead of decrementing. + literal.GetLabel()->position_ += sizeof(uint32_t); + } + } + } } // Note: make sure branch_info_[] and EmitBranch() are kept synchronized. @@ -1569,11 +1756,23 @@ const Mips64Assembler::Branch::BranchInfo Mips64Assembler::Branch::branch_info_[ { 1, 0, 1, Mips64Assembler::Branch::kOffset28, 2 }, // kUncondBranch { 2, 0, 1, Mips64Assembler::Branch::kOffset18, 2 }, // kCondBranch // Exception: kOffset23 for beqzc/bnezc - { 2, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kCall + { 1, 0, 1, Mips64Assembler::Branch::kOffset28, 2 }, // kCall + // Near label. + { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kLabel + // Near literals. + { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kLiteral + { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 2 }, // kLiteralUnsigned + { 1, 0, 0, Mips64Assembler::Branch::kOffset21, 3 }, // kLiteralLong // Long branches. { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kLongUncondBranch { 3, 1, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kLongCondBranch - { 3, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kLongCall + { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kLongCall + // Far label. + { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kFarLabel + // Far literals. + { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kFarLiteral + { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kFarLiteralUnsigned + { 2, 0, 0, Mips64Assembler::Branch::kOffset32, 0 }, // kFarLiteralLong }; // Note: make sure branch_info_[] and EmitBranch() are kept synchronized. @@ -1597,8 +1796,26 @@ void Mips64Assembler::EmitBranch(Mips64Assembler::Branch* branch) { break; case Branch::kCall: CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Balc(offset); + break; + + // Near label. + case Branch::kLabel: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); Addiupc(lhs, offset); - Jialc(lhs, 0); + break; + // Near literals. + case Branch::kLiteral: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Lwpc(lhs, offset); + break; + case Branch::kLiteralUnsigned: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Lwupc(lhs, offset); + break; + case Branch::kLiteralLong: + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Ldpc(lhs, offset); break; // Long branches. @@ -1616,11 +1833,37 @@ void Mips64Assembler::EmitBranch(Mips64Assembler::Branch* branch) { Jic(AT, Low16Bits(offset)); break; case Branch::kLongCall: + offset += (offset & 0x8000) << 1; // Account for sign extension in jialc. + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Auipc(AT, High16Bits(offset)); + Jialc(AT, Low16Bits(offset)); + break; + + // Far label. + case Branch::kFarLabel: offset += (offset & 0x8000) << 1; // Account for sign extension in daddiu. CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); - Auipc(lhs, High16Bits(offset)); - Daddiu(lhs, lhs, Low16Bits(offset)); - Jialc(lhs, 0); + Auipc(AT, High16Bits(offset)); + Daddiu(lhs, AT, Low16Bits(offset)); + break; + // Far literals. + case Branch::kFarLiteral: + offset += (offset & 0x8000) << 1; // Account for sign extension in lw. + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Auipc(AT, High16Bits(offset)); + Lw(lhs, AT, Low16Bits(offset)); + break; + case Branch::kFarLiteralUnsigned: + offset += (offset & 0x8000) << 1; // Account for sign extension in lwu. + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Auipc(AT, High16Bits(offset)); + Lwu(lhs, AT, Low16Bits(offset)); + break; + case Branch::kFarLiteralLong: + offset += (offset & 0x8000) << 1; // Account for sign extension in ld. + CHECK_EQ(overwrite_location_, branch->GetOffsetLocation()); + Auipc(AT, High16Bits(offset)); + Ld(lhs, AT, Low16Bits(offset)); break; } CHECK_EQ(overwrite_location_, branch->GetEndLocation()); @@ -1631,8 +1874,8 @@ void Mips64Assembler::Bc(Mips64Label* label) { Buncond(label); } -void Mips64Assembler::Jialc(Mips64Label* label, GpuRegister indirect_reg) { - Call(label, indirect_reg); +void Mips64Assembler::Balc(Mips64Label* label) { + Call(label); } void Mips64Assembler::Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label) { diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h index 238cb9d765..7ef5ab0d39 100644 --- a/compiler/utils/mips64/assembler_mips64.h +++ b/compiler/utils/mips64/assembler_mips64.h @@ -17,9 +17,11 @@ #ifndef ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_ #define ART_COMPILER_UTILS_MIPS64_ASSEMBLER_MIPS64_H_ +#include <deque> #include <utility> #include <vector> +#include "base/arena_containers.h" #include "base/enums.h" #include "base/macros.h" #include "constants_mips64.h" @@ -312,6 +314,49 @@ class Mips64Label : public Label { DISALLOW_COPY_AND_ASSIGN(Mips64Label); }; +// Assembler literal is a value embedded in code, retrieved using a PC-relative load. +class Literal { + public: + static constexpr size_t kMaxSize = 8; + + Literal(uint32_t size, const uint8_t* data) + : label_(), size_(size) { + DCHECK_LE(size, Literal::kMaxSize); + memcpy(data_, data, size); + } + + template <typename T> + T GetValue() const { + DCHECK_EQ(size_, sizeof(T)); + T value; + memcpy(&value, data_, sizeof(T)); + return value; + } + + uint32_t GetSize() const { + return size_; + } + + const uint8_t* GetData() const { + return data_; + } + + Mips64Label* GetLabel() { + return &label_; + } + + const Mips64Label* GetLabel() const { + return &label_; + } + + private: + Mips64Label label_; + const uint32_t size_; + uint8_t data_[kMaxSize]; + + DISALLOW_COPY_AND_ASSIGN(Literal); +}; + // Slowpath entered when Thread::Current()->_exception is non-null. class Mips64ExceptionSlowPath { public: @@ -341,6 +386,8 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer : Assembler(arena), overwriting_(false), overwrite_location_(0), + literals_(arena->Adapter(kArenaAllocAssembler)), + long_literals_(arena->Adapter(kArenaAllocAssembler)), last_position_adjustment_(0), last_old_position_(0), last_branch_id_(0) { @@ -386,18 +433,20 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Nor(GpuRegister rd, GpuRegister rs, GpuRegister rt); void Bitswap(GpuRegister rd, GpuRegister rt); - void Dbitswap(GpuRegister rd, GpuRegister rt); + void Dbitswap(GpuRegister rd, GpuRegister rt); // MIPS64 void Seb(GpuRegister rd, GpuRegister rt); void Seh(GpuRegister rd, GpuRegister rt); - void Dsbh(GpuRegister rd, GpuRegister rt); - void Dshd(GpuRegister rd, GpuRegister rt); + void Dsbh(GpuRegister rd, GpuRegister rt); // MIPS64 + void Dshd(GpuRegister rd, GpuRegister rt); // MIPS64 void Dext(GpuRegister rs, GpuRegister rt, int pos, int size); // MIPS64 void Dinsu(GpuRegister rt, GpuRegister rs, int pos, int size); // MIPS64 + void Lsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne); + void Dlsa(GpuRegister rd, GpuRegister rs, GpuRegister rt, int saPlusOne); // MIPS64 void Wsbh(GpuRegister rd, GpuRegister rt); void Sc(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); - void Scd(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); + void Scd(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); // MIPS64 void Ll(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); - void Lld(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); + void Lld(GpuRegister rt, GpuRegister base, int16_t imm9 = 0); // MIPS64 void Sll(GpuRegister rd, GpuRegister rt, int shamt); void Srl(GpuRegister rd, GpuRegister rt, int shamt); @@ -409,7 +458,7 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Srav(GpuRegister rd, GpuRegister rt, GpuRegister rs); void Dsll(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsrl(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 - void Drotr(GpuRegister rd, GpuRegister rt, int shamt); + void Drotr(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsra(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsll32(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 void Dsrl32(GpuRegister rd, GpuRegister rt, int shamt); // MIPS64 @@ -427,6 +476,9 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Lbu(GpuRegister rt, GpuRegister rs, uint16_t imm16); void Lhu(GpuRegister rt, GpuRegister rs, uint16_t imm16); void Lwu(GpuRegister rt, GpuRegister rs, uint16_t imm16); // MIPS64 + void Lwpc(GpuRegister rs, uint32_t imm19); + void Lwupc(GpuRegister rs, uint32_t imm19); // MIPS64 + void Ldpc(GpuRegister rs, uint32_t imm18); // MIPS64 void Lui(GpuRegister rt, uint16_t imm16); void Dahi(GpuRegister rs, uint16_t imm16); // MIPS64 void Dati(GpuRegister rs, uint16_t imm16); // MIPS64 @@ -445,8 +497,8 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Selnez(GpuRegister rd, GpuRegister rs, GpuRegister rt); void Clz(GpuRegister rd, GpuRegister rs); void Clo(GpuRegister rd, GpuRegister rs); - void Dclz(GpuRegister rd, GpuRegister rs); - void Dclo(GpuRegister rd, GpuRegister rs); + void Dclz(GpuRegister rd, GpuRegister rs); // MIPS64 + void Dclo(GpuRegister rd, GpuRegister rs); // MIPS64 void Jalr(GpuRegister rd, GpuRegister rs); void Jalr(GpuRegister rs); @@ -454,6 +506,7 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Auipc(GpuRegister rs, uint16_t imm16); void Addiupc(GpuRegister rs, uint32_t imm19); void Bc(uint32_t imm26); + void Balc(uint32_t imm26); void Jic(GpuRegister rt, uint16_t imm16); void Jialc(GpuRegister rt, uint16_t imm16); void Bltc(GpuRegister rs, GpuRegister rt, uint16_t imm16); @@ -605,8 +658,26 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer UNREACHABLE(); } + // Create a new literal with a given value. + // NOTE: Force the template parameter to be explicitly specified. + template <typename T> + Literal* NewLiteral(typename Identity<T>::type value) { + static_assert(std::is_integral<T>::value, "T must be an integral type."); + return NewLiteral(sizeof(value), reinterpret_cast<const uint8_t*>(&value)); + } + + // Load label address using PC-relative loads. To be used with data labels in the literal / + // jump table area only and not with regular code labels. + void LoadLabelAddress(GpuRegister dest_reg, Mips64Label* label); + + // Create a new literal with the given data. + Literal* NewLiteral(size_t size, const uint8_t* data); + + // Load literal using PC-relative loads. + void LoadLiteral(GpuRegister dest_reg, LoadOperandType load_type, Literal* literal); + void Bc(Mips64Label* label); - void Jialc(Mips64Label* label, GpuRegister indirect_reg); + void Balc(Mips64Label* label); void Bltc(GpuRegister rs, GpuRegister rt, Mips64Label* label); void Bltzc(GpuRegister rt, Mips64Label* label); void Bgtzc(GpuRegister rt, Mips64Label* label); @@ -756,12 +827,15 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer // Returns the (always-)current location of a label (can be used in class CodeGeneratorMIPS64, // must be used instead of Mips64Label::GetPosition()). - uint32_t GetLabelLocation(Mips64Label* label) const; + uint32_t GetLabelLocation(const Mips64Label* label) const; // Get the final position of a label after local fixup based on the old position // recorded before FinalizeCode(). uint32_t GetAdjustedPosition(uint32_t old_position); + // Note that PC-relative literal loads are handled as pseudo branches because they need very + // similar relocation and may similarly expand in size to accomodate for larger offsets relative + // to PC. enum BranchCondition { kCondLT, kCondGE, @@ -791,10 +865,22 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer kUncondBranch, kCondBranch, kCall, + // Near label. + kLabel, + // Near literals. + kLiteral, + kLiteralUnsigned, + kLiteralLong, // Long branches. kLongUncondBranch, kLongCondBranch, kLongCall, + // Far label. + kFarLabel, + // Far literals. + kFarLiteral, + kFarLiteralUnsigned, + kFarLiteralLong, }; // Bit sizes of offsets defined as enums to minimize chance of typos. @@ -830,16 +916,16 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer }; static const BranchInfo branch_info_[/* Type */]; - // Unconditional branch. - Branch(uint32_t location, uint32_t target); + // Unconditional branch or call. + Branch(uint32_t location, uint32_t target, bool is_call); // Conditional branch. Branch(uint32_t location, uint32_t target, BranchCondition condition, GpuRegister lhs_reg, - GpuRegister rhs_reg = ZERO); - // Call (branch and link) that stores the target address in a given register (i.e. T9). - Branch(uint32_t location, uint32_t target, GpuRegister indirect_reg); + GpuRegister rhs_reg); + // Label address (in literal area) or literal. + Branch(uint32_t location, GpuRegister dest_reg, Type label_or_literal_type); // Some conditional branches with lhs = rhs are effectively NOPs, while some // others are effectively unconditional. MIPSR6 conditional branches require lhs != rhs. @@ -923,7 +1009,7 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer private: // Completes branch construction by determining and recording its type. - void InitializeType(bool is_call); + void InitializeType(Type initial_type); // Helper for the above. void InitShortOrLong(OffsetBits ofs_size, Type short_type, Type long_type); @@ -932,7 +1018,7 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer uint32_t target_; // Offset into assembler buffer in bytes. GpuRegister lhs_reg_; // Left-hand side register in conditional branches or - // indirect call register. + // destination register in literals. GpuRegister rhs_reg_; // Right-hand side register in conditional branches. BranchCondition condition_; // Condition for conditional branches. @@ -957,12 +1043,13 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer BranchCondition condition, GpuRegister lhs, GpuRegister rhs = ZERO); - void Call(Mips64Label* label, GpuRegister indirect_reg); + void Call(Mips64Label* label); void FinalizeLabeledBranch(Mips64Label* label); Branch* GetBranch(uint32_t branch_id); const Branch* GetBranch(uint32_t branch_id) const; + void EmitLiterals(); void PromoteBranches(); void EmitBranch(Branch* branch); void EmitBranches(); @@ -981,6 +1068,11 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer // The current overwrite location. uint32_t overwrite_location_; + // Use std::deque<> for literal labels to allow insertions at the end + // without invalidating pointers and references to existing elements. + ArenaDeque<Literal> literals_; + ArenaDeque<Literal> long_literals_; // 64-bit literals separated for alignment reasons. + // Data for AdjustedPosition(), see the description there. uint32_t last_position_adjustment_; uint32_t last_old_position_; diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc index ba8f25ea77..564559f92c 100644 --- a/compiler/utils/mips64/assembler_mips64_test.cc +++ b/compiler/utils/mips64/assembler_mips64_test.cc @@ -576,83 +576,83 @@ TEST_F(AssemblerMIPS64Test, Jalr) { RepeatRRNoDupes(&mips64::Mips64Assembler::Jalr, "jalr ${reg1}, ${reg2}"), "jalr"); } -TEST_F(AssemblerMIPS64Test, Jialc) { +TEST_F(AssemblerMIPS64Test, Balc) { mips64::Mips64Label label1, label2; - __ Jialc(&label1, mips64::T9); + __ Balc(&label1); constexpr size_t kAdduCount1 = 63; for (size_t i = 0; i != kAdduCount1; ++i) { __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); } __ Bind(&label1); - __ Jialc(&label2, mips64::T9); + __ Balc(&label2); constexpr size_t kAdduCount2 = 64; for (size_t i = 0; i != kAdduCount2; ++i) { __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); } __ Bind(&label2); - __ Jialc(&label1, mips64::T9); + __ Balc(&label1); std::string expected = ".set noreorder\n" - "lapc $t9, 1f\n" - "jialc $t9, 0\n" + + "balc 1f\n" + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + "1:\n" - "lapc $t9, 2f\n" - "jialc $t9, 0\n" + + "balc 2f\n" + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + "2:\n" - "lapc $t9, 1b\n" - "jialc $t9, 0\n"; - DriverStr(expected, "Jialc"); + "balc 1b\n"; + DriverStr(expected, "Balc"); } -TEST_F(AssemblerMIPS64Test, LongJialc) { +TEST_F(AssemblerMIPS64Test, LongBalc) { + constexpr uint32_t kNopCount1 = (1u << 25) + 1; + constexpr uint32_t kNopCount2 = (1u << 25) + 1; + constexpr uint32_t kRequiredCapacity = (kNopCount1 + kNopCount2 + 6u) * 4u; + ASSERT_LT(__ GetBuffer()->Capacity(), kRequiredCapacity); + __ GetBuffer()->ExtendCapacity(kRequiredCapacity); mips64::Mips64Label label1, label2; - __ Jialc(&label1, mips64::T9); - constexpr uint32_t kAdduCount1 = (1u << 18) + 1; - for (uint32_t i = 0; i != kAdduCount1; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + __ Balc(&label1); + for (uint32_t i = 0; i != kNopCount1; ++i) { + __ Nop(); } __ Bind(&label1); - __ Jialc(&label2, mips64::T9); - constexpr uint32_t kAdduCount2 = (1u << 18) + 1; - for (uint32_t i = 0; i != kAdduCount2; ++i) { - __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + __ Balc(&label2); + for (uint32_t i = 0; i != kNopCount2; ++i) { + __ Nop(); } __ Bind(&label2); - __ Jialc(&label1, mips64::T9); + __ Balc(&label1); - uint32_t offset_forward1 = 3 + kAdduCount1; // 3: account for auipc, daddiu and jic. + uint32_t offset_forward1 = 2 + kNopCount1; // 2: account for auipc and jialc. offset_forward1 <<= 2; - offset_forward1 += (offset_forward1 & 0x8000) << 1; // Account for sign extension in daddiu. + offset_forward1 += (offset_forward1 & 0x8000) << 1; // Account for sign extension in jialc. - uint32_t offset_forward2 = 3 + kAdduCount2; // 3: account for auipc, daddiu and jic. + uint32_t offset_forward2 = 2 + kNopCount2; // 2: account for auipc and jialc. offset_forward2 <<= 2; - offset_forward2 += (offset_forward2 & 0x8000) << 1; // Account for sign extension in daddiu. + offset_forward2 += (offset_forward2 & 0x8000) << 1; // Account for sign extension in jialc. - uint32_t offset_back = -(3 + kAdduCount2); // 3: account for auipc, daddiu and jic. + uint32_t offset_back = -(2 + kNopCount2); // 2: account for auipc and jialc. offset_back <<= 2; - offset_back += (offset_back & 0x8000) << 1; // Account for sign extension in daddiu. + offset_back += (offset_back & 0x8000) << 1; // Account for sign extension in jialc. + // Note, we're using the ".fill" directive to tell the assembler to generate many NOPs + // instead of generating them ourselves in the source code. This saves a few minutes + // of test time. std::ostringstream oss; oss << ".set noreorder\n" - "auipc $t9, 0x" << std::hex << High16Bits(offset_forward1) << "\n" - "daddiu $t9, 0x" << std::hex << Low16Bits(offset_forward1) << "\n" - "jialc $t9, 0\n" << - RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") << + "auipc $at, 0x" << std::hex << High16Bits(offset_forward1) << "\n" + "jialc $at, 0x" << std::hex << Low16Bits(offset_forward1) << "\n" + ".fill 0x" << std::hex << kNopCount1 << " , 4, 0\n" "1:\n" - "auipc $t9, 0x" << std::hex << High16Bits(offset_forward2) << "\n" - "daddiu $t9, 0x" << std::hex << Low16Bits(offset_forward2) << "\n" - "jialc $t9, 0\n" << - RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") << + "auipc $at, 0x" << std::hex << High16Bits(offset_forward2) << "\n" + "jialc $at, 0x" << std::hex << Low16Bits(offset_forward2) << "\n" + ".fill 0x" << std::hex << kNopCount2 << " , 4, 0\n" "2:\n" - "auipc $t9, 0x" << std::hex << High16Bits(offset_back) << "\n" - "daddiu $t9, 0x" << std::hex << Low16Bits(offset_back) << "\n" - "jialc $t9, 0\n"; + "auipc $at, 0x" << std::hex << High16Bits(offset_back) << "\n" + "jialc $at, 0x" << std::hex << Low16Bits(offset_back) << "\n"; std::string expected = oss.str(); - DriverStr(expected, "LongJialc"); + DriverStr(expected, "LongBalc"); } TEST_F(AssemblerMIPS64Test, Bc) { @@ -827,6 +827,258 @@ TEST_F(AssemblerMIPS64Test, LongBeqc) { // MISC // ////////// +TEST_F(AssemblerMIPS64Test, Lwpc) { + // Lwpc() takes an unsigned 19-bit immediate, while the GNU assembler needs a signed offset, + // hence the sign extension from bit 18 with `imm - ((imm & 0x40000) << 1)`. + // The GNU assembler also wants the offset to be a multiple of 4, which it will shift right + // by 2 positions when encoding, hence `<< 2` to compensate for that shift. + // We capture the value of the immediate with `.set imm, {imm}` because the value is needed + // twice for the sign extension, but `{imm}` is substituted only once. + const char* code = ".set imm, {imm}\nlw ${reg}, ((imm - ((imm & 0x40000) << 1)) << 2)($pc)"; + DriverStr(RepeatRIb(&mips64::Mips64Assembler::Lwpc, 19, code), "Lwpc"); +} + +TEST_F(AssemblerMIPS64Test, Lwupc) { + // The comment for the Lwpc test applies here as well. + const char* code = ".set imm, {imm}\nlwu ${reg}, ((imm - ((imm & 0x40000) << 1)) << 2)($pc)"; + DriverStr(RepeatRIb(&mips64::Mips64Assembler::Lwupc, 19, code), "Lwupc"); +} + +TEST_F(AssemblerMIPS64Test, Ldpc) { + // The comment for the Lwpc test applies here as well. + const char* code = ".set imm, {imm}\nld ${reg}, ((imm - ((imm & 0x20000) << 1)) << 3)($pc)"; + DriverStr(RepeatRIb(&mips64::Mips64Assembler::Ldpc, 18, code), "Ldpc"); +} + +TEST_F(AssemblerMIPS64Test, LoadFarthestNearLabelAddress) { + mips64::Mips64Label label; + __ LoadLabelAddress(mips64::V0, &label); + constexpr uint32_t kAdduCount = 0x3FFDE; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + __ Bind(&label); + + std::string expected = + "lapc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n"; + DriverStr(expected, "LoadFarthestNearLabelAddress"); + EXPECT_EQ(__ GetLabelLocation(&label), (1 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadNearestFarLabelAddress) { + mips64::Mips64Label label; + __ LoadLabelAddress(mips64::V0, &label); + constexpr uint32_t kAdduCount = 0x3FFDF; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + __ Bind(&label); + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "daddiu $v0, $at, %lo(2f - 1b)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n"; + DriverStr(expected, "LoadNearestFarLabelAddress"); + EXPECT_EQ(__ GetLabelLocation(&label), (2 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteral) { + mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips64::V0, mips64::kLoadWord, literal); + constexpr uint32_t kAdduCount = 0x3FFDE; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "lwpc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadFarthestNearLiteral"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteral) { + mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips64::V0, mips64::kLoadWord, literal); + constexpr uint32_t kAdduCount = 0x3FFDF; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "lw $v0, %lo(2f - 1b)($at)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadNearestFarLiteral"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteralUnsigned) { + mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips64::V0, mips64::kLoadUnsignedWord, literal); + constexpr uint32_t kAdduCount = 0x3FFDE; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "lwupc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadFarthestNearLiteralUnsigned"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteralUnsigned) { + mips64::Literal* literal = __ NewLiteral<uint32_t>(0x12345678); + __ LoadLiteral(mips64::V0, mips64::kLoadUnsignedWord, literal); + constexpr uint32_t kAdduCount = 0x3FFDF; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "lwu $v0, %lo(2f - 1b)($at)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n" + ".word 0x12345678\n"; + DriverStr(expected, "LoadNearestFarLiteralUnsigned"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadFarthestNearLiteralLong) { + mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal); + constexpr uint32_t kAdduCount = 0x3FFDD; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "ldpc $v0, 1f\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "1:\n" + ".dword 0x0123456789ABCDEF\n"; + DriverStr(expected, "LoadFarthestNearLiteralLong"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (1 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LoadNearestFarLiteralLong) { + mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal); + constexpr uint32_t kAdduCount = 0x3FFDE; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + + std::string expected = + "1:\n" + "auipc $at, %hi(2f - 1b)\n" + "ld $v0, %lo(2f - 1b)($at)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "2:\n" + ".dword 0x0123456789ABCDEF\n"; + DriverStr(expected, "LoadNearestFarLiteralLong"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (2 + kAdduCount) * 4); +} + +TEST_F(AssemblerMIPS64Test, LongLiteralAlignmentNop) { + mips64::Literal* literal1 = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + mips64::Literal* literal2 = __ NewLiteral<uint64_t>(UINT64_C(0x5555555555555555)); + mips64::Literal* literal3 = __ NewLiteral<uint64_t>(UINT64_C(0xAAAAAAAAAAAAAAAA)); + __ LoadLiteral(mips64::A1, mips64::kLoadDoubleword, literal1); + __ LoadLiteral(mips64::A2, mips64::kLoadDoubleword, literal2); + __ LoadLiteral(mips64::A3, mips64::kLoadDoubleword, literal3); + __ LoadLabelAddress(mips64::V0, literal1->GetLabel()); + __ LoadLabelAddress(mips64::V1, literal2->GetLabel()); + // A nop will be inserted here before the 64-bit literals. + + std::string expected = + "ldpc $a1, 1f\n" + // The GNU assembler incorrectly requires the ldpc instruction to be located + // at an address that's a multiple of 8. TODO: Remove this workaround if/when + // the assembler is fixed. + // "ldpc $a2, 2f\n" + ".word 0xECD80004\n" + "ldpc $a3, 3f\n" + "lapc $v0, 1f\n" + "lapc $v1, 2f\n" + "nop\n" + "1:\n" + ".dword 0x0123456789ABCDEF\n" + "2:\n" + ".dword 0x5555555555555555\n" + "3:\n" + ".dword 0xAAAAAAAAAAAAAAAA\n"; + DriverStr(expected, "LongLiteralAlignmentNop"); + EXPECT_EQ(__ GetLabelLocation(literal1->GetLabel()), 6 * 4u); + EXPECT_EQ(__ GetLabelLocation(literal2->GetLabel()), 8 * 4u); + EXPECT_EQ(__ GetLabelLocation(literal3->GetLabel()), 10 * 4u); +} + +TEST_F(AssemblerMIPS64Test, LongLiteralAlignmentNoNop) { + mips64::Literal* literal1 = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + mips64::Literal* literal2 = __ NewLiteral<uint64_t>(UINT64_C(0x5555555555555555)); + __ LoadLiteral(mips64::A1, mips64::kLoadDoubleword, literal1); + __ LoadLiteral(mips64::A2, mips64::kLoadDoubleword, literal2); + __ LoadLabelAddress(mips64::V0, literal1->GetLabel()); + __ LoadLabelAddress(mips64::V1, literal2->GetLabel()); + + std::string expected = + "ldpc $a1, 1f\n" + // The GNU assembler incorrectly requires the ldpc instruction to be located + // at an address that's a multiple of 8. TODO: Remove this workaround if/when + // the assembler is fixed. + // "ldpc $a2, 2f\n" + ".word 0xECD80003\n" + "lapc $v0, 1f\n" + "lapc $v1, 2f\n" + "1:\n" + ".dword 0x0123456789ABCDEF\n" + "2:\n" + ".dword 0x5555555555555555\n"; + DriverStr(expected, "LongLiteralAlignmentNoNop"); + EXPECT_EQ(__ GetLabelLocation(literal1->GetLabel()), 4 * 4u); + EXPECT_EQ(__ GetLabelLocation(literal2->GetLabel()), 6 * 4u); +} + +TEST_F(AssemblerMIPS64Test, FarLongLiteralAlignmentNop) { + mips64::Literal* literal = __ NewLiteral<uint64_t>(UINT64_C(0x0123456789ABCDEF)); + __ LoadLiteral(mips64::V0, mips64::kLoadDoubleword, literal); + __ LoadLabelAddress(mips64::V1, literal->GetLabel()); + constexpr uint32_t kAdduCount = 0x3FFDF; + for (uint32_t i = 0; i != kAdduCount; ++i) { + __ Addu(mips64::ZERO, mips64::ZERO, mips64::ZERO); + } + // A nop will be inserted here before the 64-bit literal. + + std::string expected = + "1:\n" + "auipc $at, %hi(3f - 1b)\n" + "ld $v0, %lo(3f - 1b)($at)\n" + "2:\n" + "auipc $at, %hi(3f - 2b)\n" + "daddiu $v1, $at, %lo(3f - 2b)\n" + + RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") + + "nop\n" + "3:\n" + ".dword 0x0123456789ABCDEF\n"; + DriverStr(expected, "FarLongLiteralAlignmentNop"); + EXPECT_EQ(__ GetLabelLocation(literal->GetLabel()), (5 + kAdduCount) * 4); +} + TEST_F(AssemblerMIPS64Test, Bitswap) { DriverStr(RepeatRR(&mips64::Mips64Assembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap"); } @@ -889,6 +1141,22 @@ TEST_F(AssemblerMIPS64Test, Dinsu) { DriverStr(expected.str(), "Dinsu"); } +TEST_F(AssemblerMIPS64Test, Lsa) { + DriverStr(RepeatRRRIb(&mips64::Mips64Assembler::Lsa, + 2, + "lsa ${reg1}, ${reg2}, ${reg3}, {imm}", + 1), + "lsa"); +} + +TEST_F(AssemblerMIPS64Test, Dlsa) { + DriverStr(RepeatRRRIb(&mips64::Mips64Assembler::Dlsa, + 2, + "dlsa ${reg1}, ${reg2}, ${reg3}, {imm}", + 1), + "dlsa"); +} + TEST_F(AssemblerMIPS64Test, Wsbh) { DriverStr(RepeatRR(&mips64::Mips64Assembler::Wsbh, "wsbh ${reg1}, ${reg2}"), "wsbh"); } diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 52765f9e73..85ae61f1bd 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -508,7 +508,7 @@ TEST_F(VerifierDepsTest, Assignable_DestinationInBoot1) { /* src */ "LMySSLSocket;", /* is_strict */ true, /* is_assignable */ true)); - ASSERT_TRUE(HasAssignable("Ljava/net/Socket;", "LMySSLSocket;", true)); + ASSERT_TRUE(HasAssignable("Ljava/net/Socket;", "Ljavax/net/ssl/SSLSocket;", true)); } TEST_F(VerifierDepsTest, Assignable_DestinationInBoot2) { @@ -516,7 +516,7 @@ TEST_F(VerifierDepsTest, Assignable_DestinationInBoot2) { /* src */ "LMySimpleTimeZone;", /* is_strict */ true, /* is_assignable */ true)); - ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "LMySimpleTimeZone;", true)); + ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "Ljava/util/SimpleTimeZone;", true)); } TEST_F(VerifierDepsTest, Assignable_DestinationInBoot3) { @@ -539,21 +539,9 @@ TEST_F(VerifierDepsTest, Assignable_BothArrays_Resolved) { ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "Ljava/util/SimpleTimeZone;", true)); } -TEST_F(VerifierDepsTest, Assignable_BothArrays_Erroneous) { - ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "[[Ljava/util/TimeZone;", - /* src */ "[[LMyErroneousTimeZone;", - /* is_strict */ true, - /* is_assignable */ true)); - // If the component type of an array is erroneous, we record the dependency on - // the array type. - ASSERT_FALSE(HasAssignable("[[Ljava/util/TimeZone;", "[[LMyErroneousTimeZone;", true)); - ASSERT_TRUE(HasAssignable("[Ljava/util/TimeZone;", "[LMyErroneousTimeZone;", true)); - ASSERT_FALSE(HasAssignable("Ljava/util/TimeZone;", "LMyErroneousTimeZone;", true)); -} - - // We test that VerifierDeps does not try to optimize by storing assignability - // of the component types. This is due to the fact that the component type may - // be an erroneous class, even though the array type has resolved status. +// We test that VerifierDeps does not try to optimize by storing assignability +// of the component types. This is due to the fact that the component type may +// be an erroneous class, even though the array type has resolved status. TEST_F(VerifierDepsTest, Assignable_ArrayToInterface1) { ASSERT_TRUE(TestAssignabilityRecording(/* dst */ "Ljava/io/Serializable;", @@ -584,7 +572,7 @@ TEST_F(VerifierDepsTest, NotAssignable_DestinationInBoot1) { /* src */ "LMySSLSocket;", /* is_strict */ true, /* is_assignable */ false)); - ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "LMySSLSocket;", false)); + ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "Ljavax/net/ssl/SSLSocket;", false)); } TEST_F(VerifierDepsTest, NotAssignable_DestinationInBoot2) { @@ -592,7 +580,7 @@ TEST_F(VerifierDepsTest, NotAssignable_DestinationInBoot2) { /* src */ "LMySimpleTimeZone;", /* is_strict */ true, /* is_assignable */ false)); - ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "LMySimpleTimeZone;", false)); + ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "Ljava/util/SimpleTimeZone;", false)); } TEST_F(VerifierDepsTest, NotAssignable_BothArrays) { @@ -608,16 +596,6 @@ TEST_F(VerifierDepsTest, ArgumentType_ResolvedClass) { ASSERT_TRUE(HasClass("Ljava/lang/Thread;", true, "public")); } -TEST_F(VerifierDepsTest, ArgumentType_ResolvedReferenceArray) { - ASSERT_TRUE(VerifyMethod("ArgumentType_ResolvedReferenceArray")); - ASSERT_TRUE(HasClass("[Ljava/lang/Thread;", true, "public final abstract")); -} - -TEST_F(VerifierDepsTest, ArgumentType_ResolvedPrimitiveArray) { - ASSERT_TRUE(VerifyMethod("ArgumentType_ResolvedPrimitiveArray")); - ASSERT_TRUE(HasClass("[B", true, "public final abstract")); -} - TEST_F(VerifierDepsTest, ArgumentType_UnresolvedClass) { ASSERT_TRUE(VerifyMethod("ArgumentType_UnresolvedClass")); ASSERT_TRUE(HasClass("LUnresolvedClass;", false)); @@ -654,7 +632,7 @@ TEST_F(VerifierDepsTest, InvokeArgumentType) { TEST_F(VerifierDepsTest, MergeTypes_RegisterLines) { ASSERT_TRUE(VerifyMethod("MergeTypes_RegisterLines")); - ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "LMySocketTimeoutException;", true)); + ASSERT_TRUE(HasAssignable("Ljava/lang/Exception;", "Ljava/net/SocketTimeoutException;", true)); ASSERT_TRUE(HasAssignable( "Ljava/lang/Exception;", "Ljava/util/concurrent/TimeoutException;", true)); } @@ -714,11 +692,6 @@ TEST_F(VerifierDepsTest, NewInstance_Unresolved) { ASSERT_TRUE(HasClass("LUnresolvedClass;", false)); } -TEST_F(VerifierDepsTest, NewArray_Resolved) { - ASSERT_TRUE(VerifyMethod("NewArray_Resolved")); - ASSERT_TRUE(HasClass("[Ljava/lang/IllegalStateException;", true, "public final abstract")); -} - TEST_F(VerifierDepsTest, NewArray_Unresolved) { ASSERT_TRUE(VerifyMethod("NewArray_Unresolved")); ASSERT_TRUE(HasClass("[LUnresolvedClass;", false)); @@ -841,7 +814,7 @@ TEST_F(VerifierDepsTest, InstanceField_Resolved_DeclaredInReferenced) { "public", "Ljava/io/InterruptedIOException;")); ASSERT_TRUE(HasAssignable( - "Ljava/io/InterruptedIOException;", "LMySocketTimeoutException;", true)); + "Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true)); } TEST_F(VerifierDepsTest, InstanceField_Resolved_DeclaredInSuperclass1) { @@ -854,7 +827,7 @@ TEST_F(VerifierDepsTest, InstanceField_Resolved_DeclaredInSuperclass1) { "public", "Ljava/io/InterruptedIOException;")); ASSERT_TRUE(HasAssignable( - "Ljava/io/InterruptedIOException;", "LMySocketTimeoutException;", true)); + "Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true)); } TEST_F(VerifierDepsTest, InstanceField_Resolved_DeclaredInSuperclass2) { @@ -866,7 +839,7 @@ TEST_F(VerifierDepsTest, InstanceField_Resolved_DeclaredInSuperclass2) { "public", "Ljava/io/InterruptedIOException;")); ASSERT_TRUE(HasAssignable( - "Ljava/io/InterruptedIOException;", "LMySocketTimeoutException;", true)); + "Ljava/io/InterruptedIOException;", "Ljava/net/SocketTimeoutException;", true)); } TEST_F(VerifierDepsTest, InstanceField_Unresolved_ReferrerInBoot) { @@ -995,7 +968,7 @@ TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInReferenced) { "public", "Ljava/lang/Throwable;")); // Type dependency on `this` argument. - ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "LMySocketTimeoutException;", true)); + ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "Ljava/net/SocketTimeoutException;", true)); } TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass1) { @@ -1009,7 +982,7 @@ TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass1) { "public", "Ljava/lang/Throwable;")); // Type dependency on `this` argument. - ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "LMySocketTimeoutException;", true)); + ASSERT_TRUE(HasAssignable("Ljava/lang/Throwable;", "Ljava/net/SocketTimeoutException;", true)); } TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass2) { @@ -1123,7 +1096,7 @@ TEST_F(VerifierDepsTest, InvokeSuper_ThisAssignable) { TEST_F(VerifierDepsTest, InvokeSuper_ThisNotAssignable) { ASSERT_FALSE(VerifyMethod("InvokeSuper_ThisNotAssignable")); ASSERT_TRUE(HasClass("Ljava/lang/Integer;", true, "public final")); - ASSERT_TRUE(HasAssignable("Ljava/lang/Integer;", "LMain;", false)); + ASSERT_TRUE(HasAssignable("Ljava/lang/Integer;", "Ljava/lang/Thread;", false)); ASSERT_TRUE(HasMethod( "virtual", "Ljava/lang/Integer;", "intValue", "()I", true, "public", "Ljava/lang/Integer;")); } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 8fb40402b7..ece81e3919 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -33,6 +33,9 @@ #include <sys/utsname.h> #endif +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + #include "arch/instruction_set_features.h" #include "arch/mips/instruction_set_features_mips.h" #include "art_method-inl.h" @@ -85,6 +88,9 @@ namespace art { +using android::base::StringAppendV; +using android::base::StringPrintf; + static constexpr size_t kDefaultMinDexFilesForSwap = 2; static constexpr size_t kDefaultMinDexFileCumulativeSizeForSwap = 20 * MB; @@ -96,7 +102,7 @@ static std::string CommandLine() { for (int i = 0; i < original_argc; ++i) { command.push_back(original_argv[i]); } - return Join(command, ' '); + return android::base::Join(command, ' '); } // A stripped version. Remove some less essential parameters. If we see a "--zip-fd=" parameter, be @@ -108,7 +114,7 @@ static std::string StrippedCommandLine() { // Do a pre-pass to look for zip-fd. bool saw_zip_fd = false; for (int i = 0; i < original_argc; ++i) { - if (StartsWith(original_argv[i], "--zip-fd=")) { + if (android::base::StartsWith(original_argv[i], "--zip-fd=")) { saw_zip_fd = true; break; } @@ -123,17 +129,17 @@ static std::string StrippedCommandLine() { } // Any instruction-setXXX is dropped. - if (StartsWith(original_argv[i], "--instruction-set")) { + if (android::base::StartsWith(original_argv[i], "--instruction-set")) { continue; } // The boot image is dropped. - if (StartsWith(original_argv[i], "--boot-image=")) { + if (android::base::StartsWith(original_argv[i], "--boot-image=")) { continue; } // The image format is dropped. - if (StartsWith(original_argv[i], "--image-format=")) { + if (android::base::StartsWith(original_argv[i], "--image-format=")) { continue; } @@ -142,11 +148,11 @@ static std::string StrippedCommandLine() { // However, we prefer to drop this when we saw --zip-fd. if (saw_zip_fd) { // Drop anything --zip-X, --dex-X, --oat-X, --swap-X, or --app-image-X - if (StartsWith(original_argv[i], "--zip-") || - StartsWith(original_argv[i], "--dex-") || - StartsWith(original_argv[i], "--oat-") || - StartsWith(original_argv[i], "--swap-") || - StartsWith(original_argv[i], "--app-image-")) { + if (android::base::StartsWith(original_argv[i], "--zip-") || + android::base::StartsWith(original_argv[i], "--dex-") || + android::base::StartsWith(original_argv[i], "--oat-") || + android::base::StartsWith(original_argv[i], "--swap-") || + android::base::StartsWith(original_argv[i], "--app-image-")) { continue; } } @@ -159,7 +165,7 @@ static std::string StrippedCommandLine() { // It seems only "/system/bin/dex2oat" is left, or not even that. Use a pretty line. return "Starting dex2oat."; } - return Join(command, ' '); + return android::base::Join(command, ' '); } static void UsageErrorV(const char* fmt, va_list ap) { @@ -999,7 +1005,7 @@ class Dex2Oat FINAL { if (last_dex_dot != std::string::npos) { dex_file = dex_file.substr(0, last_dex_dot); } - if (StartsWith(dex_file, "core-")) { + if (android::base::StartsWith(dex_file, "core-")) { infix = dex_file.substr(strlen("core")); } } @@ -1059,7 +1065,7 @@ class Dex2Oat FINAL { in.insert(last_dot, infix); } } - if (EndsWith(in, ".jar")) { + if (android::base::EndsWith(in, ".jar")) { in = in.substr(0, in.length() - strlen(".jar")) + (replace_suffix != nullptr ? replace_suffix : ""); } @@ -1289,17 +1295,23 @@ class Dex2Oat FINAL { DCHECK_EQ(output_vdex_fd_, -1); std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex"); - std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_filename.c_str())); - if (vdex_file.get() == nullptr) { - PLOG(ERROR) << "Failed to open vdex file: " << vdex_filename; - return false; - } - if (fchmod(vdex_file->Fd(), 0644) != 0) { - PLOG(ERROR) << "Failed to make vdex file world readable: " << vdex_filename; - vdex_file->Erase(); - return false; + if (vdex_filename == input_vdex_) { + update_input_vdex_ = true; + std::unique_ptr<File> vdex_file(OS::OpenFileReadWrite(vdex_filename.c_str())); + vdex_files_.push_back(std::move(vdex_file)); + } else { + std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_filename.c_str())); + if (vdex_file.get() == nullptr) { + PLOG(ERROR) << "Failed to open vdex file: " << vdex_filename; + return false; + } + if (fchmod(vdex_file->Fd(), 0644) != 0) { + PLOG(ERROR) << "Failed to make vdex file world readable: " << vdex_filename; + vdex_file->Erase(); + return false; + } + vdex_files_.push_back(std::move(vdex_file)); } - vdex_files_.push_back(std::move(vdex_file)); } } else { std::unique_ptr<File> oat_file(new File(oat_fd_, oat_location_, /* check_usage */ true)); @@ -1313,7 +1325,6 @@ class Dex2Oat FINAL { } oat_files_.push_back(std::move(oat_file)); - DCHECK_NE(input_vdex_fd_, output_vdex_fd_); if (input_vdex_fd_ != -1) { struct stat s; int rc = TEMP_FAILURE_RETRY(fstat(input_vdex_fd_, &s)); @@ -1346,8 +1357,13 @@ class Dex2Oat FINAL { return false; } vdex_file->DisableAutoClose(); - if (vdex_file->SetLength(0) != 0) { - PLOG(WARNING) << "Truncating vdex file " << vdex_location << " failed."; + if (input_vdex_file_ != nullptr && output_vdex_fd_ == input_vdex_fd_) { + update_input_vdex_ = true; + } else { + if (vdex_file->SetLength(0) != 0) { + PLOG(ERROR) << "Truncating vdex file " << vdex_location << " failed."; + return false; + } } vdex_files_.push_back(std::move(vdex_file)); @@ -1484,7 +1500,7 @@ class Dex2Oat FINAL { for (const gc::space::ImageSpace* image_space : image_spaces) { image_filenames.push_back(image_space->GetImageFilename()); } - std::string image_file_location = Join(image_filenames, ':'); + std::string image_file_location = android::base::Join(image_filenames, ':'); if (!image_file_location.empty()) { key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location); } @@ -1536,6 +1552,7 @@ class Dex2Oat FINAL { instruction_set_features_.get(), key_value_store_.get(), verify, + update_input_vdex_, &opened_dex_files_map, &opened_dex_files)) { return false; @@ -1687,7 +1704,7 @@ class Dex2Oat FINAL { } } - if (StartsWith(dex_location, filter.c_str())) { + if (android::base::StartsWith(dex_location, filter.c_str())) { VLOG(compiler) << "Disabling inlining from " << dex_file->GetLocation(); no_inline_from_dex_files_.push_back(dex_file); break; @@ -2362,10 +2379,10 @@ class Dex2Oat FINAL { RuntimeOptions raw_options; if (boot_image_filename_.empty()) { std::string boot_class_path = "-Xbootclasspath:"; - boot_class_path += Join(dex_filenames_, ':'); + boot_class_path += android::base::Join(dex_filenames_, ':'); raw_options.push_back(std::make_pair(boot_class_path, nullptr)); std::string boot_class_path_locations = "-Xbootclasspath-locations:"; - boot_class_path_locations += Join(dex_locations_, ':'); + boot_class_path_locations += android::base::Join(dex_locations_, ':'); raw_options.push_back(std::make_pair(boot_class_path_locations, nullptr)); } else { std::string boot_image_option = "-Ximage:"; @@ -2579,7 +2596,7 @@ class Dex2Oat FINAL { while (in_stream.good()) { std::string dot; std::getline(in_stream, dot); - if (StartsWith(dot, "#") || dot.empty()) { + if (android::base::StartsWith(dot, "#") || dot.empty()) { continue; } if (process != nullptr) { @@ -2726,6 +2743,9 @@ class Dex2Oat FINAL { // See CompilerOptions.force_determinism_. bool force_determinism_; + // Whether the given input vdex is also the output. + bool update_input_vdex_ = false; + DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat); }; diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index b6b62a80dc..cdb3b9fe2a 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -19,11 +19,15 @@ #include <string> #include <vector> +#include <sys/wait.h> +#include <unistd.h> + +#include "android-base/stringprintf.h" + #include "common_runtime_test.h" #include "base/logging.h" #include "base/macros.h" -#include "base/stringprintf.h" #include "dex_file-inl.h" #include "dex2oat_environment_test.h" #include "jit/offline_profiling_info.h" @@ -31,9 +35,6 @@ #include "oat_file.h" #include "utils.h" -#include <sys/wait.h> -#include <unistd.h> - namespace art { class Dex2oatTest : public Dex2oatEnvironmentTest { @@ -217,7 +218,7 @@ class Dex2oatSwapTest : public Dex2oatTest { std::unique_ptr<ScratchFile> sf; if (use_fd) { sf.reset(new ScratchFile()); - copy.push_back(StringPrintf("--swap-fd=%d", sf->GetFd())); + copy.push_back(android::base::StringPrintf("--swap-fd=%d", sf->GetFd())); } else { std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap"; copy.push_back("--swap-file=" + swap_location); diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 916984c261..d5776fa61e 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -42,7 +42,8 @@ #include <sstream> #include <vector> -#include "base/stringprintf.h" +#include "android-base/stringprintf.h" + #include "dexdump_cfg.h" #include "dex_file-inl.h" #include "dex_file_types.h" @@ -887,8 +888,10 @@ static std::unique_ptr<char[]> indexString(const DexFile* pDexFile, const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_); const Signature signature = pDexFile->GetMethodSignature(pMethodId); const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_); - method = StringPrintf("%s.%s:%s", - backDescriptor, name, signature.ToString().c_str()); + method = android::base::StringPrintf("%s.%s:%s", + backDescriptor, + name, + signature.ToString().c_str()); } if (secondary_index < pDexFile->GetHeader().proto_ids_size_) { const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index); diff --git a/dexdump/dexdump_test.cc b/dexdump/dexdump_test.cc index d28ca2834e..53dda6a995 100644 --- a/dexdump/dexdump_test.cc +++ b/dexdump/dexdump_test.cc @@ -21,7 +21,6 @@ #include <sys/types.h> #include <unistd.h> -#include "base/stringprintf.h" #include "common_runtime_test.h" #include "runtime/arch/instruction_set.h" #include "runtime/os.h" diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index cfe48378ae..cac60900bc 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -30,7 +30,8 @@ #include <sstream> #include <vector> -#include "base/stringprintf.h" +#include "android-base/stringprintf.h" + #include "dex_ir_builder.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" @@ -43,6 +44,8 @@ namespace art { +using android::base::StringPrintf; + /* * Flags for use with createAccessFlagStr(). */ diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 665baa6c73..46a1c43548 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -21,7 +21,6 @@ #include <sys/types.h> #include <unistd.h> -#include "base/stringprintf.h" #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" #include "utils.h" diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc index da1dd7fd89..13209427c9 100644 --- a/dexlist/dexlist_test.cc +++ b/dexlist/dexlist_test.cc @@ -21,7 +21,6 @@ #include <sys/types.h> #include <unistd.h> -#include "base/stringprintf.h" #include "common_runtime_test.h" #include "runtime/arch/instruction_set.h" #include "runtime/gc/heap.h" diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc index 47873951fe..1f6b87447f 100644 --- a/disassembler/disassembler_mips.cc +++ b/disassembler/disassembler_mips.cc @@ -22,6 +22,8 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "base/bit_utils.h" + using android::base::StringPrintf; namespace art { @@ -137,6 +139,8 @@ static const MipsInstruction gMipsInstructions[] = { { kSpecial0Mask | (0x1f << 16) | 0x7ff, (0x01 << 6) | 0x11, "clo", "DS" }, { kSpecial0Mask | (0x1f << 16) | 0x7ff, (0x01 << 6) | 0x12, "dclz", "DS" }, { kSpecial0Mask | (0x1f << 16) | 0x7ff, (0x01 << 6) | 0x13, "dclo", "DS" }, + { kSpecial0Mask | 0x73f, 0x05, "lsa", "DSTj" }, + { kSpecial0Mask | 0x73f, 0x15, "dlsa", "DSTj" }, // TODO: sdbbp // SPECIAL2 @@ -154,6 +158,7 @@ static const MipsInstruction gMipsInstructions[] = { { kSpecial3Mask | 0x3f, (31 << kOpcodeShift), "ext", "TSAZ", }, { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 3, "dext", "TSAZ", }, { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 4, "ins", "TSAz", }, + { kSpecial3Mask | 0x3f, (31 << kOpcodeShift) | 6, "dinsu", "TSFz", }, { kSpecial3Mask | (0x1f << 21) | (0x1f << 6) | 0x3f, (31 << kOpcodeShift) | (16 << 6) | 32, "seb", @@ -218,8 +223,8 @@ static const MipsInstruction gMipsInstructions[] = { { kITypeMask, 12 << kOpcodeShift, "andi", "TSi", }, { kITypeMask, 13 << kOpcodeShift, "ori", "TSi", }, { kITypeMask, 14 << kOpcodeShift, "xori", "TSi", }, - { kITypeMask | (0x1f << 21), 15 << kOpcodeShift, "lui", "TI", }, - { kITypeMask, 15 << kOpcodeShift, "aui", "TSI", }, + { kITypeMask | (0x1f << 21), 15 << kOpcodeShift, "lui", "Ti", }, + { kITypeMask, 15 << kOpcodeShift, "aui", "TSi", }, { kITypeMask | (0x3e3 << 16), (17 << kOpcodeShift) | (8 << 21), "bc1f", "cB" }, { kITypeMask | (0x3e3 << 16), (17 << kOpcodeShift) | (8 << 21) | (1 << 16), "bc1t", "cB" }, @@ -335,6 +340,8 @@ static const MipsInstruction gMipsInstructions[] = { { kITypeMask | (0x1f << 16), (59u << kOpcodeShift) | (30 << 16), "auipc", "Si" }, { kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (0 << 19), "addiupc", "Sp" }, { kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (1 << 19), "lwpc", "So" }, + { kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (2 << 19), "lwupc", "So" }, + { kITypeMask | (0x7 << 18), (59u << kOpcodeShift) | (6 << 18), "ldpc", "S0" }, { kITypeMask, 61u << kOpcodeShift, "sdc1", "tO", }, { kITypeMask | (0x1f << 21), 62u << kOpcodeShift, "jialc", "Ti" }, { kITypeMask | (1 << 21), (62u << kOpcodeShift) | (1 << 21), "bnezc", "Sb" }, // TODO: de-dup? @@ -468,6 +475,7 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { case 'D': args << 'r' << rd; break; case 'd': args << 'f' << rd; break; case 'a': args << 'f' << sa; break; + case 'F': args << (sa + 32); break; // dinsu position. case 'f': // Floating point "fmt". { size_t fmt = (instruction >> 21) & 0x7; // TODO: other fmts? @@ -481,12 +489,12 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { } continue; // No ", ". } - case 'I': // Upper 16-bit immediate. - args << reinterpret_cast<void*>((instruction & 0xffff) << 16); - break; case 'i': // Sign-extended lower 16-bit immediate. args << static_cast<int16_t>(instruction & 0xffff); break; + case 'j': // sa value for lsa/dlsa. + args << (sa + 1); + break; case 'L': // Jump label. { // TODO: is this right? @@ -512,7 +520,7 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { } } break; - case 'o': // 19-bit offset in lwpc. + case 'o': // 19-bit offset in lwpc and lwupc. { int32_t offset = (instruction & 0x7ffff) - ((instruction & 0x40000) << 1); offset <<= 2; @@ -520,6 +528,15 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { args << StringPrintf(" ; %+d", offset); } break; + case '0': // 18-bit offset in ldpc. + { + int32_t offset = (instruction & 0x3ffff) - ((instruction & 0x20000) << 1); + offset <<= 3; + uintptr_t ptr = RoundDown(reinterpret_cast<uintptr_t>(instr_ptr), 8); + args << FormatInstructionPointer(reinterpret_cast<const uint8_t*>(ptr + offset)); + args << StringPrintf(" ; %+d", offset); + } + break; case 'P': // 26-bit offset in bc and balc. { int32_t offset = (instruction & 0x3ffffff) - ((instruction & 0x2000000) << 1); @@ -541,7 +558,7 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { case 'T': args << 'r' << rt; break; case 't': args << 'f' << rt; break; case 'Z': args << (rd + 1); break; // sz ([d]ext size). - case 'z': args << (rd - sa + 1); break; // sz ([d]ins size). + case 'z': args << (rd - sa + 1); break; // sz ([d]ins, dinsu size). } if (*(args_fmt + 1)) { args << ", "; @@ -551,17 +568,14 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { } } - // TODO: Simplify this once these sequences are simplified in the compiler. // Special cases for sequences of: // pc-relative +/- 2GB branch: // auipc reg, imm // jic reg, imm // pc-relative +/- 2GB branch and link: // auipc reg, imm - // daddiu reg, reg, imm - // jialc reg, 0 - if (((op == 0x36 && rs == 0 && rt != 0) || // jic - (op == 0x19 && rs == rt && rt != 0)) && // daddiu + // jialc reg, imm + if (((op == 0x36 || op == 0x3E) && rs == 0 && rt != 0) && // ji[al]c last_ptr_ && (intptr_t)instr_ptr - (intptr_t)last_ptr_ == 4 && (last_instr_ & 0xFC1F0000) == 0xEC1E0000 && // auipc ((last_instr_ >> 21) & 0x1F) == rt) { @@ -569,9 +583,9 @@ size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { offset -= (offset & 0x8000) << 1; offset -= 4; if (op == 0x36) { - args << " ; b "; + args << " ; bc "; } else { - args << " ; move r" << rt << ", "; + args << " ; balc "; } args << FormatInstructionPointer(instr_ptr + (int32_t)offset); args << StringPrintf(" ; %+d", (int32_t)offset); diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc index a374686dc5..f307cbc4f5 100644 --- a/imgdiag/imgdiag.cc +++ b/imgdiag/imgdiag.cc @@ -26,9 +26,10 @@ #include <map> #include <unordered_set> +#include "android-base/stringprintf.h" + #include "art_method-inl.h" #include "base/unix_file/fd_file.h" -#include "base/stringprintf.h" #include "gc/space/image_space.h" #include "gc/heap.h" #include "mirror/class-inl.h" @@ -46,6 +47,8 @@ namespace art { +using android::base::StringPrintf; + class ImgDiagDumper { public: explicit ImgDiagDumper(std::ostream* os, diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc index 9f771ba8a3..3f2afc0696 100644 --- a/imgdiag/imgdiag_test.cc +++ b/imgdiag/imgdiag_test.cc @@ -20,12 +20,13 @@ #include "common_runtime_test.h" +#include "android-base/stringprintf.h" + #include "runtime/os.h" #include "runtime/arch/instruction_set.h" #include "runtime/utils.h" #include "runtime/gc/space/image_space.h" #include "runtime/gc/heap.h" -#include "base/stringprintf.h" #include <sys/types.h> #include <unistd.h> @@ -57,7 +58,7 @@ class ImgDiagTest : public CommonRuntimeTest { virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { // Needs to live until CommonRuntimeTest::SetUp finishes, since we pass it a cstring. - runtime_args_image_ = StringPrintf("-Ximage:%s", GetCoreArtLocation().c_str()); + runtime_args_image_ = android::base::StringPrintf("-Ximage:%s", GetCoreArtLocation().c_str()); options->push_back(std::make_pair(runtime_args_image_, nullptr)); } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 80c7113175..148ee88669 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -26,6 +26,9 @@ #include <unordered_set> #include <vector> +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + #include "arch/instruction_set_features.h" #include "art_field-inl.h" #include "art_method-inl.h" @@ -74,6 +77,8 @@ namespace art { +using android::base::StringPrintf; + const char* image_methods_descriptions_[] = { "kResolutionMethod", "kImtConflictMethod", @@ -87,6 +92,7 @@ const char* image_methods_descriptions_[] = { const char* image_roots_descriptions_[] = { "kDexCaches", "kClassRoots", + "kClassLoader", }; // Map is so that we don't allocate multiple dex files for the same OatDexFile. @@ -668,6 +674,12 @@ class OatDumper { } private: + // All of the elements from one container to another. + template <typename Dest, typename Src> + static void AddAll(Dest& dest, const Src& src) { + dest.insert(src.begin(), src.end()); + } + void WalkClass(const DexFile& dex_file, const DexFile::ClassDef& class_def) { const uint8_t* class_data = dex_file.GetClassData(class_def); if (class_data == nullptr) { // empty class such as a marker interface? @@ -1498,12 +1510,13 @@ class ImageDumper { os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n"; static_assert(arraysize(image_roots_descriptions_) == static_cast<size_t>(ImageHeader::kImageRootsMax), "sizes must match"); - for (int i = 0; i < ImageHeader::kImageRootsMax; i++) { + DCHECK_LE(image_header_.GetImageRoots()->GetLength(), ImageHeader::kImageRootsMax); + for (int32_t i = 0, size = image_header_.GetImageRoots()->GetLength(); i != size; ++i) { ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i); const char* image_root_description = image_roots_descriptions_[i]; mirror::Object* image_root_object = image_header_.GetImageRoot(image_root); indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object); - if (image_root_object->IsObjectArray()) { + if (image_root_object != nullptr && image_root_object->IsObjectArray()) { mirror::ObjectArray<mirror::Object>* image_root_object_array = image_root_object->AsObjectArray<mirror::Object>(); ScopedIndentation indent2(&vios_); @@ -2952,7 +2965,7 @@ class IMTDumper { table_index++; std::string p_name = ptr2->PrettyMethod(true); - if (StartsWith(p_name, method.c_str())) { + if (android::base::StartsWith(p_name, method.c_str())) { std::cerr << " Slot " << index << " (" @@ -2965,7 +2978,7 @@ class IMTDumper { } } else { std::string p_name = ptr->PrettyMethod(true); - if (StartsWith(p_name, method.c_str())) { + if (android::base::StartsWith(p_name, method.c_str())) { std::cerr << " Slot " << index << " (1)" << std::endl; std::cerr << " " << p_name << std::endl; } else { @@ -2978,7 +2991,7 @@ class IMTDumper { for (ArtMethod& iface_method : iface->GetMethods(pointer_size)) { if (ImTable::GetImtIndex(&iface_method) == index) { std::string i_name = iface_method.PrettyMethod(true); - if (StartsWith(i_name, method.c_str())) { + if (android::base::StartsWith(i_name, method.c_str())) { std::cerr << " Slot " << index << " (1)" << std::endl; std::cerr << " " << p_name << " (" << i_name << ")" << std::endl; } @@ -2997,7 +3010,7 @@ class IMTDumper { while (in_stream.good()) { std::string dot; std::getline(in_stream, dot); - if (StartsWith(dot, "#") || dot.empty()) { + if (android::base::StartsWith(dot, "#") || dot.empty()) { continue; } output.push_back(dot); diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc index 22db818086..e77d03bae7 100644 --- a/oatdump/oatdump_test.cc +++ b/oatdump/oatdump_test.cc @@ -18,9 +18,10 @@ #include <string> #include <vector> +#include "android-base/strings.h" + #include "common_runtime_test.h" -#include "base/stringprintf.h" #include "base/unix_file/fd_file.h" #include "runtime/arch/instruction_set.h" #include "runtime/gc/heap.h" @@ -143,7 +144,7 @@ class OatDumpTest : public CommonRuntimeTest { } argv.push_back(nullptr); UNUSED(execv(argv[0], &argv[0])); - const std::string command_line(Join(exec_argv, ' ')); + const std::string command_line(android::base::Join(exec_argv, ' ')); PLOG(ERROR) << "Failed to execv(" << command_line << ")"; // _exit to avoid atexit handlers in child. _exit(1); diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index cb5a79068a..7ae13a574b 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -24,12 +24,14 @@ #include <string> #include <vector> +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + #include "art_field-inl.h" #include "art_method-inl.h" #include "base/dumpable.h" #include "base/scoped_flock.h" #include "base/stringpiece.h" -#include "base/stringprintf.h" #include "base/unix_file/fd_file.h" #include "base/unix_file/random_access_file_utils.h" #include "elf_utils.h" @@ -286,8 +288,8 @@ bool PatchOat::Patch(const std::string& image_location, std::string converted_image_filename = space->GetImageLocation(); std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@'); std::string output_image_filename = output_directory + - (StartsWith(converted_image_filename, "/") ? "" : "/") + - converted_image_filename; + (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") + + converted_image_filename; std::string output_vdex_filename = ImageHeader::GetVdexLocationFromImageLocation(output_image_filename); std::string output_oat_filename = @@ -343,8 +345,8 @@ bool PatchOat::Patch(const std::string& image_location, std::string converted_image_filename = space->GetImageLocation(); std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@'); std::string output_image_filename = output_directory + - (StartsWith(converted_image_filename, "/") ? "" : "/") + - converted_image_filename; + (android::base::StartsWith(converted_image_filename, "/") ? "" : "/") + + converted_image_filename; bool new_oat_out; std::unique_ptr<File> output_image_file(CreateOrOpen(output_image_filename.c_str(), &new_oat_out)); @@ -932,12 +934,12 @@ static std::string CommandLine() { for (int i = 0; i < orig_argc; ++i) { command.push_back(orig_argv[i]); } - return Join(command, ' '); + return android::base::Join(command, ' '); } static void UsageErrorV(const char* fmt, va_list ap) { std::string error; - StringAppendV(&error, fmt, ap); + android::base::StringAppendV(&error, fmt, ap); LOG(ERROR) << error; } diff --git a/profman/profman.cc b/profman/profman.cc index bfef834bd9..e5384078f1 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -25,10 +25,12 @@ #include <string> #include <vector> +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + #include "base/dumpable.h" #include "base/scoped_flock.h" #include "base/stringpiece.h" -#include "base/stringprintf.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "dex_file.h" @@ -48,7 +50,7 @@ static std::string CommandLine() { for (int i = 0; i < original_argc; ++i) { command.push_back(original_argv[i]); } - return Join(command, ' '); + return android::base::Join(command, ' '); } static constexpr int kInvalidFd = -1; @@ -59,7 +61,7 @@ static bool FdIsValid(int fd) { static void UsageErrorV(const char* fmt, va_list ap) { std::string error; - StringAppendV(&error, fmt, ap); + android::base::StringAppendV(&error, fmt, ap); LOG(ERROR) << error; } diff --git a/runtime/Android.bp b/runtime/Android.bp index 08be5b2979..86019bf71c 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -40,7 +40,6 @@ cc_defaults { "base/scoped_arena_allocator.cc", "base/scoped_flock.cc", "base/stringpiece.cc", - "base/stringprintf.cc", "base/time_utils.cc", "base/timing_logger.cc", "base/unix_file/fd_file.cc", @@ -508,7 +507,6 @@ art_cc_test { "base/histogram_test.cc", "base/mutex_test.cc", "base/scoped_flock_test.cc", - "base/stringprintf_test.cc", "base/time_utils_test.cc", "base/timing_logger_test.cc", "base/transform_array_ref_test.cc", @@ -517,6 +515,7 @@ art_cc_test { "base/unix_file/fd_file_test.cc", "cha_test.cc", "class_linker_test.cc", + "class_table_test.cc", "compiler_filter_test.cc", "dex_file_test.cc", "dex_file_verifier_test.cc", diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc index c81a93c368..181b2ed723 100644 --- a/runtime/arch/arm/instruction_set_features_arm.cc +++ b/runtime/arch/arm/instruction_set_features_arm.cc @@ -24,8 +24,10 @@ #include "signal.h" #include <fstream> -#include "base/stringprintf.h" -#include "utils.h" // For Trim. +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + +#include "base/logging.h" #if defined(__arm__) extern "C" bool artCheckForArmSdivInstruction(); @@ -33,6 +35,8 @@ extern "C" bool artCheckForArmSdivInstruction(); namespace art { +using android::base::StringPrintf; + ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromVariant( const std::string& variant, std::string* error_msg) { // Assume all ARM processors are SMP. @@ -271,7 +275,7 @@ ArmInstructionSetFeatures::AddFeaturesFromSplitString( bool has_atomic_ldrd_strd = has_atomic_ldrd_strd_; bool has_div = has_div_; for (auto i = features.begin(); i != features.end(); i++) { - std::string feature = Trim(*i); + std::string feature = android::base::Trim(*i); if (feature == "div") { has_div = true; } else if (feature == "-div") { diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc index 4e7dea3f48..52d8b3e367 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64.cc @@ -19,12 +19,16 @@ #include <fstream> #include <sstream> +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + +#include "base/logging.h" #include "base/stl_util.h" -#include "base/stringprintf.h" -#include "utils.h" // For Trim. namespace art { +using android::base::StringPrintf; + Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant( const std::string& variant, std::string* error_msg) { const bool smp = true; // Conservative default. @@ -137,7 +141,7 @@ Arm64InstructionSetFeatures::AddFeaturesFromSplitString( const bool smp, const std::vector<std::string>& features, std::string* error_msg) const { bool is_a53 = fix_cortex_a53_835769_; for (auto i = features.begin(); i != features.end(); i++) { - std::string feature = Trim(*i); + std::string feature = android::base::Trim(*i); if (feature == "a53") { is_a53 = true; } else if (feature == "-a53") { diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc index b32391f6b0..db004e7495 100644 --- a/runtime/arch/instruction_set_features.cc +++ b/runtime/arch/instruction_set_features.cc @@ -16,6 +16,8 @@ #include "instruction_set_features.h" +#include "android-base/strings.h" + #include "base/casts.h" #include "utils.h" @@ -224,7 +226,7 @@ std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::AddFeature *error_msg = "Unexpected instruction set features after 'default'"; return std::unique_ptr<const InstructionSetFeatures>(); } - std::string feature = Trim(*it); + std::string feature = android::base::Trim(*it); bool erase = false; if (feature == "default") { if (!first) { diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc index 0b8e531896..d4893923d6 100644 --- a/runtime/arch/instruction_set_features_test.cc +++ b/runtime/arch/instruction_set_features_test.cc @@ -22,11 +22,14 @@ #include "android-base/properties.h" #endif +#include "android-base/stringprintf.h" + #include "base/logging.h" -#include "base/stringprintf.h" namespace art { +using android::base::StringPrintf; + #ifdef ART_TARGET_ANDROID #if defined(__aarch64__) TEST(InstructionSetFeaturesTest, DISABLED_FeaturesFromSystemPropertyVariant) { diff --git a/runtime/arch/instruction_set_test.cc b/runtime/arch/instruction_set_test.cc index 5dfc4b4c48..5aae93acc5 100644 --- a/runtime/arch/instruction_set_test.cc +++ b/runtime/arch/instruction_set_test.cc @@ -19,7 +19,6 @@ #include <gtest/gtest.h> #include "base/enums.h" -#include "base/stringprintf.h" namespace art { diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc index a95b6f604c..5b50573695 100644 --- a/runtime/arch/mips/instruction_set_features_mips.cc +++ b/runtime/arch/mips/instruction_set_features_mips.cc @@ -19,12 +19,16 @@ #include <fstream> #include <sstream> +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + +#include "base/logging.h" #include "base/stl_util.h" -#include "base/stringprintf.h" -#include "utils.h" // For Trim. namespace art { +using android::base::StringPrintf; + // An enum for the Mips revision. enum class MipsLevel { kBase, @@ -210,7 +214,7 @@ MipsInstructionSetFeatures::AddFeaturesFromSplitString( bool mips_isa_gte2 = mips_isa_gte2_; bool r6 = r6_; for (auto i = features.begin(); i != features.end(); i++) { - std::string feature = Trim(*i); + std::string feature = android::base::Trim(*i); if (feature == "fpu32") { fpu_32bit = true; } else if (feature == "-fpu32") { diff --git a/runtime/arch/mips64/instruction_set_features_mips64.cc b/runtime/arch/mips64/instruction_set_features_mips64.cc index 490a8d2df3..92c44e85de 100644 --- a/runtime/arch/mips64/instruction_set_features_mips64.cc +++ b/runtime/arch/mips64/instruction_set_features_mips64.cc @@ -19,11 +19,15 @@ #include <fstream> #include <sstream> -#include "base/stringprintf.h" -#include "utils.h" // For Trim. +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + +#include "base/logging.h" namespace art { +using android::base::StringPrintf; + Mips64FeaturesUniquePtr Mips64InstructionSetFeatures::FromVariant( const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED) { if (variant != "default" && variant != "mips64r6") { @@ -105,7 +109,7 @@ Mips64InstructionSetFeatures::AddFeaturesFromSplitString( auto i = features.begin(); if (i != features.end()) { // We don't have any features. - std::string feature = Trim(*i); + std::string feature = android::base::Trim(*i); *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str()); return nullptr; } diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc index 90b55a97f6..c520d63cf3 100644 --- a/runtime/arch/x86/instruction_set_features_x86.cc +++ b/runtime/arch/x86/instruction_set_features_x86.cc @@ -19,12 +19,16 @@ #include <fstream> #include <sstream> +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + #include "arch/x86_64/instruction_set_features_x86_64.h" -#include "base/stringprintf.h" -#include "utils.h" // For Trim. +#include "base/logging.h" namespace art { +using android::base::StringPrintf; + // Feature-support arrays. static constexpr const char* x86_known_variants[] = { @@ -293,7 +297,7 @@ std::unique_ptr<const InstructionSetFeatures> X86InstructionSetFeatures::AddFeat bool has_AVX2 = has_AVX2_; bool has_POPCNT = has_POPCNT_; for (auto i = features.begin(); i != features.end(); i++) { - std::string feature = Trim(*i); + std::string feature = android::base::Trim(*i); if (feature == "ssse3") { has_SSSE3 = true; } else if (feature == "-ssse3") { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 96b6f18403..dfc7837aea 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -18,6 +18,8 @@ #include <cstddef> +#include "android-base/stringprintf.h" + #include "arch/context.h" #include "art_field-inl.h" #include "art_method-inl.h" @@ -46,6 +48,8 @@ namespace art { +using android::base::StringPrintf; + extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, const char*); extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 5ef1f06e77..e4972da13d 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -175,19 +175,19 @@ ADD_TEST_EQ(MIRROR_CLASS_COMPONENT_TYPE_OFFSET, #define MIRROR_CLASS_IF_TABLE_OFFSET (16 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_IF_TABLE_OFFSET, art::mirror::Class::IfTableOffset().Int32Value()) -#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (64 + MIRROR_OBJECT_HEADER_SIZE) +#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (56 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET, art::mirror::Class::AccessFlagsOffset().Int32Value()) -#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (96 + MIRROR_OBJECT_HEADER_SIZE) +#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (88 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_OFFSET, art::mirror::Class::ObjectSizeOffset().Int32Value()) -#define MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET (100 + MIRROR_OBJECT_HEADER_SIZE) +#define MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET (92 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET, art::mirror::Class::ObjectSizeAllocFastPathOffset().Int32Value()) -#define MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET (104 + MIRROR_OBJECT_HEADER_SIZE) +#define MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET (96 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET, art::mirror::Class::PrimitiveTypeOffset().Int32Value()) -#define MIRROR_CLASS_STATUS_OFFSET (112 + MIRROR_OBJECT_HEADER_SIZE) +#define MIRROR_CLASS_STATUS_OFFSET (104 + MIRROR_OBJECT_HEADER_SIZE) ADD_TEST_EQ(MIRROR_CLASS_STATUS_OFFSET, art::mirror::Class::StatusOffset().Int32Value()) diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index 5cdf6718f1..61e0aabbaf 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -83,18 +83,19 @@ const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = { "GraphChecker ", "Verifier ", "CallingConv ", + "CHA ", }; template <bool kCount> ArenaAllocatorStatsImpl<kCount>::ArenaAllocatorStatsImpl() - : num_allocations_(0u) { - std::fill_n(alloc_stats_, arraysize(alloc_stats_), 0u); + : num_allocations_(0u), + alloc_stats_(kNumArenaAllocKinds, 0u) { } template <bool kCount> void ArenaAllocatorStatsImpl<kCount>::Copy(const ArenaAllocatorStatsImpl& other) { num_allocations_ = other.num_allocations_; - std::copy(other.alloc_stats_, other.alloc_stats_ + arraysize(alloc_stats_), alloc_stats_); + std::copy_n(other.alloc_stats_.begin(), kNumArenaAllocKinds, alloc_stats_.begin()); } template <bool kCount> @@ -111,7 +112,7 @@ size_t ArenaAllocatorStatsImpl<kCount>::NumAllocations() const { template <bool kCount> size_t ArenaAllocatorStatsImpl<kCount>::BytesAllocated() const { const size_t init = 0u; // Initial value of the correct type. - return std::accumulate(alloc_stats_, alloc_stats_ + arraysize(alloc_stats_), init); + return std::accumulate(alloc_stats_.begin(), alloc_stats_.end(), init); } template <bool kCount> diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index 2feb28a778..6c764cb715 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -21,6 +21,7 @@ #include <stddef.h> #include "base/bit_utils.h" +#include "base/dchecked_vector.h" #include "base/memory_tool.h" #include "debug_stack.h" #include "macros.h" @@ -132,8 +133,7 @@ class ArenaAllocatorStatsImpl { private: size_t num_allocations_; - // TODO: Use std::array<size_t, kNumArenaAllocKinds> from C++11 when we upgrade the STL. - size_t alloc_stats_[kNumArenaAllocKinds]; // Bytes used by various allocation kinds. + dchecked_vector<size_t> alloc_stats_; // Bytes used by various allocation kinds. static const char* const kAllocNames[]; }; diff --git a/runtime/base/file_magic.cc b/runtime/base/file_magic.cc index de6f4237ff..568a7ae5d6 100644 --- a/runtime/base/file_magic.cc +++ b/runtime/base/file_magic.cc @@ -20,13 +20,16 @@ #include <sys/stat.h> #include <sys/types.h> +#include "android-base/stringprintf.h" + #include "base/logging.h" #include "base/unix_file/fd_file.h" #include "dex_file.h" -#include "stringprintf.h" namespace art { +using android::base::StringPrintf; + File OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) { CHECK(magic != nullptr); File fd(filename, O_RDONLY, /* check_usage */ false); diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index 92b7c6537c..44a84c834f 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -21,7 +21,6 @@ #include "mutex.h" -#include "base/stringprintf.h" #include "base/value_object.h" #include "thread.h" #include "utils.h" diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index ce452cbcf6..9116097604 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -19,6 +19,8 @@ #include <errno.h> #include <sys/time.h> +#include "android-base/stringprintf.h" + #include "atomic.h" #include "base/logging.h" #include "base/time_utils.h" @@ -30,6 +32,8 @@ namespace art { +using android::base::StringPrintf; + static Atomic<Locks::ClientCallback*> safe_to_call_abort_callback(nullptr); Mutex* Locks::abort_lock_ = nullptr; diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index 0e8031f4f2..d4bb56b62a 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -19,12 +19,15 @@ #include <sys/file.h> #include <sys/stat.h> +#include "android-base/stringprintf.h" + #include "base/logging.h" -#include "base/stringprintf.h" #include "base/unix_file/fd_file.h" namespace art { +using android::base::StringPrintf; + bool ScopedFlock::Init(const char* filename, std::string* error_msg) { return Init(filename, O_CREAT | O_RDWR, true, error_msg); } diff --git a/runtime/base/stringprintf.cc b/runtime/base/stringprintf.cc deleted file mode 100644 index 8fd9257048..0000000000 --- a/runtime/base/stringprintf.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "stringprintf.h" - -#include <stdio.h> - -namespace art { - -void StringAppendV(std::string* dst, const char* format, va_list ap) { - // First try with a small fixed size buffer - char space[1024]; - - // It's possible for methods that use a va_list to invalidate - // the data in it upon use. The fix is to make a copy - // of the structure before using it and use that copy instead. - va_list backup_ap; - va_copy(backup_ap, ap); - int result = vsnprintf(space, sizeof(space), format, backup_ap); - va_end(backup_ap); - - if (result < static_cast<int>(sizeof(space))) { - if (result >= 0) { - // Normal case -- everything fit. - dst->append(space, result); - return; - } - - if (result < 0) { - // Just an error. - return; - } - } - - // Increase the buffer size to the size requested by vsnprintf, - // plus one for the closing \0. - int length = result+1; - char* buf = new char[length]; - - // Restore the va_list before we use it again - va_copy(backup_ap, ap); - result = vsnprintf(buf, length, format, backup_ap); - va_end(backup_ap); - - if (result >= 0 && result < length) { - // It fit - dst->append(buf, result); - } - delete[] buf; -} - -std::string StringPrintf(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - std::string result; - StringAppendV(&result, fmt, ap); - va_end(ap); - return result; -} - -void StringAppendF(std::string* dst, const char* format, ...) { - va_list ap; - va_start(ap, format); - StringAppendV(dst, format, ap); - va_end(ap); -} - -} // namespace art diff --git a/runtime/base/stringprintf.h b/runtime/base/stringprintf.h deleted file mode 100644 index 4767a750c3..0000000000 --- a/runtime/base/stringprintf.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_BASE_STRINGPRINTF_H_ -#define ART_RUNTIME_BASE_STRINGPRINTF_H_ - -#include <stdarg.h> -#include <string> - -namespace art { - -// Returns a string corresponding to printf-like formatting of the arguments. -std::string StringPrintf(const char* fmt, ...) - __attribute__((__format__(__printf__, 1, 2))); - -// Appends a printf-like formatting of the arguments to 'dst'. -void StringAppendF(std::string* dst, const char* fmt, ...) - __attribute__((__format__(__printf__, 2, 3))); - -// Appends a printf-like formatting of the arguments to 'dst'. -void StringAppendV(std::string* dst, const char* format, va_list ap); - -} // namespace art - -#endif // ART_RUNTIME_BASE_STRINGPRINTF_H_ diff --git a/runtime/base/time_utils.cc b/runtime/base/time_utils.cc index 57f198d7e5..3c09d5a36f 100644 --- a/runtime/base/time_utils.cc +++ b/runtime/base/time_utils.cc @@ -20,8 +20,9 @@ #include "time_utils.h" +#include "android-base/stringprintf.h" + #include "base/logging.h" -#include "base/stringprintf.h" #if defined(__APPLE__) #include <sys/time.h> @@ -29,6 +30,8 @@ namespace art { +using android::base::StringPrintf; + std::string PrettyDuration(uint64_t nano_duration, size_t max_fraction_digits) { if (nano_duration == 0) { return "0"; diff --git a/runtime/cha.cc b/runtime/cha.cc index be675a82c8..d94b091ebd 100644 --- a/runtime/cha.cc +++ b/runtime/cha.cc @@ -100,7 +100,11 @@ class CHAStackVisitor FINAL : public StackVisitor { bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod* method = GetMethod(); - if (method == nullptr || method->IsRuntimeMethod() || method->IsNative()) { + // Avoid types of methods that do not have an oat quick method header. + if (method == nullptr || + method->IsRuntimeMethod() || + method->IsNative() || + method->IsProxyMethod()) { return true; } if (GetCurrentQuickFrame() == nullptr) { @@ -110,6 +114,7 @@ class CHAStackVisitor FINAL : public StackVisitor { // Method may have multiple versions of compiled code. Check // the method header to see if it has should_deoptimize flag. const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); + DCHECK(method_header != nullptr); if (!method_header->HasShouldDeoptimizeFlag()) { // This compiled version doesn't have should_deoptimize flag. Skip. return true; diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 6c27bc61e4..1c3328e484 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -20,6 +20,8 @@ #include <sys/mman.h> #include <zlib.h> +#include "android-base/stringprintf.h" + #include "art_field-inl.h" #include "art_method-inl.h" #include "base/logging.h" @@ -42,6 +44,9 @@ namespace art { +using android::base::StringAppendF; +using android::base::StringPrintf; + /* * =========================================================================== * JNI function helpers diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 0a65cd11aa..a11257f21b 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -25,6 +25,7 @@ #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" #include "mirror/iftable.h" +#include "mirror/throwable.h" #include "mirror/object_array.h" #include "handle_scope-inl.h" #include "scoped_thread_state_change-inl.h" @@ -72,7 +73,7 @@ inline mirror::String* ClassLinker::ResolveString(dex::StringIndex string_idx, // MethodVerifier refuses methods with string_idx out of bounds. DCHECK_LT(string_idx.index_, declaring_class->GetDexFile().NumStringIds()); ObjPtr<mirror::String> string = - mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(), + mirror::StringDexCachePair::Lookup(declaring_class->GetDexCache()->GetStrings(), string_idx.index_, mirror::DexCache::kDexCacheStringCacheSize).Read(); if (UNLIKELY(string == nullptr)) { @@ -89,17 +90,28 @@ inline mirror::String* ClassLinker::ResolveString(dex::StringIndex string_idx, inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) { Thread::PoisonObjectPointersIfDebug(); + if (kIsDebugBuild) { + Thread::Current()->AssertNoPendingException(); + } ObjPtr<mirror::Class> resolved_type = referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_); if (UNLIKELY(resolved_type == nullptr)) { - ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile& dex_file = *dex_cache->GetDexFile(); - resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); - // Note: We cannot check here to see whether we added the type to the cache. The type - // might be an erroneous class, which results in it being hidden from us. + // There could be an out of bounds exception from GetDexCacheResolvedType, don't call + // ResolveType for this case. + if (LIKELY(!hs.Self()->IsExceptionPending())) { + ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); + const DexFile& dex_file = *dex_cache->GetDexFile(); + resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); + // Note: We cannot check here to see whether we added the type to the cache. The type + // might be an erroneous class, which results in it being hidden from us. + } else { + // Make sure its an array out of bounds exception. + DCHECK(hs.Self()->GetException()->GetClass()->DescriptorEquals( + "Ljava/lang/ArrayIndexOutOfBoundsException;")); + } } return resolved_type.Ptr(); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index f3a5be2172..5b8d4e42a3 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -28,6 +28,8 @@ #include <utility> #include <vector> +#include "android-base/stringprintf.h" + #include "art_field-inl.h" #include "art_method-inl.h" #include "base/arena_allocator.h" @@ -106,6 +108,8 @@ namespace art { +using android::base::StringPrintf; + static constexpr bool kSanityCheckObjects = kIsDebugBuild; static constexpr bool kVerifyArtMethodDeclaringClasses = kIsDebugBuild; @@ -1401,39 +1405,20 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( return true; } -// Update the class loader and resolved string dex cache array of classes. Should only be used on -// classes in the image space. -class UpdateClassLoaderAndResolvedStringsVisitor { +// Update the class loader. Should only be used on classes in the image space. +class UpdateClassLoaderVisitor { public: - UpdateClassLoaderAndResolvedStringsVisitor(gc::space::ImageSpace* space, - ObjPtr<mirror::ClassLoader> class_loader, - bool forward_strings) + UpdateClassLoaderVisitor(gc::space::ImageSpace* space, ObjPtr<mirror::ClassLoader> class_loader) : space_(space), - class_loader_(class_loader), - forward_strings_(forward_strings) {} + class_loader_(class_loader) {} bool operator()(ObjPtr<mirror::Class> klass) const REQUIRES_SHARED(Locks::mutator_lock_) { - if (forward_strings_) { - mirror::StringDexCacheType* strings = klass->GetDexCacheStrings(); - if (strings != nullptr) { - DCHECK( - space_->GetImageHeader().GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains( - reinterpret_cast<uint8_t*>(strings) - space_->Begin())) - << "String dex cache array for " << klass->PrettyClass() << " is not in app image"; - // Dex caches have already been updated, so take the strings pointer from there. - mirror::StringDexCacheType* new_strings = klass->GetDexCache()->GetStrings(); - DCHECK_NE(strings, new_strings); - klass->SetDexCacheStrings(new_strings); - } - } - // Finally, update class loader. klass->SetClassLoader(class_loader_); return true; } gc::space::ImageSpace* const space_; ObjPtr<mirror::ClassLoader> const class_loader_; - const bool forward_strings_; }; static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file, @@ -1655,13 +1640,6 @@ bool ClassLinker::AddImageSpace( Runtime* const runtime = Runtime::Current(); gc::Heap* const heap = runtime->GetHeap(); Thread* const self = Thread::Current(); - StackHandleScope<2> hs(self); - Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches( - hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>())); - Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle( - header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>())); - const OatFile* oat_file = space->GetOatFile(); - std::unordered_set<mirror::ClassLoader*> image_class_loaders; // Check that the image is what we are expecting. if (image_pointer_size_ != space->GetImageHeader().GetPointerSize()) { *error_msg = StringPrintf("Application image pointer size does not match runtime: %zu vs %zu", @@ -1669,6 +1647,22 @@ bool ClassLinker::AddImageSpace( image_pointer_size_); return false; } + size_t expected_image_roots = ImageHeader::NumberOfImageRoots(app_image); + if (static_cast<size_t>(header.GetImageRoots()->GetLength()) != expected_image_roots) { + *error_msg = StringPrintf("Expected %zu image roots but got %d", + expected_image_roots, + header.GetImageRoots()->GetLength()); + return false; + } + StackHandleScope<3> hs(self); + Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches( + hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>())); + Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle( + header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>())); + static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax, + "Class loader should be the last image root."); + MutableHandle<mirror::ClassLoader> image_class_loader(hs.NewHandle( + app_image ? header.GetImageRoot(ImageHeader::kClassLoader)->AsClassLoader() : nullptr)); DCHECK(class_roots.Get() != nullptr); if (class_roots->GetLength() != static_cast<int32_t>(kClassRootsMax)) { *error_msg = StringPrintf("Expected %d class roots but got %d", @@ -1683,6 +1677,7 @@ bool ClassLinker::AddImageSpace( return false; } } + const OatFile* oat_file = space->GetOatFile(); if (oat_file->GetOatHeader().GetDexFileCount() != static_cast<uint32_t>(dex_caches->GetLength())) { *error_msg = "Dex cache count and dex file count mismatch while trying to initialize from " @@ -1715,15 +1710,11 @@ bool ClassLinker::AddImageSpace( // The current dex file field is bogus, overwrite it so that we can get the dex file in the // loop below. h_dex_cache->SetDexFile(dex_file.get()); - // Check that each class loader resolved the same way. - // TODO: Store image class loaders as image roots. GcRoot<mirror::Class>* const types = h_dex_cache->GetResolvedTypes(); for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) { ObjPtr<mirror::Class> klass = types[j].Read(); if (klass != nullptr) { DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); - ObjPtr<mirror::ClassLoader> image_class_loader = klass->GetClassLoader(); - image_class_loaders.insert(image_class_loader.Ptr()); } } } else { @@ -1749,59 +1740,57 @@ bool ClassLinker::AddImageSpace( // for PathClassLoader does this by looping through the array of dex files. To ensure they // resolve the same way, simply flatten the hierarchy in the way the resolution order would be, // and check that the dex file names are the same. - for (ObjPtr<mirror::ClassLoader> image_class_loader : image_class_loaders) { - if (IsBootClassLoader(soa, image_class_loader)) { - // The dex cache can reference types from the boot class loader. - continue; - } - std::list<mirror::String*> image_dex_file_names; - std::string temp_error_msg; - if (!FlattenPathClassLoader(image_class_loader, &image_dex_file_names, &temp_error_msg)) { - *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'", - temp_error_msg.c_str()); - return false; - } - std::list<mirror::String*> loader_dex_file_names; - if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) { - *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'", - temp_error_msg.c_str()); - return false; + if (IsBootClassLoader(soa, image_class_loader.Get())) { + *error_msg = "Unexpected BootClassLoader in app image"; + return false; + } + std::list<mirror::String*> image_dex_file_names; + std::string temp_error_msg; + if (!FlattenPathClassLoader(image_class_loader.Get(), &image_dex_file_names, &temp_error_msg)) { + *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'", + temp_error_msg.c_str()); + return false; + } + std::list<mirror::String*> loader_dex_file_names; + if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) { + *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'", + temp_error_msg.c_str()); + return false; + } + // Add the temporary dex path list elements at the end. + auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements); + for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) { + ObjPtr<mirror::Object> element = elements->GetWithoutChecks(i); + if (element != nullptr) { + // If we are somewhere in the middle of the array, there may be nulls at the end. + loader_dex_file_names.push_back(GetDexPathListElementName(element)); } - // Add the temporary dex path list elements at the end. - auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements); - for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) { - ObjPtr<mirror::Object> element = elements->GetWithoutChecks(i); - if (element != nullptr) { - // If we are somewhere in the middle of the array, there may be nulls at the end. - loader_dex_file_names.push_back(GetDexPathListElementName(element)); - } + } + // Ignore the number of image dex files since we are adding those to the class loader anyways. + CHECK_GE(static_cast<size_t>(image_dex_file_names.size()), + static_cast<size_t>(dex_caches->GetLength())); + size_t image_count = image_dex_file_names.size() - dex_caches->GetLength(); + // Check that the dex file names match. + bool equal = image_count == loader_dex_file_names.size(); + if (equal) { + auto it1 = image_dex_file_names.begin(); + auto it2 = loader_dex_file_names.begin(); + for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) { + equal = equal && (*it1)->Equals(*it2); } - // Ignore the number of image dex files since we are adding those to the class loader anyways. - CHECK_GE(static_cast<size_t>(image_dex_file_names.size()), - static_cast<size_t>(dex_caches->GetLength())); - size_t image_count = image_dex_file_names.size() - dex_caches->GetLength(); - // Check that the dex file names match. - bool equal = image_count == loader_dex_file_names.size(); - if (equal) { - auto it1 = image_dex_file_names.begin(); - auto it2 = loader_dex_file_names.begin(); - for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) { - equal = equal && (*it1)->Equals(*it2); - } + } + if (!equal) { + VLOG(image) << "Image dex files " << image_dex_file_names.size(); + for (ObjPtr<mirror::String> name : image_dex_file_names) { + VLOG(image) << name->ToModifiedUtf8(); } - if (!equal) { - VLOG(image) << "Image dex files " << image_dex_file_names.size(); - for (ObjPtr<mirror::String> name : image_dex_file_names) { - VLOG(image) << name->ToModifiedUtf8(); - } - VLOG(image) << "Loader dex files " << loader_dex_file_names.size(); - for (ObjPtr<mirror::String> name : loader_dex_file_names) { - VLOG(image) << name->ToModifiedUtf8(); - } - *error_msg = "Rejecting application image due to class loader mismatch"; - // Ignore class loader mismatch for now since these would just use possibly incorrect - // oat code anyways. The structural class check should be done in the parent. + VLOG(image) << "Loader dex files " << loader_dex_file_names.size(); + for (ObjPtr<mirror::String> name : loader_dex_file_names) { + VLOG(image) << name->ToModifiedUtf8(); } + *error_msg = "Rejecting application image due to class loader mismatch"; + // Ignore class loader mismatch for now since these would just use possibly incorrect + // oat code anyways. The structural class check should be done in the parent. } } @@ -1856,10 +1845,8 @@ bool ClassLinker::AddImageSpace( } // Update class loader and resolved strings. If added_class_table is false, the resolved // strings were forwarded UpdateAppImageClassLoadersAndDexCaches. - UpdateClassLoaderAndResolvedStringsVisitor visitor(space, - class_loader.Get(), - forward_dex_cache_arrays); - for (ClassTable::TableSlot& root : temp_set) { + UpdateClassLoaderVisitor visitor(space, class_loader.Get()); + for (const ClassTable::TableSlot& root : temp_set) { visitor(root.Read()); } // forward_dex_cache_arrays is true iff we copied all of the dex cache arrays into the .bss. @@ -2516,8 +2503,9 @@ mirror::Class* ClassLinker::FindClass(Thread* self, // the Java-side could still succeed for racy programs if another thread is actively // modifying the class loader's path list. - if (Runtime::Current()->IsAotCompiler()) { - // Oops, compile-time, can't run actual class-loader code. + if (!self->CanCallIntoJava()) { + // Oops, we can't call into java so we can't run actual class-loader code. + // This is true for e.g. for the compiler (jit or aot). ObjPtr<mirror::Throwable> pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); self->SetException(pre_allocated); @@ -2659,6 +2647,8 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, ObjectLock<mirror::Class> lock(self, klass); klass->SetClinitThreadId(self->GetTid()); + // Make sure we have a valid empty iftable even if there are errors. + klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); // Add the newly loaded class to the loaded classes table. ObjPtr<mirror::Class> existing = InsertClass(descriptor, klass.Get(), hash); @@ -3010,7 +3000,6 @@ void ClassLinker::SetupClass(const DexFile& dex_file, klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def)); klass->SetDexTypeIndex(dex_class_def.class_idx_); - CHECK(klass->GetDexCacheStrings() != nullptr); } void ClassLinker::LoadClass(Thread* self, diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 862585af92..42108d8621 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -19,6 +19,8 @@ #include <memory> #include <string> +#include "android-base/strings.h" + #include "art_field-inl.h" #include "art_method-inl.h" #include "base/enums.h" @@ -276,8 +278,6 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_FALSE(klass->IsArrayClass()); EXPECT_TRUE(klass->GetComponentType() == nullptr); EXPECT_TRUE(klass->IsInSamePackage(klass.Get())); - EXPECT_TRUE(klass->GetDexCacheStrings() != nullptr); - EXPECT_EQ(klass->GetDexCacheStrings(), klass->GetDexCache()->GetStrings()); std::string temp2; EXPECT_TRUE(mirror::Class::IsInSamePackage(klass->GetDescriptor(&temp), klass->GetDescriptor(&temp2))); @@ -588,7 +588,6 @@ struct ClassOffsets : public CheckOffsets<mirror::Class> { addOffset(OFFSETOF_MEMBER(mirror::Class, component_type_), "componentType"); addOffset(OFFSETOF_MEMBER(mirror::Class, copied_methods_offset_), "copiedMethodsOffset"); addOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_), "dexCache"); - addOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_strings_), "dexCacheStrings"); addOffset(OFFSETOF_MEMBER(mirror::Class, dex_class_def_idx_), "dexClassDefIndex"); addOffset(OFFSETOF_MEMBER(mirror::Class, dex_type_idx_), "dexTypeIndex"); addOffset(OFFSETOF_MEMBER(mirror::Class, ext_data_), "extData"); @@ -800,12 +799,12 @@ TEST_F(ClassLinkerTest, GetDexFiles) { jobject jclass_loader = LoadDex("Nested"); std::vector<const DexFile*> dex_files(GetDexFiles(jclass_loader)); ASSERT_EQ(dex_files.size(), 1U); - EXPECT_TRUE(EndsWith(dex_files[0]->GetLocation(), "Nested.jar")); + EXPECT_TRUE(android::base::EndsWith(dex_files[0]->GetLocation(), "Nested.jar")); jobject jclass_loader2 = LoadDex("MultiDex"); std::vector<const DexFile*> dex_files2(GetDexFiles(jclass_loader2)); ASSERT_EQ(dex_files2.size(), 2U); - EXPECT_TRUE(EndsWith(dex_files2[0]->GetLocation(), "MultiDex.jar")); + EXPECT_TRUE(android::base::EndsWith(dex_files2[0]->GetLocation(), "MultiDex.jar")); } TEST_F(ClassLinkerTest, FindClassNested) { @@ -1173,6 +1172,24 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) { EXPECT_TRUE(init->IsInitialized()); } +TEST_F(ClassLinkerTest, ErroneousClass) { + ScopedObjectAccess soa(Thread::Current()); + jobject jclass_loader = LoadMultiDex("ErroneousA", "ErroneousB"); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); + hs.Self()->AssertNoPendingException(); + const char* descriptor = "LErroneous;"; + ObjPtr<mirror::Class> klass = class_linker_->FindClass(soa.Self(), descriptor, class_loader); + // Erronenous since we are extending final class. + hs.Self()->AssertPendingException(); + EXPECT_TRUE(klass == nullptr); + klass = class_linker_->LookupClass(soa.Self(), descriptor, class_loader.Get()); + EXPECT_FALSE(klass == nullptr); + EXPECT_TRUE(klass->IsErroneous()); + EXPECT_TRUE(klass->GetIfTable() != nullptr); +} + TEST_F(ClassLinkerTest, FinalizableBit) { ScopedObjectAccess soa(Thread::Current()); mirror::Class* c; diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h index 229cd477dd..dfe8949134 100644 --- a/runtime/class_table-inl.h +++ b/runtime/class_table-inl.h @@ -71,6 +71,19 @@ bool ClassTable::Visit(Visitor& visitor) { return true; } +template <typename Visitor> +bool ClassTable::Visit(const Visitor& visitor) { + ReaderMutexLock mu(Thread::Current(), lock_); + for (ClassSet& class_set : classes_) { + for (TableSlot& table_slot : class_set) { + if (!visitor(table_slot.Read())) { + return false; + } + } + } + return true; +} + template<ReadBarrierOption kReadBarrierOption> inline mirror::Class* ClassTable::TableSlot::Read() const { const uint32_t before = data_.LoadRelaxed(); diff --git a/runtime/class_table.cc b/runtime/class_table.cc index ec33e5ef80..0f985c6424 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -33,8 +33,9 @@ void ClassTable::FreezeSnapshot() { bool ClassTable::Contains(ObjPtr<mirror::Class> klass) { ReaderMutexLock mu(Thread::Current(), lock_); + TableSlot slot(klass); for (ClassSet& class_set : classes_) { - auto it = class_set.Find(TableSlot(klass)); + auto it = class_set.Find(slot); if (it != class_set.end()) { return it->Read() == klass; } @@ -44,8 +45,9 @@ bool ClassTable::Contains(ObjPtr<mirror::Class> klass) { mirror::Class* ClassTable::LookupByDescriptor(ObjPtr<mirror::Class> klass) { ReaderMutexLock mu(Thread::Current(), lock_); + TableSlot slot(klass); for (ClassSet& class_set : classes_) { - auto it = class_set.Find(TableSlot(klass)); + auto it = class_set.Find(slot); if (it != class_set.end()) { return it->Read(); } @@ -110,8 +112,8 @@ size_t ClassTable::NumNonZygoteClasses(ObjPtr<mirror::ClassLoader> defining_load } mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) { - ReaderMutexLock mu(Thread::Current(), lock_); DescriptorHashPair pair(descriptor, hash); + ReaderMutexLock mu(Thread::Current(), lock_); for (ClassSet& class_set : classes_) { auto it = class_set.FindWithHash(pair, hash); if (it != class_set.end()) { @@ -122,12 +124,14 @@ mirror::Class* ClassTable::Lookup(const char* descriptor, size_t hash) { } void ClassTable::Insert(ObjPtr<mirror::Class> klass) { + const uint32_t hash = TableSlot::HashDescriptor(klass); WriterMutexLock mu(Thread::Current(), lock_); - classes_.back().Insert(TableSlot(klass)); + classes_.back().InsertWithHash(TableSlot(klass, hash), hash); } void ClassTable::InsertWithoutLocks(ObjPtr<mirror::Class> klass) { - classes_.back().Insert(TableSlot(klass)); + const uint32_t hash = TableSlot::HashDescriptor(klass); + classes_.back().InsertWithHash(TableSlot(klass, hash), hash); } void ClassTable::InsertWithHash(ObjPtr<mirror::Class> klass, size_t hash) { @@ -136,8 +140,8 @@ void ClassTable::InsertWithHash(ObjPtr<mirror::Class> klass, size_t hash) { } bool ClassTable::Remove(const char* descriptor) { - WriterMutexLock mu(Thread::Current(), lock_); DescriptorHashPair pair(descriptor, ComputeModifiedUtf8Hash(descriptor)); + WriterMutexLock mu(Thread::Current(), lock_); for (ClassSet& class_set : classes_) { auto it = class_set.Find(pair); if (it != class_set.end()) { @@ -250,10 +254,12 @@ void ClassTable::ClearStrongRoots() { strong_roots_.clear(); } -ClassTable::TableSlot::TableSlot(ObjPtr<mirror::Class> klass) { +ClassTable::TableSlot::TableSlot(ObjPtr<mirror::Class> klass) + : TableSlot(klass, HashDescriptor(klass)) {} + +uint32_t ClassTable::TableSlot::HashDescriptor(ObjPtr<mirror::Class> klass) { std::string temp; - data_.StoreRelaxed(Encode(klass.Ptr(), - MaskHash(ComputeModifiedUtf8Hash(klass->GetDescriptor(&temp))))); + return ComputeModifiedUtf8Hash(klass->GetDescriptor(&temp)); } } // namespace art diff --git a/runtime/class_table.h b/runtime/class_table.h index 104871ff21..f27d8093ce 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -73,6 +73,9 @@ class ClassTable { return MaskHash(other) == Hash(); } + static uint32_t HashDescriptor(ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_); + template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier> mirror::Class* Read() const REQUIRES_SHARED(Locks::mutator_lock_); @@ -174,6 +177,10 @@ class ClassTable { bool Visit(Visitor& visitor) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); + template <typename Visitor> + bool Visit(const Visitor& visitor) + REQUIRES(!lock_) + REQUIRES_SHARED(Locks::mutator_lock_); // Return the first class that matches the descriptor. Returns null if there are none. mirror::Class* Lookup(const char* descriptor, size_t hash) diff --git a/runtime/class_table_test.cc b/runtime/class_table_test.cc new file mode 100644 index 0000000000..f1248eb00c --- /dev/null +++ b/runtime/class_table_test.cc @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "class_table-inl.h" + +#include "art_field-inl.h" +#include "art_method-inl.h" +#include "class_linker-inl.h" +#include "common_runtime_test.h" +#include "dex_file.h" +#include "gc/accounting/card_table-inl.h" +#include "gc/heap.h" +#include "handle_scope-inl.h" +#include "mirror/class-inl.h" +#include "obj_ptr.h" +#include "scoped_thread_state_change-inl.h" + +namespace art { +namespace mirror { + +class CollectRootVisitor { + public: + CollectRootVisitor() {} + + template <class MirrorType> + ALWAYS_INLINE void VisitRootIfNonNull(GcRoot<MirrorType>& root) const + REQUIRES_SHARED(Locks::mutator_lock_) { + if (!root.IsNull()) { + VisitRoot(root); + } + } + + template <class MirrorType> + ALWAYS_INLINE void VisitRootIfNonNull(mirror::CompressedReference<MirrorType>* root) const + REQUIRES_SHARED(Locks::mutator_lock_) { + if (!root->IsNull()) { + VisitRoot(root); + } + } + + template <class MirrorType> + void VisitRoot(GcRoot<MirrorType>& root) const REQUIRES_SHARED(Locks::mutator_lock_) { + VisitRoot(root.AddressWithoutBarrier()); + } + + template <class MirrorType> + void VisitRoot(mirror::CompressedReference<MirrorType>* root) const + REQUIRES_SHARED(Locks::mutator_lock_) { + roots_.insert(root->AsMirrorPtr()); + } + + mutable std::set<mirror::Object*> roots_; +}; + + +class ClassTableTest : public CommonRuntimeTest {}; + +TEST_F(ClassTableTest, ClassTable) { + ScopedObjectAccess soa(Thread::Current()); + jobject jclass_loader = LoadDex("XandY"); + VariableSizedHandleScope hs(soa.Self()); + Handle<ClassLoader> class_loader(hs.NewHandle(soa.Decode<ClassLoader>(jclass_loader))); + const char* descriptor_x = "LX;"; + const char* descriptor_y = "LY;"; + Handle<mirror::Class> h_X( + hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_x, class_loader))); + Handle<mirror::Class> h_Y( + hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_y, class_loader))); + Handle<mirror::Object> obj_X = hs.NewHandle(h_X->AllocObject(soa.Self())); + ASSERT_TRUE(obj_X.Get() != nullptr); + ClassTable table; + EXPECT_EQ(table.NumZygoteClasses(class_loader.Get()), 0u); + EXPECT_EQ(table.NumNonZygoteClasses(class_loader.Get()), 0u); + + // Add h_X to the class table. + table.Insert(h_X.Get()); + EXPECT_EQ(table.LookupByDescriptor(h_X.Get()), h_X.Get()); + EXPECT_EQ(table.Lookup(descriptor_x, ComputeModifiedUtf8Hash(descriptor_x)), h_X.Get()); + EXPECT_EQ(table.Lookup("NOT_THERE", ComputeModifiedUtf8Hash("NOT_THERE")), nullptr); + EXPECT_EQ(table.NumZygoteClasses(class_loader.Get()), 0u); + EXPECT_EQ(table.NumNonZygoteClasses(class_loader.Get()), 1u); + + // Create the zygote snapshot and ensure the accounting is correct. + table.FreezeSnapshot(); + EXPECT_EQ(table.NumZygoteClasses(class_loader.Get()), 1u); + EXPECT_EQ(table.NumNonZygoteClasses(class_loader.Get()), 0u); + + // Test inserting and related lookup functions. + EXPECT_EQ(table.LookupByDescriptor(h_Y.Get()), nullptr); + EXPECT_FALSE(table.Contains(h_Y.Get())); + table.Insert(h_Y.Get()); + EXPECT_EQ(table.LookupByDescriptor(h_X.Get()), h_X.Get()); + EXPECT_EQ(table.LookupByDescriptor(h_Y.Get()), h_Y.Get()); + EXPECT_TRUE(table.Contains(h_X.Get())); + EXPECT_TRUE(table.Contains(h_Y.Get())); + + EXPECT_EQ(table.NumZygoteClasses(class_loader.Get()), 1u); + EXPECT_EQ(table.NumNonZygoteClasses(class_loader.Get()), 1u); + + // Test adding / clearing strong roots. + EXPECT_TRUE(table.InsertStrongRoot(obj_X.Get())); + EXPECT_FALSE(table.InsertStrongRoot(obj_X.Get())); + table.ClearStrongRoots(); + EXPECT_TRUE(table.InsertStrongRoot(obj_X.Get())); + + // Collect all the roots and make sure there is nothing missing. + CollectRootVisitor roots; + table.VisitRoots(roots); + EXPECT_TRUE(roots.roots_.find(h_X.Get()) != roots.roots_.end()); + EXPECT_TRUE(roots.roots_.find(h_Y.Get()) != roots.roots_.end()); + EXPECT_TRUE(roots.roots_.find(obj_X.Get()) != roots.roots_.end()); + + // Checks that vising only classes works. + std::set<mirror::Class*> classes; + table.Visit([&classes](ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { + classes.insert(klass.Ptr()); + return true; + }); + EXPECT_TRUE(classes.find(h_X.Get()) != classes.end()); + EXPECT_TRUE(classes.find(h_Y.Get()) != classes.end()); + EXPECT_EQ(classes.size(), 2u); + classes.clear(); + table.Visit([&classes](ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { + classes.insert(klass.Ptr()); + // Return false to exit the Visit early. + return false; + }); + EXPECT_EQ(classes.size(), 1u); + + // Test remove. + table.Remove(descriptor_x); + EXPECT_FALSE(table.Contains(h_X.Get())); + + // Test that WriteToMemory and ReadFromMemory work. + table.Insert(h_X.Get()); + const size_t count = table.WriteToMemory(nullptr); + std::unique_ptr<uint8_t[]> buffer(new uint8_t[count]()); + ASSERT_EQ(table.WriteToMemory(&buffer[0]), count); + ClassTable table2; + size_t count2 = table2.ReadFromMemory(&buffer[0]); + EXPECT_EQ(count, count2); + // Strong roots are not serialized, only classes. + EXPECT_TRUE(table2.Contains(h_X.Get())); + EXPECT_TRUE(table2.Contains(h_Y.Get())); + + // TODO: Add tests for UpdateClass, InsertOatFile. +} + +} // namespace mirror +} // namespace art diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 8226e6049e..743fcc87eb 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -24,11 +24,12 @@ #include <stdlib.h> #include "../../external/icu/icu4c/source/common/unicode/uvernum.h" +#include "android-base/stringprintf.h" + #include "art_field-inl.h" #include "base/macros.h" #include "base/logging.h" #include "base/stl_util.h" -#include "base/stringprintf.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "compiler_callbacks.h" @@ -65,6 +66,8 @@ int main(int argc, char **argv) { namespace art { +using android::base::StringPrintf; + ScratchFile::ScratchFile() { // ANDROID_DATA needs to be set CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA")) << diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 9f0dbbbdd0..c30272e114 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -18,6 +18,7 @@ #include <sstream> +#include "android-base/stringprintf.h" #include "ScopedLocalRef.h" #include "art_field-inl.h" @@ -37,6 +38,9 @@ namespace art { +using android::base::StringAppendV; +using android::base::StringPrintf; + static void AddReferrerLocation(std::ostream& os, ObjPtr<mirror::Class> referrer) REQUIRES_SHARED(Locks::mutator_lock_) { if (referrer != nullptr) { diff --git a/runtime/compiler_filter.cc b/runtime/compiler_filter.cc index 6e3e1d87e5..dc89d32f50 100644 --- a/runtime/compiler_filter.cc +++ b/runtime/compiler_filter.cc @@ -60,6 +60,26 @@ bool CompilerFilter::IsJniCompilationEnabled(Filter filter) { UNREACHABLE(); } +bool CompilerFilter::IsAnyMethodCompilationEnabled(Filter filter) { + switch (filter) { + case CompilerFilter::kVerifyNone: + case CompilerFilter::kVerifyAtRuntime: + case CompilerFilter::kVerifyProfile: return false; + + case CompilerFilter::kInterpretOnly: + case CompilerFilter::kSpaceProfile: + case CompilerFilter::kSpace: + case CompilerFilter::kBalanced: + case CompilerFilter::kTime: + case CompilerFilter::kSpeedProfile: + case CompilerFilter::kSpeed: + case CompilerFilter::kLayoutProfile: + case CompilerFilter::kEverythingProfile: + case CompilerFilter::kEverything: return true; + } + UNREACHABLE(); +} + bool CompilerFilter::IsVerificationEnabled(Filter filter) { switch (filter) { case CompilerFilter::kVerifyNone: diff --git a/runtime/compiler_filter.h b/runtime/compiler_filter.h index 781d43aa6e..7eb5f9a90f 100644 --- a/runtime/compiler_filter.h +++ b/runtime/compiler_filter.h @@ -52,6 +52,11 @@ class CompilerFilter FINAL { static bool IsBytecodeCompilationEnabled(Filter filter); // Returns true if an oat file with this compiler filter contains + // compiled executable code for bytecode, JNI methods, or quickened dex + // bytecode. + static bool IsAnyMethodCompilationEnabled(Filter filter); + + // Returns true if an oat file with this compiler filter contains // compiled executable code for JNI methods. static bool IsJniCompilationEnabled(Filter filter); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index e33966617f..df4413d52c 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -20,6 +20,8 @@ #include <set> +#include "android-base/stringprintf.h" + #include "arch/context.h" #include "art_field-inl.h" #include "art_method-inl.h" @@ -61,6 +63,8 @@ namespace art { +using android::base::StringPrintf; + // The key identifying the debugger to update instrumentation. static constexpr const char* kDbgInstrumentationKey = "Debugger"; diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index aa8fb38fca..7d704ad0cc 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -28,10 +28,11 @@ #include <sstream> #include <type_traits> +#include "android-base/stringprintf.h" + #include "base/enums.h" #include "base/file_magic.h" #include "base/logging.h" -#include "base/stringprintf.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" #include "dex_file-inl.h" @@ -45,6 +46,8 @@ namespace art { +using android::base::StringPrintf; + static_assert(sizeof(dex::StringIndex) == sizeof(uint32_t), "StringIndex size is wrong"); static_assert(std::is_trivially_copyable<dex::StringIndex>::value, "StringIndex not trivial"); static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong"); diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 52b9f114d2..9504e8bd29 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -18,6 +18,8 @@ #include <stdlib.h> +#include "android-base/stringprintf.h" + #include "art_field-inl.h" #include "art_method-inl.h" #include "class_linker-inl.h" @@ -31,6 +33,8 @@ namespace art { +using android::base::StringPrintf; + struct DexFile::AnnotationValue { JValue value_; uint8_t type_; diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 07f0fcae56..a3ab9fa5bd 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -22,7 +22,8 @@ #include <limits> #include <memory> -#include "base/stringprintf.h" +#include "android-base/stringprintf.h" + #include "dex_file-inl.h" #include "experimental_flags.h" #include "leb128.h" @@ -32,6 +33,9 @@ namespace art { +using android::base::StringAppendV; +using android::base::StringPrintf; + static constexpr uint32_t kTypeIdLimit = std::numeric_limits<uint16_t>::max(); static bool IsValidOrNoTypeId(uint16_t low, uint16_t high) { diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc index 99023893ba..7b8974fa5a 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex_instruction.cc @@ -21,12 +21,15 @@ #include <iomanip> #include <sstream> -#include "base/stringprintf.h" +#include "android-base/stringprintf.h" + #include "dex_file-inl.h" #include "utils.h" namespace art { +using android::base::StringPrintf; + const char* const Instruction::kInstructionNames[] = { #define INSTRUCTION_NAME(o, c, pname, f, i, a, v) pname, #include "dex_instruction_list.h" diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index 2ea7bb6778..59b734f169 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -20,9 +20,11 @@ #include <sys/types.h> #include <unistd.h> +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + #include "arch/instruction_set.h" #include "base/logging.h" -#include "base/stringprintf.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "elf_file_impl.h" @@ -32,6 +34,8 @@ namespace art { +using android::base::StringPrintf; + template <typename ElfTypes> ElfFileImpl<ElfTypes>::ElfFileImpl(File* file, bool writable, bool program_header_only, @@ -1451,7 +1455,7 @@ bool ElfFileImpl<ElfTypes>::Strip(File* file, std::string* error_msg) { section_headers_original_indexes.push_back(0); continue; } - if (StartsWith(name, ".debug") + if (android::base::StartsWith(name, ".debug") || (strcmp(name, ".strtab") == 0) || (strcmp(name, ".symtab") == 0)) { continue; diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc index f4d0bc7dbf..eb9f0395ac 100644 --- a/runtime/gc/accounting/space_bitmap.cc +++ b/runtime/gc/accounting/space_bitmap.cc @@ -16,8 +16,9 @@ #include "space_bitmap-inl.h" +#include "android-base/stringprintf.h" + #include "art_field-inl.h" -#include "base/stringprintf.h" #include "dex_file-inl.h" #include "mem_map.h" #include "mirror/object-inl.h" @@ -28,6 +29,8 @@ namespace art { namespace gc { namespace accounting { +using android::base::StringPrintf; + template<size_t kAlignment> size_t SpaceBitmap<kAlignment>::ComputeBitmapSize(uint64_t capacity) { const uint64_t kBytesCoveredPerWord = kAlignment * kBitsPerIntPtrT; diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index 2e4475f225..35a251fda8 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -16,6 +16,13 @@ #include "rosalloc.h" +#include <map> +#include <list> +#include <sstream> +#include <vector> + +#include "android-base/stringprintf.h" + #include "base/memory_tool.h" #include "base/mutex-inl.h" #include "gc/space/memory_tool_settings.h" @@ -26,15 +33,12 @@ #include "thread-inl.h" #include "thread_list.h" -#include <map> -#include <list> -#include <sstream> -#include <vector> - namespace art { namespace gc { namespace allocator { +using android::base::StringPrintf; + static constexpr bool kUsePrefetchDuringAllocRun = false; static constexpr bool kPrefetchNewRunDataByZeroing = false; static constexpr size_t kPrefetchStride = 64; diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index fbab73f022..b8899137bc 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -1360,9 +1360,10 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) { << " is_marked=" << IsMarked(to_ref); } #ifdef USE_BAKER_OR_BROOKS_READ_BARRIER + mirror::Object* referent = nullptr; if (UNLIKELY((to_ref->GetClass<kVerifyNone, kWithoutReadBarrier>()->IsTypeOfReferenceClass() && - to_ref->AsReference()->GetReferent<kWithoutReadBarrier>() != nullptr && - !IsInToSpace(to_ref->AsReference()->GetReferent<kWithoutReadBarrier>())))) { + (referent = to_ref->AsReference()->GetReferent<kWithoutReadBarrier>()) != nullptr && + !IsInToSpace(referent)))) { // Leave this reference gray in the queue so that GetReferent() will trigger a read barrier. We // will change it to white later in ReferenceQueue::DequeuePendingReference(). DCHECK(to_ref->AsReference()->GetPendingNext() != nullptr) << "Left unenqueued ref gray " << to_ref; diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index ed16854d66..01bcb7df19 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -18,6 +18,8 @@ #include "garbage_collector.h" +#include "android-base/stringprintf.h" + #include "base/dumpable.h" #include "base/histogram-inl.h" #include "base/logging.h" @@ -81,7 +83,7 @@ void GarbageCollector::ResetCumulativeStatistics() { } void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) { - ScopedTrace trace(StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName())); + ScopedTrace trace(android::base::StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName())); Thread* self = Thread::Current(); uint64_t start_time = NanoTime(); Iteration* current_iteration = GetCurrentIteration(); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 3ed138c00e..34d82845dc 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -21,6 +21,8 @@ #include <unwind.h> // For GC verification. #include <vector> +#include "android-base/stringprintf.h" + #include "allocation_listener.h" #include "art_field-inl.h" #include "base/allocator.h" @@ -82,6 +84,8 @@ namespace art { namespace gc { +using android::base::StringPrintf; + static constexpr size_t kCollectorTransitionStressIterations = 0; static constexpr size_t kCollectorTransitionStressWait = 10 * 1000; // Microseconds // Minimum amount of remaining bytes before a concurrent GC is triggered. @@ -3919,7 +3923,7 @@ void Heap::RegisterNativeFree(JNIEnv* env, size_t bytes) { ScopedObjectAccess soa(env); env->ThrowNew(WellKnownClasses::java_lang_RuntimeException, StringPrintf("Attempted to free %zd native bytes with only %zd native bytes " - "registered as allocated", bytes, expected_size).c_str()); + "registered as allocated", bytes, expected_size).c_str()); break; } } while (!native_bytes_allocated_.CompareExchangeWeakRelaxed(expected_size, diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index 2cde7d5731..081be968eb 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -75,11 +75,10 @@ ObjPtr<mirror::Object> ReferenceProcessor::GetReferent(Thread* self, MutexLock mu(self, *Locks::reference_processor_lock_); while ((!kUseReadBarrier && SlowPathEnabled()) || (kUseReadBarrier && !self->GetWeakRefAccessEnabled())) { - mirror::HeapReference<mirror::Object>* const referent_addr = - reference->GetReferentReferenceAddr(); + ObjPtr<mirror::Object> referent = reference->GetReferent<kWithoutReadBarrier>(); // If the referent became cleared, return it. Don't need barrier since thread roots can't get // updated until after we leave the function due to holding the mutator lock. - if (referent_addr->AsMirrorPtr() == nullptr) { + if (referent == nullptr) { return nullptr; } // Try to see if the referent is already marked by using the is_marked_callback. We can return @@ -91,10 +90,15 @@ ObjPtr<mirror::Object> ReferenceProcessor::GetReferent(Thread* self, // case only black nodes can be safely returned. If the GC is preserving references, the // mutator could take a white field from a grey or white node and move it somewhere else // in the heap causing corruption since this field would get swept. - if (collector_->IsMarkedHeapReference(referent_addr)) { + // Use the cached referent instead of calling GetReferent since other threads could call + // Reference.clear() after we did the null check resulting in a null pointer being + // incorrectly passed to IsMarked. b/33569625 + ObjPtr<mirror::Object> forwarded_ref = collector_->IsMarked(referent.Ptr()); + if (forwarded_ref != nullptr) { + // Non null means that it is marked. if (!preserving_references_ || (LIKELY(!reference->IsFinalizerReferenceInstance()) && reference->IsUnprocessed())) { - return referent_addr->AsMirrorPtr(); + return forwarded_ref; } } } @@ -265,11 +269,23 @@ void ReferenceProcessor::EnqueueClearedReferences(Thread* self) { } } -bool ReferenceProcessor::MakeCircularListIfUnenqueued( - ObjPtr<mirror::FinalizerReference> reference) { +void ReferenceProcessor::ClearReferent(ObjPtr<mirror::Reference> ref) { Thread* self = Thread::Current(); MutexLock mu(self, *Locks::reference_processor_lock_); - // Wait untul we are done processing reference. + // Need to wait until reference processing is done since IsMarkedHeapReference does not have a + // CAS. If we do not wait, it can result in the GC un-clearing references due to race conditions. + // This also handles the race where the referent gets cleared after a null check but before + // IsMarkedHeapReference is called. + WaitUntilDoneProcessingReferences(self); + if (Runtime::Current()->IsActiveTransaction()) { + ref->ClearReferent<true>(); + } else { + ref->ClearReferent<false>(); + } +} + +void ReferenceProcessor::WaitUntilDoneProcessingReferences(Thread* self) { + // Wait until we are done processing reference. while ((!kUseReadBarrier && SlowPathEnabled()) || (kUseReadBarrier && !self->GetWeakRefAccessEnabled())) { // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the @@ -277,6 +293,13 @@ bool ReferenceProcessor::MakeCircularListIfUnenqueued( self->CheckEmptyCheckpoint(); condition_.WaitHoldingLocks(self); } +} + +bool ReferenceProcessor::MakeCircularListIfUnenqueued( + ObjPtr<mirror::FinalizerReference> reference) { + Thread* self = Thread::Current(); + MutexLock mu(self, *Locks::reference_processor_lock_); + WaitUntilDoneProcessingReferences(self); // At this point, since the sentinel of the reference is live, it is guaranteed to not be // enqueued if we just finished processing references. Otherwise, we may be doing the main GC // phase. Since we are holding the reference processor lock, it guarantees that reference diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h index 759b7e129c..b15544d549 100644 --- a/runtime/gc/reference_processor.h +++ b/runtime/gc/reference_processor.h @@ -73,6 +73,9 @@ class ReferenceProcessor { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::reference_processor_lock_, !Locks::reference_queue_finalizer_references_lock_); + void ClearReferent(ObjPtr<mirror::Reference> ref) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::reference_processor_lock_); private: bool SlowPathEnabled() REQUIRES_SHARED(Locks::mutator_lock_); @@ -84,6 +87,10 @@ class ReferenceProcessor { // referents. void StartPreservingReferences(Thread* self) REQUIRES(!Locks::reference_processor_lock_); void StopPreservingReferences(Thread* self) REQUIRES(!Locks::reference_processor_lock_); + // Wait until reference processing is done. + void WaitUntilDoneProcessingReferences(Thread* self) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(Locks::reference_processor_lock_); // Collector which is clearing references, used by the GetReferent to return referents which are // already marked. collector::GarbageCollector* collector_ GUARDED_BY(Locks::reference_processor_lock_); diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index c7269440f0..e03958d717 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -22,6 +22,9 @@ #include <sys/types.h> #include <unistd.h> +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + #include "art_method.h" #include "base/enums.h" #include "base/macros.h" @@ -43,6 +46,9 @@ namespace art { namespace gc { namespace space { +using android::base::StringAppendF; +using android::base::StringPrintf; + Atomic<uint32_t> ImageSpace::bitmap_index_(0); ImageSpace::ImageSpace(const std::string& image_filename, @@ -137,7 +143,7 @@ static bool GenerateImage(const std::string& image_filename, arg_vector.push_back(compiler_options[i].c_str()); } - std::string command_line(Join(arg_vector, ' ')); + std::string command_line(android::base::Join(arg_vector, ' ')); LOG(INFO) << "GenerateImage: " << command_line; return Exec(arg_vector, error_msg); } @@ -257,7 +263,7 @@ static bool RelocateImage(const char* image_location, argv.push_back(instruction_set_arg); argv.push_back(base_offset_arg); - std::string command_line(Join(argv, ' ')); + std::string command_line(android::base::Join(argv, ' ')); LOG(INFO) << "RelocateImage: " << command_line; return Exec(argv, error_msg); } diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h index fa941c0376..5999548d2b 100644 --- a/runtime/gc/space/image_space_fs.h +++ b/runtime/gc/space/image_space_fs.h @@ -20,9 +20,10 @@ #include <dirent.h> #include <dlfcn.h> +#include "android-base/stringprintf.h" + #include "base/logging.h" #include "base/macros.h" -#include "base/stringprintf.h" #include "base/unix_file/fd_file.h" #include "globals.h" #include "os.h" @@ -56,7 +57,7 @@ static void DeleteDirectoryContents(const std::string& dir, bool recurse) { continue; } // We only want to delete regular files and symbolic links. - std::string file = StringPrintf("%s/%s", dir.c_str(), name); + std::string file = android::base::StringPrintf("%s/%s", dir.c_str(), name); if (de->d_type != DT_REG && de->d_type != DT_LNK) { if (de->d_type == DT_DIR) { if (recurse) { diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc index b1572cc7ea..a186f4c922 100644 --- a/runtime/gc/space/malloc_space.cc +++ b/runtime/gc/space/malloc_space.cc @@ -16,6 +16,8 @@ #include "malloc_space.h" +#include "android-base/stringprintf.h" + #include "gc/accounting/card_table-inl.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" @@ -33,6 +35,8 @@ namespace art { namespace gc { namespace space { +using android::base::StringPrintf; + size_t MallocSpace::bitmap_index_ = 0; MallocSpace::MallocSpace(const std::string& name, MemMap* mem_map, diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h index f13ff8c9bc..bebcd71951 100644 --- a/runtime/generated/asm_support_gen.h +++ b/runtime/generated/asm_support_gen.h @@ -74,8 +74,6 @@ DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_32), (static_c DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k64).Int32Value()))) #define ART_METHOD_DECLARING_CLASS_OFFSET 0 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DECLARING_CLASS_OFFSET), (static_cast<int32_t>(art::ArtMethod:: DeclaringClassOffset().Int32Value()))) -#define DECLARING_CLASS_DEX_CACHE_STRINGS_OFFSET 40 -DEFINE_CHECK_EQ(static_cast<int32_t>(DECLARING_CLASS_DEX_CACHE_STRINGS_OFFSET), (static_cast<int32_t>(art::mirror::Class:: DexCacheStringsOffset().Int32Value()))) #define STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT 3 DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT), (static_cast<int32_t>(art::WhichPowerOf2(sizeof(art::mirror::StringDexCachePair))))) #define STRING_DEX_CACHE_SIZE_MINUS_ONE 1023 diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index 8cbe49166a..fe6a6e9140 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -37,9 +37,10 @@ #include <set> +#include "android-base/stringprintf.h" + #include "art_field-inl.h" #include "base/logging.h" -#include "base/stringprintf.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" @@ -797,8 +798,9 @@ class Hprof : public SingleRootVisitor { file->Erase(); } if (!okay) { - std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s", - filename_.c_str(), strerror(errno))); + std::string msg(android::base::StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s", + filename_.c_str(), + strerror(errno))); ThrowRuntimeException("%s", msg.c_str()); LOG(ERROR) << msg; } diff --git a/runtime/image.cc b/runtime/image.cc index 52c9f4edf0..2ef60c3e07 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -25,7 +25,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '3', '\0' }; +const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '4', '\0' }; // mirror::Class update ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/image.h b/runtime/image.h index da9976a901..6c76f4915e 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -189,6 +189,7 @@ class PACKED(4) ImageHeader { enum ImageRoot { kDexCaches, kClassRoots, + kClassLoader, // App image only. kImageRootsMax, }; @@ -206,6 +207,10 @@ class PACKED(4) ImageHeader { kSectionCount, // Number of elements in enum. }; + static size_t NumberOfImageRoots(bool app_image) { + return app_image ? kImageRootsMax : kImageRootsMax - 1u; + } + ArtMethod* GetImageMethod(ImageMethod index) const; void SetImageMethod(ImageMethod index, ArtMethod* method); diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h index 9c634fa861..0e66ae96b5 100644 --- a/runtime/indirect_reference_table-inl.h +++ b/runtime/indirect_reference_table-inl.h @@ -19,6 +19,8 @@ #include "indirect_reference_table.h" +#include "android-base/stringprintf.h" + #include "base/dumpable.h" #include "gc_root-inl.h" #include "obj_ptr-inl.h" @@ -38,15 +40,15 @@ inline bool IndirectReferenceTable::GetChecked(IndirectRef iref) const { return false; } if (UNLIKELY(GetIndirectRefKind(iref) == kHandleScopeOrInvalid)) { - AbortIfNoCheckJNI(StringPrintf("JNI ERROR (app bug): invalid %s %p", - GetIndirectRefKindString(kind_), - iref)); + AbortIfNoCheckJNI(android::base::StringPrintf("JNI ERROR (app bug): invalid %s %p", + GetIndirectRefKindString(kind_), + iref)); return false; } const uint32_t top_index = segment_state_.top_index; uint32_t idx = ExtractIndex(iref); if (UNLIKELY(idx >= top_index)) { - std::string msg = StringPrintf( + std::string msg = android::base::StringPrintf( "JNI ERROR (app bug): accessed stale %s %p (index %d in a table of size %d)", GetIndirectRefKindString(kind_), iref, @@ -56,9 +58,9 @@ inline bool IndirectReferenceTable::GetChecked(IndirectRef iref) const { return false; } if (UNLIKELY(table_[idx].GetReference()->IsNull())) { - AbortIfNoCheckJNI(StringPrintf("JNI ERROR (app bug): accessed deleted %s %p", - GetIndirectRefKindString(kind_), - iref)); + AbortIfNoCheckJNI(android::base::StringPrintf("JNI ERROR (app bug): accessed deleted %s %p", + GetIndirectRefKindString(kind_), + iref)); return false; } if (UNLIKELY(!CheckEntry("use", iref, idx))) { @@ -73,7 +75,7 @@ inline bool IndirectReferenceTable::CheckEntry(const char* what, uint32_t idx) const { IndirectRef checkRef = ToIndirectRef(idx); if (UNLIKELY(checkRef != iref)) { - std::string msg = StringPrintf( + std::string msg = android::base::StringPrintf( "JNI ERROR (app bug): attempt to %s stale %s %p (should be %p)", what, GetIndirectRefKindString(kind_), diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc index 722b411f97..bf4cab24cc 100644 --- a/runtime/indirect_reference_table_test.cc +++ b/runtime/indirect_reference_table_test.cc @@ -16,6 +16,8 @@ #include "indirect_reference_table-inl.h" +#include "android-base/stringprintf.h" + #include "class_linker-inl.h" #include "common_runtime_test.h" #include "mirror/object-inl.h" @@ -23,6 +25,8 @@ namespace art { +using android::base::StringPrintf; + class IndirectReferenceTableTest : public CommonRuntimeTest {}; static void CheckDump(IndirectReferenceTable* irt, size_t num_objects, size_t num_unique) diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 870d1ae9b5..03ef962f05 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -1098,10 +1098,14 @@ TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self, uintpt Dbg::IsForcedInterpreterNeededForUpcall(self, visitor.caller)); if (deoptimize && Runtime::Current()->IsDeoptimizeable(*return_pc)) { if (kVerboseInstrumentation) { - LOG(INFO) << StringPrintf("Deoptimizing %s by returning from %s with result %#" PRIx64 " in ", - visitor.caller->PrettyMethod().c_str(), - method->PrettyMethod().c_str(), - return_value.GetJ()) << *self; + LOG(INFO) << "Deoptimizing " + << visitor.caller->PrettyMethod() + << " by returning from " + << method->PrettyMethod() + << " with result " + << std::hex << return_value.GetJ() << std::dec + << " in " + << *self; } self->PushDeoptimizationContext(return_value, return_shorty == 'L', diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 22da07dd24..a09e71b6c0 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -676,7 +676,7 @@ void AbortTransactionV(Thread* self, const char* fmt, va_list args) { CHECK(Runtime::Current()->IsActiveTransaction()); // Constructs abort message. std::string abort_msg; - StringAppendV(&abort_msg, fmt, args); + android::base::StringAppendV(&abort_msg, fmt, args); // Throws an exception so we can abort the transaction and rollback every change. Runtime::Current()->AbortTransactionAndThrowAbortError(self, abort_msg); } diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index c9a5b44c54..423f0543d5 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -25,6 +25,8 @@ #include <sstream> #include <atomic> +#include "android-base/stringprintf.h" + #include "art_field-inl.h" #include "art_method-inl.h" #include "base/enums.h" @@ -254,7 +256,7 @@ static inline ObjPtr<mirror::String> ResolveString(Thread* self, DCHECK_LT(string_idx.index_ % mirror::DexCache::kDexCacheStringCacheSize, declaring_class->GetDexFile().NumStringIds()); ObjPtr<mirror::String> string_ptr = - mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(), + mirror::StringDexCachePair::Lookup(declaring_class->GetDexCache()->GetStrings(), string_idx.index_, mirror::DexCache::kDexCacheStringCacheSize).Read(); if (UNLIKELY(string_ptr == nullptr)) { @@ -430,12 +432,12 @@ static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruc #define TRACE_LOG std::cerr std::ostringstream oss; oss << shadow_frame.GetMethod()->PrettyMethod() - << StringPrintf("\n0x%x: ", dex_pc) + << android::base::StringPrintf("\n0x%x: ", dex_pc) << inst->DumpString(shadow_frame.GetMethod()->GetDexFile()) << "\n"; for (uint32_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) { uint32_t raw_value = shadow_frame.GetVReg(i); ObjPtr<mirror::Object> ref_value = shadow_frame.GetVRegReference(i); - oss << StringPrintf(" vreg%u=0x%08X", i, raw_value); + oss << android::base::StringPrintf(" vreg%u=0x%08X", i, raw_value); if (ref_value != nullptr) { if (ref_value->GetClass()->IsStringClass() && !ref_value->AsString()->IsValueNull()) { diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 52eacd564f..b0d7fb247a 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -92,6 +92,16 @@ namespace interpreter { } \ } while (false) +#define HANDLE_BACKWARD_BRANCH(offset) \ + do { \ + if (IsBackwardBranch(offset)) { \ + HOTNESS_UPDATE(); \ + /* Record new dex pc early to have consistent suspend point at loop header. */ \ + shadow_frame.SetDexPC(inst->GetDexPc(insns)); \ + self->AllowThreadSuspension(); \ + } \ + } while (false) + template<bool do_access_check, bool transaction_active> JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register, @@ -594,55 +604,40 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, PREAMBLE(); int8_t offset = inst->VRegA_10t(inst_data); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); break; } case Instruction::GOTO_16: { PREAMBLE(); int16_t offset = inst->VRegA_20t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); break; } case Instruction::GOTO_32: { PREAMBLE(); int32_t offset = inst->VRegA_30t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); break; } case Instruction::PACKED_SWITCH: { PREAMBLE(); int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); break; } case Instruction::SPARSE_SWITCH: { PREAMBLE(); int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); break; } @@ -739,11 +734,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); } else { BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); @@ -756,11 +748,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); } else { BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); @@ -773,11 +762,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); } else { BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); @@ -790,11 +776,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); } else { BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); @@ -807,11 +790,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); } else { BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); @@ -824,11 +804,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); } else { BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); @@ -840,11 +817,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) { int16_t offset = inst->VRegB_21t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); } else { BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); @@ -856,11 +830,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) { int16_t offset = inst->VRegB_21t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); } else { BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); @@ -872,11 +843,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) { int16_t offset = inst->VRegB_21t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); } else { BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); @@ -888,11 +856,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) { int16_t offset = inst->VRegB_21t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); } else { BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); @@ -904,11 +869,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) { int16_t offset = inst->VRegB_21t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); } else { BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); @@ -920,11 +882,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) { int16_t offset = inst->VRegB_21t(); BRANCH_INSTRUMENTATION(offset); - if (IsBackwardBranch(offset)) { - HOTNESS_UPDATE(); - self->AllowThreadSuspension(); - } inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); } else { BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index a5b1038d5a..7dd3d3db4d 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -25,6 +25,7 @@ #include <locale> #include <unordered_map> +#include "android-base/stringprintf.h" #include "ScopedLocalRef.h" #include "art_method-inl.h" @@ -56,6 +57,9 @@ namespace art { namespace interpreter { +using android::base::StringAppendV; +using android::base::StringPrintf; + static void AbortTransactionOrFail(Thread* self, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index caf705a9cb..f80c43d80c 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -18,6 +18,8 @@ #include <dlfcn.h> +#include "android-base/stringprintf.h" + #include "art_method.h" #include "base/dumpable.h" #include "base/mutex.h" @@ -42,6 +44,9 @@ namespace art { +using android::base::StringAppendF; +using android::base::StringAppendV; + static constexpr size_t kGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.) static constexpr size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.) diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc index e9d6d079c1..0eff2ab47b 100644 --- a/runtime/jdwp/jdwp_adb.cc +++ b/runtime/jdwp/jdwp_adb.cc @@ -20,8 +20,9 @@ #include <sys/un.h> #include <unistd.h> +#include "android-base/stringprintf.h" + #include "base/logging.h" -#include "base/stringprintf.h" #include "jdwp/jdwp_priv.h" #ifdef ART_TARGET_ANDROID @@ -52,6 +53,8 @@ namespace art { namespace JDWP { +using android::base::StringPrintf; + struct JdwpAdbState : public JdwpNetStateBase { public: explicit JdwpAdbState(JdwpState* state) : JdwpNetStateBase(state) { diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 5574a117a7..172f52a974 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -21,10 +21,11 @@ #include <string.h> #include <unistd.h> +#include "android-base/stringprintf.h" + #include "art_field-inl.h" #include "art_method-inl.h" #include "base/logging.h" -#include "base/stringprintf.h" #include "debugger.h" #include "jdwp/jdwp_constants.h" #include "jdwp/jdwp_expand_buf.h" @@ -103,6 +104,8 @@ namespace art { namespace JDWP { +using android::base::StringPrintf; + /* * Stuff to compare against when deciding if a mod matches. Only the * values for mods valid for the event being evaluated will be filled in. diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index 0f2d188bca..964c5679d7 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -20,11 +20,12 @@ #include <memory> #include <string> +#include "android-base/stringprintf.h" + #include "atomic.h" #include "base/hex_dump.h" #include "base/logging.h" #include "base/macros.h" -#include "base/stringprintf.h" #include "debugger.h" #include "jdwp/jdwp_constants.h" #include "jdwp/jdwp_event.h" @@ -39,6 +40,8 @@ namespace art { namespace JDWP { +using android::base::StringPrintf; + std::string DescribeField(const FieldId& field_id) { return StringPrintf("%#" PRIx64 " (%s)", field_id, Dbg::GetFieldName(field_id).c_str()); } diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc index e3bf3e5a9d..7707ba4932 100644 --- a/runtime/jdwp/jdwp_main.cc +++ b/runtime/jdwp/jdwp_main.cc @@ -20,6 +20,8 @@ #include <time.h> #include <unistd.h> +#include "android-base/stringprintf.h" + #include "atomic.h" #include "base/logging.h" #include "base/time_utils.h" @@ -31,6 +33,8 @@ namespace art { namespace JDWP { +using android::base::StringPrintf; + static void* StartJdwpThread(void* arg); /* diff --git a/runtime/jdwp/jdwp_request.cc b/runtime/jdwp/jdwp_request.cc index 18f40a143c..6af267e674 100644 --- a/runtime/jdwp/jdwp_request.cc +++ b/runtime/jdwp/jdwp_request.cc @@ -18,7 +18,8 @@ #include <inttypes.h> -#include "base/stringprintf.h" +#include "android-base/stringprintf.h" + #include "jdwp/jdwp_priv.h" namespace art { @@ -100,7 +101,7 @@ MethodId Request::ReadMethodId() { ObjectId Request::ReadObjectId(const char* specific_kind) { ObjectId id = Read8BE(); - VLOG(jdwp) << StringPrintf(" %s id %#" PRIx64, specific_kind, id); + VLOG(jdwp) << android::base::StringPrintf(" %s id %#" PRIx64, specific_kind, id); return id; } diff --git a/runtime/jdwp/jdwp_socket.cc b/runtime/jdwp/jdwp_socket.cc index 3be7fd6428..97662f0727 100644 --- a/runtime/jdwp/jdwp_socket.cc +++ b/runtime/jdwp/jdwp_socket.cc @@ -26,8 +26,9 @@ #include <sys/types.h> #include <unistd.h> +#include "android-base/stringprintf.h" + #include "base/logging.h" -#include "base/stringprintf.h" #include "jdwp/jdwp_priv.h" namespace art { @@ -500,7 +501,7 @@ bool JdwpSocketState::ProcessIncoming() { */ if (IsAwaitingHandshake()) { if (memcmp(input_buffer_, kMagicHandshake, kMagicHandshakeLen) != 0) { - LOG(ERROR) << StringPrintf("ERROR: bad handshake '%.14s'", input_buffer_); + LOG(ERROR) << android::base::StringPrintf("ERROR: bad handshake '%.14s'", input_buffer_); goto fail; } diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index dac2e60987..b7125a8a43 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -291,7 +291,7 @@ void Jit::CreateThreadPool() { // is not null when we instrument. thread_pool_.reset(new ThreadPool("Jit thread pool", 1)); thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority); - thread_pool_->StartWorkers(Thread::Current()); + Start(); } void Jit::DeleteThreadPool() { @@ -710,14 +710,23 @@ void Jit::WaitForCompilationToFinish(Thread* self) { } } +void Jit::Stop() { + Thread* self = Thread::Current(); + // TODO(ngeoffray): change API to not require calling WaitForCompilationToFinish twice. + WaitForCompilationToFinish(self); + GetThreadPool()->StopWorkers(self); + WaitForCompilationToFinish(self); +} + +void Jit::Start() { + GetThreadPool()->StartWorkers(Thread::Current()); +} + ScopedJitSuspend::ScopedJitSuspend() { jit::Jit* jit = Runtime::Current()->GetJit(); was_on_ = (jit != nullptr) && (jit->GetThreadPool() != nullptr); if (was_on_) { - Thread* self = Thread::Current(); - jit->WaitForCompilationToFinish(self); - jit->GetThreadPool()->StopWorkers(self); - jit->WaitForCompilationToFinish(self); + jit->Stop(); } } @@ -725,7 +734,7 @@ ScopedJitSuspend::~ScopedJitSuspend() { if (was_on_) { DCHECK(Runtime::Current()->GetJit() != nullptr); DCHECK(Runtime::Current()->GetJit()->GetThreadPool() != nullptr); - Runtime::Current()->GetJit()->GetThreadPool()->StartWorkers(Thread::Current()); + Runtime::Current()->GetJit()->Start(); } } diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index a230c78033..05c390590b 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -179,6 +179,12 @@ class Jit { return thread_pool_.get(); } + // Stop the JIT by waiting for all current compilations and enqueued compilations to finish. + void Stop(); + + // Start JIT threads. + void Start(); + private: Jit(); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 93f50ad2b1..f43e30dd6f 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -217,6 +217,7 @@ uint8_t* JitCodeCache::CommitCode(Thread* self, size_t fp_spill_mask, const uint8_t* code, size_t code_size, + size_t data_size, bool osr, Handle<mirror::ObjectArray<mirror::Object>> roots, bool has_should_deoptimize_flag, @@ -230,6 +231,7 @@ uint8_t* JitCodeCache::CommitCode(Thread* self, fp_spill_mask, code, code_size, + data_size, osr, roots, has_should_deoptimize_flag, @@ -246,6 +248,7 @@ uint8_t* JitCodeCache::CommitCode(Thread* self, fp_spill_mask, code, code_size, + data_size, osr, roots, has_should_deoptimize_flag, @@ -297,10 +300,11 @@ static void FillRootTable(uint8_t* roots_data, Handle<mirror::ObjectArray<mirror ObjPtr<mirror::Object> object = roots->Get(i); if (kIsDebugBuild) { // Ensure the string is strongly interned. b/32995596 - CHECK(object->IsString()); - ObjPtr<mirror::String> str = reinterpret_cast<mirror::String*>(object.Ptr()); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - CHECK(class_linker->GetInternTable()->LookupStrong(Thread::Current(), str) != nullptr); + if (object->IsString()) { + ObjPtr<mirror::String> str = reinterpret_cast<mirror::String*>(object.Ptr()); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + CHECK(class_linker->GetInternTable()->LookupStrong(Thread::Current(), str) != nullptr); + } } gc_roots[i] = GcRoot<mirror::Object>(object); } @@ -316,6 +320,31 @@ static uint8_t* GetRootTable(const void* code_ptr, uint32_t* number_of_roots = n return data - ComputeRootTableSize(roots); } +// Helper for the GC to process a weak class in a JIT root table. +static inline void ProcessWeakClass(GcRoot<mirror::Class>* root_ptr, IsMarkedVisitor* visitor) + REQUIRES_SHARED(Locks::mutator_lock_) { + // This does not need a read barrier because this is called by GC. + mirror::Class* cls = root_ptr->Read<kWithoutReadBarrier>(); + if (cls != nullptr) { + DCHECK((cls->IsClass<kDefaultVerifyFlags, kWithoutReadBarrier>())); + // Look at the classloader of the class to know if it has been unloaded. + // This does not need a read barrier because this is called by GC. + mirror::Object* class_loader = + cls->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>(); + if (class_loader == nullptr || visitor->IsMarked(class_loader) != nullptr) { + // The class loader is live, update the entry if the class has moved. + mirror::Class* new_cls = down_cast<mirror::Class*>(visitor->IsMarked(cls)); + // Note that new_object can be null for CMS and newly allocated objects. + if (new_cls != nullptr && new_cls != cls) { + *root_ptr = GcRoot<mirror::Class>(new_cls); + } + } else { + // The class loader is not live, clear the entry. + *root_ptr = GcRoot<mirror::Class>(nullptr); + } + } +} + void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) { MutexLock mu(Thread::Current(), lock_); for (const auto& entry : method_code_map_) { @@ -325,17 +354,22 @@ void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) { for (uint32_t i = 0; i < number_of_roots; ++i) { // This does not need a read barrier because this is called by GC. mirror::Object* object = roots[i].Read<kWithoutReadBarrier>(); - DCHECK(object != nullptr); - mirror::Object* new_object = visitor->IsMarked(object); - // We know the string is marked because it's a strongly-interned string that - // is always alive. The IsMarked implementation of the CMS collector returns - // null for newly allocated objects, but we know those haven't moved. Therefore, - // only update the entry if we get a different non-null string. - // TODO: Do not use IsMarked for j.l.Class, and adjust once we move this method - // out of the weak access/creation pause. b/32167580 - if (new_object != nullptr && new_object != object) { - DCHECK(new_object->IsString()); - roots[i] = GcRoot<mirror::Object>(new_object); + if (object == nullptr) { + // entry got deleted in a previous sweep. + } else if (object->IsString<kDefaultVerifyFlags, kWithoutReadBarrier>()) { + mirror::Object* new_object = visitor->IsMarked(object); + // We know the string is marked because it's a strongly-interned string that + // is always alive. The IsMarked implementation of the CMS collector returns + // null for newly allocated objects, but we know those haven't moved. Therefore, + // only update the entry if we get a different non-null string. + // TODO: Do not use IsMarked for j.l.Class, and adjust once we move this method + // out of the weak access/creation pause. b/32167580 + if (new_object != nullptr && new_object != object) { + DCHECK(new_object->IsString()); + roots[i] = GcRoot<mirror::Object>(new_object); + } + } else { + ProcessWeakClass(reinterpret_cast<GcRoot<mirror::Class>*>(&roots[i]), visitor); } } } @@ -344,26 +378,7 @@ void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) { for (size_t i = 0; i < info->number_of_inline_caches_; ++i) { InlineCache* cache = &info->cache_[i]; for (size_t j = 0; j < InlineCache::kIndividualCacheSize; ++j) { - // This does not need a read barrier because this is called by GC. - mirror::Class* cls = cache->classes_[j].Read<kWithoutReadBarrier>(); - if (cls != nullptr) { - // Look at the classloader of the class to know if it has been - // unloaded. - // This does not need a read barrier because this is called by GC. - mirror::Object* class_loader = - cls->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>(); - if (class_loader == nullptr || visitor->IsMarked(class_loader) != nullptr) { - // The class loader is live, update the entry if the class has moved. - mirror::Class* new_cls = down_cast<mirror::Class*>(visitor->IsMarked(cls)); - // Note that new_object can be null for CMS and newly allocated objects. - if (new_cls != nullptr && new_cls != cls) { - cache->classes_[j] = GcRoot<mirror::Class>(new_cls); - } - } else { - // The class loader is not live, clear the entry. - cache->classes_[j] = GcRoot<mirror::Class>(nullptr); - } - } + ProcessWeakClass(&cache->classes_[j], visitor); } } } @@ -501,6 +516,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, size_t fp_spill_mask, const uint8_t* code, size_t code_size, + size_t data_size, bool osr, Handle<mirror::ObjectArray<mirror::Object>> roots, bool has_should_deoptimize_flag, @@ -535,6 +551,11 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, core_spill_mask, fp_spill_mask, code_size); + DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); + DCHECK_LE(roots_data, stack_map); + // Flush data cache, as compiled code references literals in it. + FlushDataCache(reinterpret_cast<char*>(roots_data), + reinterpret_cast<char*>(roots_data + data_size)); // Flush caches before we remove write permission because on some ARMv8 hardware, // flushing caches require write permissions. // @@ -645,12 +666,12 @@ void JitCodeCache::ClearData(Thread* self, FreeData(reinterpret_cast<uint8_t*>(roots_data)); } -void JitCodeCache::ReserveData(Thread* self, - size_t stack_map_size, - size_t number_of_roots, - ArtMethod* method, - uint8_t** stack_map_data, - uint8_t** roots_data) { +size_t JitCodeCache::ReserveData(Thread* self, + size_t stack_map_size, + size_t number_of_roots, + ArtMethod* method, + uint8_t** stack_map_data, + uint8_t** roots_data) { size_t table_size = ComputeRootTableSize(number_of_roots); size_t size = RoundUp(stack_map_size + table_size, sizeof(void*)); uint8_t* result = nullptr; @@ -683,9 +704,11 @@ void JitCodeCache::ReserveData(Thread* self, *roots_data = result; *stack_map_data = result + table_size; FillRootTableLength(*roots_data, number_of_roots); + return size; } else { *roots_data = nullptr; *stack_map_data = nullptr; + return 0; } } diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 30e2efbac5..d97742d00b 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -106,6 +106,7 @@ class JitCodeCache { size_t fp_spill_mask, const uint8_t* code, size_t code_size, + size_t data_size, bool osr, Handle<mirror::ObjectArray<mirror::Object>> roots, bool has_should_deoptimize_flag, @@ -121,12 +122,13 @@ class JitCodeCache { // Allocate a region of data that contain `size` bytes, and potentially space // for storing `number_of_roots` roots. Returns null if there is no more room. - void ReserveData(Thread* self, - size_t size, - size_t number_of_roots, - ArtMethod* method, - uint8_t** stack_map_data, - uint8_t** roots_data) + // Return the number of bytes allocated. + size_t ReserveData(Thread* self, + size_t size, + size_t number_of_roots, + ArtMethod* method, + uint8_t** stack_map_data, + uint8_t** roots_data) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!lock_); @@ -237,6 +239,7 @@ class JitCodeCache { size_t fp_spill_mask, const uint8_t* code, size_t code_size, + size_t data_size, bool osr, Handle<mirror::ObjectArray<mirror::Object>> roots, bool has_should_deoptimize_flag, diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 11d601e8c8..025d10ccc0 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -20,6 +20,8 @@ #include <sys/stat.h> #include <fcntl.h> +#include "android-base/strings.h" + #include "art_method-inl.h" #include "base/enums.h" #include "base/systrace.h" @@ -412,7 +414,7 @@ void ProfileSaver::Start(const ProfileSaverOptions& options, } VLOG(profiler) << "Starting profile saver using output file: " << output_filename - << ". Tracking: " << Join(code_paths_to_profile, ':'); + << ". Tracking: " << android::base::Join(code_paths_to_profile, ':'); instance_ = new ProfileSaver(options, output_filename, diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc index 342e0d2a53..5a3fafa726 100644 --- a/runtime/jni_env_ext.cc +++ b/runtime/jni_env_ext.cc @@ -19,6 +19,8 @@ #include <algorithm> #include <vector> +#include "android-base/stringprintf.h" + #include "check_jni.h" #include "indirect_reference_table.h" #include "java_vm_ext.h" @@ -30,6 +32,8 @@ namespace art { +using android::base::StringPrintf; + static constexpr size_t kMonitorsInitial = 32; // Arbitrary. static constexpr size_t kMonitorsMax = 4096; // Arbitrary sanity check. diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index a421c340ae..4da5e23502 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -16,6 +16,8 @@ #include "jni_internal.h" +#include "android-base/stringprintf.h" + #include "art_method-inl.h" #include "common_compiler_test.h" #include "indirect_reference_table.h" @@ -27,6 +29,8 @@ namespace art { +using android::base::StringPrintf; + // TODO: Convert to CommonRuntimeTest. Currently MakeExecutable is used. class JniInternalTest : public CommonCompilerTest { protected: diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 6da72e4625..19a65bb27e 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -24,7 +24,8 @@ #include <memory> #include <sstream> -#include "base/stringprintf.h" +#include "android-base/stringprintf.h" + #include "base/unix_file/fd_file.h" #include "os.h" #include "thread-inl.h" @@ -42,6 +43,8 @@ namespace art { +using android::base::StringPrintf; + static std::ostream& operator<<( std::ostream& os, std::pair<BacktraceMap::const_iterator, BacktraceMap::const_iterator> iters) { diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 3c22d7f656..da510ceb5c 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -14,9 +14,10 @@ * limitations under the License. */ -#include "method_handles.h" - #include "method_handles-inl.h" + +#include "android-base/stringprintf.h" + #include "jvalue.h" #include "jvalue-inl.h" #include "reflection.h" @@ -25,6 +26,8 @@ namespace art { +using android::base::StringPrintf; + namespace { #define PRIMITIVES_LIST(V) \ diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 7d7c1d7cfd..a5db0c0a8a 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -19,10 +19,11 @@ #include "array.h" +#include "android-base/stringprintf.h" + #include "base/bit_utils.h" #include "base/casts.h" #include "base/logging.h" -#include "base/stringprintf.h" #include "class-inl.h" #include "gc/heap-inl.h" #include "obj_ptr-inl.h" @@ -167,9 +168,9 @@ inline Array* Array::Alloc(Thread* self, #else // 32-bit. if (UNLIKELY(size == 0)) { - self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow", - array_class->PrettyDescriptor().c_str(), - component_count).c_str()); + self->ThrowOutOfMemoryError(android::base::StringPrintf("%s of length %d would overflow", + array_class->PrettyDescriptor().c_str(), + component_count).c_str()); return nullptr; } #endif diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index 8afa4aa744..cc548b9cc8 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "array.h" +#include "array-inl.h" #include "class.h" #include "class-inl.h" @@ -23,7 +23,6 @@ #include "dex_file-inl.h" #include "gc/accounting/card_table-inl.h" #include "object-inl.h" -#include "object_array.h" #include "object_array-inl.h" #include "handle_scope-inl.h" #include "thread.h" @@ -32,6 +31,8 @@ namespace art { namespace mirror { +using android::base::StringPrintf; + // Create a multi-dimensional array of Objects or primitive types. // // We have to generate the names for X[], X[][], X[][][], and so on. The diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 5fdf8f3c3a..2fb8d28de9 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -937,14 +937,6 @@ inline uint32_t Class::NumDirectInterfaces() { } } -inline void Class::SetDexCacheStrings(StringDexCacheType* new_dex_cache_strings) { - SetFieldPtr<false>(DexCacheStringsOffset(), new_dex_cache_strings); -} - -inline StringDexCacheType* Class::GetDexCacheStrings() { - return GetFieldPtr64<StringDexCacheType*>(DexCacheStringsOffset()); -} - template<ReadBarrierOption kReadBarrierOption, class Visitor> void Class::VisitNativeRoots(Visitor& visitor, PointerSize pointer_size) { for (ArtField& field : GetSFieldsUnchecked()) { @@ -1095,12 +1087,6 @@ inline void Class::FixupNativePointers(Class* dest, if (methods != new_methods) { dest->SetMethodsPtrInternal(new_methods); } - // Update dex cache strings. - StringDexCacheType* strings = GetDexCacheStrings(); - StringDexCacheType* new_strings = visitor(strings); - if (strings != new_strings) { - dest->SetDexCacheStrings(new_strings); - } // Fix up embedded tables. if (!IsTemp() && ShouldHaveEmbeddedVTable<kVerifyNone, kReadBarrierOption>()) { for (int32_t i = 0, count = GetEmbeddedVTableLength(); i < count; ++i) { diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index a862c9798a..9964b73c98 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -16,6 +16,8 @@ #include "class.h" +#include "android-base/stringprintf.h" + #include "art_field-inl.h" #include "art_method-inl.h" #include "class_ext.h" @@ -40,6 +42,8 @@ namespace art { namespace mirror { +using android::base::StringPrintf; + GcRoot<Class> Class::java_lang_Class_; void Class::SetClassClass(ObjPtr<Class> java_lang_Class) { @@ -187,7 +191,6 @@ void Class::SetStatus(Handle<Class> h_this, Status new_status, Thread* self) { void Class::SetDexCache(ObjPtr<DexCache> new_dex_cache) { SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache); - SetDexCacheStrings(new_dex_cache != nullptr ? new_dex_cache->GetStrings() : nullptr); } void Class::SetClassSize(uint32_t new_class_size) { diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index d7449c8008..fb2792a316 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -1247,13 +1247,6 @@ class MANAGED Class FINAL : public Object { bool GetSlowPathEnabled() REQUIRES_SHARED(Locks::mutator_lock_); void SetSlowPath(bool enabled) REQUIRES_SHARED(Locks::mutator_lock_); - StringDexCacheType* GetDexCacheStrings() REQUIRES_SHARED(Locks::mutator_lock_); - void SetDexCacheStrings(StringDexCacheType* new_dex_cache_strings) - REQUIRES_SHARED(Locks::mutator_lock_); - static MemberOffset DexCacheStringsOffset() { - return OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_strings_); - } - // May cause thread suspension due to EqualParameters. ArtMethod* GetDeclaredConstructor(Thread* self, Handle<ObjectArray<Class>> args, @@ -1438,9 +1431,6 @@ class MANAGED Class FINAL : public Object { // virtual_ methods_ for miranda methods. HeapReference<PointerArray> vtable_; - // Short cuts to dex_cache_ member for fast compiled code access. - uint64_t dex_cache_strings_; - // instance fields // // These describe the layout of the contents of an Object. diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index b54e416bc0..a59bb7b880 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -72,7 +72,7 @@ inline void DexCache::ClearString(dex::StringIndex string_idx) { inline Class* DexCache::GetResolvedType(dex::TypeIndex type_idx) { // It is theorized that a load acquire is not required since obtaining the resolved class will - // always have an address depedency or a lock. + // always have an address dependency or a lock. DCHECK_LT(type_idx.index_, NumResolvedTypes()); return GetResolvedTypes()[type_idx.index_].Read(); } diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h index 0fdf1323eb..3e04bf6474 100644 --- a/runtime/mirror/object_array-inl.h +++ b/runtime/mirror/object_array-inl.h @@ -17,12 +17,13 @@ #ifndef ART_RUNTIME_MIRROR_OBJECT_ARRAY_INL_H_ #define ART_RUNTIME_MIRROR_OBJECT_ARRAY_INL_H_ +#include "object_array.h" + #include <string> -#include "object_array.h" +#include "android-base/stringprintf.h" #include "array-inl.h" -#include "base/stringprintf.h" #include "gc/heap.h" #include "mirror/class.h" #include "obj_ptr-inl.h" @@ -330,13 +331,15 @@ inline void ObjectArray<T>::AssignableCheckingMemcpy(int32_t dst_pos, std::string actualSrcType(mirror::Object::PrettyTypeOf(o)); std::string dstType(PrettyTypeOf()); Thread* self = Thread::Current(); + std::string msg = android::base::StringPrintf( + "source[%d] of type %s cannot be stored in destination array of type %s", + src_pos + i, + actualSrcType.c_str(), + dstType.c_str()); if (throw_exception) { - self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;", - "source[%d] of type %s cannot be stored in destination array of type %s", - src_pos + i, actualSrcType.c_str(), dstType.c_str()); + self->ThrowNewException("Ljava/lang/ArrayStoreException;", msg.c_str()); } else { - LOG(FATAL) << StringPrintf("source[%d] of type %s cannot be stored in destination array of type %s", - src_pos + i, actualSrcType.c_str(), dstType.c_str()); + LOG(FATAL) << msg; } } } diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index 6870fda075..9b8445dc9e 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -16,6 +16,10 @@ #ifndef ART_RUNTIME_MIRROR_STRING_INL_H_ #define ART_RUNTIME_MIRROR_STRING_INL_H_ +#include "string.h" + +#include "android-base/stringprintf.h" + #include "array.h" #include "base/bit_utils.h" #include "class.h" @@ -24,7 +28,6 @@ #include "globals.h" #include "intern_table.h" #include "runtime.h" -#include "string.h" #include "thread.h" #include "utf.h" #include "utils.h" @@ -228,9 +231,10 @@ inline String* String::Alloc(Thread* self, int32_t utf16_length_with_flag, "kObjectAlignment must be at least as big as Java char alignment"); const size_t max_length = RoundDown(max_alloc_length, kObjectAlignment / block_size); if (UNLIKELY(length > max_length)) { - self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow", - Class::PrettyDescriptor(string_class).c_str(), - static_cast<int>(length)).c_str()); + self->ThrowOutOfMemoryError( + android::base::StringPrintf("%s of length %d would overflow", + Class::PrettyDescriptor(string_class).c_str(), + static_cast<int>(length)).c_str()); return nullptr; } @@ -305,8 +309,11 @@ inline int32_t String::GetHashCode() { template<typename MemoryType> bool String::AllASCII(const MemoryType* const chars, const int length) { + static_assert(std::is_unsigned<MemoryType>::value, "Expecting unsigned MemoryType"); for (int i = 0; i < length; ++i) { - if (chars[i] >= 0x80) { + // Valid ASCII characters are in range 1..0x7f. Zero is not considered ASCII + // because it would complicate the detection of ASCII strings in Modified-UTF8. + if ((chars[i] - 1u) >= 0x7fu) { return false; } } diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc index ade4e87afd..e50409f2c5 100644 --- a/runtime/mirror/throwable.cc +++ b/runtime/mirror/throwable.cc @@ -16,6 +16,8 @@ #include "throwable.h" +#include "android-base/stringprintf.h" + #include "art_method-inl.h" #include "base/enums.h" #include "class-inl.h" @@ -31,6 +33,8 @@ namespace art { namespace mirror { +using android::base::StringPrintf; + GcRoot<Class> Throwable::java_lang_Throwable_; void Throwable::SetDetailMessage(ObjPtr<String> new_detail_message) { diff --git a/runtime/monitor.cc b/runtime/monitor.cc index e7de7e64c6..222eb5c556 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -18,6 +18,8 @@ #include <vector> +#include "android-base/stringprintf.h" + #include "art_method-inl.h" #include "base/mutex.h" #include "base/stl_util.h" @@ -38,6 +40,8 @@ namespace art { +using android::base::StringPrintf; + static constexpr uint64_t kLongWaitMs = 100; /* diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 1a77072921..cd0e55f261 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -18,9 +18,10 @@ #include <sstream> +#include "android-base/stringprintf.h" + #include "base/logging.h" #include "base/stl_util.h" -#include "base/stringprintf.h" #include "class_linker.h" #include "common_throws.h" #include "compiler_filter.h" @@ -43,6 +44,8 @@ namespace art { +using android::base::StringPrintf; + static bool ConvertJavaArrayToDexFiles( JNIEnv* env, jobject arrayObject, diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc index db245aa0d8..981be68199 100644 --- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc +++ b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc @@ -16,6 +16,8 @@ #include "dalvik_system_InMemoryDexClassLoader_DexData.h" +#include "android-base/stringprintf.h" + #include "class_linker.h" #include "common_throws.h" #include "dex_file.h" @@ -29,6 +31,8 @@ namespace art { +using android::base::StringPrintf; + static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) { if (end <= start) { ScopedObjectAccess soa(env); diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 3058df442c..1af861929e 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -27,6 +27,8 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); #include "toStringArray.h" #pragma GCC diagnostic pop +#include "android-base/stringprintf.h" + #include "art_method-inl.h" #include "arch/instruction_set.h" #include "base/enums.h" @@ -54,6 +56,8 @@ extern "C" void android_set_application_target_sdk_version(uint32_t version); namespace art { +using android::base::StringPrintf; + static jfloat VMRuntime_getTargetHeapUtilization(JNIEnv*, jobject) { return Runtime::Current()->GetHeap()->GetTargetHeapUtilization(); } diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index a78909b879..10fc90bc27 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -18,6 +18,8 @@ #include <stdlib.h> +#include "android-base/stringprintf.h" + #include "arch/instruction_set.h" #include "debugger.h" #include "java_vm_ext.h" @@ -37,6 +39,8 @@ namespace art { +using android::base::StringPrintf; + static void EnableDebugger() { #if defined(__linux__) // To let a non-privileged gdbserver attach to this diff --git a/runtime/native/java_lang_ref_Reference.cc b/runtime/native/java_lang_ref_Reference.cc index bedca109aa..c778068bc4 100644 --- a/runtime/native/java_lang_ref_Reference.cc +++ b/runtime/native/java_lang_ref_Reference.cc @@ -33,8 +33,15 @@ static jobject Reference_getReferent(JNIEnv* env, jobject javaThis) { return soa.AddLocalReference<jobject>(referent); } +static void Reference_clearReferent(JNIEnv* env, jobject javaThis) { + ScopedFastNativeObjectAccess soa(env); + ObjPtr<mirror::Reference> ref = soa.Decode<mirror::Reference>(javaThis); + Runtime::Current()->GetHeap()->GetReferenceProcessor()->ClearReferent(ref); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(Reference, getReferent, "!()Ljava/lang/Object;"), + NATIVE_METHOD(Reference, clearReferent, "!()V"), }; void register_java_lang_ref_Reference(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc index 73b81a71f8..ee59c4a9e2 100644 --- a/runtime/native/java_lang_reflect_Executable.cc +++ b/runtime/native/java_lang_reflect_Executable.cc @@ -16,6 +16,8 @@ #include "java_lang_reflect_Executable.h" +#include "android-base/stringprintf.h" + #include "art_method-inl.h" #include "dex_file_annotations.h" #include "handle.h" @@ -30,6 +32,8 @@ namespace art { +using android::base::StringPrintf; + static jobjectArray Executable_getDeclaredAnnotationsNative(JNIEnv* env, jobject javaMethod) { ScopedFastNativeObjectAccess soa(env); ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod); diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 6206948225..374eeb5806 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -16,6 +16,8 @@ #include "java_lang_reflect_Field.h" +#include "android-base/stringprintf.h" + #include "class_linker.h" #include "class_linker-inl.h" #include "common_throws.h" @@ -30,6 +32,8 @@ namespace art { +using android::base::StringPrintf; + template<bool kIsSet> ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self, ObjPtr<mirror::Field> field, diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc index 16164d2f9b..0bb9e382d3 100644 --- a/runtime/native/java_lang_reflect_Parameter.cc +++ b/runtime/native/java_lang_reflect_Parameter.cc @@ -16,6 +16,8 @@ #include "java_lang_reflect_Parameter.h" +#include "android-base/stringprintf.h" + #include "art_method-inl.h" #include "common_throws.h" #include "dex_file-inl.h" @@ -26,6 +28,8 @@ namespace art { +using android::base::StringPrintf; + static jobject Parameter_getAnnotationNative(JNIEnv* env, jclass, jobject javaMethod, diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index 5565565c7b..7460d622b5 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -37,10 +37,11 @@ #include <sys/time.h> #include <sys/types.h> +#include "android-base/stringprintf.h" + #include "arch/instruction_set.h" #include "base/memory_tool.h" #include "base/mutex.h" -#include "base/stringprintf.h" #include "base/unix_file/fd_file.h" #include "oat_quick_method_header.h" #include "os.h" @@ -53,6 +54,8 @@ namespace art { #if defined(__linux__) +using android::base::StringPrintf; + static constexpr bool kUseAddr2line = !kIsTargetBuild; ALWAYS_INLINE diff --git a/runtime/oat.cc b/runtime/oat.cc index aab0e81047..cebe765369 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -19,12 +19,15 @@ #include <string.h> #include <zlib.h> +#include "android-base/stringprintf.h" + #include "arch/instruction_set_features.h" #include "base/bit_utils.h" -#include "base/stringprintf.h" namespace art { +using android::base::StringPrintf; + constexpr uint8_t OatHeader::kOatMagic[4]; constexpr uint8_t OatHeader::kOatVersion[4]; constexpr const char OatHeader::kTrueValue[]; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index bdf8b0e65d..0bf713679b 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -32,6 +32,8 @@ #include "android/dlext.h" #endif +#include "android-base/stringprintf.h" + #include "art_method-inl.h" #include "base/bit_vector.h" #include "base/enums.h" @@ -58,6 +60,8 @@ namespace art { +using android::base::StringPrintf; + // Whether OatFile::Open will try dlopen. Fallback is our own ELF loader. static constexpr bool kUseDlopen = true; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 6a62a166a3..ee7cf9deef 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -19,8 +19,10 @@ #include <sstream> #include <sys/stat.h> + +#include "android-base/strings.h" + #include "base/logging.h" -#include "base/stringprintf.h" #include "compiler_filter.h" #include "class_linker.h" #include "gc/heap.h" @@ -456,7 +458,7 @@ OatFileAssistant::RelocateOatFile(const std::string* input_file, std::string* er argv.push_back("--output-oat-file=" + oat_file_name); argv.push_back("--patched-image-location=" + image_info->location); - std::string command_line(Join(argv, ' ')); + std::string command_line(android::base::Join(argv, ' ')); if (!Exec(argv, error_msg)) { // Manually delete the file. This ensures there is no garbage left over if // the process unexpectedly died. @@ -605,7 +607,7 @@ bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args, argv.insert(argv.end(), args.begin(), args.end()); - std::string command_line(Join(argv, ' ')); + std::string command_line(android::base::Join(argv, ' ')); return Exec(argv, error_msg); } diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 94c12af199..26dbaab367 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -20,6 +20,7 @@ #include <vector> #include <sys/param.h> +#include "android-base/strings.h" #include <backtrace/BacktraceMap.h> #include <gtest/gtest.h> @@ -1057,7 +1058,7 @@ static std::string MakePathRelative(const std::string& target) { // Reverse again to get the right path order, and join to get the result. std::reverse(target_path.begin(), target_path.end()); - return Join(target_path, '/'); + return android::base::Join(target_path, '/'); } // Case: Non-absolute path to Dex location. @@ -1134,7 +1135,7 @@ class RaceGenerateTask : public Task { /*dex_elements*/nullptr, &oat_file, &error_msgs); - CHECK(!dex_files.empty()) << Join(error_msgs, '\n'); + CHECK(!dex_files.empty()) << android::base::Join(error_msgs, '\n'); CHECK(dex_files[0]->GetOatDexFile() != nullptr) << dex_files[0]->GetLocation(); loaded_oat_file_ = dex_files[0]->GetOatDexFile()->GetOatFile(); CHECK_EQ(loaded_oat_file_, oat_file); diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 5641459f41..33bd0f311d 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -20,6 +20,8 @@ #include <queue> #include <vector> +#include "android-base/stringprintf.h" + #include "base/logging.h" #include "base/stl_util.h" #include "base/systrace.h" @@ -38,6 +40,8 @@ namespace art { +using android::base::StringPrintf; + // If true, then we attempt to load the application image if it exists. static constexpr bool kEnableAppImage = true; diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 1ad3f0814f..5f97b60079 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -700,7 +700,7 @@ class JvmtiFunctions { jmethodID method, jint* entry_count_ptr, jvmtiLineNumberEntry** table_ptr) { - return ERR(NOT_IMPLEMENTED); + return MethodUtil::GetLineNumberTable(env, method, entry_count_ptr, table_ptr); } static jvmtiError GetMethodLocation(jvmtiEnv* env, diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc index ffa5ac7e32..a0a09239fa 100644 --- a/runtime/openjdkjvmti/ti_method.cc +++ b/runtime/openjdkjvmti/ti_method.cc @@ -130,4 +130,63 @@ jvmtiError MethodUtil::GetMethodModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(NONE); } +using LineNumberContext = std::vector<jvmtiLineNumberEntry>; + +static bool CollectLineNumbers(void* void_context, const art::DexFile::PositionInfo& entry) { + LineNumberContext* context = reinterpret_cast<LineNumberContext*>(void_context); + jvmtiLineNumberEntry jvmti_entry = { static_cast<jlocation>(entry.address_), + static_cast<jint>(entry.line_) }; + context->push_back(jvmti_entry); + return false; // Collect all, no early exit. +} + +jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLineNumberEntry** table_ptr) { + if (method == nullptr) { + return ERR(NULL_POINTER); + } + art::ArtMethod* art_method = art::jni::DecodeArtMethod(method); + DCHECK(!art_method->IsRuntimeMethod()); + + const art::DexFile::CodeItem* code_item; + const art::DexFile* dex_file; + { + art::ScopedObjectAccess soa(art::Thread::Current()); + + if (art_method->IsProxyMethod()) { + return ERR(ABSENT_INFORMATION); + } + if (art_method->IsNative()) { + return ERR(NATIVE_METHOD); + } + if (entry_count_ptr == nullptr || table_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + code_item = art_method->GetCodeItem(); + dex_file = art_method->GetDexFile(); + DCHECK(code_item != nullptr) << art_method->PrettyMethod() << " " << dex_file->GetLocation(); + } + + LineNumberContext context; + bool success = dex_file->DecodeDebugPositionInfo(code_item, CollectLineNumbers, &context); + if (!success) { + return ERR(ABSENT_INFORMATION); + } + + unsigned char* data; + jlong mem_size = context.size() * sizeof(jvmtiLineNumberEntry); + jvmtiError alloc_error = env->Allocate(mem_size, &data); + if (alloc_error != ERR(NONE)) { + return alloc_error; + } + *table_ptr = reinterpret_cast<jvmtiLineNumberEntry*>(data); + memcpy(*table_ptr, context.data(), mem_size); + *entry_count_ptr = static_cast<jint>(context.size()); + + return ERR(NONE); +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h index 43f11f97ec..fb2fbb2b27 100644 --- a/runtime/openjdkjvmti/ti_method.h +++ b/runtime/openjdkjvmti/ti_method.h @@ -52,6 +52,11 @@ class MethodUtil { static jvmtiError GetMethodModifiers(jvmtiEnv* env, jmethodID method, jint* modifiers_ptr); + + static jvmtiError GetLineNumberTable(jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLineNumberEntry** table_ptr); }; } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index d0349b987f..68815e7de0 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -33,6 +33,8 @@ #include <limits> +#include "android-base/stringprintf.h" + #include "art_jvmti.h" #include "base/logging.h" #include "events-inl.h" @@ -49,13 +51,15 @@ namespace openjdkjvmti { +using android::base::StringPrintf; + // Moves dex data to an anonymous, read-only mmap'd region. std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& original_location, jint data_len, unsigned char* dex_data, std::string* error_msg) { std::unique_ptr<art::MemMap> map(art::MemMap::MapAnonymous( - art::StringPrintf("%s-transformed", original_location.c_str()).c_str(), + StringPrintf("%s-transformed", original_location.c_str()).c_str(), nullptr, data_len, PROT_READ|PROT_WRITE, @@ -246,9 +250,9 @@ art::mirror::LongArray* Redefiner::AllocateDexFileCookie( } void Redefiner::RecordFailure(jvmtiError result, const std::string& error_msg) { - *error_msg_ = art::StringPrintf("Unable to perform redefinition of '%s': %s", - class_sig_, - error_msg.c_str()); + *error_msg_ = StringPrintf("Unable to perform redefinition of '%s': %s", + class_sig_, + error_msg.c_str()); result_ = result; } @@ -392,19 +396,14 @@ void Redefiner::RestoreJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_fil } } -// Performs updates to class that will allow us to verify it. -bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass, - art::ObjPtr<art::mirror::DexCache> new_dex_cache) { +bool Redefiner::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass, + art::ObjPtr<art::mirror::DexCache> new_dex_cache, + const art::DexFile::ClassDef& class_def) { art::ClassLinker* linker = runtime_->GetClassLinker(); art::PointerSize image_pointer_size = linker->GetImagePointerSize(); - const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef( - *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_)); - if (class_def == nullptr) { - RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!"); - return false; - } - const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def->class_idx_); + const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def.class_idx_); const art::DexFile& old_dex_file = mclass->GetDexFile(); + // Update methods. for (art::ArtMethod& method : mclass->GetMethods(image_pointer_size)) { const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(method.GetName()); art::dex::TypeIndex method_return_idx = @@ -431,15 +430,58 @@ bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass, uint32_t dex_method_idx = dex_file_->GetIndexForMethodId(*method_id); method.SetDexMethodIndex(dex_method_idx); linker->SetEntryPointsToInterpreter(&method); - method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(*class_def, dex_method_idx)); + method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx)); method.SetDexCacheResolvedMethods(new_dex_cache->GetResolvedMethods(), image_pointer_size); method.SetDexCacheResolvedTypes(new_dex_cache->GetResolvedTypes(), image_pointer_size); } + return true; +} + +bool Redefiner::UpdateFields(art::ObjPtr<art::mirror::Class> mclass) { + // TODO The IFields & SFields pointers should be combined like the methods_ arrays were. + for (auto fields_iter : {mclass->GetIFields(), mclass->GetSFields()}) { + for (art::ArtField& field : fields_iter) { + std::string declaring_class_name; + const art::DexFile::TypeId* new_declaring_id = + dex_file_->FindTypeId(field.GetDeclaringClass()->GetDescriptor(&declaring_class_name)); + const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(field.GetName()); + const art::DexFile::TypeId* new_type_id = dex_file_->FindTypeId(field.GetTypeDescriptor()); + // TODO Handle error, cleanup. + CHECK(new_name_id != nullptr && new_type_id != nullptr && new_declaring_id != nullptr); + const art::DexFile::FieldId* new_field_id = + dex_file_->FindFieldId(*new_declaring_id, *new_name_id, *new_type_id); + CHECK(new_field_id != nullptr); + // We only need to update the index since the other data in the ArtField cannot be updated. + field.SetDexFieldIndex(dex_file_->GetIndexForFieldId(*new_field_id)); + } + } + return true; +} + +// Performs updates to class that will allow us to verify it. +bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass, + art::ObjPtr<art::mirror::DexCache> new_dex_cache) { + const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef( + *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_)); + if (class_def == nullptr) { + RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!"); + return false; + } + if (!UpdateMethods(mclass, new_dex_cache, *class_def)) { + // TODO Investigate appropriate error types. + RecordFailure(ERR(INTERNAL), "Unable to update class methods."); + return false; + } + if (!UpdateFields(mclass)) { + // TODO Investigate appropriate error types. + RecordFailure(ERR(INTERNAL), "Unable to update class fields."); + return false; + } + // Update the class fields. // Need to update class last since the ArtMethod gets its DexFile from the class (which is needed // to call GetReturnTypeDescriptor and GetParameterTypeList above). mclass->SetDexCache(new_dex_cache.Ptr()); - mclass->SetDexCacheStrings(new_dex_cache->GetStrings()); mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(*class_def)); mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_))); return true; diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index c819acd5ac..73cfc2b69b 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -159,6 +159,14 @@ class Redefiner { art::ObjPtr<art::mirror::LongArray> original_cookie) REQUIRES(art::Locks::mutator_lock_); + bool UpdateFields(art::ObjPtr<art::mirror::Class> mclass) + REQUIRES(art::Locks::mutator_lock_); + + bool UpdateMethods(art::ObjPtr<art::mirror::Class> mclass, + art::ObjPtr<art::mirror::DexCache> new_dex_cache, + const art::DexFile::ClassDef& class_def) + REQUIRES(art::Locks::mutator_lock_); + bool UpdateClass(art::ObjPtr<art::mirror::Class> mclass, art::ObjPtr<art::mirror::DexCache> new_dex_cache) REQUIRES(art::Locks::mutator_lock_); diff --git a/runtime/openjdkjvmti/ti_stack.cc b/runtime/openjdkjvmti/ti_stack.cc index 6f8976f03d..579fb50ecc 100644 --- a/runtime/openjdkjvmti/ti_stack.cc +++ b/runtime/openjdkjvmti/ti_stack.cc @@ -67,14 +67,10 @@ struct GetStackTraceVisitor : public art::StackVisitor { m = m->GetInterfaceMethodIfProxy(art::kRuntimePointerSize); jmethodID id = art::jni::EncodeArtMethod(m); - art::mirror::DexCache* dex_cache = m->GetDexCache(); - int32_t line_number = -1; - if (dex_cache != nullptr) { // be tolerant of bad input - const art::DexFile* dex_file = dex_cache->GetDexFile(); - line_number = art::annotations::GetLineNumFromPC(dex_file, m, GetDexPc(false)); - } + uint32_t dex_pc = GetDexPc(false); + jlong dex_location = (dex_pc == art::DexFile::kDexNoIndex) ? -1 : static_cast<jlong>(dex_pc); - jvmtiFrameInfo info = { id, static_cast<jlong>(line_number) }; + jvmtiFrameInfo info = { id, dex_location }; frames.push_back(info); if (stop == 1) { diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc index 5b90c6adad..8948c710db 100644 --- a/runtime/parsed_options_test.cc +++ b/runtime/parsed_options_test.cc @@ -19,7 +19,6 @@ #include <memory> #include "arch/instruction_set.h" -#include "base/stringprintf.h" #include "common_runtime_test.h" namespace art { diff --git a/runtime/plugin.cc b/runtime/plugin.cc index 481b1caa15..731967c738 100644 --- a/runtime/plugin.cc +++ b/runtime/plugin.cc @@ -17,11 +17,15 @@ #include "plugin.h" #include <dlfcn.h> -#include "base/stringprintf.h" + +#include "android-base/stringprintf.h" + #include "base/logging.h" namespace art { +using android::base::StringPrintf; + const char* PLUGIN_INITIALIZATION_FUNCTION_NAME = "ArtPlugin_Initialize"; const char* PLUGIN_DEINITIALIZATION_FUNCTION_NAME = "ArtPlugin_Deinitialize"; diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc index 1c975a453a..d8b9dcc016 100644 --- a/runtime/reference_table.cc +++ b/runtime/reference_table.cc @@ -16,6 +16,8 @@ #include "reference_table.h" +#include "android-base/stringprintf.h" + #include "base/mutex.h" #include "indirect_reference_table.h" #include "mirror/array.h" @@ -30,6 +32,9 @@ namespace art { +using android::base::StringAppendF; +using android::base::StringPrintf; + ReferenceTable::ReferenceTable(const char* name, size_t initial_size, size_t max_size) : name_(name), max_size_(max_size) { CHECK_LE(initial_size, max_size); diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc index d80a9b3dd1..9523e92d7c 100644 --- a/runtime/reference_table_test.cc +++ b/runtime/reference_table_test.cc @@ -16,6 +16,8 @@ #include "reference_table.h" +#include "android-base/stringprintf.h" + #include "class_linker.h" #include "common_runtime_test.h" #include "handle_scope-inl.h" @@ -30,6 +32,8 @@ namespace art { +using android::base::StringPrintf; + class ReferenceTableTest : public CommonRuntimeTest {}; static mirror::Object* CreateWeakReference(mirror::Object* referent) diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h index 68e7a10e01..62ce9e9509 100644 --- a/runtime/reflection-inl.h +++ b/runtime/reflection-inl.h @@ -19,7 +19,8 @@ #include "reflection.h" -#include "base/stringprintf.h" +#include "android-base/stringprintf.h" + #include "common_throws.h" #include "jvalue-inl.h" #include "mirror/object-inl.h" @@ -103,13 +104,14 @@ inline bool ConvertPrimitiveValue(bool unbox_for_result, } if (!unbox_for_result) { - ThrowIllegalArgumentException(StringPrintf("Invalid primitive conversion from %s to %s", - PrettyDescriptor(srcType).c_str(), - PrettyDescriptor(dstType).c_str()).c_str()); + ThrowIllegalArgumentException( + android::base::StringPrintf("Invalid primitive conversion from %s to %s", + PrettyDescriptor(srcType).c_str(), + PrettyDescriptor(dstType).c_str()).c_str()); } else { - ThrowClassCastException(StringPrintf("Couldn't convert result of type %s to %s", - PrettyDescriptor(srcType).c_str(), - PrettyDescriptor(dstType).c_str()).c_str()); + ThrowClassCastException(android::base::StringPrintf("Couldn't convert result of type %s to %s", + PrettyDescriptor(srcType).c_str(), + PrettyDescriptor(dstType).c_str()).c_str()); } return false; } diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 8446b525ad..4d2450135e 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -34,6 +34,8 @@ namespace art { +using android::base::StringPrintf; + class ArgArray { public: ArgArray(const char* shorty, uint32_t shorty_len) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 14628f0403..2086d70791 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -37,6 +37,8 @@ #include <vector> #include <fcntl.h> +#include "android-base/strings.h" + #include "JniConstants.h" #include "ScopedLocalRef.h" #include "arch/arm/quick_method_frame_info_arm.h" @@ -869,7 +871,7 @@ static bool OpenDexFilesFromImage(const std::string& image_location, ImageHeader::GetOatLocationFromImageLocation(image_locations[index].c_str()); // Note: in the multi-image case, the image location may end in ".jar," and not ".art." Handle // that here. - if (EndsWith(oat_location, ".jar")) { + if (android::base::EndsWith(oat_location, ".jar")) { oat_location.replace(oat_location.length() - 3, 3, "oat"); } std::string error_msg; @@ -1197,6 +1199,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { CHECK_EQ(self->GetThreadId(), ThreadList::kMainThreadId); CHECK(self != nullptr); + self->SetCanCallIntoJava(!IsAotCompiler()); + // Set us to runnable so tools using a runtime can allocate and GC by default self->TransitionFromSuspendedToRunnable(); @@ -1225,7 +1229,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { for (const DexFile* dex_file : boot_class_path) { dex_locations.push_back(dex_file->GetLocation()); } - boot_class_path_string_ = Join(dex_locations, ':'); + boot_class_path_string_ = android::base::Join(dex_locations, ':'); } { ScopedTrace trace2("AddImageStringsToTable"); @@ -1719,7 +1723,7 @@ void Runtime::VisitImageRoots(RootVisitor* visitor) { if (space->IsImageSpace()) { auto* image_space = space->AsImageSpace(); const auto& image_header = image_space->GetImageHeader(); - for (size_t i = 0; i < ImageHeader::kImageRootsMax; ++i) { + for (int32_t i = 0, size = image_header.GetImageRoots()->GetLength(); i != size; ++i) { auto* obj = image_header.GetImageRoot(static_cast<ImageHeader::ImageRoot>(i)); if (obj != nullptr) { auto* after_obj = obj; @@ -1892,7 +1896,7 @@ void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths, } VLOG(profiler) << "Register app with " << profile_output_filename - << " " << Join(code_paths, ':'); + << " " << android::base::Join(code_paths, ':'); if (profile_output_filename.empty()) { LOG(WARNING) << "JIT profile information will not be recorded: profile filename is empty."; diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc index be9786024a..0a996a9e55 100644 --- a/runtime/runtime_android.cc +++ b/runtime/runtime_android.cc @@ -21,7 +21,6 @@ #include "base/logging.h" #include "base/mutex.h" -#include "base/stringprintf.h" #include "thread-inl.h" #include "utils.h" diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc index 93704a971c..b8894d2569 100644 --- a/runtime/runtime_linux.cc +++ b/runtime/runtime_linux.cc @@ -24,11 +24,12 @@ #include <iostream> #include <sstream> +#include "android-base/stringprintf.h" + #include "base/dumpable.h" #include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" -#include "base/stringprintf.h" #include "native_stack_dump.h" #include "thread-inl.h" #include "thread_list.h" @@ -36,6 +37,8 @@ namespace art { +using android::base::StringPrintf; + static constexpr bool kUseSigRTTimeout = true; static constexpr bool kDumpNativeStackOnTimeout = true; diff --git a/runtime/stack.cc b/runtime/stack.cc index 792da88a63..3fed7c9458 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -16,6 +16,8 @@ #include "stack.h" +#include "android-base/stringprintf.h" + #include "arch/context.h" #include "art_method-inl.h" #include "base/enums.h" @@ -39,6 +41,8 @@ namespace art { +using android::base::StringPrintf; + static constexpr bool kDebugStackWalk = false; mirror::Object* ShadowFrame::GetThisObject() const { diff --git a/runtime/thread.cc b/runtime/thread.cc index bc133d1370..aff12ff4c1 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -28,6 +28,8 @@ #include <list> #include <sstream> +#include "android-base/stringprintf.h" + #include "arch/context.h" #include "art_field-inl.h" #include "art_method-inl.h" @@ -88,6 +90,9 @@ namespace art { +using android::base::StringAppendV; +using android::base::StringPrintf; + extern "C" NO_RETURN void artDeoptimize(Thread* self); bool Thread::is_started_ = false; @@ -873,6 +878,62 @@ void Thread::SetThreadName(const char* name) { Dbg::DdmSendThreadNotification(this, CHUNK_TYPE("THNM")); } +static void GetThreadStack(pthread_t thread, + void** stack_base, + size_t* stack_size, + size_t* guard_size) { +#if defined(__APPLE__) + *stack_size = pthread_get_stacksize_np(thread); + void* stack_addr = pthread_get_stackaddr_np(thread); + + // Check whether stack_addr is the base or end of the stack. + // (On Mac OS 10.7, it's the end.) + int stack_variable; + if (stack_addr > &stack_variable) { + *stack_base = reinterpret_cast<uint8_t*>(stack_addr) - *stack_size; + } else { + *stack_base = stack_addr; + } + + // This is wrong, but there doesn't seem to be a way to get the actual value on the Mac. + pthread_attr_t attributes; + CHECK_PTHREAD_CALL(pthread_attr_init, (&attributes), __FUNCTION__); + CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, guard_size), __FUNCTION__); + CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), __FUNCTION__); +#else + pthread_attr_t attributes; + CHECK_PTHREAD_CALL(pthread_getattr_np, (thread, &attributes), __FUNCTION__); + CHECK_PTHREAD_CALL(pthread_attr_getstack, (&attributes, stack_base, stack_size), __FUNCTION__); + CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, guard_size), __FUNCTION__); + CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), __FUNCTION__); + +#if defined(__GLIBC__) + // If we're the main thread, check whether we were run with an unlimited stack. In that case, + // glibc will have reported a 2GB stack for our 32-bit process, and our stack overflow detection + // will be broken because we'll die long before we get close to 2GB. + bool is_main_thread = (::art::GetTid() == getpid()); + if (is_main_thread) { + rlimit stack_limit; + if (getrlimit(RLIMIT_STACK, &stack_limit) == -1) { + PLOG(FATAL) << "getrlimit(RLIMIT_STACK) failed"; + } + if (stack_limit.rlim_cur == RLIM_INFINITY) { + size_t old_stack_size = *stack_size; + + // Use the kernel default limit as our size, and adjust the base to match. + *stack_size = 8 * MB; + *stack_base = reinterpret_cast<uint8_t*>(*stack_base) + (old_stack_size - *stack_size); + + VLOG(threads) << "Limiting unlimited stack (reported as " << PrettySize(old_stack_size) << ")" + << " to " << PrettySize(*stack_size) + << " with base " << *stack_base; + } + } +#endif + +#endif +} + bool Thread::InitStackHwm() { void* read_stack_base; size_t read_stack_size; @@ -1322,6 +1383,32 @@ void Thread::FullSuspendCheck() { VLOG(threads) << this << " self-reviving"; } +static std::string GetSchedulerGroupName(pid_t tid) { + // /proc/<pid>/cgroup looks like this: + // 2:devices:/ + // 1:cpuacct,cpu:/ + // We want the third field from the line whose second field contains the "cpu" token. + std::string cgroup_file; + if (!ReadFileToString(StringPrintf("/proc/self/task/%d/cgroup", tid), &cgroup_file)) { + return ""; + } + std::vector<std::string> cgroup_lines; + Split(cgroup_file, '\n', &cgroup_lines); + for (size_t i = 0; i < cgroup_lines.size(); ++i) { + std::vector<std::string> cgroup_fields; + Split(cgroup_lines[i], ':', &cgroup_fields); + std::vector<std::string> cgroups; + Split(cgroup_fields[1], ',', &cgroups); + for (size_t j = 0; j < cgroups.size(); ++j) { + if (cgroups[j] == "cpu") { + return cgroup_fields[2].substr(1); // Skip the leading slash. + } + } + } + return ""; +} + + void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { std::string group_name; int priority; @@ -1720,7 +1807,11 @@ void Thread::Shutdown() { } } -Thread::Thread(bool daemon) : tls32_(daemon), wait_monitor_(nullptr), interrupted_(false) { +Thread::Thread(bool daemon) + : tls32_(daemon), + wait_monitor_(nullptr), + interrupted_(false), + can_call_into_java_(true) { wait_mutex_ = new Mutex("a thread wait mutex"); wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_); tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>; diff --git a/runtime/thread.h b/runtime/thread.h index 31cd0ebf9e..411d85f015 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -881,6 +881,15 @@ class Thread { --tls32_.disable_thread_flip_count; } + // Returns true if the thread is allowed to call into java. + bool CanCallIntoJava() const { + return can_call_into_java_; + } + + void SetCanCallIntoJava(bool can_call_into_java) { + can_call_into_java_ = can_call_into_java; + } + // Activates single step control for debugging. The thread takes the // ownership of the given SingleStepControl*. It is deleted by a call // to DeactivateSingleStepControl or upon thread destruction. @@ -1588,6 +1597,10 @@ class Thread { // Pending extra checkpoints if checkpoint_function_ is already used. std::list<Closure*> checkpoint_overflow_ GUARDED_BY(Locks::thread_suspend_count_lock_); + // True if the thread is allowed to call back into java (for e.g. during class resolution). + // By default this is true. + bool can_call_into_java_; + friend class Dbg; // For SetStateUnsafe. friend class gc::collector::SemiSpace; // For getting stack traces. friend class Runtime; // For CreatePeer. diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 664eeb4b94..bf9eef8370 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -25,6 +25,8 @@ #include <sstream> +#include "android-base/stringprintf.h" + #include "base/histogram-inl.h" #include "base/mutex-inl.h" #include "base/systrace.h" @@ -52,6 +54,8 @@ namespace art { +using android::base::StringPrintf; + static constexpr uint64_t kLongThreadSuspendThreshold = MsToNs(5); static constexpr uint64_t kThreadSuspendTimeoutMs = 30 * 1000; // 30s. // Use 0 since we want to yield to prevent blocking for an unpredictable amount of time. diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index d9d2ea31a2..d9179c3892 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -21,6 +21,8 @@ #include <sys/time.h> #include <sys/resource.h> +#include "android-base/stringprintf.h" + #include "base/bit_utils.h" #include "base/casts.h" #include "base/logging.h" @@ -31,6 +33,8 @@ namespace art { +using android::base::StringPrintf; + static constexpr bool kMeasureWaitTime = false; ThreadPoolWorker::ThreadPoolWorker(ThreadPool* thread_pool, const std::string& name, @@ -86,6 +90,8 @@ void* ThreadPoolWorker::Callback(void* arg) { Runtime* runtime = Runtime::Current(); CHECK(runtime->AttachCurrentThread(worker->name_.c_str(), true, nullptr, false)); worker->thread_ = Thread::Current(); + // Thread pool workers cannot call into java. + worker->thread_->SetCanCallIntoJava(false); // Do work until its time to shut down. worker->Run(); runtime->DetachCurrentThread(); diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h index eaadfe0215..7ecfcd1289 100644 --- a/runtime/thread_pool.h +++ b/runtime/thread_pool.h @@ -80,6 +80,7 @@ class ThreadPoolWorker { DISALLOW_COPY_AND_ASSIGN(ThreadPoolWorker); }; +// Note that thread pool workers will set Thread#setCanCallIntoJava to false. class ThreadPool { public: // Returns the number of threads in the thread pool. diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc index d21ff77849..0bba44c988 100644 --- a/runtime/ti/agent.cc +++ b/runtime/ti/agent.cc @@ -15,12 +15,17 @@ */ #include "agent.h" + +#include "android-base/stringprintf.h" + #include "java_vm_ext.h" #include "runtime.h" namespace art { namespace ti { +using android::base::StringPrintf; + const char* AGENT_ON_LOAD_FUNCTION_NAME = "Agent_OnLoad"; const char* AGENT_ON_ATTACH_FUNCTION_NAME = "Agent_OnAttach"; const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload"; diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h index 6561756edd..7408aeec35 100644 --- a/runtime/ti/agent.h +++ b/runtime/ti/agent.h @@ -20,7 +20,6 @@ #include <dlfcn.h> #include <jni.h> // for jint, JavaVM* etc declarations -#include "base/stringprintf.h" #include "runtime.h" #include "utils.h" diff --git a/runtime/trace.cc b/runtime/trace.cc index f564de4952..9d9360e9cb 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -19,6 +19,8 @@ #include <sys/uio.h> #include <unistd.h> +#include "android-base/stringprintf.h" + #include "art_method-inl.h" #include "base/casts.h" #include "base/enums.h" @@ -46,6 +48,8 @@ namespace art { +using android::base::StringPrintf; + static constexpr size_t TraceActionBits = MinimumBitsToStore( static_cast<size_t>(kTraceMethodActionMask)); static constexpr uint8_t kOpNewMethod = 1U; diff --git a/runtime/utils.cc b/runtime/utils.cc index 66739a9d2e..8867743ec1 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -25,6 +25,9 @@ #include <unistd.h> #include <memory> +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "dex_file-inl.h" @@ -46,6 +49,9 @@ namespace art { +using android::base::StringAppendF; +using android::base::StringPrintf; + static const uint8_t kBase64Map[256] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, @@ -139,59 +145,6 @@ std::string GetThreadName(pid_t tid) { return result; } -void GetThreadStack(pthread_t thread, void** stack_base, size_t* stack_size, size_t* guard_size) { -#if defined(__APPLE__) - *stack_size = pthread_get_stacksize_np(thread); - void* stack_addr = pthread_get_stackaddr_np(thread); - - // Check whether stack_addr is the base or end of the stack. - // (On Mac OS 10.7, it's the end.) - int stack_variable; - if (stack_addr > &stack_variable) { - *stack_base = reinterpret_cast<uint8_t*>(stack_addr) - *stack_size; - } else { - *stack_base = stack_addr; - } - - // This is wrong, but there doesn't seem to be a way to get the actual value on the Mac. - pthread_attr_t attributes; - CHECK_PTHREAD_CALL(pthread_attr_init, (&attributes), __FUNCTION__); - CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, guard_size), __FUNCTION__); - CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), __FUNCTION__); -#else - pthread_attr_t attributes; - CHECK_PTHREAD_CALL(pthread_getattr_np, (thread, &attributes), __FUNCTION__); - CHECK_PTHREAD_CALL(pthread_attr_getstack, (&attributes, stack_base, stack_size), __FUNCTION__); - CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, guard_size), __FUNCTION__); - CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), __FUNCTION__); - -#if defined(__GLIBC__) - // If we're the main thread, check whether we were run with an unlimited stack. In that case, - // glibc will have reported a 2GB stack for our 32-bit process, and our stack overflow detection - // will be broken because we'll die long before we get close to 2GB. - bool is_main_thread = (::art::GetTid() == getpid()); - if (is_main_thread) { - rlimit stack_limit; - if (getrlimit(RLIMIT_STACK, &stack_limit) == -1) { - PLOG(FATAL) << "getrlimit(RLIMIT_STACK) failed"; - } - if (stack_limit.rlim_cur == RLIM_INFINITY) { - size_t old_stack_size = *stack_size; - - // Use the kernel default limit as our size, and adjust the base to match. - *stack_size = 8 * MB; - *stack_base = reinterpret_cast<uint8_t*>(*stack_base) + (old_stack_size - *stack_size); - - VLOG(threads) << "Limiting unlimited stack (reported as " << PrettySize(old_stack_size) << ")" - << " to " << PrettySize(*stack_size) - << " with base " << *stack_base; - } - } -#endif - -#endif -} - bool ReadFileToString(const std::string& file_name, std::string* result) { File file(file_name, O_RDONLY, false); if (!file.IsOpened()) { @@ -411,6 +364,10 @@ std::string PrettySize(int64_t byte_count) { negative_str, byte_count / kBytesPerUnit[i], kUnitStrings[i]); } +static inline constexpr bool NeedsEscaping(uint16_t ch) { + return (ch < ' ' || ch > '~'); +} + std::string PrintableChar(uint16_t ch) { std::string result; result += '\''; @@ -782,67 +739,6 @@ void Split(const std::string& s, char separator, std::vector<std::string>* resul } } -std::string Trim(const std::string& s) { - std::string result; - unsigned int start_index = 0; - unsigned int end_index = s.size() - 1; - - // Skip initial whitespace. - while (start_index < s.size()) { - if (!isspace(s[start_index])) { - break; - } - start_index++; - } - - // Skip terminating whitespace. - while (end_index >= start_index) { - if (!isspace(s[end_index])) { - break; - } - end_index--; - } - - // All spaces, no beef. - if (end_index < start_index) { - return ""; - } - // Start_index is the first non-space, end_index is the last one. - return s.substr(start_index, end_index - start_index + 1); -} - -template <typename StringT> -std::string Join(const std::vector<StringT>& strings, char separator) { - if (strings.empty()) { - return ""; - } - - std::string result(strings[0]); - for (size_t i = 1; i < strings.size(); ++i) { - result += separator; - result += strings[i]; - } - return result; -} - -// Explicit instantiations. -template std::string Join<std::string>(const std::vector<std::string>& strings, char separator); -template std::string Join<const char*>(const std::vector<const char*>& strings, char separator); - -bool StartsWith(const std::string& s, const char* prefix) { - return s.compare(0, strlen(prefix), prefix) == 0; -} - -bool EndsWith(const std::string& s, const char* suffix) { - size_t suffix_length = strlen(suffix); - size_t string_length = s.size(); - if (suffix_length > string_length) { - return false; - } - size_t offset = string_length - suffix_length; - return s.compare(offset, suffix_length, suffix) == 0; -} - void SetThreadName(const char* thread_name) { int hasAt = 0; int hasDot = 0; @@ -892,31 +788,6 @@ void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu) *task_cpu = strtoull(fields[36].c_str(), nullptr, 10); } -std::string GetSchedulerGroupName(pid_t tid) { - // /proc/<pid>/cgroup looks like this: - // 2:devices:/ - // 1:cpuacct,cpu:/ - // We want the third field from the line whose second field contains the "cpu" token. - std::string cgroup_file; - if (!ReadFileToString(StringPrintf("/proc/self/task/%d/cgroup", tid), &cgroup_file)) { - return ""; - } - std::vector<std::string> cgroup_lines; - Split(cgroup_file, '\n', &cgroup_lines); - for (size_t i = 0; i < cgroup_lines.size(); ++i) { - std::vector<std::string> cgroup_fields; - Split(cgroup_lines[i], ':', &cgroup_fields); - std::vector<std::string> cgroups; - Split(cgroup_fields[1], ',', &cgroups); - for (size_t j = 0; j < cgroups.size(); ++j) { - if (cgroups[j] == "cpu") { - return cgroup_fields[2].substr(1); // Skip the leading slash. - } - } - } - return ""; -} - const char* GetAndroidRoot() { const char* android_root = getenv("ANDROID_ROOT"); if (android_root == nullptr) { @@ -1005,7 +876,9 @@ bool GetDalvikCacheFilename(const char* location, const char* cache_location, return false; } std::string cache_file(&location[1]); // skip leading slash - if (!EndsWith(location, ".dex") && !EndsWith(location, ".art") && !EndsWith(location, ".oat")) { + if (!android::base::EndsWith(location, ".dex") && + !android::base::EndsWith(location, ".art") && + !android::base::EndsWith(location, ".oat")) { cache_file += "/"; cache_file += DexFile::kClassesDex; } @@ -1032,7 +905,7 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is } int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg) { - const std::string command_line(Join(arg_vector, ' ')); + const std::string command_line(android::base::Join(arg_vector, ' ')); CHECK_GE(arg_vector.size(), 1U) << command_line; // Convert the args to char pointers. @@ -1091,7 +964,7 @@ int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_m bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { int status = ExecAndReturnCode(arg_vector, error_msg); if (status != 0) { - const std::string command_line(Join(arg_vector, ' ')); + const std::string command_line(android::base::Join(arg_vector, ' ')); *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", command_line.c_str()); return false; diff --git a/runtime/utils.h b/runtime/utils.h index 1e9805790a..16ef706159 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -64,45 +64,12 @@ bool ParseInt(const char* in, T* out) { return true; } -// Return whether x / divisor == x * (1.0f / divisor), for every float x. -static constexpr bool CanDivideByReciprocalMultiplyFloat(int32_t divisor) { - // True, if the most significant bits of divisor are 0. - return ((divisor & 0x7fffff) == 0); -} - -// Return whether x / divisor == x * (1.0 / divisor), for every double x. -static constexpr bool CanDivideByReciprocalMultiplyDouble(int64_t divisor) { - // True, if the most significant bits of divisor are 0. - return ((divisor & ((UINT64_C(1) << 52) - 1)) == 0); -} - static inline uint32_t PointerToLowMemUInt32(const void* p) { uintptr_t intp = reinterpret_cast<uintptr_t>(p); DCHECK_LE(intp, 0xFFFFFFFFU); return intp & 0xFFFFFFFFU; } -static inline bool NeedsEscaping(uint16_t ch) { - return (ch < ' ' || ch > '~'); -} - -template <typename T> T SafeAbs(T value) { - // std::abs has undefined behavior on min limits. - DCHECK_NE(value, std::numeric_limits<T>::min()); - return std::abs(value); -} - -template <typename T> T AbsOrMin(T value) { - return (value == std::numeric_limits<T>::min()) - ? value - : std::abs(value); -} - -template <typename T> -inline typename std::make_unsigned<T>::type MakeUnsigned(T x) { - return static_cast<typename std::make_unsigned<T>::type>(x); -} - uint8_t* DecodeBase64(const char* src, size_t* dst_size); std::string PrintableChar(uint16_t ch); @@ -111,12 +78,6 @@ std::string PrintableChar(uint16_t ch); // Java escapes are used for non-ASCII characters. std::string PrintableString(const char* utf8); -// Tests whether 's' starts with 'prefix'. -bool StartsWith(const std::string& s, const char* prefix); - -// Tests whether 's' ends with 'suffix'. -bool EndsWith(const std::string& s, const char* suffix); - // Used to implement PrettyClass, PrettyField, PrettyMethod, and PrettyTypeOf, // one of which is probably more useful to you. // Returns a human-readable equivalent of 'descriptor'. So "I" would be "int", @@ -167,27 +128,15 @@ bool PrintFileToLog(const std::string& file_name, LogSeverity level); // strings. Empty strings will be omitted. void Split(const std::string& s, char separator, std::vector<std::string>* result); -// Trims whitespace off both ends of the given string. -std::string Trim(const std::string& s); - -// Joins a vector of strings into a single string, using the given separator. -template <typename StringT> std::string Join(const std::vector<StringT>& strings, char separator); - // Returns the calling thread's tid. (The C libraries don't expose this.) pid_t GetTid(); // Returns the given thread's name. std::string GetThreadName(pid_t tid); -// Returns details of the given thread's stack. -void GetThreadStack(pthread_t thread, void** stack_base, size_t* stack_size, size_t* guard_size); - // Reads data from "/proc/self/task/${tid}/stat". void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu); -// Returns the name of the scheduler group for the given thread the current process, or the empty string. -std::string GetSchedulerGroupName(pid_t tid); - // Sets the name of the current thread. The name may be truncated to an // implementation-defined limit. void SetThreadName(const char* thread_name); @@ -251,15 +200,6 @@ class VoidFunctor { } }; -template <typename Vector> -void Push32(Vector* buf, int32_t data) { - static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type"); - buf->push_back(data & 0xff); - buf->push_back((data >> 8) & 0xff); - buf->push_back((data >> 16) & 0xff); - buf->push_back((data >> 24) & 0xff); -} - inline bool TestBitmap(size_t idx, const uint8_t* bitmap) { return ((bitmap[idx / kBitsPerByte] >> (idx % kBitsPerByte)) & 0x01) != 0; } @@ -334,12 +274,6 @@ static T GetRandomNumber(T min, T max) { return dist(rng); } -// All of the elements from one container to another. -template <typename Dest, typename Src> -static void AddAll(Dest& dest, const Src& src) { - dest.insert(src.begin(), src.end()); -} - // Return the file size in bytes or -1 if the file does not exists. int64_t GetFileSizeBytes(const std::string& filename); @@ -350,6 +284,12 @@ inline void FlushInstructionCache(char* begin, char* end) { __builtin___clear_cache(begin, end); } +inline void FlushDataCache(char* begin, char* end) { + // Same as FlushInstructionCache for lack of other builtin. __builtin___clear_cache + // flushes both caches. + __builtin___clear_cache(begin, end); +} + template <typename T> constexpr PointerSize ConvertToPointerSize(T any) { if (any == 4 || any == 8) { diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc index be4d394464..82d92fc2fc 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -273,58 +273,6 @@ TEST_F(UtilsTest, Split) { EXPECT_EQ(expected, actual); } -TEST_F(UtilsTest, Join) { - std::vector<std::string> strings; - - strings.clear(); - EXPECT_EQ("", Join(strings, ':')); - - strings.clear(); - strings.push_back("foo"); - EXPECT_EQ("foo", Join(strings, ':')); - - strings.clear(); - strings.push_back(""); - strings.push_back("foo"); - EXPECT_EQ(":foo", Join(strings, ':')); - - strings.clear(); - strings.push_back("foo"); - strings.push_back(""); - EXPECT_EQ("foo:", Join(strings, ':')); - - strings.clear(); - strings.push_back(""); - strings.push_back("foo"); - strings.push_back(""); - EXPECT_EQ(":foo:", Join(strings, ':')); - - strings.clear(); - strings.push_back("foo"); - strings.push_back("bar"); - EXPECT_EQ("foo:bar", Join(strings, ':')); - - strings.clear(); - strings.push_back("foo"); - strings.push_back("bar"); - strings.push_back("baz"); - EXPECT_EQ("foo:bar:baz", Join(strings, ':')); -} - -TEST_F(UtilsTest, StartsWith) { - EXPECT_FALSE(StartsWith("foo", "bar")); - EXPECT_TRUE(StartsWith("foo", "foo")); - EXPECT_TRUE(StartsWith("food", "foo")); - EXPECT_FALSE(StartsWith("fo", "foo")); -} - -TEST_F(UtilsTest, EndsWith) { - EXPECT_FALSE(EndsWith("foo", "bar")); - EXPECT_TRUE(EndsWith("foo", "foo")); - EXPECT_TRUE(EndsWith("foofoo", "foo")); - EXPECT_FALSE(EndsWith("oo", "foo")); -} - TEST_F(UtilsTest, GetDalvikCacheFilename) { std::string name; std::string error; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index ebecc85a3c..715b237a6a 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -18,6 +18,8 @@ #include <iostream> +#include "android-base/stringprintf.h" + #include "art_field-inl.h" #include "art_method-inl.h" #include "base/enums.h" @@ -55,6 +57,8 @@ namespace art { namespace verifier { +using android::base::StringPrintf; + static constexpr bool kTimeVerifyMethod = !kIsDebugBuild; static constexpr bool kDebugVerify = false; // TODO: Add a constant to method_verifier to turn on verbose logging? @@ -417,6 +421,19 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, if (method != nullptr) { if (verifier.HasInstructionThatWillThrow()) { method->AddAccessFlags(kAccCompileDontBother); + if (Runtime::Current()->IsAotCompiler() && + (callbacks != nullptr) && !callbacks->IsBootImage()) { + // When compiling apps, make HasInstructionThatWillThrow a soft error to trigger + // re-verification at runtime. + // The dead code after the throw is not verified and might be invalid. This may cause + // the JIT compiler to crash since it assumes that all the code is valid. + // + // There's a strong assumption that the entire boot image is verified and all its dex + // code is valid (even the dead and unverified one). As such this is done only for apps. + // (CompilerDriver DCHECKs in VerifyClassVisitor that methods from boot image are + // fully verified). + result.kind = kSoftFailure; + } } if ((verifier.encountered_failure_types_ & VerifyError::VERIFY_ERROR_LOCKING) != 0) { method->AddAccessFlags(kAccMustCountLocks); diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc index 52be2df06b..be5c18b9eb 100644 --- a/runtime/verifier/method_verifier_test.cc +++ b/runtime/verifier/method_verifier_test.cc @@ -19,6 +19,8 @@ #include <stdio.h> #include <memory> +#include "android-base/strings.h" + #include "class_linker-inl.h" #include "common_runtime_test.h" #include "dex_file.h" @@ -42,7 +44,7 @@ class MethodVerifierTest : public CommonRuntimeTest { MethodVerifier::FailureKind failure = MethodVerifier::VerifyClass( self, klass, nullptr, true, HardFailLogMode::kLogWarning, &error_msg); - if (StartsWith(descriptor, "Ljava/lang/invoke")) { + if (android::base::StartsWith(descriptor, "Ljava/lang/invoke")) { ASSERT_TRUE(failure == MethodVerifier::kSoftFailure || failure == MethodVerifier::kNoFailure) << error_msg; diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h index 10f1be5109..aa4a2591d9 100644 --- a/runtime/verifier/reg_type-inl.h +++ b/runtime/verifier/reg_type-inl.h @@ -112,7 +112,8 @@ inline bool RegType::AssignableFrom(const RegType& lhs, } else if (lhs.HasClass() && rhs.HasClass()) { // Test assignability from the Class point-of-view. bool result = lhs.GetClass()->IsAssignableFrom(rhs.GetClass()); - // Record assignability dependency. The `verifier` is null during unit tests. + // Record assignability dependency. The `verifier` is null during unit tests and + // VerifiedMethod::GenerateSafeCastSet. if (verifier != nullptr) { VerifierDeps::MaybeRecordAssignability( verifier->GetDexFile(), lhs.GetClass(), rhs.GetClass(), strict, result); diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index ab23773bc4..52f7e348ce 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -16,6 +16,8 @@ #include "reg_type-inl.h" +#include "android-base/stringprintf.h" + #include "base/arena_bit_vector.h" #include "base/bit_vector-inl.h" #include "base/casts.h" @@ -35,6 +37,8 @@ namespace art { namespace verifier { +using android::base::StringPrintf; + const UndefinedType* UndefinedType::instance_ = nullptr; const ConflictType* ConflictType::instance_ = nullptr; const BooleanType* BooleanType::instance_ = nullptr; diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc index a6088aa036..383d890702 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -16,7 +16,8 @@ #include "register_line.h" -#include "base/stringprintf.h" +#include "android-base/stringprintf.h" + #include "dex_instruction-inl.h" #include "method_verifier-inl.h" #include "register_line-inl.h" @@ -25,6 +26,8 @@ namespace art { namespace verifier { +using android::base::StringPrintf; + bool RegisterLine::CheckConstructorReturn(MethodVerifier* verifier) const { if (kIsDebugBuild && this_initialized_) { // Ensure that there is no UninitializedThisReference type anymore if this_initialized_ is true. diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index f9bff23054..5f94a1bd9b 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -249,11 +249,12 @@ bool VerifierDeps::IsInClassPath(ObjPtr<mirror::Class> klass) const { if (dex_cache == nullptr) { // This is a synthesized class, in this case always an array. They are not // defined in the compiled DEX files and therefore are part of the classpath. - // We could avoid recording dependencies on arrays with component types in - // the compiled DEX files but we choose to record them anyway so as to - // record the access flags VM sets for array classes. + // We do not record dependencies on arrays with component types in + // the compiled DEX files, as the only thing that might change is their + // access flags. If we were to change these flags in a breaking way, we would + // need to enforce full verification again anyways by updating the vdex version. DCHECK(klass->IsArrayClass()) << klass->PrettyDescriptor(); - return true; + return false; } const DexFile* dex_file = dex_cache->GetDexFile(); @@ -345,8 +346,21 @@ void VerifierDeps::AddAssignability(const DexFile& dex_file, // merely on no issues with linking (valid access flags, superclass and // implemented interfaces). If the class at any point reached the IsResolved // status, the requirement holds. This is guaranteed by RegTypeCache::ResolveClass. - DCHECK(destination != nullptr && !destination->IsPrimitive()); - DCHECK(source != nullptr && !source->IsPrimitive()); + DCHECK(destination != nullptr); + DCHECK(source != nullptr); + + if (destination->IsPrimitive() || source->IsPrimitive()) { + // Primitive types are trivially non-assignable to anything else. + // We do not need to record trivial assignability, as it will + // not change across releases. + return; + } + + if (source->IsObjectClass() && !is_assignable) { + // j.l.Object is trivially non-assignable to other types, don't + // record it. + return; + } if (destination == source || destination->IsObjectClass() || @@ -389,6 +403,21 @@ void VerifierDeps::AddAssignability(const DexFile& dex_file, return; } + if (!IsInClassPath(source) && !source->IsInterface() && !destination->IsInterface()) { + // Find the super class at the classpath boundary. Only that class + // can change the assignability. + // TODO: also chase the boundary for interfaces. + do { + source = source->GetSuperClass(); + } while (!IsInClassPath(source)); + + // If that class is the actual destination, no need to record it. + if (source == destination) { + return; + } + } + + // Get string IDs for both descriptors and store in the appropriate set. dex::StringIndex destination_id = GetClassDescriptorStringId(dex_file, destination); dex::StringIndex source_id = GetClassDescriptorStringId(dex_file, source); diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 35495867c7..009170cb1c 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -20,6 +20,8 @@ #include <sstream> +#include "android-base/stringprintf.h" + #include "base/logging.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "jni_internal.h" @@ -189,7 +191,7 @@ jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static, static jmethodID CachePrimitiveBoxingMethod(JNIEnv* env, char prim_name, const char* boxed_name) { ScopedLocalRef<jclass> boxed_class(env, env->FindClass(boxed_name)); return CacheMethod(env, boxed_class.get(), true, "valueOf", - StringPrintf("(%c)L%s;", prim_name, boxed_name).c_str()); + android::base::StringPrintf("(%c)L%s;", prim_name, boxed_name).c_str()); } #define STRING_INIT_LIST(V) \ diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc index d96fb429e5..cd79bb61f3 100644 --- a/runtime/zip_archive.cc +++ b/runtime/zip_archive.cc @@ -23,7 +23,6 @@ #include <unistd.h> #include <vector> -#include "base/stringprintf.h" #include "base/unix_file/fd_file.h" namespace art { diff --git a/test/021-string2/expected.txt b/test/021-string2/expected.txt index a9c6eb87bd..f269c7cf3e 100644 --- a/test/021-string2/expected.txt +++ b/test/021-string2/expected.txt @@ -1,2 +1,6 @@ Got expected npe OK + true true true true + true true true true + true true true true + true true true true diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java index 51351e1835..df0a3ddf48 100644 --- a/test/021-string2/src/Main.java +++ b/test/021-string2/src/Main.java @@ -92,6 +92,31 @@ public class Main { testCompareToAndEquals(); testIndexOf(); + + String s0_0 = "\u0000"; + String s0_1 = new String(s0_0); + String s0_2 = new String(new char[] { '\u0000' }); + String s0_3 = s0_0 + ""; + System.out.println( + " " + $noinline$equals(s0_0, s0_0) + + " " + $noinline$equals(s0_0, s0_1) + + " " + $noinline$equals(s0_0, s0_2) + + " " + $noinline$equals(s0_0, s0_3)); + System.out.println( + " " + $noinline$equals(s0_1, s0_0) + + " " + $noinline$equals(s0_1, s0_1) + + " " + $noinline$equals(s0_1, s0_2) + + " " + $noinline$equals(s0_1, s0_3)); + System.out.println( + " " + $noinline$equals(s0_2, s0_0) + + " " + $noinline$equals(s0_2, s0_1) + + " " + $noinline$equals(s0_2, s0_2) + + " " + $noinline$equals(s0_2, s0_3)); + System.out.println( + " " + $noinline$equals(s0_3, s0_0) + + " " + $noinline$equals(s0_3, s0_1) + + " " + $noinline$equals(s0_3, s0_2) + + " " + $noinline$equals(s0_3, s0_3)); } public static void testCompareToAndEquals() { diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 113b35f98d..3b237f40ff 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -25,11 +25,11 @@ #include "jni.h" +#include "android-base/stringprintf.h" #include <backtrace/Backtrace.h> #include "base/logging.h" #include "base/macros.h" -#include "base/stringprintf.h" #include "gc/heap.h" #include "gc/space/image_space.h" #include "oat_file.h" @@ -91,7 +91,7 @@ static bool CheckStack(Backtrace* bt, const std::vector<std::string>& seq) { static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) { printf("Secondary pid is %d\n", pid); - PrintFileToLog(StringPrintf("/proc/%d/maps", pid), ::android::base::ERROR); + PrintFileToLog(android::base::StringPrintf("/proc/%d/maps", pid), ::android::base::ERROR); if (sig_quit_on_fail) { int res = kill(pid, SIGQUIT); diff --git a/test/141-class-unload/expected.txt b/test/141-class-unload/expected.txt index 0a03ecb3a4..a1c5fa8a2f 100644 --- a/test/141-class-unload/expected.txt +++ b/test/141-class-unload/expected.txt @@ -20,5 +20,6 @@ null JNI_OnLoad called class null false test JNI_OnUnload called +JNI_OnLoad called Number of loaded unload-ex maps 0 Too small false diff --git a/test/141-class-unload/jni_unload.cc b/test/141-class-unload/jni_unload.cc index bbbb0a6036..9b7e171a95 100644 --- a/test/141-class-unload/jni_unload.cc +++ b/test/141-class-unload/jni_unload.cc @@ -32,5 +32,19 @@ extern "C" JNIEXPORT void JNICALL Java_IntHolder_waitForCompilation(JNIEnv*, jcl } } +extern "C" JNIEXPORT void JNICALL Java_Main_stopJit(JNIEnv*, jclass) { + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit != nullptr) { + jit->Stop(); + } +} + +extern "C" JNIEXPORT void JNICALL Java_Main_startJit(JNIEnv*, jclass) { + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit != nullptr) { + jit->Start(); + } +} + } // namespace } // namespace art diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java index 2a6e9442e8..595c70d0f6 100644 --- a/test/141-class-unload/src/Main.java +++ b/test/141-class-unload/src/Main.java @@ -55,11 +55,15 @@ public class Main { } private static void testOatFilesUnloaded(int pid) throws Exception { + System.loadLibrary(nativeLibraryName); + // Stop the JIT to ensure its threads and work queue are not keeping classes + // artifically alive. + stopJit(); + Runtime.getRuntime().gc(); + System.runFinalization(); BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps")); String line; int count = 0; - Runtime.getRuntime().gc(); - System.runFinalization(); while ((line = reader.readLine()) != null) { if (line.contains("@141-class-unload-ex.jar")) { System.out.println(line); @@ -67,6 +71,7 @@ public class Main { } } System.out.println("Number of loaded unload-ex maps " + count); + startJit(); } private static void stressTest(Constructor<?> constructor) throws Exception { @@ -229,4 +234,7 @@ public class Main { private static int getPid() throws Exception { return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName()); } + + public static native void stopJit(); + public static native void startJit(); } diff --git a/test/153-reference-stress/expected.txt b/test/153-reference-stress/expected.txt new file mode 100644 index 0000000000..7ef22e9a43 --- /dev/null +++ b/test/153-reference-stress/expected.txt @@ -0,0 +1 @@ +PASS diff --git a/test/153-reference-stress/info.txt b/test/153-reference-stress/info.txt new file mode 100644 index 0000000000..6bc00404ab --- /dev/null +++ b/test/153-reference-stress/info.txt @@ -0,0 +1 @@ +Tests java.lang.ref.Reference.get() and GC running in parallel. diff --git a/test/153-reference-stress/src/Main.java b/test/153-reference-stress/src/Main.java new file mode 100644 index 0000000000..fc6f9ccb35 --- /dev/null +++ b/test/153-reference-stress/src/Main.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.ref.WeakReference; + +public class Main { + static final int numWeakReferences = 16 * 1024; + static WeakReference[] weakReferences = new WeakReference[numWeakReferences]; + static volatile boolean done = false; + static Object keepAlive; + + public static void main(String[] args) throws Exception { + // Try to call Reference.get repeatedly while the GC is running. + Thread gcThread = new GcThread(); + Thread[] readerThread = new ReaderThread[4]; + for (int i = 0; i < readerThread.length; ++i) { + readerThread[i] = new ReaderThread(); + } + gcThread.start(); + for (int i = 0; i < readerThread.length; ++i) { + readerThread[i].start(); + } + gcThread.join(); + for (int i = 0; i < readerThread.length; ++i) { + readerThread[i].join(); + } + System.out.println("PASS"); + } + + static class GcThread extends Thread { + GcThread() { + Object temp = new Object(); + for (int j = 0; j < weakReferences.length; ++j) { + weakReferences[j] = new WeakReference(temp); + } + } + public void run() { + for (int i = 0; i < 1000; ++i) { + Object o = new Object(); + for (int j = 0; j < weakReferences.length; ++j) { + weakReferences[j] = new WeakReference(o); + } + } + done = true; + } + } + + static class ReaderThread extends Thread { + public void run() { + while (!done) { + for (int j = 0; j < weakReferences.length; ++j) { + keepAlive = weakReferences[j].get(); + } + for (int j = 0; j < weakReferences.length; ++j) { + weakReferences[j].clear(); + } + } + } + } +} diff --git a/test/530-checker-loops3/src/Main.java b/test/530-checker-loops3/src/Main.java index 209786a113..dfc4a5f98f 100644 --- a/test/530-checker-loops3/src/Main.java +++ b/test/530-checker-loops3/src/Main.java @@ -235,6 +235,59 @@ public class Main { } } + /// CHECK-START: void Main.shortBound1(int[], short) BCE (before) + /// CHECK-DAG: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: void Main.shortBound1(int[], short) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.shortBound1(int[], short) BCE (after) + /// CHECK-NOT: BoundsCheck + public static void shortBound1(int[] array, short s) { + // Lower precision bound will appear in deopt arithmetic + // and follows normal implicit widening conversion. + for (int i = 0; i < s; i++) { + array[i] = 222; + } + } + + /// CHECK-START: void Main.shortBound2(int[], short) BCE (before) + /// CHECK-DAG: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: void Main.shortBound2(int[], short) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-DAG: Deoptimize loop:none + /// CHECK-NOT: Deoptimize + // + /// CHECK-START: void Main.shortBound2(int[], short) BCE (after) + /// CHECK-NOT: BoundsCheck + public static void shortBound2(int[] array, short s) { + // Lower precision bound will appear in deopt arithmetic + // and follows normal implicit widening conversion. + for (int i = 0; s > i; i++) { + array[i] = 444; + } + } + + /// CHECK-START: void Main.narrowingFromLong(int[], int) BCE (before) + /// CHECK-DAG: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: void Main.narrowingFromLong(int[], int) BCE (after) + /// CHECK-DAG: BoundsCheck loop:{{B\d+}} + public static void narrowingFromLong(int[] array, int n) { + // Parallel induction in long precision that is narrowed provides type + // conversion challenges for BCE in deopt arithmetic when combined + // with the int loop induction. Therefore, currently skipped. + long l = 0; + for (int i = 0; i < n; i++, l++) { + array[(int)l] = 888; + } + } + // // Verifier. // @@ -316,6 +369,38 @@ public class Main { } catch (ArrayIndexOutOfBoundsException e) { } + shortBound1(a, (short)a.length); + for (int i = 0; i < a.length; i++) { + expectEquals(222, a[i]); + } + shortBound2(a, (short)a.length); + for (int i = 0; i < a.length; i++) { + expectEquals(444, a[i]); + } + + try { + shortBound1(a, (short)(a.length + 1)); + throw new Error("Should throw AIOOBE"); + } catch (ArrayIndexOutOfBoundsException e) { + } + for (int i = 0; i < a.length; i++) { + expectEquals(222, a[i]); + } + + try { + shortBound2(a, (short)(a.length + 1)); + throw new Error("Should throw AIOOBE"); + } catch (ArrayIndexOutOfBoundsException e) { + } + for (int i = 0; i < a.length; i++) { + expectEquals(444, a[i]); + } + + narrowingFromLong(a, a.length); + for (int i = 0; i < a.length; i++) { + expectEquals(888, a[i]); + } + System.out.println("passed"); } diff --git a/test/530-checker-loops4/src/Main.java b/test/530-checker-loops4/src/Main.java index 7d3d7d9bfe..91af1f4ee9 100644 --- a/test/530-checker-loops4/src/Main.java +++ b/test/530-checker-loops4/src/Main.java @@ -96,7 +96,29 @@ public class Main { /// CHECK-NOT: Phi public static int geo4(int a) { for (int i = 0; i < 10; i++) { - a %= 7; + a %= 7; // a wrap-around induction + } + return a; + } + + /// CHECK-START: int Main.geo5() loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> + /// CHECK-DAG: Shr loop:<<Loop>> + // + /// CHECK-START: int Main.geo5() loop_optimization (after) + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Int1:i\d+>> IntConstant 2147483647 loop:none + /// CHECK-DAG: <<Int2:i\d+>> IntConstant 1024 loop:none + /// CHECK-DAG: <<Div:i\d+>> Div [<<Int1>>,<<Int2>>] loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Div>>,<<Zero>>] loop:none + /// CHECK-DAG: Return [<<Add>>] loop:none + // + /// CHECK-START: int Main.geo5() loop_optimization (after) + /// CHECK-NOT: Phi + public static int geo5() { + int a = 0x7fffffff; + for (int i = 0; i < 10; i++) { + a >>= 1; } return a; } @@ -186,7 +208,7 @@ public class Main { int r = 0; for (int i = 0; i < 100; i++) { // a converges to 0 r += x[a]; - a %= 5; + a %= 5; // a wrap-around induction } return r; } @@ -305,6 +327,8 @@ public class Main { expectEquals(i % 7, geo4(i)); } + expectEquals(0x1fffff, geo5()); + expectEquals(34, geo1BCE()); expectEquals(36, geo2BCE()); expectEquals(131, geo3BCE()); diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java index 9e475ab9be..fe6ff13628 100644 --- a/test/552-checker-sharpening/src/Main.java +++ b/test/552-checker-sharpening/src/Main.java @@ -55,6 +55,9 @@ public class Main { /// CHECK-NOT: MipsDexCacheArraysBase /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative + /// CHECK-START-MIPS64: int Main.testSimple(int) sharpening (after) + /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative + /// CHECK-START-X86: int Main.testSimple(int) sharpening (after) /// CHECK-NOT: X86ComputeBaseMethodAddress /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative @@ -96,6 +99,10 @@ public class Main { /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative + /// CHECK-START-MIPS64: int Main.testDiamond(boolean, int) sharpening (after) + /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative + /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative + /// CHECK-START-X86: int Main.testDiamond(boolean, int) sharpening (after) /// CHECK-NOT: X86ComputeBaseMethodAddress /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative @@ -274,6 +281,11 @@ public class Main { // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. /// CHECK: LoadString load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}} + /// CHECK-START-MIPS64: java.lang.String Main.$noinline$getBootImageString() sharpening (after) + // Note: load kind depends on PIC/non-PIC + // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. + /// CHECK: LoadString load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}} + public static String $noinline$getBootImageString() { // Prevent inlining to avoid the string comparison being optimized away. if (doThrow) { throw new Error(); } @@ -303,6 +315,9 @@ public class Main { /// CHECK-START-MIPS: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) /// CHECK: LoadString load_kind:BssEntry + /// CHECK-START-MIPS64: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) + /// CHECK: LoadString load_kind:BssEntry + public static String $noinline$getNonBootImageString() { // Prevent inlining to avoid the string comparison being optimized away. if (doThrow) { throw new Error(); } @@ -338,6 +353,11 @@ public class Main { // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getStringClass() sharpening (after) + // Note: load kind depends on PIC/non-PIC + // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. + /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + public static Class<?> $noinline$getStringClass() { // Prevent inlining to avoid the string comparison being optimized away. if (doThrow) { throw new Error(); } @@ -375,6 +395,9 @@ public class Main { /// CHECK-DAG: MipsDexCacheArraysBase /// CHECK-DAG: LoadClass load_kind:DexCachePcRelative class_name:Other + /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) + /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + public static Class<?> $noinline$getOtherClass() { // Prevent inlining to avoid the string comparison being optimized away. if (doThrow) { throw new Error(); } diff --git a/test/616-cha-regression-proxy-method/expected.txt b/test/616-cha-regression-proxy-method/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-regression-proxy-method/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-regression-proxy-method/info.txt b/test/616-cha-regression-proxy-method/info.txt new file mode 100644 index 0000000000..386a07f398 --- /dev/null +++ b/test/616-cha-regression-proxy-method/info.txt @@ -0,0 +1 @@ +Regression test for Class Hierarchy Analysis (CHA) on visiting proxy method frame. diff --git a/test/616-cha-regression-proxy-method/src/Main.java b/test/616-cha-regression-proxy-method/src/Main.java new file mode 100644 index 0000000000..19c92be006 --- /dev/null +++ b/test/616-cha-regression-proxy-method/src/Main.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +class Main1 { + void foo(int i) { + if (i != 1) { + printError("error1"); + } + } + + void printError(String msg) { + System.out.println(msg); + } +} + +class Main2 extends Main1 { + void foo(int i) { + if (i != 2) { + printError("error2"); + } + } +} + +class Proxied implements Runnable { + public void run() { + synchronized(Main.class) { + Main.sOtherThreadStarted = true; + // Wait for Main2 to be linked and deoptimization is triggered. + try { + Main.class.wait(); + } catch (Exception e) { + } + } + } +} + +class MyInvocationHandler implements InvocationHandler { + private final Proxied proxied; + + public MyInvocationHandler(Proxied proxied) { + this.proxied = proxied; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return method.invoke(proxied, args); + } +} + +public class Main { + static Main1 sMain1; + static Main1 sMain2; + static volatile boolean sOtherThreadStarted; + + // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked. + // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined. + // After Dummy.createMain2() which links in Main2, live testOverride() on stack + // should be deoptimized. + static void testOverride() { + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + + // Wait for the other thread to start. + while (!sOtherThreadStarted); + // Create an Main2 instance and assign it to sMain2. + // sMain1 is kept the same. + sMain2 = Dummy.createMain2(); + // Wake up the other thread. + synchronized(Main.class) { + Main.class.notify(); + } + + // There should be a deoptimization here right after Main2 is linked by + // calling Dummy.createMain2(), even though sMain1 didn't change. + // The behavior here would be different if inline-cache is used, which + // doesn't deoptimize since sMain1 still hits the type cache. + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + if (sMain2 != null) { + sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2); + } + } + + // Test scenarios under which CHA-based devirtualization happens, + // and class loading that overrides a method can invalidate compiled code. + // Also create a proxy method such that a proxy method's frame is visited + // during stack walking. + public static void main(String[] args) { + System.loadLibrary(args[0]); + // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet. + sMain1 = new Main1(); + + // Create another thread that calls a proxy method. + new Thread() { + public void run() { + Runnable proxy = (Runnable)Proxy.newProxyInstance( + Proxied.class.getClassLoader(), + new Class[] { Runnable.class }, + new MyInvocationHandler(new Proxied())); + proxy.run(); + } + }.start(); + + ensureJitCompiled(Main.class, "testOverride"); + // This will create Main2 instance in the middle of testOverride(). + testOverride(); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); +} + +// Put createMain2() in another class to avoid class loading due to verifier. +class Dummy { + static Main1 createMain2() { + return new Main2(); + } +} diff --git a/test/Android.arm_vixl.mk b/test/616-cha/run index c89eb4a1d8..9c64c7dc66 100644 --- a/test/Android.arm_vixl.mk +++ b/test/616-cha/run @@ -1,3 +1,4 @@ +#!/bin/bash # # Copyright (C) 2016 The Android Open Source Project # @@ -5,16 +6,13 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# -# Known broken tests for the ARM VIXL backend. -TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \ - 562-checker-no-intermediate \ - 624-checker-stringops \ +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha/src/Main.java b/test/616-cha/src/Main.java index 787318dd67..b6179449df 100644 --- a/test/616-cha/src/Main.java +++ b/test/616-cha/src/Main.java @@ -179,7 +179,7 @@ public class Main { } } - // Test scanerios under which CHA-based devirtualization happens, + // Test scenarios under which CHA-based devirtualization happens, // and class loading that overrides a method can invalidate compiled code. // Also test pure non-overriding case, which is more for checking generated // code form. @@ -206,11 +206,6 @@ public class Main { // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet. sMain1 = new Main1(); - // Loop enough to get testOverride() JITed. - for (int i=0; i<100; i++) { - testOverride(false, false, false); - } - ensureJitCompiled(Main.class, "testOverride"); testOverride(false, false, true); @@ -244,7 +239,7 @@ public class Main { private static native boolean hasSingleImplementation(Class<?> clazz, String method_name); } -// Do it in another class to avoid class loading due to verifier. +// Put createMain2() in another class to avoid class loading due to verifier. class Dummy { static Main1 createMain2() { return new Main2(); diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java index 87a69b25c4..ad3ff448d0 100644 --- a/test/618-checker-induction/src/Main.java +++ b/test/618-checker-induction/src/Main.java @@ -248,6 +248,33 @@ public class Main { return closed; // only needs last value } + /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (before) + /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Select loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Return [<<Phi1>>] loop:none + // + /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (after) + /// CHECK-NOT: Phi + /// CHECK-NOT: Select + // + /// CHECK-START: int Main.closedFormInductionTrivialIf() instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Int:i\d+>> IntConstant 81 loop:none + /// CHECK-DAG: Return [<<Int>>] loop:none + static int closedFormInductionTrivialIf() { + int closed = 11; + for (int i = 0; i < 10; i++) { + // Trivial if becomes trivial select at HIR level. + // Make sure this is still recognized as induction. + if (i < 5) { + closed += 7; + } else { + closed += 7; + } + } + return closed; // only needs last value + } + /// CHECK-START: int Main.closedFormNested() loop_optimization (before) /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none @@ -438,6 +465,20 @@ public class Main { return i; } + // TODO: handle as closed/empty eventually? + static int mainIndexShort1(short s) { + int i = 0; + for (i = 0; i < s; i++) { } + return i; + } + + // TODO: handle as closed/empty eventually? + static int mainIndexShort2(short s) { + int i = 0; + for (i = 0; s > i; i++) { } + return i; + } + /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (before) /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none @@ -666,6 +707,75 @@ public class Main { return x; } + /// CHECK-START: float Main.periodicFloat10() loop_optimization (before) + /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Return [<<Phi2>>] loop:none + // + /// CHECK-START: float Main.periodicFloat10() loop_optimization (after) + /// CHECK-NOT: Phi + // + /// CHECK-START: float Main.periodicFloat10() loop_optimization (after) + /// CHECK-DAG: <<Float:f\d+>> FloatConstant 2 loop:none + /// CHECK-DAG: Return [<<Float>>] loop:none + private static float periodicFloat10() { + float r = 4.5f; + float s = 2.0f; + float t = -1.0f; + for (int i = 0; i < 10; i++) { + float tmp = t; t = r; r = s; s = tmp; + } + return r; + } + + /// CHECK-START: float Main.periodicFloat11() loop_optimization (before) + /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Return [<<Phi2>>] loop:none + // + /// CHECK-START: float Main.periodicFloat11() loop_optimization (after) + /// CHECK-NOT: Phi + // + /// CHECK-START: float Main.periodicFloat11() loop_optimization (after) + /// CHECK-DAG: <<Float:f\d+>> FloatConstant -1 loop:none + /// CHECK-DAG: Return [<<Float>>] loop:none + private static float periodicFloat11() { + float r = 4.5f; + float s = 2.0f; + float t = -1.0f; + for (int i = 0; i < 11; i++) { + float tmp = t; t = r; r = s; s = tmp; + } + return r; + } + + /// CHECK-START: float Main.periodicFloat12() loop_optimization (before) + /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Return [<<Phi2>>] loop:none + // + /// CHECK-START: float Main.periodicFloat12() loop_optimization (after) + /// CHECK-NOT: Phi + // + /// CHECK-START: float Main.periodicFloat12() loop_optimization (after) + /// CHECK-DAG: <<Float:f\d+>> FloatConstant 4.5 loop:none + /// CHECK-DAG: Return [<<Float>>] loop:none + private static float periodicFloat12() { + float r = 4.5f; + float s = 2.0f; + float t = -1.0f; + for (int i = 0; i < 12; i++) { + float tmp = t; t = r; r = s; s = tmp; + } + return r; + } + private static int exceptionExitBeforeAdd() { int k = 0; try { @@ -732,6 +842,7 @@ public class Main { expectEquals(12395, closedFormInductionUp()); expectEquals(12295, closedFormInductionInAndDown(12345)); + expectEquals(81, closedFormInductionTrivialIf()); expectEquals(10 * 10, closedFormNested()); expectEquals(12345 + 17 * 23 * 7, closedFormNestedAlt()); for (int n = -4; n < 10; n++) { @@ -751,6 +862,8 @@ public class Main { for (int n = -4; n < 4; n++) { int tc = (n <= 0) ? 0 : n; expectEquals(tc, mainIndexReturnedN(n)); + expectEquals(tc, mainIndexShort1((short) n)); + expectEquals(tc, mainIndexShort2((short) n)); expectEquals(tc & 1, periodicReturnedN(n)); expectEquals((tc * (tc + 1)) / 2, getSumN(n)); } @@ -775,6 +888,10 @@ public class Main { expectEquals(!even, periodicBoolIdiom3N(false, n)); } + expectEquals( 2.0f, periodicFloat10()); + expectEquals(-1.0f, periodicFloat11()); + expectEquals( 4.5f, periodicFloat12()); + expectEquals(100, exceptionExitBeforeAdd()); expectEquals(100, exceptionExitAfterAdd()); a = null; @@ -787,6 +904,12 @@ public class Main { System.out.println("passed"); } + private static void expectEquals(float expected, float result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + private static void expectEquals(int expected, int result) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java index ce5bda1393..7cc0b8b652 100644 --- a/test/623-checker-loop-regressions/src/Main.java +++ b/test/623-checker-loop-regressions/src/Main.java @@ -82,6 +82,88 @@ public class Main { return 0; } + // Regression test for b/33774618: transfer operations involving + // narrowing linear induction should be done correctly. + // + /// CHECK-START: int Main.transferNarrowWrap() loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.transferNarrowWrap() loop_optimization (after) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + static int transferNarrowWrap() { + short x = 0; + int w = 10; + int v = 3; + for (int i = 0; i < 10; i++) { + v = w + 1; // transfer on wrap-around + w = x; // wrap-around + x += 2; // narrowing linear + } + return v; + } + + // Regression test for b/33774618: transfer operations involving + // narrowing linear induction should be done correctly + // (currently rejected, could be improved). + // + /// CHECK-START: int Main.polynomialShort() loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.polynomialShort() loop_optimization (after) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + static int polynomialShort() { + int x = 0; + for (short i = 0; i < 10; i++) { + x = x - i; // polynomial on narrowing linear + } + return x; + } + + // Regression test for b/33774618: transfer operations involving + // narrowing linear induction should be done correctly + // (currently rejected, could be improved). + // + /// CHECK-START: int Main.polynomialIntFromLong() loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.polynomialIntFromLong() loop_optimization (after) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + static int polynomialIntFromLong() { + int x = 0; + for (long i = 0; i < 10; i++) { + x = x - (int) i; // polynomial on narrowing linear + } + return x; + } + + /// CHECK-START: int Main.polynomialInt() loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none + // + /// CHECK-START: int Main.polynomialInt() loop_optimization (after) + /// CHECK-NOT: Phi + // + /// CHECK-START: int Main.polynomialInt() instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Int:i\d+>> IntConstant -45 loop:none + /// CHECK-DAG: Return [<<Int>>] loop:none + static int polynomialInt() { + int x = 0; + for (int i = 0; i < 10; i++) { + x = x - i; + } + return x; + } + public static void main(String[] args) { expectEquals(10, earlyExitFirst(-1)); for (int i = 0; i <= 10; i++) { @@ -98,6 +180,11 @@ public class Main { expectEquals(2, earlyExitNested()); + expectEquals(17, transferNarrowWrap()); + expectEquals(-45, polynomialShort()); + expectEquals(-45, polynomialIntFromLong()); + expectEquals(-45, polynomialInt()); + System.out.println("passed"); } diff --git a/test/624-checker-stringops/src/Main.java b/test/624-checker-stringops/src/Main.java index d965e3ffce..75b782e8c0 100644 --- a/test/624-checker-stringops/src/Main.java +++ b/test/624-checker-stringops/src/Main.java @@ -258,6 +258,20 @@ public class Main { return b.length(); } + // Regression b/33656359: StringBuffer x is passed to constructor of String + // (this caused old code to crash due to missing nullptr check). + // + /// CHECK-START: void Main.doesNothing() instruction_simplifier (before) + /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString + // + /// CHECK-START: void Main.doesNothing() instruction_simplifier (after) + /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString + static void doesNothing() { + StringBuffer x = new StringBuffer(); + String y = new String(x); + x.toString(); + } + public static void main(String[] args) { expectEquals(1865, liveIndexOf()); expectEquals(29, deadIndexOf()); @@ -281,6 +295,8 @@ public class Main { expectEquals(0, bufferDeadLoop()); expectEquals(0, builderDeadLoop()); + doesNothing(); + System.out.println("passed"); } diff --git a/test/626-checker-arm64-scratch-register/expected.txt b/test/626-checker-arm64-scratch-register/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/626-checker-arm64-scratch-register/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/626-checker-arm64-scratch-register/info.txt b/test/626-checker-arm64-scratch-register/info.txt new file mode 100644 index 0000000000..847213119f --- /dev/null +++ b/test/626-checker-arm64-scratch-register/info.txt @@ -0,0 +1,2 @@ +Regression test checking that the ARM64 scratch register pool is not +exhausted during moves between stack slots (b/32545705). diff --git a/test/626-checker-arm64-scratch-register/src/Main.java b/test/626-checker-arm64-scratch-register/src/Main.java new file mode 100644 index 0000000000..aa211be33c --- /dev/null +++ b/test/626-checker-arm64-scratch-register/src/Main.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + boolean b00; + boolean b01; + boolean b02; + boolean b03; + boolean b04; + boolean b05; + boolean b06; + boolean b07; + boolean b08; + boolean b09; + boolean b10; + boolean b11; + boolean b12; + boolean b13; + boolean b14; + boolean b15; + boolean b16; + boolean b17; + boolean b18; + boolean b19; + boolean b20; + boolean b21; + boolean b22; + boolean b23; + boolean b24; + boolean b25; + boolean b26; + boolean b27; + boolean b28; + boolean b29; + boolean b30; + boolean b31; + boolean b32; + boolean b33; + boolean b34; + boolean b35; + boolean b36; + + boolean conditionA; + boolean conditionB; + boolean conditionC; + + /// CHECK-START-ARM64: void Main.test() register (after) + /// CHECK: begin_block + /// CHECK: name "B0" + /// CHECK: <<This:l\d+>> ParameterValue + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>" + /// CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Main.conditionB + /// CHECK: If [<<CondB>>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: name "<<ElseBlock>>" + /// CHECK: ParallelMove moves:[#100->d17,32(sp)->d1,36(sp)->d2,d17->d3,d3->d4,d4->d5,d5->d6,d6->d7,d7->d18,d18->d19,d19->d20,d20->d21,d21->d22,d22->d23,d23->d10,d10->d11,d11->d12,24(sp)->d13,28(sp)->d14,d14->16(sp),d12->20(sp),d13->24(sp),d1->28(sp),d2->32(sp),16(sp)->36(sp),20(sp)->40(sp)] + /// CHECK: end_block + + /// CHECK-START-ARM64: void Main.test() disassembly (after) + /// CHECK: begin_block + /// CHECK: name "B0" + /// CHECK: <<This:l\d+>> ParameterValue + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>" + /// CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Main.conditionB + /// CHECK: If [<<CondB>>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: name "<<ElseBlock>>" + /// CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] + /// CHECK: fmov d31, d2 + /// CHECK: ldr s2, [sp, #36] + /// CHECK: ldr w16, [sp, #16] + /// CHECK: str w16, [sp, #36] + /// CHECK: str s14, [sp, #16] + /// CHECK: ldr s14, [sp, #28] + /// CHECK: str s1, [sp, #28] + /// CHECK: ldr s1, [sp, #32] + /// CHECK: str s31, [sp, #32] + /// CHECK: ldr w16, [sp, #20] + /// CHECK: str w16, [sp, #40] + /// CHECK: str s12, [sp, #20] + /// CHECK: fmov d12, d11 + /// CHECK: fmov d11, d10 + /// CHECK: fmov d10, d23 + /// CHECK: fmov d23, d22 + /// CHECK: fmov d22, d21 + /// CHECK: fmov d21, d20 + /// CHECK: fmov d20, d19 + /// CHECK: fmov d19, d18 + /// CHECK: fmov d18, d7 + /// CHECK: fmov d7, d6 + /// CHECK: fmov d6, d5 + /// CHECK: fmov d5, d4 + /// CHECK: fmov d4, d3 + /// CHECK: fmov d3, d17 + /// CHECK: fmov d17, d13 + /// CHECK: ldr s13, [sp, #24] + /// CHECK: str s17, [sp, #24] + /// CHECK: ldr s17, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100) + /// CHECK: end_block + + public void test() { + String r = ""; + + // For the purpose of this regression test, the order of + // definition of these float variable matters. Likewise with the + // order of the instructions where these variables are used below. + // Reordering these lines make make the original (b/32545705) + // issue vanish. + float f17 = b17 ? 0.0f : 1.0f; + float f16 = b16 ? 0.0f : 1.0f; + float f18 = b18 ? 0.0f : 1.0f; + float f19 = b19 ? 0.0f : 1.0f; + float f20 = b20 ? 0.0f : 1.0f; + float f21 = b21 ? 0.0f : 1.0f; + float f15 = b15 ? 0.0f : 1.0f; + float f00 = b00 ? 0.0f : 1.0f; + float f22 = b22 ? 0.0f : 1.0f; + float f23 = b23 ? 0.0f : 1.0f; + float f24 = b24 ? 0.0f : 1.0f; + float f25 = b25 ? 0.0f : 1.0f; + float f26 = b26 ? 0.0f : 1.0f; + float f27 = b27 ? 0.0f : 1.0f; + float f29 = b29 ? 0.0f : 1.0f; + float f28 = b28 ? 0.0f : 1.0f; + float f01 = b01 ? 0.0f : 1.0f; + float f02 = b02 ? 0.0f : 1.0f; + float f03 = b03 ? 0.0f : 1.0f; + float f04 = b04 ? 0.0f : 1.0f; + float f05 = b05 ? 0.0f : 1.0f; + float f07 = b07 ? 0.0f : 1.0f; + float f06 = b06 ? 0.0f : 1.0f; + float f30 = b30 ? 0.0f : 1.0f; + float f31 = b31 ? 0.0f : 1.0f; + float f32 = b32 ? 0.0f : 1.0f; + float f33 = b33 ? 0.0f : 1.0f; + float f34 = b34 ? 0.0f : 1.0f; + float f36 = b36 ? 0.0f : 1.0f; + float f35 = b35 ? 0.0f : 1.0f; + float f08 = b08 ? 0.0f : 1.0f; + float f09 = b09 ? 0.0f : 1.0f; + float f10 = b10 ? 0.0f : 1.0f; + float f11 = b11 ? 0.0f : 1.0f; + float f12 = b12 ? 0.0f : 1.0f; + float f14 = b14 ? 0.0f : 1.0f; + float f13 = b13 ? 0.0f : 1.0f; + + if (conditionA) { + f16 /= 1000.0f; + f17 /= 1000.0f; + f18 /= 1000.0f; + f19 /= 1000.0f; + f20 /= 1000.0f; + f21 /= 1000.0f; + f15 /= 1000.0f; + f08 /= 1000.0f; + f09 /= 1000.0f; + f10 /= 1000.0f; + f11 /= 1000.0f; + f12 /= 1000.0f; + f30 /= 1000.0f; + f31 /= 1000.0f; + f32 /= 1000.0f; + f33 /= 1000.0f; + f34 /= 1000.0f; + f01 /= 1000.0f; + f02 /= 1000.0f; + f03 /= 1000.0f; + f04 /= 1000.0f; + f05 /= 1000.0f; + f23 /= 1000.0f; + f24 /= 1000.0f; + f25 /= 1000.0f; + f26 /= 1000.0f; + f27 /= 1000.0f; + f22 /= 1000.0f; + f00 /= 1000.0f; + f14 /= 1000.0f; + f13 /= 1000.0f; + f36 /= 1000.0f; + f35 /= 1000.0f; + f07 /= 1000.0f; + f06 /= 1000.0f; + f29 /= 1000.0f; + f28 /= 1000.0f; + } + // The parallel move that used to exhaust the ARM64 parallel move + // resolver's scratch register pool (provided by VIXL) was in the + // "else" branch of the following condition generated by ART's + // compiler. + if (conditionB) { + f16 /= 100.0f; + f17 /= 100.0f; + f18 /= 100.0f; + f19 /= 100.0f; + f20 /= 100.0f; + f21 /= 100.0f; + f15 /= 100.0f; + f08 /= 100.0f; + f09 /= 100.0f; + f10 /= 100.0f; + f11 /= 100.0f; + f12 /= 100.0f; + f30 /= 100.0f; + f31 /= 100.0f; + f32 /= 100.0f; + f33 /= 100.0f; + f34 /= 100.0f; + f01 /= 100.0f; + f02 /= 100.0f; + f03 /= 100.0f; + f04 /= 100.0f; + f05 /= 100.0f; + f23 /= 100.0f; + f24 /= 100.0f; + f25 /= 100.0f; + f26 /= 100.0f; + f27 /= 100.0f; + f22 /= 100.0f; + f00 /= 100.0f; + f14 /= 100.0f; + f13 /= 100.0f; + f36 /= 100.0f; + f35 /= 100.0f; + f07 /= 100.0f; + f06 /= 100.0f; + f29 /= 100.0f; + f28 /= 100.0f; + } + if (conditionC) { + f16 /= 12.0f; + f17 /= 12.0f; + f18 /= 12.0f; + f19 /= 12.0f; + f20 /= 12.0f; + f21 /= 12.0f; + f15 /= 12.0f; + f08 /= 12.0f; + f09 /= 12.0f; + f10 /= 12.0f; + f11 /= 12.0f; + f12 /= 12.0f; + f30 /= 12.0f; + f31 /= 12.0f; + f32 /= 12.0f; + f33 /= 12.0f; + f34 /= 12.0f; + f01 /= 12.0f; + f02 /= 12.0f; + f03 /= 12.0f; + f04 /= 12.0f; + f05 /= 12.0f; + f23 /= 12.0f; + f24 /= 12.0f; + f25 /= 12.0f; + f26 /= 12.0f; + f27 /= 12.0f; + f22 /= 12.0f; + f00 /= 12.0f; + f14 /= 12.0f; + f13 /= 12.0f; + f36 /= 12.0f; + f35 /= 12.0f; + f07 /= 12.0f; + f06 /= 12.0f; + f29 /= 12.0f; + f28 /= 12.0f; + } + float s = 0.0f; + s = ((float) Math.round(100.0f * s)) / 100.0f; + String res = s + r; + } + + public static void main(String[] args) { + Main main = new Main(); + main.test(); + System.out.println("passed"); + } +} diff --git a/test/628-vdex/run b/test/628-vdex/run index f1b0a95f64..4cbcea3b7e 100644 --- a/test/628-vdex/run +++ b/test/628-vdex/run @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -exec ${RUN} --vdex "${@}" +exec ${RUN} -Xcompiler-option --compiler-filter=verify-profile --vdex "${@}" diff --git a/test/630-safecast-array/expected.txt b/test/630-safecast-array/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/630-safecast-array/expected.txt diff --git a/test/630-safecast-array/info.txt b/test/630-safecast-array/info.txt new file mode 100644 index 0000000000..e10516784e --- /dev/null +++ b/test/630-safecast-array/info.txt @@ -0,0 +1,3 @@ +Regression test for vdex, which used to crash in AddAssignability +called by the dex2dex compiler, not anticipating arrays of primitive +type. diff --git a/test/630-safecast-array/smali/Main.smali b/test/630-safecast-array/smali/Main.smali new file mode 100644 index 0000000000..a50f37ccbc --- /dev/null +++ b/test/630-safecast-array/smali/Main.smali @@ -0,0 +1,33 @@ +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class LMain; +.super Ljava/lang/Object; + +.method public static main([Ljava/lang/String;)V +.registers 1 + return-void +.end method + +.method public static testPrimitiveDestination([Ljava/lang/String;)V +.registers 1 + check-cast p0, [B + return-void +.end method + +.method public static testPrimitiveSource([B)V +.registers 1 + check-cast p0, [Ljava/lang/String; + return-void +.end method diff --git a/test/706-jit-skip-compilation/expected.txt b/test/706-jit-skip-compilation/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/706-jit-skip-compilation/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/706-jit-skip-compilation/info.txt b/test/706-jit-skip-compilation/info.txt new file mode 100644 index 0000000000..e9ef86bfb3 --- /dev/null +++ b/test/706-jit-skip-compilation/info.txt @@ -0,0 +1,4 @@ +Regression test for the JIT crashing when compiling a method with invalid +dead dex code. For not compilable methods we don't gather samples and we don't +trigger JIT compilation. However kAccDontBotherCompile is not persisted in the +oat file and so we may end up compiling a method which we shouldn't. diff --git a/test/706-jit-skip-compilation/run b/test/706-jit-skip-compilation/run new file mode 100644 index 0000000000..6c5720a099 --- /dev/null +++ b/test/706-jit-skip-compilation/run @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Run without the app image, otherwise the verification results will be cached +# in the ArtMethod of the image and the test will be skewed. +exec ${RUN} "${@}" --no-app-image diff --git a/test/706-jit-skip-compilation/smali/errclass.smali b/test/706-jit-skip-compilation/smali/errclass.smali new file mode 100644 index 0000000000..410504cb2f --- /dev/null +++ b/test/706-jit-skip-compilation/smali/errclass.smali @@ -0,0 +1,34 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +.class public LErrClass; + +.super Ljava/lang/Object; + +.method public static errMethod()J + .registers 8 + const/4 v0, 0x0 + const/4 v3, 0x0 + aget v1, v0, v3 # v0 is null, this will alays throw and the invalid code + # below will not be verified. + move v3, v4 + move-wide/from16 v6, v2 # should trigger a verification error if verified as + # v3 is a single register but used as a pair here. + return v6 +.end method + +# Add a field to work around demerger bug b/18051191. +# Failure to verify dex file '...': Offset(552) should be zero when size is zero for field-ids. +.field private a:I diff --git a/test/706-jit-skip-compilation/src/Main.java b/test/706-jit-skip-compilation/src/Main.java new file mode 100644 index 0000000000..aa847248d6 --- /dev/null +++ b/test/706-jit-skip-compilation/src/Main.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + Class<?> c = Class.forName("ErrClass"); + Method m = c.getMethod("errMethod"); + + // Print the counter before invokes. The golden file expects this to be 0. + int hotnessCounter = getHotnessCounter(c, "errMethod"); + if (hotnessCounter != 0) { + throw new RuntimeException("Unexpected hotnessCounter: " + hotnessCounter); + } + + // Loop enough to make sure the interpreter reports invocations count. + long result = 0; + for (int i = 0; i < 10000; i++) { + try { + result += (Long)m.invoke(null); + hotnessCounter = getHotnessCounter(c, "errMethod"); + if (hotnessCounter != 0) { + throw new RuntimeException( + "Unexpected hotnessCounter: " + hotnessCounter); + } + + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof NullPointerException)) { + throw e; + } + } + } + + // Not compilable methods should not increase their hotness counter. + if (hotnessCounter != 0) { + throw new RuntimeException("Unexpected hotnessCounter: " + hotnessCounter); + } + } + + public static native int getHotnessCounter(Class cls, String method_name); +} diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt index 20bab7814e..f8c97ce475 100644 --- a/test/911-get-stack-trace/expected.txt +++ b/test/911-get-stack-trace/expected.txt @@ -3,206 +3,206 @@ ################### From top --------- - getStackTrace (Ljava/lang/Thread;II)[Ljava/lang/String; - print (Ljava/lang/Thread;II)V - printOrWait (IILMain$ControlData;)V - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - doTest ()V - main ([Ljava/lang/String;)V ---------- - print (Ljava/lang/Thread;II)V - printOrWait (IILMain$ControlData;)V - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - doTest ()V - main ([Ljava/lang/String;)V ---------- - getStackTrace (Ljava/lang/Thread;II)[Ljava/lang/String; - print (Ljava/lang/Thread;II)V - printOrWait (IILMain$ControlData;)V - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J ---------- - printOrWait (IILMain$ControlData;)V - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; + getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2 + print (Ljava/lang/Thread;II)V 0 124 + printOrWait (IILMain$ControlData;)V 6 151 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + doTest ()V 38 34 + main ([Ljava/lang/String;)V 6 24 +--------- + print (Ljava/lang/Thread;II)V 0 124 + printOrWait (IILMain$ControlData;)V 6 151 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + doTest ()V 42 35 + main ([Ljava/lang/String;)V 6 24 +--------- + getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2 + print (Ljava/lang/Thread;II)V 0 124 + printOrWait (IILMain$ControlData;)V 6 151 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 + bar (IIILMain$ControlData;)J 0 136 +--------- + printOrWait (IILMain$ControlData;)V 6 151 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 From bottom --------- - main ([Ljava/lang/String;)V + main ([Ljava/lang/String;)V 6 24 --------- - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - doTest ()V - main ([Ljava/lang/String;)V + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + doTest ()V 65 41 + main ([Ljava/lang/String;)V 6 24 --------- - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 ################################ ### Other thread (suspended) ### ################################ From top --------- - wait ()V - printOrWait (IILMain$ControlData;)V - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - run ()V ---------- - printOrWait (IILMain$ControlData;)V - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - run ()V ---------- - wait ()V - printOrWait (IILMain$ControlData;)V - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I ---------- - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 157 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + run ()V 4 54 +--------- + printOrWait (IILMain$ControlData;)V 24 157 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + run ()V 4 54 +--------- + wait ()V -1 -2 + printOrWait (IILMain$ControlData;)V 24 157 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 +--------- + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 From bottom --------- - run ()V + run ()V 4 54 --------- - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - run ()V + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + run ()V 4 54 --------- - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 ########################### ### Other thread (live) ### ########################### From top --------- - printOrWait (IILMain$ControlData;)V - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - run ()V ---------- - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - run ()V ---------- - printOrWait (IILMain$ControlData;)V - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; ---------- - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I + printOrWait (IILMain$ControlData;)V 44 164 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + run ()V 4 88 +--------- + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + run ()V 4 88 +--------- + printOrWait (IILMain$ControlData;)V 44 164 + baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 +--------- + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 From bottom --------- - run ()V + run ()V 4 88 --------- - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - run ()V + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + run ()V 4 88 --------- - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J - foo (IIILMain$ControlData;)I - baz (IIILMain$ControlData;)Ljava/lang/Object; - bar (IIILMain$ControlData;)J + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 + foo (IIILMain$ControlData;)I 0 131 + baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144 + bar (IIILMain$ControlData;)J 0 136 diff --git a/test/911-get-stack-trace/src/Main.java b/test/911-get-stack-trace/src/Main.java index df4501d338..722bee8056 100644 --- a/test/911-get-stack-trace/src/Main.java +++ b/test/911-get-stack-trace/src/Main.java @@ -109,13 +109,14 @@ public class Main { t.join(); } - public static void print(String[] stack) { + public static void print(String[][] stack) { System.out.println("---------"); - for (int i = 0; i < stack.length; i += 2) { - System.out.print(' '); - System.out.print(stack[i]); - System.out.print(' '); - System.out.println(stack[i + 1]); + for (String[] stackElement : stack) { + for (String part : stackElement) { + System.out.print(' '); + System.out.print(part); + } + System.out.println(); } } @@ -174,5 +175,5 @@ public class Main { volatile boolean stop = false; } - public static native String[] getStackTrace(Thread thread, int start, int max); + public static native String[][] getStackTrace(Thread thread, int start, int max); } diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc index e7d9380469..57f6a927ea 100644 --- a/test/911-get-stack-trace/stack_trace.cc +++ b/test/911-get-stack-trace/stack_trace.cc @@ -16,10 +16,14 @@ #include "stack_trace.h" +#include <inttypes.h> #include <memory> #include <stdio.h> +#include "android-base/stringprintf.h" + #include "base/logging.h" +#include "base/macros.h" #include "jni.h" #include "openjdkjvmti/jvmti.h" #include "ScopedLocalRef.h" @@ -29,6 +33,25 @@ namespace art { namespace Test911GetStackTrace { +using android::base::StringPrintf; + +static jint FindLineNumber(jint line_number_count, + jvmtiLineNumberEntry* line_number_table, + jlocation location) { + if (line_number_table == nullptr) { + return -2; + } + + jint line_number = -1; + for (jint i = 0; i != line_number_count; ++i) { + if (line_number_table[i].start_location > location) { + return line_number; + } + line_number = line_number_table[i].line_number; + } + return line_number; +} + extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace( JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint start, jint max) { std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]); @@ -44,8 +67,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace( } } - auto callback = [&](jint i) -> jstring { - size_t method_index = static_cast<size_t>(i) / 2; + auto callback = [&](jint method_index) -> jobjectArray { char* name; char* sig; char* gen; @@ -58,13 +80,47 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace( return nullptr; } } - jstring callback_result; - if (i % 2 == 0) { - callback_result = name == nullptr ? nullptr : env->NewStringUTF(name); - } else { - callback_result = sig == nullptr ? nullptr : env->NewStringUTF(sig); + + jint line_number_count; + jvmtiLineNumberEntry* line_number_table; + { + jvmtiError line_result = jvmti_env->GetLineNumberTable(frames[method_index].method, + &line_number_count, + &line_number_table); + if (line_result != JVMTI_ERROR_NONE) { + // Accept absent info and native method errors. + if (line_result != JVMTI_ERROR_ABSENT_INFORMATION && + line_result != JVMTI_ERROR_NATIVE_METHOD) { + char* err; + jvmti_env->GetErrorName(line_result, &err); + printf("Failure running GetLineNumberTable: %s\n", err); + return nullptr; + } + line_number_table = nullptr; + line_number_count = 0; + } } + auto inner_callback = [&](jint component_index) -> jstring { + switch (component_index) { + case 0: + return (name == nullptr) ? nullptr : env->NewStringUTF(name); + case 1: + return (sig == nullptr) ? nullptr : env->NewStringUTF(sig); + case 2: + return env->NewStringUTF(StringPrintf("%" PRId64, frames[method_index].location).c_str()); + case 3: { + jint line_number = FindLineNumber(line_number_count, + line_number_table, + frames[method_index].location); + return env->NewStringUTF(StringPrintf("%d", line_number).c_str()); + } + } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + }; + jobjectArray inner_array = CreateObjectArray(env, 4, "java/lang/String", inner_callback); + if (name != nullptr) { jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name)); } @@ -74,9 +130,13 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace( if (gen != nullptr) { jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen)); } - return callback_result; + if (line_number_table != nullptr) { + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(line_number_table)); + } + + return inner_array; }; - return CreateObjectArray(env, 2 * count, "java/lang/String", callback); + return CreateObjectArray(env, count, "[Ljava/lang/String;", callback); } // Don't do anything diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt index e5fa53f207..7522a659f2 100644 --- a/test/913-heaps/expected.txt +++ b/test/913-heaps/expected.txt @@ -5,12 +5,12 @@ root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vre root@root --(thread)--> 3000@0 [size=132, length=-1] 0@0 --(array-element@0)--> 1@1000 [size=16, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123, length=-1] -1002@0 --(interface)--> 2001@0 [size=132, length=-1] +1002@0 --(interface)--> 2001@0 [size=124, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123, length=-1] 1@1000 --(class)--> 1000@0 [size=123, length=-1] 1@1000 --(field@12)--> 3@1001 [size=24, length=-1] 1@1000 --(field@8)--> 2@1000 [size=16, length=-1] -2001@0 --(interface)--> 2000@0 [size=132, length=-1] +2001@0 --(interface)--> 2000@0 [size=124, length=-1] 2@1000 --(class)--> 1000@0 [size=123, length=-1] 3@1001 --(class)--> 1001@0 [size=123, length=-1] 3@1001 --(field@16)--> 4@1000 [size=16, length=-1] @@ -22,12 +22,12 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] 6@1000 --(class)--> 1000@0 [size=123, length=-1] --- 1001@0 --(superclass)--> 1000@0 [size=123, length=-1] -1002@0 --(interface)--> 2001@0 [size=132, length=-1] +1002@0 --(interface)--> 2001@0 [size=124, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123, length=-1] 1@1000 --(class)--> 1000@0 [size=123, length=-1] 1@1000 --(field@12)--> 3@1001 [size=24, length=-1] 1@1000 --(field@8)--> 2@1000 [size=16, length=-1] -2001@0 --(interface)--> 2000@0 [size=132, length=-1] +2001@0 --(interface)--> 2000@0 [size=124, length=-1] 2@1000 --(class)--> 1000@0 [size=123, length=-1] 3@1001 --(class)--> 1001@0 [size=123, length=-1] 3@1001 --(field@16)--> 4@1000 [size=16, length=-1] @@ -46,12 +46,12 @@ root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot root@root --(thread)--> 1@1000 [size=16, length=-1] root@root --(thread)--> 3000@0 [size=132, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123, length=-1] -1002@0 --(interface)--> 2001@0 [size=132, length=-1] +1002@0 --(interface)--> 2001@0 [size=124, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123, length=-1] 1@1000 --(class)--> 1000@0 [size=123, length=-1] 1@1000 --(field@12)--> 3@1001 [size=24, length=-1] 1@1000 --(field@8)--> 2@1000 [size=16, length=-1] -2001@0 --(interface)--> 2000@0 [size=132, length=-1] +2001@0 --(interface)--> 2000@0 [size=124, length=-1] 2@1000 --(class)--> 1000@0 [size=123, length=-1] 3@1001 --(class)--> 1001@0 [size=123, length=-1] 3@1001 --(field@16)--> 4@1000 [size=16, length=-1] @@ -63,12 +63,12 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] 6@1000 --(class)--> 1000@0 [size=123, length=-1] --- 1001@0 --(superclass)--> 1000@0 [size=123, length=-1] -1002@0 --(interface)--> 2001@0 [size=132, length=-1] +1002@0 --(interface)--> 2001@0 [size=124, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123, length=-1] 1@1000 --(class)--> 1000@0 [size=123, length=-1] 1@1000 --(field@12)--> 3@1001 [size=24, length=-1] 1@1000 --(field@8)--> 2@1000 [size=16, length=-1] -2001@0 --(interface)--> 2000@0 [size=132, length=-1] +2001@0 --(interface)--> 2000@0 [size=124, length=-1] 2@1000 --(class)--> 1000@0 [size=123, length=-1] 3@1001 --(class)--> 1001@0 [size=123, length=-1] 3@1001 --(field@16)--> 4@1000 [size=16, length=-1] diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc index 7b00fcdcc1..49ab7dd83e 100644 --- a/test/913-heaps/heaps.cc +++ b/test/913-heaps/heaps.cc @@ -22,9 +22,10 @@ #include <vector> +#include "android-base/stringprintf.h" + #include "base/logging.h" #include "base/macros.h" -#include "base/stringprintf.h" #include "jit/jit.h" #include "jni.h" #include "native_stack_dump.h" @@ -39,6 +40,8 @@ namespace art { namespace Test913Heaps { +using android::base::StringPrintf; + extern "C" JNIEXPORT void JNICALL Java_Main_forceGarbageCollection(JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) { jvmtiError ret = jvmti_env->ForceGarbageCollection(); @@ -180,7 +183,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_followReferences(JNIEnv* env if (*tag_ptr >= 1000) { // This is a class or interface, the size of which will be dependent on the architecture. // Do not print the size, but detect known values and "normalize" for the golden file. - if ((sizeof(void*) == 4 && size == 180) || (sizeof(void*) == 8 && size == 232)) { + if ((sizeof(void*) == 4 && size == 172) || (sizeof(void*) == 8 && size == 224)) { adapted_size = 123; } } diff --git a/test/917-fields-transformation/build b/test/917-fields-transformation/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/917-fields-transformation/build @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-build "$@" --experimental agents diff --git a/test/917-fields-transformation/expected.txt b/test/917-fields-transformation/expected.txt new file mode 100644 index 0000000000..bcdd201113 --- /dev/null +++ b/test/917-fields-transformation/expected.txt @@ -0,0 +1,12 @@ +Result is Hello +take1 is Hello +take2 is Goodbye +Result is start +take1 is start +take2 is end +Result is Goodbye +take1 is Hello +take2 is Goodbye +Result is end +take1 is start +take2 is end diff --git a/test/917-fields-transformation/info.txt b/test/917-fields-transformation/info.txt new file mode 100644 index 0000000000..4cd1bd9dbd --- /dev/null +++ b/test/917-fields-transformation/info.txt @@ -0,0 +1 @@ +Tests field access after class redefinition support in the jvmti plugin. diff --git a/test/917-fields-transformation/run b/test/917-fields-transformation/run new file mode 100755 index 0000000000..a434b63e42 --- /dev/null +++ b/test/917-fields-transformation/run @@ -0,0 +1,43 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +plugin=libopenjdkjvmtid.so +agent=libtiagentd.so +lib=tiagentd +if [[ "$@" == *"-O"* ]]; then + agent=libtiagent.so + plugin=libopenjdkjvmti.so + lib=tiagent +fi + +if [[ "$@" == *"--jvm"* ]]; then + arg="jvm" +else + arg="art" + if [[ "$@" != *"--debuggable"* ]]; then + other_args=" -Xcompiler-option --debuggable " + else + other_args="" + fi +fi + +./default-run "$@" --experimental agents \ + --experimental runtime-plugins \ + --runtime-option -agentpath:${agent}=917-fields-transformation,${arg} \ + --android-runtime-option -Xplugin:${plugin} \ + --android-runtime-option -Xfully-deoptable \ + ${other_args} \ + --args ${lib} diff --git a/test/917-fields-transformation/src/Main.java b/test/917-fields-transformation/src/Main.java new file mode 100644 index 0000000000..5378bb7a05 --- /dev/null +++ b/test/917-fields-transformation/src/Main.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Base64; +public class Main { + + // base64 encoded class/dex file for + // class Transform { + // public String take1; + // public String take2; + // + // public Transform(String a, String b) { + // take1 = a; + // take2 = b; + // } + // + // public String getResult() { + // return take2; + // } + // } + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAFwoABQARCQAEABIJAAQAEwcAFAcAFQEABXRha2UxAQASTGphdmEvbGFuZy9TdHJp" + + "bmc7AQAFdGFrZTIBAAY8aW5pdD4BACcoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJp" + + "bmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJZ2V0UmVzdWx0AQAUKClMamF2YS9sYW5n" + + "L1N0cmluZzsBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkAFgwABgAHDAAIAAcBAAlU" + + "cmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQADKClWACAABAAFAAAAAgABAAYABwAAAAEACAAH" + + "AAAAAgABAAkACgABAAsAAAAzAAIAAwAAAA8qtwABKiu1AAIqLLUAA7EAAAABAAwAAAASAAQAAAAU" + + "AAQAFQAJABYADgAXAAEADQAOAAEACwAAAB0AAQABAAAABSq0AAOwAAAAAQAMAAAABgABAAAAGgAB" + + "AA8AAAACABA="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQAGUTBb4jIABRlaI9rejdk7RCfyqR2kmNSkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAM" + + "AAAAcAAAAAQAAACgAAAAAwAAALAAAAACAAAA1AAAAAMAAADkAAAAAQAAAPwAAACIAQAAHAEAAFwB" + + "AABkAQAAZwEAAHQBAACIAQAAnAEAAKwBAACvAQAAtAEAAMgBAADTAQAA2gEAAAIAAAADAAAABAAA" + + "AAYAAAABAAAAAgAAAAAAAAAGAAAAAwAAAAAAAAAHAAAAAwAAAFQBAAAAAAIACgAAAAAAAgALAAAA" + + "AAACAAAAAAAAAAAACQAAAAEAAQAAAAAAAAAAAAAAAAABAAAAAAAAAAUAAAAAAAAA8AEAAAAAAAAD" + + "AAMAAQAAAOEBAAAIAAAAcBACAAAAWwEAAFsCAQAOAAIAAQAAAAAA6wEAAAMAAABUEAEAEQAAAAIA" + + "AAACAAIABjxpbml0PgABTAALTFRyYW5zZm9ybTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEv" + + "bGFuZy9TdHJpbmc7AA5UcmFuc2Zvcm0uamF2YQABVgADVkxMABJlbWl0dGVyOiBqYWNrLTQuMTkA" + + "CWdldFJlc3VsdAAFdGFrZTEABXRha2UyABQCAAAHDjwtLQAaAAcOAAACAQEAAQEBAIGABJwCAQG8" + + "AgAADQAAAAAAAAABAAAAAAAAAAEAAAAMAAAAcAAAAAIAAAAEAAAAoAAAAAMAAAADAAAAsAAAAAQA" + + "AAACAAAA1AAAAAUAAAADAAAA5AAAAAYAAAABAAAA/AAAAAEgAAACAAAAHAEAAAEQAAABAAAAVAEA" + + "AAIgAAAMAAAAXAEAAAMgAAACAAAA4QEAAAAgAAABAAAA8AEAAAAQAAABAAAABAIAAA=="); + + public static void main(String[] args) { + System.loadLibrary(args[1]); + doTest(new Transform("Hello", "Goodbye"), + new Transform("start", "end")); + } + + private static void printTransform(Transform t) { + System.out.println("Result is " + t.getResult()); + System.out.println("take1 is " + t.take1); + System.out.println("take2 is " + t.take2); + } + public static void doTest(Transform t1, Transform t2) { + printTransform(t1); + printTransform(t2); + doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES); + printTransform(t1); + printTransform(t2); + } + + // Transforms the class + private static native void doCommonClassRedefinition(Class<?> target, + byte[] class_file, + byte[] dex_file); +} diff --git a/runtime/base/stringprintf_test.cc b/test/917-fields-transformation/src/Transform.java index 0bfde33a3f..6fe6223776 100644 --- a/runtime/base/stringprintf_test.cc +++ b/test/917-fields-transformation/src/Transform.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,16 @@ * limitations under the License. */ -#include "stringprintf.h" +class Transform { + public String take1; + public String take2; -#include "gtest/gtest.h" + public Transform(String take1, String take2) { + this.take1 = take1; + this.take2 = take2; + } -namespace art { - -TEST(StringPrintfTest, HexSizeT) { - size_t size = 0x00107e59; - EXPECT_STREQ("00107e59", StringPrintf("%08zx", size).c_str()); - EXPECT_STREQ("0x00107e59", StringPrintf("0x%08zx", size).c_str()); + public String getResult() { + return take1; + } } - -} // namespace art diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 2f1ca6d295..fdd5b6009c 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -226,6 +226,12 @@ define name-to-var $(shell echo $(1) | tr '[:lower:]' '[:upper:]' | tr '-' '_') endef # name-to-var +# Disable 153-reference-stress temporarily until a fix arrives. b/33389022. +# Disable 080-oom-fragmentation due to flakes. b/33795328 +ART_TEST_RUN_TEST_SKIP += \ + 153-reference-stress \ + 080-oom-fragmentation + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES)) @@ -280,6 +286,7 @@ TEST_ART_BROKEN_TARGET_TESTS += \ 911-get-stack-trace \ 912-classes \ 913-heaps \ + 917-fields-transformation \ ifneq (,$(filter target,$(TARGET_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ @@ -492,6 +499,24 @@ ifneq (,$(filter trace stream,$(TRACE_TYPES))) $(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_TRACING_RUN_TESTS),$(ALL_ADDRESS_SIZES)) endif +TEST_ART_BROKEN_TRACING_RUN_TESTS := + +# These tests expect JIT compilation, which is suppressed when tracing. +TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS := \ + 604-hot-static-interface \ + 612-jit-dex-cache \ + 613-inlining-dex-cache \ + 616-cha \ + 626-set-resolved-string \ + +ifneq (,$(filter trace stream,$(TRACE_TYPES))) + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ + jit,$(RELOCATE_TYPES),trace stream,$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \ + $(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS),$(ALL_ADDRESS_SIZES)) +endif + +TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS := + # Known broken tests for the interpreter. # CFI unwinding expects managed frames. # 629 requires compilation. @@ -514,13 +539,24 @@ TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := # Test 906 iterates the heap filtering with different options. No instances should be created # between those runs to be able to have precise checks. # Test 902 hits races with the JIT compiler. b/32821077 +# Test 626-const-class-linking can deadlock with JIT. b/33567581 # Test 629 requires compilation. +# Test 914, 915, 917, & 918 are very sensitive to the exact state of the stack, +# including the jit-inserted runtime frames. This causes them to be somewhat +# flaky as JIT tests. This should be fixed once b/33630159 or b/33616143 are +# resolved but until then just disable them. Test 916 already checks this +# feature for JIT use cases in a way that is resilient to the jit frames. TEST_ART_BROKEN_JIT_RUN_TESTS := \ 137-cfi \ + 626-const-class-linking \ 629-vdex-speed \ 902-hello-transformation \ 904-object-allocation \ 906-iterate-heap \ + 914-hello-obsolescence \ + 915-obsolete-2 \ + 917-fields-transformation \ + 918-obsolete-fields \ ifneq (,$(filter jit,$(COMPILER_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ @@ -544,26 +580,6 @@ ifneq (,$(filter regalloc_gc,$(COMPILER_TYPES))) $(TEST_ART_BROKEN_OPTIMIZING_GRAPH_COLOR),$(ALL_ADDRESS_SIZES)) endif -# Known broken tests for the ARM VIXL backend. -# Android.arm_vixl.mk defines TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS. -include $(LOCAL_PATH)/Android.arm_vixl.mk - -ifdef ART_USE_VIXL_ARM_BACKEND - ifeq (arm,$(filter arm,$(TARGET_ARCH) $(TARGET_2ND_ARCH))) - ifneq (,$(filter $(OPTIMIZING_COMPILER_TYPES),$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(OPTIMIZING_COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS),32) - endif - endif - # TODO(VIXL): These two tests currently fail, but adding them to `ART_TEST_KNOWN_BROKEN` breaks - # `export ART_USE_VIXL_ARM_BACKEND=true && mma -j6 test-art-target-gtest dist` - #ART_TEST_KNOWN_BROKEN += test-art-target-gtest-dex2oat_test32 - #ART_TEST_KNOWN_BROKEN += test-art-target-gtest-image_test32 -endif - - # Known broken tests for the mips32 optimizing compiler backend. TEST_ART_BROKEN_OPTIMIZING_MIPS_RUN_TESTS := \ @@ -672,15 +688,6 @@ ifeq ($(ART_USE_READ_BARRIER),true) endif endif -# Tests disabled for GSS. -TEST_ART_BROKEN_GSS_RUN_TESTS := 080-oom-fragmentation -ifeq ($(ART_DEFAULT_GC_TYPE),GSS) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ - $(PREBUILD_TYPES),$(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \ - $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_GSS_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := diff --git a/test/ErroneousA/ErroneousA.java b/test/ErroneousA/ErroneousA.java new file mode 100644 index 0000000000..49da54452a --- /dev/null +++ b/test/ErroneousA/ErroneousA.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +final class FinalSuper {} diff --git a/test/ErroneousB/ErroneousB.java b/test/ErroneousB/ErroneousB.java new file mode 100644 index 0000000000..6c2902ab7d --- /dev/null +++ b/test/ErroneousB/ErroneousB.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Only final in first dex. +class FinalSuper {} + +class Erroneous extends FinalSuper {} diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 285f3aa541..f26e122580 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -188,4 +188,28 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasSingleImplementation(JNIEnv* return method->HasSingleImplementation(); } +extern "C" JNIEXPORT int JNICALL Java_Main_getHotnessCounter(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit == nullptr) { + // The hotness counter is valid only under JIT. + // If we don't JIT return 0 to match test expectations. + return 0; + } + + ArtMethod* method = nullptr; + { + ScopedObjectAccess soa(Thread::Current()); + + ScopedUtfChars chars(env, method_name); + CHECK(chars.c_str() != nullptr); + method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName( + chars.c_str(), kRuntimePointerSize); + } + + return method->GetCounter(); +} + } // namespace art diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index f0abb442bf..566f7ba522 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -59,6 +59,7 @@ ARGS="" EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS. DRY_RUN="n" # if y prepare to run the test but don't run it. TEST_VDEX="n" +APP_IMAGE="y" while true; do if [ "x$1" = "x--quiet" ]; then @@ -132,6 +133,9 @@ while true; do elif [ "x$1" = "x--prebuild" ]; then PREBUILD="y" shift + elif [ "x$1" = "x--no-app-image" ]; then + APP_IMAGE="n" + shift elif [ "x$1" = "x--strip-dex" ]; then STRIP_DEX="y" shift @@ -369,8 +373,8 @@ fi if [ "$JIT" = "y" ]; then INT_OPTS="-Xusejit:true" if [ "$VERIFY" = "y" ] ; then - INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify-at-runtime" - COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-at-runtime" + INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=interpret-only" + COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only" else INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify-none" COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-none" @@ -453,11 +457,14 @@ vdex_cmdline="true" mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA" strip_cmdline="true" -# Pick a base that will force the app image to get relocated. -app_image="--base=0x4000 --app-image-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.art" if [ "$PREBUILD" = "y" ]; then mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/oat/$ISA" + if [ "$APP_IMAGE" = "y" ]; then + # Pick a base that will force the app image to get relocated. + app_image="--base=0x4000 --app-image-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.art" + fi + dex2oat_cmdline="$INVOKE_WITH $ANDROID_ROOT/bin/dex2oatd \ $COMPILE_FLAGS \ --boot-image=${BOOT_IMAGE} \ diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index 2795cbc25c..38861482d2 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -66,6 +66,7 @@ AgentLib agents[] = { { "911-get-stack-trace", Test911GetStackTrace::OnLoad, nullptr }, { "912-classes", Test912Classes::OnLoad, nullptr }, { "913-heaps", Test913Heaps::OnLoad, nullptr }, + { "917-fields-transformation", common_redefine::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { diff --git a/tools/cpp-define-generator/offset_dexcache.def b/tools/cpp-define-generator/offset_dexcache.def index 4b9d481eb1..abb5e1ecac 100644 --- a/tools/cpp-define-generator/offset_dexcache.def +++ b/tools/cpp-define-generator/offset_dexcache.def @@ -38,7 +38,6 @@ DEFINE_ART_METHOD_OFFSET_SIZED(DEX_CACHE_TYPES, DexCacheResolvedTypes) DEFINE_ART_METHOD_OFFSET_SIZED(JNI, EntryPointFromJni) DEFINE_ART_METHOD_OFFSET_SIZED(QUICK_CODE, EntryPointFromQuickCompiledCode) DEFINE_ART_METHOD_OFFSET(DECLARING_CLASS, DeclaringClass) -DEFINE_DECLARING_CLASS_OFFSET(DEX_CACHE_STRINGS, DexCacheStrings) #undef DEFINE_ART_METHOD_OFFSET #undef DEFINE_ART_METHOD_OFFSET_32 diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py index fd8415d3a9..b5f856f3b7 100755 --- a/tools/jfuzz/run_jfuzz_test.py +++ b/tools/jfuzz/run_jfuzz_test.py @@ -43,10 +43,11 @@ from common.common import DeviceTestEnv BISECTABLE_RET_CODES = (RetCode.SUCCESS, RetCode.ERROR, RetCode.TIMEOUT) -def GetExecutionModeRunner(device, mode): +def GetExecutionModeRunner(use_dx, device, mode): """Returns a runner for the given execution mode. Args: + use_dx: boolean, if True use dx rather than jack device: string, target device serial number (or None) mode: string, execution mode Returns: @@ -57,13 +58,13 @@ def GetExecutionModeRunner(device, mode): if mode == 'ri': return TestRunnerRIOnHost() if mode == 'hint': - return TestRunnerArtIntOnHost() + return TestRunnerArtIntOnHost(use_dx) if mode == 'hopt': - return TestRunnerArtOptOnHost() + return TestRunnerArtOptOnHost(use_dx) if mode == 'tint': - return TestRunnerArtIntOnTarget(device) + return TestRunnerArtIntOnTarget(use_dx, device) if mode == 'topt': - return TestRunnerArtOptOnTarget(device) + return TestRunnerArtOptOnTarget(use_dx, device) raise FatalError('Unknown execution mode') @@ -113,6 +114,33 @@ class TestRunner(object): """ +class TestRunnerWithHostCompilation(TestRunner): + """Abstract test runner that supports compilation on host.""" + + def __init__(self, use_dx): + """Constructor for the runner with host compilation. + + Args: + use_dx: boolean, if True use dx rather than jack + """ + self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', + 'Test.java'] + self._use_dx = use_dx + + def CompileOnHost(self): + if self._use_dx: + if RunCommand(['javac', 'Test.java'], + out=None, err=None, timeout=30) == RetCode.SUCCESS: + retc = RunCommand(['dx', '--dex', '--output=classes.dex'] + glob('*.class'), + out=None, err='dxerr.txt', timeout=30) + else: + retc = RetCode.NOTCOMPILED + else: + retc = RunCommand(['jack'] + self._jack_args, + out=None, err='jackerr.txt', timeout=30) + return retc + + class TestRunnerRIOnHost(TestRunner): """Concrete test runner of the reference implementation on host.""" @@ -136,25 +164,24 @@ class TestRunnerRIOnHost(TestRunner): return None -class TestRunnerArtOnHost(TestRunner): +class TestRunnerArtOnHost(TestRunnerWithHostCompilation): """Abstract test runner of Art on host.""" - def __init__(self, extra_args=None): + def __init__(self, use_dx, extra_args=None): """Constructor for the Art on host tester. Args: + use_dx: boolean, if True use dx rather than jack extra_args: list of strings, extra arguments for dalvikvm """ + super().__init__(use_dx) self._art_cmd = ['/bin/bash', 'art', '-cp', 'classes.dex'] if extra_args is not None: self._art_cmd += extra_args self._art_cmd.append('Test') - self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', - 'Test.java'] def CompileAndRunTest(self): - if RunCommand(['jack'] + self._jack_args, out=None, err='jackerr.txt', - timeout=30) == RetCode.SUCCESS: + if self.CompileOnHost() == RetCode.SUCCESS: retc = RunCommand(self._art_cmd, self.output_file, 'arterr.txt') else: retc = RetCode.NOTCOMPILED @@ -164,9 +191,13 @@ class TestRunnerArtOnHost(TestRunner): class TestRunnerArtIntOnHost(TestRunnerArtOnHost): """Concrete test runner of interpreter mode Art on host.""" - def __init__(self): - """Constructor.""" - super().__init__(['-Xint']) + def __init__(self, use_dx): + """Constructor for the Art on host tester (interpreter). + + Args: + use_dx: boolean, if True use dx rather than jack + """ + super().__init__(use_dx, ['-Xint']) @property def description(self): @@ -183,9 +214,13 @@ class TestRunnerArtIntOnHost(TestRunnerArtOnHost): class TestRunnerArtOptOnHost(TestRunnerArtOnHost): """Concrete test runner of optimizing compiler mode Art on host.""" - def __init__(self): - """Constructor.""" - super().__init__(None) + def __init__(self, use_dx): + """Constructor for the Art on host tester (optimizing). + + Args: + use_dx: boolean, if True use dx rather than jack + """ + super().__init__(use_dx, None) @property def description(self): @@ -201,28 +236,27 @@ class TestRunnerArtOptOnHost(TestRunnerArtOnHost): return ['--raw-cmd={0}'.format(cmd_str), '--timeout', str(30)] -class TestRunnerArtOnTarget(TestRunner): +class TestRunnerArtOnTarget(TestRunnerWithHostCompilation): """Abstract test runner of Art on target.""" - def __init__(self, device, extra_args=None): + def __init__(self, use_dx, device, extra_args=None): """Constructor for the Art on target tester. Args: + use_dx: boolean, if True use dx rather than jack device: string, target device serial number (or None) extra_args: list of strings, extra arguments for dalvikvm """ + super().__init__(use_dx) self._test_env = DeviceTestEnv('jfuzz_', specific_device=device) self._dalvik_cmd = ['dalvikvm'] if extra_args is not None: self._dalvik_cmd += extra_args self._device = device - self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', - 'Test.java'] self._device_classpath = None def CompileAndRunTest(self): - if RunCommand(['jack'] + self._jack_args, out=None, err='jackerr.txt', - timeout=30) == RetCode.SUCCESS: + if self.CompileOnHost() == RetCode.SUCCESS: self._device_classpath = self._test_env.PushClasspath('classes.dex') cmd = self._dalvik_cmd + ['-cp', self._device_classpath, 'Test'] (output, retc) = self._test_env.RunCommand( @@ -247,13 +281,14 @@ class TestRunnerArtOnTarget(TestRunner): class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget): """Concrete test runner of interpreter mode Art on target.""" - def __init__(self, device): - """Constructor. + def __init__(self, use_dx, device): + """Constructor for the Art on target tester (interpreter). Args: + use_dx: boolean, if True use dx rather than jack device: string, target device serial number (or None) """ - super().__init__(device, ['-Xint']) + super().__init__(use_dx, device, ['-Xint']) @property def description(self): @@ -270,13 +305,14 @@ class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget): class TestRunnerArtOptOnTarget(TestRunnerArtOnTarget): """Concrete test runner of optimizing compiler mode Art on target.""" - def __init__(self, device): - """Constructor. + def __init__(self, use_dx, device): + """Constructor for the Art on target tester (optimizing). Args: + use_dx: boolean, if True use dx rather than jack device: string, target device serial number (or None) """ - super().__init__(device, None) + super().__init__(use_dx, device, None) @property def description(self): @@ -306,7 +342,7 @@ class JFuzzTester(object): """Tester that runs JFuzz many times and report divergences.""" def __init__(self, num_tests, device, mode1, mode2, jfuzz_args, - report_script, true_divergence_only): + report_script, true_divergence_only, use_dx): """Constructor for the tester. Args: @@ -317,14 +353,16 @@ class JFuzzTester(object): jfuzz_args: list of strings, additional arguments for jfuzz report_script: string, path to script called for each divergence true_divergence_only: boolean, if True don't bisect timeout divergences + use_dx: boolean, if True use dx rather than jack """ self._num_tests = num_tests self._device = device - self._runner1 = GetExecutionModeRunner(device, mode1) - self._runner2 = GetExecutionModeRunner(device, mode2) + self._runner1 = GetExecutionModeRunner(use_dx, device, mode1) + self._runner2 = GetExecutionModeRunner(use_dx, device, mode2) self._jfuzz_args = jfuzz_args self._report_script = report_script self._true_divergence_only = true_divergence_only + self._use_dx = use_dx self._save_dir = None self._results_dir = None self._jfuzz_dir = None @@ -367,6 +405,7 @@ class JFuzzTester(object): print('Directory :', self._results_dir) print('Exec-mode1:', self._runner1.description) print('Exec-mode2:', self._runner2.description) + print('Compiler :', 'dx' if self._use_dx else 'jack') print() self.ShowStats() for self._test in range(1, self._num_tests + 1): @@ -414,14 +453,16 @@ class JFuzzTester(object): retc2: int, normalized return code of second runner """ if retc1 == retc2: - # Non-divergent in return code. + # No divergence in return code. if retc1 == RetCode.SUCCESS: # Both compilations and runs were successful, inspect generated output. runner1_out = self._runner1.output_file runner2_out = self._runner2.output_file if not filecmp.cmp(runner1_out, runner2_out, shallow=False): + # Divergence in output. self.ReportDivergence(retc1, retc2, is_output_divergence=True) else: + # No divergence in output. self._num_success += 1 elif retc1 == RetCode.TIMEOUT: self._num_timed_out += 1 @@ -429,8 +470,12 @@ class JFuzzTester(object): self._num_not_compiled += 1 else: self._num_not_run += 1 + elif self._true_divergence_only and RetCode.TIMEOUT in (retc1, retc2): + # When only true divergences are requested, any divergence in return + # code where one is a time out is treated as a regular time out. + self._num_timed_out += 1 else: - # Divergent in return code. + # Divergence in return code. self.ReportDivergence(retc1, retc2, is_output_divergence=False) def GetCurrentDivergenceDir(self): @@ -450,13 +495,12 @@ class JFuzzTester(object): os.mkdir(ddir) for f in glob('*.txt') + ['Test.java']: shutil.copy(f, ddir) - if not (self._true_divergence_only and RetCode.TIMEOUT in (retc1, retc2)): - # Maybe run bisection bug search. - if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES: - self.MaybeBisectDivergence(retc1, retc2, is_output_divergence) - # Call reporting script. - if self._report_script: - self.RunReportScript(retc1, retc2, is_output_divergence) + # Maybe run bisection bug search. + if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES: + self.MaybeBisectDivergence(retc1, retc2, is_output_divergence) + # Call reporting script. + if self._report_script: + self.RunReportScript(retc1, retc2, is_output_divergence) def RunReportScript(self, retc1, retc2, is_output_divergence): """Runs report script.""" @@ -546,18 +590,21 @@ def main(): parser.add_argument('--mode2', default='hopt', help='execution mode 2 (default: hopt)') parser.add_argument('--report_script', help='script called for each' - 'divergence') + ' divergence') parser.add_argument('--jfuzz_arg', default=[], dest='jfuzz_args', action='append', help='argument for jfuzz') parser.add_argument('--true_divergence', default=False, action='store_true', help='don\'t bisect timeout divergences') + parser.add_argument('--use_dx', default=False, action='store_true', + help='use old-style dx (rather than jack)') args = parser.parse_args() if args.mode1 == args.mode2: raise FatalError('Identical execution modes given') # Run the JFuzz tester. - with JFuzzTester(args.num_tests, args.device, args.mode1, args.mode2, + with JFuzzTester(args.num_tests, + args.device, args.mode1, args.mode2, args.jfuzz_args, args.report_script, - args.true_divergence) as fuzzer: + args.true_divergence, args.use_dx) as fuzzer: fuzzer.Run() if __name__ == '__main__': diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index 53fe8fedc5..dcef8c01c2 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -169,7 +169,8 @@ description: "Lack of IPv6 on some buildbot slaves", result: EXEC_FAILED, names: ["libcore.io.OsTest#test_byteBufferPositions_sendto_recvfrom_af_inet6", - "libcore.io.OsTest#test_sendtoSocketAddress_af_inet6"], + "libcore.io.OsTest#test_sendtoSocketAddress_af_inet6", + "libcore.io.OsTest#test_recvfrom_EmptyPacket"], bug: 25178637 }, { |