diff options
158 files changed, 5384 insertions, 1550 deletions
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk index b7a2379dc9..1591e34885 100644 --- a/build/Android.common_test.mk +++ b/build/Android.common_test.mk @@ -66,9 +66,6 @@ ART_TEST_OPTIMIZING ?= true # Do you want to test the optimizing compiler with graph coloring register allocation? ART_TEST_OPTIMIZING_GRAPH_COLOR ?= $(ART_TEST_FULL) -# Do we want to test a non-PIC-compiled core image? -ART_TEST_NPIC_IMAGE ?= $(ART_TEST_FULL) - # Do we want to test PIC-compiled tests ("apps")? ART_TEST_PIC_TEST ?= $(ART_TEST_FULL) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index c319b1a387..b661e001c8 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -123,16 +123,16 @@ ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_DEFAULT_64) $(TARGET_CORE_IMAGE_DEFAULT_32) ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \ - $(HOST_CORE_IMAGE_optimizing_pic_64) \ - $(HOST_CORE_IMAGE_optimizing_pic_32) \ - $(HOST_CORE_IMAGE_interpreter_pic_64) \ - $(HOST_CORE_IMAGE_interpreter_pic_32) \ + $(HOST_CORE_IMAGE_optimizing_64) \ + $(HOST_CORE_IMAGE_optimizing_32) \ + $(HOST_CORE_IMAGE_interpreter_64) \ + $(HOST_CORE_IMAGE_interpreter_32) \ $(HOST_OUT_EXECUTABLES)/patchoatd ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \ - $(TARGET_CORE_IMAGE_optimizing_pic_64) \ - $(TARGET_CORE_IMAGE_optimizing_pic_32) \ - $(TARGET_CORE_IMAGE_interpreter_pic_64) \ - $(TARGET_CORE_IMAGE_interpreter_pic_32) \ + $(TARGET_CORE_IMAGE_optimizing_64) \ + $(TARGET_CORE_IMAGE_optimizing_32) \ + $(TARGET_CORE_IMAGE_interpreter_64) \ + $(TARGET_CORE_IMAGE_interpreter_32) \ $(TARGET_OUT_EXECUTABLES)/patchoatd ART_GTEST_oat_file_assistant_test_HOST_DEPS := \ diff --git a/build/Android.oat.mk b/build/Android.oat.mk index 0086660a05..f53740e6e0 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -38,11 +38,10 @@ endif # Use dex2oat debug version for better error reporting # $(1): compiler - optimizing, interpreter or interpreter-access-checks. -# $(2): pic/no-pic -# $(3): 2ND_ or undefined, 2ND_ for 32-bit host builds. -# $(4): wrapper, e.g., valgrind. -# $(5): dex2oat suffix, e.g, valgrind requires 32 right now. -# $(6): multi-image. +# $(2): 2ND_ or undefined, 2ND_ for 32-bit host builds. +# $(3): wrapper, e.g., valgrind. +# $(4): dex2oat suffix, e.g, valgrind requires 32 right now. +# $(5): multi-image. # NB depending on HOST_CORE_DEX_LOCATIONS so we are sure to have the dex files in frameworks for # run-test --no-image define create-core-oat-host-rules @@ -50,7 +49,6 @@ define create-core-oat-host-rules core_image_name := core_oat_name := core_infix := - core_pic_infix := core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY) ifeq ($(1),optimizing) @@ -70,19 +68,8 @@ define create-core-oat-host-rules $$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing) endif - ifeq ($(2),pic) - core_compile_options += --compile-pic - endif - ifeq ($(2),no-pic) - core_pic_infix := -npic - endif - ifneq ($(filter-out pic no-pic,$(2)),) - # Technically this test is not precise, but hopefully good enough. - $$(error found $(2) expected pic or no-pic) - endif - - # If $(6) is true, generate a multi-image. - ifeq ($(6),true) + # If $(5) is true, generate a multi-image. + ifeq ($(5),true) core_multi_infix := -multi core_multi_param := --multi-image --no-inline-from=core-oj-hostdex.jar core_multi_group := _multi @@ -92,20 +79,20 @@ define create-core-oat-host-rules core_multi_group := endif - core_image_name := $($(3)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$$(core_multi_infix)$(4)$(CORE_IMG_SUFFIX) - core_oat_name := $($(3)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$$(core_multi_infix)$(4)$(CORE_OAT_SUFFIX) + core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_IMG_SUFFIX) + core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_OAT_SUFFIX) # Using the bitness suffix makes it easier to add as a dependency for the run-test mk. - ifeq ($(3),) - $(4)HOST_CORE_IMAGE_$(1)_$(2)$$(core_multi_group)_64 := $$(core_image_name) + ifeq ($(2),) + $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name) else - $(4)HOST_CORE_IMAGE_$(1)_$(2)$$(core_multi_group)_32 := $$(core_image_name) + $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name) endif - $(4)HOST_CORE_IMG_OUTS += $$(core_image_name) - $(4)HOST_CORE_OAT_OUTS += $$(core_oat_name) + $(3)HOST_CORE_IMG_OUTS += $$(core_image_name) + $(3)HOST_CORE_OAT_OUTS += $$(core_oat_name) # If we have a wrapper, make the target phony. - ifneq ($(4),) + ifneq ($(3),) .PHONY: $$(core_image_name) endif $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options) @@ -115,15 +102,15 @@ $$(core_image_name): PRIVATE_CORE_MULTI_PARAM := $$(core_multi_param) $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency) @echo "host dex2oat: $$@" @mkdir -p $$(dir $$@) - $$(hide) $(4) $$(DEX2OAT)$(5) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ + $$(hide) $(3) $$(DEX2OAT)$(4) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \ --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \ --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(HOST_CORE_DEX_FILES)) \ $$(addprefix --dex-location=,$$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \ --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \ - --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(3)ART_HOST_ARCH) \ - $$(LOCAL_$(3)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \ + --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(2)ART_HOST_ARCH) \ + $$(LOCAL_$(2)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \ --host --android-root=$$(HOST_OUT) --include-patch-information \ - --generate-debug-info --generate-build-id \ + --generate-debug-info --generate-build-id --compile-pic \ $$(PRIVATE_CORE_MULTI_PARAM) $$(PRIVATE_CORE_COMPILE_OPTIONS) $$(core_oat_name): $$(core_image_name) @@ -134,7 +121,6 @@ $$(core_oat_name): $$(core_image_name) core_image_name := core_oat_name := core_infix := - core_pic_infix := endef # create-core-oat-host-rules # $(1): compiler - optimizing, interpreter or interpreter-access-checks. @@ -142,12 +128,10 @@ endef # create-core-oat-host-rules # $(3): dex2oat suffix. # $(4): multi-image. define create-core-oat-host-rule-combination - $(call create-core-oat-host-rules,$(1),no-pic,,$(2),$(3),$(4)) - $(call create-core-oat-host-rules,$(1),pic,,$(2),$(3),$(4)) + $(call create-core-oat-host-rules,$(1),,$(2),$(3),$(4)) ifneq ($(HOST_PREFER_32_BIT),true) - $(call create-core-oat-host-rules,$(1),no-pic,2ND_,$(2),$(3),$(4)) - $(call create-core-oat-host-rules,$(1),pic,2ND_,$(2),$(3),$(4)) + $(call create-core-oat-host-rules,$(1),2ND_,$(2),$(3),$(4)) endif endef @@ -173,7 +157,6 @@ define create-core-oat-target-rules core_image_name := core_oat_name := core_infix := - core_pic_infix := core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY) ifeq ($(1),optimizing) @@ -195,35 +178,24 @@ define create-core-oat-target-rules $$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing) endif - ifeq ($(2),pic) - core_compile_options += --compile-pic - endif - ifeq ($(2),no-pic) - core_pic_infix := -npic - endif - ifneq ($(filter-out pic no-pic,$(2)),) - #Technically this test is not precise, but hopefully good enough. - $$(error found $(2) expected pic or no-pic) - endif - - core_image_name := $($(3)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_IMG_SUFFIX) - core_oat_name := $($(3)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_OAT_SUFFIX) + core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(3)$(CORE_IMG_SUFFIX) + core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(3)$(CORE_OAT_SUFFIX) # Using the bitness suffix makes it easier to add as a dependency for the run-test mk. - ifeq ($(3),) + ifeq ($(2),) ifdef TARGET_2ND_ARCH - $(4)TARGET_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name) + $(3)TARGET_CORE_IMAGE_$(1)_64 := $$(core_image_name) else - $(4)TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name) + $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) endif else - $(4)TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name) + $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name) endif - $(4)TARGET_CORE_IMG_OUTS += $$(core_image_name) - $(4)TARGET_CORE_OAT_OUTS += $$(core_oat_name) + $(3)TARGET_CORE_IMG_OUTS += $$(core_image_name) + $(3)TARGET_CORE_OAT_OUTS += $$(core_oat_name) # If we have a wrapper, make the target phony. - ifneq ($(4),) + ifneq ($(3),) .PHONY: $$(core_image_name) endif $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options) @@ -237,11 +209,11 @@ $$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency) --image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(TARGET_CORE_DEX_FILES)) \ $$(addprefix --dex-location=,$$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \ --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \ - --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(3)TARGET_ARCH) \ - --instruction-set-variant=$$($(3)DEX2OAT_TARGET_CPU_VARIANT) \ - --instruction-set-features=$$($(3)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \ + --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(2)TARGET_ARCH) \ + --instruction-set-variant=$$($(2)DEX2OAT_TARGET_CPU_VARIANT) \ + --instruction-set-features=$$($(2)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \ --android-root=$$(PRODUCT_OUT)/system --include-patch-information \ - --generate-debug-info --generate-build-id \ + --generate-debug-info --generate-build-id --compile-pic \ $$(PRIVATE_CORE_COMPILE_OPTIONS) || (rm $$(PRIVATE_CORE_OAT_NAME); exit 1) $$(core_oat_name): $$(core_image_name) @@ -252,19 +224,16 @@ $$(core_oat_name): $$(core_image_name) core_image_name := core_oat_name := core_infix := - core_pic_infix := endef # create-core-oat-target-rules # $(1): compiler - optimizing, interpreter or interpreter-access-checks. # $(2): wrapper. # $(3): dex2oat suffix. define create-core-oat-target-rule-combination - $(call create-core-oat-target-rules,$(1),no-pic,,$(2),$(3)) - $(call create-core-oat-target-rules,$(1),pic,,$(2),$(3)) + $(call create-core-oat-target-rules,$(1),,$(2),$(3)) ifdef TARGET_2ND_ARCH - $(call create-core-oat-target-rules,$(1),no-pic,2ND_,$(2),$(3)) - $(call create-core-oat-target-rules,$(1),pic,2ND_,$(2),$(3)) + $(call create-core-oat-target-rules,$(1),2ND_,$(2),$(3)) endif endef @@ -284,7 +253,7 @@ valgrind-test-art-host-dex2oat: valgrind-test-art-host-dex2oat-host valgrind-tes # Define a default core image that can be used for things like gtests that # need some image to run, but don't otherwise care which image is used. -HOST_CORE_IMAGE_DEFAULT_32 := $(HOST_CORE_IMAGE_optimizing_pic_32) -HOST_CORE_IMAGE_DEFAULT_64 := $(HOST_CORE_IMAGE_optimizing_pic_64) -TARGET_CORE_IMAGE_DEFAULT_32 := $(TARGET_CORE_IMAGE_optimizing_pic_32) -TARGET_CORE_IMAGE_DEFAULT_64 := $(TARGET_CORE_IMAGE_optimizing_pic_64) +HOST_CORE_IMAGE_DEFAULT_32 := $(HOST_CORE_IMAGE_optimizing_32) +HOST_CORE_IMAGE_DEFAULT_64 := $(HOST_CORE_IMAGE_optimizing_64) +TARGET_CORE_IMAGE_DEFAULT_32 := $(TARGET_CORE_IMAGE_optimizing_32) +TARGET_CORE_IMAGE_DEFAULT_64 := $(TARGET_CORE_IMAGE_optimizing_64) diff --git a/compiler/Android.bp b/compiler/Android.bp index f6a4db49fb..f5589cd7a3 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -111,6 +111,7 @@ art_cc_defaults { "optimizing/instruction_simplifier_shared.cc", "optimizing/intrinsics_arm.cc", "optimizing/intrinsics_arm_vixl.cc", + "optimizing/nodes_shared.cc", "utils/arm/assembler_arm.cc", "utils/arm/assembler_arm_vixl.cc", "utils/arm/assembler_thumb2.cc", @@ -127,7 +128,6 @@ art_cc_defaults { "optimizing/scheduler_arm64.cc", "optimizing/instruction_simplifier_arm64.cc", "optimizing/intrinsics_arm64.cc", - "optimizing/nodes_arm64.cc", "utils/arm64/assembler_arm64.cc", "utils/arm64/jni_macro_assembler_arm64.cc", "utils/arm64/managed_register_arm64.cc", diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 2f9164c0e0..d89cdbabf8 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -175,6 +175,7 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa, size_t number_of_threads) { compiler_options_->boot_image_ = true; + compiler_options_->SetCompilerFilter(GetCompilerFilter()); compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), kind, diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 0d45a50053..98dcf20714 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -77,6 +77,10 @@ class CommonCompilerTest : public CommonRuntimeTest { virtual ProfileCompilationInfo* GetProfileCompilationInfo(); + virtual CompilerFilter::Filter GetCompilerFilter() const { + return CompilerFilter::kDefaultCompilerFilter; + } + virtual void TearDown(); void CompileClass(mirror::ClassLoader* class_loader, const char* class_name) diff --git a/compiler/compiler.h b/compiler/compiler.h index 2ca0b77a73..908d3669ed 100644 --- a/compiler/compiler.h +++ b/compiler/compiler.h @@ -27,6 +27,7 @@ namespace jit { class JitCodeCache; } namespace mirror { + class ClassLoader; class DexCache; } @@ -63,7 +64,7 @@ class Compiler { InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - jobject class_loader, + Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) const = 0; diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index d4f6545c59..76aeaa55d7 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -284,16 +284,13 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, } uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader>(unit_.GetClassLoader()))); ClassLinker* class_linker = unit_.GetClassLinker(); ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>( GetDexFile(), method_idx, unit_.GetDexCache(), - class_loader, + unit_.GetClassLoader(), /* referrer */ nullptr, kVirtual); @@ -330,7 +327,7 @@ CompiledMethod* ArtCompileDEX( InvokeType invoke_type ATTRIBUTE_UNUSED, uint16_t class_def_idx, uint32_t method_idx, - jobject class_loader, + Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, DexToDexCompilationLevel dex_to_dex_compilation_level) { DCHECK(driver != nullptr); diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h index 0a00d45297..00c596d60e 100644 --- a/compiler/dex/dex_to_dex_compiler.h +++ b/compiler/dex/dex_to_dex_compiler.h @@ -18,6 +18,7 @@ #define ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_ #include "dex_file.h" +#include "handle.h" #include "invoke_type.h" namespace art { @@ -25,6 +26,10 @@ namespace art { class CompiledMethod; class CompilerDriver; +namespace mirror { +class ClassLoader; +} // namespace mirror + namespace optimizer { enum class DexToDexCompilationLevel { @@ -40,7 +45,7 @@ CompiledMethod* ArtCompileDEX(CompilerDriver* driver, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - jobject class_loader, + Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, DexToDexCompilationLevel dex_to_dex_compilation_level); diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index f296851ebf..582330611d 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -31,17 +31,12 @@ namespace art { -inline mirror::ClassLoader* CompilerDriver::GetClassLoader(const ScopedObjectAccess& soa, - const DexCompilationUnit* mUnit) { - return soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader()).Ptr(); -} - inline mirror::Class* CompilerDriver::ResolveClass( const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, dex::TypeIndex cls_index, const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); - DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit)); + DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); mirror::Class* cls = mUnit->GetClassLinker()->ResolveType( *mUnit->GetDexFile(), cls_index, dex_cache, class_loader); DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending()); @@ -56,7 +51,7 @@ inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass( const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); - DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit)); + DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); const DexFile::MethodId& referrer_method_id = mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex()); return ResolveClass(soa, dex_cache, class_loader, referrer_method_id.class_idx_, mUnit); @@ -87,7 +82,7 @@ inline ArtField* CompilerDriver::ResolveField( const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit, uint32_t field_idx, bool is_static) { - DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit)); + DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); return ResolveFieldWithDexFile(soa, dex_cache, class_loader, mUnit->GetDexFile(), field_idx, is_static); } @@ -139,7 +134,7 @@ inline ArtMethod* CompilerDriver::ResolveMethod( ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit, uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change) { - DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit)); + DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); ArtMethod* resolved_method = check_incompatible_class_change ? mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>( diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 26c0818b85..52ffa55342 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -580,7 +580,7 @@ static void CompileMethod(Thread* self, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - jobject class_loader, + Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level, bool compilation_enabled, @@ -621,9 +621,6 @@ static void CompileMethod(Thread* self, // Look-up the ArtMethod associated with this code_item (if any) // -- It is later used to lookup any [optimization] annotations for this method. ScopedObjectAccess soa(self); - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ClassLoader> class_loader_handle(hs.NewHandle( - soa.Decode<mirror::ClassLoader>(class_loader))); // TODO: Lookup annotation from DexFile directly without resolving method. ArtMethod* method = @@ -631,7 +628,7 @@ static void CompileMethod(Thread* self, dex_file, method_idx, dex_cache, - class_loader_handle, + class_loader, /* referrer */ nullptr, invoke_type); @@ -678,9 +675,14 @@ static void CompileMethod(Thread* self, if (compile) { // NOTE: if compiler declines to compile this method, it will return null. - compiled_method = driver->GetCompiler()->Compile(code_item, access_flags, invoke_type, - class_def_idx, method_idx, class_loader, - dex_file, dex_cache); + compiled_method = driver->GetCompiler()->Compile(code_item, + access_flags, + invoke_type, + class_def_idx, + method_idx, + class_loader, + dex_file, + dex_cache); } if (compiled_method == nullptr && dex_to_dex_compilation_level != optimizer::DexToDexCompilationLevel::kDontDexToDexCompile) { @@ -727,12 +729,14 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t uint32_t method_idx = method->GetDexMethodIndex(); uint32_t access_flags = method->GetAccessFlags(); InvokeType invoke_type = method->GetInvokeType(); - StackHandleScope<1> hs(self); + StackHandleScope<2> hs(self); Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache())); + Handle<mirror::ClassLoader> class_loader( + hs.NewHandle(method->GetDeclaringClass()->GetClassLoader())); { ScopedObjectAccessUnchecked soa(self); ScopedLocalRef<jobject> local_class_loader( - soa.Env(), soa.AddLocalReference<jobject>(method->GetDeclaringClass()->GetClassLoader())); + soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get())); jclass_loader = soa.Env()->NewGlobalRef(local_class_loader.get()); // Find the dex_file dex_file = method->GetDexFile(); @@ -766,7 +770,7 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t invoke_type, class_def_idx, method_idx, - jclass_loader, + class_loader, *dex_file, dex_to_dex_compilation_level, true, @@ -792,7 +796,7 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t invoke_type, class_def_idx, method_idx, - jclass_loader, + class_loader, *dex_file, dex_to_dex_compilation_level, true, @@ -1050,9 +1054,9 @@ bool CompilerDriver::IsMethodToCompile(const MethodReference& method_ref) const } bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_ref) const { - if (profile_compilation_info_ == nullptr) { - // If we miss profile information it means that we don't do a profile guided compilation. - // Return true, and let the other filters decide if the method should be compiled. + if (!CompilerFilter::DependsOnProfile(compiler_options_->GetCompilerFilter())) { + // Use the compiler filter instead of the presence of profile_compilation_info_ since + // we may want to have full speed compilation along with profile based layout optimizations. return true; } bool result = profile_compilation_info_->ContainsMethod(method_ref); @@ -1067,22 +1071,30 @@ bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_r class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { public: - explicit ResolveCatchBlockExceptionsClassVisitor( - std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve) - : exceptions_to_resolve_(exceptions_to_resolve) {} + ResolveCatchBlockExceptionsClassVisitor() : classes_() {} virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + classes_.push_back(c); + return true; + } + + void FindExceptionTypesToResolve( + std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve) + REQUIRES_SHARED(Locks::mutator_lock_) { const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - for (auto& m : c->GetMethods(pointer_size)) { - ResolveExceptionsForMethod(&m); + for (ObjPtr<mirror::Class> klass : classes_) { + for (ArtMethod& method : klass->GetMethods(pointer_size)) { + FindExceptionTypesToResolveForMethod(&method, exceptions_to_resolve); + } } - return true; } private: - void ResolveExceptionsForMethod(ArtMethod* method_handle) + void FindExceptionTypesToResolveForMethod( + ArtMethod* method, + std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = method_handle->GetCodeItem(); + const DexFile::CodeItem* code_item = method->GetCodeItem(); if (code_item == nullptr) { return; // native or abstract method } @@ -1102,9 +1114,9 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { dex::TypeIndex encoded_catch_handler_handlers_type_idx = dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list)); // Add to set of types to resolve if not already in the dex cache resolved types - if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) { - exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx, - method_handle->GetDexFile()); + if (!method->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) { + exceptions_to_resolve->emplace(encoded_catch_handler_handlers_type_idx, + method->GetDexFile()); } // ignore address associated with catch handler DecodeUnsignedLeb128(&encoded_catch_handler_list); @@ -1116,7 +1128,7 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { } } - std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve_; + std::vector<ObjPtr<mirror::Class>> classes_; }; class RecordImageClassesVisitor : public ClassVisitor { @@ -1170,8 +1182,14 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/lang/Throwable;"))); do { unresolved_exception_types.clear(); - ResolveCatchBlockExceptionsClassVisitor visitor(unresolved_exception_types); - class_linker->VisitClasses(&visitor); + { + // Thread suspension is not allowed while ResolveCatchBlockExceptionsClassVisitor + // is using a std::vector<ObjPtr<mirror::Class>>. + ScopedAssertNoThreadSuspension ants(__FUNCTION__); + ResolveCatchBlockExceptionsClassVisitor visitor; + class_linker->VisitClasses(&visitor); + visitor.FindExceptionTypesToResolve(&unresolved_exception_types); + } for (const auto& exception_type : unresolved_exception_types) { dex::TypeIndex exception_type_idx = exception_type.first; const DexFile* dex_file = exception_type.second; @@ -1422,19 +1440,14 @@ void CompilerDriver::MarkForDexToDexCompilation(Thread* self, const MethodRefere dex_to_dex_references_.back().GetMethodIndexes().SetBit(method_ref.dex_method_index); } -bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, - Handle<mirror::DexCache> dex_cache, - dex::TypeIndex type_idx) { - // Get type from dex cache assuming it was populated by the verifier - mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); +bool CompilerDriver::CanAccessTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class, + ObjPtr<mirror::Class> resolved_class) { if (resolved_class == nullptr) { stats_->TypeNeedsAccessCheck(); return false; // Unknown class needs access checks. } - const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx); bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible. if (!is_accessible) { - mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_); if (referrer_class == nullptr) { stats_->TypeNeedsAccessCheck(); return false; // Incomplete referrer knowledge needs access check. @@ -1451,12 +1464,9 @@ bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, return is_accessible; } -bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, - Handle<mirror::DexCache> dex_cache, - dex::TypeIndex type_idx, +bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class, + ObjPtr<mirror::Class> resolved_class, bool* finalizable) { - // Get type from dex cache assuming it was populated by the verifier. - mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); if (resolved_class == nullptr) { stats_->TypeNeedsAccessCheck(); // Be conservative. @@ -1464,10 +1474,8 @@ bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_id return false; // Unknown class needs access checks. } *finalizable = resolved_class->IsFinalizable(); - const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx); bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible. if (!is_accessible) { - mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_); if (referrer_class == nullptr) { stats_->TypeNeedsAccessCheck(); return false; // Incomplete referrer knowledge needs access check. @@ -1511,9 +1519,7 @@ ArtField* CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, mirror::Class* referrer_class; Handle<mirror::DexCache> dex_cache(mUnit->GetDexCache()); { - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ClassLoader> class_loader_handle( - hs.NewHandle(soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader()))); + Handle<mirror::ClassLoader> class_loader_handle = mUnit->GetClassLoader(); resolved_field = ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, false); referrer_class = resolved_field != nullptr ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader_handle, mUnit) : nullptr; @@ -2585,10 +2591,18 @@ class CompileClassVisitor : public CompilationVisitor { continue; } previous_direct_method_idx = method_idx; - CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(), - it.GetMethodInvokeType(class_def), class_def_index, - method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level, - compilation_enabled, dex_cache); + CompileMethod(soa.Self(), + driver, + it.GetMethodCodeItem(), + it.GetMethodAccessFlags(), + it.GetMethodInvokeType(class_def), + class_def_index, + method_idx, + class_loader, + dex_file, + dex_to_dex_compilation_level, + compilation_enabled, + dex_cache); it.Next(); } // Compile virtual methods @@ -2602,10 +2616,17 @@ class CompileClassVisitor : public CompilationVisitor { continue; } previous_virtual_method_idx = method_idx; - CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(), - it.GetMethodInvokeType(class_def), class_def_index, - method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level, - compilation_enabled, dex_cache); + CompileMethod(soa.Self(), + driver, it.GetMethodCodeItem(), + it.GetMethodAccessFlags(), + it.GetMethodInvokeType(class_def), + class_def_index, + method_idx, + class_loader, + dex_file, + dex_to_dex_compilation_level, + compilation_enabled, + dex_cache); it.Next(); } DCHECK(!it.HasNext()); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 5b4c751c4a..1e5c43d833 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -187,16 +187,14 @@ class CompilerDriver { REQUIRES(!requires_constructor_barrier_lock_); // Are runtime access checks necessary in the compiled code? - bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, - Handle<mirror::DexCache> dex_cache, - dex::TypeIndex type_idx) + bool CanAccessTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class, + ObjPtr<mirror::Class> resolved_class) REQUIRES_SHARED(Locks::mutator_lock_); // Are runtime access and instantiable checks necessary in the code? // out_is_finalizable is set to whether the type is finalizable. - bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, - Handle<mirror::DexCache> dex_cache, - dex::TypeIndex type_idx, + bool CanAccessInstantiableTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class, + ObjPtr<mirror::Class> resolved_class, bool* out_is_finalizable) REQUIRES_SHARED(Locks::mutator_lock_); @@ -370,10 +368,6 @@ class CompilerDriver { uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); - mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa, - const DexCompilationUnit* mUnit) - REQUIRES_SHARED(Locks::mutator_lock_); - private: void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files, diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 1e4ca16844..97954f3c29 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -101,6 +101,7 @@ class CompilerDriverTest : public CommonCompilerTest { }; // Disabled due to 10 second runtime on host +// TODO: Update the test for hash-based dex cache arrays. Bug: 30627598 TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) { CompileAll(nullptr); @@ -246,6 +247,11 @@ class CompilerDriverProfileTest : public CompilerDriverTest { return &profile_info_; } + CompilerFilter::Filter GetCompilerFilter() const OVERRIDE { + // Use a profile based filter. + return CompilerFilter::kSpeedProfile; + } + std::unordered_set<std::string> GetExpectedMethodsForClass(const std::string& clazz) { if (clazz == "Main") { return std::unordered_set<std::string>({ @@ -304,7 +310,6 @@ TEST_F(CompilerDriverProfileTest, ProfileGuidedCompilation) { // Need to enable dex-file writability. Methods rejected to be compiled will run through the // dex-to-dex compiler. - ProfileCompilationInfo info; for (const DexFile* dex_file : GetDexFiles(class_loader)) { ASSERT_TRUE(dex_file->EnableWrite()); } diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc index 47b19297e5..7e8e812c4a 100644 --- a/compiler/driver/dex_compilation_unit.cc +++ b/compiler/driver/dex_compilation_unit.cc @@ -21,7 +21,7 @@ namespace art { -DexCompilationUnit::DexCompilationUnit(jobject class_loader, +DexCompilationUnit::DexCompilationUnit(Handle<mirror::ClassLoader> class_loader, ClassLinker* class_linker, const DexFile& dex_file, const DexFile::CodeItem* code_item, diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h index 854927d747..24a9a5b653 100644 --- a/compiler/driver/dex_compilation_unit.h +++ b/compiler/driver/dex_compilation_unit.h @@ -34,7 +34,7 @@ class VerifiedMethod; class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> { public: - DexCompilationUnit(jobject class_loader, + DexCompilationUnit(Handle<mirror::ClassLoader> class_loader, ClassLinker* class_linker, const DexFile& dex_file, const DexFile::CodeItem* code_item, @@ -44,7 +44,7 @@ class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> { const VerifiedMethod* verified_method, Handle<mirror::DexCache> dex_cache); - jobject GetClassLoader() const { + Handle<mirror::ClassLoader> GetClassLoader() const { return class_loader_; } @@ -113,7 +113,7 @@ class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> { } private: - const jobject class_loader_; + const Handle<mirror::ClassLoader> class_loader_; ClassLinker* const class_linker_; @@ -125,7 +125,7 @@ class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> { const uint32_t access_flags_; const VerifiedMethod* verified_method_; - Handle<mirror::DexCache> dex_cache_; + const Handle<mirror::DexCache> dex_cache_; std::string symbol_; }; diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index d2dd30d8e6..117d1131b5 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -940,9 +940,11 @@ void ImageWriter::PruneNonImageClasses() { } ObjPtr<mirror::DexCache> dex_cache = self->DecodeJObject(data.weak_root)->AsDexCache(); for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { - Class* klass = dex_cache->GetResolvedType(dex::TypeIndex(i)); + mirror::TypeDexCachePair pair = + dex_cache->GetResolvedTypes()[i].load(std::memory_order_relaxed); + mirror::Class* klass = pair.object.Read(); if (klass != nullptr && !KeepClass(klass)) { - dex_cache->SetResolvedType(dex::TypeIndex(i), nullptr); + dex_cache->ClearResolvedType(dex::TypeIndex(pair.index)); } } ArtMethod** resolved_methods = dex_cache->GetResolvedMethods(); @@ -1922,8 +1924,7 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { // above comment for intern tables. ClassTable temp_class_table; temp_class_table.ReadFromMemory(class_table_memory_ptr); - CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u); - mirror::ClassLoader* class_loader = compile_app_image_ ? *class_loaders_.begin() : nullptr; + ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader(); CHECK_EQ(temp_class_table.NumZygoteClasses(class_loader), table->NumNonZygoteClasses(class_loader) + table->NumZygoteClasses(class_loader)); UnbufferedRootVisitor visitor(&root_visitor, RootInfo(kRootUnknown)); @@ -2213,7 +2214,7 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache, orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings, orig_dex_cache), ImageAddressVisitor(this)); } - GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes(); + mirror::TypeDexCacheType* orig_types = orig_dex_cache->GetResolvedTypes(); if (orig_types != nullptr) { copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedTypesOffset(), NativeLocationInImage(orig_types), diff --git a/compiler/image_writer.h b/compiler/image_writer.h index cc7df1ce21..bdc7146632 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -51,8 +51,13 @@ class ImageSpace; } // namespace space } // namespace gc +namespace mirror { +class ClassLoader; +} // namespace mirror + class ClassLoaderVisitor; class ClassTable; +class ImtConflictTable; static constexpr int kInvalidFd = -1; @@ -79,6 +84,11 @@ class ImageWriter FINAL { return true; } + ObjPtr<mirror::ClassLoader> GetClassLoader() { + CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u); + return compile_app_image_ ? *class_loaders_.begin() : nullptr; + } + template <typename T> T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) { if (object == nullptr || IsInBootImage(object)) { diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 7c0cdbf270..0ea11255a8 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -1060,6 +1060,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset, size_t relative_offset) SHARED_LOCK_FUNCTION(Locks::mutator_lock_) : OatDexMethodVisitor(writer, relative_offset), + class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr), out_(out), file_offset_(file_offset), soa_(Thread::Current()), @@ -1245,6 +1246,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { } private: + ObjPtr<mirror::ClassLoader> class_loader_; OutputStream* const out_; const size_t file_offset_; const ScopedObjectAccess soa_; @@ -1303,10 +1305,12 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { } mirror::Class* GetTargetType(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(writer_->HasImage()); ObjPtr<mirror::DexCache> dex_cache = GetDexCache(patch.TargetTypeDexFile()); - mirror::Class* type = dex_cache->GetResolvedType(patch.TargetTypeIndex()); + ObjPtr<mirror::Class> type = + ClassLinker::LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_); CHECK(type != nullptr); - return type; + return type.Ptr(); } mirror::String* GetTargetString(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index e4ad4222fb..3a4c9dbd16 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -54,7 +54,10 @@ class HGraphBuilder : public ValueObject { compiler_driver_(driver), compilation_stats_(compiler_stats), block_builder_(graph, dex_file, code_item), - ssa_builder_(graph, dex_compilation_unit->GetDexCache(), handles), + ssa_builder_(graph, + dex_compilation_unit->GetClassLoader(), + dex_compilation_unit->GetDexCache(), + handles), instruction_builder_(graph, &block_builder_, &ssa_builder_, @@ -80,10 +83,12 @@ class HGraphBuilder : public ValueObject { code_item_(code_item), dex_compilation_unit_(nullptr), compiler_driver_(nullptr), - null_dex_cache_(), compilation_stats_(nullptr), block_builder_(graph, nullptr, code_item), - ssa_builder_(graph, null_dex_cache_, handles), + ssa_builder_(graph, + handles->NewHandle<mirror::ClassLoader>(nullptr), + handles->NewHandle<mirror::DexCache>(nullptr), + handles), instruction_builder_(graph, &block_builder_, &ssa_builder_, @@ -96,7 +101,7 @@ class HGraphBuilder : public ValueObject { /* code_generator */ nullptr, /* interpreter_metadata */ nullptr, /* compiler_stats */ nullptr, - null_dex_cache_, + handles->NewHandle<mirror::DexCache>(nullptr), handles) {} GraphAnalysisResult BuildGraph(); @@ -117,8 +122,6 @@ class HGraphBuilder : public ValueObject { CompilerDriver* const compiler_driver_; - ScopedNullHandle<mirror::DexCache> null_dex_cache_; - OptimizingCompilerStats* compilation_stats_; HBasicBlockBuilder block_builder_; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 759a951d6b..7b84ef83cd 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -19,6 +19,7 @@ #include "arch/arm/instruction_set_features_arm.h" #include "art_method.h" #include "code_generator_utils.h" +#include "common_arm.h" #include "compiled_method.h" #include "entrypoints/quick/quick_entrypoints.h" #include "gc/accounting/card_table.h" @@ -1132,10 +1133,6 @@ class ReadBarrierForRootSlowPathARM : public SlowPathCodeARM { DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARM); }; -#undef __ -// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy. -#define __ down_cast<ArmAssembler*>(GetAssembler())-> // NOLINT - inline Condition ARMCondition(IfCondition cond) { switch (cond) { case kCondEQ: return EQ; @@ -1191,6 +1188,197 @@ inline Condition ARMFPCondition(IfCondition cond, bool gt_bias) { } } +inline Shift ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) { + switch (op_kind) { + case HDataProcWithShifterOp::kASR: return ASR; + case HDataProcWithShifterOp::kLSL: return LSL; + case HDataProcWithShifterOp::kLSR: return LSR; + default: + LOG(FATAL) << "Unexpected op kind " << op_kind; + UNREACHABLE(); + } +} + +static void GenerateDataProcInstruction(HInstruction::InstructionKind kind, + Register out, + Register first, + const ShifterOperand& second, + CodeGeneratorARM* codegen) { + if (second.IsImmediate() && second.GetImmediate() == 0) { + const ShifterOperand in = kind == HInstruction::kAnd + ? ShifterOperand(0) + : ShifterOperand(first); + + __ mov(out, in); + } else { + switch (kind) { + case HInstruction::kAdd: + __ add(out, first, second); + break; + case HInstruction::kAnd: + __ and_(out, first, second); + break; + case HInstruction::kOr: + __ orr(out, first, second); + break; + case HInstruction::kSub: + __ sub(out, first, second); + break; + case HInstruction::kXor: + __ eor(out, first, second); + break; + default: + LOG(FATAL) << "Unexpected instruction kind: " << kind; + UNREACHABLE(); + } + } +} + +static void GenerateDataProc(HInstruction::InstructionKind kind, + const Location& out, + const Location& first, + const ShifterOperand& second_lo, + const ShifterOperand& second_hi, + CodeGeneratorARM* codegen) { + const Register first_hi = first.AsRegisterPairHigh<Register>(); + const Register first_lo = first.AsRegisterPairLow<Register>(); + const Register out_hi = out.AsRegisterPairHigh<Register>(); + const Register out_lo = out.AsRegisterPairLow<Register>(); + + if (kind == HInstruction::kAdd) { + __ adds(out_lo, first_lo, second_lo); + __ adc(out_hi, first_hi, second_hi); + } else if (kind == HInstruction::kSub) { + __ subs(out_lo, first_lo, second_lo); + __ sbc(out_hi, first_hi, second_hi); + } else { + GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen); + GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen); + } +} + +static ShifterOperand GetShifterOperand(Register rm, Shift shift, uint32_t shift_imm) { + return shift_imm == 0 ? ShifterOperand(rm) : ShifterOperand(rm, shift, shift_imm); +} + +static void GenerateLongDataProc(HDataProcWithShifterOp* instruction, CodeGeneratorARM* codegen) { + DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong); + DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())); + + const LocationSummary* const locations = instruction->GetLocations(); + const uint32_t shift_value = instruction->GetShiftAmount(); + const HInstruction::InstructionKind kind = instruction->GetInstrKind(); + const Location first = locations->InAt(0); + const Location second = locations->InAt(1); + const Location out = locations->Out(); + const Register first_hi = first.AsRegisterPairHigh<Register>(); + const Register first_lo = first.AsRegisterPairLow<Register>(); + const Register out_hi = out.AsRegisterPairHigh<Register>(); + const Register out_lo = out.AsRegisterPairLow<Register>(); + const Register second_hi = second.AsRegisterPairHigh<Register>(); + const Register second_lo = second.AsRegisterPairLow<Register>(); + const Shift shift = ShiftFromOpKind(instruction->GetOpKind()); + + if (shift_value >= 32) { + if (shift == LSL) { + GenerateDataProcInstruction(kind, + out_hi, + first_hi, + ShifterOperand(second_lo, LSL, shift_value - 32), + codegen); + GenerateDataProcInstruction(kind, + out_lo, + first_lo, + ShifterOperand(0), + codegen); + } else if (shift == ASR) { + GenerateDataProc(kind, + out, + first, + GetShifterOperand(second_hi, ASR, shift_value - 32), + ShifterOperand(second_hi, ASR, 31), + codegen); + } else { + DCHECK_EQ(shift, LSR); + GenerateDataProc(kind, + out, + first, + GetShifterOperand(second_hi, LSR, shift_value - 32), + ShifterOperand(0), + codegen); + } + } else { + DCHECK_GT(shift_value, 1U); + DCHECK_LT(shift_value, 32U); + + if (shift == LSL) { + // We are not doing this for HInstruction::kAdd because the output will require + // Location::kOutputOverlap; not applicable to other cases. + if (kind == HInstruction::kOr || kind == HInstruction::kXor) { + GenerateDataProcInstruction(kind, + out_hi, + first_hi, + ShifterOperand(second_hi, LSL, shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_hi, + out_hi, + ShifterOperand(second_lo, LSR, 32 - shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_lo, + first_lo, + ShifterOperand(second_lo, LSL, shift_value), + codegen); + } else { + __ Lsl(IP, second_hi, shift_value); + __ orr(IP, IP, ShifterOperand(second_lo, LSR, 32 - shift_value)); + GenerateDataProc(kind, + out, + first, + ShifterOperand(second_lo, LSL, shift_value), + ShifterOperand(IP), + codegen); + } + } else { + DCHECK(shift == ASR || shift == LSR); + + // We are not doing this for HInstruction::kAdd because the output will require + // Location::kOutputOverlap; not applicable to other cases. + if (kind == HInstruction::kOr || kind == HInstruction::kXor) { + GenerateDataProcInstruction(kind, + out_lo, + first_lo, + ShifterOperand(second_lo, LSR, shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_lo, + out_lo, + ShifterOperand(second_hi, LSL, 32 - shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_hi, + first_hi, + ShifterOperand(second_hi, shift, shift_value), + codegen); + } else { + __ Lsr(IP, second_lo, shift_value); + __ orr(IP, IP, ShifterOperand(second_hi, LSL, 32 - shift_value)); + GenerateDataProc(kind, + out, + first, + ShifterOperand(IP), + ShifterOperand(second_hi, shift, shift_value), + codegen); + } + } + } +} + +#undef __ +// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy. +#define __ down_cast<ArmAssembler*>(GetAssembler())-> // NOLINT + void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const { stream << Register(reg); } @@ -6709,6 +6897,63 @@ void InstructionCodeGeneratorARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* } } +void LocationsBuilderARM::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* instruction) { + DCHECK(instruction->GetType() == Primitive::kPrimInt || + instruction->GetType() == Primitive::kPrimLong); + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + const bool overlap = instruction->GetType() == Primitive::kPrimLong && + HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind()); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), + overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* instruction) { + const LocationSummary* const locations = instruction->GetLocations(); + const HInstruction::InstructionKind kind = instruction->GetInstrKind(); + const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind(); + const Location left = locations->InAt(0); + const Location right = locations->InAt(1); + const Location out = locations->Out(); + + if (instruction->GetType() == Primitive::kPrimInt) { + DCHECK(!HDataProcWithShifterOp::IsExtensionOp(op_kind)); + + const Register second = instruction->InputAt(1)->GetType() == Primitive::kPrimLong + ? right.AsRegisterPairLow<Register>() + : right.AsRegister<Register>(); + + GenerateDataProcInstruction(kind, + out.AsRegister<Register>(), + left.AsRegister<Register>(), + ShifterOperand(second, + ShiftFromOpKind(op_kind), + instruction->GetShiftAmount()), + codegen_); + } else { + DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong); + + if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) { + const Register second = right.AsRegister<Register>(); + + DCHECK_NE(out.AsRegisterPairLow<Register>(), second); + GenerateDataProc(kind, + out, + left, + ShifterOperand(second), + ShifterOperand(second, ASR, 31), + codegen_); + } else { + GenerateLongDataProc(instruction, codegen_); + } + } +} + void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) { // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier). if (value == 0xffffffffu) { diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index e6032d2381..edccbd4904 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2277,8 +2277,8 @@ void InstructionCodeGeneratorARM64::VisitBitwiseNegatedRight(HBitwiseNegatedRigh } } -void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp( - HArm64DataProcWithShifterOp* instruction) { +void LocationsBuilderARM64::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* instruction) { DCHECK(instruction->GetType() == Primitive::kPrimInt || instruction->GetType() == Primitive::kPrimLong); LocationSummary* locations = @@ -2292,8 +2292,8 @@ void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp( locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } -void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp( - HArm64DataProcWithShifterOp* instruction) { +void InstructionCodeGeneratorARM64::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* instruction) { Primitive::Type type = instruction->GetType(); HInstruction::InstructionKind kind = instruction->GetInstrKind(); DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); @@ -2302,21 +2302,20 @@ void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp( if (kind != HInstruction::kNeg) { left = InputRegisterAt(instruction, 0); } - // If this `HArm64DataProcWithShifterOp` was created by merging a type conversion as the + // If this `HDataProcWithShifterOp` was created by merging a type conversion as the // shifter operand operation, the IR generating `right_reg` (input to the type // conversion) can have a different type from the current instruction's type, // so we manually indicate the type. Register right_reg = RegisterFrom(instruction->GetLocations()->InAt(1), type); - int64_t shift_amount = instruction->GetShiftAmount() & - (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance); - Operand right_operand(0); - HArm64DataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind(); - if (HArm64DataProcWithShifterOp::IsExtensionOp(op_kind)) { + HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind(); + if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) { right_operand = Operand(right_reg, helpers::ExtendFromOpKind(op_kind)); } else { - right_operand = Operand(right_reg, helpers::ShiftFromOpKind(op_kind), shift_amount); + right_operand = Operand(right_reg, + helpers::ShiftFromOpKind(op_kind), + instruction->GetShiftAmount()); } // Logical binary operations do not support extension operations in the diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 5c4ca5bc17..6bfbe4a9c9 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -1216,6 +1216,17 @@ inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) { } } +inline ShiftType ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) { + switch (op_kind) { + case HDataProcWithShifterOp::kASR: return ShiftType::ASR; + case HDataProcWithShifterOp::kLSL: return ShiftType::LSL; + case HDataProcWithShifterOp::kLSR: return ShiftType::LSR; + default: + LOG(FATAL) << "Unexpected op kind " << op_kind; + UNREACHABLE(); + } +} + void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const { stream << vixl32::Register(reg); } @@ -1260,6 +1271,185 @@ size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATT return 0; } +static void GenerateDataProcInstruction(HInstruction::InstructionKind kind, + vixl32::Register out, + vixl32::Register first, + const Operand& second, + CodeGeneratorARMVIXL* codegen) { + if (second.IsImmediate() && second.GetImmediate() == 0) { + const Operand in = kind == HInstruction::kAnd + ? Operand(0) + : Operand(first); + + __ Mov(out, in); + } else { + switch (kind) { + case HInstruction::kAdd: + __ Add(out, first, second); + break; + case HInstruction::kAnd: + __ And(out, first, second); + break; + case HInstruction::kOr: + __ Orr(out, first, second); + break; + case HInstruction::kSub: + __ Sub(out, first, second); + break; + case HInstruction::kXor: + __ Eor(out, first, second); + break; + default: + LOG(FATAL) << "Unexpected instruction kind: " << kind; + UNREACHABLE(); + } + } +} + +static void GenerateDataProc(HInstruction::InstructionKind kind, + const Location& out, + const Location& first, + const Operand& second_lo, + const Operand& second_hi, + CodeGeneratorARMVIXL* codegen) { + const vixl32::Register first_hi = HighRegisterFrom(first); + const vixl32::Register first_lo = LowRegisterFrom(first); + const vixl32::Register out_hi = HighRegisterFrom(out); + const vixl32::Register out_lo = LowRegisterFrom(out); + + if (kind == HInstruction::kAdd) { + __ Adds(out_lo, first_lo, second_lo); + __ Adc(out_hi, first_hi, second_hi); + } else if (kind == HInstruction::kSub) { + __ Subs(out_lo, first_lo, second_lo); + __ Sbc(out_hi, first_hi, second_hi); + } else { + GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen); + GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen); + } +} + +static Operand GetShifterOperand(vixl32::Register rm, ShiftType shift, uint32_t shift_imm) { + return shift_imm == 0 ? Operand(rm) : Operand(rm, shift, shift_imm); +} + +static void GenerateLongDataProc(HDataProcWithShifterOp* instruction, + CodeGeneratorARMVIXL* codegen) { + DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong); + DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())); + + const LocationSummary* const locations = instruction->GetLocations(); + const uint32_t shift_value = instruction->GetShiftAmount(); + const HInstruction::InstructionKind kind = instruction->GetInstrKind(); + const Location first = locations->InAt(0); + const Location second = locations->InAt(1); + const Location out = locations->Out(); + const vixl32::Register first_hi = HighRegisterFrom(first); + const vixl32::Register first_lo = LowRegisterFrom(first); + const vixl32::Register out_hi = HighRegisterFrom(out); + const vixl32::Register out_lo = LowRegisterFrom(out); + const vixl32::Register second_hi = HighRegisterFrom(second); + const vixl32::Register second_lo = LowRegisterFrom(second); + const ShiftType shift = ShiftFromOpKind(instruction->GetOpKind()); + + if (shift_value >= 32) { + if (shift == ShiftType::LSL) { + GenerateDataProcInstruction(kind, + out_hi, + first_hi, + Operand(second_lo, ShiftType::LSL, shift_value - 32), + codegen); + GenerateDataProcInstruction(kind, out_lo, first_lo, 0, codegen); + } else if (shift == ShiftType::ASR) { + GenerateDataProc(kind, + out, + first, + GetShifterOperand(second_hi, ShiftType::ASR, shift_value - 32), + Operand(second_hi, ShiftType::ASR, 31), + codegen); + } else { + DCHECK_EQ(shift, ShiftType::LSR); + GenerateDataProc(kind, + out, + first, + GetShifterOperand(second_hi, ShiftType::LSR, shift_value - 32), + 0, + codegen); + } + } else { + DCHECK_GT(shift_value, 1U); + DCHECK_LT(shift_value, 32U); + + UseScratchRegisterScope temps(codegen->GetVIXLAssembler()); + + if (shift == ShiftType::LSL) { + // We are not doing this for HInstruction::kAdd because the output will require + // Location::kOutputOverlap; not applicable to other cases. + if (kind == HInstruction::kOr || kind == HInstruction::kXor) { + GenerateDataProcInstruction(kind, + out_hi, + first_hi, + Operand(second_hi, ShiftType::LSL, shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_hi, + out_hi, + Operand(second_lo, ShiftType::LSR, 32 - shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_lo, + first_lo, + Operand(second_lo, ShiftType::LSL, shift_value), + codegen); + } else { + const vixl32::Register temp = temps.Acquire(); + + __ Lsl(temp, second_hi, shift_value); + __ Orr(temp, temp, Operand(second_lo, ShiftType::LSR, 32 - shift_value)); + GenerateDataProc(kind, + out, + first, + Operand(second_lo, ShiftType::LSL, shift_value), + temp, + codegen); + } + } else { + DCHECK(shift == ShiftType::ASR || shift == ShiftType::LSR); + + // We are not doing this for HInstruction::kAdd because the output will require + // Location::kOutputOverlap; not applicable to other cases. + if (kind == HInstruction::kOr || kind == HInstruction::kXor) { + GenerateDataProcInstruction(kind, + out_lo, + first_lo, + Operand(second_lo, ShiftType::LSR, shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_lo, + out_lo, + Operand(second_hi, ShiftType::LSL, 32 - shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_hi, + first_hi, + Operand(second_hi, shift, shift_value), + codegen); + } else { + const vixl32::Register temp = temps.Acquire(); + + __ Lsr(temp, second_lo, shift_value); + __ Orr(temp, temp, Operand(second_hi, ShiftType::LSL, 32 - shift_value)); + GenerateDataProc(kind, + out, + first, + temp, + Operand(second_hi, shift, shift_value), + codegen); + } + } + } +} + #undef __ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, @@ -6781,6 +6971,60 @@ void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRi } } +void LocationsBuilderARMVIXL::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* instruction) { + DCHECK(instruction->GetType() == Primitive::kPrimInt || + instruction->GetType() == Primitive::kPrimLong); + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + const bool overlap = instruction->GetType() == Primitive::kPrimLong && + HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind()); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), + overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARMVIXL::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* instruction) { + const LocationSummary* const locations = instruction->GetLocations(); + const HInstruction::InstructionKind kind = instruction->GetInstrKind(); + const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind(); + + if (instruction->GetType() == Primitive::kPrimInt) { + DCHECK(!HDataProcWithShifterOp::IsExtensionOp(op_kind)); + + const vixl32::Register second = instruction->InputAt(1)->GetType() == Primitive::kPrimLong + ? LowRegisterFrom(locations->InAt(1)) + : InputRegisterAt(instruction, 1); + + GenerateDataProcInstruction(kind, + OutputRegister(instruction), + InputRegisterAt(instruction, 0), + Operand(second, + ShiftFromOpKind(op_kind), + instruction->GetShiftAmount()), + codegen_); + } else { + DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong); + + if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) { + const vixl32::Register second = InputRegisterAt(instruction, 1); + + DCHECK(!LowRegisterFrom(locations->Out()).Is(second)); + GenerateDataProc(kind, + locations->Out(), + locations->InAt(0), + second, + Operand(second, ShiftType::ASR, 31), + codegen_); + } else { + GenerateLongDataProc(instruction, codegen_); + } + } +} + // TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl. void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out, vixl32::Register first, diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 09612c8dbf..b779aed763 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -5262,7 +5262,7 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { // Branch cases into compressed and uncompressed for each index's type. uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); NearLabel done, not_compressed; - __ testl(Address(obj, count_offset), Immediate(1)); + __ testb(Address(obj, count_offset), Immediate(1)); codegen_->MaybeRecordImplicitNullCheck(instruction); static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, "Expecting 0=compressed, 1=uncompressed"); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 0879992e32..179bf6d3d1 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -4720,7 +4720,7 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { // Branch cases into compressed and uncompressed for each index's type. uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); NearLabel done, not_compressed; - __ testl(Address(obj, count_offset), Immediate(1)); + __ testb(Address(obj, count_offset), Immediate(1)); codegen_->MaybeRecordImplicitNullCheck(instruction); static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, "Expecting 0=compressed, 1=uncompressed"); diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h index ecb86875d6..e184745520 100644 --- a/compiler/optimizing/common_arm.h +++ b/compiler/optimizing/common_arm.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM_H_ #define ART_COMPILER_OPTIMIZING_COMMON_ARM_H_ +#include "instruction_simplifier_shared.h" #include "debug/dwarf/register.h" #include "locations.h" #include "nodes.h" @@ -29,6 +30,9 @@ #pragma GCC diagnostic pop namespace art { + +using helpers::HasShifterOperand; + namespace arm { namespace helpers { @@ -218,6 +222,14 @@ inline Location LocationFrom(const vixl::aarch32::SRegister& low, return Location::FpuRegisterPairLocation(low.GetCode(), high.GetCode()); } +inline bool ShifterOperandSupportsExtension(HInstruction* instruction) { + DCHECK(HasShifterOperand(instruction, kArm)); + // TODO: HAdd applied to the other integral types could make use of + // the SXTAB, SXTAH, UXTAB and UXTAH instructions. + return instruction->GetType() == Primitive::kPrimLong && + (instruction->IsAdd() || instruction->IsSub()); +} + } // namespace helpers } // namespace arm } // namespace art diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h index 93ea090583..d3f431e327 100644 --- a/compiler/optimizing/common_arm64.h +++ b/compiler/optimizing/common_arm64.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_ #include "code_generator.h" +#include "instruction_simplifier_shared.h" #include "locations.h" #include "nodes.h" #include "utils/arm64/assembler_arm64.h" @@ -31,6 +32,10 @@ #pragma GCC diagnostic pop namespace art { + +using helpers::CanFitInShifterOperand; +using helpers::HasShifterOperand; + namespace arm64 { namespace helpers { @@ -290,11 +295,11 @@ inline bool ArtVixlRegCodeCoherentForRegSet(uint32_t art_core_registers, return true; } -inline vixl::aarch64::Shift ShiftFromOpKind(HArm64DataProcWithShifterOp::OpKind op_kind) { +inline vixl::aarch64::Shift ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) { switch (op_kind) { - case HArm64DataProcWithShifterOp::kASR: return vixl::aarch64::ASR; - case HArm64DataProcWithShifterOp::kLSL: return vixl::aarch64::LSL; - case HArm64DataProcWithShifterOp::kLSR: return vixl::aarch64::LSR; + case HDataProcWithShifterOp::kASR: return vixl::aarch64::ASR; + case HDataProcWithShifterOp::kLSL: return vixl::aarch64::LSL; + case HDataProcWithShifterOp::kLSR: return vixl::aarch64::LSR; default: LOG(FATAL) << "Unexpected op kind " << op_kind; UNREACHABLE(); @@ -302,14 +307,14 @@ inline vixl::aarch64::Shift ShiftFromOpKind(HArm64DataProcWithShifterOp::OpKind } } -inline vixl::aarch64::Extend ExtendFromOpKind(HArm64DataProcWithShifterOp::OpKind op_kind) { +inline vixl::aarch64::Extend ExtendFromOpKind(HDataProcWithShifterOp::OpKind op_kind) { switch (op_kind) { - case HArm64DataProcWithShifterOp::kUXTB: return vixl::aarch64::UXTB; - case HArm64DataProcWithShifterOp::kUXTH: return vixl::aarch64::UXTH; - case HArm64DataProcWithShifterOp::kUXTW: return vixl::aarch64::UXTW; - case HArm64DataProcWithShifterOp::kSXTB: return vixl::aarch64::SXTB; - case HArm64DataProcWithShifterOp::kSXTH: return vixl::aarch64::SXTH; - case HArm64DataProcWithShifterOp::kSXTW: return vixl::aarch64::SXTW; + case HDataProcWithShifterOp::kUXTB: return vixl::aarch64::UXTB; + case HDataProcWithShifterOp::kUXTH: return vixl::aarch64::UXTH; + case HDataProcWithShifterOp::kUXTW: return vixl::aarch64::UXTW; + case HDataProcWithShifterOp::kSXTB: return vixl::aarch64::SXTB; + case HDataProcWithShifterOp::kSXTH: return vixl::aarch64::SXTH; + case HDataProcWithShifterOp::kSXTW: return vixl::aarch64::SXTW; default: LOG(FATAL) << "Unexpected op kind " << op_kind; UNREACHABLE(); @@ -317,31 +322,8 @@ inline vixl::aarch64::Extend ExtendFromOpKind(HArm64DataProcWithShifterOp::OpKin } } -inline bool CanFitInShifterOperand(HInstruction* instruction) { - if (instruction->IsTypeConversion()) { - HTypeConversion* conversion = instruction->AsTypeConversion(); - Primitive::Type result_type = conversion->GetResultType(); - Primitive::Type input_type = conversion->GetInputType(); - // We don't expect to see the same type as input and result. - return Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type) && - (result_type != input_type); - } else { - return (instruction->IsShl() && instruction->AsShl()->InputAt(1)->IsIntConstant()) || - (instruction->IsShr() && instruction->AsShr()->InputAt(1)->IsIntConstant()) || - (instruction->IsUShr() && instruction->AsUShr()->InputAt(1)->IsIntConstant()); - } -} - -inline bool HasShifterOperand(HInstruction* instr) { - // `neg` instructions are an alias of `sub` using the zero register as the - // first register input. - bool res = instr->IsAdd() || instr->IsAnd() || instr->IsNeg() || - instr->IsOr() || instr->IsSub() || instr->IsXor(); - return res; -} - inline bool ShifterOperandSupportsExtension(HInstruction* instruction) { - DCHECK(HasShifterOperand(instruction)); + DCHECK(HasShifterOperand(instruction, kArm64)); // Although the `neg` instruction is an alias of the `sub` instruction, `HNeg` // does *not* support extension. This is because the `extended register` form // of the `sub` instruction interprets the left register with code 31 as the diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index f6fba883bd..2bf5c53e17 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -511,12 +511,10 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { void VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetOpKind(); } -#endif -#ifdef ART_ENABLE_CODEGEN_arm64 - void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE { + void VisitDataProcWithShifterOp(HDataProcWithShifterOp* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind(); - if (HArm64DataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) { + if (HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) { StartAttributeStream("shift") << instruction->GetShiftAmount(); } } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index e012a4287f..8c73f1d036 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -192,9 +192,9 @@ static uint32_t FindMethodIndexIn(ArtMethod* method, } static dex::TypeIndex FindClassIndexIn(mirror::Class* cls, - const DexFile& dex_file, - Handle<mirror::DexCache> dex_cache) + const DexCompilationUnit& compilation_unit) REQUIRES_SHARED(Locks::mutator_lock_) { + const DexFile& dex_file = *compilation_unit.GetDexFile(); dex::TypeIndex index; if (cls->GetDexCache() == nullptr) { DCHECK(cls->IsArrayClass()) << cls->PrettyClass(); @@ -203,22 +203,19 @@ static dex::TypeIndex FindClassIndexIn(mirror::Class* cls, DCHECK(cls->IsProxyClass()) << cls->PrettyClass(); // TODO: deal with proxy classes. } else if (IsSameDexFile(cls->GetDexFile(), dex_file)) { - DCHECK_EQ(cls->GetDexCache(), dex_cache.Get()); + DCHECK_EQ(cls->GetDexCache(), compilation_unit.GetDexCache().Get()); index = cls->GetDexTypeIndex(); - // Update the dex cache to ensure the class is in. The generated code will - // consider it is. We make it safe by updating the dex cache, as other - // dex files might also load the class, and there is no guarantee the dex - // cache of the dex file of the class will be updated. - if (dex_cache->GetResolvedType(index) == nullptr) { - dex_cache->SetResolvedType(index, cls); - } } else { index = cls->FindTypeIndexInOtherDexFile(dex_file); - // We cannot guarantee the entry in the dex cache will resolve to the same class, + // We cannot guarantee the entry will resolve to the same class, // as there may be different class loaders. So only return the index if it's - // the right class in the dex cache already. - if (index.IsValid() && dex_cache->GetResolvedType(index) != cls) { - index = dex::TypeIndex::Invalid(); + // the right class already resolved with the class loader. + if (index.IsValid()) { + ObjPtr<mirror::Class> resolved = ClassLinker::LookupResolvedType( + index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); + if (resolved != cls) { + index = dex::TypeIndex::Invalid(); + } } } @@ -445,9 +442,8 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface()) << invoke_instruction->DebugName(); - const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); dex::TypeIndex class_index = FindClassIndexIn( - GetMonomorphicType(classes), caller_dex_file, caller_compilation_unit_.GetDexCache()); + GetMonomorphicType(classes), caller_compilation_unit_); if (!class_index.IsValid()) { VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method) << " from inline cache is not inlined because its class is not" @@ -490,6 +486,7 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, // Run type propagation to get the guard typed, and eventually propagate the // type of the receiver. ReferenceTypePropagation rtp_fixup(graph_, + outer_compilation_unit_.GetClassLoader(), outer_compilation_unit_.GetDexCache(), handles_, /* is_first_run */ false); @@ -583,7 +580,6 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); PointerSize pointer_size = class_linker->GetImagePointerSize(); - const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); bool all_targets_inlined = true; bool one_target_inlined = false; @@ -605,8 +601,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - dex::TypeIndex class_index = FindClassIndexIn( - handle.Get(), caller_dex_file, caller_compilation_unit_.GetDexCache()); + dex::TypeIndex class_index = FindClassIndexIn(handle.Get(), caller_compilation_unit_); HInstruction* return_replacement = nullptr; if (!class_index.IsValid() || !TryBuildAndInline(invoke_instruction, @@ -662,6 +657,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, // Run type propagation to get the guards typed. ReferenceTypePropagation rtp_fixup(graph_, + outer_compilation_unit_.GetClassLoader(), outer_compilation_unit_.GetDexCache(), handles_, /* is_first_run */ false); @@ -855,6 +851,7 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget( // Run type propagation to get the guard typed. ReferenceTypePropagation rtp_fixup(graph_, + outer_compilation_unit_.GetClassLoader(), outer_compilation_unit_.GetDexCache(), handles_, /* is_first_run */ false); @@ -923,6 +920,7 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, // Actual return value has a more specific type than the method's declared // return type. Run RTP again on the outer graph to propagate it. ReferenceTypePropagation(graph_, + outer_compilation_unit_.GetClassLoader(), outer_compilation_unit_.GetDexCache(), handles_, /* is_first_run */ false).Run(); @@ -1175,7 +1173,11 @@ HInstanceFieldGet* HInliner::CreateInstanceFieldGet(Handle<mirror::DexCache> dex /* dex_pc */ 0); if (iget->GetType() == Primitive::kPrimNot) { // Use the same dex_cache that we used for field lookup as the hint_dex_cache. - ReferenceTypePropagation rtp(graph_, dex_cache, handles_, /* is_first_run */ false); + ReferenceTypePropagation rtp(graph_, + outer_compilation_unit_.GetClassLoader(), + dex_cache, + handles_, + /* is_first_run */ false); rtp.Visit(iget); } return iget; @@ -1221,7 +1223,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, resolved_method->GetDeclaringClass()->GetClassLoader())); DexCompilationUnit dex_compilation_unit( - class_loader.ToJObject(), + class_loader, class_linker, callee_dex_file, code_item, @@ -1338,6 +1340,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, // are more specific than the declared ones, run RTP again on the inner graph. if (run_rtp || ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) { ReferenceTypePropagation(callee_graph, + outer_compilation_unit_.GetClassLoader(), dex_compilation_unit.GetDexCache(), handles_, /* is_first_run */ false).Run(); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 3374e42955..c60f6e5393 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -669,11 +669,10 @@ static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) { ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<3> hs(soa.Self()); + StackHandleScope<2> hs(soa.Self()); ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker(); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); + Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass())); // We fetch the referenced class eagerly (that is, the class pointed by in the MethodId // at method_idx), as `CanAccessResolvedMethod` expects it be be in the dex cache. @@ -1260,9 +1259,7 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio static mirror::Class* GetClassFrom(CompilerDriver* driver, const DexCompilationUnit& compilation_unit) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader>(compilation_unit.GetClassLoader()))); + Handle<mirror::ClassLoader> class_loader = compilation_unit.GetClassLoader(); Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache(); return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit); @@ -1278,10 +1275,9 @@ mirror::Class* HInstructionBuilder::GetCompilingClass() const { bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) const { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<3> hs(soa.Self()); + StackHandleScope<2> hs(soa.Self()); Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); + Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass( soa, dex_cache, class_loader, type_index, dex_compilation_unit_))); Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); @@ -1317,8 +1313,7 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, StackHandleScope<2> hs(soa.Self()); ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker(); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); + Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass())); ArtField* resolved_field = class_linker->ResolveField(*dex_compilation_unit_->GetDexFile(), @@ -1635,10 +1630,8 @@ static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader()))); + Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader(); Handle<mirror::Class> klass = handles_->NewHandle(compiler_driver_->ResolveClass( soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_)); @@ -1722,17 +1715,9 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, } } -bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, - Handle<mirror::DexCache> dex_cache, - bool* finalizable) const { - return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks( - dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index, finalizable); -} - bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, bool* finalizable) const { - ScopedObjectAccess soa(Thread::Current()); - Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); - return NeedsAccessCheck(type_index, dex_cache, finalizable); + return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks( + LookupReferrerClass(), LookupResolvedType(type_index, *dex_compilation_unit_), finalizable); } bool HInstructionBuilder::CanDecodeQuickenedInfo() const { @@ -2772,4 +2757,18 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, return true; } // NOLINT(readability/fn_size) +ObjPtr<mirror::Class> HInstructionBuilder::LookupResolvedType( + dex::TypeIndex type_index, + const DexCompilationUnit& compilation_unit) const { + return ClassLinker::LookupResolvedType( + type_index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); +} + +ObjPtr<mirror::Class> HInstructionBuilder::LookupReferrerClass() const { + // TODO: Cache the result in a Handle<mirror::Class>. + const DexFile::MethodId& method_id = + dex_compilation_unit_->GetDexFile()->GetMethodId(dex_compilation_unit_->GetDexMethodIndex()); + return LookupResolvedType(method_id.class_idx_, *dex_compilation_unit_); +} + } // namespace art diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 3bb680ce44..e735a0c46d 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -106,11 +106,8 @@ class HInstructionBuilder : public ValueObject { // Returns whether the current method needs access check for the type. // Output parameter finalizable is set to whether the type is finalizable. - bool NeedsAccessCheck(dex::TypeIndex type_index, - Handle<mirror::DexCache> dex_cache, - /*out*/bool* finalizable) const + bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const REQUIRES_SHARED(Locks::mutator_lock_); - bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const; template<typename T> void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc); @@ -300,6 +297,12 @@ class HInstructionBuilder : public ValueObject { // be found. ArtField* ResolveField(uint16_t field_idx, bool is_static, bool is_put); + ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_index, + const DexCompilationUnit& compilation_unit) const + REQUIRES_SHARED(Locks::mutator_lock_); + + ObjPtr<mirror::Class> LookupReferrerClass() const REQUIRES_SHARED(Locks::mutator_lock_); + ArenaAllocator* const arena_; HGraph* const graph_; VariableSizedHandleScope* handles_; diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc index 56e4c7a9c2..5f5e29b024 100644 --- a/compiler/optimizing/instruction_simplifier_arm.cc +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -15,23 +15,124 @@ */ #include "code_generator.h" +#include "common_arm.h" #include "instruction_simplifier_arm.h" #include "instruction_simplifier_shared.h" #include "mirror/array-inl.h" +#include "nodes.h" namespace art { + +using helpers::CanFitInShifterOperand; +using helpers::HasShifterOperand; + namespace arm { -void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) { - if (TryCombineMultiplyAccumulate(instruction, kArm)) { +using helpers::ShifterOperandSupportsExtension; + +bool InstructionSimplifierArmVisitor::TryMergeIntoShifterOperand(HInstruction* use, + HInstruction* bitfield_op, + bool do_merge) { + DCHECK(HasShifterOperand(use, kArm)); + DCHECK(use->IsBinaryOperation()); + DCHECK(CanFitInShifterOperand(bitfield_op)); + DCHECK(!bitfield_op->HasEnvironmentUses()); + + Primitive::Type type = use->GetType(); + if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) { + return false; + } + + HInstruction* left = use->InputAt(0); + HInstruction* right = use->InputAt(1); + DCHECK(left == bitfield_op || right == bitfield_op); + + if (left == right) { + // TODO: Handle special transformations in this situation? + // For example should we transform `(x << 1) + (x << 1)` into `(x << 2)`? + // Or should this be part of a separate transformation logic? + return false; + } + + bool is_commutative = use->AsBinaryOperation()->IsCommutative(); + HInstruction* other_input; + if (bitfield_op == right) { + other_input = left; + } else { + if (is_commutative) { + other_input = right; + } else { + return false; + } + } + + HDataProcWithShifterOp::OpKind op_kind; + int shift_amount = 0; + + HDataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount); + shift_amount &= use->GetType() == Primitive::kPrimInt + ? kMaxIntShiftDistance + : kMaxLongShiftDistance; + + if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) { + if (!ShifterOperandSupportsExtension(use)) { + return false; + } + // Shift by 1 is a special case that results in the same number and type of instructions + // as this simplification, but potentially shorter code. + } else if (type == Primitive::kPrimLong && shift_amount == 1) { + return false; + } + + if (do_merge) { + HDataProcWithShifterOp* alu_with_op = + new (GetGraph()->GetArena()) HDataProcWithShifterOp(use, + other_input, + bitfield_op->InputAt(0), + op_kind, + shift_amount, + use->GetDexPc()); + use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op); + if (bitfield_op->GetUses().empty()) { + bitfield_op->GetBlock()->RemoveInstruction(bitfield_op); + } RecordSimplification(); } + + return true; } -void InstructionSimplifierArmVisitor::VisitOr(HOr* instruction) { - if (TryMergeNegatedInput(instruction)) { - RecordSimplification(); +// Merge a bitfield move instruction into its uses if it can be merged in all of them. +bool InstructionSimplifierArmVisitor::TryMergeIntoUsersShifterOperand(HInstruction* bitfield_op) { + DCHECK(CanFitInShifterOperand(bitfield_op)); + + if (bitfield_op->HasEnvironmentUses()) { + return false; + } + + const HUseList<HInstruction*>& uses = bitfield_op->GetUses(); + + // Check whether we can merge the instruction in all its users' shifter operand. + for (const HUseListNode<HInstruction*>& use : uses) { + HInstruction* user = use.GetUser(); + if (!HasShifterOperand(user, kArm)) { + return false; + } + if (!CanMergeIntoShifterOperand(user, bitfield_op)) { + return false; + } } + + // Merge the instruction into its uses. + for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) { + HInstruction* user = it->GetUser(); + // Increment `it` now because `*it` will disappear thanks to MergeIntoShifterOperand(). + ++it; + bool merged = MergeIntoShifterOperand(user, bitfield_op); + DCHECK(merged); + } + + return true; } void InstructionSimplifierArmVisitor::VisitAnd(HAnd* instruction) { @@ -89,5 +190,49 @@ void InstructionSimplifierArmVisitor::VisitArraySet(HArraySet* instruction) { } } +void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) { + if (TryCombineMultiplyAccumulate(instruction, kArm)) { + RecordSimplification(); + } +} + +void InstructionSimplifierArmVisitor::VisitOr(HOr* instruction) { + if (TryMergeNegatedInput(instruction)) { + RecordSimplification(); + } +} + +void InstructionSimplifierArmVisitor::VisitShl(HShl* instruction) { + if (instruction->InputAt(1)->IsConstant()) { + TryMergeIntoUsersShifterOperand(instruction); + } +} + +void InstructionSimplifierArmVisitor::VisitShr(HShr* instruction) { + if (instruction->InputAt(1)->IsConstant()) { + TryMergeIntoUsersShifterOperand(instruction); + } +} + +void InstructionSimplifierArmVisitor::VisitTypeConversion(HTypeConversion* instruction) { + Primitive::Type result_type = instruction->GetResultType(); + Primitive::Type input_type = instruction->GetInputType(); + + if (input_type == result_type) { + // We let the arch-independent code handle this. + return; + } + + if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) { + TryMergeIntoUsersShifterOperand(instruction); + } +} + +void InstructionSimplifierArmVisitor::VisitUShr(HUShr* instruction) { + if (instruction->InputAt(1)->IsConstant()) { + TryMergeIntoUsersShifterOperand(instruction); + } +} + } // namespace arm } // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h index 9b54511340..e2ed257777 100644 --- a/compiler/optimizing/instruction_simplifier_arm.h +++ b/compiler/optimizing/instruction_simplifier_arm.h @@ -35,11 +35,41 @@ class InstructionSimplifierArmVisitor : public HGraphVisitor { } } - void VisitMul(HMul* instruction) OVERRIDE; - void VisitOr(HOr* instruction) OVERRIDE; + bool TryMergeIntoUsersShifterOperand(HInstruction* instruction); + bool TryMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op, bool do_merge); + bool CanMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) { + return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ false); + } + bool MergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) { + DCHECK(CanMergeIntoShifterOperand(use, bitfield_op)); + return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ true); + } + + /** + * This simplifier uses a special-purpose BB visitor. + * (1) No need to visit Phi nodes. + * (2) Since statements can be removed in a "forward" fashion, + * the visitor should test if each statement is still there. + */ + void VisitBasicBlock(HBasicBlock* block) OVERRIDE { + // TODO: fragile iteration, provide more robust iterators? + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { + HInstruction* instruction = it.Current(); + if (instruction->IsInBlock()) { + instruction->Accept(this); + } + } + } + void VisitAnd(HAnd* instruction) OVERRIDE; void VisitArrayGet(HArrayGet* instruction) OVERRIDE; void VisitArraySet(HArraySet* instruction) OVERRIDE; + void VisitMul(HMul* instruction) OVERRIDE; + void VisitOr(HOr* instruction) OVERRIDE; + void VisitShl(HShl* instruction) OVERRIDE; + void VisitShr(HShr* instruction) OVERRIDE; + void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE; + void VisitUShr(HUShr* instruction) OVERRIDE; OptimizingCompilerStats* stats_; }; diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 6d107d571f..73b7b2bd95 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -22,16 +22,18 @@ #include "mirror/string.h" namespace art { -namespace arm64 { using helpers::CanFitInShifterOperand; using helpers::HasShifterOperand; + +namespace arm64 { + using helpers::ShifterOperandSupportsExtension; bool InstructionSimplifierArm64Visitor::TryMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op, bool do_merge) { - DCHECK(HasShifterOperand(use)); + DCHECK(HasShifterOperand(use, kArm64)); DCHECK(use->IsBinaryOperation() || use->IsNeg()); DCHECK(CanFitInShifterOperand(bitfield_op)); DCHECK(!bitfield_op->HasEnvironmentUses()); @@ -72,23 +74,22 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoShifterOperand(HInstruction* } } - HArm64DataProcWithShifterOp::OpKind op_kind; + HDataProcWithShifterOp::OpKind op_kind; int shift_amount = 0; - HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount); + HDataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount); - if (HArm64DataProcWithShifterOp::IsExtensionOp(op_kind) && - !ShifterOperandSupportsExtension(use)) { + if (HDataProcWithShifterOp::IsExtensionOp(op_kind) && !ShifterOperandSupportsExtension(use)) { return false; } if (do_merge) { - HArm64DataProcWithShifterOp* alu_with_op = - new (GetGraph()->GetArena()) HArm64DataProcWithShifterOp(use, - other_input, - bitfield_op->InputAt(0), - op_kind, - shift_amount, - use->GetDexPc()); + HDataProcWithShifterOp* alu_with_op = + new (GetGraph()->GetArena()) HDataProcWithShifterOp(use, + other_input, + bitfield_op->InputAt(0), + op_kind, + shift_amount, + use->GetDexPc()); use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op); if (bitfield_op->GetUses().empty()) { bitfield_op->GetBlock()->RemoveInstruction(bitfield_op); @@ -112,7 +113,7 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruc // Check whether we can merge the instruction in all its users' shifter operand. for (const HUseListNode<HInstruction*>& use : uses) { HInstruction* user = use.GetUser(); - if (!HasShifterOperand(user)) { + if (!HasShifterOperand(user, kArm64)) { return false; } if (!CanMergeIntoShifterOperand(user, bitfield_op)) { diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index d4cb1f14b7..65654f50f4 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -40,11 +40,11 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { HInstruction* bitfield_op, bool do_merge); bool CanMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) { - return TryMergeIntoShifterOperand(use, bitfield_op, false); + return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ false); } bool MergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) { DCHECK(CanMergeIntoShifterOperand(use, bitfield_op)); - return TryMergeIntoShifterOperand(use, bitfield_op, true); + return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ true); } /** diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h index 56804f5e90..83e3ffca57 100644 --- a/compiler/optimizing/instruction_simplifier_shared.h +++ b/compiler/optimizing/instruction_simplifier_shared.h @@ -21,6 +21,33 @@ namespace art { +namespace helpers { + +inline bool CanFitInShifterOperand(HInstruction* instruction) { + if (instruction->IsTypeConversion()) { + HTypeConversion* conversion = instruction->AsTypeConversion(); + Primitive::Type result_type = conversion->GetResultType(); + Primitive::Type input_type = conversion->GetInputType(); + // We don't expect to see the same type as input and result. + return Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type) && + (result_type != input_type); + } else { + return (instruction->IsShl() && instruction->AsShl()->InputAt(1)->IsIntConstant()) || + (instruction->IsShr() && instruction->AsShr()->InputAt(1)->IsIntConstant()) || + (instruction->IsUShr() && instruction->AsUShr()->InputAt(1)->IsIntConstant()); + } +} + +inline bool HasShifterOperand(HInstruction* instr, InstructionSet isa) { + // On ARM64 `neg` instructions are an alias of `sub` using the zero register + // as the first register input. + bool res = instr->IsAdd() || instr->IsAnd() || (isa == kArm64 && instr->IsNeg()) || + instr->IsOr() || instr->IsSub() || instr->IsXor(); + return res; +} + +} // namespace helpers + bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa); // For bitwise operations (And/Or/Xor) with a negated input, try to use // a negated bitwise instruction. diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 95838380cc..26c9ab83c2 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -71,7 +71,7 @@ HLoopOptimization::HLoopOptimization(HGraph* graph, void HLoopOptimization::Run() { // Well-behaved loops only. // TODO: make this less of a sledgehammer. - if (graph_->HasTryCatch() || graph_->HasIrreducibleLoops()) { + if (!graph_->HasLoops() || graph_->HasTryCatch() || graph_->HasIrreducibleLoops()) { return; } @@ -84,6 +84,10 @@ void HLoopOptimization::Run() { // Perform loop optimizations. LocalRun(); + if (top_loop_ == nullptr) { + graph_->SetHasLoops(false); + } + // Detach. loop_allocator_ = nullptr; last_loop_ = top_loop_ = nullptr; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 71a26ebe79..62c89100eb 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -688,6 +688,7 @@ void HLoopInformation::Populate() { contains_irreducible_loop_ = true; graph->SetHasIrreducibleLoops(true); } + graph->SetHasLoops(true); } HBasicBlock* HLoopInformation::GetPreHeader() const { @@ -2032,9 +2033,19 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { } } outer_graph->UpdateMaximumNumberOfOutVRegs(GetMaximumNumberOfOutVRegs()); + if (HasBoundsChecks()) { outer_graph->SetHasBoundsChecks(true); } + if (HasLoops()) { + outer_graph->SetHasLoops(true); + } + if (HasIrreducibleLoops()) { + outer_graph->SetHasIrreducibleLoops(true); + } + if (HasTryCatch()) { + outer_graph->SetHasTryCatch(true); + } HInstruction* return_value = nullptr; if (GetBlocks().size() == 3) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 96f9abafbf..8a9e61875a 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -323,6 +323,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { temporaries_vreg_slots_(0), has_bounds_checks_(false), has_try_catch_(false), + has_loops_(false), has_irreducible_loops_(false), debuggable_(debuggable), current_instruction_id_(start_instruction_id), @@ -559,6 +560,9 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { bool HasTryCatch() const { return has_try_catch_; } void SetHasTryCatch(bool value) { has_try_catch_ = value; } + bool HasLoops() const { return has_loops_; } + void SetHasLoops(bool value) { has_loops_ = value; } + bool HasIrreducibleLoops() const { return has_irreducible_loops_; } void SetHasIrreducibleLoops(bool value) { has_irreducible_loops_ = value; } @@ -637,14 +641,26 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { // Number of vreg size slots that the temporaries use (used in baseline compiler). size_t temporaries_vreg_slots_; - // Has bounds checks. We can totally skip BCE if it's false. + // Flag whether there are bounds checks in the graph. We can skip + // BCE if it's false. It's only best effort to keep it up to date in + // the presence of code elimination so there might be false positives. bool has_bounds_checks_; - // Flag whether there are any try/catch blocks in the graph. We will skip - // try/catch-related passes if false. + // Flag whether there are try/catch blocks in the graph. We will skip + // try/catch-related passes if it's false. It's only best effort to keep + // it up to date in the presence of code elimination so there might be + // false positives. bool has_try_catch_; - // Flag whether there are any irreducible loops in the graph. + // Flag whether there are any loops in the graph. We can skip loop + // optimization if it's false. It's only best effort to keep it up + // to date in the presence of code elimination so there might be false + // positives. + bool has_loops_; + + // Flag whether there are any irreducible loops in the graph. It's only + // best effort to keep it up to date in the presence of code elimination + // so there might be false positives. bool has_irreducible_loops_; // Indicates whether the graph should be compiled in a way that @@ -1346,6 +1362,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #else #define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ M(BitwiseNegatedRight, Instruction) \ + M(DataProcWithShifterOp, Instruction) \ M(MultiplyAccumulate, Instruction) \ M(IntermediateAddress, Instruction) #endif @@ -1357,12 +1374,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(ArmDexCacheArraysBase, Instruction) #endif -#ifndef ART_ENABLE_CODEGEN_arm64 #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) -#else -#define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ - M(Arm64DataProcWithShifterOp, Instruction) -#endif #ifndef ART_ENABLE_CODEGEN_mips #define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) @@ -6603,9 +6615,6 @@ class HParallelMove FINAL : public HTemplateInstruction<0> { #ifdef ART_ENABLE_CODEGEN_arm #include "nodes_arm.h" #endif -#ifdef ART_ENABLE_CODEGEN_arm64 -#include "nodes_arm64.h" -#endif #ifdef ART_ENABLE_CODEGEN_mips #include "nodes_mips.h" #endif diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h deleted file mode 100644 index 3f88717c2a..0000000000 --- a/compiler/optimizing/nodes_arm64.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2015 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_NODES_ARM64_H_ -#define ART_COMPILER_OPTIMIZING_NODES_ARM64_H_ - -#include "nodes.h" - -namespace art { - -class HArm64DataProcWithShifterOp FINAL : public HExpression<2> { - public: - enum OpKind { - kLSL, // Logical shift left. - kLSR, // Logical shift right. - kASR, // Arithmetic shift right. - kUXTB, // Unsigned extend byte. - kUXTH, // Unsigned extend half-word. - kUXTW, // Unsigned extend word. - kSXTB, // Signed extend byte. - kSXTH, // Signed extend half-word. - kSXTW, // Signed extend word. - - // Aliases. - kFirstShiftOp = kLSL, - kLastShiftOp = kASR, - kFirstExtensionOp = kUXTB, - kLastExtensionOp = kSXTW - }; - HArm64DataProcWithShifterOp(HInstruction* instr, - HInstruction* left, - HInstruction* right, - OpKind op, - // The shift argument is unused if the operation - // is an extension. - int shift = 0, - uint32_t dex_pc = kNoDexPc) - : HExpression(instr->GetType(), SideEffects::None(), dex_pc), - instr_kind_(instr->GetKind()), op_kind_(op), shift_amount_(shift) { - DCHECK(!instr->HasSideEffects()); - SetRawInputAt(0, left); - SetRawInputAt(1, right); - } - - bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(const HInstruction* other_instr) const OVERRIDE { - const HArm64DataProcWithShifterOp* other = other_instr->AsArm64DataProcWithShifterOp(); - return instr_kind_ == other->instr_kind_ && - op_kind_ == other->op_kind_ && - shift_amount_ == other->shift_amount_; - } - - static bool IsShiftOp(OpKind op_kind) { - return kFirstShiftOp <= op_kind && op_kind <= kLastShiftOp; - } - - static bool IsExtensionOp(OpKind op_kind) { - return kFirstExtensionOp <= op_kind && op_kind <= kLastExtensionOp; - } - - // Find the operation kind and shift amount from a bitfield move instruction. - static void GetOpInfoFromInstruction(HInstruction* bitfield_op, - /*out*/OpKind* op_kind, - /*out*/int* shift_amount); - - InstructionKind GetInstrKind() const { return instr_kind_; } - OpKind GetOpKind() const { return op_kind_; } - int GetShiftAmount() const { return shift_amount_; } - - DECLARE_INSTRUCTION(Arm64DataProcWithShifterOp); - - private: - InstructionKind instr_kind_; - OpKind op_kind_; - int shift_amount_; - - friend std::ostream& operator<<(std::ostream& os, OpKind op); - - DISALLOW_COPY_AND_ASSIGN(HArm64DataProcWithShifterOp); -}; - -std::ostream& operator<<(std::ostream& os, const HArm64DataProcWithShifterOp::OpKind op); - -} // namespace art - -#endif // ART_COMPILER_OPTIMIZING_NODES_ARM64_H_ diff --git a/compiler/optimizing/nodes_arm64.cc b/compiler/optimizing/nodes_shared.cc index ac2f093847..f145bf9130 100644 --- a/compiler/optimizing/nodes_arm64.cc +++ b/compiler/optimizing/nodes_shared.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2017 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. @@ -15,15 +15,15 @@ */ #include "common_arm64.h" -#include "nodes.h" +#include "nodes_shared.h" namespace art { -using arm64::helpers::CanFitInShifterOperand; +using helpers::CanFitInShifterOperand; -void HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruction, - /*out*/OpKind* op_kind, - /*out*/int* shift_amount) { +void HDataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruction, + /*out*/OpKind* op_kind, + /*out*/int* shift_amount) { DCHECK(CanFitInShifterOperand(instruction)); if (instruction->IsShl()) { *op_kind = kLSL; @@ -41,12 +41,11 @@ void HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruc int result_size = Primitive::ComponentSize(result_type); int input_size = Primitive::ComponentSize(input_type); int min_size = std::min(result_size, input_size); - // This follows the logic in - // `InstructionCodeGeneratorARM64::VisitTypeConversion()`. if (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimLong) { - // There is actually nothing to do. The register will be used as a W - // register, discarding the top bits. This is represented by the default - // encoding 'LSL 0'. + // There is actually nothing to do. On ARM the high register from the + // pair will be ignored. On ARM64 the register will be used as a W + // register, discarding the top bits. This is represented by the + // default encoding 'LSL 0'. *op_kind = kLSL; *shift_amount = 0; } else if (result_type == Primitive::kPrimChar || @@ -64,17 +63,17 @@ void HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruc } } -std::ostream& operator<<(std::ostream& os, const HArm64DataProcWithShifterOp::OpKind op) { +std::ostream& operator<<(std::ostream& os, const HDataProcWithShifterOp::OpKind op) { switch (op) { - case HArm64DataProcWithShifterOp::kLSL: return os << "LSL"; - case HArm64DataProcWithShifterOp::kLSR: return os << "LSR"; - case HArm64DataProcWithShifterOp::kASR: return os << "ASR"; - case HArm64DataProcWithShifterOp::kUXTB: return os << "UXTB"; - case HArm64DataProcWithShifterOp::kUXTH: return os << "UXTH"; - case HArm64DataProcWithShifterOp::kUXTW: return os << "UXTW"; - case HArm64DataProcWithShifterOp::kSXTB: return os << "SXTB"; - case HArm64DataProcWithShifterOp::kSXTH: return os << "SXTH"; - case HArm64DataProcWithShifterOp::kSXTW: return os << "SXTW"; + case HDataProcWithShifterOp::kLSL: return os << "LSL"; + case HDataProcWithShifterOp::kLSR: return os << "LSR"; + case HDataProcWithShifterOp::kASR: return os << "ASR"; + case HDataProcWithShifterOp::kUXTB: return os << "UXTB"; + case HDataProcWithShifterOp::kUXTH: return os << "UXTH"; + case HDataProcWithShifterOp::kUXTW: return os << "UXTW"; + case HDataProcWithShifterOp::kSXTB: return os << "SXTB"; + case HDataProcWithShifterOp::kSXTH: return os << "SXTH"; + case HDataProcWithShifterOp::kSXTW: return os << "SXTW"; default: LOG(FATAL) << "Invalid OpKind " << static_cast<int>(op); UNREACHABLE(); diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h index 814202e97b..c6bfbcc7fb 100644 --- a/compiler/optimizing/nodes_shared.h +++ b/compiler/optimizing/nodes_shared.h @@ -150,6 +150,81 @@ class HIntermediateAddress FINAL : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HIntermediateAddress); }; +class HDataProcWithShifterOp FINAL : public HExpression<2> { + public: + enum OpKind { + kLSL, // Logical shift left. + kLSR, // Logical shift right. + kASR, // Arithmetic shift right. + kUXTB, // Unsigned extend byte. + kUXTH, // Unsigned extend half-word. + kUXTW, // Unsigned extend word. + kSXTB, // Signed extend byte. + kSXTH, // Signed extend half-word. + kSXTW, // Signed extend word. + + // Aliases. + kFirstShiftOp = kLSL, + kLastShiftOp = kASR, + kFirstExtensionOp = kUXTB, + kLastExtensionOp = kSXTW + }; + HDataProcWithShifterOp(HInstruction* instr, + HInstruction* left, + HInstruction* right, + OpKind op, + // The shift argument is unused if the operation + // is an extension. + int shift = 0, + uint32_t dex_pc = kNoDexPc) + : HExpression(instr->GetType(), SideEffects::None(), dex_pc), + instr_kind_(instr->GetKind()), op_kind_(op), + shift_amount_(shift & (instr->GetType() == Primitive::kPrimInt + ? kMaxIntShiftDistance + : kMaxLongShiftDistance)) { + DCHECK(!instr->HasSideEffects()); + SetRawInputAt(0, left); + SetRawInputAt(1, right); + } + + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(const HInstruction* other_instr) const OVERRIDE { + const HDataProcWithShifterOp* other = other_instr->AsDataProcWithShifterOp(); + return instr_kind_ == other->instr_kind_ && + op_kind_ == other->op_kind_ && + shift_amount_ == other->shift_amount_; + } + + static bool IsShiftOp(OpKind op_kind) { + return kFirstShiftOp <= op_kind && op_kind <= kLastShiftOp; + } + + static bool IsExtensionOp(OpKind op_kind) { + return kFirstExtensionOp <= op_kind && op_kind <= kLastExtensionOp; + } + + // Find the operation kind and shift amount from a bitfield move instruction. + static void GetOpInfoFromInstruction(HInstruction* bitfield_op, + /*out*/OpKind* op_kind, + /*out*/int* shift_amount); + + InstructionKind GetInstrKind() const { return instr_kind_; } + OpKind GetOpKind() const { return op_kind_; } + int GetShiftAmount() const { return shift_amount_; } + + DECLARE_INSTRUCTION(DataProcWithShifterOp); + + private: + InstructionKind instr_kind_; + OpKind op_kind_; + int shift_amount_; + + friend std::ostream& operator<<(std::ostream& os, OpKind op); + + DISALLOW_COPY_AND_ASSIGN(HDataProcWithShifterOp); +}; + +std::ostream& operator<<(std::ostream& os, const HDataProcWithShifterOp::OpKind op); } // namespace art diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 8638e346fb..f72bd6a5a3 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -306,7 +306,7 @@ class OptimizingCompiler FINAL : public Compiler { InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - jobject class_loader, + Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) const OVERRIDE; @@ -375,7 +375,7 @@ class OptimizingCompiler FINAL : public Compiler { InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - jobject class_loader, + Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache, ArtMethod* method, @@ -875,7 +875,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - jobject class_loader, + Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache, ArtMethod* method, @@ -946,11 +946,8 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, const uint8_t* interpreter_metadata = nullptr; if (method == nullptr) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ClassLoader> loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader>(class_loader))); method = compiler_driver->ResolveMethod( - soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type); + soa, dex_cache, class_loader, &dex_compilation_unit, method_idx, invoke_type); } // For AOT compilation, we may not get a method, for example if its class is erroneous. // JIT should always have a method. @@ -959,16 +956,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, graph->SetArtMethod(method); ScopedObjectAccess soa(Thread::Current()); interpreter_metadata = method->GetQuickenedInfo(class_linker->GetImagePointerSize()); - dex::TypeIndex type_index = method->GetDeclaringClass()->GetDexTypeIndex(); - - // Update the dex cache if the type is not in it yet. Note that under AOT, - // the verifier must have set it, but under JIT, there's no guarantee, as we - // don't necessarily run the verifier. - // The compiler and the compiler driver assume the compiling class is - // in the dex cache. - if (dex_cache->GetResolvedType(type_index) == nullptr) { - dex_cache->SetResolvedType(type_index, method->GetDeclaringClass()); - } } std::unique_ptr<CodeGenerator> codegen( @@ -1049,7 +1036,7 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx, - jobject jclass_loader, + Handle<mirror::ClassLoader> jclass_loader, const DexFile& dex_file, Handle<mirror::DexCache> dex_cache) const { CompilerDriver* compiler_driver = GetCompilerDriver(); @@ -1163,7 +1150,6 @@ bool OptimizingCompiler::JitCompile(Thread* self, Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache())); DCHECK(method->IsCompilable()); - jobject jclass_loader = class_loader.ToJObject(); const DexFile* dex_file = method->GetDexFile(); const uint16_t class_def_idx = method->GetClassDefIndex(); const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); @@ -1187,7 +1173,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, invoke_type, class_def_idx, method_idx, - jclass_loader, + class_loader, *dex_file, dex_cache, method, diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index c55fccc7d3..6e332ca59b 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -65,11 +65,13 @@ ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetThrowabl class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { public: RTPVisitor(HGraph* graph, + Handle<mirror::ClassLoader> class_loader, Handle<mirror::DexCache> hint_dex_cache, HandleCache* handle_cache, ArenaVector<HInstruction*>* worklist, bool is_first_run) : HGraphDelegateVisitor(graph), + class_loader_(class_loader), hint_dex_cache_(hint_dex_cache), handle_cache_(handle_cache), worklist_(worklist), @@ -101,6 +103,7 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { bool is_exact); private: + Handle<mirror::ClassLoader> class_loader_; Handle<mirror::DexCache> hint_dex_cache_; HandleCache* handle_cache_; ArenaVector<HInstruction*>* worklist_; @@ -108,11 +111,13 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { }; ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph, + Handle<mirror::ClassLoader> class_loader, Handle<mirror::DexCache> hint_dex_cache, VariableSizedHandleScope* handles, bool is_first_run, const char* name) : HOptimization(graph, name), + class_loader_(class_loader), hint_dex_cache_(hint_dex_cache), handle_cache_(handles), worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)), @@ -147,7 +152,12 @@ void ReferenceTypePropagation::ValidateTypes() { } void ReferenceTypePropagation::Visit(HInstruction* instruction) { - RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_); + RTPVisitor visitor(graph_, + class_loader_, + hint_dex_cache_, + &handle_cache_, + &worklist_, + is_first_run_); instruction->Accept(&visitor); } @@ -321,7 +331,12 @@ void ReferenceTypePropagation::Run() { } void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) { - RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_); + RTPVisitor visitor(graph_, + class_loader_, + hint_dex_cache_, + &handle_cache_, + &worklist_, + is_first_run_); // Handle Phis first as there might be instructions in the same block who depend on them. for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { VisitPhi(it.Current()->AsPhi()); @@ -542,8 +557,9 @@ void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* ScopedObjectAccess soa(Thread::Current()); ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_); - // Get type from dex cache assuming it was populated by the verifier. - SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact); + ObjPtr<mirror::Class> klass = + ClassLinker::LookupResolvedType(type_idx, dex_cache, class_loader_.Get()); + SetClassAsTypeInfo(instr, klass, is_exact); } void ReferenceTypePropagation::RTPVisitor::VisitNewInstance(HNewInstance* instr) { @@ -556,25 +572,13 @@ void ReferenceTypePropagation::RTPVisitor::VisitNewArray(HNewArray* instr) { SetClassAsTypeInfo(instr, instr->GetLoadClass()->GetClass().Get(), /* is_exact */ true); } -static mirror::Class* GetClassFromDexCache(Thread* self, - const DexFile& dex_file, - dex::TypeIndex type_idx, - Handle<mirror::DexCache> hint_dex_cache) - REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(self, dex_file, hint_dex_cache); - // Get type from dex cache assuming it was populated by the verifier. - return dex_cache->GetResolvedType(type_idx); -} - void ReferenceTypePropagation::RTPVisitor::VisitParameterValue(HParameterValue* instr) { // We check if the existing type is valid: the inliner may have set it. if (instr->GetType() == Primitive::kPrimNot && !instr->GetReferenceTypeInfo().IsValid()) { - ScopedObjectAccess soa(Thread::Current()); - mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(), - instr->GetDexFile(), - instr->GetTypeIndex(), - hint_dex_cache_); - SetClassAsTypeInfo(instr, resolved_class, /* is_exact */ false); + UpdateReferenceTypeInfo(instr, + instr->GetTypeIndex(), + instr->GetDexFile(), + /* is_exact */ false); } } diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 4663471729..215e96786b 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -33,6 +33,7 @@ namespace art { class ReferenceTypePropagation : public HOptimization { public: ReferenceTypePropagation(HGraph* graph, + Handle<mirror::ClassLoader> class_loader, Handle<mirror::DexCache> hint_dex_cache, VariableSizedHandleScope* handles, bool is_first_run, @@ -105,6 +106,8 @@ class ReferenceTypePropagation : public HOptimization { void ValidateTypes(); + Handle<mirror::ClassLoader> class_loader_; + // Note: hint_dex_cache_ is usually, but not necessarily, the dex cache associated with // graph_->GetDexFile(). Since we may look up also in other dex files, it's used only // as a hint, to reduce the number of calls to the costly ClassLinker::FindDexCache(). diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc index b061c871b0..84a4bab1a9 100644 --- a/compiler/optimizing/reference_type_propagation_test.cc +++ b/compiler/optimizing/reference_type_propagation_test.cc @@ -38,6 +38,7 @@ class ReferenceTypePropagationTest : public CommonCompilerTest { void SetupPropagation(VariableSizedHandleScope* handles) { graph_->InitializeInexactObjectRTI(handles); propagation_ = new (&allocator_) ReferenceTypePropagation(graph_, + Handle<mirror::ClassLoader>(), Handle<mirror::DexCache>(), handles, true, diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc index e3701fbcb1..558dcc4cbc 100644 --- a/compiler/optimizing/scheduler_arm64.cc +++ b/compiler/optimizing/scheduler_arm64.cc @@ -31,8 +31,8 @@ void SchedulingLatencyVisitorARM64::VisitBitwiseNegatedRight( last_visited_latency_ = kArm64IntegerOpLatency; } -void SchedulingLatencyVisitorARM64::VisitArm64DataProcWithShifterOp( - HArm64DataProcWithShifterOp* ATTRIBUTE_UNUSED) { +void SchedulingLatencyVisitorARM64::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* ATTRIBUTE_UNUSED) { last_visited_latency_ = kArm64DataProcWithShifterOpLatency; } diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h index 702027c535..7a33720655 100644 --- a/compiler/optimizing/scheduler_arm64.h +++ b/compiler/optimizing/scheduler_arm64.h @@ -74,7 +74,8 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { #define FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(M) \ M(BitwiseNegatedRight, unused) \ M(MultiplyAccumulate, unused) \ - M(IntermediateAddress, unused) + M(IntermediateAddress, unused) \ + M(DataProcWithShifterOp, unused) #define DECLARE_VISIT_INSTRUCTION(type, unused) \ void Visit##type(H##type* instruction) OVERRIDE; diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index 487e4dd498..50ab11bc23 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -499,7 +499,11 @@ GraphAnalysisResult SsaBuilder::BuildSsa() { // 4) Compute type of reference type instructions. The pass assumes that // NullConstant has been fixed up. - ReferenceTypePropagation(graph_, dex_cache_, handles_, /* is_first_run */ true).Run(); + ReferenceTypePropagation(graph_, + class_loader_, + dex_cache_, + handles_, + /* is_first_run */ true).Run(); // 5) HInstructionBuilder duplicated ArrayGet instructions with ambiguous type // (int/float or long/double) and marked ArraySets with ambiguous input type. diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h index 45dac54115..978f113ec4 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -48,9 +48,11 @@ namespace art { class SsaBuilder : public ValueObject { public: SsaBuilder(HGraph* graph, + Handle<mirror::ClassLoader> class_loader, Handle<mirror::DexCache> dex_cache, VariableSizedHandleScope* handles) : graph_(graph), + class_loader_(class_loader), dex_cache_(dex_cache), handles_(handles), agets_fixed_(false), @@ -115,6 +117,7 @@ class SsaBuilder : public ValueObject { void RemoveRedundantUninitializedStrings(); HGraph* graph_; + Handle<mirror::ClassLoader> class_loader_; Handle<mirror::DexCache> dex_cache_; VariableSizedHandleScope* const handles_; diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index 5a466e1d5d..6eab302dab 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -642,39 +642,6 @@ void X86Assembler::movhpd(const Address& dst, XmmRegister src) { } -void X86Assembler::psrldq(XmmRegister reg, const Immediate& shift_count) { - DCHECK(shift_count.is_uint8()); - - AssemblerBuffer::EnsureCapacity ensured(&buffer_); - EmitUint8(0x66); - EmitUint8(0x0F); - EmitUint8(0x73); - EmitXmmRegisterOperand(3, reg); - EmitUint8(shift_count.value()); -} - - -void X86Assembler::psrlq(XmmRegister reg, const Immediate& shift_count) { - DCHECK(shift_count.is_uint8()); - - AssemblerBuffer::EnsureCapacity ensured(&buffer_); - EmitUint8(0x66); - EmitUint8(0x0F); - EmitUint8(0x73); - EmitXmmRegisterOperand(2, reg); - EmitUint8(shift_count.value()); -} - - -void X86Assembler::punpckldq(XmmRegister dst, XmmRegister src) { - AssemblerBuffer::EnsureCapacity ensured(&buffer_); - EmitUint8(0x66); - EmitUint8(0x0F); - EmitUint8(0x62); - EmitXmmRegisterOperand(dst, src); -} - - void X86Assembler::addsd(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF2); @@ -828,6 +795,51 @@ void X86Assembler::movdqu(const Address& dst, XmmRegister src) { } +void X86Assembler::paddb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xFC); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xF8); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::paddw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xFD); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xF9); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::pmullw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xD5); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::paddd(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); @@ -856,6 +868,24 @@ void X86Assembler::pmulld(XmmRegister dst, XmmRegister src) { } +void X86Assembler::paddq(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xD4); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubq(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xFB); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::cvtsi2ss(XmmRegister dst, Register src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); @@ -1186,6 +1216,141 @@ void X86Assembler::pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm } +void X86Assembler::punpcklbw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x60); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::punpcklwd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x61); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::punpckldq(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x62); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::punpcklqdq(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x6C); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psllw(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x71); + EmitXmmRegisterOperand(6, reg); + EmitUint8(shift_count.value()); +} + + +void X86Assembler::pslld(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x72); + EmitXmmRegisterOperand(6, reg); + EmitUint8(shift_count.value()); +} + + +void X86Assembler::psllq(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x73); + EmitXmmRegisterOperand(6, reg); + EmitUint8(shift_count.value()); +} + + +void X86Assembler::psraw(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x71); + EmitXmmRegisterOperand(4, reg); + EmitUint8(shift_count.value()); +} + + +void X86Assembler::psrad(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x72); + EmitXmmRegisterOperand(4, reg); + EmitUint8(shift_count.value()); +} + + +void X86Assembler::psrlw(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x71); + EmitXmmRegisterOperand(2, reg); + EmitUint8(shift_count.value()); +} + + +void X86Assembler::psrld(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x72); + EmitXmmRegisterOperand(2, reg); + EmitUint8(shift_count.value()); +} + + +void X86Assembler::psrlq(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x73); + EmitXmmRegisterOperand(2, reg); + EmitUint8(shift_count.value()); +} + + +void X86Assembler::psrldq(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x73); + EmitXmmRegisterOperand(3, reg); + EmitUint8(shift_count.value()); +} + + void X86Assembler::fldl(const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xDD); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 4343e2e734..2999599fc5 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -408,14 +408,9 @@ class X86Assembler FINAL : public Assembler { void movsd(const Address& dst, XmmRegister src); void movsd(XmmRegister dst, XmmRegister src); - void psrlq(XmmRegister reg, const Immediate& shift_count); - void punpckldq(XmmRegister dst, XmmRegister src); - void movhpd(XmmRegister dst, const Address& src); void movhpd(const Address& dst, XmmRegister src); - void psrldq(XmmRegister reg, const Immediate& shift_count); - void addsd(XmmRegister dst, XmmRegister src); void addsd(XmmRegister dst, const Address& src); void subsd(XmmRegister dst, XmmRegister src); @@ -436,10 +431,20 @@ class X86Assembler FINAL : public Assembler { void movdqa(const Address& dst, XmmRegister src); // store aligned void movdqu(const Address& dst, XmmRegister src); // store unaligned - void paddd(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void paddb(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void psubb(XmmRegister dst, XmmRegister src); + + void paddw(XmmRegister dst, XmmRegister src); + void psubw(XmmRegister dst, XmmRegister src); + void pmullw(XmmRegister dst, XmmRegister src); + + void paddd(XmmRegister dst, XmmRegister src); void psubd(XmmRegister dst, XmmRegister src); void pmulld(XmmRegister dst, XmmRegister src); + void paddq(XmmRegister dst, XmmRegister src); + void psubq(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, Register src); void cvtsi2sd(XmmRegister dst, Register src); @@ -489,6 +494,24 @@ class X86Assembler FINAL : public Assembler { void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm); void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm); + void punpcklbw(XmmRegister dst, XmmRegister src); + void punpcklwd(XmmRegister dst, XmmRegister src); + void punpckldq(XmmRegister dst, XmmRegister src); + void punpcklqdq(XmmRegister dst, XmmRegister src); + + void psllw(XmmRegister reg, const Immediate& shift_count); + void pslld(XmmRegister reg, const Immediate& shift_count); + void psllq(XmmRegister reg, const Immediate& shift_count); + + void psraw(XmmRegister reg, const Immediate& shift_count); + void psrad(XmmRegister reg, const Immediate& shift_count); + // no psraq + + void psrlw(XmmRegister reg, const Immediate& shift_count); + void psrld(XmmRegister reg, const Immediate& shift_count); + void psrlq(XmmRegister reg, const Immediate& shift_count); + void psrldq(XmmRegister reg, const Immediate& shift_count); + void flds(const Address& src); void fstps(const Address& dst); void fsts(const Address& dst); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index c6ab893aea..a74bea207e 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -122,18 +122,6 @@ TEST_F(AssemblerX86Test, Movntl) { DriverStr(expected, "movntl"); } -TEST_F(AssemblerX86Test, psrlq) { - GetAssembler()->psrlq(x86::XMM0, CreateImmediate(32)); - const char* expected = "psrlq $0x20, %xmm0\n"; - DriverStr(expected, "psrlq"); -} - -TEST_F(AssemblerX86Test, punpckldq) { - GetAssembler()->punpckldq(x86::XMM0, x86::XMM1); - const char* expected = "punpckldq %xmm1, %xmm0\n"; - DriverStr(expected, "punpckldq"); -} - TEST_F(AssemblerX86Test, LoadLongConstant) { GetAssembler()->LoadLongConstant(x86::XMM0, 51); const char* expected = @@ -521,6 +509,26 @@ TEST_F(AssemblerX86Test, DivPD) { DriverStr(RepeatFF(&x86::X86Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd"); } +TEST_F(AssemblerX86Test, PAddB) { + DriverStr(RepeatFF(&x86::X86Assembler::paddb, "paddb %{reg2}, %{reg1}"), "paddb"); +} + +TEST_F(AssemblerX86Test, PSubB) { + DriverStr(RepeatFF(&x86::X86Assembler::psubb, "psubb %{reg2}, %{reg1}"), "psubb"); +} + +TEST_F(AssemblerX86Test, PAddW) { + DriverStr(RepeatFF(&x86::X86Assembler::paddw, "paddw %{reg2}, %{reg1}"), "paddw"); +} + +TEST_F(AssemblerX86Test, PSubW) { + DriverStr(RepeatFF(&x86::X86Assembler::psubw, "psubw %{reg2}, %{reg1}"), "psubw"); +} + +TEST_F(AssemblerX86Test, PMullW) { + DriverStr(RepeatFF(&x86::X86Assembler::pmullw, "pmullw %{reg2}, %{reg1}"), "pmullw"); +} + TEST_F(AssemblerX86Test, PAddD) { DriverStr(RepeatFF(&x86::X86Assembler::paddd, "paddd %{reg2}, %{reg1}"), "paddd"); } @@ -533,6 +541,14 @@ TEST_F(AssemblerX86Test, PMullD) { DriverStr(RepeatFF(&x86::X86Assembler::pmulld, "pmulld %{reg2}, %{reg1}"), "pmulld"); } +TEST_F(AssemblerX86Test, PAddQ) { + DriverStr(RepeatFF(&x86::X86Assembler::paddq, "paddq %{reg2}, %{reg1}"), "paddq"); +} + +TEST_F(AssemblerX86Test, PSubQ) { + DriverStr(RepeatFF(&x86::X86Assembler::psubq, "psubq %{reg2}, %{reg1}"), "psubq"); +} + TEST_F(AssemblerX86Test, XorPD) { DriverStr(RepeatFF(&x86::X86Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd"); } @@ -581,6 +597,67 @@ TEST_F(AssemblerX86Test, PShufD) { DriverStr(RepeatFFI(&x86::X86Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd"); } +TEST_F(AssemblerX86Test, Punpcklbw) { + DriverStr(RepeatFF(&x86::X86Assembler::punpcklbw, "punpcklbw %{reg2}, %{reg1}"), "punpcklbw"); +} + +TEST_F(AssemblerX86Test, Punpcklwd) { + DriverStr(RepeatFF(&x86::X86Assembler::punpcklwd, "punpcklwd %{reg2}, %{reg1}"), "punpcklwd"); +} + +TEST_F(AssemblerX86Test, Punpckldq) { + DriverStr(RepeatFF(&x86::X86Assembler::punpckldq, "punpckldq %{reg2}, %{reg1}"), "punpckldq"); +} + +TEST_F(AssemblerX86Test, Punpcklqdq) { + DriverStr(RepeatFF(&x86::X86Assembler::punpcklqdq, "punpcklqdq %{reg2}, %{reg1}"), "punpcklqdq"); +} + +TEST_F(AssemblerX86Test, psllw) { + GetAssembler()->psllw(x86::XMM0, CreateImmediate(16)); + DriverStr("psllw $0x10, %xmm0\n", "psllwi"); +} + +TEST_F(AssemblerX86Test, pslld) { + GetAssembler()->pslld(x86::XMM0, CreateImmediate(16)); + DriverStr("pslld $0x10, %xmm0\n", "pslldi"); +} + +TEST_F(AssemblerX86Test, psllq) { + GetAssembler()->psllq(x86::XMM0, CreateImmediate(16)); + DriverStr("psllq $0x10, %xmm0\n", "psllqi"); +} + +TEST_F(AssemblerX86Test, psraw) { + GetAssembler()->psraw(x86::XMM0, CreateImmediate(16)); + DriverStr("psraw $0x10, %xmm0\n", "psrawi"); +} + +TEST_F(AssemblerX86Test, psrad) { + GetAssembler()->psrad(x86::XMM0, CreateImmediate(16)); + DriverStr("psrad $0x10, %xmm0\n", "psradi"); +} + +TEST_F(AssemblerX86Test, psrlw) { + GetAssembler()->psrlw(x86::XMM0, CreateImmediate(16)); + DriverStr("psrlw $0x10, %xmm0\n", "psrlwi"); +} + +TEST_F(AssemblerX86Test, psrld) { + GetAssembler()->psrld(x86::XMM0, CreateImmediate(16)); + DriverStr("psrld $0x10, %xmm0\n", "psrldi"); +} + +TEST_F(AssemblerX86Test, psrlq) { + GetAssembler()->psrlq(x86::XMM0, CreateImmediate(16)); + DriverStr("psrlq $0x10, %xmm0\n", "psrlqi"); +} + +TEST_F(AssemblerX86Test, psrldq) { + GetAssembler()->psrldq(x86::XMM0, CreateImmediate(16)); + DriverStr("psrldq $0x10, %xmm0\n", "psrldqi"); +} + ///////////////// // Near labels // ///////////////// diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index b41be80ae4..458204aca9 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -882,6 +882,56 @@ void X86_64Assembler::movdqu(const Address& dst, XmmRegister src) { } +void X86_64Assembler::paddb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xFC); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubb(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xF8); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::paddw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xFD); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xF9); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::pmullw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xD5); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + void X86_64Assembler::paddd(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); @@ -913,6 +963,26 @@ void X86_64Assembler::pmulld(XmmRegister dst, XmmRegister src) { } +void X86_64Assembler::paddq(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xD4); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubq(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xFB); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src) { cvtsi2ss(dst, src, false); } @@ -1354,6 +1424,142 @@ void X86_64Assembler::pshufd(XmmRegister dst, XmmRegister src, const Immediate& } +void X86_64Assembler::punpcklbw(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x60); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::punpcklwd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x61); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::punpckldq(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x62); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::punpcklqdq(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x6C); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psllw(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex(false, false, false, false, reg.NeedsRex()); + EmitUint8(0x0F); + EmitUint8(0x71); + EmitXmmRegisterOperand(6, reg); + EmitUint8(shift_count.value()); +} + + +void X86_64Assembler::pslld(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex(false, false, false, false, reg.NeedsRex()); + EmitUint8(0x0F); + EmitUint8(0x72); + EmitXmmRegisterOperand(6, reg); + EmitUint8(shift_count.value()); +} + + +void X86_64Assembler::psllq(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex(false, false, false, false, reg.NeedsRex()); + EmitUint8(0x0F); + EmitUint8(0x73); + EmitXmmRegisterOperand(6, reg); + EmitUint8(shift_count.value()); +} + + +void X86_64Assembler::psraw(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex(false, false, false, false, reg.NeedsRex()); + EmitUint8(0x0F); + EmitUint8(0x71); + EmitXmmRegisterOperand(4, reg); + EmitUint8(shift_count.value()); +} + + +void X86_64Assembler::psrad(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex(false, false, false, false, reg.NeedsRex()); + EmitUint8(0x0F); + EmitUint8(0x72); + EmitXmmRegisterOperand(4, reg); + EmitUint8(shift_count.value()); +} + + +void X86_64Assembler::psrlw(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex(false, false, false, false, reg.NeedsRex()); + EmitUint8(0x0F); + EmitUint8(0x71); + EmitXmmRegisterOperand(2, reg); + EmitUint8(shift_count.value()); +} + + +void X86_64Assembler::psrld(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex(false, false, false, false, reg.NeedsRex()); + EmitUint8(0x0F); + EmitUint8(0x72); + EmitXmmRegisterOperand(2, reg); + EmitUint8(shift_count.value()); +} + + +void X86_64Assembler::psrlq(XmmRegister reg, const Immediate& shift_count) { + DCHECK(shift_count.is_uint8()); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex(false, false, false, false, reg.NeedsRex()); + EmitUint8(0x0F); + EmitUint8(0x73); + EmitXmmRegisterOperand(2, reg); + EmitUint8(shift_count.value()); +} + + void X86_64Assembler::fldl(const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xDD); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 43ea12a4cb..0dc11d840b 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -452,10 +452,20 @@ class X86_64Assembler FINAL : public Assembler { void movdqa(const Address& dst, XmmRegister src); // store aligned void movdqu(const Address& dst, XmmRegister src); // store unaligned - void paddd(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void paddb(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void psubb(XmmRegister dst, XmmRegister src); + + void paddw(XmmRegister dst, XmmRegister src); + void psubw(XmmRegister dst, XmmRegister src); + void pmullw(XmmRegister dst, XmmRegister src); + + void paddd(XmmRegister dst, XmmRegister src); void psubd(XmmRegister dst, XmmRegister src); void pmulld(XmmRegister dst, XmmRegister src); + void paddq(XmmRegister dst, XmmRegister src); + void psubq(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, CpuRegister src); // Note: this is the r/m32 version. void cvtsi2ss(XmmRegister dst, CpuRegister src, bool is64bit); void cvtsi2ss(XmmRegister dst, const Address& src, bool is64bit); @@ -512,6 +522,23 @@ class X86_64Assembler FINAL : public Assembler { void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm); void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm); + void punpcklbw(XmmRegister dst, XmmRegister src); + void punpcklwd(XmmRegister dst, XmmRegister src); + void punpckldq(XmmRegister dst, XmmRegister src); + void punpcklqdq(XmmRegister dst, XmmRegister src); + + void psllw(XmmRegister reg, const Immediate& shift_count); + void pslld(XmmRegister reg, const Immediate& shift_count); + void psllq(XmmRegister reg, const Immediate& shift_count); + + void psraw(XmmRegister reg, const Immediate& shift_count); + void psrad(XmmRegister reg, const Immediate& shift_count); + // no psraq + + void psrlw(XmmRegister reg, const Immediate& shift_count); + void psrld(XmmRegister reg, const Immediate& shift_count); + void psrlq(XmmRegister reg, const Immediate& shift_count); + void flds(const Address& src); void fstps(const Address& dst); void fsts(const Address& dst); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index aeb1911835..fe9449720f 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -1128,6 +1128,26 @@ TEST_F(AssemblerX86_64Test, Divpd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd"); } +TEST_F(AssemblerX86_64Test, Paddb) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddb, "paddb %{reg2}, %{reg1}"), "paddb"); +} + +TEST_F(AssemblerX86_64Test, Psubb) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubb, "psubb %{reg2}, %{reg1}"), "psubb"); +} + +TEST_F(AssemblerX86_64Test, Paddw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddw, "paddw %{reg2}, %{reg1}"), "paddw"); +} + +TEST_F(AssemblerX86_64Test, Psubw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubw, "psubw %{reg2}, %{reg1}"), "psubw"); +} + +TEST_F(AssemblerX86_64Test, Pmullw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pmullw, "pmullw %{reg2}, %{reg1}"), "pmullw"); +} + TEST_F(AssemblerX86_64Test, Paddd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddd, "paddd %{reg2}, %{reg1}"), "paddd"); } @@ -1140,6 +1160,14 @@ TEST_F(AssemblerX86_64Test, Pmulld) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::pmulld, "pmulld %{reg2}, %{reg1}"), "pmulld"); } +TEST_F(AssemblerX86_64Test, Paddq) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddq, "paddq %{reg2}, %{reg1}"), "paddq"); +} + +TEST_F(AssemblerX86_64Test, Psubq) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubq, "psubq %{reg2}, %{reg1}"), "psubq"); +} + TEST_F(AssemblerX86_64Test, Cvtsi2ss) { DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2ss, "cvtsi2ss %{reg2}, %{reg1}"), "cvtsi2ss"); } @@ -1261,6 +1289,78 @@ TEST_F(AssemblerX86_64Test, PShufd) { DriverStr(RepeatFFI(&x86_64::X86_64Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd"); } +TEST_F(AssemblerX86_64Test, Punpcklbw) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpcklbw, "punpcklbw %{reg2}, %{reg1}"), "punpcklbw"); +} + +TEST_F(AssemblerX86_64Test, Punpcklwd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpcklwd, "punpcklwd %{reg2}, %{reg1}"), "punpcklwd"); +} + +TEST_F(AssemblerX86_64Test, Punpckldq) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpckldq, "punpckldq %{reg2}, %{reg1}"), "punpckldq"); +} + +TEST_F(AssemblerX86_64Test, Punpcklqdq) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpcklqdq, "punpcklqdq %{reg2}, %{reg1}"), "punpcklqdq"); +} + +TEST_F(AssemblerX86_64Test, Psllw) { + GetAssembler()->psllw(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1)); + GetAssembler()->psllw(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2)); + DriverStr("psllw $1, %xmm0\n" + "psllw $2, %xmm15\n", "psllwi"); +} + +TEST_F(AssemblerX86_64Test, Pslld) { + GetAssembler()->pslld(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1)); + GetAssembler()->pslld(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2)); + DriverStr("pslld $1, %xmm0\n" + "pslld $2, %xmm15\n", "pslldi"); +} + +TEST_F(AssemblerX86_64Test, Psllq) { + GetAssembler()->psllq(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1)); + GetAssembler()->psllq(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2)); + DriverStr("psllq $1, %xmm0\n" + "psllq $2, %xmm15\n", "psllqi"); +} + +TEST_F(AssemblerX86_64Test, Psraw) { + GetAssembler()->psraw(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1)); + GetAssembler()->psraw(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2)); + DriverStr("psraw $1, %xmm0\n" + "psraw $2, %xmm15\n", "psrawi"); +} + +TEST_F(AssemblerX86_64Test, Psrad) { + GetAssembler()->psrad(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1)); + GetAssembler()->psrad(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2)); + DriverStr("psrad $1, %xmm0\n" + "psrad $2, %xmm15\n", "psradi"); +} + +TEST_F(AssemblerX86_64Test, Psrlw) { + GetAssembler()->psrlw(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1)); + GetAssembler()->psrlw(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2)); + DriverStr("psrlw $1, %xmm0\n" + "psrlw $2, %xmm15\n", "psrlwi"); +} + +TEST_F(AssemblerX86_64Test, Psrld) { + GetAssembler()->psrld(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1)); + GetAssembler()->psrld(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2)); + DriverStr("psrld $1, %xmm0\n" + "psrld $2, %xmm15\n", "pslldi"); +} + +TEST_F(AssemblerX86_64Test, Psrlq) { + GetAssembler()->psrlq(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1)); + GetAssembler()->psrlq(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2)); + DriverStr("psrlq $1, %xmm0\n" + "psrlq $2, %xmm15\n", "pslrqi"); +} + TEST_F(AssemblerX86_64Test, UcomissAddress) { GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address( x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 026a567fe0..e6b793011f 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -2095,7 +2095,7 @@ class Dex2Oat FINAL { } bool UseProfileGuidedCompilation() const { - return CompilerFilter::DependsOnProfile(compiler_options_->GetCompilerFilter()); + return profile_file_fd_ != -1 || !profile_file_.empty(); } bool LoadProfile() { diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 5dcdd9e69a..6881f75c75 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -400,9 +400,9 @@ class Dex2oatSwapUseTest : public Dex2oatSwapTest { TEST_F(Dex2oatSwapUseTest, CheckSwapUsage) { // The `native_alloc_2_ >= native_alloc_1_` assertion below may not - // hold true on some x86 systems when read barriers are enabled; - // disable this test while we investigate (b/29259363). - TEST_DISABLED_FOR_READ_BARRIER_ON_X86(); + // hold true on some x86 systems; disable this test while we + // investigate (b/29259363). + TEST_DISABLED_FOR_X86(); RunTest(false /* use_fd */, false /* expect_use */); diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index 43de34241b..2d9bbfdbb7 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -466,8 +466,8 @@ AnnotationItem* Collections::CreateAnnotationItem(const DexFile::AnnotationItem* AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file, - const DexFile::AnnotationSetItem& disk_annotations_item, uint32_t offset) { - if (disk_annotations_item.size_ == 0 && offset == 0) { + const DexFile::AnnotationSetItem* disk_annotations_item, uint32_t offset) { + if (disk_annotations_item == nullptr || (disk_annotations_item->size_ == 0 && offset == 0)) { return nullptr; } auto found_anno_set_item = AnnotationSetItems().find(offset); @@ -475,14 +475,14 @@ AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file, return found_anno_set_item->second.get(); } std::vector<AnnotationItem*>* items = new std::vector<AnnotationItem*>(); - for (uint32_t i = 0; i < disk_annotations_item.size_; ++i) { + for (uint32_t i = 0; i < disk_annotations_item->size_; ++i) { const DexFile::AnnotationItem* annotation = - dex_file.GetAnnotationItem(&disk_annotations_item, i); + dex_file.GetAnnotationItem(disk_annotations_item, i); if (annotation == nullptr) { continue; } AnnotationItem* annotation_item = - CreateAnnotationItem(annotation, disk_annotations_item.entries_[i]); + CreateAnnotationItem(annotation, disk_annotations_item->entries_[i]); items->push_back(annotation_item); } AnnotationSetItem* annotation_set_item = new AnnotationSetItem(items); @@ -501,7 +501,7 @@ AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexF AnnotationSetItem* class_annotation = nullptr; if (class_set_item != nullptr) { uint32_t offset = disk_annotations_item->class_annotations_off_; - class_annotation = CreateAnnotationSetItem(dex_file, *class_set_item, offset); + class_annotation = CreateAnnotationSetItem(dex_file, class_set_item, offset); } const DexFile::FieldAnnotationsItem* fields = dex_file.GetFieldAnnotations(disk_annotations_item); @@ -514,7 +514,7 @@ AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexF dex_file.GetFieldAnnotationSetItem(fields[i]); uint32_t annotation_set_offset = fields[i].annotations_off_; AnnotationSetItem* annotation_set_item = - CreateAnnotationSetItem(dex_file, *field_set_item, annotation_set_offset); + CreateAnnotationSetItem(dex_file, field_set_item, annotation_set_offset); field_annotations->push_back(std::unique_ptr<FieldAnnotation>( new FieldAnnotation(field_id, annotation_set_item))); } @@ -530,7 +530,7 @@ AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexF dex_file.GetMethodAnnotationSetItem(methods[i]); uint32_t annotation_set_offset = methods[i].annotations_off_; AnnotationSetItem* annotation_set_item = - CreateAnnotationSetItem(dex_file, *method_set_item, annotation_set_offset); + CreateAnnotationSetItem(dex_file, method_set_item, annotation_set_offset); method_annotations->push_back(std::unique_ptr<MethodAnnotation>( new MethodAnnotation(method_id, annotation_set_item))); } @@ -569,7 +569,7 @@ ParameterAnnotation* Collections::GenerateParameterAnnotation( const DexFile::AnnotationSetItem* annotation_set_item = dex_file.GetSetRefItemItem(&annotation_set_ref_list->list_[i]); uint32_t set_offset = annotation_set_ref_list->list_[i].annotations_off_; - annotations->push_back(CreateAnnotationSetItem(dex_file, *annotation_set_item, set_offset)); + annotations->push_back(CreateAnnotationSetItem(dex_file, annotation_set_item, set_offset)); } set_ref_list = new AnnotationSetRefList(annotations); annotation_set_ref_lists_.AddItem(set_ref_list, offset); diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 3a5b64480f..96afb906c7 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -194,7 +194,7 @@ class Collections { EncodedArrayItem* CreateEncodedArrayItem(const uint8_t* static_data, uint32_t offset); AnnotationItem* CreateAnnotationItem(const DexFile::AnnotationItem* annotation, uint32_t offset); AnnotationSetItem* CreateAnnotationSetItem(const DexFile& dex_file, - const DexFile::AnnotationSetItem& disk_annotations_item, uint32_t offset); + const DexFile::AnnotationSetItem* disk_annotations_item, uint32_t offset); AnnotationsDirectoryItem* CreateAnnotationsDirectoryItem(const DexFile& dex_file, const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset); CodeItem* CreateCodeItem( diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index da1e1d26dc..2d85e8fe7b 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -41,7 +41,7 @@ static const char kDexFileLayoutInputDex[] = "AAAAdQEAAAAQAAABAAAAjAEAAA=="; static const char kDexFileLayoutInputProfile[] = - "cHJvADAwMgABAAsAAAABAPUpbf5jbGFzc2VzLmRleAEA"; + "cHJvADAwMwABCwABAAAAAAD1KW3+Y2xhc3Nlcy5kZXgBAA=="; static const char kDexFileLayoutExpectedOutputDex[] = "ZGV4CjAzNQD1KW3+B8NAB0f2A/ZVIBJ0aHrGIqcpVTAUAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAH" diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index ff05733345..a289433af5 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -1306,7 +1306,7 @@ DISASSEMBLER_ENTRY(cmp, has_modrm = true; reg_is_opcode = true; store = true; - immediate_bytes = ((instr[1] & 0x38) == 0) ? 1 : 0; + immediate_bytes = ((instr[1] & 0x38) == 0) ? (instr[0] == 0xF7 ? 4 : 1) : 0; break; case 0xFF: { diff --git a/oatdump/Android.mk b/oatdump/Android.mk index d80df70738..aa07d2491a 100644 --- a/oatdump/Android.mk +++ b/oatdump/Android.mk @@ -41,7 +41,7 @@ endif .PHONY: dump-oat-core-target-$(TARGET_ARCH) ifeq ($(ART_BUILD_TARGET),true) -dump-oat-core-target-$(TARGET_ARCH): $(TARGET_CORE_IMAGE_default_no-pic_$(ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP) +dump-oat-core-target-$(TARGET_ARCH): $(TARGET_CORE_IMAGE_default_$(ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP) $(OATDUMP) --image=$(TARGET_CORE_IMG_LOCATION) \ --output=$(ART_DUMP_OAT_PATH)/core.target.$(TARGET_ARCH).oatdump.txt --instruction-set=$(TARGET_ARCH) @echo Output in $(ART_DUMP_OAT_PATH)/core.target.$(TARGET_ARCH).oatdump.txt @@ -50,7 +50,7 @@ endif ifdef TARGET_2ND_ARCH .PHONY: dump-oat-core-target-$(TARGET_2ND_ARCH) ifeq ($(ART_BUILD_TARGET),true) -dump-oat-core-target-$(TARGET_2ND_ARCH): $(TARGET_CORE_IMAGE_default_no-pic_$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP) +dump-oat-core-target-$(TARGET_2ND_ARCH): $(TARGET_CORE_IMAGE_default_$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP) $(OATDUMP) --image=$(TARGET_CORE_IMG_LOCATION) \ --output=$(ART_DUMP_OAT_PATH)/core.target.$(TARGET_2ND_ARCH).oatdump.txt --instruction-set=$(TARGET_2ND_ARCH) @echo Output in $(ART_DUMP_OAT_PATH)/core.target.$(TARGET_2ND_ARCH).oatdump.txt diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 44132868a1..becb827d20 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1018,7 +1018,9 @@ class OatDumper { dex_orig_name = dex_file_location.substr(dex_orig_pos + 1); // A more elegant approach to efficiently name user installed apps is welcome - if (dex_orig_name.size() == 8 && !dex_orig_name.compare("base.apk")) { + if (dex_orig_name.size() == 8 && + dex_orig_name.compare("base.apk") == 0 && + dex_orig_pos != std::string::npos) { dex_file_location.erase(dex_orig_pos, strlen("base.apk") + 1); size_t apk_orig_pos = dex_file_location.rfind('/'); if (apk_orig_pos != std::string::npos) { @@ -2242,9 +2244,14 @@ class ImageDumper { ScopedIndentation indent2(&state->vios_); auto* resolved_types = dex_cache->GetResolvedTypes(); for (size_t i = 0; i < num_types; ++i) { - auto* elem = resolved_types[i].Read(); + auto pair = resolved_types[i].load(std::memory_order_relaxed); size_t run = 0; - for (size_t j = i + 1; j != num_types && elem == resolved_types[j].Read(); ++j) { + for (size_t j = i + 1; j != num_types; ++j) { + auto other_pair = resolved_types[j].load(std::memory_order_relaxed); + if (pair.index != other_pair.index || + pair.object.Read() != other_pair.object.Read()) { + break; + } ++run; } if (run == 0) { @@ -2254,12 +2261,13 @@ class ImageDumper { i = i + run; } std::string msg; + auto* elem = pair.object.Read(); if (elem == nullptr) { msg = "null"; } else { msg = elem->PrettyClass(); } - os << StringPrintf("%p %s\n", elem, msg.c_str()); + os << StringPrintf("%p %u %s\n", elem, pair.index, msg.c_str()); } } } diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index b9be5f2605..491e73980f 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -643,8 +643,8 @@ void PatchOat::PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots if (orig_strings != nullptr) { orig_dex_cache->FixupStrings(RelocatedCopyOf(orig_strings), RelocatedPointerVisitor(this)); } - GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes(); - GcRoot<mirror::Class>* relocated_types = RelocatedAddressOfPointer(orig_types); + mirror::TypeDexCacheType* orig_types = orig_dex_cache->GetResolvedTypes(); + mirror::TypeDexCacheType* relocated_types = RelocatedAddressOfPointer(orig_types); copy_dex_cache->SetField64<false>( mirror::DexCache::ResolvedTypesOffset(), static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_types))); diff --git a/profman/profman.cc b/profman/profman.cc index 8f35a76b6d..a42e4f1db1 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -593,7 +593,7 @@ class ProfMan FINAL { } // Generate the profile data structure. ProfileCompilationInfo info; - std::vector<MethodReference> methods; // No methods for now. + std::vector<ProfileMethodInfo> methods; // No methods for now. info.AddMethodsAndClasses(methods, resolved_class_set); // Write the profile file. CHECK(info.Save(fd)); diff --git a/runtime/Android.bp b/runtime/Android.bp index d3a81a9add..d136aa15b3 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -143,7 +143,6 @@ cc_defaults { "native_bridge_art_interface.cc", "native_stack_dump.cc", "native/dalvik_system_DexFile.cc", - "native/dalvik_system_InMemoryDexClassLoader_DexData.cc", "native/dalvik_system_VMDebug.cc", "native/dalvik_system_VMRuntime.cc", "native/dalvik_system_VMStack.cc", diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 80af8e7bde..16b73c681f 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -311,6 +311,8 @@ inline bool ArtField::IsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_) { template <bool kResolve> inline ObjPtr<mirror::Class> ArtField::GetType() { + // TODO: Refactor this function into two functions, ResolveType() and LookupType() + // so that we can properly annotate it with no-suspension possible / suspension possible. const uint32_t field_index = GetDexFieldIndex(); ObjPtr<mirror::Class> declaring_class = GetDeclaringClass(); if (UNLIKELY(declaring_class->IsProxyClass())) { @@ -320,9 +322,16 @@ inline ObjPtr<mirror::Class> ArtField::GetType() { const DexFile* const dex_file = dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file->GetFieldId(field_index); ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(field_id.type_idx_); - if (kResolve && UNLIKELY(type == nullptr)) { - type = ResolveGetType(field_id.type_idx_); - CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); + if (UNLIKELY(type == nullptr)) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + if (kResolve) { + type = class_linker->ResolveType(*dex_file, field_id.type_idx_, declaring_class); + CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); + } else { + type = class_linker->LookupResolvedType( + *dex_file, field_id.type_idx_, dex_cache, declaring_class->GetClassLoader()); + DCHECK(!Thread::Current()->IsExceptionPending()); + } } return type; } diff --git a/runtime/art_field.cc b/runtime/art_field.cc index a4a6e5a4fb..7e131040be 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -48,10 +48,6 @@ ObjPtr<mirror::Class> ArtField::ProxyFindSystemClass(const char* descriptor) { return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor); } -ObjPtr<mirror::Class> ArtField::ResolveGetType(dex::TypeIndex type_idx) { - return Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this); -} - ObjPtr<mirror::String> ArtField::ResolveGetStringName(Thread* self, const DexFile& dex_file, dex::StringIndex string_idx, diff --git a/runtime/art_field.h b/runtime/art_field.h index 427e103749..75dd981136 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -217,8 +217,6 @@ class ArtField FINAL { private: ObjPtr<mirror::Class> ProxyFindSystemClass(const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr<mirror::Class> ResolveGetType(dex::TypeIndex type_idx) - REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr<mirror::String> ResolveGetStringName(Thread* self, const DexFile& dex_file, dex::StringIndex string_idx, diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 950f1aa9f4..473d9cf74e 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -175,12 +175,19 @@ inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod* other, PointerS } inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) { + // TODO: Refactor this function into two functions, Resolve...() and Lookup...() + // so that we can properly annotate it with no-suspension possible / suspension possible. ObjPtr<mirror::DexCache> dex_cache = GetDexCache(); ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr) && resolve) { + if (UNLIKELY(type == nullptr)) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - type = class_linker->ResolveType(type_idx, this); - CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); + if (resolve) { + type = class_linker->ResolveType(type_idx, this); + CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); + } else { + type = class_linker->LookupResolvedType( + *dex_cache->GetDexFile(), type_idx, dex_cache, GetClassLoader()); + } } return type.Ptr(); } diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 3438810069..bd510ca0e1 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -78,6 +78,18 @@ inline mirror::String* ClassLinker::ResolveString(dex::StringIndex string_idx, return string.Ptr(); } +inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType( + dex::TypeIndex type_idx, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) { + ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); + if (type == nullptr) { + type = Runtime::Current()->GetClassLinker()->LookupResolvedType( + *dex_cache->GetDexFile(), type_idx, dex_cache, class_loader); + } + return type; +} + inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) { Thread::PoisonObjectPointersIfDebug(); if (kIsDebugBuild) { @@ -91,25 +103,6 @@ inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMetho 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. - } - return resolved_type.Ptr(); -} - -inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtField* referrer) { - Thread::PoisonObjectPointersIfDebug(); - ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); - ObjPtr<mirror::DexCache> dex_cache_ptr = declaring_class->GetDexCache(); - ObjPtr<mirror::Class> resolved_type = dex_cache_ptr->GetResolvedType(type_idx); - if (UNLIKELY(resolved_type == nullptr)) { - StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(dex_cache_ptr)); - 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. } return resolved_type.Ptr(); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index d02cf17d44..46f16449ed 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -58,6 +58,7 @@ #include "gc/heap.h" #include "gc/scoped_gc_critical_section.h" #include "gc/space/image_space.h" +#include "gc/space/space-inl.h" #include "handle_scope-inl.h" #include "image-inl.h" #include "imt_conflict_table.h" @@ -1156,6 +1157,35 @@ class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor { ClassTable* const table_; }; +class VerifyDirectInterfacesInTableClassVisitor { + public: + explicit VerifyDirectInterfacesInTableClassVisitor(ObjPtr<mirror::ClassLoader> class_loader) + : class_loader_(class_loader), self_(Thread::Current()) { } + + bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { + if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader_) { + classes_.push_back(klass); + } + return true; + } + + void Check() const REQUIRES_SHARED(Locks::mutator_lock_) { + for (ObjPtr<mirror::Class> klass : classes_) { + for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) { + CHECK(klass->GetDirectInterface(self_, klass, i) != nullptr) + << klass->PrettyDescriptor() << " iface #" << i + << klass->GetDexFile().StringByTypeIdx(klass->GetDirectInterfaceTypeIdx(i)) + << " Bug: 34839984"; + } + } + } + + private: + ObjPtr<mirror::ClassLoader> class_loader_; + Thread* self_; + std::vector<ObjPtr<mirror::Class>> classes_; +}; + class VerifyDeclaringClassVisitor : public ArtMethodVisitor { public: VerifyDeclaringClassVisitor() REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) @@ -1187,6 +1217,23 @@ static void CopyNonNull(const T* src, size_t count, T* dst, const NullPred& pred } } +template <typename T> +static void CopyDexCachePairs(const std::atomic<mirror::DexCachePair<T>>* src, + size_t count, + std::atomic<mirror::DexCachePair<T>>* dst) { + DCHECK_NE(count, 0u); + DCHECK(!src[0].load(std::memory_order_relaxed).object.IsNull() || + src[0].load(std::memory_order_relaxed).index != 0u); + for (size_t i = 0; i < count; ++i) { + DCHECK_EQ(dst[i].load(std::memory_order_relaxed).index, 0u); + DCHECK(dst[i].load(std::memory_order_relaxed).object.IsNull()); + mirror::DexCachePair<T> source = src[i].load(std::memory_order_relaxed); + if (source.index != 0u || !source.object.IsNull()) { + dst[i].store(source, std::memory_order_relaxed); + } + } +} + bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( gc::space::ImageSpace* space, Handle<mirror::ClassLoader> class_loader, @@ -1240,7 +1287,10 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( if (dex_file->NumStringIds() < num_strings) { num_strings = dex_file->NumStringIds(); } - const size_t num_types = dex_file->NumTypeIds(); + size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize; + if (dex_file->NumTypeIds() < num_types) { + num_types = dex_file->NumTypeIds(); + } const size_t num_methods = dex_file->NumMethodIds(); const size_t num_fields = dex_file->NumFieldIds(); size_t num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize; @@ -1260,28 +1310,14 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( mirror::StringDexCacheType* const image_resolved_strings = dex_cache->GetStrings(); mirror::StringDexCacheType* const strings = reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset()); - for (size_t j = 0; j < num_strings; ++j) { - DCHECK_EQ(strings[j].load(std::memory_order_relaxed).index, 0u); - DCHECK(strings[j].load(std::memory_order_relaxed).object.IsNull()); - strings[j].store(image_resolved_strings[j].load(std::memory_order_relaxed), - std::memory_order_relaxed); - } - mirror::StringDexCachePair::Initialize(strings); + CopyDexCachePairs(image_resolved_strings, num_strings, strings); dex_cache->SetStrings(strings); } if (num_types != 0u) { - GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes(); - GcRoot<mirror::Class>* const types = - reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset()); - for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) { - DCHECK(types[j].IsNull()); - } - CopyNonNull(image_resolved_types, - num_types, - types, - [](const GcRoot<mirror::Class>& elem) { - return elem.IsNull(); - }); + mirror::TypeDexCacheType* const image_resolved_types = dex_cache->GetResolvedTypes(); + mirror::TypeDexCacheType* const types = + reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset()); + CopyDexCachePairs(image_resolved_types, num_types, types); dex_cache->SetResolvedTypes(types); } if (num_methods != 0u) { @@ -1322,15 +1358,7 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( mirror::MethodTypeDexCacheType* const method_types = reinterpret_cast<mirror::MethodTypeDexCacheType*>( raw_arrays + layout.MethodTypesOffset()); - for (size_t j = 0; j < num_method_types; ++j) { - DCHECK_EQ(method_types[j].load(std::memory_order_relaxed).index, 0u); - DCHECK(method_types[j].load(std::memory_order_relaxed).object.IsNull()); - method_types[j].store( - image_resolved_method_types[j].load(std::memory_order_relaxed), - std::memory_order_relaxed); - } - - mirror::MethodTypeDexCachePair::Initialize(method_types); + CopyDexCachePairs(image_resolved_method_types, num_method_types, method_types); dex_cache->SetResolvedMethodTypes(method_types); } if (num_call_sites != 0u) { @@ -1360,11 +1388,11 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( } if (kIsDebugBuild) { CHECK(new_class_set != nullptr); - GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes(); + mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes(); const size_t num_types = dex_cache->NumResolvedTypes(); - for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) { + for (size_t j = 0; j != num_types; ++j) { // The image space is not yet added to the heap, avoid read barriers. - ObjPtr<mirror::Class> klass = types[j].Read(); + ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read(); if (space->HasAddress(klass.Ptr())) { DCHECK(!klass->IsErroneous()) << klass->GetStatus(); auto it = new_class_set->Find(ClassTable::TableSlot(klass)); @@ -1723,9 +1751,9 @@ bool ClassLinker::AddImageSpace( // The current dex file field is bogus, overwrite it so that we can get the dex file in the // loop below. dex_cache->SetDexFile(dex_file.get()); - GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes(); + mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes(); for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) { - ObjPtr<mirror::Class> klass = types[j].Read(); + ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read(); if (klass != nullptr) { DCHECK(!klass->IsErroneous()) << klass->GetStatus(); } @@ -1893,6 +1921,12 @@ bool ClassLinker::AddImageSpace( VerifyClassInTableArtMethodVisitor visitor2(class_table); header.VisitPackedArtMethods(&visitor2, space->Begin(), kRuntimePointerSize); } + if (app_image) { + // TODO: Restrict this check to debug builds. Bug: 34839984 + VerifyDirectInterfacesInTableClassVisitor visitor(class_loader.Get()); + class_table->Visit(visitor); + visitor.Check(); + } VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time); return true; } @@ -1908,6 +1942,13 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { const bool tracing_enabled = Trace::IsTracingEnabled(); Thread* const self = Thread::Current(); WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + if (kUseReadBarrier) { + // We do not track new roots for CC. + DCHECK_EQ(0, flags & (kVisitRootFlagNewRoots | + kVisitRootFlagClearRootLog | + kVisitRootFlagStartLoggingNewRoots | + kVisitRootFlagStopLoggingNewRoots)); + } if ((flags & kVisitRootFlagAllRoots) != 0) { // Argument for how root visiting deals with ArtField and ArtMethod roots. // There is 3 GC cases to handle: @@ -1937,7 +1978,7 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { root.VisitRoot(visitor, RootInfo(kRootVMInternal)); } } - } else if ((flags & kVisitRootFlagNewRoots) != 0) { + } else if (!kUseReadBarrier && (flags & kVisitRootFlagNewRoots) != 0) { for (auto& root : new_class_roots_) { ObjPtr<mirror::Class> old_ref = root.Read<kWithoutReadBarrier>(); root.VisitRoot(visitor, RootInfo(kRootStickyClass)); @@ -1958,13 +1999,13 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { } } } - if ((flags & kVisitRootFlagClearRootLog) != 0) { + if (!kUseReadBarrier && (flags & kVisitRootFlagClearRootLog) != 0) { new_class_roots_.clear(); new_bss_roots_boot_oat_files_.clear(); } - if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) { + if (!kUseReadBarrier && (flags & kVisitRootFlagStartLoggingNewRoots) != 0) { log_new_roots_ = true; - } else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) { + } else if (!kUseReadBarrier && (flags & kVisitRootFlagStopLoggingNewRoots) != 0) { log_new_roots_ = false; } // We deliberately ignore the class roots in the image since we @@ -3757,10 +3798,14 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, ObjPtr<mirror::C } void ClassLinker::WriteBarrierForBootOatFileBssRoots(const OatFile* oat_file) { - WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - DCHECK(!oat_file->GetBssGcRoots().empty()) << oat_file->GetLocation(); - if (log_new_roots_ && !ContainsElement(new_bss_roots_boot_oat_files_, oat_file)) { - new_bss_roots_boot_oat_files_.push_back(oat_file); + if (!kUseReadBarrier) { + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + DCHECK(!oat_file->GetBssGcRoots().empty()) << oat_file->GetLocation(); + if (log_new_roots_ && !ContainsElement(new_bss_roots_boot_oat_files_, oat_file)) { + new_bss_roots_boot_oat_files_.push_back(oat_file); + } + } else { + LOG(FATAL) << "UNREACHABLE"; } } @@ -4501,6 +4546,108 @@ bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_ini return CanWeInitializeClass(super_class, can_init_statics, can_init_parents); } +std::string DescribeSpace(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { + std::ostringstream oss; + gc::Heap* heap = Runtime::Current()->GetHeap(); + gc::space::ContinuousSpace* cs = heap->FindContinuousSpaceFromAddress(klass.Ptr()); + if (cs != nullptr) { + if (cs->IsImageSpace()) { + oss << "image/" << cs->GetName() << "/" << cs->AsImageSpace()->GetImageFilename(); + } else { + oss << "continuous/" << cs->GetName(); + } + } else { + gc::space::DiscontinuousSpace* ds = + heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true); + if (ds != nullptr) { + oss << "discontinuous/" << ds->GetName(); + } else { + oss << "invalid"; + } + } + return oss.str(); +} + +std::string DescribeLoaders(ObjPtr<mirror::Class> klass, const char* iface_descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + std::ostringstream oss; + uint32_t hash = ComputeModifiedUtf8Hash(iface_descriptor); + ScopedObjectAccessUnchecked soa(Thread::Current()); + ObjPtr<mirror::Class> path_class_loader = + soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader); + ObjPtr<mirror::Class> dex_class_loader = + soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader); + + // Print the class loader chain. + bool found_iface; + const char* loader_separator = ""; + for (ObjPtr<mirror::ClassLoader> loader = klass->GetClassLoader(); + loader != nullptr; + loader = loader->GetParent()) { + oss << loader_separator << loader->GetClass()->PrettyDescriptor(); + loader_separator = ";"; + // If we didn't find the interface yet, try to find it in the current class loader. + if (!found_iface) { + ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader); + ObjPtr<mirror::Class> iface = + (table != nullptr) ? table->Lookup(iface_descriptor, hash) : nullptr; + if (iface != nullptr) { + found_iface = true; + oss << "[hit:" << DescribeSpace(iface) << "]"; + } + } + + // For PathClassLoader or DexClassLoader also dump the dex file locations. + if (loader->GetClass() == path_class_loader || loader->GetClass() == dex_class_loader) { + ArtField* const cookie_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); + ArtField* const dex_file_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + ObjPtr<mirror::Object> dex_path_list = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)-> + GetObject(loader); + if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) { + ObjPtr<mirror::Object> dex_elements_obj = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> + GetObject(dex_path_list); + if (dex_elements_obj != nullptr) { + ObjPtr<mirror::ObjectArray<mirror::Object>> dex_elements = + dex_elements_obj->AsObjectArray<mirror::Object>(); + oss << "("; + const char* path_separator = ""; + for (int32_t i = 0; i != dex_elements->GetLength(); ++i) { + ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i); + ObjPtr<mirror::Object> dex_file = + (element != nullptr) ? dex_file_field->GetObject(element) : nullptr; + ObjPtr<mirror::LongArray> long_array = + (dex_file != nullptr) ? cookie_field->GetObject(dex_file)->AsLongArray() : nullptr; + if (long_array != nullptr) { + int32_t long_array_size = long_array->GetLength(); + // First element is the oat file. + for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) { + const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>( + static_cast<uintptr_t>(long_array->GetWithoutChecks(j))); + oss << path_separator << cp_dex_file->GetLocation(); + path_separator = ":"; + } + } + } + oss << ")"; + } + } + } + } + + // Do a paranoid check that the `klass` itself is in the class table. + ClassTable* table = + Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(klass->GetClassLoader()); + ObjPtr<mirror::Class> k = (table != nullptr) ? table->LookupByDescriptor(klass) : nullptr; + if (k != klass) { + oss << "{FAIL:" << k.Ptr() << "!=" << klass.Ptr() << "}"; + } + return oss.str(); +} + bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, bool can_init_statics, bool can_init_parents) { // see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol @@ -4648,7 +4795,15 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, MutableHandle<mirror::Class> handle_scope_iface(hs_iface.NewHandle<mirror::Class>(nullptr)); for (size_t i = 0; i < num_direct_interfaces; i++) { handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass.Get(), i)); - CHECK(handle_scope_iface != nullptr); + if (UNLIKELY(handle_scope_iface == nullptr)) { + const char* iface_descriptor = + klass->GetDexFile().StringByTypeIdx(klass->GetDirectInterfaceTypeIdx(i)); + LOG(FATAL) << "Check failed: handle_scope_iface != nullptr " + << "Debug data for bug 34839984: " + << klass->PrettyDescriptor() << " iface #" << i << " " << iface_descriptor + << " space: " << DescribeSpace(klass.Get()) + << " loaders: " << DescribeLoaders(klass.Get(), iface_descriptor); + } CHECK(handle_scope_iface->IsInterface()); if (handle_scope_iface->HasBeenRecursivelyInitialized()) { // We have already done this for this interface. Skip it. @@ -7759,7 +7914,9 @@ mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, uint32_t utf16_length; const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); ObjPtr<mirror::String> string = intern_table_->InternStrong(utf16_length, utf8_data); - dex_cache->SetResolvedString(string_idx, string); + if (string != nullptr) { + dex_cache->SetResolvedString(string_idx, string); + } return string.Ptr(); } @@ -7800,11 +7957,16 @@ ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(const DexFile& dex_file, // Find the class in the loaded classes table. type = LookupClass(self, descriptor, hash, class_loader.Ptr()); } + if (type != nullptr) { + if (type->IsResolved()) { + dex_cache->SetResolvedType(type_idx, type); + } else { + type = nullptr; + } + } } - if (type != nullptr && type->IsResolved()) { - return type.Ptr(); - } - return nullptr; + DCHECK(type == nullptr || type->IsResolved()); + return type; } mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, @@ -7824,6 +7986,12 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, Thread::PoisonObjectPointersIfDebug(); ObjPtr<mirror::Class> resolved = dex_cache->GetResolvedType(type_idx); if (resolved == nullptr) { + // TODO: Avoid this lookup as it duplicates work done in FindClass(). It is here + // as a workaround for FastNative JNI to avoid AssertNoPendingException() when + // trying to resolve annotations while an exception may be pending. Bug: 34659969 + resolved = LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()); + } + if (resolved == nullptr) { Thread* self = Thread::Current(); const char* descriptor = dex_file.StringByTypeIdx(type_idx); resolved = FindClass(self, descriptor, class_loader); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index e27a53d15c..33eed3c8e3 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -266,10 +266,6 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - mirror::Class* ResolveType(dex::TypeIndex type_idx, ArtField* referrer) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Look up a resolved type with the given ID from the DexFile. The ClassLoader is used to search // for the type, since it may be referenced from but not contained within the given DexFile. ObjPtr<mirror::Class> LookupResolvedType(const DexFile& dex_file, @@ -277,6 +273,10 @@ class ClassLinker { ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_); + static ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_idx, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); // Resolve a type with the given ID from the DexFile, storing the // result in DexCache. The ClassLoader is used to search for the diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 07f3744b69..21cdede06b 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -935,7 +935,7 @@ TEST_F(ClassLinkerTest, LookupResolvedType) { class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()), klass); // Zero out the resolved type and make sure LookupResolvedType still finds it. - dex_cache->SetResolvedType(type_idx, nullptr); + dex_cache->ClearResolvedType(type_idx); EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); EXPECT_OBJ_PTR_EQ( class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()), @@ -970,7 +970,7 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeArray) { class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), array_klass); // Zero out the resolved type and make sure LookupResolvedType() still finds it. - dex_cache->SetResolvedType(array_idx, nullptr); + dex_cache->ClearResolvedType(array_idx); EXPECT_TRUE(dex_cache->GetResolvedType(array_idx) == nullptr); EXPECT_OBJ_PTR_EQ( class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), @@ -993,7 +993,7 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) { class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); // Zero out the resolved type and make sure LookupResolvedType still finds it. - dex_cache->SetResolvedType(type_idx, nullptr); + dex_cache->ClearResolvedType(type_idx); EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); EXPECT_OBJ_PTR_EQ( class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), @@ -1011,7 +1011,7 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) { class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); // Zero out the resolved type and make sure LookupResolvedType() still finds it. - dex_cache->SetResolvedType(type_idx, nullptr); + dex_cache->ClearResolvedType(type_idx); EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); EXPECT_OBJ_PTR_EQ( class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 17e3729a20..d7abe2a8f4 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -223,9 +223,9 @@ class CheckJniAbortCatcher { return; \ } -#define TEST_DISABLED_FOR_READ_BARRIER_ON_X86() \ - if (kUseReadBarrier && kRuntimeISA == kX86) { \ - printf("WARNING: TEST DISABLED FOR READ BARRIER ON X86\n"); \ +#define TEST_DISABLED_FOR_X86() \ + if (kRuntimeISA == kX86) { \ + printf("WARNING: TEST DISABLED FOR X86\n"); \ return; \ } diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index a95f94cabb..d39ea35a90 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -299,6 +299,7 @@ mirror::Object* ProcessEncodedAnnotation(Handle<mirror::Class> klass, const uint return result.GetL(); } +template <bool kTransactionActive> bool ProcessAnnotationValue(Handle<mirror::Class> klass, const uint8_t** annotation_ptr, DexFile::AnnotationValue* annotation_value, @@ -409,22 +410,21 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, } PointerSize pointer_size = class_linker->GetImagePointerSize(); set_object = true; - DCHECK(!Runtime::Current()->IsActiveTransaction()); if (method->IsConstructor()) { if (pointer_size == PointerSize::k64) { element_object = mirror::Constructor::CreateFromArtMethod<PointerSize::k64, - false>(self, method); + kTransactionActive>(self, method); } else { element_object = mirror::Constructor::CreateFromArtMethod<PointerSize::k32, - false>(self, method); + kTransactionActive>(self, method); } } else { if (pointer_size == PointerSize::k64) { element_object = mirror::Method::CreateFromArtMethod<PointerSize::k64, - false>(self, method); + kTransactionActive>(self, method); } else { element_object = mirror::Method::CreateFromArtMethod<PointerSize::k32, - false>(self, method); + kTransactionActive>(self, method); } } if (element_object == nullptr) { @@ -449,9 +449,11 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, set_object = true; PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); if (pointer_size == PointerSize::k64) { - element_object = mirror::Field::CreateFromArtField<PointerSize::k64>(self, field, true); + element_object = mirror::Field::CreateFromArtField<PointerSize::k64, + kTransactionActive>(self, field, true); } else { - element_object = mirror::Field::CreateFromArtField<PointerSize::k32>(self, field, true); + element_object = mirror::Field::CreateFromArtField<PointerSize::k32, + kTransactionActive>(self, field, true); } if (element_object == nullptr) { return false; @@ -497,45 +499,49 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, } DexFile::AnnotationValue new_annotation_value; for (uint32_t i = 0; i < size; ++i) { - if (!ProcessAnnotationValue(klass, &annotation, &new_annotation_value, - component_type, DexFile::kPrimitivesOrObjects)) { + if (!ProcessAnnotationValue<kTransactionActive>(klass, + &annotation, + &new_annotation_value, + component_type, + DexFile::kPrimitivesOrObjects)) { return false; } if (!component_type->IsPrimitive()) { mirror::Object* obj = new_annotation_value.value_.GetL(); - new_array->AsObjectArray<mirror::Object>()->SetWithoutChecks<false>(i, obj); + new_array->AsObjectArray<mirror::Object>()-> + SetWithoutChecks<kTransactionActive>(i, obj); } else { switch (new_annotation_value.type_) { case DexFile::kDexAnnotationByte: - new_array->AsByteArray()->SetWithoutChecks<false>( + new_array->AsByteArray()->SetWithoutChecks<kTransactionActive>( i, new_annotation_value.value_.GetB()); break; case DexFile::kDexAnnotationShort: - new_array->AsShortArray()->SetWithoutChecks<false>( + new_array->AsShortArray()->SetWithoutChecks<kTransactionActive>( i, new_annotation_value.value_.GetS()); break; case DexFile::kDexAnnotationChar: - new_array->AsCharArray()->SetWithoutChecks<false>( + new_array->AsCharArray()->SetWithoutChecks<kTransactionActive>( i, new_annotation_value.value_.GetC()); break; case DexFile::kDexAnnotationInt: - new_array->AsIntArray()->SetWithoutChecks<false>( + new_array->AsIntArray()->SetWithoutChecks<kTransactionActive>( i, new_annotation_value.value_.GetI()); break; case DexFile::kDexAnnotationLong: - new_array->AsLongArray()->SetWithoutChecks<false>( + new_array->AsLongArray()->SetWithoutChecks<kTransactionActive>( i, new_annotation_value.value_.GetJ()); break; case DexFile::kDexAnnotationFloat: - new_array->AsFloatArray()->SetWithoutChecks<false>( + new_array->AsFloatArray()->SetWithoutChecks<kTransactionActive>( i, new_annotation_value.value_.GetF()); break; case DexFile::kDexAnnotationDouble: - new_array->AsDoubleArray()->SetWithoutChecks<false>( + new_array->AsDoubleArray()->SetWithoutChecks<kTransactionActive>( i, new_annotation_value.value_.GetD()); break; case DexFile::kDexAnnotationBoolean: - new_array->AsBooleanArray()->SetWithoutChecks<false>( + new_array->AsBooleanArray()->SetWithoutChecks<kTransactionActive>( i, new_annotation_value.value_.GetZ()); break; default: @@ -611,8 +617,11 @@ mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, annotation_method->GetReturnType(true /* resolve */))); DexFile::AnnotationValue annotation_value; - if (!ProcessAnnotationValue(klass, annotation, &annotation_value, method_return, - DexFile::kAllObjects)) { + if (!ProcessAnnotationValue<false>(klass, + annotation, + &annotation_value, + method_return, + DexFile::kAllObjects)) { return nullptr; } Handle<mirror::Object> value_object(hs.NewHandle(annotation_value.value_.GetL())); @@ -716,8 +725,18 @@ mirror::Object* GetAnnotationValue(Handle<mirror::Class> klass, return nullptr; } DexFile::AnnotationValue annotation_value; - if (!ProcessAnnotationValue(klass, &annotation, &annotation_value, array_class, - DexFile::kAllObjects)) { + bool result = Runtime::Current()->IsActiveTransaction() + ? ProcessAnnotationValue<true>(klass, + &annotation, + &annotation_value, + array_class, + DexFile::kAllObjects) + : ProcessAnnotationValue<false>(klass, + &annotation, + &annotation_value, + array_class, + DexFile::kAllObjects); + if (!result) { return nullptr; } if (annotation_value.type_ != expected_type) { @@ -949,8 +968,11 @@ mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { StackHandleScope<2> hs(Thread::Current()); Handle<mirror::Class> h_klass(hs.NewHandle(klass)); Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */))); - if (!ProcessAnnotationValue(h_klass, &annotation, &annotation_value, return_type, - DexFile::kAllObjects)) { + if (!ProcessAnnotationValue<false>(h_klass, + &annotation, + &annotation_value, + return_type, + DexFile::kAllObjects)) { return nullptr; } return annotation_value.value_.GetL(); @@ -1201,8 +1223,11 @@ mirror::Class* GetEnclosingClass(Handle<mirror::Class> klass) { return nullptr; } DexFile::AnnotationValue annotation_value; - if (!ProcessAnnotationValue(klass, &annotation, &annotation_value, - ScopedNullHandle<mirror::Class>(), DexFile::kAllRaw)) { + if (!ProcessAnnotationValue<false>(klass, + &annotation, + &annotation_value, + ScopedNullHandle<mirror::Class>(), + DexFile::kAllRaw)) { return nullptr; } if (annotation_value.type_ != DexFile::kDexAnnotationMethod) { @@ -1252,9 +1277,11 @@ bool GetInnerClass(Handle<mirror::Class> klass, mirror::String** name) { return false; } DexFile::AnnotationValue annotation_value; - if (!ProcessAnnotationValue(klass, &annotation, &annotation_value, - ScopedNullHandle<mirror::Class>(), - DexFile::kAllObjects)) { + if (!ProcessAnnotationValue<false>(klass, + &annotation, + &annotation_value, + ScopedNullHandle<mirror::Class>(), + DexFile::kAllObjects)) { return false; } if (annotation_value.type_ != DexFile::kDexAnnotationNull && @@ -1283,8 +1310,11 @@ bool GetInnerClassFlags(Handle<mirror::Class> klass, uint32_t* flags) { return false; } DexFile::AnnotationValue annotation_value; - if (!ProcessAnnotationValue(klass, &annotation, &annotation_value, - ScopedNullHandle<mirror::Class>(), DexFile::kAllRaw)) { + if (!ProcessAnnotationValue<false>(klass, + &annotation, + &annotation_value, + ScopedNullHandle<mirror::Class>(), + DexFile::kAllRaw)) { return false; } if (annotation_value.type_ != DexFile::kDexAnnotationInt) { diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index 69c6151d9e..51678699b4 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -70,6 +70,11 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location, // rather than use this flag. args.push_back("-Xnorelocate"); + ScratchFile profile_file; + if (CompilerFilter::DependsOnProfile(filter)) { + args.push_back("--profile-file=" + profile_file.GetFilename()); + } + if (pic) { args.push_back("--compile-pic"); } diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 28aca6c905..3bc49b8506 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -709,10 +709,10 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, return resolved_method; } else if (type == kSuper) { // TODO This lookup is rather slow. - dex::TypeIndex method_type_idx = - referrer->GetDexFile()->GetMethodId(method_idx).class_idx_; - mirror::Class* method_reference_class = - referrer->GetDexCache()->GetResolvedType(method_type_idx); + ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache(); + dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_; + ObjPtr<mirror::Class> method_reference_class = ClassLinker::LookupResolvedType( + method_type_idx, dex_cache, referrer->GetClassLoader()); if (method_reference_class == nullptr) { // Need to do full type resolution... return nullptr; diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index 699cf91c70..47c6b514d5 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -32,23 +32,33 @@ namespace art { static inline void BssWriteBarrier(ArtMethod* outer_method) REQUIRES_SHARED(Locks::mutator_lock_) { - // For AOT code, we need a write barrier for the class loader that holds - // the GC roots in the .bss. - const DexFile* dex_file = outer_method->GetDexFile(); + // For non-CC AOT code, we need a write barrier for the class loader that holds the + // GC roots in the .bss. For CC, we do not need to do anything because the roots + // we're storing are all referencing to-space and do not need to be re-visited. + // However, we do the DCHECK() for the registration of oat files with .bss sections. + const DexFile* dex_file = + (kUseReadBarrier && !kIsDebugBuild) ? nullptr : outer_method->GetDexFile(); if (dex_file != nullptr && dex_file->GetOatDexFile() != nullptr && !dex_file->GetOatDexFile()->GetOatFile()->GetBssGcRoots().empty()) { - mirror::ClassLoader* class_loader = outer_method->GetClassLoader(); - if (class_loader != nullptr) { - DCHECK(!class_loader->GetClassTable()->InsertOatFile(dex_file->GetOatDexFile()->GetOatFile())) + ObjPtr<mirror::ClassLoader> class_loader = outer_method->GetClassLoader(); + if (kIsDebugBuild) { + ClassTable* class_table = + Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader); + CHECK(class_table != nullptr && + !class_table->InsertOatFile(dex_file->GetOatDexFile()->GetOatFile())) << "Oat file with .bss GC roots was not registered in class table: " << dex_file->GetOatDexFile()->GetOatFile()->GetLocation(); - // Note that we emit the barrier before the compiled code stores the String or Class - // as a GC root. This is OK as there is no suspend point point in between. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); - } else { - Runtime::Current()->GetClassLinker()->WriteBarrierForBootOatFileBssRoots( - dex_file->GetOatDexFile()->GetOatFile()); + } + if (!kUseReadBarrier) { + if (class_loader != nullptr) { + // Note that we emit the barrier before the compiled code stores the String or Class + // as a GC root. This is OK as there is no suspend point point in between. + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); + } else { + Runtime::Current()->GetClassLinker()->WriteBarrierForBootOatFileBssRoots( + dex_file->GetOatDexFile()->GetOatFile()); + } } } } diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h index 7014357527..eef4fba20d 100644 --- a/runtime/gc/collector_type.h +++ b/runtime/gc/collector_type.h @@ -55,6 +55,8 @@ enum CollectorType { kCollectorTypeClassLinker, // JIT Code cache fake collector. kCollectorTypeJitCodeCache, + // Hprof fake collector. + kCollectorTypeHprof, // Fake collector for installing/removing a system-weak holder. kCollectorTypeAddRemoveSystemWeakHolder, }; diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc index 7ff845d98e..9e34346686 100644 --- a/runtime/gc/gc_cause.cc +++ b/runtime/gc/gc_cause.cc @@ -39,6 +39,7 @@ const char* PrettyCause(GcCause cause) { case kGcCauseClassLinker: return "ClassLinker"; case kGcCauseJitCodeCache: return "JitCodeCache"; case kGcCauseAddRemoveSystemWeakHolder: return "SystemWeakHolder"; + case kGcCauseHprof: return "Hprof"; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h index f54f0e4901..9b285b12a4 100644 --- a/runtime/gc/gc_cause.h +++ b/runtime/gc/gc_cause.h @@ -53,6 +53,8 @@ enum GcCause { kGcCauseJitCodeCache, // Not a real GC cause, used to add or remove system-weak holders. kGcCauseAddRemoveSystemWeakHolder, + // Not a real GC cause, used to hprof running in the middle of GC. + kGcCauseHprof, }; const char* PrettyCause(GcCause cause); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index a78de37796..34afa2aa7e 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -29,6 +29,7 @@ #include "base/arena_allocator.h" #include "base/dumpable.h" #include "base/histogram-inl.h" +#include "base/memory_tool.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" @@ -188,6 +189,7 @@ Heap::Heap(size_t initial_size, disable_thread_flip_count_(0), thread_flip_running_(false), collector_type_running_(kCollectorTypeNone), + thread_running_gc_(nullptr), last_gc_type_(collector::kGcTypeNone), next_gc_type_(collector::kGcTypePartial), capacity_(capacity), @@ -286,7 +288,7 @@ Heap::Heap(size_t initial_size, if (foreground_collector_type_ == kCollectorTypeCC) { // Need to use a low address so that we can allocate a contiguous // 2 * Xmx space when there's no image (dex2oat for target). -#if defined(__LP64__) +#if defined(__LP64__) || !defined(ADDRESS_SANITIZER) CHECK_GE(300 * MB, non_moving_space_capacity); requested_alloc_space_begin = reinterpret_cast<uint8_t*>(300 * MB) - non_moving_space_capacity; #else @@ -367,7 +369,7 @@ Heap::Heap(size_t initial_size, &error_str)); CHECK(non_moving_space_mem_map != nullptr) << error_str; // Try to reserve virtual memory at a lower address if we have a separate non moving space. -#if defined(__LP64__) +#if defined(__LP64__) || !defined(ADDRESS_SANITIZER) request_begin = reinterpret_cast<uint8_t*>(300 * MB); #else // For 32-bit, use 0x20000000 because asan reserves 0x04000000 - 0x20000000. @@ -935,7 +937,14 @@ void Heap::VisitObjectsInternalRegionSpace(ObjectCallback callback, void* arg) { // calls VerifyHeapReferences() as part of the zygote compaction // which then would call here without the moving GC disabled, // which is fine. - DCHECK(IsMovingGCDisabled(self)); + bool is_thread_running_gc = false; + if (kIsDebugBuild) { + MutexLock mu(self, *gc_complete_lock_); + is_thread_running_gc = self == thread_running_gc_; + } + // If we are not the thread running the GC on in a GC exclusive region, then moving GC + // must be disabled. + DCHECK(is_thread_running_gc || IsMovingGCDisabled(self)); } region_space_->Walk(callback, arg); } @@ -1386,6 +1395,7 @@ void Heap::StartGC(Thread* self, GcCause cause, CollectorType collector_type) { // Ensure there is only one GC at a time. WaitForGcToCompleteLocked(cause, self); collector_type_running_ = collector_type; + thread_running_gc_ = self; } void Heap::TrimSpaces(Thread* self) { @@ -2785,6 +2795,7 @@ void Heap::FinishGC(Thread* self, collector::GcType gc_type) { } // Reset. running_collection_is_blocking_ = false; + thread_running_gc_ = nullptr; // Wake anyone who may have been waiting for the GC to complete. gc_complete_cond_->Broadcast(self); } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index a4d300b110..1a782b409b 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -1189,6 +1189,9 @@ class Heap { // True while the garbage collector is running. volatile CollectorType collector_type_running_ GUARDED_BY(gc_complete_lock_); + // The thread currently running the GC. + volatile Thread* thread_running_gc_ GUARDED_BY(gc_complete_lock_); + // Last Gc type we ran. Used by WaitForConcurrentGc to know which Gc was waited on. volatile collector::GcType last_gc_type_ GUARDED_BY(gc_complete_lock_); collector::GcType next_gc_type_; diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 2163a20e87..010ef1156a 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1237,9 +1237,9 @@ class ImageSpaceLoader { } dex_cache->FixupStrings<kWithoutReadBarrier>(new_strings, fixup_adapter); } - GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes(); + mirror::TypeDexCacheType* types = dex_cache->GetResolvedTypes(); if (types != nullptr) { - GcRoot<mirror::Class>* new_types = fixup_adapter.ForwardObject(types); + mirror::TypeDexCacheType* new_types = fixup_adapter.ForwardObject(types); if (types != new_types) { dex_cache->SetResolvedTypes(new_types); } diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index f390645a32..e59c4bb28e 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -50,6 +50,7 @@ #include "gc_root.h" #include "gc/accounting/heap_bitmap.h" #include "gc/allocation_record.h" +#include "gc/scoped_gc_critical_section.h" #include "gc/heap.h" #include "gc/space/space.h" #include "globals.h" @@ -1449,22 +1450,15 @@ void Hprof::VisitRoot(mirror::Object* obj, const RootInfo& info) { // Otherwise, "filename" is used to create an output file. void DumpHeap(const char* filename, int fd, bool direct_to_ddms) { CHECK(filename != nullptr); - Thread* self = Thread::Current(); - gc::Heap* heap = Runtime::Current()->GetHeap(); - if (heap->IsGcConcurrentAndMoving()) { - // Need to take a heap dump while GC isn't running. See the - // comment in Heap::VisitObjects(). - heap->IncrementDisableMovingGC(self); - } - { - ScopedSuspendAll ssa(__FUNCTION__, true /* long suspend */); - Hprof hprof(filename, fd, direct_to_ddms); - hprof.Dump(); - } - if (heap->IsGcConcurrentAndMoving()) { - heap->DecrementDisableMovingGC(self); - } + // Need to take a heap dump while GC isn't running. See the comment in Heap::VisitObjects(). + // Also we need the critical section to avoid visiting the same object twice. See b/34967844 + gc::ScopedGCCriticalSection gcs(self, + gc::kGcCauseHprof, + gc::kCollectorTypeHprof); + ScopedSuspendAll ssa(__FUNCTION__, true /* long suspend */); + Hprof hprof(filename, fd, direct_to_ddms); + hprof.Dump(); } } // namespace hprof diff --git a/runtime/image.cc b/runtime/image.cc index 54b099eb14..4e6da79205 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', '6', '\0' }; // Erroneous resolved class. +const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '8', '\0' }; // hash-based DexCache types ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 66f14b99d8..af0478c1eb 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -175,56 +175,61 @@ static mirror::String* GetClassName(Thread* self, ShadowFrame* shadow_frame, siz return param->AsString(); } -void UnstartedRuntime::UnstartedClassForName( - Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { +void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self, + ShadowFrame* shadow_frame, + JValue* result, + size_t arg_offset, + bool long_form, + const char* caller) { mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset); if (class_name == nullptr) { return; } + bool initialize_class; + mirror::ClassLoader* class_loader; + if (long_form) { + initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0; + class_loader = down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset + 2)); + } else { + initialize_class = true; + // TODO: This is really only correct for the boot classpath, and for robustness we should + // check the caller. + class_loader = nullptr; + } + + ScopedObjectAccessUnchecked soa(self); + if (class_loader != nullptr && !ClassLinker::IsBootClassLoader(soa, class_loader)) { + AbortTransactionOrFail(self, + "Only the boot classloader is supported: %s", + mirror::Object::PrettyTypeOf(class_loader).c_str()); + return; + } + StackHandleScope<1> hs(self); Handle<mirror::String> h_class_name(hs.NewHandle(class_name)); UnstartedRuntimeFindClass(self, h_class_name, ScopedNullHandle<mirror::ClassLoader>(), result, - "Class.forName", - true, + caller, + initialize_class, false); CheckExceptionGenerateClassNotFound(self); } +void UnstartedRuntime::UnstartedClassForName( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { + UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, false, "Class.forName"); +} + void UnstartedRuntime::UnstartedClassForNameLong( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { - mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset); - if (class_name == nullptr) { - return; - } - bool initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0; - mirror::ClassLoader* class_loader = - down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset + 2)); - StackHandleScope<2> hs(self); - Handle<mirror::String> h_class_name(hs.NewHandle(class_name)); - Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader)); - UnstartedRuntimeFindClass(self, h_class_name, h_class_loader, result, "Class.forName", - initialize_class, false); - CheckExceptionGenerateClassNotFound(self); + UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, true, "Class.forName"); } void UnstartedRuntime::UnstartedClassClassForName( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { - mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset); - if (class_name == nullptr) { - return; - } - bool initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0; - mirror::ClassLoader* class_loader = - down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset + 2)); - StackHandleScope<2> hs(self); - Handle<mirror::String> h_class_name(hs.NewHandle(class_name)); - Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader)); - UnstartedRuntimeFindClass(self, h_class_name, h_class_loader, result, "Class.classForName", - initialize_class, false); - CheckExceptionGenerateClassNotFound(self); + UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, true, "Class.classForName"); } void UnstartedRuntime::UnstartedClassNewInstance( @@ -440,6 +445,20 @@ void UnstartedRuntime::UnstartedClassGetInnerClassFlags( result->SetI(mirror::Class::GetInnerClassFlags(klass, default_value)); } +void UnstartedRuntime::UnstartedClassGetSignatureAnnotation( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { + StackHandleScope<1> hs(self); + Handle<mirror::Class> klass(hs.NewHandle( + reinterpret_cast<mirror::Class*>(shadow_frame->GetVRegReference(arg_offset)))); + + if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { + result->SetL(nullptr); + return; + } + + result->SetL(annotations::GetSignatureAnnotationForClass(klass)); +} + void UnstartedRuntime::UnstartedClassIsAnonymousClass( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { StackHandleScope<1> hs(self); @@ -613,6 +632,72 @@ void UnstartedRuntime::UnstartedClassLoaderGetResourceAsStream( GetResourceAsStream(self, shadow_frame, result, arg_offset); } +void UnstartedRuntime::UnstartedConstructorNewInstance0( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { + // This is a cutdown version of java_lang_reflect_Constructor.cc's implementation. + StackHandleScope<4> hs(self); + Handle<mirror::Constructor> m = hs.NewHandle( + reinterpret_cast<mirror::Constructor*>(shadow_frame->GetVRegReference(arg_offset))); + Handle<mirror::ObjectArray<mirror::Object>> args = hs.NewHandle( + reinterpret_cast<mirror::ObjectArray<mirror::Object>*>( + shadow_frame->GetVRegReference(arg_offset + 1))); + Handle<mirror::Class> c(hs.NewHandle(m->GetDeclaringClass())); + if (UNLIKELY(c->IsAbstract())) { + AbortTransactionOrFail(self, "Cannot handle abstract classes"); + return; + } + // Verify that we can access the class. + if (!m->IsAccessible() && !c->IsPublic()) { + // Go 2 frames back, this method is always called from newInstance0, which is called from + // Constructor.newInstance(Object... args). + ObjPtr<mirror::Class> caller = GetCallingClass(self, 2); + // If caller is null, then we called from JNI, just avoid the check since JNI avoids most + // access checks anyways. TODO: Investigate if this the correct behavior. + if (caller != nullptr && !caller->CanAccess(c.Get())) { + AbortTransactionOrFail(self, "Cannot access class"); + return; + } + } + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, c, true, true)) { + DCHECK(self->IsExceptionPending()); + return; + } + if (c->IsClassClass()) { + AbortTransactionOrFail(self, "new Class() is not supported"); + return; + } + + // String constructor is replaced by a StringFactory method in InvokeMethod. + if (c->IsStringClass()) { + // We don't support strings. + AbortTransactionOrFail(self, "String construction is not supported"); + return; + } + + Handle<mirror::Object> receiver = hs.NewHandle(c->AllocObject(self)); + if (receiver == nullptr) { + AbortTransactionOrFail(self, "Could not allocate"); + return; + } + + // It's easier to use reflection to make the call, than create the uint32_t array. + { + ScopedObjectAccessUnchecked soa(self); + ScopedLocalRef<jobject> method_ref(self->GetJniEnv(), + soa.AddLocalReference<jobject>(m.Get())); + ScopedLocalRef<jobject> object_ref(self->GetJniEnv(), + soa.AddLocalReference<jobject>(receiver.Get())); + ScopedLocalRef<jobject> args_ref(self->GetJniEnv(), + soa.AddLocalReference<jobject>(args.Get())); + InvokeMethod(soa, method_ref.get(), object_ref.get(), args_ref.get(), 2); + } + if (self->IsExceptionPending()) { + AbortTransactionOrFail(self, "Failed running constructor"); + } else { + result->SetL(receiver.Get()); + } +} + void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); diff --git a/runtime/interpreter/unstarted_runtime.h b/runtime/interpreter/unstarted_runtime.h index 3f36a27e92..bc9ead8360 100644 --- a/runtime/interpreter/unstarted_runtime.h +++ b/runtime/interpreter/unstarted_runtime.h @@ -89,6 +89,13 @@ class UnstartedRuntime { #undef UNSTARTED_RUNTIME_JNI_LIST #undef UNSTARTED_JNI + static void UnstartedClassForNameCommon(Thread* self, + ShadowFrame* shadow_frame, + JValue* result, + size_t arg_offset, + bool long_form, + const char* caller) REQUIRES_SHARED(Locks::mutator_lock_); + static void InitializeInvokeHandlers(); static void InitializeJNIHandlers(); diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h index 929b747840..6fc7989acf 100644 --- a/runtime/interpreter/unstarted_runtime_list.h +++ b/runtime/interpreter/unstarted_runtime_list.h @@ -31,8 +31,10 @@ V(ClassGetDeclaringClass, "java.lang.Class java.lang.Class.getDeclaringClass()") \ V(ClassGetEnclosingClass, "java.lang.Class java.lang.Class.getEnclosingClass()") \ V(ClassGetInnerClassFlags, "int java.lang.Class.getInnerClassFlags(int)") \ + V(ClassGetSignatureAnnotation, "java.lang.String[] java.lang.Class.getSignatureAnnotation()") \ V(ClassIsAnonymousClass, "boolean java.lang.Class.isAnonymousClass()") \ V(ClassLoaderGetResourceAsStream, "java.io.InputStream java.lang.ClassLoader.getResourceAsStream(java.lang.String)") \ + V(ConstructorNewInstance0, "java.lang.Object java.lang.reflect.Constructor.newInstance0(java.lang.Object[])") \ V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \ V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \ V(SystemArraycopy, "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") \ diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 98a17e41f9..db222fad6d 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -29,6 +29,8 @@ #include "handle_scope-inl.h" #include "interpreter/interpreter_common.h" #include "mirror/class_loader.h" +#include "mirror/object_array-inl.h" +#include "mirror/object-inl.h" #include "mirror/string-inl.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" @@ -1077,12 +1079,293 @@ TEST_F(UnstartedRuntimeTest, LogManager) { StackHandleScope<1> hs(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> log_manager_class = hs.NewHandle( - class_linker->FindClass(self, - "Ljava/util/logging/LogManager;", - ScopedNullHandle<mirror::ClassLoader>())); + class_linker->FindClass(self, + "Ljava/util/logging/LogManager;", + ScopedNullHandle<mirror::ClassLoader>())); ASSERT_TRUE(log_manager_class.Get() != nullptr); ASSERT_TRUE(class_linker->EnsureInitialized(self, log_manager_class, true, true)); } +class UnstartedClassForNameTest : public UnstartedRuntimeTest { + public: + template <typename T> + void RunTest(T& runner, bool in_transaction, bool should_succeed) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + // Ensure that Class is initialized. + { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + StackHandleScope<1> hs(self); + Handle<mirror::Class> h_class = hs.NewHandle(mirror::Class::GetJavaLangClass()); + CHECK(class_linker->EnsureInitialized(self, h_class, true, true)); + } + + // A selection of classes from different core classpath components. + constexpr const char* kTestCases[] = { + "java.net.CookieManager", // From libcore. + "dalvik.system.ClassExt", // From libart. + }; + + if (in_transaction) { + // For transaction mode, we cannot load any classes, as the pre-fence initialization of + // classes isn't transactional. Load them ahead of time. + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + for (const char* name : kTestCases) { + class_linker->FindClass(self, + DotToDescriptor(name).c_str(), + ScopedNullHandle<mirror::ClassLoader>()); + CHECK(!self->IsExceptionPending()) << self->GetException()->Dump(); + } + } + + if (!should_succeed) { + // Negative test. In general, currentThread should fail (as we should not leak a peer that will + // be recreated at runtime). + PrepareForAborts(); + } + + JValue result; + ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + + for (const char* name : kTestCases) { + mirror::String* name_string = mirror::String::AllocFromModifiedUtf8(self, name); + CHECK(name_string != nullptr); + + Transaction transaction; + if (in_transaction) { + Runtime::Current()->EnterTransactionMode(&transaction); + } + CHECK(!self->IsExceptionPending()); + + runner(self, shadow_frame, name_string, &result); + + if (in_transaction) { + Runtime::Current()->ExitTransactionMode(); + } + + if (should_succeed) { + CHECK(!self->IsExceptionPending()) << name << " " << self->GetException()->Dump(); + CHECK(result.GetL() != nullptr) << name; + } else { + CHECK(self->IsExceptionPending()) << name; + if (in_transaction) { + ASSERT_TRUE(transaction.IsAborted()); + } + self->ClearException(); + } + } + + ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); + } + + mirror::ClassLoader* GetBootClassLoader() REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* self = Thread::Current(); + StackHandleScope<2> hs(self); + MutableHandle<mirror::ClassLoader> boot_cp = hs.NewHandle<mirror::ClassLoader>(nullptr); + + { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + + // Create the fake boot classloader. Any instance is fine, they are technically interchangeable. + Handle<mirror::Class> boot_cp_class = hs.NewHandle( + class_linker->FindClass(self, + "Ljava/lang/BootClassLoader;", + ScopedNullHandle<mirror::ClassLoader>())); + CHECK(boot_cp_class != nullptr); + CHECK(class_linker->EnsureInitialized(self, boot_cp_class, true, true)); + + boot_cp.Assign(boot_cp_class->AllocObject(self)->AsClassLoader()); + CHECK(boot_cp != nullptr); + + ArtMethod* boot_cp_init = boot_cp_class->FindDeclaredDirectMethod( + "<init>", "()V", class_linker->GetImagePointerSize()); + CHECK(boot_cp_init != nullptr); + + JValue result; + ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, boot_cp_init, 0); + shadow_frame->SetVRegReference(0, boot_cp.Get()); + + // create instruction data for invoke-direct {v0} of method with fake index + uint16_t inst_data[3] = { 0x1070, 0x0000, 0x0010 }; + const Instruction* inst = Instruction::At(inst_data); + + interpreter::DoCall<false, false>(boot_cp_init, + self, + *shadow_frame, + inst, + inst_data[0], + &result); + CHECK(!self->IsExceptionPending()); + + ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); + } + + return boot_cp.Get(); + } +}; + +TEST_F(UnstartedClassForNameTest, ClassForName) { + auto runner = [](Thread* self, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + shadow_frame->SetVRegReference(0, name); + UnstartedClassForName(self, shadow_frame, result, 0); + }; + RunTest(runner, false, true); +} + +TEST_F(UnstartedClassForNameTest, ClassForNameLong) { + auto runner = [](Thread* self, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + shadow_frame->SetVRegReference(0, name); + shadow_frame->SetVReg(1, 0); + shadow_frame->SetVRegReference(2, nullptr); + UnstartedClassForNameLong(self, shadow_frame, result, 0); + }; + RunTest(runner, false, true); +} + +TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoader) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> boot_cp = hs.NewHandle(GetBootClassLoader()); + + auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + shadow_frame->SetVRegReference(0, name); + shadow_frame->SetVReg(1, 0); + shadow_frame->SetVRegReference(2, boot_cp.Get()); + UnstartedClassForNameLong(th, shadow_frame, result, 0); + }; + RunTest(runner, false, true); +} + +TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderTransaction) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> boot_cp = hs.NewHandle(GetBootClassLoader()); + + auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + shadow_frame->SetVRegReference(0, name); + shadow_frame->SetVReg(1, 0); + shadow_frame->SetVRegReference(2, boot_cp.Get()); + UnstartedClassForNameLong(th, shadow_frame, result, 0); + }; + RunTest(runner, true, true); +} + +TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderFail) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + StackHandleScope<2> hs(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + jobject path_jobj = class_linker->CreatePathClassLoader(self, {}); + ASSERT_TRUE(path_jobj != nullptr); + Handle<mirror::ClassLoader> path_cp = hs.NewHandle<mirror::ClassLoader>( + self->DecodeJObject(path_jobj)->AsClassLoader()); + + auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + shadow_frame->SetVRegReference(0, name); + shadow_frame->SetVReg(1, 0); + shadow_frame->SetVRegReference(2, path_cp.Get()); + UnstartedClassForNameLong(th, shadow_frame, result, 0); + }; + RunTest(runner, true, false); +} + +TEST_F(UnstartedRuntimeTest, ClassGetSignatureAnnotation) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + StackHandleScope<1> hs(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Handle<mirror::Class> list_class = hs.NewHandle( + class_linker->FindClass(self, + "Ljava/util/List;", + ScopedNullHandle<mirror::ClassLoader>())); + ASSERT_TRUE(list_class.Get() != nullptr); + ASSERT_TRUE(class_linker->EnsureInitialized(self, list_class, true, true)); + + JValue result; + ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + + shadow_frame->SetVRegReference(0, list_class.Get()); + UnstartedClassGetSignatureAnnotation(self, shadow_frame, &result, 0); + ASSERT_TRUE(result.GetL() != nullptr); + ASSERT_FALSE(self->IsExceptionPending()); + + ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); + + ASSERT_TRUE(result.GetL()->IsObjectArray()); + ObjPtr<mirror::ObjectArray<mirror::Object>> array = + result.GetL()->AsObjectArray<mirror::Object>(); + std::ostringstream oss; + for (int32_t i = 0; i != array->GetLength(); ++i) { + ObjPtr<mirror::Object> elem = array->Get(i); + ASSERT_TRUE(elem != nullptr); + ASSERT_TRUE(elem->IsString()); + oss << elem->AsString()->ToModifiedUtf8(); + } + std::string output_string = oss.str(); + ASSERT_EQ(output_string, "<E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;"); +} + +TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) { + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + StackHandleScope<4> hs(self); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + + // Get Throwable. + Handle<mirror::Class> throw_class = hs.NewHandle(mirror::Throwable::GetJavaLangThrowable()); + ASSERT_TRUE(class_linker->EnsureInitialized(self, throw_class, true, true)); + + // Get an input object. + Handle<mirror::String> input = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "abd")); + + // Find the constructor. + ArtMethod* throw_cons = throw_class->FindDeclaredDirectMethod( + "<init>", "(Ljava/lang/String;)V", class_linker->GetImagePointerSize()); + ASSERT_TRUE(throw_cons != nullptr); + + Handle<mirror::Constructor> cons = hs.NewHandle( + mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize, false>(self, throw_cons)); + ASSERT_TRUE(cons != nullptr); + + Handle<mirror::ObjectArray<mirror::Object>> args = hs.NewHandle( + class_linker->AllocObjectArray<mirror::Object>(self, 1)); + ASSERT_TRUE(args != nullptr); + args->Set(0, input.Get()); + + // OK, we're ready now. + JValue result; + ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0); + shadow_frame->SetVRegReference(0, cons.Get()); + shadow_frame->SetVRegReference(1, args.Get()); + UnstartedConstructorNewInstance0(self, shadow_frame, &result, 0); + + ASSERT_TRUE(result.GetL() != nullptr); + ASSERT_FALSE(self->IsExceptionPending()); + + // Should be a new object. + ASSERT_NE(result.GetL(), input.Get()); + // Should be a String. + ASSERT_EQ(mirror::Throwable::GetJavaLangThrowable(), result.GetL()->GetClass()); + // Should have the right string. + ObjPtr<mirror::String> result_msg = + reinterpret_cast<mirror::Throwable*>(result.GetL())->GetDetailMessage(); + EXPECT_EQ(input.Get(), result_msg.Ptr()); + + ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); +} + } // namespace interpreter } // namespace art diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 60ab275641..c226a38299 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -1245,15 +1245,40 @@ void* JitCodeCache::MoreCore(const void* mspace, intptr_t increment) NO_THREAD_S } void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_locations, - std::vector<MethodReference>& methods) { + std::vector<ProfileMethodInfo>& methods) { ScopedTrace trace(__FUNCTION__); MutexLock mu(Thread::Current(), lock_); for (const ProfilingInfo* info : profiling_infos_) { ArtMethod* method = info->GetMethod(); const DexFile* dex_file = method->GetDexFile(); - if (ContainsElement(dex_base_locations, dex_file->GetBaseLocation())) { - methods.emplace_back(dex_file, method->GetDexMethodIndex()); + if (!ContainsElement(dex_base_locations, dex_file->GetBaseLocation())) { + // Skip dex files which are not profiled. + continue; } + std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches; + for (size_t i = 0; i < info->number_of_inline_caches_; ++i) { + std::vector<ProfileMethodInfo::ProfileClassReference> profile_classes; + const InlineCache& cache = info->cache_[i]; + for (size_t k = 0; k < InlineCache::kIndividualCacheSize; k++) { + mirror::Class* cls = cache.classes_[k].Read(); + if (cls == nullptr) { + break; + } + const DexFile& class_dex_file = cls->GetDexFile(); + dex::TypeIndex type_index = cls->GetDexTypeIndex(); + if (ContainsElement(dex_base_locations, class_dex_file.GetBaseLocation())) { + // Only consider classes from the same apk (including multidex). + profile_classes.emplace_back(/*ProfileMethodInfo::ProfileClassReference*/ + &class_dex_file, type_index); + } + } + if (!profile_classes.empty()) { + inline_caches.emplace_back(/*ProfileMethodInfo::ProfileInlineCache*/ + cache.dex_pc_, profile_classes); + } + } + methods.emplace_back(/*ProfileMethodInfo*/ + dex_file, method->GetDexMethodIndex(), inline_caches); } } diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index b5e31769ab..33a792fe75 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -30,6 +30,7 @@ #include "method_reference.h" #include "oat_file.h" #include "object_callbacks.h" +#include "profile_compilation_info.h" #include "safe_map.h" #include "thread_pool.h" @@ -192,7 +193,7 @@ class JitCodeCache { // Adds to `methods` all profiled methods which are part of any of the given dex locations. void GetProfiledMethods(const std::set<std::string>& dex_base_locations, - std::vector<MethodReference>& methods) + std::vector<ProfileMethodInfo>& methods) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 54fc0386e1..5638ce1160 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -37,7 +37,7 @@ namespace art { const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' }; -const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '2', '\0' }; +const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '3', '\0' }; // inline caches static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; @@ -46,6 +46,25 @@ static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; // using the same test profile. static constexpr bool kDebugIgnoreChecksum = false; +static constexpr uint8_t kMegamorphicEncoding = 7; + +static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t), + "InlineCache::kIndividualCacheSize does not have the expect type size"); +static_assert(InlineCache::kIndividualCacheSize < kMegamorphicEncoding, + "InlineCache::kIndividualCacheSize is larger than expected"); + +void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx, + const dex::TypeIndex& type_idx) { + if (is_megamorphic) { + return; + } + classes.emplace(dex_profile_idx, type_idx); + if (classes.size() >= InlineCache::kIndividualCacheSize) { + is_megamorphic = true; + classes.clear(); + } +} + // Transform the actual dex location into relative paths. // Note: this is OK because we don't store profiles of different apps into the same file. // Apps with split apks don't cause trouble because each split has a different name and will not @@ -62,12 +81,10 @@ std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_ } bool ProfileCompilationInfo::AddMethodsAndClasses( - const std::vector<MethodReference>& methods, + const std::vector<ProfileMethodInfo>& methods, const std::set<DexCacheResolvedClasses>& resolved_classes) { - for (const MethodReference& method : methods) { - if (!AddMethodIndex(GetProfileDexFileKey(method.dex_file->GetLocation()), - method.dex_file->GetLocationChecksum(), - method.dex_method_index)) { + for (const ProfileMethodInfo& method : methods) { + if (!AddMethod(method)) { return false; } } @@ -170,29 +187,40 @@ static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) { } static constexpr size_t kLineHeaderSize = - 3 * sizeof(uint16_t) + // method_set.size + class_set.size + dex_location.size - sizeof(uint32_t); // checksum + 2 * sizeof(uint16_t) + // class_set.size + dex_location.size + 2 * sizeof(uint32_t); // method_map.size + checksum /** * Serialization format: - * magic,version,number_of_lines - * dex_location1,number_of_methods1,number_of_classes1,dex_location_checksum1, \ - * method_id11,method_id12...,class_id1,class_id2... - * dex_location2,number_of_methods2,number_of_classes2,dex_location_checksum2, \ - * method_id21,method_id22...,,class_id1,class_id2... + * magic,version,number_of_dex_files + * dex_location1,number_of_classes1,methods_region_size,dex_location_checksum1, \ + * method_encoding_11,method_encoding_12...,class_id1,class_id2... + * dex_location2,number_of_classes2,methods_region_size,dex_location_checksum2, \ + * method_encoding_21,method_encoding_22...,,class_id1,class_id2... * ..... + * The method_encoding is: + * method_id,number_of_inline_caches,inline_cache1,inline_cache2... + * The inline_cache is: + * dex_pc,[M|dex_map_size], dex_profile_index,class_id1,class_id2...,dex_profile_index2,... + * dex_map_size is the number of dex_indeces that follows. + * Classes are grouped per their dex files and the line + * `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the + * mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...` + * M stands for megamorphic and it's encoded as the byte kMegamorphicEncoding. + * When present, there will be no class ids following. **/ bool ProfileCompilationInfo::Save(int fd) { ScopedTrace trace(__PRETTY_FUNCTION__); DCHECK_GE(fd, 0); - // Cache at most 5KB before writing. - static constexpr size_t kMaxSizeToKeepBeforeWriting = 5 * KB; + // Cache at most 50KB before writing. + static constexpr size_t kMaxSizeToKeepBeforeWriting = 50 * KB; // Use a vector wrapper to avoid keeping track of offsets when we add elements. std::vector<uint8_t> buffer; WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic)); WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion)); - AddUintToBuffer(&buffer, static_cast<uint16_t>(info_.size())); + DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max()); + AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size())); for (const auto& it : info_) { if (buffer.size() > kMaxSizeToKeepBeforeWriting) { @@ -203,9 +231,9 @@ bool ProfileCompilationInfo::Save(int fd) { } const std::string& dex_location = it.first; const DexFileData& dex_data = it.second; - if (dex_data.method_set.empty() && dex_data.class_set.empty()) { - continue; - } + + // Note that we allow dex files without any methods or classes, so that + // inline caches can refer valid dex files. if (dex_location.size() >= kMaxDexFileKeyLength) { LOG(WARNING) << "DexFileKey exceeds allocated limit"; @@ -214,42 +242,128 @@ bool ProfileCompilationInfo::Save(int fd) { // Make sure that the buffer has enough capacity to avoid repeated resizings // while we add data. + uint32_t methods_region_size = GetMethodsRegionSize(dex_data); size_t required_capacity = buffer.size() + kLineHeaderSize + dex_location.size() + - sizeof(uint16_t) * (dex_data.class_set.size() + dex_data.method_set.size()); + sizeof(uint16_t) * dex_data.class_set.size() + + methods_region_size; buffer.reserve(required_capacity); - DCHECK_LE(dex_location.size(), std::numeric_limits<uint16_t>::max()); - DCHECK_LE(dex_data.method_set.size(), std::numeric_limits<uint16_t>::max()); DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max()); AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_location.size())); - AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.method_set.size())); AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size())); + AddUintToBuffer(&buffer, methods_region_size); // uint32_t AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t AddStringToBuffer(&buffer, dex_location); - for (auto method_it : dex_data.method_set) { - AddUintToBuffer(&buffer, method_it); + for (const auto& method_it : dex_data.method_map) { + AddUintToBuffer(&buffer, method_it.first); + AddInlineCacheToBuffer(&buffer, method_it.second); } - for (auto class_id : dex_data.class_set) { + for (const auto& class_id : dex_data.class_set) { AddUintToBuffer(&buffer, class_id.index_); } - DCHECK_EQ(required_capacity, buffer.size()) + + DCHECK_LE(required_capacity, buffer.size()) << "Failed to add the expected number of bytes in the buffer"; } return WriteBuffer(fd, buffer.data(), buffer.size()); } +void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer, + const InlineCacheMap& inline_cache_map) { + // Add inline cache map size. + AddUintToBuffer(buffer, static_cast<uint16_t>(inline_cache_map.size())); + if (inline_cache_map.size() == 0) { + return; + } + for (const auto& inline_cache_it : inline_cache_map) { + uint16_t dex_pc = inline_cache_it.first; + const DexPcData dex_pc_data = inline_cache_it.second; + const ClassSet& classes = dex_pc_data.classes; + + // Add the dex pc. + AddUintToBuffer(buffer, dex_pc); + + if (dex_pc_data.is_megamorphic) { + // Add the megamorphic encoding if needed and continue. + // If megamorphic, we don't add the rest of the classes. + AddUintToBuffer(buffer, kMegamorphicEncoding); + continue; + } + + DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize); + DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes"; + + SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map; + // Group the classes by dex. We expect that most of the classes will come from + // the same dex, so this will be more efficient than encoding the dex index + // for each class reference. + GroupClassesByDex(classes, &dex_to_classes_map); + // Add the dex map size. + AddUintToBuffer(buffer, static_cast<uint8_t>(dex_to_classes_map.size())); + for (const auto& dex_it : dex_to_classes_map) { + uint8_t dex_profile_index = dex_it.first; + const std::vector<dex::TypeIndex>& dex_classes = dex_it.second; + // Add the dex profile index. + AddUintToBuffer(buffer, dex_profile_index); + // Add the the number of classes for each dex profile index. + AddUintToBuffer(buffer, static_cast<uint8_t>(dex_classes.size())); + for (size_t i = 0; i < dex_classes.size(); i++) { + // Add the type index of the classes. + AddUintToBuffer(buffer, dex_classes[i].index_); + } + } + } +} + +uint32_t ProfileCompilationInfo::GetMethodsRegionSize(const DexFileData& dex_data) { + // ((uint16_t)method index + (uint16_t)inline cache size) * number of methods + uint32_t size = 2 * sizeof(uint16_t) * dex_data.method_map.size(); + for (const auto& method_it : dex_data.method_map) { + const InlineCacheMap& inline_cache = method_it.second; + size += sizeof(uint16_t) * inline_cache.size(); // dex_pc + for (const auto& inline_cache_it : inline_cache) { + const ClassSet& classes = inline_cache_it.second.classes; + SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map; + GroupClassesByDex(classes, &dex_to_classes_map); + size += sizeof(uint8_t); // dex_to_classes_map size + for (const auto& dex_it : dex_to_classes_map) { + size += sizeof(uint8_t); // dex profile index + size += sizeof(uint8_t); // number of classes + const std::vector<dex::TypeIndex>& dex_classes = dex_it.second; + size += sizeof(uint16_t) * dex_classes.size(); // the actual classes + } + } + } + return size; +} + +void ProfileCompilationInfo::GroupClassesByDex( + const ClassSet& classes, + /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map) { + for (const auto& classes_it : classes) { + auto dex_it = dex_to_classes_map->FindOrAdd(classes_it.dex_profile_index); + dex_it->second.push_back(classes_it.type_index); + } +} + ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData( const std::string& dex_location, uint32_t checksum) { - auto info_it = info_.find(dex_location); - if (info_it == info_.end()) { - info_it = info_.Put(dex_location, DexFileData(checksum)); + auto info_it = info_.FindOrAdd(dex_location, DexFileData(checksum, info_.size())); + if (info_.size() > std::numeric_limits<uint8_t>::max()) { + // Allow only 255 dex files to be profiled. This allows us to save bytes + // when encoding. The number is well above what we expect for normal applications. + if (kIsDebugBuild) { + LOG(WARNING) << "Exceeded the maximum number of dex files (255). Something went wrong"; + } + info_.erase(dex_location); + return nullptr; } if (info_it->second.checksum != checksum) { LOG(WARNING) << "Checksum mismatch for dex " << dex_location; @@ -270,13 +384,65 @@ bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& c } bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location, - uint32_t checksum, - uint16_t method_idx) { - DexFileData* const data = GetOrAddDexFileData(dex_location, checksum); - if (data == nullptr) { + uint32_t dex_checksum, + uint16_t method_index) { + return AddMethod(dex_location, dex_checksum, method_index, OfflineProfileMethodInfo()); +} + +bool ProfileCompilationInfo::AddMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t method_index, + const OfflineProfileMethodInfo& pmi) { + DexFileData* const data = GetOrAddDexFileData( + GetProfileDexFileKey(dex_location), + dex_checksum); + if (data == nullptr) { // checksum mismatch return false; } - data->method_set.insert(method_idx); + auto inline_cache_it = data->method_map.FindOrAdd(method_index); + for (const auto& pmi_inline_cache_it : pmi.inline_caches) { + uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first; + const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second; + auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(pmi_ic_dex_pc); + if (pmi_ic_dex_pc_data.is_megamorphic) { + dex_pc_data_it->second.SetMegamorphic(); + continue; + } + for (const ClassReference& class_ref : pmi_ic_dex_pc_data.classes) { + const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index]; + DexFileData* class_dex_data = GetOrAddDexFileData( + GetProfileDexFileKey(dex_ref.dex_location), + dex_ref.dex_checksum); + if (class_dex_data == nullptr) { // checksum mismatch + return false; + } + dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index); + } + } + return true; +} + +bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) { + DexFileData* const data = GetOrAddDexFileData( + GetProfileDexFileKey(pmi.dex_file->GetLocation()), + pmi.dex_file->GetLocationChecksum()); + if (data == nullptr) { // checksum mismatch + return false; + } + auto inline_cache_it = data->method_map.FindOrAdd(pmi.dex_method_index); + + for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) { + for (const ProfileMethodInfo::ProfileClassReference& class_ref : cache.classes) { + DexFileData* class_dex_data = GetOrAddDexFileData( + GetProfileDexFileKey(class_ref.dex_file->GetLocation()), + class_ref.dex_file->GetLocationChecksum()); + if (class_dex_data == nullptr) { // checksum mismatch + return false; + } + auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(cache.dex_pc); + dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index); + } + } return true; } @@ -291,21 +457,79 @@ bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location, return true; } -bool ProfileCompilationInfo::ProcessLine(SafeBuffer& line_buffer, - uint16_t method_set_size, - uint16_t class_set_size, - uint32_t checksum, - const std::string& dex_location) { - for (uint16_t i = 0; i < method_set_size; i++) { - uint16_t method_idx = line_buffer.ReadUintAndAdvance<uint16_t>(); - if (!AddMethodIndex(dex_location, checksum, method_idx)) { +#define READ_UINT(type, buffer, dest, error) \ + do { \ + if (!buffer.ReadUintAndAdvance<type>(&dest)) { \ + *error = "Could not read "#dest; \ + return false; \ + } \ + } \ + while (false) + +bool ProfileCompilationInfo::ReadInlineCache(SafeBuffer& buffer, + uint8_t number_of_dex_files, + /*out*/ InlineCacheMap* inline_cache, + /*out*/ std::string* error) { + uint16_t inline_cache_size; + READ_UINT(uint16_t, buffer, inline_cache_size, error); + for (; inline_cache_size > 0; inline_cache_size--) { + uint16_t dex_pc; + uint8_t dex_to_classes_map_size; + READ_UINT(uint16_t, buffer, dex_pc, error); + READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error); + auto dex_pc_data_it = inline_cache->FindOrAdd(dex_pc); + if (dex_to_classes_map_size == kMegamorphicEncoding) { + dex_pc_data_it->second.SetMegamorphic(); + continue; + } + for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) { + uint8_t dex_profile_index; + uint8_t dex_classes_size; + READ_UINT(uint8_t, buffer, dex_profile_index, error); + READ_UINT(uint8_t, buffer, dex_classes_size, error); + if (dex_profile_index >= number_of_dex_files) { + *error = "dex_profile_index out of bounds "; + *error += std::to_string(dex_profile_index) + " " + std::to_string(number_of_dex_files); + return false; + } + for (; dex_classes_size > 0; dex_classes_size--) { + uint16_t type_index; + READ_UINT(uint16_t, buffer, type_index, error); + dex_pc_data_it->second.AddClass(dex_profile_index, dex::TypeIndex(type_index)); + } + } + } + return true; +} + +bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer, + uint8_t number_of_dex_files, + const ProfileLineHeader& line_header, + /*out*/std::string* error) { + while (buffer.HasMoreData()) { + DexFileData* const data = GetOrAddDexFileData(line_header.dex_location, line_header.checksum); + uint16_t method_index; + READ_UINT(uint16_t, buffer, method_index, error); + + auto it = data->method_map.FindOrAdd(method_index); + if (!ReadInlineCache(buffer, number_of_dex_files, &(it->second), error)) { return false; } } - for (uint16_t i = 0; i < class_set_size; i++) { - uint16_t type_idx = line_buffer.ReadUintAndAdvance<uint16_t>(); - if (!AddClassIndex(dex_location, checksum, dex::TypeIndex(type_idx))) { + return true; +} + +bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer, + uint16_t classes_to_read, + const ProfileLineHeader& line_header, + /*out*/std::string* error) { + for (uint16_t i = 0; i < classes_to_read; i++) { + uint16_t type_index; + READ_UINT(uint16_t, buffer, type_index, error); + if (!AddClassIndex(line_header.dex_location, + line_header.checksum, + dex::TypeIndex(type_index))) { return false; } } @@ -324,15 +548,17 @@ static int testEOF(int fd) { // Reads an uint value previously written with AddUintToBuffer. template <typename T> -T ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance() { +bool ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance(/*out*/T* value) { static_assert(std::is_unsigned<T>::value, "Type is not unsigned"); - CHECK_LE(ptr_current_ + sizeof(T), ptr_end_); - T value = 0; + if (ptr_current_ + sizeof(T) > ptr_end_) { + return false; + } + *value = 0; for (size_t i = 0; i < sizeof(T); i++) { - value += ptr_current_[i] << (i * kBitsPerByte); + *value += ptr_current_[i] << (i * kBitsPerByte); } ptr_current_ += sizeof(T); - return value; + return true; } bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, size_t data_size) { @@ -346,6 +572,10 @@ bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, return false; } +bool ProfileCompilationInfo::SafeBuffer::HasMoreData() { + return ptr_current_ < ptr_end_; +} + ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd( int fd, const std::string& source, @@ -369,13 +599,13 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::Fil ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader( int fd, - /*out*/uint16_t* number_of_lines, + /*out*/uint8_t* number_of_dex_files, /*out*/std::string* error) { // Read magic and version const size_t kMagicVersionSize = sizeof(kProfileMagic) + sizeof(kProfileVersion) + - sizeof(uint16_t); // number of lines + sizeof(uint8_t); // number of dex files SafeBuffer safe_buffer(kMagicVersionSize); @@ -392,24 +622,38 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHead *error = "Profile version mismatch"; return kProfileLoadVersionMismatch; } - *number_of_lines = safe_buffer.ReadUintAndAdvance<uint16_t>(); + if (!safe_buffer.ReadUintAndAdvance<uint8_t>(number_of_dex_files)) { + *error = "Cannot read the number of dex files"; + return kProfileLoadBadData; + } return kProfileLoadSuccess; } +bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer, + /*out*/uint16_t* dex_location_size, + /*out*/ProfileLineHeader* line_header, + /*out*/std::string* error) { + READ_UINT(uint16_t, buffer, *dex_location_size, error); + READ_UINT(uint16_t, buffer, line_header->class_set_size, error); + READ_UINT(uint32_t, buffer, line_header->method_region_size_bytes, error); + READ_UINT(uint32_t, buffer, line_header->checksum, error); + return true; +} + ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader( int fd, /*out*/ProfileLineHeader* line_header, /*out*/std::string* error) { SafeBuffer header_buffer(kLineHeaderSize); - ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileHeader", error); + ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileLineHeader", error); if (status != kProfileLoadSuccess) { return status; } - uint16_t dex_location_size = header_buffer.ReadUintAndAdvance<uint16_t>(); - line_header->method_set_size = header_buffer.ReadUintAndAdvance<uint16_t>(); - line_header->class_set_size = header_buffer.ReadUintAndAdvance<uint16_t>(); - line_header->checksum = header_buffer.ReadUintAndAdvance<uint32_t>(); + uint16_t dex_location_size; + if (!ReadProfileLineHeaderElements(header_buffer, &dex_location_size, line_header, error)) { + return kProfileLoadBadData; + } if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) { *error = "DexFileKey has an invalid size: " + @@ -429,37 +673,38 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine( int fd, + uint8_t number_of_dex_files, const ProfileLineHeader& line_header, /*out*/std::string* error) { - // Make sure that we don't try to read everything in memory (in case the profile if full). - // Split readings in chunks of at most 10kb. - static constexpr uint16_t kMaxNumberOfEntriesToRead = 5120; - uint16_t methods_left_to_read = line_header.method_set_size; - uint16_t classes_left_to_read = line_header.class_set_size; + if (GetOrAddDexFileData(line_header.dex_location, line_header.checksum) == nullptr) { + *error = "Error when reading profile file line header: checksum mismatch for " + + line_header.dex_location; + return kProfileLoadBadData; + } - while ((methods_left_to_read > 0) || (classes_left_to_read > 0)) { - uint16_t methods_to_read = std::min(kMaxNumberOfEntriesToRead, methods_left_to_read); - uint16_t max_classes_to_read = kMaxNumberOfEntriesToRead - methods_to_read; - uint16_t classes_to_read = std::min(max_classes_to_read, classes_left_to_read); + { + SafeBuffer buffer(line_header.method_region_size_bytes); + ProfileLoadSatus status = buffer.FillFromFd(fd, "ReadProfileLineMethods", error); + if (status != kProfileLoadSuccess) { + return status; + } - size_t line_size = sizeof(uint16_t) * (methods_to_read + classes_to_read); - SafeBuffer line_buffer(line_size); + if (!ReadMethods(buffer, number_of_dex_files, line_header, error)) { + return kProfileLoadBadData; + } + } - ProfileLoadSatus status = line_buffer.FillFromFd(fd, "ReadProfileLine", error); + { + SafeBuffer buffer(sizeof(uint16_t) * line_header.class_set_size); + ProfileLoadSatus status = buffer.FillFromFd(fd, "ReadProfileLineClasses", error); if (status != kProfileLoadSuccess) { return status; } - if (!ProcessLine(line_buffer, - methods_to_read, - classes_to_read, - line_header.checksum, - line_header.dex_location)) { - *error = "Error when reading profile file line"; + if (!ReadClasses(buffer, line_header.class_set_size, line_header, error)) { return kProfileLoadBadData; } - methods_left_to_read -= methods_to_read; - classes_left_to_read -= classes_to_read; } + return kProfileLoadSuccess; } @@ -470,7 +715,7 @@ bool ProfileCompilationInfo::Load(int fd) { if (status == kProfileLoadSuccess) { return true; } else { - PLOG(WARNING) << "Error when reading profile " << error; + LOG(WARNING) << "Error when reading profile: " << error; return false; } } @@ -490,15 +735,16 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal( if (stat_buffer.st_size == 0) { return kProfileLoadSuccess; } - // Read profile header: magic + version + number_of_lines. - uint16_t number_of_lines; - ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_lines, error); + // Read profile header: magic + version + number_of_dex_files. + uint8_t number_of_dex_files; + ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_dex_files, error); if (status != kProfileLoadSuccess) { return status; } - while (number_of_lines > 0) { + for (uint8_t k = 0; k < number_of_dex_files; k++) { ProfileLineHeader line_header; + // First, read the line header to get the amount of data we need to read. status = ReadProfileLineHeader(fd, &line_header, error); if (status != kProfileLoadSuccess) { @@ -506,11 +752,10 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal( } // Now read the actual profile line. - status = ReadProfileLine(fd, line_header, error); + status = ReadProfileLine(fd, number_of_dex_files, line_header, error); if (status != kProfileLoadSuccess) { return status; } - number_of_lines--; } // Check that we read everything and that profiles don't contain junk data. @@ -538,37 +783,112 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) { } } // All checksums match. Import the data. + + // The other profile might have a different indexing of dex files. + // That is because each dex files gets a 'dex_profile_index' on a first come first served basis. + // That means that the order in with the methods are added to the profile matters for the + // actual indices. + // The reason we cannot rely on the actual multidex index is that a single profile may store + // data from multiple splits. This means that a profile may contain a classes2.dex from split-A + // and one from split-B. + + // First, build a mapping from other_dex_profile_index to this_dex_profile_index. + // This will make sure that the ClassReferences will point to the correct dex file. + SafeMap<uint8_t, uint8_t> dex_profile_index_remap; + for (const auto& other_it : other.info_) { + const std::string& other_dex_location = other_it.first; + const DexFileData& other_dex_data = other_it.second; + auto info_it = info_.FindOrAdd(other_dex_location, DexFileData(other_dex_data.checksum, 0)); + const DexFileData& dex_data = info_it->second; + dex_profile_index_remap.Put(other_dex_data.profile_index, dex_data.profile_index); + } + + // Merge the actual profile data. for (const auto& other_it : other.info_) { const std::string& other_dex_location = other_it.first; const DexFileData& other_dex_data = other_it.second; auto info_it = info_.find(other_dex_location); - if (info_it == info_.end()) { - info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum)); - } - info_it->second.method_set.insert(other_dex_data.method_set.begin(), - other_dex_data.method_set.end()); + DCHECK(info_it != info_.end()); + + // Merge the classes. info_it->second.class_set.insert(other_dex_data.class_set.begin(), other_dex_data.class_set.end()); + + // Merge the methods and the inline caches. + for (const auto& other_method_it : other_dex_data.method_map) { + uint16_t other_method_index = other_method_it.first; + auto method_it = info_it->second.method_map.FindOrAdd(other_method_index); + const auto& other_inline_cache = other_method_it.second; + for (const auto& other_ic_it : other_inline_cache) { + uint16_t other_dex_pc = other_ic_it.first; + const ClassSet& other_class_set = other_ic_it.second.classes; + auto class_set = method_it->second.FindOrAdd(other_dex_pc); + for (const auto& class_it : other_class_set) { + class_set->second.AddClass(dex_profile_index_remap.Get( + class_it.dex_profile_index), class_it.type_index); + } + } + } } return true; } +static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) { + return kDebugIgnoreChecksum || dex_file_checksum == checksum; +} + static bool ChecksumMatch(const DexFile& dex_file, uint32_t checksum) { - return kDebugIgnoreChecksum || dex_file.GetLocationChecksum() == checksum; + return ChecksumMatch(dex_file.GetLocationChecksum(), checksum); } bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const { - auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation())); + return FindMethod(method_ref.dex_file->GetLocation(), + method_ref.dex_file->GetLocationChecksum(), + method_ref.dex_method_index) != nullptr; +} + +const ProfileCompilationInfo::InlineCacheMap* +ProfileCompilationInfo::FindMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t dex_method_index) const { + auto info_it = info_.find(GetProfileDexFileKey(dex_location)); if (info_it != info_.end()) { - if (!ChecksumMatch(*method_ref.dex_file, info_it->second.checksum)) { - return false; + if (!ChecksumMatch(dex_checksum, info_it->second.checksum)) { + return nullptr; } - const std::set<uint16_t>& methods = info_it->second.method_set; - return methods.find(method_ref.dex_method_index) != methods.end(); + const MethodMap& methods = info_it->second.method_map; + const auto method_it = methods.find(dex_method_index); + return method_it == methods.end() ? nullptr : &(method_it->second); } - return false; + return nullptr; } +void ProfileCompilationInfo::DexFileToProfileIndex( + /*out*/std::vector<DexReference>* dex_references) const { + dex_references->resize(info_.size()); + for (const auto& info_it : info_) { + DexReference& dex_ref = (*dex_references)[info_it.second.profile_index]; + dex_ref.dex_location = info_it.first; + dex_ref.dex_checksum = info_it.second.checksum; + } +} + +bool ProfileCompilationInfo::GetMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t dex_method_index, + /*out*/OfflineProfileMethodInfo* pmi) const { + const InlineCacheMap* inline_caches = FindMethod(dex_location, dex_checksum, dex_method_index); + if (inline_caches == nullptr) { + return false; + } + + DexFileToProfileIndex(&pmi->dex_references); + // TODO(calin): maybe expose a direct pointer to avoid copying + pmi->inline_caches = *inline_caches; + return true; +} + + bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const { auto info_it = info_.find(GetProfileDexFileKey(dex_file.GetLocation())); if (info_it != info_.end()) { @@ -584,7 +904,7 @@ bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeInd uint32_t ProfileCompilationInfo::GetNumberOfMethods() const { uint32_t total = 0; for (const auto& it : info_) { - total += it.second.method_set.size(); + total += it.second.method_map.size(); } return total; } @@ -645,19 +965,34 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* } } os << "\n\tmethods: "; - for (const auto method_it : dex_data.method_set) { + for (const auto method_it : dex_data.method_map) { if (dex_file != nullptr) { - os << "\n\t\t" << dex_file->PrettyMethod(method_it, true); + os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true); } else { - os << method_it << ","; + os << method_it.first; + } + + os << "["; + for (const auto& inline_cache_it : method_it.second) { + os << "{" << std::hex << inline_cache_it.first << std::dec << ":"; + if (inline_cache_it.second.is_megamorphic) { + os << "M"; + } else { + for (const ClassReference& class_ref : inline_cache_it.second.classes) { + os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index) + << "," << class_ref.type_index.index_ << ")"; + } + } + os << "}"; } + os << "], "; } os << "\n\tclasses: "; for (const auto class_it : dex_data.class_set) { if (dex_file != nullptr) { os << "\n\t\t" << dex_file->PrettyType(class_it); } else { - os << class_it << ","; + os << class_it.index_ << ","; } } } @@ -762,4 +1097,44 @@ bool ProfileCompilationInfo::GenerateTestProfile(int fd, return info.Save(fd); } +bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==( + const OfflineProfileMethodInfo& other) const { + if (inline_caches.size() != other.inline_caches.size()) { + return false; + } + + // We can't use a simple equality test because we need to match the dex files + // of the inline caches which might have different profile indices. + for (const auto& inline_cache_it : inline_caches) { + uint16_t dex_pc = inline_cache_it.first; + const DexPcData dex_pc_data = inline_cache_it.second; + const auto other_it = other.inline_caches.find(dex_pc); + if (other_it == other.inline_caches.end()) { + return false; + } + const DexPcData& other_dex_pc_data = other_it->second; + if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic) { + return false; + } + for (const ClassReference& class_ref : dex_pc_data.classes) { + bool found = false; + for (const ClassReference& other_class_ref : other_dex_pc_data.classes) { + CHECK_LE(class_ref.dex_profile_index, dex_references.size()); + CHECK_LE(other_class_ref.dex_profile_index, other.dex_references.size()); + const DexReference& dex_ref = dex_references[class_ref.dex_profile_index]; + const DexReference& other_dex_ref = other.dex_references[other_class_ref.dex_profile_index]; + if (class_ref.type_index == other_class_ref.type_index && + dex_ref == other_dex_ref) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + } + return true; +} + } // namespace art diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index 758b46d74a..4bfbfcd287 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -31,6 +31,40 @@ namespace art { /** + * Convenient class to pass around profile information (including inline caches) + * without the need to hold GC-able objects. + */ +struct ProfileMethodInfo { + struct ProfileClassReference { + ProfileClassReference(const DexFile* dex, const dex::TypeIndex& index) + : dex_file(dex), type_index(index) {} + + const DexFile* dex_file; + const dex::TypeIndex type_index; + }; + + struct ProfileInlineCache { + ProfileInlineCache(uint32_t pc, const std::vector<ProfileClassReference>& profile_classes) + : dex_pc(pc), classes(profile_classes) {} + + const uint32_t dex_pc; + const std::vector<ProfileClassReference> classes; + }; + + ProfileMethodInfo(const DexFile* dex, uint32_t method_index) + : dex_file(dex), dex_method_index(method_index) {} + + ProfileMethodInfo(const DexFile* dex, + uint32_t method_index, + const std::vector<ProfileInlineCache>& caches) + : dex_file(dex), dex_method_index(method_index), inline_caches(caches) {} + + const DexFile* dex_file; + const uint32_t dex_method_index; + const std::vector<ProfileInlineCache> inline_caches; +}; + +/** * Profile information in a format suitable to be queried by the compiler and * performing profile guided compilation. * It is a serialize-friendly format based on information collected by the @@ -42,34 +76,130 @@ class ProfileCompilationInfo { static const uint8_t kProfileMagic[]; static const uint8_t kProfileVersion[]; + // Data structures for encoding the offline representation of inline caches. + // This is exposed as public in order to make it available to dex2oat compilations + // (see compiler/optimizing/inliner.cc). + + // A dex location together with its checksum. + struct DexReference { + DexReference() {} + + DexReference(const std::string& location, uint32_t checksum) + : dex_location(location), dex_checksum(checksum) {} + + bool operator==(const DexReference& other) const { + return dex_checksum == other.dex_checksum && dex_location == other.dex_location; + } + + std::string dex_location; + uint32_t dex_checksum; + }; + + // Encodes a class reference in the profile. + // The owning dex file is encoded as the index (dex_profile_index) it has in the + // profile rather than as a full DexRefence(location,checksum). + // This avoids excessive string copying when managing the profile data. + // The dex_profile_index is an index in either of: + // - OfflineProfileMethodInfo#dex_references vector (public use) + // - DexFileData#profile_index (internal use). + // Note that the dex_profile_index is not necessary the multidex index. + // We cannot rely on the actual multidex index because a single profile may store + // data from multiple splits. This means that a profile may contain a classes2.dex from split-A + // and one from split-B. + struct ClassReference { + ClassReference(uint8_t dex_profile_idx, const dex::TypeIndex& type_idx) : + dex_profile_index(dex_profile_idx), type_index(type_idx) {} + + bool operator==(const ClassReference& other) const { + return dex_profile_index == other.dex_profile_index && type_index == other.type_index; + } + bool operator<(const ClassReference& other) const { + return dex_profile_index == other.dex_profile_index + ? type_index < other.type_index + : dex_profile_index < other.dex_profile_index; + } + + uint8_t dex_profile_index; // the index of the owning dex in the profile info + dex::TypeIndex type_index; // the type index of the class + }; + + // The set of classes that can be found at a given dex pc. + using ClassSet = std::set<ClassReference>; + + // Encodes the actual inline cache for a given dex pc (whether or not the receiver is + // megamorphic and its possible types). + // If the receiver is megamorphic the set of classes will be empty. + struct DexPcData { + DexPcData() : is_megamorphic(false) {} + void AddClass(uint16_t dex_profile_idx, const dex::TypeIndex& type_idx); + void SetMegamorphic() { + is_megamorphic = true; + classes.clear(); + } + bool operator==(const DexPcData& other) const { + return is_megamorphic == other.is_megamorphic && classes == other.classes; + } + + bool is_megamorphic; + ClassSet classes; + }; + + // The inline cache map: DexPc -> DexPcData. + using InlineCacheMap = SafeMap<uint16_t, DexPcData>; + + // Encodes the full set of inline caches for a given method. + // The dex_references vector is indexed according to the ClassReference::dex_profile_index. + // i.e. the dex file of any ClassReference present in the inline caches can be found at + // dex_references[ClassReference::dex_profile_index]. + struct OfflineProfileMethodInfo { + bool operator==(const OfflineProfileMethodInfo& other) const; + + std::vector<DexReference> dex_references; + InlineCacheMap inline_caches; + }; + + // Public methods to create, extend or query the profile. + // Add the given methods and classes to the current profile object. - bool AddMethodsAndClasses(const std::vector<MethodReference>& methods, + bool AddMethodsAndClasses(const std::vector<ProfileMethodInfo>& methods, const std::set<DexCacheResolvedClasses>& resolved_classes); - // Loads profile information from the given file descriptor. + + // Load profile information from the given file descriptor. bool Load(int fd); + // Merge the data from another ProfileCompilationInfo into the current object. bool MergeWith(const ProfileCompilationInfo& info); - // Saves the profile data to the given file descriptor. + + // Save the profile data to the given file descriptor. bool Save(int fd); - // Loads and merges profile information from the given file into the current + + // Load and merge profile information from the given file into the current // object and tries to save it back to disk. // If `force` is true then the save will go through even if the given file // has bad data or its version does not match. In this cases the profile content // is ignored. bool MergeAndSave(const std::string& filename, uint64_t* bytes_written, bool force); - // Returns the number of methods that were profiled. + // Return the number of methods that were profiled. uint32_t GetNumberOfMethods() const; - // Returns the number of resolved classes that were profiled. + + // Return the number of resolved classes that were profiled. uint32_t GetNumberOfResolvedClasses() const; - // Returns true if the method reference is present in the profiling info. + // Return true if the method reference is present in the profiling info. bool ContainsMethod(const MethodReference& method_ref) const; - // Returns true if the class's type is present in the profiling info. + // Return true if the class's type is present in the profiling info. bool ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const; - // Dumps all the loaded profile info into a string and returns it. + // Return true if the method is present in the profiling info. + // If the method is found, `pmi` is populated with its inline caches. + bool GetMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t dex_method_index, + /*out*/OfflineProfileMethodInfo* pmi) const; + + // Dump all the loaded profile info into a string and returns it. // If dex_files is not null then the method indices will be resolved to their // names. // This is intended for testing and debugging. @@ -80,26 +210,35 @@ class ProfileCompilationInfo { void GetClassNames(const std::vector<std::unique_ptr<const DexFile>>* dex_files, std::set<std::string>* class_names) const; + void GetClassNames(const std::vector<const DexFile*>* dex_files, std::set<std::string>* class_names) const; + // Perform an equality test with the `other` profile information. bool Equals(const ProfileCompilationInfo& other); - static std::string GetProfileDexFileKey(const std::string& dex_location); - - // Returns the class descriptors for all of the classes in the profiles' class sets. + // Return the class descriptors for all of the classes in the profiles' class sets. // Note the dex location is actually the profile key, the caller needs to call back in to the // profile info stuff to generate a map back to the dex location. std::set<DexCacheResolvedClasses> GetResolvedClasses() const; - // Clears the resolved classes from the current object. + // Clear the resolved classes from the current object. void ClearResolvedClasses(); + // Return the profile key associated with the given dex location. + static std::string GetProfileDexFileKey(const std::string& dex_location); + + // Generate a test profile which will contain a percentage of the total maximum + // number of methods and classes (method_ratio and class_ratio). static bool GenerateTestProfile(int fd, uint16_t number_of_dex_files, uint16_t method_ratio, uint16_t class_ratio); + // Check that the given profile method info contain the same data. + static bool Equals(const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi1, + const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi2); + private: enum ProfileLoadSatus { kProfileLoadIOError, @@ -108,30 +247,71 @@ class ProfileCompilationInfo { kProfileLoadSuccess }; + // Maps a method dex index to its inline cache. + using MethodMap = SafeMap<uint16_t, InlineCacheMap>; + + // Internal representation of the profile information belonging to a dex file. struct DexFileData { - explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {} + DexFileData(uint32_t location_checksum, uint16_t index) + : profile_index(index), checksum(location_checksum) {} + // The profile index of this dex file (matches ClassReference#dex_profile_index) + uint8_t profile_index; + // The dex checksum uint32_t checksum; - std::set<uint16_t> method_set; + // The methonds' profile information + MethodMap method_map; + // The classes which have been profiled. Note that these don't necessarily include + // all the classes that can be found in the inline caches reference. std::set<dex::TypeIndex> class_set; bool operator==(const DexFileData& other) const { - return checksum == other.checksum && method_set == other.method_set; + return checksum == other.checksum && method_map == other.method_map; } }; + // Maps dex file to their profile information. using DexFileToProfileInfoMap = SafeMap<const std::string, DexFileData>; + // Return the profile data for the given dex location or null if the dex location + // already exists but has a different checksum DexFileData* GetOrAddDexFileData(const std::string& dex_location, uint32_t checksum); + + // Add a method index to the profile (without inline caches). bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx); + + // Add a method to the profile using its online representation (containing runtime structures). + bool AddMethod(const ProfileMethodInfo& pmi); + + // Add a method to the profile using its offline representation. + // This is mostly used to facilitate testing. + bool AddMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t method_index, + const OfflineProfileMethodInfo& pmi); + + // Add a class index to the profile. bool AddClassIndex(const std::string& dex_location, uint32_t checksum, dex::TypeIndex type_idx); + + // Add all classes from the given dex cache to the the profile. bool AddResolvedClasses(const DexCacheResolvedClasses& classes); + // Search for the given method in the profile. + // If found, its inline cache map is returned, otherwise the method returns null. + const InlineCacheMap* FindMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t dex_method_index) const; + + // Encode the known dex_files into a vector. The index of a dex_reference will + // be the same as the profile index of the dex file (used to encode the ClassReferences). + void DexFileToProfileIndex(/*out*/std::vector<DexReference>* dex_references) const; + // Parsing functionality. + // The information present in the header of each profile line. struct ProfileLineHeader { std::string dex_location; - uint16_t method_set_size; uint16_t class_set_size; + uint32_t method_region_size_bytes; uint32_t checksum; }; @@ -150,12 +330,15 @@ class ProfileCompilationInfo { // Reads an uint value (high bits to low bits) and advances the current pointer // with the number of bits read. - template <typename T> T ReadUintAndAdvance(); + template <typename T> bool ReadUintAndAdvance(/*out*/ T* value); // Compares the given data with the content current pointer. If the contents are // equal it advances the current pointer by data_size. bool CompareAndAdvance(const uint8_t* data, size_t data_size); + // Returns true if the buffer has more data to read. + bool HasMoreData(); + // Get the underlying raw buffer. uint8_t* Get() { return storage_.get(); } @@ -165,24 +348,63 @@ class ProfileCompilationInfo { uint8_t* ptr_end_; }; + // Entry point for profile loding functionality. ProfileLoadSatus LoadInternal(int fd, std::string* error); + // Read the profile header from the given fd and store the number of profile + // lines into number_of_dex_files. ProfileLoadSatus ReadProfileHeader(int fd, - /*out*/uint16_t* number_of_lines, + /*out*/uint8_t* number_of_dex_files, /*out*/std::string* error); + // Read the header of a profile line from the given fd. ProfileLoadSatus ReadProfileLineHeader(int fd, /*out*/ProfileLineHeader* line_header, /*out*/std::string* error); + + // Read individual elements from the profile line header. + bool ReadProfileLineHeaderElements(SafeBuffer& buffer, + /*out*/uint16_t* dex_location_size, + /*out*/ProfileLineHeader* line_header, + /*out*/std::string* error); + + // Read a single profile line from the given fd. ProfileLoadSatus ReadProfileLine(int fd, + uint8_t number_of_dex_files, const ProfileLineHeader& line_header, /*out*/std::string* error); - bool ProcessLine(SafeBuffer& line_buffer, - uint16_t method_set_size, - uint16_t class_set_size, - uint32_t checksum, - const std::string& dex_location); + // Read all the classes from the buffer into the profile `info_` structure. + bool ReadClasses(SafeBuffer& buffer, + uint16_t classes_to_read, + const ProfileLineHeader& line_header, + /*out*/std::string* error); + + // Read all the methods from the buffer into the profile `info_` structure. + bool ReadMethods(SafeBuffer& buffer, + uint8_t number_of_dex_files, + const ProfileLineHeader& line_header, + /*out*/std::string* error); + + // Read the inline cache encoding from line_bufer into inline_cache. + bool ReadInlineCache(SafeBuffer& buffer, + uint8_t number_of_dex_files, + /*out*/InlineCacheMap* inline_cache, + /*out*/std::string* error); + + // Encode the inline cache into the given buffer. + void AddInlineCacheToBuffer(std::vector<uint8_t>* buffer, + const InlineCacheMap& inline_cache); + + // Return the number of bytes needed to encode the profile information + // for the methods in dex_data. + uint32_t GetMethodsRegionSize(const DexFileData& dex_data); + + // Group `classes` by their owning dex profile index and put the result in + // `dex_to_classes_map`. + void GroupClassesByDex( + const ClassSet& classes, + /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map); friend class ProfileCompilationInfoTest; friend class CompilerDriverProfileTest; diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index 835a5f3495..93b47acf46 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -57,6 +57,14 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { return info->AddMethodIndex(dex_location, checksum, method_index); } + bool AddMethod(const std::string& dex_location, + uint32_t checksum, + uint16_t method_index, + const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi, + ProfileCompilationInfo* info) { + return info->AddMethod(dex_location, checksum, method_index, pmi); + } + bool AddClass(const std::string& dex_location, uint32_t checksum, uint16_t class_index, @@ -73,17 +81,132 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { const std::vector<ArtMethod*>& methods, const std::set<DexCacheResolvedClasses>& resolved_classes) { ProfileCompilationInfo info; - std::vector<MethodReference> method_refs; + std::vector<ProfileMethodInfo> profile_methods; + ScopedObjectAccess soa(Thread::Current()); + for (ArtMethod* method : methods) { + profile_methods.emplace_back(method->GetDexFile(), method->GetDexMethodIndex()); + } + if (!info.AddMethodsAndClasses(profile_methods, resolved_classes)) { + return false; + } + if (info.GetNumberOfMethods() != profile_methods.size()) { + return false; + } + return info.MergeAndSave(filename, nullptr, false); + } + + // Saves the given art methods to a profile backed by 'filename' and adds + // some fake inline caches to it. The added inline caches are returned in + // the out map `profile_methods_map`. + bool SaveProfilingInfoWithFakeInlineCaches( + const std::string& filename, + const std::vector<ArtMethod*>& methods, + /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) { + ProfileCompilationInfo info; + std::vector<ProfileMethodInfo> profile_methods; ScopedObjectAccess soa(Thread::Current()); for (ArtMethod* method : methods) { - method_refs.emplace_back(method->GetDexFile(), method->GetDexMethodIndex()); + std::vector<ProfileMethodInfo::ProfileInlineCache> caches; + // Monomorphic + for (uint16_t dex_pc = 0; dex_pc < 1; dex_pc++) { + std::vector<ProfileMethodInfo::ProfileClassReference> classes; + classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0)); + caches.emplace_back(dex_pc, classes); + } + // Polymorphic + for (uint16_t dex_pc = 1; dex_pc < 2; dex_pc++) { + std::vector<ProfileMethodInfo::ProfileClassReference> classes; + for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) { + classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); + } + caches.emplace_back(dex_pc, classes); + } + // Megamorphic + for (uint16_t dex_pc = 2; dex_pc < 3; dex_pc++) { + std::vector<ProfileMethodInfo::ProfileClassReference> classes; + for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) { + classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); + } + caches.emplace_back(dex_pc, classes); + } + ProfileMethodInfo pmi(method->GetDexFile(), method->GetDexMethodIndex(), caches); + profile_methods.push_back(pmi); + profile_methods_map->Put(method, pmi); + } + + if (!info.AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>())) { + return false; } - if (!info.AddMethodsAndClasses(method_refs, resolved_classes)) { + if (info.GetNumberOfMethods() != profile_methods.size()) { return false; } return info.MergeAndSave(filename, nullptr, false); } + ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo( + const ProfileMethodInfo& pmi) { + ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi; + SafeMap<DexFile*, uint8_t> dex_map; // dex files to profile index + for (const auto& inline_cache : pmi.inline_caches) { + for (const auto& class_ref : inline_cache.classes) { + uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file), + static_cast<uint8_t>(dex_map.size()))->second; + offline_pmi.inline_caches + .FindOrAdd(inline_cache.dex_pc)->second + .AddClass(dex_profile_index, class_ref.type_index); + if (dex_profile_index >= offline_pmi.dex_references.size()) { + // This is a new dex. + const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey( + class_ref.dex_file->GetLocation()); + offline_pmi.dex_references.emplace_back(dex_key, + class_ref.dex_file->GetLocationChecksum()); + } + } + } + return offline_pmi; + } + + // Creates an offline profile used for testing inline caches. + ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() { + ProfileCompilationInfo::OfflineProfileMethodInfo pmi; + + pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); + pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2); + pmi.dex_references.emplace_back("dex_location3", /* checksum */ 3); + + // Monomorphic + for (uint16_t dex_pc = 0; dex_pc < 1; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data; + dex_pc_data.AddClass(0, dex::TypeIndex(0)); + pmi.inline_caches.Put(dex_pc, dex_pc_data); + } + // Polymorphic + for (uint16_t dex_pc = 1; dex_pc < 2; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data; + dex_pc_data.AddClass(0, dex::TypeIndex(0)); + dex_pc_data.AddClass(1, dex::TypeIndex(1)); + dex_pc_data.AddClass(2, dex::TypeIndex(2)); + + pmi.inline_caches.Put(dex_pc, dex_pc_data); + } + // Megamorphic + for (uint16_t dex_pc = 2; dex_pc < 3; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data; + dex_pc_data.is_megamorphic = true; + pmi.inline_caches.Put(dex_pc, dex_pc_data); + } + + return pmi; + } + + void MakeMegamorphic(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) { + for (auto it : pmi->inline_caches) { + for (uint16_t k = 0; k <= 2 * InlineCache::kIndividualCacheSize; k++) { + it.second.AddClass(0, dex::TypeIndex(k)); + } + } + } + // Cannot sizeof the actual arrays so hardcode the values here. // They should not change anyway. static constexpr int kProfileMagicSize = 4; @@ -235,12 +358,12 @@ TEST_F(ProfileCompilationInfoTest, SaveEmpty) { TEST_F(ProfileCompilationInfoTest, LoadEmpty) { ScratchFile profile; - ProfileCompilationInfo empyt_info; + ProfileCompilationInfo empty_info; ProfileCompilationInfo loaded_info; ASSERT_TRUE(profile.GetFile()->ResetOffset()); ASSERT_TRUE(loaded_info.Load(GetFd(profile))); - ASSERT_TRUE(loaded_info.Equals(empyt_info)); + ASSERT_TRUE(loaded_info.Equals(empty_info)); } TEST_F(ProfileCompilationInfoTest, BadMagic) { @@ -324,4 +447,214 @@ TEST_F(ProfileCompilationInfoTest, UnexpectedContent) { ASSERT_FALSE(loaded_info.Load(GetFd(profile))); } +TEST_F(ProfileCompilationInfoTest, SaveInlineCaches) { + ScratchFile profile; + + ProfileCompilationInfo saved_info; + ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo(); + + // Add methods with inline caches. + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + // Add a method which is part of the same dex file as one of the + // class from the inline caches. + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info)); + // Add a method which is outside the set of dex files. + ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info)); + } + + ASSERT_TRUE(saved_info.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + // Check that we get back what we saved. + ProfileCompilationInfo loaded_info; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(loaded_info.Load(GetFd(profile))); + + ASSERT_TRUE(loaded_info.Equals(saved_info)); + + ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1; + ASSERT_TRUE(loaded_info.GetMethod("dex_location1", + /* checksum */ 1, + /* method_idx */ 3, + &loaded_pmi1)); + ASSERT_TRUE(loaded_pmi1 == pmi); + ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi2; + ASSERT_TRUE(loaded_info.GetMethod("dex_location4", + /* checksum */ 4, + /* method_idx */ 3, + &loaded_pmi2)); + ASSERT_TRUE(loaded_pmi2 == pmi); +} + +TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) { + ScratchFile profile; + + ProfileCompilationInfo saved_info; + ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo(); + + // Add methods with inline caches. + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info)); + } + + ASSERT_TRUE(saved_info.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + // Make the inline caches megamorphic and add them to the profile again. + ProfileCompilationInfo saved_info_extra; + ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo(); + MakeMegamorphic(&pmi_extra); + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra)); + } + + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(saved_info_extra.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + // Merge the profiles so that we have the same view as the file. + ASSERT_TRUE(saved_info.MergeWith(saved_info_extra)); + + // Check that we get back what we saved. + ProfileCompilationInfo loaded_info; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(loaded_info.Load(GetFd(profile))); + + ASSERT_TRUE(loaded_info.Equals(saved_info)); + + ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1; + ASSERT_TRUE(loaded_info.GetMethod("dex_location1", + /* checksum */ 1, + /* method_idx */ 3, + &loaded_pmi1)); + ASSERT_TRUE(loaded_pmi1 == pmi_extra); +} + +TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) { + ScratchFile profile; + + Thread* self = Thread::Current(); + jobject class_loader; + { + ScopedObjectAccess soa(self); + class_loader = LoadDex("ProfileTestMultiDex"); + } + ASSERT_NE(class_loader, nullptr); + + // Save virtual methods from Main. + std::set<DexCacheResolvedClasses> resolved_classes; + std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;"); + + SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map; + ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches( + profile.GetFilename(), main_methods, &profile_methods_map)); + + // Check that what we saved is in the profile. + ProfileCompilationInfo info; + ASSERT_TRUE(info.Load(GetFd(profile))); + ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size()); + { + ScopedObjectAccess soa(self); + for (ArtMethod* m : main_methods) { + ASSERT_TRUE(info.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); + const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second; + ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi; + ASSERT_TRUE(info.GetMethod(m->GetDexFile()->GetLocation(), + m->GetDexFile()->GetLocationChecksum(), + m->GetDexMethodIndex(), + &offline_pmi)); + ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi = + ConvertProfileMethodInfo(pmi); + ASSERT_EQ(converted_pmi, offline_pmi); + } + } +} + +TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCahce) { + ScratchFile profile; + + ProfileCompilationInfo info; + ProfileCompilationInfo::OfflineProfileMethodInfo pmi1 = GetOfflineProfileMethodInfo(); + ProfileCompilationInfo::OfflineProfileMethodInfo pmi2 = GetOfflineProfileMethodInfo(); + // Modify the checksum to trigger a mismatch. + pmi2.dex_references[0].dex_checksum++; + + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /*method_idx*/ 0, pmi1, &info)); + ASSERT_FALSE(AddMethod("dex_location2", /* checksum */ 2, /*method_idx*/ 0, pmi2, &info)); +} + +// Verify that profiles behave correctly even if the methods are added in a different +// order and with a different dex profile indices for the dex files. +TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) { + ScratchFile profile; + + ProfileCompilationInfo info; + ProfileCompilationInfo info_reindexed; + + ProfileCompilationInfo::OfflineProfileMethodInfo pmi; + pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); + pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2); + for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data; + dex_pc_data.AddClass(0, dex::TypeIndex(0)); + dex_pc_data.AddClass(1, dex::TypeIndex(1)); + pmi.inline_caches.Put(dex_pc, dex_pc_data); + } + + ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed; + pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2); + pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1); + for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data; + dex_pc_data.AddClass(1, dex::TypeIndex(0)); + dex_pc_data.AddClass(0, dex::TypeIndex(1)); + pmi_reindexed.inline_caches.Put(dex_pc, dex_pc_data); + } + + // Profile 1 and Profile 2 get the same methods but in different order. + // This will trigger a different dex numbers. + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &info)); + ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &info)); + } + + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ASSERT_TRUE(AddMethod( + "dex_location2", /* checksum */ 2, method_idx, pmi_reindexed, &info_reindexed)); + ASSERT_TRUE(AddMethod( + "dex_location1", /* checksum */ 1, method_idx, pmi_reindexed, &info_reindexed)); + } + + ProfileCompilationInfo info_backup = info; + ASSERT_TRUE(info.MergeWith(info_reindexed)); + // Merging should have no effect as we're adding the exact same stuff. + ASSERT_TRUE(info.Equals(info_backup)); + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1; + ASSERT_TRUE(info.GetMethod("dex_location1", + /* checksum */ 1, + /* method_idx */ method_idx, + &loaded_pmi1)); + ASSERT_TRUE(loaded_pmi1 == pmi); + ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi2; + ASSERT_TRUE(info.GetMethod("dex_location2", + /* checksum */ 2, + /* method_idx */ method_idx, + &loaded_pmi2)); + ASSERT_TRUE(loaded_pmi2 == pmi); + } +} + +TEST_F(ProfileCompilationInfoTest, AddMoreDexFileThanLimit) { + ProfileCompilationInfo info; + // Save a few methods. + for (uint16_t i = 0; i < std::numeric_limits<uint8_t>::max(); i++) { + std::string dex_location = std::to_string(i); + ASSERT_TRUE(AddMethod(dex_location, /* checksum */ 1, /* method_idx */ i, &info)); + } + // We only support at most 255 dex files. + ASSERT_FALSE(AddMethod( + /*dex_location*/ "256", /* checksum */ 1, /* method_idx */ 0, &info)); +} + } // namespace art diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 025d10ccc0..61e6c4126a 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -236,10 +236,10 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { std::set<DexCacheResolvedClasses> resolved_classes_for_location; const std::string& filename = it.first; const std::set<std::string>& locations = it.second; - std::vector<MethodReference> methods_for_location; + std::vector<ProfileMethodInfo> profile_methods_for_location; for (const MethodReference& ref : methods) { if (locations.find(ref.dex_file->GetBaseLocation()) != locations.end()) { - methods_for_location.push_back(ref); + profile_methods_for_location.emplace_back(ref.dex_file, ref.dex_method_index); } } for (const DexCacheResolvedClasses& classes : resolved_classes) { @@ -253,7 +253,7 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { } } ProfileCompilationInfo* info = GetCachedProfiledInfo(filename); - info->AddMethodsAndClasses(methods_for_location, resolved_classes_for_location); + info->AddMethodsAndClasses(profile_methods_for_location, resolved_classes_for_location); total_number_of_profile_entries_cached += resolved_classes_for_location.size(); } max_number_of_profile_entries_cached_ = std::max( @@ -280,15 +280,15 @@ bool ProfileSaver::ProcessProfilingInfo(uint16_t* new_methods) { } const std::string& filename = it.first; const std::set<std::string>& locations = it.second; - std::vector<MethodReference> methods; + std::vector<ProfileMethodInfo> profile_methods; { ScopedObjectAccess soa(Thread::Current()); - jit_code_cache_->GetProfiledMethods(locations, methods); + jit_code_cache_->GetProfiledMethods(locations, profile_methods); total_number_of_code_cache_queries_++; } ProfileCompilationInfo* cached_info = GetCachedProfiledInfo(filename); - cached_info->AddMethodsAndClasses(methods, std::set<DexCacheResolvedClasses>()); + cached_info->AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>()); int64_t delta_number_of_methods = cached_info->GetNumberOfMethods() - static_cast<int64_t>(last_save_number_of_methods_); diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index 1c58a83679..f42a8da8fa 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -39,7 +39,7 @@ class Class; // Once the classes_ array is full, we consider the INVOKE to be megamorphic. class InlineCache { public: - static constexpr uint16_t kIndividualCacheSize = 5; + static constexpr uint8_t kIndividualCacheSize = 5; private: uint32_t dex_pc_; diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 6ecfd8c714..58c5d17d1c 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -419,6 +419,32 @@ static inline bool DoCallPolymorphic(ArtMethod* called_method, JValue* result, const mirror::MethodHandle::Kind handle_kind) REQUIRES_SHARED(Locks::mutator_lock_) { + // For virtual and interface methods ensure called_method points to + // the actual method to invoke. + if (handle_kind == mirror::MethodHandle::Kind::kInvokeVirtual || + handle_kind == mirror::MethodHandle::Kind::kInvokeInterface) { + uint32_t receiver_reg = is_range ? first_arg : args[0]; + ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(receiver_reg)); + if (IsCallerTransformer(callsite_type)) { + // The current receiver is an emulated stack frame, the method's + // receiver needs to be fetched from there as the emulated frame + // will be unpacked into a new frame. + receiver = ObjPtr<mirror::EmulatedStackFrame>::DownCast(receiver)->GetReceiver(); + } + + ObjPtr<mirror::Class> declaring_class(called_method->GetDeclaringClass()); + if (receiver == nullptr || receiver->GetClass() != declaring_class) { + // Verify that _vRegC is an object reference and of the type expected by + // the receiver. + if (!VerifyObjectIsClass(receiver, declaring_class)) { + DCHECK(self->IsExceptionPending()); + return false; + } + called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface( + called_method, kRuntimePointerSize); + } + } + // Compute method information. const DexFile::CodeItem* code_item = called_method->GetCodeItem(); @@ -502,24 +528,6 @@ static inline bool DoCallPolymorphic(ArtMethod* called_method, } } - // See TODO in DoInvokePolymorphic : We need to perform this dynamic, receiver - // based dispatch right before we perform the actual call, because the - // receiver isn't known very early. - if (handle_kind == mirror::MethodHandle::Kind::kInvokeVirtual || - handle_kind == mirror::MethodHandle::Kind::kInvokeInterface) { - ObjPtr<mirror::Object> receiver(new_shadow_frame->GetVRegReference(first_dest_reg)); - ObjPtr<mirror::Class> declaring_class(called_method->GetDeclaringClass()); - // Verify that _vRegC is an object reference and of the type expected by - // the receiver. - if (!VerifyObjectIsClass(receiver, declaring_class)) { - DCHECK(self->IsExceptionPending()); - return false; - } - - called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface( - called_method, kRuntimePointerSize); - } - PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result); if (self->IsExceptionPending()) { return false; diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 9a9a5d8398..eb2ec9b3c8 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -951,7 +951,8 @@ ObjPtr<Class> Class::GetDirectInterface(Thread* self, ObjPtr<Class> klass, uint3 return interfaces->Get(idx); } else { dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx); - ObjPtr<Class> interface = klass->GetDexCache()->GetResolvedType(type_idx); + ObjPtr<Class> interface = ClassLinker::LookupResolvedType( + type_idx, klass->GetDexCache(), klass->GetClassLoader()); return interface; } } diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 973c8ed07d..29bf6a0240 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -41,14 +41,22 @@ inline uint32_t DexCache::ClassSize(PointerSize pointer_size) { return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size); } -inline mirror::String* DexCache::GetResolvedString(dex::StringIndex string_idx) { +inline uint32_t DexCache::StringSlotIndex(dex::StringIndex string_idx) { DCHECK_LT(string_idx.index_, GetDexFile()->NumStringIds()); - return StringDexCachePair::Lookup(GetStrings(), string_idx.index_, NumStrings()).Read(); + const uint32_t slot_idx = string_idx.index_ % kDexCacheStringCacheSize; + DCHECK_LT(slot_idx, NumStrings()); + return slot_idx; } -inline void DexCache::SetResolvedString(dex::StringIndex string_idx, - ObjPtr<mirror::String> resolved) { - StringDexCachePair::Assign(GetStrings(), string_idx.index_, resolved.Ptr(), NumStrings()); +inline String* DexCache::GetResolvedString(dex::StringIndex string_idx) { + return GetStrings()[StringSlotIndex(string_idx)].load( + std::memory_order_relaxed).GetObjectForIndex(string_idx.index_); +} + +inline void DexCache::SetResolvedString(dex::StringIndex string_idx, ObjPtr<String> resolved) { + DCHECK(resolved != nullptr); + GetStrings()[StringSlotIndex(string_idx)].store( + StringDexCachePair(resolved, string_idx.index_), std::memory_order_relaxed); Runtime* const runtime = Runtime::Current(); if (UNLIKELY(runtime->IsActiveTransaction())) { DCHECK(runtime->IsAotCompiler()); @@ -59,50 +67,70 @@ inline void DexCache::SetResolvedString(dex::StringIndex string_idx, } inline void DexCache::ClearString(dex::StringIndex string_idx) { - const uint32_t slot_idx = string_idx.index_ % NumStrings(); DCHECK(Runtime::Current()->IsAotCompiler()); + uint32_t slot_idx = StringSlotIndex(string_idx); StringDexCacheType* slot = &GetStrings()[slot_idx]; // This is racy but should only be called from the transactional interpreter. if (slot->load(std::memory_order_relaxed).index == string_idx.index_) { - StringDexCachePair cleared( - nullptr, - StringDexCachePair::InvalidIndexForSlot(slot_idx)); + StringDexCachePair cleared(nullptr, StringDexCachePair::InvalidIndexForSlot(slot_idx)); slot->store(cleared, std::memory_order_relaxed); } } +inline uint32_t DexCache::TypeSlotIndex(dex::TypeIndex type_idx) { + DCHECK_LT(type_idx.index_, GetDexFile()->NumTypeIds()); + const uint32_t slot_idx = type_idx.index_ % kDexCacheTypeCacheSize; + DCHECK_LT(slot_idx, NumResolvedTypes()); + return slot_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 dependency or a lock. - DCHECK_LT(type_idx.index_, NumResolvedTypes()); - return GetResolvedTypes()[type_idx.index_].Read(); + return GetResolvedTypes()[TypeSlotIndex(type_idx)].load( + std::memory_order_relaxed).GetObjectForIndex(type_idx.index_); } inline void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) { - DCHECK_LT(type_idx.index_, NumResolvedTypes()); // NOTE: Unchecked, i.e. not throwing AIOOB. + DCHECK(resolved != nullptr); // TODO default transaction support. // Use a release store for SetResolvedType. This is done to prevent other threads from seeing a // class but not necessarily seeing the loaded members like the static fields array. // See b/32075261. - reinterpret_cast<Atomic<GcRoot<mirror::Class>>&>(GetResolvedTypes()[type_idx.index_]). - StoreRelease(GcRoot<Class>(resolved)); + GetResolvedTypes()[TypeSlotIndex(type_idx)].store( + TypeDexCachePair(resolved, type_idx.index_), std::memory_order_release); // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); } -inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) { - DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); - DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds()); - return MethodTypeDexCachePair::Lookup( - GetResolvedMethodTypes(), proto_idx, NumResolvedMethodTypes()).Read(); +inline void DexCache::ClearResolvedType(dex::TypeIndex type_idx) { + DCHECK(Runtime::Current()->IsAotCompiler()); + uint32_t slot_idx = TypeSlotIndex(type_idx); + TypeDexCacheType* slot = &GetResolvedTypes()[slot_idx]; + // This is racy but should only be called from the single-threaded ImageWriter and tests. + if (slot->load(std::memory_order_relaxed).index == type_idx.index_) { + TypeDexCachePair cleared(nullptr, TypeDexCachePair::InvalidIndexForSlot(slot_idx)); + slot->store(cleared, std::memory_order_relaxed); + } } -inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) { +inline uint32_t DexCache::MethodTypeSlotIndex(uint32_t proto_idx) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds()); + const uint32_t slot_idx = proto_idx % kDexCacheMethodTypeCacheSize; + DCHECK_LT(slot_idx, NumResolvedMethodTypes()); + return slot_idx; +} - MethodTypeDexCachePair::Assign(GetResolvedMethodTypes(), proto_idx, resolved, - NumResolvedMethodTypes()); +inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) { + return GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].load( + std::memory_order_relaxed).GetObjectForIndex(proto_idx); +} + +inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) { + DCHECK(resolved != nullptr); + GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].store( + MethodTypeDexCachePair(resolved, proto_idx), std::memory_order_relaxed); // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); } @@ -228,15 +256,13 @@ inline void DexCache::VisitReferences(ObjPtr<Class> klass, const Visitor& visito VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor); // Visit arrays after. if (kVisitNativeRoots) { - VisitDexCachePairs<mirror::String, kReadBarrierOption, Visitor>( + VisitDexCachePairs<String, kReadBarrierOption, Visitor>( GetStrings(), NumStrings(), visitor); - GcRoot<mirror::Class>* resolved_types = GetResolvedTypes(); - for (size_t i = 0, num_types = NumResolvedTypes(); i != num_types; ++i) { - visitor.VisitRootIfNonNull(resolved_types[i].AddressWithoutBarrier()); - } + VisitDexCachePairs<Class, kReadBarrierOption, Visitor>( + GetResolvedTypes(), NumResolvedTypes(), visitor); - VisitDexCachePairs<mirror::MethodType, kReadBarrierOption, Visitor>( + VisitDexCachePairs<MethodType, kReadBarrierOption, Visitor>( GetResolvedMethodTypes(), NumResolvedMethodTypes(), visitor); GcRoot<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites(); @@ -247,35 +273,37 @@ inline void DexCache::VisitReferences(ObjPtr<Class> klass, const Visitor& visito } template <ReadBarrierOption kReadBarrierOption, typename Visitor> -inline void DexCache::FixupStrings(mirror::StringDexCacheType* dest, const Visitor& visitor) { - mirror::StringDexCacheType* src = GetStrings(); +inline void DexCache::FixupStrings(StringDexCacheType* dest, const Visitor& visitor) { + StringDexCacheType* src = GetStrings(); for (size_t i = 0, count = NumStrings(); i < count; ++i) { StringDexCachePair source = src[i].load(std::memory_order_relaxed); - mirror::String* ptr = source.object.Read<kReadBarrierOption>(); - mirror::String* new_source = visitor(ptr); + String* ptr = source.object.Read<kReadBarrierOption>(); + String* new_source = visitor(ptr); source.object = GcRoot<String>(new_source); dest[i].store(source, std::memory_order_relaxed); } } template <ReadBarrierOption kReadBarrierOption, typename Visitor> -inline void DexCache::FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visitor& visitor) { - GcRoot<mirror::Class>* src = GetResolvedTypes(); +inline void DexCache::FixupResolvedTypes(TypeDexCacheType* dest, const Visitor& visitor) { + TypeDexCacheType* src = GetResolvedTypes(); for (size_t i = 0, count = NumResolvedTypes(); i < count; ++i) { - mirror::Class* source = src[i].Read<kReadBarrierOption>(); - mirror::Class* new_source = visitor(source); - dest[i] = GcRoot<mirror::Class>(new_source); + TypeDexCachePair source = src[i].load(std::memory_order_relaxed); + Class* ptr = source.object.Read<kReadBarrierOption>(); + Class* new_source = visitor(ptr); + source.object = GcRoot<Class>(new_source); + dest[i].store(source, std::memory_order_relaxed); } } template <ReadBarrierOption kReadBarrierOption, typename Visitor> -inline void DexCache::FixupResolvedMethodTypes(mirror::MethodTypeDexCacheType* dest, +inline void DexCache::FixupResolvedMethodTypes(MethodTypeDexCacheType* dest, const Visitor& visitor) { - mirror::MethodTypeDexCacheType* src = GetResolvedMethodTypes(); + MethodTypeDexCacheType* src = GetResolvedMethodTypes(); for (size_t i = 0, count = NumResolvedMethodTypes(); i < count; ++i) { MethodTypeDexCachePair source = src[i].load(std::memory_order_relaxed); - mirror::MethodType* ptr = source.object.Read<kReadBarrierOption>(); - mirror::MethodType* new_source = visitor(ptr); + MethodType* ptr = source.object.Read<kReadBarrierOption>(); + MethodType* new_source = visitor(ptr); source.object = GcRoot<MethodType>(new_source); dest[i].store(source, std::memory_order_relaxed); } diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index 0f6acab7e1..1b8b3913b9 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -58,8 +58,8 @@ void DexCache::InitializeDexCache(Thread* self, mirror::StringDexCacheType* strings = (dex_file->NumStringIds() == 0u) ? nullptr : reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset()); - GcRoot<mirror::Class>* types = (dex_file->NumTypeIds() == 0u) ? nullptr : - reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset()); + mirror::TypeDexCacheType* types = (dex_file->NumTypeIds() == 0u) ? nullptr : + reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset()); ArtMethod** methods = (dex_file->NumMethodIds() == 0u) ? nullptr : reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset()); ArtField** fields = (dex_file->NumFieldIds() == 0u) ? nullptr : @@ -69,6 +69,10 @@ void DexCache::InitializeDexCache(Thread* self, if (dex_file->NumStringIds() < num_strings) { num_strings = dex_file->NumStringIds(); } + size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize; + if (dex_file->NumTypeIds() < num_types) { + num_types = dex_file->NumTypeIds(); + } // Note that we allocate the method type dex caches regardless of this flag, // and we make sure here that they're not used by the runtime. This is in the @@ -108,8 +112,9 @@ void DexCache::InitializeDexCache(Thread* self, CHECK_EQ(strings[i].load(std::memory_order_relaxed).index, 0u); CHECK(strings[i].load(std::memory_order_relaxed).object.IsNull()); } - for (size_t i = 0; i < dex_file->NumTypeIds(); ++i) { - CHECK(types[i].IsNull()); + for (size_t i = 0; i < num_types; ++i) { + CHECK_EQ(types[i].load(std::memory_order_relaxed).index, 0u); + CHECK(types[i].load(std::memory_order_relaxed).object.IsNull()); } for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) { CHECK(mirror::DexCache::GetElementPtrSize(methods, i, image_pointer_size) == nullptr); @@ -128,6 +133,9 @@ void DexCache::InitializeDexCache(Thread* self, if (strings != nullptr) { mirror::StringDexCachePair::Initialize(strings); } + if (types != nullptr) { + mirror::TypeDexCachePair::Initialize(types); + } if (method_types != nullptr) { mirror::MethodTypeDexCachePair::Initialize(method_types); } @@ -136,7 +144,7 @@ void DexCache::InitializeDexCache(Thread* self, strings, num_strings, types, - dex_file->NumTypeIds(), + num_types, methods, dex_file->NumMethodIds(), fields, @@ -152,7 +160,7 @@ void DexCache::Init(const DexFile* dex_file, ObjPtr<String> location, StringDexCacheType* strings, uint32_t num_strings, - GcRoot<Class>* resolved_types, + TypeDexCacheType* resolved_types, uint32_t num_resolved_types, ArtMethod** resolved_methods, uint32_t num_resolved_methods, diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index 10bb5aa01c..057919806f 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -18,14 +18,14 @@ #define ART_RUNTIME_MIRROR_DEX_CACHE_H_ #include "array.h" -#include "art_field.h" -#include "class.h" +#include "base/bit_utils.h" #include "dex_file_types.h" #include "object.h" #include "object_array.h" namespace art { +class ArtField; class ArtMethod; struct DexCacheOffsets; class DexFile; @@ -37,6 +37,7 @@ class Thread; namespace mirror { class CallSite; +class Class; class MethodType; class String; @@ -61,7 +62,7 @@ template <typename T> struct PACKED(8) DexCachePair { // it's always non-null if the id branch succeeds (except for the 0th id). // Set the initial state for the 0th entry to be {0,1} which is guaranteed to fail // the lookup id == stored id branch. - DexCachePair(T* object, uint32_t index) + DexCachePair(ObjPtr<T> object, uint32_t index) : object(object), index(index) {} DexCachePair() = default; @@ -75,39 +76,28 @@ template <typename T> struct PACKED(8) DexCachePair { dex_cache[0].store(first_elem, std::memory_order_relaxed); } - static GcRoot<T> Lookup(std::atomic<DexCachePair<T>>* dex_cache, - uint32_t idx, - uint32_t cache_size) { - DCHECK_NE(cache_size, 0u); - DexCachePair<T> element = dex_cache[idx % cache_size].load(std::memory_order_relaxed); - if (idx != element.index) { - return GcRoot<T>(nullptr); - } - - DCHECK(!element.object.IsNull()); - return element.object; - } - - static void Assign(std::atomic<DexCachePair<T>>* dex_cache, - uint32_t idx, - T* object, - uint32_t cache_size) { - DCHECK_LT(idx % cache_size, cache_size); - dex_cache[idx % cache_size].store( - DexCachePair<T>(object, idx), std::memory_order_relaxed); - } - static uint32_t InvalidIndexForSlot(uint32_t slot) { // Since the cache size is a power of two, 0 will always map to slot 0. // Use 1 for slot 0 and 0 for all other slots. return (slot == 0) ? 1u : 0u; } + + T* GetObjectForIndex(uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_) { + if (idx != index) { + return nullptr; + } + DCHECK(!object.IsNull()); + return object.Read(); + } }; -using StringDexCachePair = DexCachePair<mirror::String>; +using TypeDexCachePair = DexCachePair<Class>; +using TypeDexCacheType = std::atomic<TypeDexCachePair>; + +using StringDexCachePair = DexCachePair<String>; using StringDexCacheType = std::atomic<StringDexCachePair>; -using MethodTypeDexCachePair = DexCachePair<mirror::MethodType>; +using MethodTypeDexCachePair = DexCachePair<MethodType>; using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>; // C++ mirror of java.lang.DexCache. @@ -116,6 +106,11 @@ class MANAGED DexCache FINAL : public Object { // Size of java.lang.DexCache.class. static uint32_t ClassSize(PointerSize pointer_size); + // Size of type dex cache. Needs to be a power of 2 for entrypoint assumptions to hold. + static constexpr size_t kDexCacheTypeCacheSize = 1024; + static_assert(IsPowerOfTwo(kDexCacheTypeCacheSize), + "Type dex cache size is not a power of 2."); + // Size of string dex cache. Needs to be a power of 2 for entrypoint assumptions to hold. static constexpr size_t kDexCacheStringCacheSize = 1024; static_assert(IsPowerOfTwo(kDexCacheStringCacheSize), @@ -127,6 +122,10 @@ class MANAGED DexCache FINAL : public Object { static_assert(IsPowerOfTwo(kDexCacheMethodTypeCacheSize), "MethodType dex cache size is not a power of 2."); + static constexpr size_t StaticTypeSize() { + return kDexCacheTypeCacheSize; + } + static constexpr size_t StaticStringSize() { return kDexCacheStringCacheSize; } @@ -157,7 +156,7 @@ class MANAGED DexCache FINAL : public Object { REQUIRES_SHARED(Locks::mutator_lock_); template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor> - void FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visitor& visitor) + void FixupResolvedTypes(TypeDexCacheType* dest, const Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor> @@ -224,7 +223,7 @@ class MANAGED DexCache FINAL : public Object { return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_call_sites_); } - mirror::String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE + String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_); void SetResolvedString(dex::StringIndex string_idx, ObjPtr<mirror::String> resolved) ALWAYS_INLINE @@ -239,6 +238,8 @@ class MANAGED DexCache FINAL : public Object { void SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) REQUIRES_SHARED(Locks::mutator_lock_); + void ClearResolvedType(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); + ALWAYS_INLINE ArtMethod* GetResolvedMethod(uint32_t method_idx, PointerSize ptr_size) REQUIRES_SHARED(Locks::mutator_lock_); @@ -278,11 +279,11 @@ class MANAGED DexCache FINAL : public Object { SetFieldPtr<false>(StringsOffset(), strings); } - GcRoot<Class>* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { - return GetFieldPtr<GcRoot<Class>*>(ResolvedTypesOffset()); + TypeDexCacheType* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldPtr<TypeDexCacheType*>(ResolvedTypesOffset()); } - void SetResolvedTypes(GcRoot<Class>* resolved_types) + void SetResolvedTypes(TypeDexCacheType* resolved_types) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { SetFieldPtr<false>(ResolvedTypesOffset(), resolved_types); @@ -363,7 +364,7 @@ class MANAGED DexCache FINAL : public Object { SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file); } - void SetLocation(ObjPtr<mirror::String> location) REQUIRES_SHARED(Locks::mutator_lock_); + void SetLocation(ObjPtr<String> location) REQUIRES_SHARED(Locks::mutator_lock_); // NOTE: Get/SetElementPtrSize() are intended for working with ArtMethod** and ArtField** // provided by GetResolvedMethods/Fields() and ArtMethod::GetDexCacheResolvedMethods(), @@ -380,7 +381,7 @@ class MANAGED DexCache FINAL : public Object { ObjPtr<String> location, StringDexCacheType* strings, uint32_t num_strings, - GcRoot<Class>* resolved_types, + TypeDexCacheType* resolved_types, uint32_t num_resolved_types, ArtMethod** resolved_methods, uint32_t num_resolved_methods, @@ -393,12 +394,16 @@ class MANAGED DexCache FINAL : public Object { PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); + uint32_t StringSlotIndex(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_); + uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_); + uint32_t MethodTypeSlotIndex(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); + // Visit instance fields of the dex cache as well as its associated arrays. template <bool kVisitNativeRoots, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor> - void VisitReferences(ObjPtr<mirror::Class> klass, const Visitor& visitor) + void VisitReferences(ObjPtr<Class> klass, const Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_); HeapReference<Object> dex_; @@ -410,7 +415,7 @@ class MANAGED DexCache FINAL : public Object { uint64_t resolved_method_types_; // std::atomic<MethodTypeDexCachePair>* array with // num_resolved_method_types_ elements. uint64_t resolved_methods_; // ArtMethod*, array with num_resolved_methods_ elements. - uint64_t resolved_types_; // GcRoot<Class>*, array with num_resolved_types_ elements. + uint64_t resolved_types_; // TypeDexCacheType*, array with num_resolved_types_ elements. uint64_t strings_; // std::atomic<StringDexCachePair>*, array with num_strings_ // elements. diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index 5a2ab7151c..ef0aaaaa70 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -51,7 +51,8 @@ TEST_F(DexCacheTest, Open) { EXPECT_TRUE(dex_cache->StaticStringSize() == dex_cache->NumStrings() || java_lang_dex_file_->NumStringIds() == dex_cache->NumStrings()); - EXPECT_EQ(java_lang_dex_file_->NumTypeIds(), dex_cache->NumResolvedTypes()); + EXPECT_TRUE(dex_cache->StaticTypeSize() == dex_cache->NumResolvedTypes() + || java_lang_dex_file_->NumTypeIds() == dex_cache->NumResolvedTypes()); EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods()); EXPECT_EQ(java_lang_dex_file_->NumFieldIds(), dex_cache->NumResolvedFields()); EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes() diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h index ddd84a167d..76859ef1c8 100644 --- a/runtime/mirror/emulated_stack_frame.h +++ b/runtime/mirror/emulated_stack_frame.h @@ -62,6 +62,10 @@ class MANAGED EmulatedStackFrame : public Object { return GetFieldObject<MethodType>(OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, type_)); } + mirror::Object* GetReceiver() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetReferences()->Get(0); + } + static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 1234933db9..0e61cf64f9 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -155,6 +155,105 @@ class NullableScopedUtfChars { void operator=(const NullableScopedUtfChars&); }; +static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) { + if (end <= start) { + ScopedObjectAccess soa(env); + ThrowWrappedIOException("Bad range"); + return nullptr; + } + + std::string error_message; + size_t length = static_cast<size_t>(end - start); + std::unique_ptr<MemMap> dex_mem_map(MemMap::MapAnonymous("DEX data", + nullptr, + length, + PROT_READ | PROT_WRITE, + /* low_4gb */ false, + /* reuse */ false, + &error_message)); + if (dex_mem_map == nullptr) { + ScopedObjectAccess soa(env); + ThrowWrappedIOException("%s", error_message.c_str()); + } + return dex_mem_map; +} + +static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map) { + std::string location = StringPrintf("Anonymous-DexFile@%p-%p", + dex_mem_map->Begin(), + dex_mem_map->End()); + std::string error_message; + std::unique_ptr<const DexFile> dex_file(DexFile::Open(location, + 0, + std::move(dex_mem_map), + /* verify */ true, + /* verify_location */ true, + &error_message)); + if (dex_file == nullptr) { + ScopedObjectAccess soa(env); + ThrowWrappedIOException("%s", error_message.c_str()); + return nullptr; + } + + if (!dex_file->DisableWrite()) { + ScopedObjectAccess soa(env); + ThrowWrappedIOException("Failed to make dex file read-only"); + return nullptr; + } + + return dex_file.release(); +} + +static jobject CreateSingleDexFileCookie(JNIEnv* env, std::unique_ptr<MemMap> data) { + std::unique_ptr<const DexFile> dex_file(CreateDexFile(env, std::move(data))); + if (dex_file.get() == nullptr) { + DCHECK(env->ExceptionCheck()); + return nullptr; + } + std::vector<std::unique_ptr<const DexFile>> dex_files; + dex_files.push_back(std::move(dex_file)); + return ConvertDexFilesToJavaArray(env, nullptr, dex_files); +} + +static jobject DexFile_createCookieWithDirectBuffer(JNIEnv* env, + jclass, + jobject buffer, + jint start, + jint end) { + uint8_t* base_address = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer)); + if (base_address == nullptr) { + ScopedObjectAccess soa(env); + ThrowWrappedIOException("dexFileBuffer not direct"); + return 0; + } + + std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end)); + if (dex_mem_map == nullptr) { + DCHECK(Thread::Current()->IsExceptionPending()); + return 0; + } + + size_t length = static_cast<size_t>(end - start); + memcpy(dex_mem_map->Begin(), base_address, length); + return CreateSingleDexFileCookie(env, std::move(dex_mem_map)); +} + +static jobject DexFile_createCookieWithArray(JNIEnv* env, + jclass, + jbyteArray buffer, + jint start, + jint end) { + std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end)); + if (dex_mem_map == nullptr) { + DCHECK(Thread::Current()->IsExceptionPending()); + return 0; + } + + auto destination = reinterpret_cast<jbyte*>(dex_mem_map.get()->Begin()); + env->GetByteArrayRegion(buffer, start, end - start, destination); + return CreateSingleDexFileCookie(env, std::move(dex_mem_map)); +} + static jobject DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceName, @@ -591,6 +690,9 @@ static JNINativeMethod gMethods[] = { "Ljava/lang/ClassLoader;" "[Ldalvik/system/DexPathList$Element;" ")Ljava/lang/Object;"), + NATIVE_METHOD(DexFile, createCookieWithDirectBuffer, + "(Ljava/nio/ByteBuffer;II)Ljava/lang/Object;"), + NATIVE_METHOD(DexFile, createCookieWithArray, "([BII)Ljava/lang/Object;"), NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"), NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"), NATIVE_METHOD(DexFile, diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc deleted file mode 100644 index 07959607fc..0000000000 --- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc +++ /dev/null @@ -1,198 +0,0 @@ -/* - * 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 "dalvik_system_InMemoryDexClassLoader_DexData.h" - -#include "android-base/stringprintf.h" - -#include "class_linker.h" -#include "common_throws.h" -#include "dex_file.h" -#include "jni_internal.h" -#include "mem_map.h" -#include "mirror/class_loader.h" -#include "mirror/object-inl.h" -#include "oat_file.h" -#include "scoped_thread_state_change-inl.h" -#include "ScopedUtfChars.h" - -namespace art { - -using android::base::StringPrintf; - -static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) { - if (end <= start) { - ScopedObjectAccess soa(env); - ThrowWrappedIOException("Bad range"); - return nullptr; - } - - std::string error_message; - size_t length = static_cast<size_t>(end - start); - std::unique_ptr<MemMap> dex_mem_map(MemMap::MapAnonymous("DEX data", - nullptr, - length, - PROT_READ | PROT_WRITE, - /* low_4gb */ false, - /* reuse */ false, - &error_message)); - if (dex_mem_map == nullptr) { - ScopedObjectAccess soa(env); - ThrowWrappedIOException("%s", error_message.c_str()); - } - return dex_mem_map; -} - -static jlong DexFileToCookie(const DexFile* dex_file) { - return reinterpret_cast<jlong>(dex_file); -} - -static const DexFile* CookieToDexFile(jlong cookie) { - return reinterpret_cast<const DexFile*>(cookie); -} - -static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map) { - std::string location = StringPrintf("InMemoryDexClassLoader_DexData@%p-%p", - dex_mem_map->Begin(), - dex_mem_map->End()); - std::string error_message; - std::unique_ptr<const DexFile> dex_file(DexFile::Open(location, - 0, - std::move(dex_mem_map), - /* verify */ true, - /* verify_location */ true, - &error_message)); - if (dex_file == nullptr) { - ScopedObjectAccess soa(env); - ThrowWrappedIOException("%s", error_message.c_str()); - return nullptr; - } - - if (!dex_file->DisableWrite()) { - ScopedObjectAccess soa(env); - ThrowWrappedIOException("Failed to make dex file read-only"); - return nullptr; - } - - return dex_file.release(); -} - -static jlong InMemoryDexClassLoader_DexData_initializeWithDirectBuffer( - JNIEnv* env, jclass, jobject buffer, jint start, jint end) { - uint8_t* base_address = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer)); - if (base_address == nullptr) { - ScopedObjectAccess soa(env); - ThrowWrappedIOException("dexFileBuffer not direct"); - return 0; - } - - std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end)); - if (dex_mem_map == nullptr) { - DCHECK(Thread::Current()->IsExceptionPending()); - return 0; - } - - size_t length = static_cast<size_t>(end - start); - memcpy(dex_mem_map->Begin(), base_address, length); - return DexFileToCookie(CreateDexFile(env, std::move(dex_mem_map))); -} - -static jlong InMemoryDexClassLoader_DexData_initializeWithArray( - JNIEnv* env, jclass, jbyteArray buffer, jint start, jint end) { - std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end)); - if (dex_mem_map == nullptr) { - DCHECK(Thread::Current()->IsExceptionPending()); - return 0; - } - - auto destination = reinterpret_cast<jbyte*>(dex_mem_map.get()->Begin()); - env->GetByteArrayRegion(buffer, start, end - start, destination); - return DexFileToCookie(CreateDexFile(env, std::move(dex_mem_map))); -} - -static void InMemoryDexClassLoader_DexData_uninitialize(JNIEnv* env, jclass, jlong cookie) { - const DexFile* dex_file = CookieToDexFile(cookie); - if (kIsDebugBuild) { - ScopedObjectAccess soa(env); - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - CHECK(!class_linker->IsDexFileRegistered(soa.Self(), *dex_file)); - } - delete dex_file; -} - -static jclass InMemoryDexClassLoader_DexData_findClass( - JNIEnv* env, jobject dexData, jstring name, jobject loader, jlong cookie) { - ScopedUtfChars scoped_class_name(env, name); - if (env->ExceptionCheck()) { - return nullptr; - } - - const char* class_name = scoped_class_name.c_str(); - const std::string descriptor(DotToDescriptor(class_name)); - const char* class_descriptor = descriptor.c_str(); - const size_t hash = ComputeModifiedUtf8Hash(class_descriptor); - const DexFile* dex_file = CookieToDexFile(cookie); - const DexFile::ClassDef* dex_class_def = - OatDexFile::FindClassDef(*dex_file, class_descriptor, hash); - if (dex_class_def != nullptr) { - ScopedObjectAccess soa(env); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - StackHandleScope<1> handle_scope(soa.Self()); - Handle<mirror::ClassLoader> class_loader( - handle_scope.NewHandle(soa.Decode<mirror::ClassLoader>(loader))); - ObjPtr<mirror::DexCache> dex_cache = - class_linker->RegisterDexFile(*dex_file, class_loader.Get()); - if (dex_cache == nullptr) { - // OOME or InternalError (dexFile already registered with a different class loader). - soa.Self()->AssertPendingException(); - return nullptr; - } - ObjPtr<mirror::Class> result = class_linker->DefineClass( - soa.Self(), - class_descriptor, - hash, class_loader, - *dex_file, - *dex_class_def); - if (result != nullptr) { - // Ensure the class table has a strong reference to the - // InMemoryClassLoader/DexData instance now that a class has - // been loaded. - class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexData), - class_loader.Get()); - return soa.AddLocalReference<jclass>(result); - } - } - - VLOG(class_linker) << "Failed to find dex_class_def " << class_name; - return nullptr; -} - -static JNINativeMethod gMethods[] = { - NATIVE_METHOD(InMemoryDexClassLoader_DexData, - initializeWithDirectBuffer, - "(Ljava/nio/ByteBuffer;II)J"), - NATIVE_METHOD(InMemoryDexClassLoader_DexData, initializeWithArray, "([BII)J"), - NATIVE_METHOD(InMemoryDexClassLoader_DexData, uninitialize, "(J)V"), - NATIVE_METHOD(InMemoryDexClassLoader_DexData, - findClass, - "(Ljava/lang/String;Ljava/lang/ClassLoader;J)Ljava/lang/Class;"), -}; - -void register_dalvik_system_InMemoryDexClassLoader_DexData(JNIEnv* env) { - REGISTER_NATIVE_METHODS("dalvik/system/InMemoryDexClassLoader$DexData"); -} - -} // namespace art diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc index b1ed74a6de..ee6dda56a5 100644 --- a/runtime/native/java_lang_DexCache.cc +++ b/runtime/native/java_lang_DexCache.cc @@ -53,7 +53,7 @@ static jobject DexCache_getDexNative(JNIEnv* env, jobject javaDexCache) { static jobject DexCache_getResolvedType(JNIEnv* env, jobject javaDexCache, jint type_index) { ScopedFastNativeObjectAccess soa(env); ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache); - CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes()); + CHECK_LT(static_cast<size_t>(type_index), dex_cache->GetDexFile()->NumTypeIds()); return soa.AddLocalReference<jobject>(dex_cache->GetResolvedType(dex::TypeIndex(type_index))); } diff --git a/runtime/oat.h b/runtime/oat.h index 656b86812e..1544121ed1 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '1', '1', '3', '\0' }; // Invoke info change. + static constexpr uint8_t kOatVersion[] = { '1', '1', '4', '\0' }; // hash-based DexCache types. static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index 7ca233fb10..a8a0ded7dc 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -41,6 +41,7 @@ #include "class_table-inl.h" #include "class_linker.h" #include "common_throws.h" +#include "dex_file_annotations.h" #include "events-inl.h" #include "gc/heap.h" #include "gc_root.h" @@ -50,6 +51,7 @@ #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" +#include "mirror/object_array-inl.h" #include "mirror/object_reference.h" #include "mirror/object-inl.h" #include "mirror/reference.h" @@ -685,9 +687,30 @@ jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env, *signature_ptr = reinterpret_cast<char*>(tmp); } - // TODO: Support generic signature. if (generic_ptr != nullptr) { *generic_ptr = nullptr; + if (!klass->IsProxyClass() && klass->GetDexCache() != nullptr) { + art::StackHandleScope<1> hs(soa.Self()); + art::Handle<art::mirror::Class> h_klass = hs.NewHandle(klass); + art::mirror::ObjectArray<art::mirror::String>* str_array = + art::annotations::GetSignatureAnnotationForClass(h_klass); + if (str_array != nullptr) { + std::ostringstream oss; + for (int32_t i = 0; i != str_array->GetLength(); ++i) { + oss << str_array->Get(i)->ToModifiedUtf8(); + } + std::string output_string = oss.str(); + unsigned char* tmp; + jvmtiError ret = CopyString(env, output_string.c_str(), &tmp); + if (ret != ERR(NONE)) { + return ret; + } + *generic_ptr = reinterpret_cast<char*>(tmp); + } else if (soa.Self()->IsExceptionPending()) { + // TODO: Should we report an error here? + soa.Self()->ClearException(); + } + } } // Everything is fine, release the buffers. diff --git a/runtime/openjdkjvmti/ti_field.cc b/runtime/openjdkjvmti/ti_field.cc index a762830d85..131e6c3632 100644 --- a/runtime/openjdkjvmti/ti_field.cc +++ b/runtime/openjdkjvmti/ti_field.cc @@ -34,7 +34,9 @@ #include "art_jvmti.h" #include "art_field-inl.h" #include "base/enums.h" +#include "dex_file_annotations.h" #include "jni_internal.h" +#include "mirror/object_array-inl.h" #include "modifiers.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" @@ -91,6 +93,26 @@ jvmtiError FieldUtil::GetFieldName(jvmtiEnv* env, // TODO: Support generic signature. if (generic_ptr != nullptr) { *generic_ptr = nullptr; + if (!art_field->GetDeclaringClass()->IsProxyClass()) { + art::mirror::ObjectArray<art::mirror::String>* str_array = + art::annotations::GetSignatureAnnotationForField(art_field); + if (str_array != nullptr) { + std::ostringstream oss; + for (int32_t i = 0; i != str_array->GetLength(); ++i) { + oss << str_array->Get(i)->ToModifiedUtf8(); + } + std::string output_string = oss.str(); + unsigned char* tmp; + jvmtiError ret = CopyString(env, output_string.c_str(), &tmp); + if (ret != ERR(NONE)) { + return ret; + } + *generic_ptr = reinterpret_cast<char*>(tmp); + } else if (soa.Self()->IsExceptionPending()) { + // TODO: Should we report an error here? + soa.Self()->ClearException(); + } + } } // Everything is fine, release the buffers. diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc index 2ddd64a2bc..a6cfcc12bc 100644 --- a/runtime/openjdkjvmti/ti_method.cc +++ b/runtime/openjdkjvmti/ti_method.cc @@ -34,7 +34,9 @@ #include "art_jvmti.h" #include "art_method-inl.h" #include "base/enums.h" +#include "dex_file_annotations.h" #include "jni_internal.h" +#include "mirror/object_array-inl.h" #include "modifiers.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" @@ -139,6 +141,26 @@ jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env, // TODO: Support generic signature. if (generic_ptr != nullptr) { *generic_ptr = nullptr; + if (!art_method->GetDeclaringClass()->IsProxyClass()) { + art::mirror::ObjectArray<art::mirror::String>* str_array = + art::annotations::GetSignatureAnnotationForMethod(art_method); + if (str_array != nullptr) { + std::ostringstream oss; + for (int32_t i = 0; i != str_array->GetLength(); ++i) { + oss << str_array->Get(i)->ToModifiedUtf8(); + } + std::string output_string = oss.str(); + unsigned char* tmp; + jvmtiError ret = CopyString(env, output_string.c_str(), &tmp); + if (ret != ERR(NONE)) { + return ret; + } + *generic_ptr = reinterpret_cast<char*>(tmp); + } else if (soa.Self()->IsExceptionPending()) { + // TODO: Should we report an error here? + soa.Self()->ClearException(); + } + } } // Everything is fine, release the buffers. diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index f01acc11aa..8436045072 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -747,8 +747,6 @@ bool Redefiner::ClassRedefinition::CheckClass() { } } } - LOG(WARNING) << "No verification is done on annotations of redefined classes."; - return true; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index f8f3d766c0..69dcfebcb1 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -101,7 +101,6 @@ #include "mirror/throwable.h" #include "monitor.h" #include "native/dalvik_system_DexFile.h" -#include "native/dalvik_system_InMemoryDexClassLoader_DexData.h" #include "native/dalvik_system_VMDebug.h" #include "native/dalvik_system_VMRuntime.h" #include "native/dalvik_system_VMStack.h" @@ -1534,7 +1533,6 @@ jobject Runtime::GetSystemClassLoader() const { void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) { register_dalvik_system_DexFile(env); - register_dalvik_system_InMemoryDexClassLoader_DexData(env); register_dalvik_system_VMDebug(env); register_dalvik_system_VMRuntime(env); register_dalvik_system_VMStack(env); diff --git a/runtime/safe_map.h b/runtime/safe_map.h index 49f80f31a8..e638fdb504 100644 --- a/runtime/safe_map.h +++ b/runtime/safe_map.h @@ -137,6 +137,16 @@ class SafeMap { return it->second; } + iterator FindOrAdd(const K& k, const V& v) { + iterator it = find(k); + return it == end() ? Put(k, v) : it; + } + + iterator FindOrAdd(const K& k) { + iterator it = find(k); + return it == end() ? Put(k, V()) : it; + } + bool Equals(const Self& rhs) const { return map_ == rhs.map_; } diff --git a/runtime/thread.cc b/runtime/thread.cc index 3abb9fca7b..ff66cc1697 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -2549,11 +2549,18 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( return nullptr; } const char* source_file = method->GetDeclaringClassSourceFile(); - if (source_file != nullptr) { - source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); - if (source_name_object == nullptr) { - soa.Self()->AssertPendingOOMException(); - return nullptr; + if (line_number == -1) { + // Make the line_number field of StackTraceElement hold the dex pc. + // source_name_object is intentionally left null if we failed to map the dex pc to + // a line number (most probably because there is no debug info). See b/30183883. + line_number = dex_pc; + } else { + if (source_file != nullptr) { + source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); + if (source_name_object == nullptr) { + soa.Self()->AssertPendingOOMException(); + return nullptr; + } } } } @@ -2564,11 +2571,11 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( if (method_name_object == nullptr) { return nullptr; } - ObjPtr<mirror::StackTraceElement> obj =mirror::StackTraceElement::Alloc(soa.Self(), - class_name_object, - method_name_object, - source_name_object, - line_number); + ObjPtr<mirror::StackTraceElement> obj = mirror::StackTraceElement::Alloc(soa.Self(), + class_name_object, + method_name_object, + source_name_object, + line_number); if (obj == nullptr) { return nullptr; } diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h index 98658215f7..f9a1405354 100644 --- a/runtime/utils/dex_cache_arrays_layout-inl.h +++ b/runtime/utils/dex_cache_arrays_layout-inl.h @@ -51,9 +51,11 @@ inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, cons : DexCacheArraysLayout(pointer_size, dex_file->GetHeader(), dex_file->NumCallSiteIds()) { } -inline constexpr size_t DexCacheArraysLayout::Alignment() { - // GcRoot<> alignment is 4, i.e. lower than or equal to the pointer alignment. - static_assert(alignof(GcRoot<mirror::Class>) == 4, "Expecting alignof(GcRoot<>) == 4"); +constexpr size_t DexCacheArraysLayout::Alignment() { + // mirror::Type/String/MethodTypeDexCacheType alignment is 8, + // i.e. higher than or equal to the pointer alignment. + static_assert(alignof(mirror::TypeDexCacheType) == 8, + "Expecting alignof(ClassDexCacheType) == 8"); static_assert(alignof(mirror::StringDexCacheType) == 8, "Expecting alignof(StringDexCacheType) == 8"); static_assert(alignof(mirror::MethodTypeDexCacheType) == 8, @@ -63,17 +65,22 @@ inline constexpr size_t DexCacheArraysLayout::Alignment() { } template <typename T> -static constexpr PointerSize GcRootAsPointerSize() { +constexpr PointerSize GcRootAsPointerSize() { static_assert(sizeof(GcRoot<T>) == 4U, "Unexpected GcRoot size"); return PointerSize::k32; } inline size_t DexCacheArraysLayout::TypeOffset(dex::TypeIndex type_idx) const { - return types_offset_ + ElementOffset(GcRootAsPointerSize<mirror::Class>(), type_idx.index_); + return types_offset_ + ElementOffset(PointerSize::k64, + type_idx.index_ % mirror::DexCache::kDexCacheTypeCacheSize); } inline size_t DexCacheArraysLayout::TypesSize(size_t num_elements) const { - return ArraySize(GcRootAsPointerSize<mirror::Class>(), num_elements); + size_t cache_size = mirror::DexCache::kDexCacheTypeCacheSize; + if (num_elements < cache_size) { + cache_size = num_elements; + } + return ArraySize(PointerSize::k64, cache_size); } inline size_t DexCacheArraysLayout::TypesAlignment() const { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 16739fa3bc..38d151bc67 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2399,7 +2399,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& res_type = ResolveClassAndCheckAccess(type_idx); if (res_type.IsConflict()) { // If this is a primitive type, fail HARD. - mirror::Class* klass = dex_cache_->GetResolvedType(type_idx); + ObjPtr<mirror::Class> klass = + ClassLinker::LookupResolvedType(type_idx, dex_cache_.Get(), class_loader_.Get()); if (klass != nullptr && klass->IsPrimitive()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "using primitive type " << dex_file_->StringByTypeIdx(type_idx) << " in instanceof in " @@ -3722,9 +3723,16 @@ inline bool MethodVerifier::IsInstantiableOrPrimitive(mirror::Class* klass) { } const RegType& MethodVerifier::ResolveClassAndCheckAccess(dex::TypeIndex class_idx) { - mirror::Class* klass = dex_cache_->GetResolvedType(class_idx); + mirror::Class* klass = can_load_classes_ + ? Runtime::Current()->GetClassLinker()->ResolveType( + *dex_file_, class_idx, dex_cache_, class_loader_) + : ClassLinker::LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()).Ptr(); + if (can_load_classes_ && klass == nullptr) { + DCHECK(self_->IsExceptionPending()); + self_->ClearException(); + } const RegType* result = nullptr; - if (klass != nullptr) { + if (klass != nullptr && !klass->IsErroneous()) { bool precise = klass->CannotBeAssignedFromOtherTypes(); if (precise && !IsInstantiableOrPrimitive(klass)) { const char* descriptor = dex_file_->StringByTypeIdx(class_idx); @@ -3747,10 +3755,6 @@ const RegType& MethodVerifier::ResolveClassAndCheckAccess(dex::TypeIndex class_i << "' in " << GetDeclaringClass(); return *result; } - if (klass == nullptr && !result->IsUnresolvedTypes()) { - klass = result->GetClass(); - dex_cache_->SetResolvedType(class_idx, klass); - } // Record result of class resolution attempt. VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass); diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java index 5a43a4f23f..0dd82abf6f 100644 --- a/test/021-string2/src/Main.java +++ b/test/021-string2/src/Main.java @@ -16,6 +16,7 @@ import junit.framework.Assert; import java.lang.reflect.Method; +import java.util.Locale; /** * more string tests @@ -120,6 +121,12 @@ public class Main { testEqualsConstString(); testConstStringEquals(); + + // Regression tests for String.setCharAt() breaking string compression invariants. + Locale en_US = new Locale("en", "US"); + Assert.assertEquals("I", /* Small latin dotless i */ "\u0131".toUpperCase()); + Assert.assertEquals("abc", "a\u0131c".replace('\u0131', 'b')); + Assert.assertEquals("a\u0131c", "abc".replace('b', '\u0131')); } public static void testCompareToAndEquals() { diff --git a/test/155-java-set-resolved-type/src/Main.java b/test/155-java-set-resolved-type/src/Main.java index f92363e915..56b8c3ece9 100644 --- a/test/155-java-set-resolved-type/src/Main.java +++ b/test/155-java-set-resolved-type/src/Main.java @@ -55,11 +55,7 @@ public class Main { Class<?> timpl = Class.forName("TestImplementation", false, mainLoader); // Clear the dex cache resolved types to force a proper lookup the next time // we need to find TestInterface. - // TODO: Enable clearing the dex cache when we switch to the hash-based type array - // and do a proper lookup. Currently, ClassLinker fully relies on the DexCache. - if (false) { - clearResolvedTypes(timpl); - } + clearResolvedTypes(timpl); // Force intialization of TestClass2. This expects the interface type to be // resolved and found through simple lookup. diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java index a4561b83da..e9673987da 100644 --- a/test/551-checker-shifter-operand/src/Main.java +++ b/test/551-checker-shifter-operand/src/Main.java @@ -76,6 +76,25 @@ public class Main { * the shifter operand. */ + /// CHECK-START-ARM: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm (before) + /// CHECK-DAG: <<l:j\d+>> ParameterValue + /// CHECK-DAG: <<b:b\d+>> ParameterValue + /// CHECK: <<tmp:j\d+>> TypeConversion [<<b>>] + /// CHECK: Sub [<<l>>,<<tmp>>] + + /// CHECK-START-ARM: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm (after) + /// CHECK-DAG: <<l:j\d+>> ParameterValue + /// CHECK-DAG: <<b:b\d+>> ParameterValue + /// CHECK: DataProcWithShifterOp [<<l>>,<<b>>] kind:Sub+SXTB + + /// CHECK-START-ARM: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm (after) + /// CHECK-NOT: TypeConversion + /// CHECK-NOT: Sub + + /// CHECK-START-ARM: long Main.$opt$noinline$translate(long, byte) disassembly (after) + /// CHECK: subs r{{\d+}}, r{{\d+}}, r{{\d+}} + /// CHECK: sbc r{{\d+}}, r{{\d+}}, r{{\d+}}, asr #31 + /// CHECK-START-ARM64: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm64 (before) /// CHECK-DAG: <<l:j\d+>> ParameterValue /// CHECK-DAG: <<b:b\d+>> ParameterValue @@ -85,7 +104,7 @@ public class Main { /// CHECK-START-ARM64: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm64 (after) /// CHECK-DAG: <<l:j\d+>> ParameterValue /// CHECK-DAG: <<b:b\d+>> ParameterValue - /// CHECK: Arm64DataProcWithShifterOp [<<l>>,<<b>>] kind:Sub+SXTB + /// CHECK: DataProcWithShifterOp [<<l>>,<<b>>] kind:Sub+SXTB /// CHECK-START-ARM64: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm64 (after) /// CHECK-NOT: TypeConversion @@ -106,6 +125,21 @@ public class Main { * inputs are the the IR. */ + /// CHECK-START-ARM: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm (before) + /// CHECK: <<a:i\d+>> ParameterValue + /// CHECK: <<Const2:i\d+>> IntConstant 2 + /// CHECK: <<tmp:i\d+>> Shl [<<a>>,<<Const2>>] + /// CHECK: Add [<<tmp>>,<<tmp>>] + + /// CHECK-START-ARM: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm (after) + /// CHECK-DAG: <<a:i\d+>> ParameterValue + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 + /// CHECK: <<Shl:i\d+>> Shl [<<a>>,<<Const2>>] + /// CHECK: Add [<<Shl>>,<<Shl>>] + + /// CHECK-START-ARM: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm (after) + /// CHECK-NOT: DataProcWithShifterOp + /// CHECK-START-ARM64: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm64 (before) /// CHECK: <<a:i\d+>> ParameterValue /// CHECK: <<Const2:i\d+>> IntConstant 2 @@ -119,7 +153,7 @@ public class Main { /// CHECK: Add [<<Shl>>,<<Shl>>] /// CHECK-START-ARM64: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Arm64DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp public static int $opt$noinline$sameInput(int a) { if (doThrow) throw new Error(); @@ -131,6 +165,28 @@ public class Main { * Check that we perform the merge for multiple uses. */ + /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm (before) + /// CHECK: <<arg:i\d+>> ParameterValue + /// CHECK: <<Const23:i\d+>> IntConstant 23 + /// CHECK: <<tmp:i\d+>> Shl [<<arg>>,<<Const23>>] + /// CHECK: Add [<<tmp>>,{{i\d+}}] + /// CHECK: Add [<<tmp>>,{{i\d+}}] + /// CHECK: Add [<<tmp>>,{{i\d+}}] + /// CHECK: Add [<<tmp>>,{{i\d+}}] + /// CHECK: Add [<<tmp>>,{{i\d+}}] + + /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm (after) + /// CHECK: <<arg:i\d+>> ParameterValue + /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 + /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 + /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 + /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 + /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 + + /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm (after) + /// CHECK-NOT: Shl + /// CHECK-NOT: Add + /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm64 (before) /// CHECK: <<arg:i\d+>> ParameterValue /// CHECK: <<Const23:i\d+>> IntConstant 23 @@ -143,11 +199,11 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm64 (after) /// CHECK: <<arg:i\d+>> ParameterValue - /// CHECK: Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 - /// CHECK: Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 - /// CHECK: Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 - /// CHECK: Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 - /// CHECK: Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 + /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 + /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 + /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 + /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 + /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23 /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm64 (after) /// CHECK-NOT: Shl @@ -171,9 +227,19 @@ public class Main { * operand, so test that only the shifts are merged. */ + /// CHECK-START-ARM: void Main.$opt$noinline$testAnd(long, long) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp + + /// CHECK-START-ARM: void Main.$opt$noinline$testAnd(long, long) disassembly (after) + /// CHECK: and lsl + /// CHECK: sbfx + /// CHECK: asr + /// CHECK: and + /// CHECK-START-ARM64: void Main.$opt$noinline$testAnd(long, long) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK-NOT: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$noinline$testAnd(long, long) disassembly (after) /// CHECK: and lsl @@ -186,9 +252,18 @@ public class Main { (a & (b << 5)) | (a & (byte)b)); } + /// CHECK-START-ARM: void Main.$opt$noinline$testOr(int, int) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp + + /// CHECK-START-ARM: void Main.$opt$noinline$testOr(int, int) disassembly (after) + /// CHECK: orr asr + /// CHECK: ubfx + /// CHECK: orr + /// CHECK-START-ARM64: void Main.$opt$noinline$testOr(int, int) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK-NOT: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$noinline$testOr(int, int) disassembly (after) /// CHECK: orr asr @@ -201,9 +276,19 @@ public class Main { (a | (b >> 6)) | (a | (char)b)); } + /// CHECK-START-ARM: void Main.$opt$noinline$testXor(long, long) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp + + /// CHECK-START-ARM: void Main.$opt$noinline$testXor(long, long) disassembly (after) + /// CHECK: eor lsr + /// CHECK: mov + /// CHECK: asr + /// CHECK: eor + /// CHECK-START-ARM64: void Main.$opt$noinline$testXor(long, long) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK-NOT: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$noinline$testXor(long, long) disassembly (after) /// CHECK: eor lsr @@ -216,9 +301,12 @@ public class Main { (a ^ (b >>> 7)) | (a ^ (int)b)); } + /// CHECK-START-ARM: void Main.$opt$noinline$testNeg(int) instruction_simplifier_arm (after) + /// CHECK-NOT: DataProcWithShifterOp + /// CHECK-START-ARM64: void Main.$opt$noinline$testNeg(int) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK-NOT: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$noinline$testNeg(int) disassembly (after) /// CHECK: neg lsl @@ -239,9 +327,12 @@ public class Main { * does occur on the right-hand. */ + /// CHECK-START-ARM: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm (after) + /// CHECK-NOT: DataProcWithShifterOp + /// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK-NOT: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after) /// CHECK-NOT: TypeConversion @@ -252,9 +343,11 @@ public class Main { assertIntEquals(a + $noinline$byteToShort(b), a + (short)b); } + /// CHECK-START-ARM: void Main.$opt$validateExtendByteInt2(int, byte) instruction_simplifier_arm (after) + /// CHECK-NOT: DataProcWithShifterOp + /// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt2(int, byte) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Arm64DataProcWithShifterOp - /// CHECK-NOT: Arm64DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp public static void $opt$validateExtendByteInt2(int a, byte b) { // The conversion to `int` has been optimized away, so there is nothing to merge. @@ -263,13 +356,25 @@ public class Main { assertLongEquals(a + $noinline$byteToLong(b), a + (long)b); } + /// CHECK-START-ARM: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp + + /// CHECK-START-ARM: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm (after) + /// CHECK: TypeConversion + /// CHECK-NOT: TypeConversion + /// CHECK-START-ARM64: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK-NOT: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm64 (after) /// CHECK: TypeConversion @@ -294,9 +399,12 @@ public class Main { $opt$validateExtendByteLong(a, b); } + /// CHECK-START-ARM: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm (after) + /// CHECK-NOT: DataProcWithShifterOp + /// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm64 (after) /// CHECK-NOT: TypeConversion @@ -306,22 +414,41 @@ public class Main { assertIntEquals(a + $noinline$charToShort(b), a + (short)b); } + /// CHECK-START-ARM: void Main.$opt$validateExtendCharInt2(int, char) instruction_simplifier_arm (after) + /// CHECK-NOT: DataProcWithShifterOp + /// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt2(int, char) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Arm64DataProcWithShifterOp - /// CHECK-NOT: Arm64DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp public static void $opt$validateExtendCharInt2(int a, char b) { // The conversion to `int` has been optimized away, so there is nothing to merge. assertIntEquals (a + $noinline$charToInt (b), a + (int)b); - // There is an environment use for `(long)b`, preventing the merge. + // There is an environment use for `(long)b` and the implicit `(long)a`, preventing the merge. assertLongEquals(a + $noinline$charToLong(b), a + (long)b); } + /// CHECK-START-ARM: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp + + /// CHECK-START-ARM: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm (after) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK-NOT: TypeConversion + /// CHECK-START-ARM64: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm64 (after) /// CHECK: TypeConversion @@ -332,7 +459,7 @@ public class Main { // The first two tests have a type conversion. assertLongEquals(a + $noinline$charToByte (b), a + (byte)b); assertLongEquals(a + $noinline$charToShort(b), a + (short)b); - // This test does not because the conversion to `int` is optimized away. + // On ARM64 this test does not because the conversion to `int` is optimized away. assertLongEquals(a + $noinline$charToInt (b), a + (int)b); } @@ -342,9 +469,12 @@ public class Main { $opt$validateExtendCharLong(a, b); } + /// CHECK-START-ARM: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm (after) + /// CHECK-NOT: DataProcWithShifterOp + /// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm64 (after) /// CHECK-NOT: TypeConversion @@ -354,21 +484,41 @@ public class Main { assertIntEquals(a + $noinline$shortToChar (b), a + (char)b); } + /// CHECK-START-ARM: void Main.$opt$validateExtendShortInt2(int, short) instruction_simplifier_arm (after) + /// CHECK-NOT: DataProcWithShifterOp + /// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt2(int, short) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Arm64DataProcWithShifterOp - /// CHECK-NOT: Arm64DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp public static void $opt$validateExtendShortInt2(int a, short b) { // The conversion to `int` has been optimized away, so there is nothing to merge. assertIntEquals (a + $noinline$shortToInt (b), a + (int)b); - // There is an environment use for `(long)b`, preventing the merge. + // There is an environment use for `(long)b` and the implicit `(long)a`, preventing the merge. assertLongEquals(a + $noinline$shortToLong (b), a + (long)b); } + /// CHECK-START-ARM: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp + + /// CHECK-START-ARM: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm (after) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK-NOT: TypeConversion + /// CHECK-START-ARM64: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm64 (after) /// CHECK: TypeConversion @@ -379,7 +529,7 @@ public class Main { // The first two tests have a type conversion. assertLongEquals(a + $noinline$shortToByte(b), a + (byte)b); assertLongEquals(a + $noinline$shortToChar(b), a + (char)b); - // This test does not because the conversion to `int` is optimized away. + // On ARM64 this test does not because the conversion to `int` is optimized away. assertLongEquals(a + $noinline$shortToInt (b), a + (int)b); } @@ -389,11 +539,31 @@ public class Main { $opt$validateExtendShortLong(a, b); } + /// CHECK-START-ARM: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp + + /// CHECK-START-ARM: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm (after) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK-NOT: TypeConversion + /// CHECK-START-ARM64: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm64 (after) /// CHECK: TypeConversion @@ -411,11 +581,34 @@ public class Main { assertLongEquals(a + $noinline$intToLong (b), a + (long)b); } + /// CHECK-START-ARM: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp + + /// CHECK-START-ARM: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm (after) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK-NOT: TypeConversion + /// CHECK-START-ARM64: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm64 (after) /// CHECK: TypeConversion @@ -449,40 +642,83 @@ public class Main { // Each test line below should see one merge. + /// CHECK-START-ARM: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp + // Note: `b << 32`, `b >> 32` and `b >>> 32` are optimized away by generic simplifier. + + /// CHECK-START-ARM: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: Shl + /// CHECK-NOT: Shr + /// CHECK-NOT: UShr + /// CHECK-START-ARM64: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp // Note: `b << 32`, `b >> 32` and `b >>> 32` are optimized away by generic simplifier. /// CHECK-START-ARM64: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm64 (after) @@ -552,43 +788,89 @@ public class Main { } // Each test line below should see one merge. + /// CHECK-START-ARM: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after) + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp + + // On ARM shifts by 1 are not merged. + /// CHECK-START-ARM: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after) + /// CHECK: Shl + /// CHECK-NOT: Shl + /// CHECK: Shr + /// CHECK-NOT: Shr + /// CHECK: UShr + /// CHECK-NOT: UShr + /// CHECK-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after) - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK: DataProcWithShifterOp + /// CHECK-NOT: DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after) /// CHECK-NOT: Shl diff --git a/test/593-checker-shift-and-simplifier/src/Main.java b/test/593-checker-shift-and-simplifier/src/Main.java index 65e809a30e..c9826bc546 100644 --- a/test/593-checker-shift-and-simplifier/src/Main.java +++ b/test/593-checker-shift-and-simplifier/src/Main.java @@ -21,6 +21,17 @@ public class Main { // A very particular set of operations that caused a double removal by the // ARM64 simplifier doing "forward" removals (b/27851582). + /// CHECK-START-ARM: int Main.operations() instruction_simplifier_arm (before) + /// CHECK-DAG: <<Get:i\d+>> ArrayGet + /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>] + /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}] + /// CHECK-DAG: And [<<Not>>,<<Shl>>] + // + /// CHECK-START-ARM: int Main.operations() instruction_simplifier_arm (after) + /// CHECK-DAG: <<Get:i\d+>> ArrayGet + /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>] + /// CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2 + /// CHECK-START-ARM64: int Main.operations() instruction_simplifier_arm64 (before) /// CHECK-DAG: <<Get:i\d+>> ArrayGet /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>] @@ -30,7 +41,7 @@ public class Main { /// CHECK-START-ARM64: int Main.operations() instruction_simplifier_arm64 (after) /// CHECK-DAG: <<Get:i\d+>> ArrayGet /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>] - /// CHECK-DAG: Arm64DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2 + /// CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2 private static int operations() { int r = a[0]; int n = ~r; diff --git a/test/624-checker-stringops/src/Main.java b/test/624-checker-stringops/src/Main.java index 75b782e8c0..63da4f5560 100644 --- a/test/624-checker-stringops/src/Main.java +++ b/test/624-checker-stringops/src/Main.java @@ -232,8 +232,9 @@ public class Main { /// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter static int bufferDeadLoop() { StringBuffer b = new StringBuffer(); + String x = "x"; for (int i = 0; i < 10; i++) { - int d = b.toString().indexOf("x", 1); + int d = b.toString().indexOf(x, 1); } return b.length(); } @@ -252,8 +253,9 @@ public class Main { /// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter static int builderDeadLoop() { StringBuilder b = new StringBuilder(); + String x = "x"; for (int i = 0; i < 10; i++) { - int d = b.toString().indexOf("x", 1); + int d = b.toString().indexOf(x, 1); } return b.length(); } diff --git a/test/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc index b35dff48ee..e1af02edfd 100644 --- a/test/626-const-class-linking/clear_dex_cache_types.cc +++ b/test/626-const-class-linking/clear_dex_cache_types.cc @@ -27,7 +27,8 @@ extern "C" JNIEXPORT void JNICALL Java_Main_nativeClearResolvedTypes(JNIEnv*, jc ScopedObjectAccess soa(Thread::Current()); mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache(); for (size_t i = 0, num_types = dex_cache->NumResolvedTypes(); i != num_types; ++i) { - dex_cache->SetResolvedType(dex::TypeIndex(i), ObjPtr<mirror::Class>(nullptr)); + mirror::TypeDexCachePair cleared(nullptr, mirror::TypeDexCachePair::InvalidIndexForSlot(i)); + dex_cache->GetResolvedTypes()[i].store(cleared, std::memory_order_relaxed); } } diff --git a/test/638-no-line-number/build b/test/638-no-line-number/build new file mode 100644 index 0000000000..7eaf50e938 --- /dev/null +++ b/test/638-no-line-number/build @@ -0,0 +1,25 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +# Stop if something fails. +set -e + +mkdir classes +# Only keep the source name, to make sure we do remove it in the stack trace +# when there is no line number mapping. +${JAVAC} -g:source -source 7 -target 7 -d classes `find src -name '*.java'` +${DX} --dex --output=classes.dex classes +zip $TEST_NAME.jar classes.dex diff --git a/test/638-no-line-number/expected.txt b/test/638-no-line-number/expected.txt new file mode 100644 index 0000000000..ffde15312b --- /dev/null +++ b/test/638-no-line-number/expected.txt @@ -0,0 +1,5 @@ +java.lang.Error + at Main.main(Unknown Source:2) +java.lang.NullPointerException: throw with null exception + at Main.doThrow(Unknown Source:0) + at Main.main(Unknown Source:9) diff --git a/test/638-no-line-number/info.txt b/test/638-no-line-number/info.txt new file mode 100644 index 0000000000..89e6432e66 --- /dev/null +++ b/test/638-no-line-number/info.txt @@ -0,0 +1 @@ +Test for b/30183883, that we emit the dex pc when the line number is missing. diff --git a/test/638-no-line-number/src/Main.java b/test/638-no-line-number/src/Main.java new file mode 100644 index 0000000000..7fe0404204 --- /dev/null +++ b/test/638-no-line-number/src/Main.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) { + try { + doThrow(new Error()); + } catch (Error e) { + e.printStackTrace(); + } + try { + doThrow(null); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + public static void doThrow(Error e) { + throw e; + } +} diff --git a/test/909-attach-agent/attach.cc b/test/909-attach-agent/attach.cc index 2b50eb83b4..adae844ef0 100644 --- a/test/909-attach-agent/attach.cc +++ b/test/909-attach-agent/attach.cc @@ -28,7 +28,7 @@ namespace Test909AttachAgent { jint OnAttach(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) { - printf("Attached Agent for test 909-attach-agent\n"); + fprintf(stderr, "Attached Agent for test 909-attach-agent\n"); fsync(1); jvmtiEnv* env = nullptr; jvmtiEnv* env2 = nullptr; @@ -36,7 +36,7 @@ jint OnAttach(JavaVM* vm, #define CHECK_CALL_SUCCESS(c) \ do { \ if ((c) != JNI_OK) { \ - printf("call " #c " did not succeed\n"); \ + fprintf(stderr, "call " #c " did not succeed\n"); \ return -1; \ } \ } while (false) @@ -44,7 +44,7 @@ jint OnAttach(JavaVM* vm, CHECK_CALL_SUCCESS(vm->GetEnv(reinterpret_cast<void**>(&env), JVMTI_VERSION_1_0)); CHECK_CALL_SUCCESS(vm->GetEnv(reinterpret_cast<void**>(&env2), JVMTI_VERSION_1_0)); if (env == env2) { - printf("GetEnv returned same environment twice!\n"); + fprintf(stderr, "GetEnv returned same environment twice!\n"); return -1; } unsigned char* local_data = nullptr; @@ -54,19 +54,19 @@ jint OnAttach(JavaVM* vm, unsigned char* get_data = nullptr; CHECK_CALL_SUCCESS(env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&get_data))); if (get_data != local_data) { - printf("Got different data from local storage then what was set!\n"); + fprintf(stderr, "Got different data from local storage then what was set!\n"); return -1; } CHECK_CALL_SUCCESS(env2->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&get_data))); if (get_data != nullptr) { - printf("env2 did not have nullptr local storage.\n"); + fprintf(stderr, "env2 did not have nullptr local storage.\n"); return -1; } CHECK_CALL_SUCCESS(env->Deallocate(local_data)); jint version = 0; CHECK_CALL_SUCCESS(env->GetVersionNumber(&version)); if ((version & JVMTI_VERSION_1) != JVMTI_VERSION_1) { - printf("Unexpected version number!\n"); + fprintf(stderr, "Unexpected version number!\n"); return -1; } CHECK_CALL_SUCCESS(env->DisposeEnvironment()); diff --git a/test/910-methods/expected.txt b/test/910-methods/expected.txt index c913b3ffe5..e87929f00c 100644 --- a/test/910-methods/expected.txt +++ b/test/910-methods/expected.txt @@ -28,7 +28,7 @@ Location end: JVMTI_ERROR_NATIVE_METHOD Is native: true Is obsolete: false Is synthetic: false -[add, (Ljava/lang/Object;)Z, null] +[add, (Ljava/lang/Object;)Z, (TE;)Z] interface java.util.List 1025 Max locals: 0 diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt index 328216b324..e932b206c0 100644 --- a/test/912-classes/expected.txt +++ b/test/912-classes/expected.txt @@ -1,10 +1,10 @@ [Ljava/lang/Object;, null] 1 -[Ljava/lang/String;, null] +[Ljava/lang/String;, Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/lang/CharSequence;] 11 [Ljava/lang/Math;, null] 11 -[Ljava/util/List;, null] +[Ljava/util/List;, <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;] 601 [L$Proxy0;, null] 11 diff --git a/test/918-fields/expected.txt b/test/918-fields/expected.txt index 39d3e70550..1a1209c34c 100644 --- a/test/918-fields/expected.txt +++ b/test/918-fields/expected.txt @@ -14,3 +14,7 @@ true interface Main$Bar 25 false +[generics, Ljava/lang/Object;, TT;] +class Main$Generics +0 +false diff --git a/test/918-fields/src/Main.java b/test/918-fields/src/Main.java index 3ba535b31b..ad0d0c5dc2 100644 --- a/test/918-fields/src/Main.java +++ b/test/918-fields/src/Main.java @@ -27,6 +27,7 @@ public class Main { testField(Integer.class, "value"); testField(Foo.class, "this$0"); testField(Bar.class, "VAL"); + testField(Generics.class, "generics"); } private static void testField(Class<?> base, String fieldName) @@ -65,4 +66,8 @@ public class Main { private static interface Bar { public static int VAL = 1; } + + private static class Generics<T> { + T generics; + } } diff --git a/test/948-change-annotations/build b/test/948-change-annotations/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/948-change-annotations/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/948-change-annotations/expected.txt b/test/948-change-annotations/expected.txt new file mode 100644 index 0000000000..7974c7a694 --- /dev/null +++ b/test/948-change-annotations/expected.txt @@ -0,0 +1,21 @@ +Running test class RemoveAnnotationsTest +Type annotations: [@TestClassAnnotation1(value=hello)] +method public void Transform.sayHi() -> [@TestMethodAnnotation1(value=hi hi)] +hello +Goodbye +Type annotations: [] +method public void Transform.sayHi() -> [] +Running test class AddAnnotationsTest +Type annotations: [@TestClassAnnotation1(value=hello)] +method public void Transform.sayHi() -> [@TestMethodAnnotation1(value=hi hi)] +hello +Goodbye +Type annotations: [@TestClassAnnotation1(value=hello), @TestClassAnnotation2(value=hello2)] +method public void Transform.sayHi() -> [@TestMethodAnnotation1(value=hi hi), @TestMethodAnnotation2(value=hi hi2)] +Running test class ChangeAnnotationValues +Type annotations: [@TestClassAnnotation1(value=hello)] +method public void Transform.sayHi() -> [@TestMethodAnnotation1(value=hi hi)] +hello +Goodbye +Type annotations: [@TestClassAnnotation1(value=Goodbye)] +method public void Transform.sayHi() -> [@TestMethodAnnotation1(value=Bye Bye)] diff --git a/test/948-change-annotations/info.txt b/test/948-change-annotations/info.txt new file mode 100644 index 0000000000..875a5f6ec1 --- /dev/null +++ b/test/948-change-annotations/info.txt @@ -0,0 +1 @@ +Tests basic functions in the jvmti plugin. diff --git a/test/948-change-annotations/run b/test/948-change-annotations/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/948-change-annotations/run @@ -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-run "$@" --jvmti diff --git a/test/948-change-annotations/src/AddAnnotationsTest.java b/test/948-change-annotations/src/AddAnnotationsTest.java new file mode 100644 index 0000000000..6876e8736e --- /dev/null +++ b/test/948-change-annotations/src/AddAnnotationsTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 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 AddAnnotationsTest implements TestCase { + /** + * base64 encoded class/dex file for + * @TestClassAnnotation1("hello") + * @TestClassAnnotation2("hello2") + * class Transform { + * @TestMethodAnnotation1("hi hi") + * @TestMethodAnnotation2("hi hi2") + * public void sayHi() { + * System.out.println("Goodbye"); + * } + * } + */ + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAKQoABgAbCQAcAB0IAB4KAB8AIAcAIQcAIgEABjxpbml0PgEAAygpVgEABENvZGUB" + + "AA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQALTFRyYW5zZm9y" + + "bTsBAAVzYXlIaQEAGVJ1bnRpbWVWaXNpYmxlQW5ub3RhdGlvbnMBABdMVGVzdE1ldGhvZEFubm90" + + "YXRpb24xOwEABXZhbHVlAQAFaGkgaGkBABdMVGVzdE1ldGhvZEFubm90YXRpb24yOwEABmhpIGhp" + + "MgEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQEAFkxUZXN0Q2xhc3NBbm5vdGF0aW9uMTsB" + + "AAVoZWxsbwEAFkxUZXN0Q2xhc3NBbm5vdGF0aW9uMjsBAAZoZWxsbzIMAAcACAcAIwwAJAAlAQAH" + + "R29vZGJ5ZQcAJgwAJwAoAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFu" + + "Zy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3Ry" + + "ZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAgAAUABgAAAAAAAgAAAAcACAAB" + + "AAkAAAAvAAEAAQAAAAUqtwABsQAAAAIACgAAAAYAAQAAABMACwAAAAwAAQAAAAUADAANAAAAAQAO" + + "AAgAAgAJAAAANwACAAEAAAAJsgACEgO2AASxAAAAAgAKAAAACgACAAAAFwAIABgACwAAAAwAAQAA" + + "AAkADAANAAAADwAAABQAAgAQAAEAEXMAEgATAAEAEXMAFAACABUAAAACABYADwAAABQAAgAXAAEA" + + "EXMAGAAZAAEAEXMAGg=="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQA7mPKPjUKe43s+OLHHgFVRVCAPn/rRz9z0AwAAcAAAAHhWNBIAAAAAAAAAADADAAAX" + + "AAAAcAAAAAoAAADMAAAAAgAAAPQAAAABAAAADAEAAAQAAAAUAQAAAQAAADQBAACgAgAAVAEAAMYB" + + "AADOAQAA1wEAAO8BAAAHAgAAIAIAADkCAABGAgAAXQIAAHECAACFAgAAmQIAAKkCAACsAgAAsAIA" + + "AMQCAADLAgAA0wIAANoCAADiAgAA5wIAAPACAAD3AgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAA" + + "CAAAAAkAAAAKAAAADAAAAAwAAAAJAAAAAAAAAA0AAAAJAAAAwAEAAAgABQATAAAABAAAAAAAAAAE" + + "AAAAFQAAAAUAAQAUAAAABgAAAAAAAAAEAAAAAAAAAAYAAAAAAAAACwAAAKgBAAAhAwAAAAAAAAIA" + + "AAAJAwAADwMAAAIAAAAVAwAAGwMAAAEAAQABAAAA/gIAAAQAAABwEAMAAAAOAAMAAQACAAAAAwMA" + + "AAkAAABiAAAAGwEBAAAAbiACABAADgAAAFQBAAAAAAAAAQAAAAAAAAABAAAAYAEAAAEAAAAHAAY8" + + "aW5pdD4AB0dvb2RieWUAFkxUZXN0Q2xhc3NBbm5vdGF0aW9uMTsAFkxUZXN0Q2xhc3NBbm5vdGF0" + + "aW9uMjsAF0xUZXN0TWV0aG9kQW5ub3RhdGlvbjE7ABdMVGVzdE1ldGhvZEFubm90YXRpb24yOwAL" + + "TFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJM" + + "amF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYA" + + "AlZMABJlbWl0dGVyOiBqYWNrLTQuMjUABWhlbGxvAAZoZWxsbzIABWhpIGhpAAZoaSBoaTIAA291" + + "dAAHcHJpbnRsbgAFc2F5SGkABXZhbHVlABMABw4AFwAHDocAAQABFhcPAQEBFhcQAQIBFhcRAQMB" + + "FhcSAAABAQCAgATsAgEBhAMAEAAAAAAAAAABAAAAAAAAAAEAAAAXAAAAcAAAAAIAAAAKAAAAzAAA" + + "AAMAAAACAAAA9AAAAAQAAAABAAAADAEAAAUAAAAEAAAAFAEAAAYAAAABAAAANAEAAAMQAAACAAAA" + + "VAEAAAEgAAACAAAAbAEAAAYgAAABAAAAqAEAAAEQAAABAAAAwAEAAAIgAAAXAAAAxgEAAAMgAAAC" + + "AAAA/gIAAAQgAAAEAAAACQMAAAAgAAABAAAAIQMAAAAQAAABAAAAMAMAAA=="); + + public void runTest(Transform t) { + t.sayHi(); + Main.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES); + t.sayHi(); + } +} diff --git a/test/948-change-annotations/src/ChangeAnnotationValues.java b/test/948-change-annotations/src/ChangeAnnotationValues.java new file mode 100644 index 0000000000..89a766cdeb --- /dev/null +++ b/test/948-change-annotations/src/ChangeAnnotationValues.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 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 ChangeAnnotationValues implements TestCase { + /** + * base64 encoded class/dex file for + * @TestClassAnnotation1("Goodbye") + * class Transform { + * @TestMethodAnnotation1("Bye Bye") + * public void sayHi() { + * System.out.println("Goodbye"); + * } + * } + */ + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAJAoABgAXCQAYABkIABYKABoAGwcAHAcAHQEABjxpbml0PgEAAygpVgEABENvZGUB" + + "AA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQALTFRyYW5zZm9y" + + "bTsBAAVzYXlIaQEAGVJ1bnRpbWVWaXNpYmxlQW5ub3RhdGlvbnMBABdMVGVzdE1ldGhvZEFubm90" + + "YXRpb24xOwEABXZhbHVlAQAHQnllIEJ5ZQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQEA" + + "FkxUZXN0Q2xhc3NBbm5vdGF0aW9uMTsBAAdHb29kYnllDAAHAAgHAB4MAB8AIAcAIQwAIgAjAQAJ" + + "VHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVM" + + "amF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShM" + + "amF2YS9sYW5nL1N0cmluZzspVgAgAAUABgAAAAAAAgAAAAcACAABAAkAAAAvAAEAAQAAAAUqtwAB" + + "sQAAAAIACgAAAAYAAQAAAAIACwAAAAwAAQAAAAUADAANAAAAAQAOAAgAAgAJAAAANwACAAEAAAAJ" + + "sgACEgO2AASxAAAAAgAKAAAACgACAAAABQAIAAYACwAAAAwAAQAAAAkADAANAAAADwAAAAsAAQAQ" + + "AAEAEXMAEgACABMAAAACABQADwAAAAsAAQAVAAEAEXMAFg=="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQAXfYs9FUE830lxfnB+X66S7iZiP5A7uDSAAwAAcAAAAHhWNBIAAAAAAAAAALwCAAAS" + + "AAAAcAAAAAgAAAC4AAAAAgAAANgAAAABAAAA8AAAAAQAAAD4AAAAAQAAABgBAABIAgAAOAEAAKIB" + + "AACqAQAAswEAALwBAADUAQAA7QEAAPoBAAARAgAAJQIAADkCAABNAgAAXQIAAGACAABkAgAAeAIA" + + "AH0CAACGAgAAjQIAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAALAAAACwAAAAcAAAAAAAAA" + + "DAAAAAcAAACcAQAABgADAA4AAAACAAAAAAAAAAIAAAAQAAAAAwABAA8AAAAEAAAAAAAAAAIAAAAA" + + "AAAABAAAAAAAAAAKAAAAhAEAAKsCAAAAAAAAAQAAAJ8CAAABAAAApQIAAAEAAQABAAAAlAIAAAQA" + + "AABwEAMAAAAOAAMAAQACAAAAmQIAAAkAAABiAAAAGwECAAAAbiACABAADgAAADgBAAAAAAAAAQAA" + + "AAAAAAABAAAAQAEAAAEAAAAFAAY8aW5pdD4AB0J5ZSBCeWUAB0dvb2RieWUAFkxUZXN0Q2xhc3NB" + + "bm5vdGF0aW9uMTsAF0xUZXN0TWV0aG9kQW5ub3RhdGlvbjE7AAtMVHJhbnNmb3JtOwAVTGphdmEv" + + "aW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAS" + + "TGphdmEvbGFuZy9TeXN0ZW07AA5UcmFuc2Zvcm0uamF2YQABVgACVkwAEmVtaXR0ZXI6IGphY2st" + + "NC4yNQADb3V0AAdwcmludGxuAAVzYXlIaQAFdmFsdWUAAgAHDgAFAAcOhwABAAERFwIBAQERFwEA" + + "AAEBAICABMgCAQHgAgAAABAAAAAAAAAAAQAAAAAAAAABAAAAEgAAAHAAAAACAAAACAAAALgAAAAD" + + "AAAAAgAAANgAAAAEAAAAAQAAAPAAAAAFAAAABAAAAPgAAAAGAAAAAQAAABgBAAADEAAAAgAAADgB" + + "AAABIAAAAgAAAEgBAAAGIAAAAQAAAIQBAAABEAAAAQAAAJwBAAACIAAAEgAAAKIBAAADIAAAAgAA" + + "AJQCAAAEIAAAAgAAAJ8CAAAAIAAAAQAAAKsCAAAAEAAAAQAAALwCAAA="); + + public void runTest(Transform t) { + t.sayHi(); + Main.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES); + t.sayHi(); + } +} diff --git a/test/948-change-annotations/src/Main.java b/test/948-change-annotations/src/Main.java new file mode 100644 index 0000000000..30bfbf9fc6 --- /dev/null +++ b/test/948-change-annotations/src/Main.java @@ -0,0 +1,90 @@ +/* + * 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.Arrays; +import java.util.Base64; +import java.util.Comparator; +import java.lang.reflect.*; +import java.lang.annotation.*; +public class Main { + + /** + * base64 encoded class/dex file for for initial Transform.java + */ + private static final byte[] INITIAL_CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAJAoABgAXCQAYABkIABYKABoAGwcAHAcAHQEABjxpbml0PgEAAygpVgEABENvZGUB" + + "AA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQALTFRyYW5zZm9y" + + "bTsBAAVzYXlIaQEAGVJ1bnRpbWVWaXNpYmxlQW5ub3RhdGlvbnMBABdMVGVzdE1ldGhvZEFubm90" + + "YXRpb24xOwEABXZhbHVlAQAFaGkgaGkBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEBABZM" + + "VGVzdENsYXNzQW5ub3RhdGlvbjE7AQAFaGVsbG8MAAcACAcAHgwAHwAgBwAhDAAiACMBAAlUcmFu" + + "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" + + "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" + + "L2xhbmcvU3RyaW5nOylWACAABQAGAAAAAAACAAAABwAIAAEACQAAAC8AAQABAAAABSq3AAGxAAAA" + + "AgAKAAAABgABAAAAEgALAAAADAABAAAABQAMAA0AAAABAA4ACAACAAkAAAA3AAIAAQAAAAmyAAIS" + + "A7YABLEAAAACAAoAAAAKAAIAAAAVAAgAFgALAAAADAABAAAACQAMAA0AAAAPAAAACwABABAAAQAR" + + "cwASAAIAEwAAAAIAFAAPAAAACwABABUAAQARcwAW"); + private static final byte[] INITIAL_DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQCufKz9atC18kWgSsEfRq699UEcX4cHonN8AwAAcAAAAHhWNBIAAAAAAAAAALgCAAAS" + + "AAAAcAAAAAgAAAC4AAAAAgAAANgAAAABAAAA8AAAAAQAAAD4AAAAAQAAABgBAABEAgAAOAEAAKIB" + + "AACqAQAAwgEAANsBAADoAQAA/wEAABMCAAAnAgAAOwIAAEsCAABOAgAAUgIAAGYCAABtAgAAdAIA" + + "AHkCAACCAgAAiQIAAAEAAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAcAAAAAAAAA" + + "CgAAAAcAAACcAQAABgADAA4AAAACAAAAAAAAAAIAAAAQAAAAAwABAA8AAAAEAAAAAAAAAAIAAAAA" + + "AAAABAAAAAAAAAAIAAAAhAEAAKcCAAAAAAAAAQAAAJsCAAABAAAAoQIAAAEAAQABAAAAkAIAAAQA" + + "AABwEAMAAAAOAAMAAQACAAAAlQIAAAkAAABiAAAAGwEMAAAAbiACABAADgAAADgBAAAAAAAAAQAA" + + "AAAAAAABAAAAQAEAAAEAAAAFAAY8aW5pdD4AFkxUZXN0Q2xhc3NBbm5vdGF0aW9uMTsAF0xUZXN0" + + "TWV0aG9kQW5ub3RhdGlvbjE7AAtMVHJhbnNmb3JtOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJM" + + "amF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07" + + "AA5UcmFuc2Zvcm0uamF2YQABVgACVkwAEmVtaXR0ZXI6IGphY2stNC4yNQAFaGVsbG8ABWhpIGhp" + + "AANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQASAAcOABUABw6HAAEAAREXDAEBAREXDQAAAQEA" + + "gIAEyAIBAeACAAAAEAAAAAAAAAABAAAAAAAAAAEAAAASAAAAcAAAAAIAAAAIAAAAuAAAAAMAAAAC" + + "AAAA2AAAAAQAAAABAAAA8AAAAAUAAAAEAAAA+AAAAAYAAAABAAAAGAEAAAMQAAACAAAAOAEAAAEg" + + "AAACAAAASAEAAAYgAAABAAAAhAEAAAEQAAABAAAAnAEAAAIgAAASAAAAogEAAAMgAAACAAAAkAIA" + + "AAQgAAACAAAAmwIAAAAgAAABAAAApwIAAAAQAAABAAAAuAIAAA=="); + + public static void main(String[] args) { + doTest(new RemoveAnnotationsTest()); + doTest(new AddAnnotationsTest()); + doTest(new ChangeAnnotationValues()); + } + + private static Annotation[] sortedAnno(Annotation[] annos) { + Arrays.sort(annos, Comparator.comparing((v) -> v.toString())); + return annos; + } + + public static void doTest(TestCase t) { + // Get back to normal first. + doCommonClassRedefinition(Transform.class, INITIAL_CLASS_BYTES, INITIAL_DEX_BYTES); + System.out.println("Running test " + t.getClass()); + printAnnotations(Transform.class); + t.runTest(new Transform()); + printAnnotations(Transform.class); + } + + private static void printAnnotations(Class<?> transform) { + System.out.println("Type annotations: " + + Arrays.toString(sortedAnno(transform.getAnnotations()))); + for (Method m : transform.getDeclaredMethods()) { + System.out.println("method " + m + " -> " + + Arrays.toString(sortedAnno(m.getDeclaredAnnotations()))); + } + } + + // Transforms the class + public static native void doCommonClassRedefinition(Class<?> target, + byte[] class_file, + byte[] dex_file); +} diff --git a/test/948-change-annotations/src/RemoveAnnotationsTest.java b/test/948-change-annotations/src/RemoveAnnotationsTest.java new file mode 100644 index 0000000000..3b1725a67e --- /dev/null +++ b/test/948-change-annotations/src/RemoveAnnotationsTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 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 RemoveAnnotationsTest implements TestCase { + /** + * base64 encoded class/dex file for + * class Transform { + * public void sayHi() { + * System.out.println("Goodbye"); + * } + * } + */ + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" + + "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" + + "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" + + "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" + + "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" + + "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" + + "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" + + "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" + + "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" + + "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" + + "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" + + "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" + + "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" + + "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" + + "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA="); + + public void runTest(Transform t) { + t.sayHi(); + Main.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES); + t.sayHi(); + } +} diff --git a/test/948-change-annotations/src/TestCase.java b/test/948-change-annotations/src/TestCase.java new file mode 100644 index 0000000000..9edc01e4f9 --- /dev/null +++ b/test/948-change-annotations/src/TestCase.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 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 interface TestCase { + public void runTest(Transform t); +} diff --git a/test/948-change-annotations/src/TestClassAnnotation1.java b/test/948-change-annotations/src/TestClassAnnotation1.java new file mode 100644 index 0000000000..adef98f5d4 --- /dev/null +++ b/test/948-change-annotations/src/TestClassAnnotation1.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 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.annotation.*; +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @ interface TestClassAnnotation1 { + public String value(); +} diff --git a/test/948-change-annotations/src/TestClassAnnotation2.java b/test/948-change-annotations/src/TestClassAnnotation2.java new file mode 100644 index 0000000000..67e6260459 --- /dev/null +++ b/test/948-change-annotations/src/TestClassAnnotation2.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 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.annotation.*; +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @ interface TestClassAnnotation2 { + public String value(); +} diff --git a/test/948-change-annotations/src/TestMethodAnnotation1.java b/test/948-change-annotations/src/TestMethodAnnotation1.java new file mode 100644 index 0000000000..d3920f3976 --- /dev/null +++ b/test/948-change-annotations/src/TestMethodAnnotation1.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 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.annotation.*; +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @ interface TestMethodAnnotation1 { + public String value(); +} diff --git a/test/948-change-annotations/src/TestMethodAnnotation2.java b/test/948-change-annotations/src/TestMethodAnnotation2.java new file mode 100644 index 0000000000..2d5bb728a4 --- /dev/null +++ b/test/948-change-annotations/src/TestMethodAnnotation2.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 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.annotation.*; +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @ interface TestMethodAnnotation2 { + public String value(); +} diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h b/test/948-change-annotations/src/Transform.java index f73d07a618..1c6a145da4 100644 --- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h +++ b/test/948-change-annotations/src/Transform.java @@ -14,15 +14,10 @@ * limitations under the License. */ -#ifndef ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_ -#define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_ - -#include <jni.h> - -namespace art { - -void register_dalvik_system_InMemoryDexClassLoader_DexData(JNIEnv* env); - -} // namespace art - -#endif // ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_ +@TestClassAnnotation1("hello") +class Transform { + @TestMethodAnnotation1("hi hi") + public void sayHi() { + System.out.println("hello"); + } +} diff --git a/test/949-in-memory-transform/expected.txt b/test/949-in-memory-transform/expected.txt new file mode 100644 index 0000000000..4774b81b49 --- /dev/null +++ b/test/949-in-memory-transform/expected.txt @@ -0,0 +1,2 @@ +hello +Goodbye diff --git a/test/949-in-memory-transform/info.txt b/test/949-in-memory-transform/info.txt new file mode 100644 index 0000000000..7753729dc7 --- /dev/null +++ b/test/949-in-memory-transform/info.txt @@ -0,0 +1,4 @@ +Tests basic functions in the jvmti plugin. + +Tests that transformation works even when the class being transformed comes from +an in-memory dex file (i.e. loaded from an InMemoryDexClassLoader). diff --git a/test/949-in-memory-transform/run b/test/949-in-memory-transform/run new file mode 100755 index 0000000000..e92b873956 --- /dev/null +++ b/test/949-in-memory-transform/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2017 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-run "$@" --jvmti diff --git a/test/949-in-memory-transform/src/Main.java b/test/949-in-memory-transform/src/Main.java new file mode 100644 index 0000000000..2ffabf5424 --- /dev/null +++ b/test/949-in-memory-transform/src/Main.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2017 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; +import java.lang.reflect.*; +import java.nio.ByteBuffer; + +public class Main { + /** + * base64 encoded class/dex file for + * public class Transform { + * public void sayHi() { + * System.out.println("hello"); + * } + * } + */ + private static final byte[] INITIAL_CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" + + "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" + + "BwAIBwAWDAAXABgBAAVoZWxsbwcAGQwAGgAbAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVj" + + "dAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZh" + + "L2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAA" + + "AAAAAgABAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAABEAAQALAAgAAQAJ" + + "AAAAJQACAAEAAAAJsgACEgO2AASxAAAAAQAKAAAACgACAAAAGgAIABsAAQAMAAAAAgAN"); + private static final byte[] INITIAL_DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQAJX3mZphwHJCT1qdTz/GS+jXOR+O/9e3fMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" + + "AABqAQAAdwEAAI4BAACiAQAAtgEAAMoBAADaAQAA3QEAAOEBAAD1AQAA/AEAAAECAAAKAgAAAQAA" + + "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAABAAAAAgAAAAAAAAAGAAAAAAAAABwCAAAA" + + "AAAAAQABAAEAAAARAgAABAAAAHAQAwAAAA4AAwABAAIAAAAWAgAACQAAAGIAAAAbAQoAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" + + "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" + + "OwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjUABWhlbGxvAANvdXQA" + + "B3ByaW50bG4ABXNheUhpABEABw4AGgAHDocAAAABAQCBgASgAgEBuAIAAA0AAAAAAAAAAQAAAAAA" + + "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" + + "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" + + "AgAAABECAAAAIAAAAQAAABwCAAAAEAAAAQAAACwCAAA="); + + + /** + * base64 encoded class/dex file for + * public class Transform { + * public void sayHi() { + * System.out.println("Goodbye"); + * } + * } + */ + private static final byte[] TRANSFORMED_CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" + + "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" + + "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" + + "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" + + "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAG" + + "AAAAAAACAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" + + "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAaAAgAGwABAAwAAAACAA0="); + private static final byte[] TRANSFORMED_DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQAPXh6T3l1FObhHsKf1U2vi+0GmAvElxBLMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" + + "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAABAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" + + "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" + + "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" + + "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjUAA291" + + "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgAaAAcOhwAAAAEBAIGABKACAQG4Ag0AAAAAAAAAAQAAAAAA" + + "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" + + "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" + + "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA="); + + public static void main(String[] args) throws Exception { + ClassLoader loader; + try { + // Art uses this classloader to do in-memory dex files. There is no support for defineClass + loader = (ClassLoader)Class.forName("dalvik.system.InMemoryDexClassLoader") + .getConstructor(ByteBuffer.class, ClassLoader.class) + .newInstance(ByteBuffer.wrap(INITIAL_DEX_BYTES), + ClassLoader.getSystemClassLoader()); + } catch (ClassNotFoundException e) { + // Seem to be on RI. Just make a new ClassLoader that calls defineClass. + loader = new ClassLoader() { + public Class<?> findClass(String name) throws ClassNotFoundException { + if (name.equals("Transform")) { + return defineClass(name, INITIAL_CLASS_BYTES, 0, INITIAL_CLASS_BYTES.length); + } else { + throw new ClassNotFoundException("Couldn't find class: " + name); + } + } + }; + } + doTest(loader); + } + + public static void doTest(ClassLoader loader) throws Exception { + // Get the class + Class<?> transform_class = loader.loadClass("Transform"); + Method say_hi_method = transform_class.getMethod("sayHi"); + Object t = transform_class.newInstance(); + + // Run the actual test. + say_hi_method.invoke(t); + doCommonClassRedefinition(transform_class, TRANSFORMED_CLASS_BYTES, TRANSFORMED_DEX_BYTES); + say_hi_method.invoke(t); + } + + // Transforms the class + private static native void doCommonClassRedefinition(Class<?> target, + byte[] class_file, + byte[] dex_file); +} diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index fc9f030559..cb06e4263e 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -183,15 +183,23 @@ public class Main { public String bar(); } - public static class BarSuper { + public static abstract class BarAbstractSuper { + public abstract String abstractSuperPublicMethod(); + } + + public static class BarSuper extends BarAbstractSuper { public String superPublicMethod() { return "superPublicMethod"; } - public String superProtectedMethod() { + protected String superProtectedMethod() { return "superProtectedMethod"; } + public String abstractSuperPublicMethod() { + return "abstractSuperPublicMethod"; + } + String superPackageMethod() { return "superPackageMethod"; } @@ -288,15 +296,19 @@ public class Main { System.out.println("Unexpected return value for BarImpl#bar: " + str); } - // TODO(narayan): Fix this case, we're using the wrong ArtMethod for the - // invoke resulting in a failing check in the interpreter. - // - // mh = MethodHandles.lookup().findVirtual(Bar.class, "bar", - // MethodType.methodType(String.class)); - // str = (String) mh.invoke(new BarImpl()); - // if (!"bar".equals(str)) { - // System.out.println("Unexpected return value for BarImpl#bar: " + str); - // } + mh = MethodHandles.lookup().findVirtual(Bar.class, "bar", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + if (!"bar".equals(str)) { + System.out.println("Unexpected return value for BarImpl#bar: " + str); + } + + mh = MethodHandles.lookup().findVirtual(BarAbstractSuper.class, "abstractSuperPublicMethod", + MethodType.methodType(String.class)); + str = (String) mh.invoke(new BarImpl()); + if (!"abstractSuperPublicMethod".equals(str)) { + System.out.println("Unexpected return value for BarImpl#abstractSuperPublicMethod: " + str); + } // We should also be able to lookup public / protected / package methods in // the super class, given sufficient access privileges. diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 1938b92db8..95967b527c 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -170,12 +170,6 @@ endif ifeq ($(ART_TEST_RUN_TEST_MULTI_IMAGE),true) IMAGE_TYPES := multipicimage endif -ifeq ($(ART_TEST_NPIC_IMAGE),true) - IMAGE_TYPES += npicimage - ifeq ($(ART_TEST_RUN_TEST_MULTI_IMAGE),true) - IMAGE_TYPES := multinpicimage - endif -endif PICTEST_TYPES := npictest ifeq ($(ART_TEST_PIC_TEST),true) PICTEST_TYPES += pictest @@ -878,32 +872,36 @@ define core-image-dependencies endif endif ifeq ($(2),no-image) - $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_pic_$(4)) + $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_$(4)) else - ifeq ($(2),npicimage) - $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_no-pic_$(4)) + ifeq ($(2),picimage) + $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_$(4)) else - ifeq ($(2),picimage) - $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_pic_$(4)) - else - ifeq ($(2),multinpicimage) - $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_no-pic_multi_$(4)) - else - ifeq ($(2),multipicimage) - $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_pic_multi_$(4)) - endif - endif + ifeq ($(2),multipicimage) + $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_multi_$(4)) endif endif endif endef +COMPILER_TYPES_2 := optimizing +COMPILER_TYPES_2 += interpreter +COMPILER_TYPES_2 += jit +COMPILER_TYPES_2 += regalloc_gc +COMPILER_TYPES_2 += interp-ac +ALL_ADDRESS_SIZES_2 := 32 64 +IMAGE_TYPES_2 := picimage +IMAGE_TYPES_2 += no-image +IMAGE_TYPES_2 += npicimage +IMAGE_TYPES_2 += multinpicimage +IMAGE_TYPES_2 += multipicimage + # Add core image dependencies required for given target - HOST or TARGET, # IMAGE_TYPE, COMPILER_TYPE and ADDRESS_SIZE to the prereq_rules. $(foreach target, $(TARGET_TYPES), \ - $(foreach image, $(IMAGE_TYPES), \ - $(foreach compiler, $(COMPILER_TYPES), \ - $(foreach address_size, $(ALL_ADDRESS_SIZES), $(eval \ + $(foreach image, $(IMAGE_TYPES_2), \ + $(foreach compiler, $(COMPILER_TYPES_2), \ + $(foreach address_size, $(ALL_ADDRESS_SIZES_2), $(eval \ $(call core-image-dependencies,$(target),$(image),$(compiler),$(address_size))))))) test-art-host-run-test-dependencies : $(host_prereq_rules) @@ -1081,50 +1079,29 @@ define define-test-art-run-test # Add the core dependency. This is required for pre-building. # Use the PIC image, as it is the default in run-test, to match dependencies. ifeq ($(1),host) - prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_pic_$(13)) + prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_$(13)) else - prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_pic_$(13)) + prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_$(13)) endif else - ifeq ($(9),npicimage) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_IMAGE_RULES - run_test_options += --npic-image - # Add the core dependency. + ifeq ($(9),picimage) + test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PICIMAGE_RULES ifeq ($(1),host) - prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_no-pic_$(13)) + prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_$(13)) else - prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_no-pic_$(13)) + prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_$(13)) endif else - ifeq ($(9),picimage) + ifeq ($(9),multipicimage) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PICIMAGE_RULES + run_test_options += --multi-image ifeq ($(1),host) - prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_pic_$(13)) + prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_multi_$(13)) else - prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_pic_$(13)) + prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_multi_$(13)) endif else - ifeq ($(9),multinpicimage) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_IMAGE_RULES - run_test_options += --npic-image --multi-image - ifeq ($(1),host) - prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_no-pic_multi_$(13)) - else - prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_no-pic_multi_$(13)) - endif - else - ifeq ($(9),multipicimage) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PICIMAGE_RULES - run_test_options += --multi-image - ifeq ($(1),host) - prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_pic_multi_$(13)) - else - prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_pic_multi_$(13)) - endif - else - $$(error found $(9) expected $(IMAGE_TYPES)) - endif - endif + $$(error found $(9) expected $(IMAGE_TYPES)) endif endif endif diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 7d218f1d61..f3d4332009 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -19,7 +19,7 @@ DEBUGGER="n" DEV_MODE="n" DEX2OAT="" EXPERIMENTAL="" -FALSE_BIN="/system/bin/false" +FALSE_BIN="false" FLAGS="" ANDROID_FLAGS="" GDB="" diff --git a/test/run-test b/test/run-test index c926c115e2..e808deef52 100755 --- a/test/run-test +++ b/test/run-test @@ -143,7 +143,6 @@ never_clean="no" have_dex2oat="yes" have_patchoat="yes" have_image="yes" -pic_image_suffix="" multi_image_suffix="" android_root="/system" bisection_search="no" @@ -199,9 +198,6 @@ while true; do elif [ "x$1" = "x--no-image" ]; then have_image="no" shift - elif [ "x$1" = "x--npic-image" ]; then - pic_image_suffix="-npic" - shift elif [ "x$1" = "x--multi-image" ]; then multi_image_suffix="-multi" shift @@ -507,12 +503,12 @@ if [ "$runtime" = "dalvik" ]; then elif [ "$runtime" = "art" ]; then if [ "$target_mode" = "no" ]; then guess_host_arch_name - run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art" + run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}${multi_image_suffix}.art" run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}:${ANDROID_HOST_OUT}/nativetest${suffix64}" else guess_target_arch_name run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}" - run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art" + run_args="${run_args} --boot /data/art-test/core${image_suffix}${multi_image_suffix}.art" fi if [ "$relocate" = "yes" ]; then run_args="${run_args} --relocate" @@ -668,8 +664,6 @@ if [ "$usage" = "yes" ]; then echo " --dex2oat-swap Use a dex2oat swap file." echo " --instruction-set-features [string]" echo " Set instruction-set-features for compilation." - echo " --npic-image Use an image compiled with non-position independent code " - echo " for the boot class path." echo " --multi-image Use a set of images compiled with dex2oat multi-image for" echo " the boot class path." echo " --pic-test Compile the test code position independent." diff --git a/test/testrunner/env.py b/test/testrunner/env.py index 0b69718f93..f327974dae 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -105,9 +105,6 @@ ART_TEST_OPTIMIZING = getEnvBoolean('ART_TEST_OPTIMIZING', ART_TEST_FULL) # Do you want to test the optimizing compiler with graph coloring register allocation? ART_TEST_OPTIMIZING_GRAPH_COLOR = getEnvBoolean('ART_TEST_OPTIMIZING_GRAPH_COLOR', ART_TEST_FULL) -# Do we want to test a non-PIC-compiled core image? -ART_TEST_NPIC_IMAGE = getEnvBoolean('ART_TEST_NPIC_IMAGE', ART_TEST_FULL) - # Do we want to test PIC-compiled tests ("apps")? ART_TEST_PIC_TEST = getEnvBoolean('ART_TEST_PIC_TEST', ART_TEST_FULL) # Do you want tracing tests run? diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index a5bfcffae1..748ec31ae4 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -133,8 +133,7 @@ def gather_test_info(): VARIANT_TYPE_DICT['run'] = {'ndebug', 'debug'} VARIANT_TYPE_DICT['target'] = {'target', 'host'} VARIANT_TYPE_DICT['trace'] = {'trace', 'ntrace', 'stream'} - VARIANT_TYPE_DICT['image'] = {'picimage', 'no-image', 'npicimage', - 'multinpicimage', 'multipicimage'} + VARIANT_TYPE_DICT['image'] = {'picimage', 'no-image', 'multipicimage'} VARIANT_TYPE_DICT['debuggable'] = {'ndebuggable', 'debuggable'} VARIANT_TYPE_DICT['gc'] = {'gcstress', 'gcverify', 'cms'} VARIANT_TYPE_DICT['prebuild'] = {'no-prebuild', 'no-dex2oat', 'prebuild'} @@ -218,10 +217,6 @@ def setup_test_env(): IMAGE_TYPES.add('no-image') if env.ART_TEST_RUN_TEST_MULTI_IMAGE: IMAGE_TYPES.add('multipicimage') - if env.ART_TEST_NPIC_IMAGE: - IMAGE_TYPES.add('npicimage') - if env.ART_TEST_RUN_TEST_MULTI_IMAGE: - IMAGE_TYPES.add('multinpicimage') if env.ART_TEST_RUN_TEST_IMAGE or not IMAGE_TYPES: # Default IMAGE_TYPES.add('picimage') @@ -388,10 +383,6 @@ def run_tests(tests): if image == 'no-image': options_test += ' --no-image' - elif image == 'npicimage': - options_test += ' --npic-image' - elif image == 'multinpicimage': - options_test += ' --npic-image --multi-image' elif image == 'multipicimage': options_test += ' --multi-image' @@ -446,29 +437,34 @@ def run_test(command, test, test_variant, test_name): test_name: The name of the test along with the variants. """ global stop_testrunner - if is_test_disabled(test, test_variant): - test_skipped = True - else: - test_skipped = False - proc = subprocess.Popen(command.split(), stderr=subprocess.STDOUT, stdout=subprocess.PIPE) - script_output = proc.stdout.read().strip() - test_passed = not proc.wait() - - if not test_skipped: - if test_passed: - print_test_info(test_name, 'PASS') + try: + if is_test_disabled(test, test_variant): + test_skipped = True else: - failed_tests.append(test_name) - if not env.ART_TEST_KEEP_GOING: - stop_testrunner = True - print_test_info(test_name, 'FAIL', ('%s\n%s') % ( - command, script_output)) - elif not dry_run: - print_test_info(test_name, 'SKIP') - skipped_tests.append(test_name) - else: - print_test_info(test_name, '') - semaphore.release() + test_skipped = False + proc = subprocess.Popen(command.split(), stderr=subprocess.STDOUT, stdout=subprocess.PIPE) + script_output = proc.stdout.read().strip() + test_passed = not proc.wait() + + if not test_skipped: + if test_passed: + print_test_info(test_name, 'PASS') + else: + failed_tests.append(test_name) + if not env.ART_TEST_KEEP_GOING: + stop_testrunner = True + print_test_info(test_name, 'FAIL', ('%s\n%s') % ( + command, script_output)) + elif not dry_run: + print_test_info(test_name, 'SKIP') + skipped_tests.append(test_name) + else: + print_test_info(test_name, '') + except Exception, e: + failed_tests.append(test_name) + print_text(('%s\n%s\n') % (command, str(e))) + finally: + semaphore.release() def print_test_info(test_name, result, failed_test_info=""): @@ -485,6 +481,7 @@ def print_test_info(test_name, result, failed_test_info=""): command used to invoke the script. It doesn't override the failing test information in either of the cases. """ + global test_count info = '' if not verbose: @@ -493,48 +490,53 @@ def print_test_info(test_name, result, failed_test_info=""): # the console width. console_width = int(os.popen('stty size', 'r').read().split()[1]) info = '\r' + ' ' * console_width + '\r' - print_mutex.acquire() - test_count += 1 - percent = (test_count * 100) / total_test_count - progress_info = ('[ %d%% %d/%d ]') % ( - percent, - test_count, - total_test_count) - - if result == "FAIL": - info += ('%s %s %s\n%s\n') % ( - progress_info, - test_name, - COLOR_ERROR + 'FAIL' + COLOR_NORMAL, - failed_test_info) - else: - result_text = '' - if result == 'PASS': - result_text += COLOR_PASS + 'PASS' + COLOR_NORMAL - elif result == 'SKIP': - result_text += COLOR_SKIP + 'SKIP' + COLOR_NORMAL - - if verbose: - info += ('%s %s %s\n') % ( - progress_info, - test_name, - result_text) - else: - total_output_length = 2 # Two spaces - total_output_length += len(progress_info) - total_output_length += len(result) - allowed_test_length = console_width - total_output_length - test_name_len = len(test_name) - if allowed_test_length < test_name_len: - test_name = ('%s...%s') % ( - test_name[:(allowed_test_length - 3)/2], - test_name[-(allowed_test_length - 3)/2:]) - info += ('%s %s %s') % ( + try: + print_mutex.acquire() + test_count += 1 + percent = (test_count * 100) / total_test_count + progress_info = ('[ %d%% %d/%d ]') % ( + percent, + test_count, + total_test_count) + + if result == "FAIL": + info += ('%s %s %s\n%s\n') % ( progress_info, test_name, - result_text) - print_text(info) - print_mutex.release() + COLOR_ERROR + 'FAIL' + COLOR_NORMAL, + failed_test_info) + else: + result_text = '' + if result == 'PASS': + result_text += COLOR_PASS + 'PASS' + COLOR_NORMAL + elif result == 'SKIP': + result_text += COLOR_SKIP + 'SKIP' + COLOR_NORMAL + + if verbose: + info += ('%s %s %s\n') % ( + progress_info, + test_name, + result_text) + else: + total_output_length = 2 # Two spaces + total_output_length += len(progress_info) + total_output_length += len(result) + allowed_test_length = console_width - total_output_length + test_name_len = len(test_name) + if allowed_test_length < test_name_len: + test_name = ('%s...%s') % ( + test_name[:(allowed_test_length - 3)/2], + test_name[-(allowed_test_length - 3)/2:]) + info += ('%s %s %s') % ( + progress_info, + test_name, + result_text) + print_text(info) + except Exception, e: + print_text(('%s\n%s\n') % (test_name, str(e))) + failed_tests.append(test_name) + finally: + print_mutex.release() def get_disabled_test_info(): """Generate set of known failures. @@ -788,10 +790,6 @@ def parse_option(): TRACE_TYPES.add('ntrace') if options.cms: GC_TYPES.add('cms') - if options.npicimage: - IMAGE_TYPES.add('npicimage') - if options.multinpicimage: - IMAGE_TYPES.add('multinpicimage') if options.multipicimage: IMAGE_TYPES.add('multipicimage') if options.verbose: @@ -837,15 +835,13 @@ def main(): while threading.active_count() > 1: time.sleep(0.1) print_analysis() - if failed_tests: - sys.exit(1) - sys.exit(0) - except SystemExit: - pass except Exception, e: print_analysis() print_text(str(e)) sys.exit(1) + if failed_tests: + sys.exit(1) + sys.exit(0) if __name__ == '__main__': main() diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index dcef8c01c2..08abdb3713 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -105,12 +105,6 @@ names: ["org.apache.harmony.tests.java.lang.ProcessTest#test_getErrorStream"] }, { - description: "Short date format flag ignored for es_US locale.", - result: EXEC_FAILED, - name: "libcore.icu.DateIntervalFormatTest#test_formatDateInterval", - bug: 18619426 -}, -{ description: "Error decoding digital signature bytes.", result: EXEC_FAILED, name: "org.apache.harmony.security.tests.java.security.Signature2Test#test_verify$BII", @@ -134,12 +128,6 @@ names: ["org.apache.harmony.tests.java.lang.ProcessManagerTest#testEnvironment"] }, { - description: "Crypto failures", - result: EXEC_FAILED, - names: ["libcore.javax.crypto.CipherTest#testCipher_ShortBlock_Failure", - "libcore.javax.crypto.CipherTest#testCipher_Success"] -}, -{ description: "Flake when running with libartd.so or interpreter", result: EXEC_FAILED, bug:22106064, |