summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--build/Android.gtest.mk2
-rw-r--r--compiler/Android.bp1
-rw-r--r--compiler/driver/compiler_driver.cc115
-rw-r--r--compiler/optimizing/code_generator.h2
-rw-r--r--compiler/optimizing/code_generator_arm64.cc77
-rw-r--r--compiler/optimizing/code_generator_arm64.h2
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc121
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.h3
-rw-r--r--compiler/optimizing/code_generator_mips.cc98
-rw-r--r--compiler/optimizing/code_generator_mips.h1
-rw-r--r--compiler/optimizing/code_generator_mips64.cc98
-rw-r--r--compiler/optimizing/code_generator_mips64.h1
-rw-r--r--compiler/optimizing/code_generator_x86.cc62
-rw-r--r--compiler/optimizing/code_generator_x86.h1
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc71
-rw-r--r--compiler/optimizing/code_generator_x86_64.h1
-rw-r--r--compiler/optimizing/graph_checker.cc92
-rw-r--r--compiler/optimizing/graph_checker.h6
-rw-r--r--compiler/optimizing/graph_visualizer.cc50
-rw-r--r--compiler/optimizing/induction_var_range.cc28
-rw-r--r--compiler/optimizing/induction_var_range.h18
-rw-r--r--compiler/optimizing/instruction_builder.cc107
-rw-r--r--compiler/optimizing/instruction_builder.h7
-rw-r--r--compiler/optimizing/instruction_simplifier.cc43
-rw-r--r--compiler/optimizing/loop_analysis.cc120
-rw-r--r--compiler/optimizing/loop_analysis.h139
-rw-r--r--compiler/optimizing/loop_optimization.cc289
-rw-r--r--compiler/optimizing/loop_optimization.h25
-rw-r--r--compiler/optimizing/nodes.cc2
-rw-r--r--compiler/optimizing/nodes.h216
-rw-r--r--compiler/optimizing/optimizing_compiler_stats.h1
-rw-r--r--compiler/optimizing/prepare_for_register_allocation.cc14
-rw-r--r--compiler/optimizing/prepare_for_register_allocation.h2
-rw-r--r--compiler/optimizing/reference_type_propagation.cc35
-rw-r--r--compiler/optimizing/sharpening.cc69
-rw-r--r--compiler/optimizing/sharpening.h27
-rw-r--r--compiler/optimizing/superblock_cloner.cc2
-rw-r--r--compiler/optimizing/superblock_cloner.h1
-rw-r--r--compiler/utils/atomic_dex_ref_map-inl.h3
-rw-r--r--libdexfile/dex/dex_file.cc3
-rw-r--r--libdexfile/dex/dex_file.h11
-rw-r--r--libdexfile/dex/hidden_api_access_flags.h1
-rw-r--r--openjdkjvmti/fixed_up_dex_file.cc12
-rw-r--r--openjdkjvmti/ti_stack.cc4
-rw-r--r--runtime/Android.bp9
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S4
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S4
-rw-r--r--runtime/arch/mips/quick_entrypoints_mips.S7
-rw-r--r--runtime/arch/mips64/quick_entrypoints_mips64.S6
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S5
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S5
-rw-r--r--runtime/base/arena_allocator.cc1
-rw-r--r--runtime/base/arena_allocator.h1
-rw-r--r--runtime/base/file_utils.cc17
-rw-r--r--runtime/base/file_utils.h3
-rw-r--r--runtime/base/quasi_atomic.h12
-rw-r--r--runtime/class_linker.cc15
-rw-r--r--runtime/debugger.cc15
-rw-r--r--runtime/dex/art_dex_file_loader.cc36
-rw-r--r--runtime/dex/art_dex_file_loader.h13
-rw-r--r--runtime/dex/art_dex_file_loader_test.cc90
-rw-r--r--runtime/entrypoints/quick/quick_throw_entrypoints.cc23
-rw-r--r--runtime/gc/collector/concurrent_copying.cc15
-rw-r--r--runtime/gc/heap.cc59
-rw-r--r--runtime/gc/heap.h2
-rw-r--r--runtime/hidden_api.cc172
-rw-r--r--runtime/hidden_api.h152
-rw-r--r--runtime/hidden_api_test.cc275
-rw-r--r--runtime/image.cc2
-rw-r--r--runtime/interpreter/interpreter.cc13
-rw-r--r--runtime/interpreter/interpreter_intrinsics.cc2
-rw-r--r--runtime/interpreter/unstarted_runtime.cc3
-rw-r--r--runtime/jit/jit_code_cache.cc8
-rw-r--r--runtime/jit/jit_code_cache.h4
-rw-r--r--runtime/jni_internal.cc9
-rw-r--r--runtime/mirror/class-inl.h1
-rw-r--r--runtime/monitor.cc2
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc16
-rw-r--r--runtime/native/dalvik_system_ZygoteHooks.cc1
-rw-r--r--runtime/native/java_lang_Class.cc10
-rw-r--r--runtime/native/sun_misc_Unsafe.cc6
-rw-r--r--runtime/native_stack_dump.cc7
-rw-r--r--runtime/native_stack_dump.h3
-rw-r--r--runtime/parsed_options.cc5
-rw-r--r--runtime/runtime.cc3
-rw-r--r--runtime/runtime.h18
-rw-r--r--runtime/runtime_common.cc3
-rw-r--r--runtime/runtime_common.h4
-rw-r--r--runtime/runtime_options.def1
-rw-r--r--runtime/subtype_check.h21
-rw-r--r--runtime/thread.cc12
-rw-r--r--runtime/thread.h2
-rw-r--r--runtime/thread_list.cc21
-rw-r--r--runtime/thread_list.h2
-rw-r--r--test/036-finalizer/src/Main.java14
-rwxr-xr-xtest/1940-ddms-ext/check21
-rw-r--r--test/1940-ddms-ext/expected_error.txt4
-rwxr-xr-xtest/1940-ddms-ext/remove_error.py38
-rw-r--r--test/411-optimizing-arith-mul/info.txt1
-rw-r--r--test/411-optimizing-arith/expected.txt (renamed from test/411-optimizing-arith-mul/expected.txt)0
-rw-r--r--test/411-optimizing-arith/info.txt7
-rw-r--r--test/411-optimizing-arith/src/DivTest.java (renamed from test/417-optimizing-arith-div/src/Main.java)8
-rw-r--r--test/411-optimizing-arith/src/Main.java26
-rw-r--r--test/411-optimizing-arith/src/MulTest.java (renamed from test/411-optimizing-arith-mul/src/Main.java)17
-rw-r--r--test/411-optimizing-arith/src/NegTest.java (renamed from test/415-optimizing-arith-neg/src/Main.java)30
-rw-r--r--test/411-optimizing-arith/src/RemTest.java (renamed from test/428-optimizing-arith-rem/src/Main.java)4
-rw-r--r--test/411-optimizing-arith/src/ShiftsTest.java (renamed from test/431-optimizing-arith-shifts/src/Main.java)4
-rw-r--r--test/411-optimizing-arith/src/SubTest.java (renamed from test/414-optimizing-arith-sub/src/Main.java)4
-rw-r--r--test/414-optimizing-arith-sub/expected.txt0
-rw-r--r--test/414-optimizing-arith-sub/info.txt1
-rw-r--r--test/415-optimizing-arith-neg/expected.txt0
-rw-r--r--test/415-optimizing-arith-neg/info.txt1
-rw-r--r--test/417-optimizing-arith-div/expected.txt0
-rw-r--r--test/417-optimizing-arith-div/info.txt1
-rw-r--r--test/428-optimizing-arith-rem/expected.txt0
-rw-r--r--test/428-optimizing-arith-rem/info.txt1
-rw-r--r--test/431-optimizing-arith-shifts/expected.txt0
-rw-r--r--test/431-optimizing-arith-shifts/info.txt1
-rw-r--r--test/646-checker-hadd-short/src/Main.java38
-rw-r--r--test/651-checker-byte-simd-minmax/expected.txt1
-rw-r--r--test/651-checker-char-simd-minmax/expected.txt1
-rw-r--r--test/651-checker-char-simd-minmax/info.txt1
-rw-r--r--test/651-checker-double-simd-minmax/expected.txt1
-rw-r--r--test/651-checker-double-simd-minmax/info.txt1
-rw-r--r--test/651-checker-float-simd-minmax/expected.txt1
-rw-r--r--test/651-checker-float-simd-minmax/info.txt1
-rw-r--r--test/651-checker-int-simd-minmax/expected.txt1
-rw-r--r--test/651-checker-int-simd-minmax/info.txt1
-rw-r--r--test/651-checker-long-simd-minmax/expected.txt1
-rw-r--r--test/651-checker-long-simd-minmax/info.txt1
-rw-r--r--test/651-checker-short-simd-minmax/expected.txt1
-rw-r--r--test/651-checker-short-simd-minmax/info.txt1
-rw-r--r--test/651-checker-simd-minmax/expected.txt7
-rw-r--r--test/651-checker-simd-minmax/info.txt (renamed from test/651-checker-byte-simd-minmax/info.txt)0
-rw-r--r--test/651-checker-simd-minmax/src/ByteSimdMinMax.java (renamed from test/651-checker-byte-simd-minmax/src/Main.java)30
-rw-r--r--test/651-checker-simd-minmax/src/CharSimdMinMax.java (renamed from test/651-checker-char-simd-minmax/src/Main.java)18
-rw-r--r--test/651-checker-simd-minmax/src/DoubleSimdMinMax.java (renamed from test/651-checker-double-simd-minmax/src/Main.java)14
-rw-r--r--test/651-checker-simd-minmax/src/FloatSimdMinMax.java (renamed from test/651-checker-float-simd-minmax/src/Main.java)14
-rw-r--r--test/651-checker-simd-minmax/src/IntSimdMinMax.java (renamed from test/651-checker-int-simd-minmax/src/Main.java)14
-rw-r--r--test/651-checker-simd-minmax/src/LongSimdMinMax.java (renamed from test/651-checker-long-simd-minmax/src/Main.java)18
-rw-r--r--test/651-checker-simd-minmax/src/Main.java27
-rw-r--r--test/651-checker-simd-minmax/src/ShortSimdMinMax.java (renamed from test/651-checker-short-simd-minmax/src/Main.java)30
-rw-r--r--test/660-checker-simd-sad-short/src/Main.java106
-rw-r--r--test/678-checker-simd-saturation/src/Main.java137
-rw-r--r--test/680-checker-deopt-dex-pc-0/expected.txt2
-rw-r--r--test/680-checker-deopt-dex-pc-0/info.txt2
-rw-r--r--test/680-checker-deopt-dex-pc-0/src/Main.java57
-rw-r--r--test/HiddenApiSignatures/Class1.java35
-rw-r--r--test/HiddenApiSignatures/Class12.java24
-rw-r--r--test/HiddenApiSignatures/Class2.java27
-rw-r--r--test/HiddenApiSignatures/Class3.java27
-rwxr-xr-xtest/etc/run-test-jar4
-rw-r--r--test/knownfailures.json6
-rw-r--r--test/testrunner/target_config.py9
-rw-r--r--tools/veridex/Android.bp4
-rw-r--r--tools/veridex/Android.mk35
-rw-r--r--tools/veridex/README.md14
-rwxr-xr-xtools/veridex/appcompat.sh51
-rw-r--r--tools/veridex/hidden_api.cc68
-rw-r--r--tools/veridex/hidden_api.h75
-rw-r--r--tools/veridex/hidden_api_finder.cc266
-rw-r--r--tools/veridex/hidden_api_finder.h59
-rw-r--r--tools/veridex/resolver.cc1
-rw-r--r--tools/veridex/resolver.h6
-rw-r--r--tools/veridex/veridex.cc10
166 files changed, 3713 insertions, 822 deletions
diff --git a/Android.mk b/Android.mk
index 558986e562..e4f4e74cb2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -67,6 +67,7 @@ include $(art_path)/tools/Android.mk
include $(art_path)/tools/ahat/Android.mk
include $(art_path)/tools/amm/Android.mk
include $(art_path)/tools/dexfuzz/Android.mk
+include $(art_path)/tools/veridex/Android.mk
include $(art_path)/libart_fake/Android.mk
ART_HOST_DEPENDENCIES := \
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 8681642c0e..b342abe17c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -37,6 +37,7 @@ GTEST_DEX_DIRECTORIES := \
ExceptionHandle \
GetMethodSignature \
HiddenApi \
+ HiddenApiSignatures \
ImageLayoutA \
ImageLayoutB \
IMTA \
@@ -160,6 +161,7 @@ ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEP
ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi
+ART_GTEST_hidden_api_test_DEX_DEPS := HiddenApiSignatures
ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods
ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB
ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
diff --git a/compiler/Android.bp b/compiler/Android.bp
index e42261c556..6bed48e107 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -70,6 +70,7 @@ art_cc_defaults {
"optimizing/load_store_analysis.cc",
"optimizing/load_store_elimination.cc",
"optimizing/locations.cc",
+ "optimizing/loop_analysis.cc",
"optimizing/loop_optimization.cc",
"optimizing/nodes.cc",
"optimizing/optimization.cc",
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 127833233a..4093833e0b 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -781,7 +781,8 @@ void CompilerDriver::Resolve(jobject class_loader,
// TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a
// stable order.
-static void ResolveConstStrings(Handle<mirror::DexCache> dex_cache,
+static void ResolveConstStrings(ClassLinker* class_linker,
+ Handle<mirror::DexCache> dex_cache,
const DexFile& dex_file,
const DexFile::CodeItem* code_item)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -790,7 +791,6 @@ static void ResolveConstStrings(Handle<mirror::DexCache> dex_cache,
return;
}
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) {
switch (inst->Opcode()) {
case Instruction::CONST_STRING:
@@ -838,22 +838,105 @@ static void ResolveConstStrings(CompilerDriver* driver,
dex_file->StringByTypeIdx(class_def.class_idx_));
if (!compilation_enabled) {
// Compilation is skipped, do not resolve const-string in code of this class.
- // TODO: Make sure that inlining honors this.
+ // FIXME: Make sure that inlining honors this. b/26687569
continue;
}
// Direct and virtual methods.
- int64_t previous_method_idx = -1;
while (it.HasNextMethod()) {
- uint32_t method_idx = it.GetMemberIndex();
- if (method_idx == previous_method_idx) {
- // smali can create dex files with two encoded_methods sharing the same method_idx
- // http://code.google.com/p/smali/issues/detail?id=119
- it.Next();
- continue;
+ ResolveConstStrings(class_linker, dex_cache, *dex_file, it.GetMethodCodeItem());
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+ }
+ }
+}
+
+// Initialize type check bit strings for check-cast and instance-of in the code. Done to have
+// deterministic allocation behavior. Right now this is single-threaded for simplicity.
+// TODO: Collect the relevant type indices in parallel, then process them sequentially in a
+// stable order.
+
+static void InitializeTypeCheckBitstrings(CompilerDriver* driver,
+ ClassLinker* class_linker,
+ Handle<mirror::DexCache> dex_cache,
+ const DexFile& dex_file,
+ const DexFile::CodeItem* code_item)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (code_item == nullptr) {
+ // Abstract or native method.
+ return;
+ }
+
+ for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) {
+ switch (inst->Opcode()) {
+ case Instruction::CHECK_CAST:
+ case Instruction::INSTANCE_OF: {
+ dex::TypeIndex type_index(
+ (inst->Opcode() == Instruction::CHECK_CAST) ? inst->VRegB_21c() : inst->VRegC_22c());
+ const char* descriptor = dex_file.StringByTypeIdx(type_index);
+ // We currently do not use the bitstring type check for array or final (including
+ // primitive) classes. We may reconsider this in future if it's deemed to be beneficial.
+ // And we cannot use it for classes outside the boot image as we do not know the runtime
+ // value of their bitstring when compiling (it may not even get assigned at runtime).
+ if (descriptor[0] == 'L' && driver->IsImageClass(descriptor)) {
+ ObjPtr<mirror::Class> klass =
+ class_linker->LookupResolvedType(type_index,
+ dex_cache.Get(),
+ /* class_loader */ nullptr);
+ CHECK(klass != nullptr) << descriptor << " should have been previously resolved.";
+ // Now assign the bitstring if the class is not final. Keep this in sync with sharpening.
+ if (!klass->IsFinal()) {
+ MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
+ SubtypeCheck<ObjPtr<mirror::Class>>::EnsureAssigned(klass);
+ }
}
- previous_method_idx = method_idx;
- ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem());
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}
+
+static void InitializeTypeCheckBitstrings(CompilerDriver* driver,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ MutableHandle<mirror::DexCache> dex_cache(hs.NewHandle<mirror::DexCache>(nullptr));
+
+ for (const DexFile* dex_file : dex_files) {
+ dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file));
+ TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings);
+
+ size_t class_def_count = dex_file->NumClassDefs();
+ for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+ if (class_data == nullptr) {
+ // empty class, probably a marker interface
+ continue;
+ }
+
+ ClassDataItemIterator it(*dex_file, class_data);
+ it.SkipAllFields();
+
+ bool compilation_enabled = driver->IsClassToCompile(
+ dex_file->StringByTypeIdx(class_def.class_idx_));
+ if (!compilation_enabled) {
+ // Compilation is skipped, do not look for type checks in code of this class.
+ // FIXME: Make sure that inlining honors this. b/26687569
+ continue;
+ }
+
+ // Direct and virtual methods.
+ while (it.HasNextMethod()) {
+ InitializeTypeCheckBitstrings(
+ driver, class_linker, dex_cache, *dex_file, it.GetMethodCodeItem());
it.Next();
}
DCHECK(!it.HasNext());
@@ -955,6 +1038,14 @@ void CompilerDriver::PreCompile(jobject class_loader,
UpdateImageClasses(timings);
VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false);
+
+ if (kBitstringSubtypeCheckEnabled &&
+ GetCompilerOptions().IsForceDeterminism() && GetCompilerOptions().IsBootImage()) {
+ // Initialize type check bit string used by check-cast and instanceof.
+ // Do this now to have a deterministic image.
+ // Note: This is done after UpdateImageClasses() at it relies on the image classes to be final.
+ InitializeTypeCheckBitstrings(this, dex_files, timings);
+ }
}
bool CompilerDriver::IsImageClass(const char* descriptor) const {
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index a4873202b2..3bd5e14539 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -438,6 +438,8 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> {
case TypeCheckKind::kArrayCheck:
case TypeCheckKind::kUnresolvedCheck:
return false;
+ case TypeCheckKind::kBitstringCheck:
+ return true;
}
LOG(FATAL) << "Unreachable";
UNREACHABLE();
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index a024df8537..273346ab4a 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2129,6 +2129,26 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod
__ Bind(slow_path->GetExitLabel());
}
+void InstructionCodeGeneratorARM64::GenerateBitstringTypeCheckCompare(
+ HTypeCheckInstruction* check, vixl::aarch64::Register temp) {
+ uint32_t path_to_root = check->GetBitstringPathToRoot();
+ uint32_t mask = check->GetBitstringMask();
+ DCHECK(IsPowerOfTwo(mask + 1));
+ size_t mask_bits = WhichPowerOf2(mask + 1);
+
+ if (mask_bits == 16u) {
+ // Load only the bitstring part of the status word.
+ __ Ldrh(temp, HeapOperand(temp, mirror::Class::StatusOffset()));
+ } else {
+ // /* uint32_t */ temp = temp->status_
+ __ Ldr(temp, HeapOperand(temp, mirror::Class::StatusOffset()));
+ // Extract the bitstring bits.
+ __ Ubfx(temp, temp, 0, mask_bits);
+ }
+ // Compare the bitstring bits to `path_to_root`.
+ __ Cmp(temp, path_to_root);
+}
+
void CodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) {
BarrierType type = BarrierAll;
@@ -3866,6 +3886,8 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
+ case TypeCheckKind::kBitstringCheck:
+ break;
}
LocationSummary* locations =
@@ -3874,7 +3896,13 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
}
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ if (type_check_kind == TypeCheckKind::kBitstringCheck) {
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
+ locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
// The "out" register is used as a temporary, so it overlaps with the inputs.
// Note that TypeCheckSlowPathARM64 uses this register too.
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
@@ -3887,7 +3915,9 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
Register obj = InputRegisterAt(instruction, 0);
- Register cls = InputRegisterAt(instruction, 1);
+ Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
+ ? Register()
+ : InputRegisterAt(instruction, 1);
Location out_loc = locations->Out();
Register out = OutputRegister(instruction);
const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
@@ -4073,6 +4103,23 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) {
}
break;
}
+
+ case TypeCheckKind::kBitstringCheck: {
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(instruction,
+ out_loc,
+ obj_loc,
+ class_offset,
+ maybe_temp_loc,
+ kWithoutReadBarrier);
+
+ GenerateBitstringTypeCheckCompare(instruction, out);
+ __ Cset(out, eq);
+ if (zero.IsLinked()) {
+ __ B(&done);
+ }
+ break;
+ }
}
if (zero.IsLinked()) {
@@ -4095,7 +4142,13 @@ void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ if (type_check_kind == TypeCheckKind::kBitstringCheck) {
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
+ locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
// Add temps for read barriers and other uses. One is used by TypeCheckSlowPathARM64.
locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
}
@@ -4105,7 +4158,9 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
Register obj = InputRegisterAt(instruction, 0);
- Register cls = InputRegisterAt(instruction, 1);
+ Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
+ ? Register()
+ : InputRegisterAt(instruction, 1);
const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
DCHECK_GE(num_temps, 1u);
DCHECK_LE(num_temps, 3u);
@@ -4286,6 +4341,20 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) {
__ B(ne, &start_loop);
break;
}
+
+ case TypeCheckKind::kBitstringCheck: {
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(instruction,
+ temp_loc,
+ obj_loc,
+ class_offset,
+ maybe_temp2_loc,
+ kWithoutReadBarrier);
+
+ GenerateBitstringTypeCheckCompare(instruction, temp);
+ __ B(ne, type_check_slow_path->GetEntryLabel());
+ break;
+ }
}
__ Bind(&done);
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index a8a9802f9a..6a52eecbd3 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -264,6 +264,8 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator {
private:
void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path,
vixl::aarch64::Register class_reg);
+ void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
+ vixl::aarch64::Register temp);
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void HandleBinaryOp(HBinaryOperation* instr);
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 6ebcc67b49..b38a006305 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -7523,6 +7523,67 @@ void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
__ Bind(slow_path->GetExitLabel());
}
+void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare(
+ HTypeCheckInstruction* check,
+ vixl32::Register temp,
+ vixl32::FlagsUpdate flags_update) {
+ uint32_t path_to_root = check->GetBitstringPathToRoot();
+ uint32_t mask = check->GetBitstringMask();
+ DCHECK(IsPowerOfTwo(mask + 1));
+ size_t mask_bits = WhichPowerOf2(mask + 1);
+
+ // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs
+ // the Z flag for BNE. This is indicated by the `flags_update` parameter.
+ if (mask_bits == 16u) {
+ // Load only the bitstring part of the status word.
+ __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
+ // Check if the bitstring bits are equal to `path_to_root`.
+ if (flags_update == SetFlags) {
+ __ Cmp(temp, path_to_root);
+ } else {
+ __ Sub(temp, temp, path_to_root);
+ }
+ } else {
+ // /* uint32_t */ temp = temp->status_
+ __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
+ if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) {
+ // Compare the bitstring bits using SUB.
+ __ Sub(temp, temp, path_to_root);
+ // Shift out bits that do not contribute to the comparison.
+ __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
+ } else if (IsUint<16>(path_to_root)) {
+ if (temp.IsLow()) {
+ // Note: Optimized for size but contains one more dependent instruction than necessary.
+ // MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the
+ // macro assembler would use the high reg IP for the constant by default.
+ // Compare the bitstring bits using SUB.
+ __ Sub(temp, temp, path_to_root & 0x00ffu); // 16-bit SUB (immediate) T2
+ __ Sub(temp, temp, path_to_root & 0xff00u); // 32-bit SUB (immediate) T3
+ // Shift out bits that do not contribute to the comparison.
+ __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
+ } else {
+ // Extract the bitstring bits.
+ __ Ubfx(temp, temp, 0, mask_bits);
+ // Check if the bitstring bits are equal to `path_to_root`.
+ if (flags_update == SetFlags) {
+ __ Cmp(temp, path_to_root);
+ } else {
+ __ Sub(temp, temp, path_to_root);
+ }
+ }
+ } else {
+ // Shift out bits that do not contribute to the comparison.
+ __ Lsl(temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
+ // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`.
+ if (flags_update == SetFlags) {
+ __ Cmp(temp, path_to_root << (32u - mask_bits));
+ } else {
+ __ Sub(temp, temp, path_to_root << (32u - mask_bits));
+ }
+ }
+ }
+}
+
HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) {
switch (desired_string_load_kind) {
@@ -7714,6 +7775,8 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
+ case TypeCheckKind::kBitstringCheck:
+ break;
}
LocationSummary* locations =
@@ -7722,7 +7785,13 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
}
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ if (type_check_kind == TypeCheckKind::kBitstringCheck) {
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
+ locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
// The "out" register is used as a temporary, so it overlaps with the inputs.
// Note that TypeCheckSlowPathARM uses this register too.
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
@@ -7737,7 +7806,9 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction)
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
vixl32::Register obj = InputRegisterAt(instruction, 0);
- vixl32::Register cls = InputRegisterAt(instruction, 1);
+ vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
+ ? vixl32::Register()
+ : InputRegisterAt(instruction, 1);
Location out_loc = locations->Out();
vixl32::Register out = OutputRegister(instruction);
const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
@@ -7977,6 +8048,26 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction)
__ B(slow_path->GetEntryLabel());
break;
}
+
+ case TypeCheckKind::kBitstringCheck: {
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(instruction,
+ out_loc,
+ obj_loc,
+ class_offset,
+ maybe_temp_loc,
+ kWithoutReadBarrier);
+
+ GenerateBitstringTypeCheckCompare(instruction, out, DontCare);
+ // If `out` is a low reg and we would have another low reg temp, we could
+ // optimize this as RSBS+ADC, see GenerateConditionWithZero().
+ //
+ // Also, in some cases when `out` is a low reg and we're loading a constant to IP
+ // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size
+ // would be the same and we would have fewer direct data dependencies.
+ codegen_->GenerateConditionWithZero(kCondEQ, out, out); // CLZ+LSR
+ break;
+ }
}
if (done.IsReferenced()) {
@@ -7994,7 +8085,13 @@ void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ if (type_check_kind == TypeCheckKind::kBitstringCheck) {
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
+ locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
}
@@ -8003,7 +8100,9 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
vixl32::Register obj = InputRegisterAt(instruction, 0);
- vixl32::Register cls = InputRegisterAt(instruction, 1);
+ vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
+ ? vixl32::Register()
+ : InputRegisterAt(instruction, 1);
Location temp_loc = locations->GetTemp(0);
vixl32::Register temp = RegisterFrom(temp_loc);
const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
@@ -8188,6 +8287,20 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
__ B(ne, &start_loop, /* far_target */ false);
break;
}
+
+ case TypeCheckKind::kBitstringCheck: {
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(instruction,
+ temp_loc,
+ obj_loc,
+ class_offset,
+ maybe_temp2_loc,
+ kWithoutReadBarrier);
+
+ GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags);
+ __ B(ne, type_check_slow_path->GetEntryLabel());
+ break;
+ }
}
if (done.IsReferenced()) {
__ Bind(&done);
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 6a07e36022..2114ea1ba1 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -322,6 +322,9 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator {
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL* slow_path,
vixl32::Register class_reg);
+ void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
+ vixl::aarch32::Register temp,
+ vixl::aarch32::FlagsUpdate flags_update);
void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index be9ff48a6b..25e2eddbfa 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -1950,6 +1950,34 @@ void InstructionCodeGeneratorMIPS::GenerateClassInitializationCheck(SlowPathCode
__ Bind(slow_path->GetExitLabel());
}
+void InstructionCodeGeneratorMIPS::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
+ Register temp) {
+ uint32_t path_to_root = check->GetBitstringPathToRoot();
+ uint32_t mask = check->GetBitstringMask();
+ DCHECK(IsPowerOfTwo(mask + 1));
+ size_t mask_bits = WhichPowerOf2(mask + 1);
+
+ if (mask_bits == 16u) {
+ // Load only the bitstring part of the status word.
+ __ LoadFromOffset(
+ kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value());
+ // Compare the bitstring bits using XOR.
+ __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
+ } else {
+ // /* uint32_t */ temp = temp->status_
+ __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value());
+ // Compare the bitstring bits using XOR.
+ if (IsUint<16>(path_to_root)) {
+ __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
+ } else {
+ __ LoadConst32(TMP, path_to_root);
+ __ Xor(temp, temp, TMP);
+ }
+ // Shift out bits that do not contribute to the comparison.
+ __ Sll(temp, temp, 32 - mask_bits);
+ }
+}
+
void InstructionCodeGeneratorMIPS::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) {
__ Sync(0); // Only stype 0 is supported.
}
@@ -3301,7 +3329,13 @@ void LocationsBuilderMIPS::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ if (type_check_kind == TypeCheckKind::kBitstringCheck) {
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
+ locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
}
@@ -3310,7 +3344,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
Register obj = obj_loc.AsRegister<Register>();
- Register cls = locations->InAt(1).AsRegister<Register>();
+ Location cls = locations->InAt(1);
Location temp_loc = locations->GetTemp(0);
Register temp = temp_loc.AsRegister<Register>();
const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
@@ -3349,7 +3383,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
kWithoutReadBarrier);
// Jump to slow path for throwing the exception or doing a
// more involved array check.
- __ Bne(temp, cls, slow_path->GetEntryLabel());
+ __ Bne(temp, cls.AsRegister<Register>(), slow_path->GetEntryLabel());
break;
}
@@ -3375,7 +3409,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
// exception.
__ Beqz(temp, slow_path->GetEntryLabel());
// Otherwise, compare the classes.
- __ Bne(temp, cls, &loop);
+ __ Bne(temp, cls.AsRegister<Register>(), &loop);
break;
}
@@ -3390,7 +3424,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
// Walk over the class hierarchy to find a match.
MipsLabel loop;
__ Bind(&loop);
- __ Beq(temp, cls, &done);
+ __ Beq(temp, cls.AsRegister<Register>(), &done);
// /* HeapReference<Class> */ temp = temp->super_class_
GenerateReferenceLoadOneRegister(instruction,
temp_loc,
@@ -3413,7 +3447,7 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
maybe_temp2_loc,
kWithoutReadBarrier);
// Do an exact check.
- __ Beq(temp, cls, &done);
+ __ Beq(temp, cls.AsRegister<Register>(), &done);
// Otherwise, we need to check that the object's class is a non-primitive array.
// /* HeapReference<Class> */ temp = temp->component_type_
GenerateReferenceLoadOneRegister(instruction,
@@ -3472,7 +3506,21 @@ void InstructionCodeGeneratorMIPS::VisitCheckCast(HCheckCast* instruction) {
// Go to next interface.
__ Addiu(TMP, TMP, -2);
// Compare the classes and continue the loop if they do not match.
- __ Bne(AT, cls, &loop);
+ __ Bne(AT, cls.AsRegister<Register>(), &loop);
+ break;
+ }
+
+ case TypeCheckKind::kBitstringCheck: {
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(instruction,
+ temp_loc,
+ obj_loc,
+ class_offset,
+ maybe_temp2_loc,
+ kWithoutReadBarrier);
+
+ GenerateBitstringTypeCheckCompare(instruction, temp);
+ __ Bnez(temp, slow_path->GetEntryLabel());
break;
}
}
@@ -7415,6 +7463,8 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
+ case TypeCheckKind::kBitstringCheck:
+ break;
}
LocationSummary* locations =
@@ -7423,7 +7473,13 @@ void LocationsBuilderMIPS::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
}
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ if (type_check_kind == TypeCheckKind::kBitstringCheck) {
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
+ locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
// The output does overlap inputs.
// Note that TypeCheckSlowPathMIPS uses this register too.
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
@@ -7435,7 +7491,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
Register obj = obj_loc.AsRegister<Register>();
- Register cls = locations->InAt(1).AsRegister<Register>();
+ Location cls = locations->InAt(1);
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
@@ -7467,7 +7523,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
maybe_temp_loc,
read_barrier_option);
// Classes must be equal for the instanceof to succeed.
- __ Xor(out, out, cls);
+ __ Xor(out, out, cls.AsRegister<Register>());
__ Sltiu(out, out, 1);
break;
}
@@ -7494,7 +7550,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
read_barrier_option);
// If `out` is null, we use it for the result, and jump to `done`.
__ Beqz(out, &done);
- __ Bne(out, cls, &loop);
+ __ Bne(out, cls.AsRegister<Register>(), &loop);
__ LoadConst32(out, 1);
break;
}
@@ -7512,7 +7568,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
// Walk over the class hierarchy to find a match.
MipsLabel loop, success;
__ Bind(&loop);
- __ Beq(out, cls, &success);
+ __ Beq(out, cls.AsRegister<Register>(), &success);
// /* HeapReference<Class> */ out = out->super_class_
GenerateReferenceLoadOneRegister(instruction,
out_loc,
@@ -7539,7 +7595,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
read_barrier_option);
// Do an exact check.
MipsLabel success;
- __ Beq(out, cls, &success);
+ __ Beq(out, cls.AsRegister<Register>(), &success);
// Otherwise, we need to check that the object's class is a non-primitive array.
// /* HeapReference<Class> */ out = out->component_type_
GenerateReferenceLoadOneRegister(instruction,
@@ -7571,7 +7627,7 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS(
instruction, /* is_fatal */ false);
codegen_->AddSlowPath(slow_path);
- __ Bne(out, cls, slow_path->GetEntryLabel());
+ __ Bne(out, cls.AsRegister<Register>(), slow_path->GetEntryLabel());
__ LoadConst32(out, 1);
break;
}
@@ -7603,6 +7659,20 @@ void InstructionCodeGeneratorMIPS::VisitInstanceOf(HInstanceOf* instruction) {
__ B(slow_path->GetEntryLabel());
break;
}
+
+ case TypeCheckKind::kBitstringCheck: {
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(instruction,
+ out_loc,
+ obj_loc,
+ class_offset,
+ maybe_temp_loc,
+ kWithoutReadBarrier);
+
+ GenerateBitstringTypeCheckCompare(instruction, out);
+ __ Sltiu(out, out, 1);
+ break;
+ }
}
__ Bind(&done);
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 1f1743ff9e..2e7c736dbd 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -237,6 +237,7 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator {
private:
void GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path, Register class_reg);
void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
+ void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp);
void HandleBinaryOp(HBinaryOperation* operation);
void HandleCondition(HCondition* instruction);
void HandleShift(HBinaryOperation* operation);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index f8851b4eea..5b07b55cbb 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1794,6 +1794,34 @@ void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCo
__ Bind(slow_path->GetExitLabel());
}
+void InstructionCodeGeneratorMIPS64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
+ GpuRegister temp) {
+ uint32_t path_to_root = check->GetBitstringPathToRoot();
+ uint32_t mask = check->GetBitstringMask();
+ DCHECK(IsPowerOfTwo(mask + 1));
+ size_t mask_bits = WhichPowerOf2(mask + 1);
+
+ if (mask_bits == 16u) {
+ // Load only the bitstring part of the status word.
+ __ LoadFromOffset(
+ kLoadUnsignedHalfword, temp, temp, mirror::Class::StatusOffset().Int32Value());
+ // Compare the bitstring bits using XOR.
+ __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
+ } else {
+ // /* uint32_t */ temp = temp->status_
+ __ LoadFromOffset(kLoadWord, temp, temp, mirror::Class::StatusOffset().Int32Value());
+ // Compare the bitstring bits using XOR.
+ if (IsUint<16>(path_to_root)) {
+ __ Xori(temp, temp, dchecked_integral_cast<uint16_t>(path_to_root));
+ } else {
+ __ LoadConst32(TMP, path_to_root);
+ __ Xor(temp, temp, TMP);
+ }
+ // Shift out bits that do not contribute to the comparison.
+ __ Sll(temp, temp, 32 - mask_bits);
+ }
+}
+
void InstructionCodeGeneratorMIPS64::GenerateMemoryBarrier(MemBarrierKind kind ATTRIBUTE_UNUSED) {
__ Sync(0); // only stype 0 is supported
}
@@ -2854,7 +2882,13 @@ void LocationsBuilderMIPS64::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ if (type_check_kind == TypeCheckKind::kBitstringCheck) {
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
+ locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
}
@@ -2863,7 +2897,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
GpuRegister obj = obj_loc.AsRegister<GpuRegister>();
- GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>();
+ Location cls = locations->InAt(1);
Location temp_loc = locations->GetTemp(0);
GpuRegister temp = temp_loc.AsRegister<GpuRegister>();
const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
@@ -2902,7 +2936,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
kWithoutReadBarrier);
// Jump to slow path for throwing the exception or doing a
// more involved array check.
- __ Bnec(temp, cls, slow_path->GetEntryLabel());
+ __ Bnec(temp, cls.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());
break;
}
@@ -2928,7 +2962,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
// exception.
__ Beqzc(temp, slow_path->GetEntryLabel());
// Otherwise, compare the classes.
- __ Bnec(temp, cls, &loop);
+ __ Bnec(temp, cls.AsRegister<GpuRegister>(), &loop);
break;
}
@@ -2943,7 +2977,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
// Walk over the class hierarchy to find a match.
Mips64Label loop;
__ Bind(&loop);
- __ Beqc(temp, cls, &done);
+ __ Beqc(temp, cls.AsRegister<GpuRegister>(), &done);
// /* HeapReference<Class> */ temp = temp->super_class_
GenerateReferenceLoadOneRegister(instruction,
temp_loc,
@@ -2966,7 +3000,7 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
maybe_temp2_loc,
kWithoutReadBarrier);
// Do an exact check.
- __ Beqc(temp, cls, &done);
+ __ Beqc(temp, cls.AsRegister<GpuRegister>(), &done);
// Otherwise, we need to check that the object's class is a non-primitive array.
// /* HeapReference<Class> */ temp = temp->component_type_
GenerateReferenceLoadOneRegister(instruction,
@@ -3025,7 +3059,21 @@ void InstructionCodeGeneratorMIPS64::VisitCheckCast(HCheckCast* instruction) {
__ Daddiu(temp, temp, 2 * kHeapReferenceSize);
__ Addiu(TMP, TMP, -2);
// Compare the classes and continue the loop if they do not match.
- __ Bnec(AT, cls, &loop);
+ __ Bnec(AT, cls.AsRegister<GpuRegister>(), &loop);
+ break;
+ }
+
+ case TypeCheckKind::kBitstringCheck: {
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(instruction,
+ temp_loc,
+ obj_loc,
+ class_offset,
+ maybe_temp2_loc,
+ kWithoutReadBarrier);
+
+ GenerateBitstringTypeCheckCompare(instruction, temp);
+ __ Bnezc(temp, slow_path->GetEntryLabel());
break;
}
}
@@ -5529,6 +5577,8 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
+ case TypeCheckKind::kBitstringCheck:
+ break;
}
LocationSummary* locations =
@@ -5537,7 +5587,13 @@ void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
}
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ if (type_check_kind == TypeCheckKind::kBitstringCheck) {
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
+ locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
// The output does overlap inputs.
// Note that TypeCheckSlowPathMIPS64 uses this register too.
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
@@ -5549,7 +5605,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
GpuRegister obj = obj_loc.AsRegister<GpuRegister>();
- GpuRegister cls = locations->InAt(1).AsRegister<GpuRegister>();
+ Location cls = locations->InAt(1);
Location out_loc = locations->Out();
GpuRegister out = out_loc.AsRegister<GpuRegister>();
const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
@@ -5581,7 +5637,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
maybe_temp_loc,
read_barrier_option);
// Classes must be equal for the instanceof to succeed.
- __ Xor(out, out, cls);
+ __ Xor(out, out, cls.AsRegister<GpuRegister>());
__ Sltiu(out, out, 1);
break;
}
@@ -5608,7 +5664,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
read_barrier_option);
// If `out` is null, we use it for the result, and jump to `done`.
__ Beqzc(out, &done);
- __ Bnec(out, cls, &loop);
+ __ Bnec(out, cls.AsRegister<GpuRegister>(), &loop);
__ LoadConst32(out, 1);
break;
}
@@ -5626,7 +5682,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
// Walk over the class hierarchy to find a match.
Mips64Label loop, success;
__ Bind(&loop);
- __ Beqc(out, cls, &success);
+ __ Beqc(out, cls.AsRegister<GpuRegister>(), &success);
// /* HeapReference<Class> */ out = out->super_class_
GenerateReferenceLoadOneRegister(instruction,
out_loc,
@@ -5653,7 +5709,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
read_barrier_option);
// Do an exact check.
Mips64Label success;
- __ Beqc(out, cls, &success);
+ __ Beqc(out, cls.AsRegister<GpuRegister>(), &success);
// Otherwise, we need to check that the object's class is a non-primitive array.
// /* HeapReference<Class> */ out = out->component_type_
GenerateReferenceLoadOneRegister(instruction,
@@ -5685,7 +5741,7 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathMIPS64(
instruction, /* is_fatal */ false);
codegen_->AddSlowPath(slow_path);
- __ Bnec(out, cls, slow_path->GetEntryLabel());
+ __ Bnec(out, cls.AsRegister<GpuRegister>(), slow_path->GetEntryLabel());
__ LoadConst32(out, 1);
break;
}
@@ -5717,6 +5773,20 @@ void InstructionCodeGeneratorMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
__ Bc(slow_path->GetEntryLabel());
break;
}
+
+ case TypeCheckKind::kBitstringCheck: {
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(instruction,
+ out_loc,
+ obj_loc,
+ class_offset,
+ maybe_temp_loc,
+ kWithoutReadBarrier);
+
+ GenerateBitstringTypeCheckCompare(instruction, out);
+ __ Sltiu(out, out, 1);
+ break;
+ }
}
__ Bind(&done);
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 74c947e5d5..6e69e4611a 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -233,6 +233,7 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator {
private:
void GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg);
+ void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, GpuRegister temp);
void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
void HandleBinaryOp(HBinaryOperation* operation);
void HandleCondition(HCondition* instruction);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 4818084a72..4053f557d9 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -6571,6 +6571,26 @@ void InstructionCodeGeneratorX86::GenerateClassInitializationCheck(
// No need for memory fence, thanks to the X86 memory model.
}
+void InstructionCodeGeneratorX86::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
+ Register temp) {
+ uint32_t path_to_root = check->GetBitstringPathToRoot();
+ uint32_t mask = check->GetBitstringMask();
+ DCHECK(IsPowerOfTwo(mask + 1));
+ size_t mask_bits = WhichPowerOf2(mask + 1);
+
+ if (mask_bits == 16u) {
+ // Compare the bitstring in memory.
+ __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root));
+ } else {
+ // /* uint32_t */ temp = temp->status_
+ __ movl(temp, Address(temp, mirror::Class::StatusOffset()));
+ // Compare the bitstring bits using SUB.
+ __ subl(temp, Immediate(path_to_root));
+ // Shift out bits that do not contribute to the comparison.
+ __ shll(temp, Immediate(32u - mask_bits));
+ }
+}
+
HLoadString::LoadKind CodeGeneratorX86::GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) {
switch (desired_string_load_kind) {
@@ -6764,6 +6784,8 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
+ case TypeCheckKind::kBitstringCheck:
+ break;
}
LocationSummary* locations =
@@ -6772,7 +6794,13 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
}
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::Any());
+ if (type_check_kind == TypeCheckKind::kBitstringCheck) {
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
+ locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
// Note that TypeCheckSlowPathX86 uses this "out" register too.
locations->SetOut(Location::RequiresRegister());
// When read barriers are enabled, we need a temporary register for some cases.
@@ -6993,6 +7021,21 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) {
}
break;
}
+
+ case TypeCheckKind::kBitstringCheck: {
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(instruction,
+ out_loc,
+ obj_loc,
+ class_offset,
+ kWithoutReadBarrier);
+
+ GenerateBitstringTypeCheckCompare(instruction, out);
+ __ j(kNotEqual, &zero);
+ __ movl(out, Immediate(1));
+ __ jmp(&done);
+ break;
+ }
}
if (zero.IsLinked()) {
@@ -7019,6 +7062,10 @@ void LocationsBuilderX86::VisitCheckCast(HCheckCast* instruction) {
// Require a register for the interface check since there is a loop that compares the class to
// a memory address.
locations->SetInAt(1, Location::RequiresRegister());
+ } else if (type_check_kind == TypeCheckKind::kBitstringCheck) {
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
+ locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
} else {
locations->SetInAt(1, Location::Any());
}
@@ -7238,6 +7285,19 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) {
__ MaybeUnpoisonHeapReference(cls.AsRegister<Register>());
break;
}
+
+ case TypeCheckKind::kBitstringCheck: {
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(instruction,
+ temp_loc,
+ obj_loc,
+ class_offset,
+ kWithoutReadBarrier);
+
+ GenerateBitstringTypeCheckCompare(instruction, temp);
+ __ j(kNotEqual, type_check_slow_path->GetEntryLabel());
+ break;
+ }
}
__ Bind(&done);
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 9c537a7371..6c76e27d35 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -211,6 +211,7 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator {
// the suspend call.
void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
void GenerateClassInitializationCheck(SlowPathCode* slow_path, Register class_reg);
+ void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, Register temp);
void HandleBitwiseOperation(HBinaryOperation* instruction);
void GenerateDivRemIntegral(HBinaryOperation* instruction);
void DivRemOneOrMinusOne(HBinaryOperation* instruction);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index c378c5b957..496d79d6c8 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -5716,6 +5716,26 @@ void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck(
// No need for memory fence, thanks to the x86-64 memory model.
}
+void InstructionCodeGeneratorX86_64::GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check,
+ CpuRegister temp) {
+ uint32_t path_to_root = check->GetBitstringPathToRoot();
+ uint32_t mask = check->GetBitstringMask();
+ DCHECK(IsPowerOfTwo(mask + 1));
+ size_t mask_bits = WhichPowerOf2(mask + 1);
+
+ if (mask_bits == 16u) {
+ // Compare the bitstring in memory.
+ __ cmpw(Address(temp, mirror::Class::StatusOffset()), Immediate(path_to_root));
+ } else {
+ // /* uint32_t */ temp = temp->status_
+ __ movl(temp, Address(temp, mirror::Class::StatusOffset()));
+ // Compare the bitstring bits using SUB.
+ __ subl(temp, Immediate(path_to_root));
+ // Shift out bits that do not contribute to the comparison.
+ __ shll(temp, Immediate(32u - mask_bits));
+ }
+}
+
HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind(
HLoadClass::LoadKind desired_class_load_kind) {
switch (desired_class_load_kind) {
@@ -6082,6 +6102,8 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) {
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCallOnSlowPath;
break;
+ case TypeCheckKind::kBitstringCheck:
+ break;
}
LocationSummary* locations =
@@ -6090,7 +6112,13 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) {
locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
}
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::Any());
+ if (type_check_kind == TypeCheckKind::kBitstringCheck) {
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
+ locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
// Note that TypeCheckSlowPathX86_64 uses this "out" register too.
locations->SetOut(Location::RequiresRegister());
// When read barriers are enabled, we need a temporary register for
@@ -6319,6 +6347,27 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {
}
break;
}
+
+ case TypeCheckKind::kBitstringCheck: {
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(instruction,
+ out_loc,
+ obj_loc,
+ class_offset,
+ kWithoutReadBarrier);
+
+ GenerateBitstringTypeCheckCompare(instruction, out);
+ if (zero.IsLinked()) {
+ __ j(kNotEqual, &zero);
+ __ movl(out, Immediate(1));
+ __ jmp(&done);
+ } else {
+ __ setcc(kEqual, out);
+ // setcc only sets the low byte.
+ __ andl(out, Immediate(1));
+ }
+ break;
+ }
}
if (zero.IsLinked()) {
@@ -6345,6 +6394,10 @@ void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) {
// Require a register for the interface check since there is a loop that compares the class to
// a memory address.
locations->SetInAt(1, Location::RequiresRegister());
+ } else if (type_check_kind == TypeCheckKind::kBitstringCheck) {
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
+ locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
} else {
locations->SetInAt(1, Location::Any());
}
@@ -6531,7 +6584,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
break;
}
- case TypeCheckKind::kInterfaceCheck:
+ case TypeCheckKind::kInterfaceCheck: {
// Fast path for the interface check. Try to avoid read barriers to improve the fast path.
// We can not get false positives by doing this.
// /* HeapReference<Class> */ temp = obj->klass_
@@ -6567,6 +6620,20 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
// If `cls` was poisoned above, unpoison it.
__ MaybeUnpoisonHeapReference(cls.AsRegister<CpuRegister>());
break;
+ }
+
+ case TypeCheckKind::kBitstringCheck: {
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(instruction,
+ temp_loc,
+ obj_loc,
+ class_offset,
+ kWithoutReadBarrier);
+
+ GenerateBitstringTypeCheckCompare(instruction, temp);
+ __ j(kNotEqual, type_check_slow_path->GetEntryLabel());
+ break;
+ }
}
if (done.IsLinked()) {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index e8d1efe702..9a4c53b524 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -208,6 +208,7 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator {
// the suspend call.
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void GenerateClassInitializationCheck(SlowPathCode* slow_path, CpuRegister class_reg);
+ void GenerateBitstringTypeCheckCompare(HTypeCheckInstruction* check, CpuRegister temp);
void HandleBitwiseOperation(HBinaryOperation* operation);
void GenerateRemFP(HRem* rem);
void DivRemOneOrMinusOne(HBinaryOperation* instruction);
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index c88baa8610..fbcbe3608e 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -25,6 +25,11 @@
#include "base/bit_vector-inl.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
+#include "handle.h"
+#include "mirror/class.h"
+#include "obj_ptr-inl.h"
+#include "scoped_thread_state_change-inl.h"
+#include "subtype_check.h"
namespace art {
@@ -548,30 +553,85 @@ void GraphChecker::VisitReturnVoid(HReturnVoid* ret) {
}
}
-void GraphChecker::VisitCheckCast(HCheckCast* check) {
- VisitInstruction(check);
- HInstruction* input = check->InputAt(1);
- if (!input->IsLoadClass()) {
- AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.",
+void GraphChecker::CheckTypeCheckBitstringInput(HTypeCheckInstruction* check,
+ size_t input_pos,
+ bool check_value,
+ uint32_t expected_value,
+ const char* name) {
+ if (!check->InputAt(input_pos)->IsIntConstant()) {
+ AddError(StringPrintf("%s:%d (bitstring) expects a HIntConstant input %zu (%s), not %s:%d.",
check->DebugName(),
check->GetId(),
- input->DebugName(),
- input->GetId()));
+ input_pos,
+ name,
+ check->InputAt(2)->DebugName(),
+ check->InputAt(2)->GetId()));
+ } else if (check_value) {
+ uint32_t actual_value =
+ static_cast<uint32_t>(check->InputAt(input_pos)->AsIntConstant()->GetValue());
+ if (actual_value != expected_value) {
+ AddError(StringPrintf("%s:%d (bitstring) has %s 0x%x, not 0x%x as expected.",
+ check->DebugName(),
+ check->GetId(),
+ name,
+ actual_value,
+ expected_value));
+ }
}
}
-void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) {
- VisitInstruction(instruction);
- HInstruction* input = instruction->InputAt(1);
- if (!input->IsLoadClass()) {
- AddError(StringPrintf("%s:%d expects a HLoadClass as second input, not %s:%d.",
- instruction->DebugName(),
- instruction->GetId(),
- input->DebugName(),
- input->GetId()));
+void GraphChecker::HandleTypeCheckInstruction(HTypeCheckInstruction* check) {
+ VisitInstruction(check);
+ HInstruction* input = check->InputAt(1);
+ if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
+ if (!input->IsNullConstant()) {
+ AddError(StringPrintf("%s:%d (bitstring) expects a HNullConstant as second input, not %s:%d.",
+ check->DebugName(),
+ check->GetId(),
+ input->DebugName(),
+ input->GetId()));
+ }
+ bool check_values = false;
+ BitString::StorageType expected_path_to_root = 0u;
+ BitString::StorageType expected_mask = 0u;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ ObjPtr<mirror::Class> klass = check->GetClass().Get();
+ MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
+ SubtypeCheckInfo::State state = SubtypeCheck<ObjPtr<mirror::Class>>::GetState(klass);
+ if (state == SubtypeCheckInfo::kAssigned) {
+ expected_path_to_root =
+ SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootForTarget(klass);
+ expected_mask = SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootMask(klass);
+ check_values = true;
+ } else {
+ AddError(StringPrintf("%s:%d (bitstring) references a class with unassigned bitstring.",
+ check->DebugName(),
+ check->GetId()));
+ }
+ }
+ CheckTypeCheckBitstringInput(
+ check, /* input_pos */ 2, check_values, expected_path_to_root, "path_to_root");
+ CheckTypeCheckBitstringInput(check, /* input_pos */ 3, check_values, expected_mask, "mask");
+ } else {
+ if (!input->IsLoadClass()) {
+ AddError(StringPrintf("%s:%d (classic) expects a HLoadClass as second input, not %s:%d.",
+ check->DebugName(),
+ check->GetId(),
+ input->DebugName(),
+ input->GetId()));
+ }
}
}
+void GraphChecker::VisitCheckCast(HCheckCast* check) {
+ HandleTypeCheckInstruction(check);
+}
+
+void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) {
+ HandleTypeCheckInstruction(instruction);
+}
+
void GraphChecker::HandleLoop(HBasicBlock* loop_header) {
int id = loop_header->GetBlockId();
HLoopInformation* loop_information = loop_header->GetLoopInformation();
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 0f0b49d240..dbedc40518 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -71,6 +71,12 @@ class GraphChecker : public HGraphDelegateVisitor {
void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE;
void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
+ void CheckTypeCheckBitstringInput(HTypeCheckInstruction* check,
+ size_t input_pos,
+ bool check_value,
+ uint32_t expected_value,
+ const char* name);
+ void HandleTypeCheckInstruction(HTypeCheckInstruction* instruction);
void HandleLoop(HBasicBlock* loop_header);
void HandleBooleanInput(HInstruction* instruction, size_t input_index);
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 5ff31cead5..54d4644580 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -390,16 +390,23 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
StartAttributeStream("load_kind") << load_string->GetLoadKind();
}
- void VisitCheckCast(HCheckCast* check_cast) OVERRIDE {
- StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind();
+ void HandleTypeCheckInstruction(HTypeCheckInstruction* check) {
+ StartAttributeStream("check_kind") << check->GetTypeCheckKind();
StartAttributeStream("must_do_null_check") << std::boolalpha
- << check_cast->MustDoNullCheck() << std::noboolalpha;
+ << check->MustDoNullCheck() << std::noboolalpha;
+ if (check->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
+ StartAttributeStream("path_to_root") << std::hex
+ << "0x" << check->GetBitstringPathToRoot() << std::dec;
+ StartAttributeStream("mask") << std::hex << "0x" << check->GetBitstringMask() << std::dec;
+ }
+ }
+
+ void VisitCheckCast(HCheckCast* check_cast) OVERRIDE {
+ HandleTypeCheckInstruction(check_cast);
}
void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE {
- StartAttributeStream("check_kind") << instance_of->GetTypeCheckKind();
- StartAttributeStream("must_do_null_check") << std::boolalpha
- << instance_of->MustDoNullCheck() << std::noboolalpha;
+ HandleTypeCheckInstruction(instance_of);
}
void VisitArrayLength(HArrayLength* array_length) OVERRIDE {
@@ -576,6 +583,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
}
StartAttributeStream() << input_list;
}
+ if (instruction->GetDexPc() != kNoDexPc) {
+ StartAttributeStream("dex_pc") << instruction->GetDexPc();
+ } else {
+ StartAttributeStream("dex_pc") << "n/a";
+ }
instruction->Accept(this);
if (instruction->HasEnvironment()) {
StringList envs;
@@ -641,20 +653,32 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
<< std::boolalpha << loop_info->IsIrreducible() << std::noboolalpha;
}
+ // For the builder and the inliner, we want to add extra information on HInstructions
+ // that have reference types, and also HInstanceOf/HCheckcast.
if ((IsPass(HGraphBuilder::kBuilderPassName)
|| IsPass(HInliner::kInlinerPassName))
- && (instruction->GetType() == DataType::Type::kReference)) {
- ReferenceTypeInfo info = instruction->IsLoadClass()
- ? instruction->AsLoadClass()->GetLoadedClassRTI()
- : instruction->GetReferenceTypeInfo();
+ && (instruction->GetType() == DataType::Type::kReference ||
+ instruction->IsInstanceOf() ||
+ instruction->IsCheckCast())) {
+ ReferenceTypeInfo info = (instruction->GetType() == DataType::Type::kReference)
+ ? instruction->IsLoadClass()
+ ? instruction->AsLoadClass()->GetLoadedClassRTI()
+ : instruction->GetReferenceTypeInfo()
+ : instruction->IsInstanceOf()
+ ? instruction->AsInstanceOf()->GetTargetClassRTI()
+ : instruction->AsCheckCast()->GetTargetClassRTI();
ScopedObjectAccess soa(Thread::Current());
if (info.IsValid()) {
StartAttributeStream("klass")
<< mirror::Class::PrettyDescriptor(info.GetTypeHandle().Get());
- StartAttributeStream("can_be_null")
- << std::boolalpha << instruction->CanBeNull() << std::noboolalpha;
+ if (instruction->GetType() == DataType::Type::kReference) {
+ StartAttributeStream("can_be_null")
+ << std::boolalpha << instruction->CanBeNull() << std::noboolalpha;
+ }
StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
- } else if (instruction->IsLoadClass()) {
+ } else if (instruction->IsLoadClass() ||
+ instruction->IsInstanceOf() ||
+ instruction->IsCheckCast()) {
StartAttributeStream("klass") << "unresolved";
} else {
// The NullConstant may be added to the graph during other passes that happen between
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 0a310ca940..55eca2316a 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -352,13 +352,15 @@ void InductionVarRange::Replace(HInstruction* instruction,
}
bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* trip_count) const {
- HInductionVarAnalysis::InductionInfo *trip =
- induction_analysis_->LookupInfo(loop, GetLoopControl(loop));
- if (trip != nullptr && !IsUnsafeTripCount(trip)) {
- IsConstant(trip->op_a, kExact, trip_count);
- return true;
- }
- return false;
+ bool is_constant_unused = false;
+ return CheckForFiniteAndConstantProps(loop, &is_constant_unused, trip_count);
+}
+
+bool InductionVarRange::HasKnownTripCount(HLoopInformation* loop,
+ /*out*/ int64_t* trip_count) const {
+ bool is_constant = false;
+ CheckForFiniteAndConstantProps(loop, &is_constant, trip_count);
+ return is_constant;
}
bool InductionVarRange::IsUnitStride(HInstruction* context,
@@ -417,6 +419,18 @@ HInstruction* InductionVarRange::GenerateTripCount(HLoopInformation* loop,
// Private class methods.
//
+bool InductionVarRange::CheckForFiniteAndConstantProps(HLoopInformation* loop,
+ /*out*/ bool* is_constant,
+ /*out*/ int64_t* trip_count) const {
+ HInductionVarAnalysis::InductionInfo *trip =
+ induction_analysis_->LookupInfo(loop, GetLoopControl(loop));
+ if (trip != nullptr && !IsUnsafeTripCount(trip)) {
+ *is_constant = IsConstant(trip->op_a, kExact, trip_count);
+ return true;
+ }
+ return false;
+}
+
bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info,
ConstantRequest request,
/*out*/ int64_t* value) const {
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 0b980f596a..906dc6bb7b 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -161,9 +161,15 @@ class InductionVarRange {
}
/**
- * Checks if header logic of a loop terminates. Sets trip-count tc if known.
+ * Checks if header logic of a loop terminates. If trip count is known sets 'trip_count' to its
+ * value.
*/
- bool IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) const;
+ bool IsFinite(HLoopInformation* loop, /*out*/ int64_t* trip_count) const;
+
+ /**
+ * Checks if a trip count is known for the loop and sets 'trip_count' to its value in this case.
+ */
+ bool HasKnownTripCount(HLoopInformation* loop, /*out*/ int64_t* trip_count) const;
/**
* Checks if the given instruction is a unit stride induction inside the closest enveloping
@@ -194,6 +200,14 @@ class InductionVarRange {
};
/**
+ * Checks if header logic of a loop terminates. If trip count is known (constant) sets
+ * 'is_constant' to true and 'trip_count' to the trip count value.
+ */
+ bool CheckForFiniteAndConstantProps(HLoopInformation* loop,
+ /*out*/ bool* is_constant,
+ /*out*/ int64_t* trip_count) const;
+
+ /**
* Returns true if exact or upper/lower bound on the given induction
* information is known as a 64-bit constant, which is returned in value.
*/
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index c7aef3779d..9647dd5d41 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1815,29 +1815,6 @@ void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object,
}
}
-static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (cls == nullptr) {
- return TypeCheckKind::kUnresolvedCheck;
- } else if (cls->IsInterface()) {
- return TypeCheckKind::kInterfaceCheck;
- } else if (cls->IsArrayClass()) {
- if (cls->GetComponentType()->IsObjectClass()) {
- return TypeCheckKind::kArrayObjectCheck;
- } else if (cls->CannotBeAssignedFromOtherTypes()) {
- return TypeCheckKind::kExactCheck;
- } else {
- return TypeCheckKind::kArrayCheck;
- }
- } else if (cls->IsFinal()) {
- return TypeCheckKind::kExactCheck;
- } else if (cls->IsAbstract()) {
- return TypeCheckKind::kAbstractClassCheck;
- } else {
- return TypeCheckKind::kClassHierarchyCheck;
- }
-}
-
void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc) {
HLoadString* load_string =
new (allocator_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc);
@@ -1852,22 +1829,8 @@ void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_
HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) {
ScopedObjectAccess soa(Thread::Current());
const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
- 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_));
-
- bool needs_access_check = true;
- if (klass != nullptr) {
- if (klass->IsPublic()) {
- needs_access_check = false;
- } else {
- ObjPtr<mirror::Class> compiling_class = GetCompilingClass();
- if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) {
- needs_access_check = false;
- }
- }
- }
-
+ Handle<mirror::Class> klass = ResolveClass(soa, type_index);
+ bool needs_access_check = LoadClassNeedsAccessCheck(klass);
return BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check);
}
@@ -1912,25 +1875,83 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index,
return load_class;
}
+Handle<mirror::Class> HInstructionBuilder::ResolveClass(ScopedObjectAccess& soa,
+ dex::TypeIndex type_index) {
+ Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
+ ObjPtr<mirror::Class> klass = compiler_driver_->ResolveClass(
+ soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_);
+ // TODO: Avoid creating excessive handles if the method references the same class repeatedly.
+ // (Use a map on the local_allocator_.)
+ return handles_->NewHandle(klass);
+}
+
+bool HInstructionBuilder::LoadClassNeedsAccessCheck(Handle<mirror::Class> klass) {
+ if (klass == nullptr) {
+ return true;
+ } else if (klass->IsPublic()) {
+ return false;
+ } else {
+ ObjPtr<mirror::Class> compiling_class = GetCompilingClass();
+ return compiling_class == nullptr || !compiling_class->CanAccess(klass.Get());
+ }
+}
+
void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction,
uint8_t destination,
uint8_t reference,
dex::TypeIndex type_index,
uint32_t dex_pc) {
HInstruction* object = LoadLocal(reference, DataType::Type::kReference);
- HLoadClass* cls = BuildLoadClass(type_index, dex_pc);
ScopedObjectAccess soa(Thread::Current());
- TypeCheckKind check_kind = ComputeTypeCheckKind(cls->GetClass());
+ const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
+ Handle<mirror::Class> klass = ResolveClass(soa, type_index);
+ bool needs_access_check = LoadClassNeedsAccessCheck(klass);
+ TypeCheckKind check_kind = HSharpening::ComputeTypeCheckKind(
+ klass.Get(), code_generator_, compiler_driver_, needs_access_check);
+
+ HInstruction* class_or_null = nullptr;
+ HIntConstant* bitstring_path_to_root = nullptr;
+ HIntConstant* bitstring_mask = nullptr;
+ if (check_kind == TypeCheckKind::kBitstringCheck) {
+ // TODO: Allow using the bitstring check also if we need an access check.
+ DCHECK(!needs_access_check);
+ class_or_null = graph_->GetNullConstant(dex_pc);
+ MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
+ uint32_t path_to_root =
+ SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootForTarget(klass.Get());
+ uint32_t mask = SubtypeCheck<ObjPtr<mirror::Class>>::GetEncodedPathToRootMask(klass.Get());
+ bitstring_path_to_root = graph_->GetIntConstant(static_cast<int32_t>(path_to_root), dex_pc);
+ bitstring_mask = graph_->GetIntConstant(static_cast<int32_t>(mask), dex_pc);
+ } else {
+ class_or_null = BuildLoadClass(type_index, dex_file, klass, dex_pc, needs_access_check);
+ }
+ DCHECK(class_or_null != nullptr);
+
if (instruction.Opcode() == Instruction::INSTANCE_OF) {
- AppendInstruction(new (allocator_) HInstanceOf(object, cls, check_kind, dex_pc));
+ AppendInstruction(new (allocator_) HInstanceOf(object,
+ class_or_null,
+ check_kind,
+ klass,
+ dex_pc,
+ allocator_,
+ bitstring_path_to_root,
+ bitstring_mask));
UpdateLocal(destination, current_block_->GetLastInstruction());
} else {
DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);
// We emit a CheckCast followed by a BoundType. CheckCast is a statement
// which may throw. If it succeeds BoundType sets the new type of `object`
// for all subsequent uses.
- AppendInstruction(new (allocator_) HCheckCast(object, cls, check_kind, dex_pc));
+ AppendInstruction(
+ new (allocator_) HCheckCast(object,
+ class_or_null,
+ check_kind,
+ klass,
+ dex_pc,
+ allocator_,
+ bitstring_path_to_root,
+ bitstring_mask));
AppendInstruction(new (allocator_) HBoundType(object, dex_pc));
UpdateLocal(reference, current_block_->GetLastInstruction());
}
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 4428c53277..f78829232d 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -39,6 +39,7 @@ class DexCompilationUnit;
class HBasicBlockBuilder;
class Instruction;
class OptimizingCompilerStats;
+class ScopedObjectAccess;
class SsaBuilder;
class VariableSizedHandleScope;
@@ -232,6 +233,12 @@ class HInstructionBuilder : public ValueObject {
bool needs_access_check)
REQUIRES_SHARED(Locks::mutator_lock_);
+ Handle<mirror::Class> ResolveClass(ScopedObjectAccess& soa, dex::TypeIndex type_index)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ bool LoadClassNeedsAccessCheck(Handle<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Returns the outer-most compiling method's class.
ObjPtr<mirror::Class> GetOutermostCompilingClass() const;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 0b2297d157..676fe6bcb7 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -579,7 +579,9 @@ bool InstructionSimplifierVisitor::CanEnsureNotNullAt(HInstruction* input, HInst
// Returns whether doing a type test between the class of `object` against `klass` has
// a statically known outcome. The result of the test is stored in `outcome`.
-static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) {
+static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti,
+ HInstruction* object,
+ /*out*/bool* outcome) {
DCHECK(!object->IsNullConstant()) << "Null constants should be special cased";
ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo();
ScopedObjectAccess soa(Thread::Current());
@@ -589,7 +591,6 @@ static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bo
return false;
}
- ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI();
if (!class_rti.IsValid()) {
// Happens when the loaded class is unresolved.
return false;
@@ -614,8 +615,8 @@ static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bo
void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
HInstruction* object = check_cast->InputAt(0);
- HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
- if (load_class->NeedsAccessCheck()) {
+ if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck &&
+ check_cast->GetTargetClass()->NeedsAccessCheck()) {
// If we need to perform an access check we cannot remove the instruction.
return;
}
@@ -633,15 +634,18 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
// Note: The `outcome` is initialized to please valgrind - the compiler can reorder
// the return value check with the `outcome` check, b/27651442 .
bool outcome = false;
- if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) {
+ if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) {
if (outcome) {
check_cast->GetBlock()->RemoveInstruction(check_cast);
MaybeRecordStat(stats_, MethodCompilationStat::kRemovedCheckedCast);
- if (!load_class->HasUses()) {
- // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
- // However, here we know that it cannot because the checkcast was successfull, hence
- // the class was already loaded.
- load_class->GetBlock()->RemoveInstruction(load_class);
+ if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) {
+ HLoadClass* load_class = check_cast->GetTargetClass();
+ if (!load_class->HasUses()) {
+ // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
+ // However, here we know that it cannot because the checkcast was successfull, hence
+ // the class was already loaded.
+ load_class->GetBlock()->RemoveInstruction(load_class);
+ }
}
} else {
// Don't do anything for exceptional cases for now. Ideally we should remove
@@ -652,8 +656,8 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
HInstruction* object = instruction->InputAt(0);
- HLoadClass* load_class = instruction->InputAt(1)->AsLoadClass();
- if (load_class->NeedsAccessCheck()) {
+ if (instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck &&
+ instruction->GetTargetClass()->NeedsAccessCheck()) {
// If we need to perform an access check we cannot remove the instruction.
return;
}
@@ -676,7 +680,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
// Note: The `outcome` is initialized to please valgrind - the compiler can reorder
// the return value check with the `outcome` check, b/27651442 .
bool outcome = false;
- if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) {
+ if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) {
MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf);
if (outcome && can_be_null) {
// Type test will succeed, we just need a null test.
@@ -689,11 +693,14 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
}
RecordSimplification();
instruction->GetBlock()->RemoveInstruction(instruction);
- if (outcome && !load_class->HasUses()) {
- // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
- // However, here we know that it cannot because the instanceof check was successfull, hence
- // the class was already loaded.
- load_class->GetBlock()->RemoveInstruction(load_class);
+ if (outcome && instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) {
+ HLoadClass* load_class = instruction->GetTargetClass();
+ if (!load_class->HasUses()) {
+ // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
+ // However, here we know that it cannot because the instanceof check was successfull, hence
+ // the class was already loaded.
+ load_class->GetBlock()->RemoveInstruction(load_class);
+ }
}
}
}
diff --git a/compiler/optimizing/loop_analysis.cc b/compiler/optimizing/loop_analysis.cc
new file mode 100644
index 0000000000..cd3bdaf016
--- /dev/null
+++ b/compiler/optimizing/loop_analysis.cc
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2018 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 "loop_analysis.h"
+
+namespace art {
+
+void LoopAnalysis::CalculateLoopBasicProperties(HLoopInformation* loop_info,
+ LoopAnalysisInfo* analysis_results) {
+ for (HBlocksInLoopIterator block_it(*loop_info);
+ !block_it.Done();
+ block_it.Advance()) {
+ HBasicBlock* block = block_it.Current();
+
+ for (HBasicBlock* successor : block->GetSuccessors()) {
+ if (!loop_info->Contains(*successor)) {
+ analysis_results->exits_num_++;
+ }
+ }
+
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (MakesScalarUnrollingNonBeneficial(instruction)) {
+ analysis_results->has_instructions_preventing_scalar_unrolling_ = true;
+ }
+ analysis_results->instr_num_++;
+ }
+ analysis_results->bb_num_++;
+ }
+}
+
+class Arm64LoopHelper : public ArchDefaultLoopHelper {
+ public:
+ // Scalar loop unrolling parameters and heuristics.
+ //
+ // Maximum possible unrolling factor.
+ static constexpr uint32_t kArm64ScalarMaxUnrollFactor = 2;
+ // Loop's maximum instruction count. Loops with higher count will not be peeled/unrolled.
+ static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeInstr = 40;
+ // Loop's maximum basic block count. Loops with higher count will not be peeled/unrolled.
+ static constexpr uint32_t kArm64ScalarHeuristicMaxBodySizeBlocks = 8;
+
+ // SIMD loop unrolling parameters and heuristics.
+ //
+ // Maximum possible unrolling factor.
+ static constexpr uint32_t kArm64SimdMaxUnrollFactor = 8;
+ // Loop's maximum instruction count. Loops with higher count will not be unrolled.
+ static constexpr uint32_t kArm64SimdHeuristicMaxBodySizeInstr = 50;
+
+ bool IsLoopTooBigForScalarUnrolling(LoopAnalysisInfo* loop_analysis_info) const OVERRIDE {
+ size_t instr_num = loop_analysis_info->GetNumberOfInstructions();
+ size_t bb_num = loop_analysis_info->GetNumberOfBasicBlocks();
+ return (instr_num >= kArm64ScalarHeuristicMaxBodySizeInstr ||
+ bb_num >= kArm64ScalarHeuristicMaxBodySizeBlocks);
+ }
+
+ uint32_t GetScalarUnrollingFactor(HLoopInformation* loop_info ATTRIBUTE_UNUSED,
+ uint64_t trip_count) const OVERRIDE {
+ uint32_t desired_unrolling_factor = kArm64ScalarMaxUnrollFactor;
+ if (trip_count < desired_unrolling_factor || trip_count % desired_unrolling_factor != 0) {
+ return kNoUnrollingFactor;
+ }
+
+ return desired_unrolling_factor;
+ }
+
+ uint32_t GetSIMDUnrollingFactor(HBasicBlock* block,
+ int64_t trip_count,
+ uint32_t max_peel,
+ uint32_t vector_length) const OVERRIDE {
+ // Don't unroll with insufficient iterations.
+ // TODO: Unroll loops with unknown trip count.
+ DCHECK_NE(vector_length, 0u);
+ if (trip_count < (2 * vector_length + max_peel)) {
+ return kNoUnrollingFactor;
+ }
+ // Don't unroll for large loop body size.
+ uint32_t instruction_count = block->GetInstructions().CountSize();
+ if (instruction_count >= kArm64SimdHeuristicMaxBodySizeInstr) {
+ return kNoUnrollingFactor;
+ }
+ // Find a beneficial unroll factor with the following restrictions:
+ // - At least one iteration of the transformed loop should be executed.
+ // - The loop body shouldn't be "too big" (heuristic).
+
+ uint32_t uf1 = kArm64SimdHeuristicMaxBodySizeInstr / instruction_count;
+ uint32_t uf2 = (trip_count - max_peel) / vector_length;
+ uint32_t unroll_factor =
+ TruncToPowerOfTwo(std::min({uf1, uf2, kArm64SimdMaxUnrollFactor}));
+ DCHECK_GE(unroll_factor, 1u);
+ return unroll_factor;
+ }
+};
+
+ArchDefaultLoopHelper* ArchDefaultLoopHelper::Create(InstructionSet isa,
+ ArenaAllocator* allocator) {
+ switch (isa) {
+ case InstructionSet::kArm64: {
+ return new (allocator) Arm64LoopHelper;
+ }
+ default: {
+ return new (allocator) ArchDefaultLoopHelper;
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/loop_analysis.h b/compiler/optimizing/loop_analysis.h
new file mode 100644
index 0000000000..bad406f10b
--- /dev/null
+++ b/compiler/optimizing/loop_analysis.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 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_LOOP_ANALYSIS_H_
+#define ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_
+
+#include "nodes.h"
+
+namespace art {
+
+class LoopAnalysis;
+
+// No loop unrolling factor (just one copy of the loop-body).
+static constexpr uint32_t kNoUnrollingFactor = 1;
+
+// Class to hold cached information on properties of the loop.
+class LoopAnalysisInfo : public ValueObject {
+ public:
+ explicit LoopAnalysisInfo(HLoopInformation* loop_info)
+ : bb_num_(0),
+ instr_num_(0),
+ exits_num_(0),
+ has_instructions_preventing_scalar_unrolling_(false),
+ loop_info_(loop_info) {}
+
+ size_t GetNumberOfBasicBlocks() const { return bb_num_; }
+ size_t GetNumberOfInstructions() const { return instr_num_; }
+ size_t GetNumberOfExits() const { return exits_num_; }
+
+ bool HasInstructionsPreventingScalarUnrolling() const {
+ return has_instructions_preventing_scalar_unrolling_;
+ }
+
+ const HLoopInformation* GetLoopInfo() const { return loop_info_; }
+
+ private:
+ // Number of basic blocks in the loop body.
+ size_t bb_num_;
+ // Number of instructions in the loop body.
+ size_t instr_num_;
+ // Number of loop's exits.
+ size_t exits_num_;
+ // Whether the loop has instructions which make scalar loop unrolling non-beneficial.
+ bool has_instructions_preventing_scalar_unrolling_;
+
+ // Corresponding HLoopInformation.
+ const HLoopInformation* loop_info_;
+
+ friend class LoopAnalysis;
+};
+
+// Placeholder class for methods and routines used to analyse loops, calculate loop properties
+// and characteristics.
+class LoopAnalysis : public ValueObject {
+ public:
+ // Calculates loops basic properties like body size, exits number, etc. and fills
+ // 'analysis_results' with this information.
+ static void CalculateLoopBasicProperties(HLoopInformation* loop_info,
+ LoopAnalysisInfo* analysis_results);
+
+ private:
+ // Returns whether an instruction makes scalar loop unrolling non-beneficial.
+ //
+ // If in the loop body we have a dex/runtime call then its contribution to the whole
+ // loop performance will probably prevail. So unrolling optimization will not bring
+ // any noticeable performance improvement however will increase the code size.
+ static bool MakesScalarUnrollingNonBeneficial(HInstruction* instruction) {
+ return (instruction->IsNewArray() ||
+ instruction->IsNewInstance() ||
+ instruction->IsUnresolvedInstanceFieldGet() ||
+ instruction->IsUnresolvedInstanceFieldSet() ||
+ instruction->IsUnresolvedStaticFieldGet() ||
+ instruction->IsUnresolvedStaticFieldSet() ||
+ // TODO: Unroll loops with intrinsified invokes.
+ instruction->IsInvoke() ||
+ // TODO: Unroll loops with ClinitChecks.
+ instruction->IsClinitCheck());
+ }
+};
+
+//
+// Helper class which holds target-dependent methods and constants needed for loop optimizations.
+//
+// To support peeling/unrolling for a new architecture one needs to create new helper class,
+// inherit it from this and add implementation for the following methods.
+//
+class ArchDefaultLoopHelper : public ArenaObject<kArenaAllocOptimization> {
+ public:
+ virtual ~ArchDefaultLoopHelper() {}
+
+ // Creates an instance of specialised helper for the target or default helper if the target
+ // doesn't support loop peeling and unrolling.
+ static ArchDefaultLoopHelper* Create(InstructionSet isa, ArenaAllocator* allocator);
+
+ // Returns whether the loop is too big for loop unrolling by checking its total number of
+ // basic blocks and instructions.
+ //
+ // If the loop body has too many instructions then unrolling optimization will not bring
+ // any noticeable performance improvement however will increase the code size.
+ //
+ // Returns 'true' by default, should be overridden by particular target loop helper.
+ virtual bool IsLoopTooBigForScalarUnrolling(
+ LoopAnalysisInfo* loop_analysis_info ATTRIBUTE_UNUSED) const { return true; }
+
+ // Returns optimal scalar unrolling factor for the loop.
+ //
+ // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper.
+ virtual uint32_t GetScalarUnrollingFactor(HLoopInformation* loop_info ATTRIBUTE_UNUSED,
+ uint64_t trip_count ATTRIBUTE_UNUSED) const {
+ return kNoUnrollingFactor;
+ }
+
+ // Returns optimal SIMD unrolling factor for the loop.
+ //
+ // Returns kNoUnrollingFactor by default, should be overridden by particular target loop helper.
+ virtual uint32_t GetSIMDUnrollingFactor(HBasicBlock* block ATTRIBUTE_UNUSED,
+ int64_t trip_count ATTRIBUTE_UNUSED,
+ uint32_t max_peel ATTRIBUTE_UNUSED,
+ uint32_t vector_length ATTRIBUTE_UNUSED) const {
+ return kNoUnrollingFactor;
+ }
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 758aca2d0c..71e24de141 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -33,8 +33,8 @@ namespace art {
// Enables vectorization (SIMDization) in the loop optimizer.
static constexpr bool kEnableVectorization = true;
-// No loop unrolling factor (just one copy of the loop-body).
-static constexpr uint32_t kNoUnrollingFactor = 1;
+// Enables scalar loop unrolling in the loop optimizer.
+static constexpr bool kEnableScalarUnrolling = false;
//
// Static helpers.
@@ -227,6 +227,7 @@ static bool IsNarrowerOperands(HInstruction* a,
/*out*/ HInstruction** r,
/*out*/ HInstruction** s,
/*out*/ bool* is_unsigned) {
+ DCHECK(a != nullptr && b != nullptr);
// Look for a matching sign extension.
DataType::Type stype = HVecOperation::ToSignedType(type);
if (IsSignExtensionAndGet(a, stype, r) && IsSignExtensionAndGet(b, stype, s)) {
@@ -247,6 +248,7 @@ static bool IsNarrowerOperand(HInstruction* a,
DataType::Type type,
/*out*/ HInstruction** r,
/*out*/ bool* is_unsigned) {
+ DCHECK(a != nullptr);
// Look for a matching sign extension.
DataType::Type stype = HVecOperation::ToSignedType(type);
if (IsSignExtensionAndGet(a, stype, r)) {
@@ -270,20 +272,28 @@ static uint32_t GetOtherVL(DataType::Type other_type, DataType::Type vector_type
return vl >> (DataType::SizeShift(other_type) - DataType::SizeShift(vector_type));
}
-// Detect up to two instructions a and b, and an acccumulated constant c.
-static bool IsAddConstHelper(HInstruction* instruction,
- /*out*/ HInstruction** a,
- /*out*/ HInstruction** b,
- /*out*/ int64_t* c,
- int32_t depth) {
- static constexpr int32_t kMaxDepth = 8; // don't search too deep
+// Detect up to two added operands a and b and an acccumulated constant c.
+static bool IsAddConst(HInstruction* instruction,
+ /*out*/ HInstruction** a,
+ /*out*/ HInstruction** b,
+ /*out*/ int64_t* c,
+ int32_t depth = 8) { // don't search too deep
int64_t value = 0;
+ // Enter add/sub while still within reasonable depth.
+ if (depth > 0) {
+ if (instruction->IsAdd()) {
+ return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1) &&
+ IsAddConst(instruction->InputAt(1), a, b, c, depth - 1);
+ } else if (instruction->IsSub() &&
+ IsInt64AndGet(instruction->InputAt(1), &value)) {
+ *c -= value;
+ return IsAddConst(instruction->InputAt(0), a, b, c, depth - 1);
+ }
+ }
+ // Otherwise, deal with leaf nodes.
if (IsInt64AndGet(instruction, &value)) {
*c += value;
return true;
- } else if (instruction->IsAdd() && depth <= kMaxDepth) {
- return IsAddConstHelper(instruction->InputAt(0), a, b, c, depth + 1) &&
- IsAddConstHelper(instruction->InputAt(1), a, b, c, depth + 1);
} else if (*a == nullptr) {
*a = instruction;
return true;
@@ -291,42 +301,40 @@ static bool IsAddConstHelper(HInstruction* instruction,
*b = instruction;
return true;
}
- return false; // too many non-const operands
+ return false; // too many operands
}
-// Detect a + b + c for an optional constant c.
-static bool IsAddConst(HInstruction* instruction,
- /*out*/ HInstruction** a,
- /*out*/ HInstruction** b,
- /*out*/ int64_t* c) {
- if (instruction->IsAdd()) {
- // Try to find a + b and accumulated c.
- if (IsAddConstHelper(instruction->InputAt(0), a, b, c, /*depth*/ 0) &&
- IsAddConstHelper(instruction->InputAt(1), a, b, c, /*depth*/ 0) &&
- *b != nullptr) {
- return true;
+// Detect a + b + c with optional constant c.
+static bool IsAddConst2(HGraph* graph,
+ HInstruction* instruction,
+ /*out*/ HInstruction** a,
+ /*out*/ HInstruction** b,
+ /*out*/ int64_t* c) {
+ if (IsAddConst(instruction, a, b, c) && *a != nullptr) {
+ if (*b == nullptr) {
+ // Constant is usually already present, unless accumulated.
+ *b = graph->GetConstant(instruction->GetType(), (*c));
+ *c = 0;
}
- // Found a + b.
- *a = instruction->InputAt(0);
- *b = instruction->InputAt(1);
- *c = 0;
return true;
}
return false;
}
-// Detect a + c for constant c.
-static bool IsAddConst(HInstruction* instruction,
- /*out*/ HInstruction** a,
- /*out*/ int64_t* c) {
- if (instruction->IsAdd()) {
- if (IsInt64AndGet(instruction->InputAt(0), c)) {
- *a = instruction->InputAt(1);
- return true;
- } else if (IsInt64AndGet(instruction->InputAt(1), c)) {
- *a = instruction->InputAt(0);
- return true;
- }
+// Detect a direct a - b or a hidden a - (-c).
+static bool IsSubConst2(HGraph* graph,
+ HInstruction* instruction,
+ /*out*/ HInstruction** a,
+ /*out*/ HInstruction** b) {
+ int64_t c = 0;
+ if (instruction->IsSub()) {
+ *a = instruction->InputAt(0);
+ *b = instruction->InputAt(1);
+ return true;
+ } else if (IsAddConst(instruction, a, b, &c) && *a != nullptr && *b == nullptr) {
+ // Constant for the hidden subtraction.
+ *b = graph->GetConstant(instruction->GetType(), -c);
+ return true;
}
return false;
}
@@ -378,7 +386,8 @@ static bool CanSetRange(DataType::Type type,
}
// Accept various saturated addition forms.
-static bool IsSaturatedAdd(HInstruction* clippee,
+static bool IsSaturatedAdd(HInstruction* a,
+ HInstruction* b,
DataType::Type type,
int64_t lo,
int64_t hi,
@@ -390,8 +399,7 @@ static bool IsSaturatedAdd(HInstruction* clippee,
// Tighten the range for signed single clipping on constant.
if (!is_unsigned) {
int64_t c = 0;
- HInstruction* notused = nullptr;
- if (IsAddConst(clippee, &notused, &c)) {
+ if (IsInt64AndGet(a, &c) || IsInt64AndGet(b, &c)) {
// For c in proper range and narrower operand r:
// MIN(r + c, 127) c > 0
// or MAX(r + c, -128) c < 0 (and possibly redundant bound).
@@ -413,7 +421,7 @@ static bool IsSaturatedAdd(HInstruction* clippee,
}
// Accept various saturated subtraction forms.
-static bool IsSaturatedSub(HInstruction* clippee,
+static bool IsSaturatedSub(HInstruction* a,
DataType::Type type,
int64_t lo,
int64_t hi,
@@ -425,7 +433,7 @@ static bool IsSaturatedSub(HInstruction* clippee,
// Tighten the range for signed single clipping on constant.
if (!is_unsigned) {
int64_t c = 0;
- if (IsInt64AndGet(clippee->InputAt(0), /*out*/ &c)) {
+ if (IsInt64AndGet(a, /*out*/ &c)) {
// For c in proper range and narrower operand r:
// MIN(c - r, 127) c > 0
// or MAX(c - r, -128) c < 0 (and possibly redundant bound).
@@ -532,7 +540,11 @@ HLoopOptimization::HLoopOptimization(HGraph* graph,
vector_preheader_(nullptr),
vector_header_(nullptr),
vector_body_(nullptr),
- vector_index_(nullptr) {
+ vector_index_(nullptr),
+ arch_loop_helper_(ArchDefaultLoopHelper::Create(compiler_driver_ != nullptr
+ ? compiler_driver_->GetInstructionSet()
+ : InstructionSet::kNone,
+ global_allocator_)) {
}
void HLoopOptimization::Run() {
@@ -743,7 +755,7 @@ void HLoopOptimization::SimplifyBlocks(LoopNode* node) {
}
}
-bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
+bool HLoopOptimization::TryOptimizeInnerLoopFinite(LoopNode* node) {
HBasicBlock* header = node->loop_info->GetHeader();
HBasicBlock* preheader = node->loop_info->GetPreHeader();
// Ensure loop header logic is finite.
@@ -813,6 +825,83 @@ bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
return false;
}
+bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
+ return TryOptimizeInnerLoopFinite(node) ||
+ TryUnrollingForBranchPenaltyReduction(node);
+}
+
+void HLoopOptimization::PeelOrUnrollOnce(LoopNode* loop_node,
+ bool do_unrolling,
+ SuperblockCloner::HBasicBlockMap* bb_map,
+ SuperblockCloner::HInstructionMap* hir_map) {
+ // TODO: peel loop nests.
+ DCHECK(loop_node->inner == nullptr);
+
+ // Check that loop info is up-to-date.
+ HLoopInformation* loop_info = loop_node->loop_info;
+ HBasicBlock* header = loop_info->GetHeader();
+ DCHECK(loop_info == header->GetLoopInformation());
+
+ PeelUnrollHelper helper(loop_info, bb_map, hir_map);
+ DCHECK(helper.IsLoopClonable());
+ HBasicBlock* new_header = do_unrolling ? helper.DoUnrolling() : helper.DoPeeling();
+ DCHECK(header == new_header);
+ DCHECK(loop_info == new_header->GetLoopInformation());
+}
+
+//
+// Loop unrolling: generic part methods.
+//
+
+bool HLoopOptimization::TryUnrollingForBranchPenaltyReduction(LoopNode* loop_node) {
+ // Don't run peeling/unrolling if compiler_driver_ is nullptr (i.e., running under tests)
+ // as InstructionSet is needed.
+ if (!kEnableScalarUnrolling || compiler_driver_ == nullptr) {
+ return false;
+ }
+
+ HLoopInformation* loop_info = loop_node->loop_info;
+ int64_t trip_count = 0;
+ // Only unroll loops with a known tripcount.
+ if (!induction_range_.HasKnownTripCount(loop_info, &trip_count)) {
+ return false;
+ }
+
+ uint32_t unrolling_factor = arch_loop_helper_->GetScalarUnrollingFactor(loop_info, trip_count);
+ if (unrolling_factor == kNoUnrollingFactor) {
+ return false;
+ }
+
+ LoopAnalysisInfo loop_analysis_info(loop_info);
+ LoopAnalysis::CalculateLoopBasicProperties(loop_info, &loop_analysis_info);
+
+ // Check "IsLoopClonable" last as it can be time-consuming.
+ if (arch_loop_helper_->IsLoopTooBigForScalarUnrolling(&loop_analysis_info) ||
+ (loop_analysis_info.GetNumberOfExits() > 1) ||
+ loop_analysis_info.HasInstructionsPreventingScalarUnrolling() ||
+ !PeelUnrollHelper::IsLoopClonable(loop_info)) {
+ return false;
+ }
+
+ // TODO: support other unrolling factors.
+ DCHECK_EQ(unrolling_factor, 2u);
+
+ // Perform unrolling.
+ ArenaAllocator* arena = loop_info->GetHeader()->GetGraph()->GetAllocator();
+ SuperblockCloner::HBasicBlockMap bb_map(
+ std::less<HBasicBlock*>(), arena->Adapter(kArenaAllocSuperblockCloner));
+ SuperblockCloner::HInstructionMap hir_map(
+ std::less<HInstruction*>(), arena->Adapter(kArenaAllocSuperblockCloner));
+ PeelOrUnrollOnce(loop_node, /* unrolling */ true, &bb_map, &hir_map);
+
+ // Remove the redundant loop check after unrolling.
+ HIf* copy_hif = bb_map.Get(loop_info->GetHeader())->GetLastInstruction()->AsIf();
+ int32_t constant = loop_info->Contains(*copy_hif->IfTrueSuccessor()) ? 1 : 0;
+ copy_hif->ReplaceInput(graph_->GetIntConstant(constant), 0u);
+
+ return true;
+}
+
//
// Loop vectorization. The implementation is based on the book by Aart J.C. Bik:
// "The Software Vectorization Handbook. Applying Multimedia Extensions for Maximum Performance."
@@ -943,7 +1032,8 @@ void HLoopOptimization::Vectorize(LoopNode* node,
HBasicBlock* preheader = node->loop_info->GetPreHeader();
// Pick a loop unrolling factor for the vector loop.
- uint32_t unroll = GetUnrollingFactor(block, trip_count);
+ uint32_t unroll = arch_loop_helper_->GetSIMDUnrollingFactor(
+ block, trip_count, MaxNumberPeeled(), vector_length_);
uint32_t chunk = vector_length_ * unroll;
DCHECK(trip_count == 0 || (trip_count >= MaxNumberPeeled() + chunk));
@@ -1439,8 +1529,7 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node,
return false; // reject, unless all operands are same-extension narrower
}
// Accept MIN/MAX(x, y) for vectorizable operands.
- DCHECK(r != nullptr);
- DCHECK(s != nullptr);
+ DCHECK(r != nullptr && s != nullptr);
if (generate_code && vector_mode_ != kVector) { // de-idiom
r = opa;
s = opb;
@@ -1944,31 +2033,37 @@ bool HLoopOptimization::VectorizeSaturationIdiom(LoopNode* node,
instruction->GetType() != DataType::Type::kInt64) {
return false;
}
- // Clipped addition or subtraction?
+ // Clipped addition or subtraction on narrower operands? We will try both
+ // formats since, e.g., x+c can be interpreted as x+c and x-(-c), depending
+ // on what clipping values are used, to get most benefits.
int64_t lo = std::numeric_limits<int64_t>::min();
int64_t hi = std::numeric_limits<int64_t>::max();
HInstruction* clippee = FindClippee(instruction, &lo, &hi);
- bool is_add = true;
- if (clippee->IsAdd()) {
- is_add = true;
- } else if (clippee->IsSub()) {
- is_add = false;
- } else {
- return false; // clippee is not add/sub
- }
- // Addition or subtraction on narrower operands?
+ HInstruction* a = nullptr;
+ HInstruction* b = nullptr;
HInstruction* r = nullptr;
HInstruction* s = nullptr;
bool is_unsigned = false;
- if (IsNarrowerOperands(clippee->InputAt(0), clippee->InputAt(1), type, &r, &s, &is_unsigned) &&
- (is_add ? IsSaturatedAdd(clippee, type, lo, hi, is_unsigned)
- : IsSaturatedSub(clippee, type, lo, hi, is_unsigned))) {
- DCHECK(r != nullptr);
- DCHECK(s != nullptr);
+ bool is_add = true;
+ int64_t c = 0;
+ // First try for saturated addition.
+ if (IsAddConst2(graph_, clippee, /*out*/ &a, /*out*/ &b, /*out*/ &c) && c == 0 &&
+ IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) &&
+ IsSaturatedAdd(r, s, type, lo, hi, is_unsigned)) {
+ is_add = true;
} else {
- return false;
+ // Then try again for saturated subtraction.
+ a = b = r = s = nullptr;
+ if (IsSubConst2(graph_, clippee, /*out*/ &a, /*out*/ &b) &&
+ IsNarrowerOperands(a, b, type, &r, &s, &is_unsigned) &&
+ IsSaturatedSub(r, type, lo, hi, is_unsigned)) {
+ is_add = false;
+ } else {
+ return false;
+ }
}
// Accept saturation idiom for vectorizable operands.
+ DCHECK(r != nullptr && s != nullptr);
if (generate_code && vector_mode_ != kVector) { // de-idiom
r = instruction->InputAt(0);
s = instruction->InputAt(1);
@@ -2019,8 +2114,7 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node,
HInstruction* a = nullptr;
HInstruction* b = nullptr;
int64_t c = 0;
- if (IsAddConst(instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) {
- DCHECK(a != nullptr && b != nullptr);
+ if (IsAddConst2(graph_, instruction->InputAt(0), /*out*/ &a, /*out*/ &b, /*out*/ &c)) {
// Accept c == 1 (rounded) or c == 0 (not rounded).
bool is_rounded = false;
if (c == 1) {
@@ -2042,8 +2136,7 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node,
}
// Accept recognized halving add for vectorizable operands. Vectorized code uses the
// shorthand idiomatic operation. Sequential code uses the original scalar expressions.
- DCHECK(r != nullptr);
- DCHECK(s != nullptr);
+ DCHECK(r != nullptr && s != nullptr);
if (generate_code && vector_mode_ != kVector) { // de-idiom
r = instruction->InputAt(0);
s = instruction->InputAt(1);
@@ -2093,19 +2186,11 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node,
HInstruction* v = instruction->InputAt(1);
HInstruction* a = nullptr;
HInstruction* b = nullptr;
- if (v->GetType() == reduction_type && v->IsAbs()) {
- HInstruction* x = v->InputAt(0);
- if (x->GetType() == reduction_type) {
- int64_t c = 0;
- if (x->IsSub()) {
- a = x->InputAt(0);
- b = x->InputAt(1);
- } else if (IsAddConst(x, /*out*/ &a, /*out*/ &c)) {
- b = graph_->GetConstant(reduction_type, -c); // hidden SUB!
- }
- }
- }
- if (a == nullptr || b == nullptr) {
+ if (v->IsAbs() &&
+ v->GetType() == reduction_type &&
+ IsSubConst2(graph_, v->InputAt(0), /*out*/ &a, /*out*/ &b)) {
+ DCHECK(a != nullptr && b != nullptr);
+ } else {
return false;
}
// Accept same-type or consistent sign extension for narrower-type on operands a and b.
@@ -2138,8 +2223,7 @@ bool HLoopOptimization::VectorizeSADIdiom(LoopNode* node,
}
// Accept SAD idiom for vectorizable operands. Vectorized code uses the shorthand
// idiomatic operation. Sequential code uses the original scalar expressions.
- DCHECK(r != nullptr);
- DCHECK(s != nullptr);
+ DCHECK(r != nullptr && s != nullptr);
if (generate_code && vector_mode_ != kVector) { // de-idiom
r = s = v->InputAt(0);
}
@@ -2226,41 +2310,6 @@ bool HLoopOptimization::IsVectorizationProfitable(int64_t trip_count) {
return true;
}
-static constexpr uint32_t ARM64_SIMD_MAXIMUM_UNROLL_FACTOR = 8;
-static constexpr uint32_t ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE = 50;
-
-uint32_t HLoopOptimization::GetUnrollingFactor(HBasicBlock* block, int64_t trip_count) {
- uint32_t max_peel = MaxNumberPeeled();
- switch (compiler_driver_->GetInstructionSet()) {
- case InstructionSet::kArm64: {
- // Don't unroll with insufficient iterations.
- // TODO: Unroll loops with unknown trip count.
- DCHECK_NE(vector_length_, 0u);
- if (trip_count < (2 * vector_length_ + max_peel)) {
- return kNoUnrollingFactor;
- }
- // Don't unroll for large loop body size.
- uint32_t instruction_count = block->GetInstructions().CountSize();
- if (instruction_count >= ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE) {
- return kNoUnrollingFactor;
- }
- // Find a beneficial unroll factor with the following restrictions:
- // - At least one iteration of the transformed loop should be executed.
- // - The loop body shouldn't be "too big" (heuristic).
- uint32_t uf1 = ARM64_SIMD_HEURISTIC_MAX_BODY_SIZE / instruction_count;
- uint32_t uf2 = (trip_count - max_peel) / vector_length_;
- uint32_t unroll_factor =
- TruncToPowerOfTwo(std::min({uf1, uf2, ARM64_SIMD_MAXIMUM_UNROLL_FACTOR}));
- DCHECK_GE(unroll_factor, 1u);
- return unroll_factor;
- }
- case InstructionSet::kX86:
- case InstructionSet::kX86_64:
- default:
- return kNoUnrollingFactor;
- }
-}
-
//
// Helpers.
//
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index 9414e5a0c6..0120cffa56 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -20,12 +20,15 @@
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "induction_var_range.h"
+#include "loop_analysis.h"
#include "nodes.h"
#include "optimization.h"
+#include "superblock_cloner.h"
namespace art {
class CompilerDriver;
+class ArchDefaultLoopHelper;
/**
* Loop optimizations. Builds a loop hierarchy and applies optimizations to
@@ -135,10 +138,26 @@ class HLoopOptimization : public HOptimization {
void SimplifyInduction(LoopNode* node);
void SimplifyBlocks(LoopNode* node);
- // Performs optimizations specific to inner loop (empty loop removal,
+ // Performs optimizations specific to inner loop with finite header logic (empty loop removal,
// unrolling, vectorization). Returns true if anything changed.
+ bool TryOptimizeInnerLoopFinite(LoopNode* node);
+
+ // Performs optimizations specific to inner loop. Returns true if anything changed.
bool OptimizeInnerLoop(LoopNode* node);
+ // Performs loop peeling/unrolling once (depends on the 'do_unrolling'); the transformation
+ // preserves the header and the loop info.
+ //
+ // Note: the function records copying information about blocks and instructions.
+ void PeelOrUnrollOnce(LoopNode* loop_node,
+ bool do_unrolling,
+ SuperblockCloner::HBasicBlockMap* bb_map,
+ SuperblockCloner::HInstructionMap* hir_map);
+
+ // Tries to apply loop unrolling for branch penalty reduction and better instruction scheduling
+ // opportunities. Returns whether transformation happened.
+ bool TryUnrollingForBranchPenaltyReduction(LoopNode* loop_node);
+
//
// Vectorization analysis and synthesis.
//
@@ -203,7 +222,6 @@ class HLoopOptimization : public HOptimization {
const ArrayReference* peeling_candidate);
uint32_t MaxNumberPeeled();
bool IsVectorizationProfitable(int64_t trip_count);
- uint32_t GetUnrollingFactor(HBasicBlock* block, int64_t trip_count);
//
// Helpers.
@@ -297,6 +315,9 @@ class HLoopOptimization : public HOptimization {
HBasicBlock* vector_body_; // body of the new loop
HInstruction* vector_index_; // normalized index of the new loop
+ // Helper for target-specific behaviour for loop optimizations.
+ ArchDefaultLoopHelper* arch_loop_helper_;
+
friend class LoopOptimizationTest;
DISALLOW_COPY_AND_ASSIGN(HLoopOptimization);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index d3212cbbc0..f784f8f7f3 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -3103,6 +3103,8 @@ std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs) {
return os << "array_object_check";
case TypeCheckKind::kArrayCheck:
return os << "array_check";
+ case TypeCheckKind::kBitstringCheck:
+ return os << "bitstring_check";
default:
LOG(FATAL) << "Unknown TypeCheckKind: " << static_cast<int>(rhs);
UNREACHABLE();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index a8fcea2097..79d733060b 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -6178,8 +6178,7 @@ class HLoadClass FINAL : public HInstruction {
special_input_(HUserRecord<HInstruction*>(current_method)),
type_index_(type_index),
dex_file_(dex_file),
- klass_(klass),
- loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
+ klass_(klass) {
// Referrers class should not need access check. We never inline unverified
// methods so we can't possibly end up in this situation.
DCHECK(!is_referrers_class || !needs_access_check);
@@ -6189,6 +6188,7 @@ class HLoadClass FINAL : public HInstruction {
SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
SetPackedFlag<kFlagIsInBootImage>(false);
SetPackedFlag<kFlagGenerateClInitCheck>(false);
+ SetPackedFlag<kFlagValidLoadedClassRTI>(false);
}
bool IsClonable() const OVERRIDE { return true; }
@@ -6243,13 +6243,18 @@ class HLoadClass FINAL : public HInstruction {
}
ReferenceTypeInfo GetLoadedClassRTI() {
- return loaded_class_rti_;
+ if (GetPackedFlag<kFlagValidLoadedClassRTI>()) {
+ // Note: The is_exact flag from the return value should not be used.
+ return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true);
+ } else {
+ return ReferenceTypeInfo::CreateInvalid();
+ }
}
- void SetLoadedClassRTI(ReferenceTypeInfo rti) {
- // Make sure we only set exact types (the loaded class should never be merged).
- DCHECK(rti.IsExact());
- loaded_class_rti_ = rti;
+ // Loaded class RTI is marked as valid by RTP if the klass_ is admissible.
+ void SetValidLoadedClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(klass_ != nullptr);
+ SetPackedFlag<kFlagValidLoadedClassRTI>(true);
}
dex::TypeIndex GetTypeIndex() const { return type_index_; }
@@ -6302,7 +6307,8 @@ class HLoadClass FINAL : public HInstruction {
static constexpr size_t kFieldLoadKind = kFlagGenerateClInitCheck + 1;
static constexpr size_t kFieldLoadKindSize =
MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast));
- static constexpr size_t kNumberOfLoadClassPackedBits = kFieldLoadKind + kFieldLoadKindSize;
+ static constexpr size_t kFlagValidLoadedClassRTI = kFieldLoadKind + kFieldLoadKindSize;
+ static constexpr size_t kNumberOfLoadClassPackedBits = kFlagValidLoadedClassRTI + 1;
static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields.");
using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>;
@@ -6329,8 +6335,6 @@ class HLoadClass FINAL : public HInstruction {
const DexFile& dex_file_;
Handle<mirror::Class> klass_;
-
- ReferenceTypeInfo loaded_class_rti_;
};
std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs);
@@ -6882,72 +6886,159 @@ enum class TypeCheckKind {
kInterfaceCheck, // No optimization yet when checking against an interface.
kArrayObjectCheck, // Can just check if the array is not primitive.
kArrayCheck, // No optimization yet when checking against a generic array.
+ kBitstringCheck, // Compare the type check bitstring.
kLast = kArrayCheck
};
std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs);
-class HInstanceOf FINAL : public HExpression<2> {
+// Note: HTypeCheckInstruction is just a helper class, not an abstract instruction with an
+// `IsTypeCheckInstruction()`. (New virtual methods in the HInstruction class have a high cost.)
+class HTypeCheckInstruction : public HVariableInputSizeInstruction {
public:
- HInstanceOf(HInstruction* object,
- HLoadClass* target_class,
- TypeCheckKind check_kind,
- uint32_t dex_pc)
- : HExpression(kInstanceOf,
- DataType::Type::kBool,
- SideEffectsForArchRuntimeCalls(check_kind),
- dex_pc) {
+ HTypeCheckInstruction(InstructionKind kind,
+ HInstruction* object,
+ HInstruction* target_class_or_null,
+ TypeCheckKind check_kind,
+ Handle<mirror::Class> klass,
+ uint32_t dex_pc,
+ ArenaAllocator* allocator,
+ HIntConstant* bitstring_path_to_root,
+ HIntConstant* bitstring_mask,
+ SideEffects side_effects)
+ : HVariableInputSizeInstruction(
+ kind,
+ side_effects,
+ dex_pc,
+ allocator,
+ /* number_of_inputs */ check_kind == TypeCheckKind::kBitstringCheck ? 4u : 2u,
+ kArenaAllocTypeCheckInputs),
+ klass_(klass) {
SetPackedField<TypeCheckKindField>(check_kind);
SetPackedFlag<kFlagMustDoNullCheck>(true);
+ SetPackedFlag<kFlagValidTargetClassRTI>(false);
SetRawInputAt(0, object);
- SetRawInputAt(1, target_class);
+ SetRawInputAt(1, target_class_or_null);
+ DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_path_to_root != nullptr);
+ DCHECK_EQ(check_kind == TypeCheckKind::kBitstringCheck, bitstring_mask != nullptr);
+ if (check_kind == TypeCheckKind::kBitstringCheck) {
+ DCHECK(target_class_or_null->IsNullConstant());
+ SetRawInputAt(2, bitstring_path_to_root);
+ SetRawInputAt(3, bitstring_mask);
+ } else {
+ DCHECK(target_class_or_null->IsLoadClass());
+ }
}
HLoadClass* GetTargetClass() const {
+ DCHECK_NE(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck);
HInstruction* load_class = InputAt(1);
DCHECK(load_class->IsLoadClass());
return load_class->AsLoadClass();
}
- bool IsClonable() const OVERRIDE { return true; }
- bool CanBeMoved() const OVERRIDE { return true; }
+ uint32_t GetBitstringPathToRoot() const {
+ DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck);
+ HInstruction* path_to_root = InputAt(2);
+ DCHECK(path_to_root->IsIntConstant());
+ return static_cast<uint32_t>(path_to_root->AsIntConstant()->GetValue());
+ }
- bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
- return true;
+ uint32_t GetBitstringMask() const {
+ DCHECK_EQ(GetTypeCheckKind(), TypeCheckKind::kBitstringCheck);
+ HInstruction* mask = InputAt(3);
+ DCHECK(mask->IsIntConstant());
+ return static_cast<uint32_t>(mask->AsIntConstant()->GetValue());
}
- bool NeedsEnvironment() const OVERRIDE {
- return CanCallRuntime(GetTypeCheckKind());
+ bool IsClonable() const OVERRIDE { return true; }
+ bool CanBeMoved() const OVERRIDE { return true; }
+
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ DCHECK(other->IsInstanceOf() || other->IsCheckCast()) << other->DebugName();
+ return GetPackedFields() == down_cast<const HTypeCheckInstruction*>(other)->GetPackedFields();
}
- // Used only in code generation.
bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); }
void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); }
TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); }
bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; }
- static bool CanCallRuntime(TypeCheckKind check_kind) {
- // Mips currently does runtime calls for any other checks.
- return check_kind != TypeCheckKind::kExactCheck;
+ ReferenceTypeInfo GetTargetClassRTI() {
+ if (GetPackedFlag<kFlagValidTargetClassRTI>()) {
+ // Note: The is_exact flag from the return value should not be used.
+ return ReferenceTypeInfo::CreateUnchecked(klass_, /* is_exact */ true);
+ } else {
+ return ReferenceTypeInfo::CreateInvalid();
+ }
}
- static SideEffects SideEffectsForArchRuntimeCalls(TypeCheckKind check_kind) {
- return CanCallRuntime(check_kind) ? SideEffects::CanTriggerGC() : SideEffects::None();
+ // Target class RTI is marked as valid by RTP if the klass_ is admissible.
+ void SetValidTargetClassRTI() REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(klass_ != nullptr);
+ SetPackedFlag<kFlagValidTargetClassRTI>(true);
}
- DECLARE_INSTRUCTION(InstanceOf);
+ Handle<mirror::Class> GetClass() const {
+ return klass_;
+ }
protected:
- DEFAULT_COPY_CONSTRUCTOR(InstanceOf);
+ DEFAULT_COPY_CONSTRUCTOR(TypeCheckInstruction);
private:
- static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits;
static constexpr size_t kFieldTypeCheckKindSize =
MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));
static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize;
- static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1;
+ static constexpr size_t kFlagValidTargetClassRTI = kFlagMustDoNullCheck + 1;
+ static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagValidTargetClassRTI + 1;
static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
+
+ Handle<mirror::Class> klass_;
+};
+
+class HInstanceOf FINAL : public HTypeCheckInstruction {
+ public:
+ HInstanceOf(HInstruction* object,
+ HInstruction* target_class_or_null,
+ TypeCheckKind check_kind,
+ Handle<mirror::Class> klass,
+ uint32_t dex_pc,
+ ArenaAllocator* allocator,
+ HIntConstant* bitstring_path_to_root,
+ HIntConstant* bitstring_mask)
+ : HTypeCheckInstruction(kInstanceOf,
+ object,
+ target_class_or_null,
+ check_kind,
+ klass,
+ dex_pc,
+ allocator,
+ bitstring_path_to_root,
+ bitstring_mask,
+ SideEffectsForArchRuntimeCalls(check_kind)) {}
+
+ DataType::Type GetType() const OVERRIDE { return DataType::Type::kBool; }
+
+ bool NeedsEnvironment() const OVERRIDE {
+ return CanCallRuntime(GetTypeCheckKind());
+ }
+
+ static bool CanCallRuntime(TypeCheckKind check_kind) {
+ // Mips currently does runtime calls for any other checks.
+ return check_kind != TypeCheckKind::kExactCheck;
+ }
+
+ static SideEffects SideEffectsForArchRuntimeCalls(TypeCheckKind check_kind) {
+ return CanCallRuntime(check_kind) ? SideEffects::CanTriggerGC() : SideEffects::None();
+ }
+
+ DECLARE_INSTRUCTION(InstanceOf);
+
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(InstanceOf);
};
class HBoundType FINAL : public HExpression<1> {
@@ -6997,31 +7088,26 @@ class HBoundType FINAL : public HExpression<1> {
ReferenceTypeInfo upper_bound_;
};
-class HCheckCast FINAL : public HTemplateInstruction<2> {
+class HCheckCast FINAL : public HTypeCheckInstruction {
public:
HCheckCast(HInstruction* object,
- HLoadClass* target_class,
+ HInstruction* target_class_or_null,
TypeCheckKind check_kind,
- uint32_t dex_pc)
- : HTemplateInstruction(kCheckCast, SideEffects::CanTriggerGC(), dex_pc) {
- SetPackedField<TypeCheckKindField>(check_kind);
- SetPackedFlag<kFlagMustDoNullCheck>(true);
- SetRawInputAt(0, object);
- SetRawInputAt(1, target_class);
- }
-
- HLoadClass* GetTargetClass() const {
- HInstruction* load_class = InputAt(1);
- DCHECK(load_class->IsLoadClass());
- return load_class->AsLoadClass();
- }
-
- bool IsClonable() const OVERRIDE { return true; }
- bool CanBeMoved() const OVERRIDE { return true; }
-
- bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
- return true;
- }
+ Handle<mirror::Class> klass,
+ uint32_t dex_pc,
+ ArenaAllocator* allocator,
+ HIntConstant* bitstring_path_to_root,
+ HIntConstant* bitstring_mask)
+ : HTypeCheckInstruction(kCheckCast,
+ object,
+ target_class_or_null,
+ check_kind,
+ klass,
+ dex_pc,
+ allocator,
+ bitstring_path_to_root,
+ bitstring_mask,
+ SideEffects::CanTriggerGC()) {}
bool NeedsEnvironment() const OVERRIDE {
// Instruction may throw a CheckCastError.
@@ -7030,24 +7116,10 @@ class HCheckCast FINAL : public HTemplateInstruction<2> {
bool CanThrow() const OVERRIDE { return true; }
- bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); }
- void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); }
- TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); }
- bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; }
-
DECLARE_INSTRUCTION(CheckCast);
protected:
DEFAULT_COPY_CONSTRUCTOR(CheckCast);
-
- private:
- static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits;
- static constexpr size_t kFieldTypeCheckKindSize =
- MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));
- static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize;
- static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1;
- static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
- using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
};
/**
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 00194ff1fe..e0a9cfb934 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -99,6 +99,7 @@ enum class MethodCompilationStat {
kConstructorFenceRemovedLSE,
kConstructorFenceRemovedPFRA,
kConstructorFenceRemovedCFRE,
+ kBitstringTypeCheck,
kJitOutOfMemoryForCommit,
kLastStat
};
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index f843c008d8..59733397bf 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -34,6 +34,20 @@ void PrepareForRegisterAllocation::Run() {
}
}
+void PrepareForRegisterAllocation::VisitCheckCast(HCheckCast* check_cast) {
+ // Record only those bitstring type checks that make it to the codegen stage.
+ if (check_cast->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
+ MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck);
+ }
+}
+
+void PrepareForRegisterAllocation::VisitInstanceOf(HInstanceOf* instance_of) {
+ // Record only those bitstring type checks that make it to the codegen stage.
+ if (instance_of->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
+ MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck);
+ }
+}
+
void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) {
check->ReplaceWith(check->InputAt(0));
}
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index 2c64f016c1..f6e4d3ef99 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -40,6 +40,8 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor {
"prepare_for_register_allocation";
private:
+ void VisitCheckCast(HCheckCast* check_cast) OVERRIDE;
+ void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE;
void VisitNullCheck(HNullCheck* check) OVERRIDE;
void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE;
void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE;
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 67a61fc01d..4030883a57 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -87,6 +87,7 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {
void VisitDeoptimize(HDeoptimize* deopt) OVERRIDE;
void VisitNewInstance(HNewInstance* new_instance) OVERRIDE;
void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
+ void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE;
void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
void VisitLoadString(HLoadString* instr) OVERRIDE;
void VisitLoadException(HLoadException* instr) OVERRIDE;
@@ -171,6 +172,12 @@ void ReferenceTypePropagation::ValidateTypes() {
<< "NullCheck " << instr->GetReferenceTypeInfo()
<< "Input(0) " << instr->InputAt(0)->GetReferenceTypeInfo();
}
+ } else if (instr->IsInstanceOf()) {
+ HInstanceOf* iof = instr->AsInstanceOf();
+ DCHECK(!iof->GetTargetClassRTI().IsValid() || iof->GetTargetClassRTI().IsExact());
+ } else if (instr->IsCheckCast()) {
+ HCheckCast* check = instr->AsCheckCast();
+ DCHECK(!check->GetTargetClassRTI().IsValid() || check->GetTargetClassRTI().IsExact());
}
}
}
@@ -499,8 +506,7 @@ void ReferenceTypePropagation::RTPVisitor::BoundTypeForIfInstanceOf(HBasicBlock*
return;
}
- HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
- ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+ ReferenceTypeInfo class_rti = instanceOf->GetTargetClassRTI();
if (!class_rti.IsValid()) {
// He have loaded an unresolved class. Don't bother bounding the type.
return;
@@ -643,15 +649,20 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet(
void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) {
ScopedObjectAccess soa(Thread::Current());
- Handle<mirror::Class> resolved_class = instr->GetClass();
- if (IsAdmissible(resolved_class.Get())) {
- instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(
- resolved_class, /* is_exact */ true));
+ if (IsAdmissible(instr->GetClass().Get())) {
+ instr->SetValidLoadedClassRTI();
}
instr->SetReferenceTypeInfo(
ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true));
}
+void ReferenceTypePropagation::RTPVisitor::VisitInstanceOf(HInstanceOf* instr) {
+ ScopedObjectAccess soa(Thread::Current());
+ if (IsAdmissible(instr->GetClass().Get())) {
+ instr->SetValidTargetClassRTI();
+ }
+}
+
void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) {
instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo());
}
@@ -719,8 +730,6 @@ void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) {
}
void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast) {
- HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
- ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
HBoundType* bound_type = check_cast->GetNext()->AsBoundType();
if (bound_type == nullptr || bound_type->GetUpperBound().IsValid()) {
// The next instruction is not an uninitialized BoundType. This must be
@@ -729,12 +738,14 @@ void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast
}
DCHECK_EQ(bound_type->InputAt(0), check_cast->InputAt(0));
- if (class_rti.IsValid()) {
+ ScopedObjectAccess soa(Thread::Current());
+ Handle<mirror::Class> klass = check_cast->GetClass();
+ if (IsAdmissible(klass.Get())) {
DCHECK(is_first_run_);
- ScopedObjectAccess soa(Thread::Current());
+ check_cast->SetValidTargetClassRTI();
// This is the first run of RTP and class is resolved.
- bool is_exact = class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes();
- bound_type->SetUpperBound(ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), is_exact),
+ bool is_exact = klass->CannotBeAssignedFromOtherTypes();
+ bound_type->SetUpperBound(ReferenceTypeInfo::Create(klass, is_exact),
/* CheckCast succeeds for nulls. */ true);
} else {
// This is the first run of RTP and class is unresolved. Remove the binding.
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 7dffb2a378..70b45763af 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -240,6 +240,75 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(
return load_kind;
}
+static inline bool CanUseTypeCheckBitstring(ObjPtr<mirror::Class> klass,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!klass->IsProxyClass());
+ DCHECK(!klass->IsArrayClass());
+
+ if (Runtime::Current()->UseJitCompilation()) {
+ // If we're JITting, try to assign a type check bitstring (fall through).
+ } else if (codegen->GetCompilerOptions().IsBootImage()) {
+ const char* descriptor = klass->GetDexFile().StringByTypeIdx(klass->GetDexTypeIndex());
+ if (!compiler_driver->IsImageClass(descriptor)) {
+ return false;
+ }
+ // If the target is a boot image class, try to assign a type check bitstring (fall through).
+ // (If --force-determinism, this was already done; repeating is OK and yields the same result.)
+ } else {
+ // TODO: Use the bitstring also for AOT app compilation if the target class has a bitstring
+ // already assigned in the boot image.
+ return false;
+ }
+
+ // Try to assign a type check bitstring.
+ MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
+ if ((false) && // FIXME: Inliner does not respect compiler_driver->IsClassToCompile()
+ // and we're hitting an unassigned bitstring in dex2oat_image_test. b/26687569
+ kIsDebugBuild &&
+ codegen->GetCompilerOptions().IsBootImage() &&
+ codegen->GetCompilerOptions().IsForceDeterminism()) {
+ SubtypeCheckInfo::State old_state = SubtypeCheck<ObjPtr<mirror::Class>>::GetState(klass);
+ CHECK(old_state == SubtypeCheckInfo::kAssigned || old_state == SubtypeCheckInfo::kOverflowed)
+ << klass->PrettyDescriptor() << "/" << old_state
+ << " in " << codegen->GetGraph()->PrettyMethod();
+ }
+ SubtypeCheckInfo::State state = SubtypeCheck<ObjPtr<mirror::Class>>::EnsureAssigned(klass);
+ return state == SubtypeCheckInfo::kAssigned;
+}
+
+TypeCheckKind HSharpening::ComputeTypeCheckKind(ObjPtr<mirror::Class> klass,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver,
+ bool needs_access_check) {
+ if (klass == nullptr) {
+ return TypeCheckKind::kUnresolvedCheck;
+ } else if (klass->IsInterface()) {
+ return TypeCheckKind::kInterfaceCheck;
+ } else if (klass->IsArrayClass()) {
+ if (klass->GetComponentType()->IsObjectClass()) {
+ return TypeCheckKind::kArrayObjectCheck;
+ } else if (klass->CannotBeAssignedFromOtherTypes()) {
+ return TypeCheckKind::kExactCheck;
+ } else {
+ return TypeCheckKind::kArrayCheck;
+ }
+ } else if (klass->IsFinal()) { // TODO: Consider using bitstring for final classes.
+ return TypeCheckKind::kExactCheck;
+ } else if (kBitstringSubtypeCheckEnabled &&
+ !needs_access_check &&
+ CanUseTypeCheckBitstring(klass, codegen, compiler_driver)) {
+ // TODO: We should not need the `!needs_access_check` check but getting rid of that
+ // requires rewriting some optimizations in instruction simplifier.
+ return TypeCheckKind::kBitstringCheck;
+ } else if (klass->IsAbstract()) {
+ return TypeCheckKind::kAbstractClassCheck;
+ } else {
+ return TypeCheckKind::kClassHierarchyCheck;
+ }
+}
+
void HSharpening::ProcessLoadString(
HLoadString* load_string,
CodeGenerator* codegen,
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index 6df7d6d91e..fa3e948eeb 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -44,12 +44,10 @@ class HSharpening : public HOptimization {
static constexpr const char* kSharpeningPassName = "sharpening";
- // Used by the builder.
- static void ProcessLoadString(HLoadString* load_string,
- CodeGenerator* codegen,
- CompilerDriver* compiler_driver,
- const DexCompilationUnit& dex_compilation_unit,
- VariableSizedHandleScope* handles);
+ // Used by Sharpening and InstructionSimplifier.
+ static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver);
// Used by the builder and the inliner.
static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class,
@@ -58,10 +56,19 @@ class HSharpening : public HOptimization {
const DexCompilationUnit& dex_compilation_unit)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Used by Sharpening and InstructionSimplifier.
- static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke,
- CodeGenerator* codegen,
- CompilerDriver* compiler_driver);
+ // Used by the builder.
+ static TypeCheckKind ComputeTypeCheckKind(ObjPtr<mirror::Class> klass,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver,
+ bool needs_access_check)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Used by the builder.
+ static void ProcessLoadString(HLoadString* load_string,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver,
+ const DexCompilationUnit& dex_compilation_unit,
+ VariableSizedHandleScope* handles);
private:
CodeGenerator* codegen_;
diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc
index 04942f9a4a..ee74f1001f 100644
--- a/compiler/optimizing/superblock_cloner.cc
+++ b/compiler/optimizing/superblock_cloner.cc
@@ -853,7 +853,7 @@ void SuperblockCloner::CleanUp() {
}
}
- if (kSuperblockClonerVerify) {
+ if (kIsDebugBuild) {
VerifyGraph();
}
}
diff --git a/compiler/optimizing/superblock_cloner.h b/compiler/optimizing/superblock_cloner.h
index 19c9dd471c..afd5a5d6e7 100644
--- a/compiler/optimizing/superblock_cloner.h
+++ b/compiler/optimizing/superblock_cloner.h
@@ -25,7 +25,6 @@
namespace art {
static const bool kSuperblockClonerLogging = false;
-static const bool kSuperblockClonerVerify = false;
// Represents an edge between two HBasicBlocks.
//
diff --git a/compiler/utils/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h
index 4bd323dadb..9915498acc 100644
--- a/compiler/utils/atomic_dex_ref_map-inl.h
+++ b/compiler/utils/atomic_dex_ref_map-inl.h
@@ -81,8 +81,7 @@ inline bool AtomicDexRefMap<DexFileReferenceType, Value>::Remove(const DexFileRe
if (array == nullptr) {
return false;
}
- *out = (*array)[ref.index].load(std::memory_order_relaxed);
- (*array)[ref.index].store(nullptr, std::memory_order_seq_cst);
+ *out = (*array)[ref.index].exchange(nullptr, std::memory_order_seq_cst);
return true;
}
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index 4613b40457..1f471106df 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -121,7 +121,8 @@ DexFile::DexFile(const uint8_t* base,
num_call_site_ids_(0),
oat_dex_file_(oat_dex_file),
container_(std::move(container)),
- is_compact_dex_(is_compact_dex) {
+ is_compact_dex_(is_compact_dex),
+ is_platform_dex_(false) {
CHECK(begin_ != nullptr) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
// Check base (=header) alignment.
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index aeb49d2c25..683a8243ed 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -992,6 +992,14 @@ class DexFile {
ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const;
ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const;
+ ALWAYS_INLINE bool IsPlatformDexFile() const {
+ return is_platform_dex_;
+ }
+
+ ALWAYS_INLINE void SetIsPlatformDexFile() {
+ is_platform_dex_ = true;
+ }
+
bool IsInMainSection(const void* addr) const {
return Begin() <= addr && addr < Begin() + Size();
}
@@ -1094,6 +1102,9 @@ class DexFile {
// If the dex file is a compact dex file. If false then the dex file is a standard dex file.
const bool is_compact_dex_;
+ // If the dex file is located in /system/framework/.
+ bool is_platform_dex_;
+
friend class DexFileLoader;
friend class DexFileVerifierTest;
friend class OatWriter;
diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h
index 441b3c14b5..b62d044c6a 100644
--- a/libdexfile/dex/hidden_api_access_flags.h
+++ b/libdexfile/dex/hidden_api_access_flags.h
@@ -18,6 +18,7 @@
#define ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_
#include "base/bit_utils.h"
+#include "base/macros.h"
#include "dex/modifiers.h"
namespace art {
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index ee83c4a4e8..fcbafe7e71 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -31,7 +31,6 @@
#include "base/leb128.h"
#include "fixed_up_dex_file.h"
-#include "dex/art_dex_file_loader.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_loader.h"
#include "dex/dex_file_verifier.h"
@@ -108,7 +107,12 @@ std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& origi
std::vector<unsigned char> data;
std::unique_ptr<const art::DexFile> new_dex_file;
std::string error;
- const art::ArtDexFileLoader dex_file_loader;
+
+ // Do not use ArtDexFileLoader here. This code runs in a signal handler and
+ // its stack is too small to invoke the required LocationIsOnSystemFramework
+ // (b/76429651). Instead, we use DexFileLoader and copy the IsPlatformDexFile
+ // property from `original` to `new_dex_file`.
+ const art::DexFileLoader dex_file_loader;
if (original.IsCompactDexFile()) {
// Since we are supposed to return a standard dex, convert back using dexlayout. It's OK to do
@@ -159,6 +163,10 @@ std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& origi
return nullptr;
}
+ if (original.IsPlatformDexFile()) {
+ const_cast<art::DexFile*>(new_dex_file.get())->SetIsPlatformDexFile();
+ }
+
DoDexUnquicken(*new_dex_file, original);
RecomputeDexChecksum(const_cast<art::DexFile*>(new_dex_file.get()));
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index 41a649b5e3..4526be4cbe 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -925,7 +925,9 @@ static jvmtiError GetOwnedMonitorInfoCommon(const art::ScopedObjectAccessAlready
if (target != self) {
called_method = true;
// RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution.
- if (!target->RequestSynchronousCheckpoint(&closure)) {
+ // Since this deals with object references we need to avoid going to sleep.
+ art::ScopedAssertNoThreadSuspension sants("Getting owned monitor usage");
+ if (!target->RequestSynchronousCheckpoint(&closure, art::ThreadState::kRunnable)) {
return ERR(THREAD_NOT_ALIVE);
}
} else {
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 590a399738..c0f1c366b8 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -87,6 +87,7 @@ cc_defaults {
"gc/space/zygote_space.cc",
"gc/task_processor.cc",
"gc/verification.cc",
+ "hidden_api.cc",
"hprof/hprof.cc",
"image.cc",
"index_bss_mapping.cc",
@@ -476,6 +477,13 @@ art_cc_library {
export_shared_lib_headers: [
"libdexfile",
],
+ target: {
+ android: {
+ lto: {
+ thin: true,
+ },
+ },
+ },
}
art_cc_library {
@@ -565,6 +573,7 @@ art_cc_test {
"gc/task_processor_test.cc",
"gtest_test.cc",
"handle_scope_test.cc",
+ "hidden_api_test.cc",
"imtable_test.cc",
"indenter_test.cc",
"indirect_reference_table_test.cc",
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 98214fb684..0fd239a244 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -809,6 +809,9 @@ END art_quick_unlock_object_no_inline
.extern artInstanceOfFromCode
.extern artThrowClassCastExceptionForObject
ENTRY art_quick_check_instance_of
+ // Type check using the bit string passes null as the target class. In that case just throw.
+ cbz r1, .Lthrow_class_cast_exception_for_bitstring_check
+
push {r0-r2, lr} @ save arguments, padding (r2) and link register
.cfi_adjust_cfa_offset 16
.cfi_rel_offset r0, 0
@@ -827,6 +830,7 @@ ENTRY art_quick_check_instance_of
.cfi_restore r2
.cfi_restore lr
+.Lthrow_class_cast_exception_for_bitstring_check:
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r2 @ save all registers as basis for long jump context
mov r2, r9 @ pass Thread::Current
bl artThrowClassCastExceptionForObject @ (Object*, Class*, Thread*)
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index fb449ed5c7..9ff5ebede3 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1269,6 +1269,9 @@ END art_quick_unlock_object_no_inline
.extern artInstanceOfFromCode
.extern artThrowClassCastExceptionForObject
ENTRY art_quick_check_instance_of
+ // Type check using the bit string passes null as the target class. In that case just throw.
+ cbz x1, .Lthrow_class_cast_exception_for_bitstring_check
+
// Store arguments and link register
// Stack needs to be 16B aligned on calls.
SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32
@@ -1294,6 +1297,7 @@ ENTRY art_quick_check_instance_of
// Restore
RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32
+.Lthrow_class_cast_exception_for_bitstring_check:
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context
mov x2, xSELF // pass Thread::Current
bl artThrowClassCastExceptionForObject // (Object*, Class*, Thread*)
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index b2f7e10f52..d8fe480719 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1423,6 +1423,10 @@ END art_quick_unlock_object_no_inline
.extern artInstanceOfFromCode
.extern artThrowClassCastExceptionForObject
ENTRY art_quick_check_instance_of
+ // Type check using the bit string passes null as the target class. In that case just throw.
+ beqz $a1, .Lthrow_class_cast_exception_for_bitstring_check
+ nop
+
addiu $sp, $sp, -32
.cfi_adjust_cfa_offset 32
sw $gp, 16($sp)
@@ -1441,12 +1445,15 @@ ENTRY art_quick_check_instance_of
jalr $zero, $ra
addiu $sp, $sp, 32
.cfi_adjust_cfa_offset -32
+
.Lthrow_class_cast_exception:
lw $t9, 8($sp)
lw $a1, 4($sp)
lw $a0, 0($sp)
addiu $sp, $sp, 32
.cfi_adjust_cfa_offset -32
+
+.Lthrow_class_cast_exception_for_bitstring_check:
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
la $t9, artThrowClassCastExceptionForObject
jalr $zero, $t9 # artThrowClassCastException (Object*, Class*, Thread*)
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 58e0e44813..8d2a7bd6c1 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1364,6 +1364,9 @@ END art_quick_unlock_object_no_inline
.extern artInstanceOfFromCode
.extern artThrowClassCastExceptionForObject
ENTRY art_quick_check_instance_of
+ // Type check using the bit string passes null as the target class. In that case just throw.
+ beqzc $a1, .Lthrow_class_cast_exception_for_bitstring_check
+
daddiu $sp, $sp, -32
.cfi_adjust_cfa_offset 32
sd $ra, 24($sp)
@@ -1379,12 +1382,15 @@ ENTRY art_quick_check_instance_of
jalr $zero, $ra
daddiu $sp, $sp, 32
.cfi_adjust_cfa_offset -32
+
.Lthrow_class_cast_exception:
ld $t9, 16($sp)
ld $a1, 8($sp)
ld $a0, 0($sp)
daddiu $sp, $sp, 32
.cfi_adjust_cfa_offset -32
+
+.Lthrow_class_cast_exception_for_bitstring_check:
SETUP_GP
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
dla $t9, artThrowClassCastExceptionForObject
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 5c4ae4ea12..df43aef94b 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1432,6 +1432,10 @@ DEFINE_FUNCTION art_quick_instance_of
END_FUNCTION art_quick_instance_of
DEFINE_FUNCTION art_quick_check_instance_of
+ // Type check using the bit string passes null as the target class. In that case just throw.
+ testl %ecx, %ecx
+ jz .Lthrow_class_cast_exception_for_bitstring_check
+
PUSH eax // alignment padding
PUSH ecx // pass arg2 - checked class
PUSH eax // pass arg1 - obj
@@ -1449,6 +1453,7 @@ DEFINE_FUNCTION art_quick_check_instance_of
addl LITERAL(4), %esp
CFI_ADJUST_CFA_OFFSET(-4)
+.Lthrow_class_cast_exception_for_bitstring_check:
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx, ebx // save all registers as basis for long jump context
// Outgoing argument set up
PUSH eax // alignment padding
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index a813200606..4f941e1c48 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1403,6 +1403,10 @@ DEFINE_FUNCTION art_quick_unlock_object_no_inline
END_FUNCTION art_quick_unlock_object_no_inline
DEFINE_FUNCTION art_quick_check_instance_of
+ // Type check using the bit string passes null as the target class. In that case just throw.
+ testl %esi, %esi
+ jz .Lthrow_class_cast_exception_for_bitstring_check
+
// We could check the super classes here but that is usually already checked in the caller.
PUSH rdi // Save args for exc
PUSH rsi
@@ -1426,6 +1430,7 @@ DEFINE_FUNCTION art_quick_check_instance_of
POP rsi // Pop arguments
POP rdi
+.Lthrow_class_cast_exception_for_bitstring_check:
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // save all registers as basis for long jump context
mov %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current()
call SYMBOL(artThrowClassCastExceptionForObject) // (Object* src, Class* dest, Thread*)
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 292bde0272..fe0f876d66 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -56,6 +56,7 @@ const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = {
"CtorFenceIns ",
"InvokeInputs ",
"PhiInputs ",
+ "TypeCheckIns ",
"LoopInfo ",
"LIBackEdges ",
"TryCatchInf ",
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index c3011091e8..688f01b71f 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -62,6 +62,7 @@ enum ArenaAllocKind {
kArenaAllocConstructorFenceInputs,
kArenaAllocInvokeInputs,
kArenaAllocPhiInputs,
+ kArenaAllocTypeCheckInputs,
kArenaAllocLoopInfo,
kArenaAllocLoopInfoBackEdges,
kArenaAllocTryCatchInfo,
diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc
index 1cb3b9c380..2b3e360650 100644
--- a/runtime/base/file_utils.cc
+++ b/runtime/base/file_utils.cc
@@ -319,4 +319,21 @@ bool LocationIsOnSystem(const char* location) {
return path != nullptr && android::base::StartsWith(path.get(), GetAndroidRoot().c_str());
}
+bool LocationIsOnSystemFramework(const char* location) {
+ std::string error_msg;
+ std::string root_path = GetAndroidRootSafe(&error_msg);
+ if (root_path.empty()) {
+ // Could not find Android root.
+ // TODO(dbrazdil): change to stricter GetAndroidRoot() once b/76452688 is resolved.
+ return false;
+ }
+ std::string framework_path = root_path + "/framework/";
+
+ // Warning: Bionic implementation of realpath() allocates > 12KB on the stack.
+ // Do not run this code on a small stack, e.g. in signal handler.
+ UniqueCPtr<const char[]> path(realpath(location, nullptr));
+ return path != nullptr &&
+ android::base::StartsWith(path.get(), framework_path.c_str());
+}
+
} // namespace art
diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h
index 7f691d546b..8adb4f7bf8 100644
--- a/runtime/base/file_utils.h
+++ b/runtime/base/file_utils.h
@@ -75,6 +75,9 @@ std::string ReplaceFileExtension(const std::string& filename, const std::string&
// Return whether the location is on system (i.e. android root).
bool LocationIsOnSystem(const char* location);
+// Return whether the location is on system/framework (i.e. android_root/framework).
+bool LocationIsOnSystemFramework(const char* location);
+
} // namespace art
#endif // ART_RUNTIME_BASE_FILE_UTILS_H_
diff --git a/runtime/base/quasi_atomic.h b/runtime/base/quasi_atomic.h
index 067d01db01..0012f6482b 100644
--- a/runtime/base/quasi_atomic.h
+++ b/runtime/base/quasi_atomic.h
@@ -152,14 +152,6 @@ class QuasiAtomic {
return NeedSwapMutexes(isa);
}
- static void ThreadFenceAcquire() {
- std::atomic_thread_fence(std::memory_order_acquire);
- }
-
- static void ThreadFenceRelease() {
- std::atomic_thread_fence(std::memory_order_release);
- }
-
static void ThreadFenceForConstructor() {
#if defined(__aarch64__)
__asm__ __volatile__("dmb ishst" : : : "memory");
@@ -168,10 +160,6 @@ class QuasiAtomic {
#endif
}
- static void ThreadFenceSequentiallyConsistent() {
- std::atomic_thread_fence(std::memory_order_seq_cst);
- }
-
private:
static Mutex* GetSwapMutex(const volatile int64_t* addr);
static int64_t SwapMutexRead64(volatile const int64_t* addr);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 8b64b8def0..3025818ab7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -72,6 +72,7 @@
#include "gc/space/space-inl.h"
#include "gc_root-inl.h"
#include "handle_scope-inl.h"
+#include "hidden_api.h"
#include "image-inl.h"
#include "imt_conflict_table.h"
#include "imtable-inl.h"
@@ -6178,7 +6179,7 @@ ArtMethod* ClassLinker::AddMethodToConflictTable(ObjPtr<mirror::Class> klass,
// Note that there is a race in the presence of multiple threads and we may leak
// memory from the LinearAlloc, but that's a tradeoff compared to using
// atomic operations.
- QuasiAtomic::ThreadFenceRelease();
+ std::atomic_thread_fence(std::memory_order_release);
new_conflict_method->SetImtConflictTable(new_table, image_pointer_size_);
return new_conflict_method;
}
@@ -7935,7 +7936,8 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass,
}
DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
if (resolved != nullptr &&
- hiddenapi::ShouldBlockAccessToMember(resolved, class_loader, hiddenapi::kLinking)) {
+ hiddenapi::ShouldBlockAccessToMember(
+ resolved, class_loader, dex_cache, hiddenapi::kLinking)) {
resolved = nullptr;
}
if (resolved != nullptr) {
@@ -8077,7 +8079,8 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx,
resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_);
}
if (resolved != nullptr &&
- hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) {
+ hiddenapi::ShouldBlockAccessToMember(
+ resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) {
resolved = nullptr;
}
return resolved;
@@ -8156,7 +8159,8 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx,
}
if (resolved == nullptr ||
- hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) {
+ hiddenapi::ShouldBlockAccessToMember(
+ resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) {
const char* name = dex_file.GetFieldName(field_id);
const char* type = dex_file.GetFieldTypeDescriptor(field_id);
ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name);
@@ -8189,7 +8193,8 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx,
StringPiece type(dex_file.GetFieldTypeDescriptor(field_id));
resolved = mirror::Class::FindField(self, klass, name, type);
if (resolved != nullptr &&
- hiddenapi::ShouldBlockAccessToMember(resolved, class_loader.Get(), hiddenapi::kLinking)) {
+ hiddenapi::ShouldBlockAccessToMember(
+ resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) {
resolved = nullptr;
}
if (resolved != nullptr) {
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 99a4c77979..4a9449640b 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -4354,9 +4354,11 @@ bool Dbg::DdmHandleChunk(JNIEnv* env,
WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch,
type, dataArray.get(), 0, data.size()));
if (env->ExceptionCheck()) {
- LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type);
- env->ExceptionDescribe();
- env->ExceptionClear();
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type) << std::endl
+ << self->GetException()->Dump();
+ self->ClearException();
return false;
}
@@ -4400,10 +4402,11 @@ bool Dbg::DdmHandleChunk(JNIEnv* env,
reinterpret_cast<jbyte*>(out_data->data()));
if (env->ExceptionCheck()) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
LOG(INFO) << StringPrintf("Exception thrown when reading response data from dispatcher 0x%08x",
- type);
- env->ExceptionDescribe();
- env->ExceptionClear();
+ type) << std::endl << self->GetException()->Dump();
+ self->ClearException();
return false;
}
diff --git a/runtime/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc
index 9802c6904b..f3e6a69279 100644
--- a/runtime/dex/art_dex_file_loader.cc
+++ b/runtime/dex/art_dex_file_loader.cc
@@ -22,6 +22,7 @@
#include "android-base/stringprintf.h"
#include "base/file_magic.h"
+#include "base/file_utils.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/unix_file/fd_file.h"
@@ -505,4 +506,39 @@ bool ArtDexFileLoader::OpenAllDexFilesFromZip(
}
}
+std::unique_ptr<DexFile> ArtDexFileLoader::OpenCommon(const uint8_t* base,
+ size_t size,
+ const uint8_t* data_base,
+ size_t data_size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg,
+ std::unique_ptr<DexFileContainer> container,
+ VerifyResult* verify_result) {
+ std::unique_ptr<DexFile> dex_file = DexFileLoader::OpenCommon(base,
+ size,
+ data_base,
+ data_size,
+ location,
+ location_checksum,
+ oat_dex_file,
+ verify,
+ verify_checksum,
+ error_msg,
+ std::move(container),
+ verify_result);
+
+ // Check if this dex file is located in the framework directory.
+ // If it is, set a flag on the dex file. This is used by hidden API
+ // policy decision logic.
+ if (dex_file != nullptr && LocationIsOnSystemFramework(location.c_str())) {
+ dex_file->SetIsPlatformDexFile();
+ }
+
+ return dex_file;
+}
+
} // namespace art
diff --git a/runtime/dex/art_dex_file_loader.h b/runtime/dex/art_dex_file_loader.h
index 7c7a59b37f..7577945632 100644
--- a/runtime/dex/art_dex_file_loader.h
+++ b/runtime/dex/art_dex_file_loader.h
@@ -120,6 +120,19 @@ class ArtDexFileLoader : public DexFileLoader {
bool verify_checksum,
std::string* error_msg,
ZipOpenErrorCode* error_code) const;
+
+ static std::unique_ptr<DexFile> OpenCommon(const uint8_t* base,
+ size_t size,
+ const uint8_t* data_base,
+ size_t data_size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg,
+ std::unique_ptr<DexFileContainer> container,
+ VerifyResult* verify_result);
};
} // namespace art
diff --git a/runtime/dex/art_dex_file_loader_test.cc b/runtime/dex/art_dex_file_loader_test.cc
index 6e2cfec381..3e0d6662c7 100644
--- a/runtime/dex/art_dex_file_loader_test.cc
+++ b/runtime/dex/art_dex_file_loader_test.cc
@@ -16,9 +16,11 @@
#include <sys/mman.h>
+#include <fstream>
#include <memory>
#include "art_dex_file_loader.h"
+#include "base/file_utils.h"
#include "base/os.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
@@ -35,7 +37,40 @@
namespace art {
-class ArtDexFileLoaderTest : public CommonRuntimeTest {};
+static void Copy(const std::string& src, const std::string& dst) {
+ std::ifstream src_stream(src, std::ios::binary);
+ std::ofstream dst_stream(dst, std::ios::binary);
+ dst_stream << src_stream.rdbuf();
+}
+
+class ArtDexFileLoaderTest : public CommonRuntimeTest {
+ public:
+ virtual void SetUp() {
+ CommonRuntimeTest::SetUp();
+
+ std::string dex_location = GetTestDexFileName("Main");
+
+ data_location_path_ = android_data_ + "/foo.jar";
+ system_location_path_ = GetAndroidRoot() + "/foo.jar";
+ system_framework_location_path_ = GetAndroidRoot() + "/framework/foo.jar";
+
+ Copy(dex_location, data_location_path_);
+ Copy(dex_location, system_location_path_);
+ Copy(dex_location, system_framework_location_path_);
+ }
+
+ virtual void TearDown() {
+ remove(data_location_path_.c_str());
+ remove(system_location_path_.c_str());
+ remove(system_framework_location_path_.c_str());
+ CommonRuntimeTest::TearDown();
+ }
+
+ protected:
+ std::string data_location_path_;
+ std::string system_location_path_;
+ std::string system_framework_location_path_;
+};
// TODO: Port OpenTestDexFile(s) need to be ported to use non-ART utilities, and
// the tests that depend upon them should be moved to dex_file_loader_test.cc
@@ -304,4 +339,57 @@ TEST_F(ArtDexFileLoaderTest, GetDexCanonicalLocation) {
ASSERT_EQ(0, unlink(dex_location_sym.c_str()));
}
+TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile) {
+ ArtDexFileLoader loader;
+ bool success;
+ std::string error_msg;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+
+ // Load file from a non-system directory and check that it is not flagged as framework.
+ ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path_.c_str()));
+ success = loader.Open(data_location_path_.c_str(),
+ data_location_path_,
+ /* verify */ false,
+ /* verify_checksum */ false,
+ &error_msg,
+ &dex_files);
+ ASSERT_TRUE(success);
+ ASSERT_GE(dex_files.size(), 1u);
+ for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
+ ASSERT_FALSE(dex_file->IsPlatformDexFile());
+ }
+
+ dex_files.clear();
+
+ // Load file from a system, non-framework directory and check that it is not flagged as framework.
+ ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path_.c_str()));
+ success = loader.Open(system_location_path_.c_str(),
+ system_location_path_,
+ /* verify */ false,
+ /* verify_checksum */ false,
+ &error_msg,
+ &dex_files);
+ ASSERT_TRUE(success);
+ ASSERT_GE(dex_files.size(), 1u);
+ for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
+ ASSERT_FALSE(dex_file->IsPlatformDexFile());
+ }
+
+ dex_files.clear();
+
+ // Load file from a system/framework directory and check that it is flagged as a framework dex.
+ ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path_.c_str()));
+ success = loader.Open(system_framework_location_path_.c_str(),
+ system_framework_location_path_,
+ /* verify */ false,
+ /* verify_checksum */ false,
+ &error_msg,
+ &dex_files);
+ ASSERT_TRUE(success);
+ ASSERT_GE(dex_files.size(), 1u);
+ for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
+ ASSERT_TRUE(dex_file->IsPlatformDexFile());
+ }
+}
+
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index 9b0756b529..ba7fb6b9db 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -16,8 +16,11 @@
#include "art_method-inl.h"
#include "callee_save_frame.h"
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_instruction-inl.h"
#include "common_throws.h"
#include "mirror/object-inl.h"
+#include "nth_caller_visitor.h"
#include "thread.h"
#include "well_known_classes.h"
@@ -112,6 +115,26 @@ extern "C" NO_RETURN void artThrowClassCastException(mirror::Class* dest_type,
Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
+ if (dest_type == nullptr) {
+ // Find the target class for check cast using the bitstring check (dest_type == null).
+ NthCallerVisitor visitor(self, 0u);
+ visitor.WalkStack();
+ DCHECK(visitor.caller != nullptr);
+ uint32_t dex_pc = visitor.GetDexPc();
+ CodeItemDataAccessor accessor(*visitor.caller->GetDexFile(), visitor.caller->GetCodeItem());
+ const Instruction& check_cast = accessor.InstructionAt(dex_pc);
+ DCHECK_EQ(check_cast.Opcode(), Instruction::CHECK_CAST);
+ dex::TypeIndex type_index(check_cast.VRegB_21c());
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ dest_type = linker->LookupResolvedType(type_index, visitor.caller).Ptr();
+ CHECK(dest_type != nullptr) << "Target class should have been previously resolved: "
+ << visitor.caller->GetDexFile()->PrettyType(type_index);
+ CHECK(!dest_type->IsAssignableFrom(src_type))
+ << " " << std::hex << dest_type->PrettyDescriptor() << ";" << dest_type->Depth()
+ << "/" << dest_type->GetField32(mirror::Class::StatusOffset())
+ << " <: " << src_type->PrettyDescriptor() << ";" << src_type->Depth()
+ << "/" << src_type->GetField32(mirror::Class::StatusOffset());
+ }
DCHECK(!dest_type->IsAssignableFrom(src_type));
ThrowClassCastException(dest_type, src_type);
self->QuickDeliverException();
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index bb5167f15d..0747c3c77b 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -2357,14 +2357,13 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref,
size_t non_moving_space_bytes_allocated = 0U;
size_t bytes_allocated = 0U;
size_t dummy;
+ bool fall_back_to_non_moving = false;
mirror::Object* to_ref = region_space_->AllocNonvirtual</*kForEvac*/ true>(
region_space_alloc_size, &region_space_bytes_allocated, nullptr, &dummy);
bytes_allocated = region_space_bytes_allocated;
- if (to_ref != nullptr) {
+ if (LIKELY(to_ref != nullptr)) {
DCHECK_EQ(region_space_alloc_size, region_space_bytes_allocated);
- }
- bool fall_back_to_non_moving = false;
- if (UNLIKELY(to_ref == nullptr)) {
+ } else {
// Failed to allocate in the region space. Try the skipped blocks.
to_ref = AllocateInSkippedBlock(region_space_alloc_size);
if (to_ref != nullptr) {
@@ -2374,6 +2373,9 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref,
region_space_->RecordAlloc(to_ref);
}
bytes_allocated = region_space_alloc_size;
+ heap_->num_bytes_allocated_.fetch_sub(bytes_allocated, std::memory_order_seq_cst);
+ to_space_bytes_skipped_.fetch_sub(bytes_allocated, std::memory_order_seq_cst);
+ to_space_objects_skipped_.fetch_sub(1, std::memory_order_seq_cst);
} else {
// Fall back to the non-moving space.
fall_back_to_non_moving = true;
@@ -2383,7 +2385,6 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref,
<< " skipped_objects="
<< to_space_objects_skipped_.load(std::memory_order_seq_cst);
}
- fall_back_to_non_moving = true;
to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size,
&non_moving_space_bytes_allocated, nullptr, &dummy);
if (UNLIKELY(to_ref == nullptr)) {
@@ -2471,7 +2472,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref,
// Do a fence to prevent the field CAS in ConcurrentCopying::Process from possibly reordering
// before the object copy.
- QuasiAtomic::ThreadFenceRelease();
+ std::atomic_thread_fence(std::memory_order_release);
LockWord new_lock_word = LockWord::FromForwardingAddress(reinterpret_cast<size_t>(to_ref));
@@ -2566,7 +2567,7 @@ mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) {
bool ConcurrentCopying::IsOnAllocStack(mirror::Object* ref) {
// TODO: Explain why this is here. What release operation does it pair with?
- QuasiAtomic::ThreadFenceAcquire();
+ std::atomic_thread_fence(std::memory_order_acquire);
accounting::ObjectStack* alloc_stack = GetAllocationStack();
return alloc_stack->Contains(ref);
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 52afb3850c..f16138c058 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2753,12 +2753,10 @@ class ScanVisitor {
// Verify a reference from an object.
class VerifyReferenceVisitor : public SingleRootVisitor {
public:
- VerifyReferenceVisitor(Heap* heap, Atomic<size_t>* fail_count, bool verify_referent)
+ VerifyReferenceVisitor(Thread* self, Heap* heap, size_t* fail_count, bool verify_referent)
REQUIRES_SHARED(Locks::mutator_lock_)
- : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {}
-
- size_t GetFailureCount() const {
- return fail_count_->load(std::memory_order_seq_cst);
+ : self_(self), heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {
+ CHECK_EQ(self_, Thread::Current());
}
void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED, ObjPtr<mirror::Reference> ref) const
@@ -2810,8 +2808,10 @@ class VerifyReferenceVisitor : public SingleRootVisitor {
// Verify that the reference is live.
return true;
}
- if (fail_count_->fetch_add(1, std::memory_order_seq_cst) == 0) {
- // Print message on only on first failure to prevent spam.
+ CHECK_EQ(self_, Thread::Current()); // fail_count_ is private to the calling thread.
+ *fail_count_ += 1;
+ if (*fail_count_ == 1) {
+ // Only print message for the first failure to prevent spam.
LOG(ERROR) << "!!!!!!!!!!!!!!Heap corruption detected!!!!!!!!!!!!!!!!!!!";
}
if (obj != nullptr) {
@@ -2897,38 +2897,41 @@ class VerifyReferenceVisitor : public SingleRootVisitor {
return false;
}
+ Thread* const self_;
Heap* const heap_;
- Atomic<size_t>* const fail_count_;
+ size_t* const fail_count_;
const bool verify_referent_;
};
// Verify all references within an object, for use with HeapBitmap::Visit.
class VerifyObjectVisitor {
public:
- VerifyObjectVisitor(Heap* heap, Atomic<size_t>* fail_count, bool verify_referent)
- : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {}
+ VerifyObjectVisitor(Thread* self, Heap* heap, size_t* fail_count, bool verify_referent)
+ : self_(self), heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {}
void operator()(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
// Note: we are verifying the references in obj but not obj itself, this is because obj must
// be live or else how did we find it in the live bitmap?
- VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_);
+ VerifyReferenceVisitor visitor(self_, heap_, fail_count_, verify_referent_);
// The class doesn't count as a reference but we should verify it anyways.
obj->VisitReferences(visitor, visitor);
}
void VerifyRoots() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_) {
ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
- VerifyReferenceVisitor visitor(heap_, fail_count_, verify_referent_);
+ VerifyReferenceVisitor visitor(self_, heap_, fail_count_, verify_referent_);
Runtime::Current()->VisitRoots(&visitor);
}
- size_t GetFailureCount() const {
- return fail_count_->load(std::memory_order_seq_cst);
+ uint32_t GetFailureCount() const REQUIRES(Locks::mutator_lock_) {
+ CHECK_EQ(self_, Thread::Current());
+ return *fail_count_;
}
private:
+ Thread* const self_;
Heap* const heap_;
- Atomic<size_t>* const fail_count_;
+ size_t* const fail_count_;
const bool verify_referent_;
};
@@ -2980,8 +2983,8 @@ size_t Heap::VerifyHeapReferences(bool verify_referents) {
// Since we sorted the allocation stack content, need to revoke all
// thread-local allocation stacks.
RevokeAllThreadLocalAllocationStacks(self);
- Atomic<size_t> fail_count_(0);
- VerifyObjectVisitor visitor(this, &fail_count_, verify_referents);
+ size_t fail_count = 0;
+ VerifyObjectVisitor visitor(self, this, &fail_count, verify_referents);
// Verify objects in the allocation stack since these will be objects which were:
// 1. Allocated prior to the GC (pre GC verification).
// 2. Allocated during the GC (pre sweep GC verification).
@@ -3727,13 +3730,21 @@ void Heap::RequestTrim(Thread* self) {
task_processor_->AddTask(self, added_task);
}
+void Heap::IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke) {
+ size_t previous_num_bytes_freed_revoke =
+ num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst);
+ // Check the updated value is less than the number of bytes allocated. There is a risk of
+ // execution being suspended between the increment above and the CHECK below, leading to
+ // the use of previous_num_bytes_freed_revoke in the comparison.
+ CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed),
+ previous_num_bytes_freed_revoke + freed_bytes_revoke);
+}
+
void Heap::RevokeThreadLocalBuffers(Thread* thread) {
if (rosalloc_space_ != nullptr) {
size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread);
if (freed_bytes_revoke > 0U) {
- num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst);
- CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed),
- num_bytes_freed_revoke_.load(std::memory_order_relaxed));
+ IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke);
}
}
if (bump_pointer_space_ != nullptr) {
@@ -3748,9 +3759,7 @@ void Heap::RevokeRosAllocThreadLocalBuffers(Thread* thread) {
if (rosalloc_space_ != nullptr) {
size_t freed_bytes_revoke = rosalloc_space_->RevokeThreadLocalBuffers(thread);
if (freed_bytes_revoke > 0U) {
- num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst);
- CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed),
- num_bytes_freed_revoke_.load(std::memory_order_relaxed));
+ IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke);
}
}
}
@@ -3759,9 +3768,7 @@ void Heap::RevokeAllThreadLocalBuffers() {
if (rosalloc_space_ != nullptr) {
size_t freed_bytes_revoke = rosalloc_space_->RevokeAllThreadLocalBuffers();
if (freed_bytes_revoke > 0U) {
- num_bytes_freed_revoke_.fetch_add(freed_bytes_revoke, std::memory_order_seq_cst);
- CHECK_GE(num_bytes_allocated_.load(std::memory_order_relaxed),
- num_bytes_freed_revoke_.load(std::memory_order_relaxed));
+ IncrementNumberOfBytesFreedRevoke(freed_bytes_revoke);
}
}
if (bump_pointer_space_ != nullptr) {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 9af57d17e5..ef1c0887bb 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1091,6 +1091,8 @@ class Heap {
return max_free_;
}
+ ALWAYS_INLINE void IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke);
+
void TraceHeapSize(size_t heap_size);
// Remove a vlog code from heap-inl.h which is transitively included in half the world.
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
new file mode 100644
index 0000000000..f0b36a090a
--- /dev/null
+++ b/runtime/hidden_api.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2018 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 "hidden_api.h"
+
+#include "base/dumpable.h"
+
+namespace art {
+namespace hiddenapi {
+
+static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
+ switch (value) {
+ case kReflection:
+ os << "reflection";
+ break;
+ case kJNI:
+ os << "JNI";
+ break;
+ case kLinking:
+ os << "linking";
+ break;
+ }
+ return os;
+}
+
+static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) {
+ return static_cast<int>(policy) == static_cast<int>(apiList);
+}
+
+// GetMemberAction-related static_asserts.
+static_assert(
+ EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) &&
+ EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) &&
+ EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist),
+ "Mismatch between EnforcementPolicy and ApiList enums");
+static_assert(
+ EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList &&
+ EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
+ "EnforcementPolicy values ordering not correct");
+
+namespace detail {
+
+MemberSignature::MemberSignature(ArtField* field) {
+ member_type_ = "field";
+ signature_parts_ = {
+ field->GetDeclaringClass()->GetDescriptor(&tmp_),
+ "->",
+ field->GetName(),
+ ":",
+ field->GetTypeDescriptor()
+ };
+}
+
+MemberSignature::MemberSignature(ArtMethod* method) {
+ member_type_ = "method";
+ signature_parts_ = {
+ method->GetDeclaringClass()->GetDescriptor(&tmp_),
+ "->",
+ method->GetName(),
+ method->GetSignature().ToString()
+ };
+}
+
+bool MemberSignature::DoesPrefixMatch(const std::string& prefix) const {
+ size_t pos = 0;
+ for (const std::string& part : signature_parts_) {
+ size_t count = std::min(prefix.length() - pos, part.length());
+ if (prefix.compare(pos, count, part, 0, count) == 0) {
+ pos += count;
+ } else {
+ return false;
+ }
+ }
+ // We have a complete match if all parts match (we exit the loop without
+ // returning) AND we've matched the whole prefix.
+ return pos == prefix.length();
+}
+
+bool MemberSignature::IsExempted(const std::vector<std::string>& exemptions) {
+ for (const std::string& exemption : exemptions) {
+ if (DoesPrefixMatch(exemption)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void MemberSignature::Dump(std::ostream& os) const {
+ for (std::string part : signature_parts_) {
+ os << part;
+ }
+}
+
+void MemberSignature::WarnAboutAccess(AccessMethod access_method,
+ HiddenApiAccessFlags::ApiList list) {
+ LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable<MemberSignature>(*this)
+ << " (" << list << ", " << access_method << ")";
+}
+
+template<typename T>
+bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access_method) {
+ // Get the signature, we need it later.
+ MemberSignature member_signature(member);
+
+ Runtime* runtime = Runtime::Current();
+
+ if (action == kDeny) {
+ // If we were about to deny, check for an exemption first.
+ // Exempted APIs are treated as light grey list.
+ if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
+ action = kAllowButWarn;
+ // Avoid re-examining the exemption list next time.
+ // Note this results in the warning below showing "light greylist", which
+ // seems like what one would expect. Exemptions effectively add new members to
+ // the light greylist.
+ member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
+ member->GetAccessFlags(), HiddenApiAccessFlags::kLightGreylist));
+ }
+ }
+
+ // Print a log message with information about this class member access.
+ // We do this regardless of whether we block the access or not.
+ member_signature.WarnAboutAccess(access_method,
+ HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags()));
+
+ if (action == kDeny) {
+ // Block access
+ return true;
+ }
+
+ // Allow access to this member but print a warning.
+ DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
+
+ // Depending on a runtime flag, we might move the member into whitelist and
+ // skip the warning the next time the member is accessed.
+ if (runtime->ShouldDedupeHiddenApiWarnings()) {
+ member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
+ member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
+ }
+
+ // If this action requires a UI warning, set the appropriate flag.
+ if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
+ runtime->SetPendingHiddenApiWarning(true);
+ }
+
+ return false;
+}
+
+// Need to instantiate this.
+template bool ShouldBlockAccessToMemberImpl<ArtField>(ArtField* member,
+ Action action,
+ AccessMethod access_method);
+template bool ShouldBlockAccessToMemberImpl<ArtMethod>(ArtMethod* member,
+ Action action,
+ AccessMethod access_method);
+
+} // namespace detail
+} // namespace hiddenapi
+} // namespace art
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 5c6b4b56bc..cc6c146f00 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -19,6 +19,7 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
+#include "base/mutex.h"
#include "dex/hidden_api_access_flags.h"
#include "mirror/class-inl.h"
#include "reflection.h"
@@ -57,25 +58,6 @@ enum AccessMethod {
kLinking,
};
-inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
- switch (value) {
- case kReflection:
- os << "reflection";
- break;
- case kJNI:
- os << "JNI";
- break;
- case kLinking:
- os << "linking";
- break;
- }
- return os;
-}
-
-static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) {
- return static_cast<int>(policy) == static_cast<int>(apiList);
-}
-
inline Action GetMemberAction(uint32_t access_flags) {
EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
if (policy == EnforcementPolicy::kNoChecks) {
@@ -88,16 +70,7 @@ inline Action GetMemberAction(uint32_t access_flags) {
return kAllow;
}
// The logic below relies on equality of values in the enums EnforcementPolicy and
- // HiddenApiAccessFlags::ApiList, and their ordering. Assert that this is as expected.
- static_assert(
- EnumsEqual(EnforcementPolicy::kAllLists, HiddenApiAccessFlags::kLightGreylist) &&
- EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) &&
- EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist),
- "Mismatch between EnforcementPolicy and ApiList enums");
- static_assert(
- EnforcementPolicy::kAllLists < EnforcementPolicy::kDarkGreyAndBlackList &&
- EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
- "EnforcementPolicy values ordering not correct");
+ // HiddenApiAccessFlags::ApiList, and their ordering. Assertions are in hidden_api.cc.
if (static_cast<int>(policy) > static_cast<int>(api_list)) {
return api_list == HiddenApiAccessFlags::kDarkGreylist
? kAllowButWarnAndToast
@@ -107,38 +80,67 @@ inline Action GetMemberAction(uint32_t access_flags) {
}
}
-// Issue a warning about field access.
-inline void WarnAboutMemberAccess(ArtField* field, AccessMethod access_method)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- std::string tmp;
- LOG(WARNING) << "Accessing hidden field "
- << field->GetDeclaringClass()->GetDescriptor(&tmp) << "->"
- << field->GetName() << ":" << field->GetTypeDescriptor()
- << " (" << HiddenApiAccessFlags::DecodeFromRuntime(field->GetAccessFlags())
- << ", " << access_method << ")";
-}
+// Implementation details. DO NOT ACCESS DIRECTLY.
+namespace detail {
+
+// Class to encapsulate the signature of a member (ArtField or ArtMethod). This
+// is used as a helper when matching prefixes, and when logging the signature.
+class MemberSignature {
+ private:
+ std::string member_type_;
+ std::vector<std::string> signature_parts_;
+ std::string tmp_;
-// Issue a warning about method access.
-inline void WarnAboutMemberAccess(ArtMethod* method, AccessMethod access_method)
+ public:
+ explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_);
+ explicit MemberSignature(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void Dump(std::ostream& os) const;
+
+ // Performs prefix match on this member. Since the full member signature is
+ // composed of several parts, we match each part in turn (rather than
+ // building the entire thing in memory and performing a simple prefix match)
+ bool DoesPrefixMatch(const std::string& prefix) const;
+
+ bool IsExempted(const std::vector<std::string>& exemptions);
+
+ void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list);
+};
+
+template<typename T>
+bool ShouldBlockAccessToMemberImpl(T* member,
+ Action action,
+ AccessMethod access_method)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+// Returns true if the caller is either loaded by the boot strap class loader or comes from
+// a dex file located in ${ANDROID_ROOT}/framework/.
+ALWAYS_INLINE
+inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loader,
+ ObjPtr<mirror::DexCache> caller_dex_cache)
REQUIRES_SHARED(Locks::mutator_lock_) {
- std::string tmp;
- LOG(WARNING) << "Accessing hidden method "
- << method->GetDeclaringClass()->GetDescriptor(&tmp) << "->"
- << method->GetName() << method->GetSignature().ToString()
- << " (" << HiddenApiAccessFlags::DecodeFromRuntime(method->GetAccessFlags())
- << ", " << access_method << ")";
+ if (caller_class_loader.IsNull()) {
+ return true;
+ } else if (caller_dex_cache.IsNull()) {
+ return false;
+ } else {
+ const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
+ return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile();
+ }
}
+} // namespace detail
+
// Returns true if access to `member` should be denied to the caller of the
-// reflective query. The decision is based on whether the caller is in boot
-// class path or not. Because different users of this function determine this
-// in a different way, `fn_caller_in_boot(self)` is called and should return
-// true if the caller is in boot class path.
+// reflective query. The decision is based on whether the caller is in the
+// platform or not. Because different users of this function determine this
+// in a different way, `fn_caller_in_platform(self)` is called and should
+// return true if the caller is located in the platform.
// This function might print warnings into the log if the member is hidden.
template<typename T>
inline bool ShouldBlockAccessToMember(T* member,
Thread* self,
- std::function<bool(Thread*)> fn_caller_in_boot,
+ std::function<bool(Thread*)> fn_caller_in_platform,
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
@@ -149,42 +151,21 @@ inline bool ShouldBlockAccessToMember(T* member,
return false;
}
- // Member is hidden. Walk the stack to find the caller.
+ // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.
// This can be *very* expensive. Save it for last.
- if (fn_caller_in_boot(self)) {
- // Caller in boot class path. Exit.
+ if (fn_caller_in_platform(self)) {
+ // Caller in the platform. Exit.
return false;
}
- // Member is hidden and we are not in the boot class path.
-
- // Print a log message with information about this class member access.
- // We do this regardless of whether we block the access or not.
- WarnAboutMemberAccess(member, access_method);
-
- if (action == kDeny) {
- // Block access
- return true;
- }
-
- // Allow access to this member but print a warning.
- DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
-
- Runtime* runtime = Runtime::Current();
-
- // Depending on a runtime flag, we might move the member into whitelist and
- // skip the warning the next time the member is accessed.
- if (runtime->ShouldDedupeHiddenApiWarnings()) {
- member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
- member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
- }
-
- // If this action requires a UI warning, set the appropriate flag.
- if (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag()) {
- runtime->SetPendingHiddenApiWarning(true);
- }
+ // Member is hidden and caller is not in the platform.
+ return detail::ShouldBlockAccessToMemberImpl(member, action, access_method);
+}
- return false;
+inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return !caller.IsNull() &&
+ detail::IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache());
}
// Returns true if access to `member` should be denied to a caller loaded with
@@ -193,12 +174,13 @@ inline bool ShouldBlockAccessToMember(T* member,
template<typename T>
inline bool ShouldBlockAccessToMember(T* member,
ObjPtr<mirror::ClassLoader> caller_class_loader,
+ ObjPtr<mirror::DexCache> caller_dex_cache,
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
- bool caller_in_boot = (caller_class_loader.IsNull());
+ bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache);
return ShouldBlockAccessToMember(member,
/* thread */ nullptr,
- [caller_in_boot] (Thread*) { return caller_in_boot; },
+ [caller_in_platform] (Thread*) { return caller_in_platform; },
access_method);
}
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
new file mode 100644
index 0000000000..5a31dd4972
--- /dev/null
+++ b/runtime/hidden_api_test.cc
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018 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 "hidden_api.h"
+
+#include "common_runtime_test.h"
+#include "jni_internal.h"
+
+namespace art {
+
+using hiddenapi::detail::MemberSignature;
+
+class HiddenApiTest : public CommonRuntimeTest {
+ protected:
+ void SetUp() OVERRIDE {
+ // Do the normal setup.
+ CommonRuntimeTest::SetUp();
+ self_ = Thread::Current();
+ self_->TransitionFromSuspendedToRunnable();
+ LoadDex("HiddenApiSignatures");
+ bool started = runtime_->Start();
+ CHECK(started);
+
+ class1_field1_ = getArtField("mypackage/packagea/Class1", "field1", "I");
+ class1_field12_ = getArtField("mypackage/packagea/Class1", "field12", "I");
+ class1_init_ = getArtMethod("mypackage/packagea/Class1", "<init>", "()V");
+ class1_method1_ = getArtMethod("mypackage/packagea/Class1", "method1", "()V");
+ class1_method1_i_ = getArtMethod("mypackage/packagea/Class1", "method1", "(I)V");
+ class1_method12_ = getArtMethod("mypackage/packagea/Class1", "method12", "()V");
+ class12_field1_ = getArtField("mypackage/packagea/Class12", "field1", "I");
+ class12_method1_ = getArtMethod("mypackage/packagea/Class12", "method1", "()V");
+ class2_field1_ = getArtField("mypackage/packagea/Class2", "field1", "I");
+ class2_method1_ = getArtMethod("mypackage/packagea/Class2", "method1", "()V");
+ class2_method1_i_ = getArtMethod("mypackage/packagea/Class2", "method1", "(I)V");
+ class3_field1_ = getArtField("mypackage/packageb/Class3", "field1", "I");
+ class3_method1_ = getArtMethod("mypackage/packageb/Class3", "method1", "()V");
+ class3_method1_i_ = getArtMethod("mypackage/packageb/Class3", "method1", "(I)V");
+ }
+
+ ArtMethod* getArtMethod(const char* class_name, const char* name, const char* signature) {
+ JNIEnv* env = Thread::Current()->GetJniEnv();
+ jclass klass = env->FindClass(class_name);
+ jmethodID method_id = env->GetMethodID(klass, name, signature);
+ ArtMethod* art_method = jni::DecodeArtMethod(method_id);
+ return art_method;
+ }
+
+ ArtField* getArtField(const char* class_name, const char* name, const char* signature) {
+ JNIEnv* env = Thread::Current()->GetJniEnv();
+ jclass klass = env->FindClass(class_name);
+ jfieldID field_id = env->GetFieldID(klass, name, signature);
+ ArtField* art_field = jni::DecodeArtField(field_id);
+ return art_field;
+ }
+
+ protected:
+ Thread* self_;
+ ArtField* class1_field1_;
+ ArtField* class1_field12_;
+ ArtMethod* class1_init_;
+ ArtMethod* class1_method1_;
+ ArtMethod* class1_method1_i_;
+ ArtMethod* class1_method12_;
+ ArtField* class12_field1_;
+ ArtMethod* class12_method1_;
+ ArtField* class2_field1_;
+ ArtMethod* class2_method1_;
+ ArtMethod* class2_method1_i_;
+ ArtField* class3_field1_;
+ ArtMethod* class3_method1_;
+ ArtMethod* class3_method1_i_;
+};
+
+TEST_F(HiddenApiTest, CheckMembersRead) {
+ ASSERT_NE(nullptr, class1_field1_);
+ ASSERT_NE(nullptr, class1_field12_);
+ ASSERT_NE(nullptr, class1_init_);
+ ASSERT_NE(nullptr, class1_method1_);
+ ASSERT_NE(nullptr, class1_method1_i_);
+ ASSERT_NE(nullptr, class1_method12_);
+ ASSERT_NE(nullptr, class12_field1_);
+ ASSERT_NE(nullptr, class12_method1_);
+ ASSERT_NE(nullptr, class2_field1_);
+ ASSERT_NE(nullptr, class2_method1_);
+ ASSERT_NE(nullptr, class2_method1_i_);
+ ASSERT_NE(nullptr, class3_field1_);
+ ASSERT_NE(nullptr, class3_method1_);
+ ASSERT_NE(nullptr, class3_method1_i_);
+}
+
+TEST_F(HiddenApiTest, CheckEverythingMatchesL) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("L");
+ ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class3_field1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class3_method1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckPackageMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/");
+ ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class3_field1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class3_method1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckClassMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1");
+ ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckClassExactMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;");
+ ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckMethodMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;->method1");
+ ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckMethodExactMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;->method1(");
+ ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckMethodSignatureMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;->method1(I)");
+ ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckMethodSignatureAndReturnMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;->method1()V");
+ ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckFieldMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;->field1");
+ ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+ ASSERT_TRUE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckFieldExactMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;->field1:");
+ ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckFieldTypeMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;->field1:I");
+ ASSERT_TRUE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckConstructorMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;-><init>");
+ ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckConstructorExactMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;-><init>()V");
+ ASSERT_TRUE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckMethodSignatureTrailingCharsNoMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;->method1()Vfoo");
+ ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckConstructorTrailingCharsNoMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;-><init>()Vfoo");
+ ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+}
+
+TEST_F(HiddenApiTest, CheckFieldTrailingCharsNoMatch) {
+ ScopedObjectAccess soa(self_);
+ std::string prefix("Lmypackage/packagea/Class1;->field1:Ifoo");
+ ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+}
+
+} // namespace art
diff --git a/runtime/image.cc b/runtime/image.cc
index 5af3e5451b..f14707874b 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '7', '\0' }; // R^2 Bitstring type check.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '8', '\0' }; // R^3 Bitstring type check.
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 735c0e815a..f23304c391 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -243,11 +243,13 @@ static inline JValue Execute(
const CodeItemDataAccessor& accessor,
ShadowFrame& shadow_frame,
JValue result_register,
- bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool stay_in_interpreter = false,
+ bool from_deoptimize = false) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!shadow_frame.GetMethod()->IsAbstract());
DCHECK(!shadow_frame.GetMethod()->IsNative());
- if (LIKELY(shadow_frame.GetDexPC() == 0)) { // Entering the method, but not via deoptimization.
+ if (LIKELY(!from_deoptimize)) { // Entering the method, but not via deoptimization.
if (kIsDebugBuild) {
+ CHECK_EQ(shadow_frame.GetDexPC(), 0u);
self->AssertNoPendingException();
}
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
@@ -568,7 +570,12 @@ void EnterInterpreterFromDeoptimize(Thread* self,
}
if (new_dex_pc != dex::kDexNoIndex) {
shadow_frame->SetDexPC(new_dex_pc);
- value = Execute(self, accessor, *shadow_frame, value);
+ value = Execute(self,
+ accessor,
+ *shadow_frame,
+ value,
+ /* stay_in_interpreter */ true,
+ /* from_deoptimize */ true);
}
ShadowFrame* old_frame = shadow_frame;
shadow_frame = shadow_frame->GetLink();
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 681a582b5d..022b1395bf 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -91,7 +91,7 @@ BINARY_II_INTRINSIC(MterpIntegerRotateLeft, (Rot<int32_t, true>), SetI);
// java.lang.Integer.signum(I)I
UNARY_INTRINSIC(MterpIntegerSignum, Signum, GetVReg, SetI);
-// java.lang.Long.reverse(I)I
+// java.lang.Long.reverse(J)J
UNARY_INTRINSIC(MterpLongReverse, ReverseBits64, GetVRegLong, SetJ);
// java.lang.Long.reverseBytes(J)J
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 76df65f730..4c7a97dfa8 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -184,6 +184,7 @@ static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* fram
return hiddenapi::ShouldBlockAccessToMember(
member,
frame->GetMethod()->GetDeclaringClass()->GetClassLoader(),
+ frame->GetMethod()->GetDeclaringClass()->GetDexCache(),
hiddenapi::kReflection); // all uses in this file are from reflection
}
@@ -1537,7 +1538,7 @@ void UnstartedRuntime::UnstartedUnsafePutOrderedObject(
}
int64_t offset = shadow_frame->GetVRegLong(arg_offset + 2);
mirror::Object* newValue = shadow_frame->GetVRegReference(arg_offset + 4);
- QuasiAtomic::ThreadFenceRelease();
+ std::atomic_thread_fence(std::memory_order_release);
if (Runtime::Current()->IsActiveTransaction()) {
obj->SetFieldObject<true>(MemberOffset(offset), newValue);
} else {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 1c4b93eb48..de64fdd2c9 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -248,7 +248,6 @@ JitCodeCache::JitCodeCache(MemMap* code_map,
code_end_(initial_code_capacity),
data_end_(initial_data_capacity),
last_collection_increased_code_cache_(false),
- last_update_time_ns_(0),
garbage_collect_code_(garbage_collect_code),
used_memory_for_data_(0),
used_memory_for_code_(0),
@@ -820,7 +819,6 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
// code.
GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr));
}
- last_update_time_ns_.store(NanoTime(), std::memory_order_release);
VLOG(jit)
<< "JIT added (osr=" << std::boolalpha << osr << std::noboolalpha << ") "
<< ArtMethod::PrettyMethod(method) << "@" << method
@@ -1541,7 +1539,7 @@ ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self ATTRIBUTE_UNU
// Make sure other threads see the data in the profiling info object before the
// store in the ArtMethod's ProfilingInfo pointer.
- QuasiAtomic::ThreadFenceRelease();
+ std::atomic_thread_fence(std::memory_order_release);
method->SetProfilingInfo(info);
profiling_infos_.push_back(info);
@@ -1646,10 +1644,6 @@ void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_loca
}
}
-uint64_t JitCodeCache::GetLastUpdateTimeNs() const {
- return last_update_time_ns_.load(std::memory_order_acquire);
-}
-
bool JitCodeCache::IsOsrCompiled(ArtMethod* method) {
MutexLock mu(Thread::Current(), lock_);
return osr_code_map_.find(method) != osr_code_map_.end();
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index dfa7ac0970..f1c99fb85a 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -407,10 +407,6 @@ class JitCodeCache {
// Whether the last collection round increased the code cache.
bool last_collection_increased_code_cache_ GUARDED_BY(lock_);
- // Last time the the code_cache was updated.
- // It is atomic to avoid locking when reading it.
- Atomic<uint64_t> last_update_time_ns_;
-
// Whether we can do garbage collection. Not 'const' as tests may override this.
bool garbage_collect_code_;
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index b78fcacc08..f309581735 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -80,18 +80,15 @@ namespace art {
// things not rendering correctly. E.g. b/16858794
static constexpr bool kWarnJniAbort = false;
-static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
- ObjPtr<mirror::Class> klass = GetCallingClass(self, /* num_frames */ 1);
- // If `klass` is null, it is an unattached native thread. Assume this is
- // *not* boot class path.
- return klass != nullptr && klass->IsBootStrapClassLoaded();
+static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return hiddenapi::IsCallerInPlatformDex(GetCallingClass(self, /* num_frames */ 1));
}
template<typename T>
ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return hiddenapi::ShouldBlockAccessToMember(
- member, self, IsCallerInBootClassPath, hiddenapi::kJNI);
+ member, self, IsCallerInPlatformDex, hiddenapi::kJNI);
}
// Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index f0898f49d3..72b31790f0 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -31,7 +31,6 @@
#include "dex/invoke_type.h"
#include "dex_cache.h"
#include "gc/heap-inl.h"
-#include "hidden_api.h"
#include "iftable.h"
#include "subtype_check.h"
#include "object-inl.h"
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index e110763300..f246d8b1c0 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1101,7 +1101,7 @@ mirror::Object* Monitor::MonitorEnter(Thread* self, mirror::Object* obj, bool tr
case LockWord::kFatLocked: {
// We should have done an acquire read of the lockword initially, to ensure
// visibility of the monitor data structure. Use an explicit fence instead.
- QuasiAtomic::ThreadFenceAcquire();
+ std::atomic_thread_fence(std::memory_order_acquire);
Monitor* mon = lock_word.FatLockMonitor();
if (trylock) {
return mon->TryLock(self) ? h_obj.Get() : nullptr;
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 505b745200..a5ade6f30f 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -78,6 +78,21 @@ static jboolean VMRuntime_hasUsedHiddenApi(JNIEnv*, jobject) {
return Runtime::Current()->HasPendingHiddenApiWarning() ? JNI_TRUE : JNI_FALSE;
}
+static void VMRuntime_setHiddenApiExemptions(JNIEnv* env,
+ jclass,
+ jobjectArray exemptions) {
+ std::vector<std::string> exemptions_vec;
+ int exemptions_length = env->GetArrayLength(exemptions);
+ for (int i = 0; i < exemptions_length; i++) {
+ jstring exemption = reinterpret_cast<jstring>(env->GetObjectArrayElement(exemptions, i));
+ const char* raw_exemption = env->GetStringUTFChars(exemption, nullptr);
+ exemptions_vec.push_back(raw_exemption);
+ env->ReleaseStringUTFChars(exemption, raw_exemption);
+ }
+
+ Runtime::Current()->SetHiddenApiExemptions(exemptions_vec);
+}
+
static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass,
jint length) {
ScopedFastNativeObjectAccess soa(env);
@@ -672,6 +687,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMRuntime, concurrentGC, "()V"),
NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"),
NATIVE_METHOD(VMRuntime, hasUsedHiddenApi, "()Z"),
+ NATIVE_METHOD(VMRuntime, setHiddenApiExemptions, "([Ljava/lang/String;)V"),
NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"),
FAST_NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"),
FAST_NATIVE_METHOD(VMRuntime, isNativeDebuggable, "()Z"),
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index d9a5096331..cf0a72a477 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -27,6 +27,7 @@
#include "base/mutex.h"
#include "base/runtime_debug.h"
#include "debugger.h"
+#include "hidden_api.h"
#include "java_vm_ext.h"
#include "jit/jit.h"
#include "jni_internal.h"
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index fc61c9597e..ad05856eaf 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -49,8 +49,8 @@
namespace art {
-// Returns true if the first non-ClassClass caller up the stack is in boot class path.
-static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+// Returns true if the first non-ClassClass caller up the stack is in a platform dex file.
+static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
// Walk the stack and find the first frame not from java.lang.Class.
// This is very expensive. Save this till the last.
struct FirstNonClassClassCallerVisitor : public StackVisitor {
@@ -82,7 +82,7 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator
FirstNonClassClassCallerVisitor visitor(self);
visitor.WalkStack();
return visitor.caller != nullptr &&
- visitor.caller->GetDeclaringClass()->IsBootStrapClassLoaded();
+ hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass());
}
// Returns true if the first non-ClassClass caller up the stack is not allowed to
@@ -90,7 +90,7 @@ static bool IsCallerInBootClassPath(Thread* self) REQUIRES_SHARED(Locks::mutator
ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
- return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInBootClassPath(self);
+ return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInPlatformDex(self);
}
// Returns true if the first non-ClassClass caller up the stack should not be
@@ -99,7 +99,7 @@ template<typename T>
ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return hiddenapi::ShouldBlockAccessToMember(
- member, self, IsCallerInBootClassPath, hiddenapi::kReflection);
+ member, self, IsCallerInPlatformDex, hiddenapi::kReflection);
}
// Returns true if a class member should be discoverable with reflection given
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 25f984f6be..fb00ae3967 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -116,7 +116,7 @@ static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong of
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
// TODO: A release store is likely to be faster on future processors.
- QuasiAtomic::ThreadFenceRelease();
+ std::atomic_thread_fence(std::memory_order_release);
// JNI must use non transactional mode.
obj->SetField32<false>(MemberOffset(offset), newValue);
}
@@ -152,7 +152,7 @@ static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong o
jlong newValue) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
- QuasiAtomic::ThreadFenceRelease();
+ std::atomic_thread_fence(std::memory_order_release);
// JNI must use non transactional mode.
obj->SetField64<false>(MemberOffset(offset), newValue);
}
@@ -194,7 +194,7 @@ static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue);
- QuasiAtomic::ThreadFenceRelease();
+ std::atomic_thread_fence(std::memory_order_release);
// JNI must use non transactional mode.
obj->SetFieldObject<false>(MemberOffset(offset), newValue);
}
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index c26c26e544..14f3f45f9e 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -287,7 +287,8 @@ void DumpNativeStack(std::ostream& os,
BacktraceMap* existing_map,
const char* prefix,
ArtMethod* current_method,
- void* ucontext_ptr) {
+ void* ucontext_ptr,
+ bool skip_frames) {
// b/18119146
if (RUNNING_ON_MEMORY_TOOL != 0) {
return;
@@ -300,6 +301,7 @@ void DumpNativeStack(std::ostream& os,
map = tmp_map.get();
}
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid, map));
+ backtrace->SetSkipFrames(skip_frames);
if (!backtrace->Unwind(0, reinterpret_cast<ucontext*>(ucontext_ptr))) {
os << prefix << "(backtrace::Unwind failed for thread " << tid
<< ": " << backtrace->GetErrorString(backtrace->GetError()) << ")" << std::endl;
@@ -427,7 +429,8 @@ void DumpNativeStack(std::ostream& os ATTRIBUTE_UNUSED,
BacktraceMap* existing_map ATTRIBUTE_UNUSED,
const char* prefix ATTRIBUTE_UNUSED,
ArtMethod* current_method ATTRIBUTE_UNUSED,
- void* ucontext_ptr ATTRIBUTE_UNUSED) {
+ void* ucontext_ptr ATTRIBUTE_UNUSED,
+ bool skip_frames ATTRIBUTE_UNUSED) {
}
void DumpKernelStack(std::ostream& os ATTRIBUTE_UNUSED,
diff --git a/runtime/native_stack_dump.h b/runtime/native_stack_dump.h
index d64bc824a5..ad4bfab0e9 100644
--- a/runtime/native_stack_dump.h
+++ b/runtime/native_stack_dump.h
@@ -35,7 +35,8 @@ void DumpNativeStack(std::ostream& os,
BacktraceMap* map = nullptr,
const char* prefix = "",
ArtMethod* current_method = nullptr,
- void* ucontext = nullptr)
+ void* ucontext = nullptr,
+ bool skip_frames = true)
NO_THREAD_SAFETY_ANALYSIS;
// Dumps the kernel stack for thread 'tid' to 'os'. Note that this is only available on linux-x86.
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 5518eb2c49..470287b449 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -161,10 +161,6 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"})
.WithValues({true, false})
.IntoKey(M::EnableHSpaceCompactForOOM)
- .Define("-XX:DumpNativeStackOnSigQuit:_")
- .WithType<bool>()
- .WithValueMap({{"false", false}, {"true", true}})
- .IntoKey(M::DumpNativeStackOnSigQuit)
.Define("-XX:MadviseRandomAccess:_")
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
@@ -735,7 +731,6 @@ void ParsedOptions::Usage(const char* fmt, ...) {
UsageMessage(stream, " -XX:BackgroundGC=none\n");
UsageMessage(stream, " -XX:LargeObjectSpace={disabled,map,freelist}\n");
UsageMessage(stream, " -XX:LargeObjectThreshold=N\n");
- UsageMessage(stream, " -XX:DumpNativeStackOnSigQuit=booleanvalue\n");
UsageMessage(stream, " -XX:MadviseRandomAccess:booleanvalue\n");
UsageMessage(stream, " -XX:SlowDebug={false,true}\n");
UsageMessage(stream, " -Xmethod-trace\n");
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 9a626bab00..00ccc19b5d 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -86,6 +86,7 @@
#include "gc/space/space-inl.h"
#include "gc/system_weak.h"
#include "handle_scope-inl.h"
+#include "hidden_api.h"
#include "image-inl.h"
#include "instrumentation.h"
#include "intern_table.h"
@@ -271,7 +272,6 @@ Runtime::Runtime()
pending_hidden_api_warning_(false),
dedupe_hidden_api_warnings_(true),
always_set_hidden_api_warning_flag_(false),
- dump_native_stack_on_sig_quit_(true),
pruned_dalvik_cache_(false),
// Initially assume we perceive jank in case the process state is never updated.
process_state_(kProcessStateJankPerceptible),
@@ -1153,7 +1153,6 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC);
dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat);
image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat);
- dump_native_stack_on_sig_quit_ = runtime_options.GetOrDefault(Opt::DumpNativeStackOnSigQuit);
vfprintf_ = runtime_options.GetOrDefault(Opt::HookVfprintf);
exit_ = runtime_options.GetOrDefault(Opt::HookExit);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index dba31b2939..f5f3e31656 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -536,6 +536,14 @@ class Runtime {
pending_hidden_api_warning_ = value;
}
+ void SetHiddenApiExemptions(const std::vector<std::string>& exemptions) {
+ hidden_api_exemptions_ = exemptions;
+ }
+
+ const std::vector<std::string>& GetHiddenApiExemptions() {
+ return hidden_api_exemptions_;
+ }
+
bool HasPendingHiddenApiWarning() const {
return pending_hidden_api_warning_;
}
@@ -655,10 +663,6 @@ class Runtime {
safe_mode_ = mode;
}
- bool GetDumpNativeStackOnSigQuit() const {
- return dump_native_stack_on_sig_quit_;
- }
-
bool GetPrunedDalvikCache() const {
return pruned_dalvik_cache_;
}
@@ -996,6 +1000,9 @@ class Runtime {
// Whether access checks on hidden API should be performed.
hiddenapi::EnforcementPolicy hidden_api_policy_;
+ // List of signature prefixes of methods that have been removed from the blacklist
+ std::vector<std::string> hidden_api_exemptions_;
+
// Whether the application has used an API which is not restricted but we
// should issue a warning about it.
bool pending_hidden_api_warning_;
@@ -1009,9 +1016,6 @@ class Runtime {
// when there is a warning. This is only used for testing.
bool always_set_hidden_api_warning_flag_;
- // Whether threads should dump their native stack on SIGQUIT.
- bool dump_native_stack_on_sig_quit_;
-
// Whether the dalvik cache was pruned when initializing the runtime.
bool pruned_dalvik_cache_;
diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc
index 59af9187f9..41bfb58d93 100644
--- a/runtime/runtime_common.cc
+++ b/runtime/runtime_common.cc
@@ -41,7 +41,6 @@ namespace art {
using android::base::StringPrintf;
static constexpr bool kUseSigRTTimeout = true;
-static constexpr bool kDumpNativeStackOnTimeout = true;
const char* GetSignalName(int signal_number) {
switch (signal_number) {
@@ -441,7 +440,7 @@ void HandleUnexpectedSignalCommon(int signal_number,
// Special timeout signal. Try to dump all threads.
// Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts
// are of value here.
- runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout);
+ runtime->GetThreadList()->Dump(std::cerr);
std::cerr << std::endl;
}
diff --git a/runtime/runtime_common.h b/runtime/runtime_common.h
index 3fba441b55..698d0603ee 100644
--- a/runtime/runtime_common.h
+++ b/runtime/runtime_common.h
@@ -40,7 +40,9 @@ struct Backtrace {
public:
explicit Backtrace(void* raw_context) : raw_context_(raw_context) {}
void Dump(std::ostream& os) const {
- DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_);
+ // This is a backtrace from a crash, do not skip any frames in case the
+ // crash is in the unwinder itself.
+ DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_, false);
}
private:
// Stores the context of the signal that was unexpected and will terminate the runtime. The
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 4121ad69ed..dcb1335023 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -70,7 +70,6 @@ RUNTIME_OPTIONS_KEY (Unit, LowMemoryMode)
RUNTIME_OPTIONS_KEY (bool, UseTLAB, (kUseTlab || kUseReadBarrier))
RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true)
RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, false)
-RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true)
RUNTIME_OPTIONS_KEY (bool, MadviseRandomAccess, false)
RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold)
diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h
index 3b1d5f8c4a..1fe62e8f46 100644
--- a/runtime/subtype_check.h
+++ b/runtime/subtype_check.h
@@ -286,6 +286,17 @@ struct SubtypeCheck {
return SubtypeCheckInfo::kUninitialized;
}
+ // Retrieve the state of this class's SubtypeCheckInfo.
+ //
+ // Cost: O(Depth(Class)).
+ //
+ // Returns: The precise SubtypeCheckInfo::State.
+ static SubtypeCheckInfo::State GetState(ClassPtr klass)
+ REQUIRES(Locks::subtype_check_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetSubtypeCheckInfo(klass).GetState();
+ }
+
// Retrieve the path to root bitstring as a plain uintN_t value that is amenable to
// be used by a fast check "encoded_src & mask_target == encoded_target".
//
@@ -308,8 +319,9 @@ struct SubtypeCheck {
static BitString::StorageType GetEncodedPathToRootForTarget(ClassPtr klass)
REQUIRES(Locks::subtype_check_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState());
- return GetSubtypeCheckInfo(klass).GetEncodedPathToRoot();
+ SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass);
+ DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState());
+ return sci.GetEncodedPathToRoot();
}
// Retrieve the path to root bitstring mask as a plain uintN_t value that is amenable to
@@ -321,8 +333,9 @@ struct SubtypeCheck {
static BitString::StorageType GetEncodedPathToRootMask(ClassPtr klass)
REQUIRES(Locks::subtype_check_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState());
- return GetSubtypeCheckInfo(klass).GetEncodedPathToRootMask();
+ SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass);
+ DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState());
+ return sci.GetEncodedPathToRootMask();
}
// Is the source class a subclass of the target?
diff --git a/runtime/thread.cc b/runtime/thread.cc
index b13d8ec42a..50cf9e0bc4 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1161,10 +1161,9 @@ void Thread::ShortDump(std::ostream& os) const {
<< "]";
}
-void Thread::Dump(std::ostream& os, bool dump_native_stack, BacktraceMap* backtrace_map,
- bool force_dump_stack) const {
+void Thread::Dump(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const {
DumpState(os);
- DumpStack(os, dump_native_stack, backtrace_map, force_dump_stack);
+ DumpStack(os, backtrace_map, force_dump_stack);
}
mirror::String* Thread::GetThreadName() const {
@@ -1968,10 +1967,7 @@ void Thread::DumpJavaStack(std::ostream& os, bool check_suspended, bool dump_loc
}
}
-void Thread::DumpStack(std::ostream& os,
- bool dump_native_stack,
- BacktraceMap* backtrace_map,
- bool force_dump_stack) const {
+void Thread::DumpStack(std::ostream& os, BacktraceMap* backtrace_map, bool force_dump_stack) const {
// TODO: we call this code when dying but may not have suspended the thread ourself. The
// IsSuspended check is therefore racy with the use for dumping (normally we inhibit
// the race with the thread_suspend_count_lock_).
@@ -1984,7 +1980,7 @@ void Thread::DumpStack(std::ostream& os,
}
if (safe_to_dump || force_dump_stack) {
// If we're currently in native code, dump that stack before dumping the managed stack.
- if (dump_native_stack && (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this))) {
+ if (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this)) {
DumpKernelStack(os, GetTid(), " kernel: ", false);
ArtMethod* method =
GetCurrentMethod(nullptr,
diff --git a/runtime/thread.h b/runtime/thread.h
index 22b77eea64..af1401ee93 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -207,7 +207,6 @@ class Thread {
// Dumps the detailed thread state and the thread stack (used for SIGQUIT).
void Dump(std::ostream& os,
- bool dump_native_stack = true,
BacktraceMap* backtrace_map = nullptr,
bool force_dump_stack = false) const
REQUIRES(!Locks::thread_suspend_count_lock_)
@@ -1318,7 +1317,6 @@ class Thread {
void DumpState(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_);
void DumpStack(std::ostream& os,
- bool dump_native_stack = true,
BacktraceMap* backtrace_map = nullptr,
bool force_dump_stack = false) const
REQUIRES(!Locks::thread_suspend_count_lock_)
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 44af867d60..ee683992ba 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -152,9 +152,8 @@ void ThreadList::DumpForSigQuit(std::ostream& os) {
suspend_all_historam_.PrintConfidenceIntervals(os, 0.99, data); // Dump time to suspend.
}
}
- bool dump_native_stack = Runtime::Current()->GetDumpNativeStackOnSigQuit();
- Dump(os, dump_native_stack);
- DumpUnattachedThreads(os, dump_native_stack && kDumpUnattachedThreadNativeStackForSigQuit);
+ Dump(os);
+ DumpUnattachedThreads(os, kDumpUnattachedThreadNativeStackForSigQuit);
}
static void DumpUnattachedThread(std::ostream& os, pid_t tid, bool dump_native_stack)
@@ -201,11 +200,10 @@ static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 100000 : 20000;
// A closure used by Thread::Dump.
class DumpCheckpoint FINAL : public Closure {
public:
- DumpCheckpoint(std::ostream* os, bool dump_native_stack)
+ explicit DumpCheckpoint(std::ostream* os)
: os_(os),
barrier_(0),
- backtrace_map_(dump_native_stack ? BacktraceMap::Create(getpid()) : nullptr),
- dump_native_stack_(dump_native_stack) {
+ backtrace_map_(BacktraceMap::Create(getpid())) {
if (backtrace_map_ != nullptr) {
backtrace_map_->SetSuffixesToIgnore(std::vector<std::string> { "oat", "odex" });
}
@@ -219,7 +217,7 @@ class DumpCheckpoint FINAL : public Closure {
std::ostringstream local_os;
{
ScopedObjectAccess soa(self);
- thread->Dump(local_os, dump_native_stack_, backtrace_map_.get());
+ thread->Dump(local_os, backtrace_map_.get());
}
{
// Use the logging lock to ensure serialization when writing to the common ostream.
@@ -247,18 +245,16 @@ class DumpCheckpoint FINAL : public Closure {
Barrier barrier_;
// A backtrace map, so that all threads use a shared info and don't reacquire/parse separately.
std::unique_ptr<BacktraceMap> backtrace_map_;
- // Whether we should dump the native stack.
- const bool dump_native_stack_;
};
-void ThreadList::Dump(std::ostream& os, bool dump_native_stack) {
+void ThreadList::Dump(std::ostream& os) {
Thread* self = Thread::Current();
{
MutexLock mu(self, *Locks::thread_list_lock_);
os << "DALVIK THREADS (" << list_.size() << "):\n";
}
if (self != nullptr) {
- DumpCheckpoint checkpoint(&os, dump_native_stack);
+ DumpCheckpoint checkpoint(&os);
size_t threads_running_checkpoint;
{
// Use SOA to prevent deadlocks if multiple threads are calling Dump() at the same time.
@@ -269,7 +265,7 @@ void ThreadList::Dump(std::ostream& os, bool dump_native_stack) {
checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint);
}
} else {
- DumpUnattachedThreads(os, dump_native_stack);
+ DumpUnattachedThreads(os, /* dump_native_stack */ true);
}
}
@@ -491,7 +487,6 @@ void ThreadList::RunEmptyCheckpoint() {
// Found a runnable thread that hasn't responded to the empty checkpoint request.
// Assume it's stuck and safe to dump its stack.
thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT),
- /*dump_native_stack*/ true,
/*backtrace_map*/ nullptr,
/*force_dump_stack*/ true);
}
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 895c1a41ce..09b10d2ad3 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -57,7 +57,7 @@ class ThreadList {
void DumpForSigQuit(std::ostream& os)
REQUIRES(!Locks::thread_list_lock_, !Locks::mutator_lock_);
// For thread suspend timeout dumps.
- void Dump(std::ostream& os, bool dump_native_stack = true)
+ void Dump(std::ostream& os)
REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
pid_t GetLockOwner(); // For SignalCatcher.
diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java
index ff6186b240..51d4a81150 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -70,15 +70,17 @@ public class Main {
return s[0];
}
- private static void printWeakReference(WeakReference<FinalizerTest> wimp) {
+ public static void main(String[] args) {
+ WeakReference<FinalizerTest> wimp = makeRef();
// Reference ft so we are sure the WeakReference cannot be cleared.
+ // Note: This is very fragile. It was previously in a helper function but that
+ // doesn't work for JIT-on-first-use with --gcstress where the object would be
+ // collected when JIT internally allocates an array. Also adding a scope around
+ // the keepLive lifetime somehow keeps a non-null `keepLive` around and makes
+ // the test fail (even when keeping the `null` assignment). b/76454261
FinalizerTest keepLive = wimp.get();
System.out.println("wimp: " + wimpString(wimp));
- }
-
- public static void main(String[] args) {
- WeakReference<FinalizerTest> wimp = makeRef();
- printWeakReference(wimp);
+ keepLive = null; // Clear the reference.
/* this will try to collect and finalize ft */
System.out.println("gc");
diff --git a/test/1940-ddms-ext/check b/test/1940-ddms-ext/check
deleted file mode 100755
index 91966b41a0..0000000000
--- a/test/1940-ddms-ext/check
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/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.
-
-# Need to pull out the describeException ouput since that won't be there on
-# device.
-./remove_error.py "$2" "./expected_error.txt" > "$2.tmp"
-
-./default-check "$1" "$2.tmp"
diff --git a/test/1940-ddms-ext/expected_error.txt b/test/1940-ddms-ext/expected_error.txt
deleted file mode 100644
index 73883b46e2..0000000000
--- a/test/1940-ddms-ext/expected_error.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-java.lang.ArrayIndexOutOfBoundsException: byte[] offset=12 length=55 src.length=1
- at art.Test1940.processChunk(Native Method)
- at art.Test1940.run(Test1940.java:156)
- at Main.main(Main.java:19)
diff --git a/test/1940-ddms-ext/remove_error.py b/test/1940-ddms-ext/remove_error.py
deleted file mode 100755
index 638c479a31..0000000000
--- a/test/1940-ddms-ext/remove_error.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2018 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 argparse
-
-def main():
- parser = argparse.ArgumentParser()
- parser.add_argument('input_data', type=open)
- parser.add_argument('expected_error', type=str)
- args = parser.parse_args()
-
- for line in map(str.rstrip, args.input_data.readlines()):
- print_full = True
- with open(args.expected_error) as err_file:
- for err_line in map(str.rstrip, err_file):
- if line.startswith(err_line):
- print_full = False
- if line != err_line:
- print(line[len(err_line):])
- break
- if print_full and line != '':
- print(line)
-
-if __name__ == '__main__':
- main()
diff --git a/test/411-optimizing-arith-mul/info.txt b/test/411-optimizing-arith-mul/info.txt
deleted file mode 100644
index 10155512f0..0000000000
--- a/test/411-optimizing-arith-mul/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for basic arithmethic operations.
diff --git a/test/411-optimizing-arith-mul/expected.txt b/test/411-optimizing-arith/expected.txt
index e69de29bb2..e69de29bb2 100644
--- a/test/411-optimizing-arith-mul/expected.txt
+++ b/test/411-optimizing-arith/expected.txt
diff --git a/test/411-optimizing-arith/info.txt b/test/411-optimizing-arith/info.txt
new file mode 100644
index 0000000000..42be5d564f
--- /dev/null
+++ b/test/411-optimizing-arith/info.txt
@@ -0,0 +1,7 @@
+Tests for basic arithmethic operations:
+ - multiply,
+ - subtract,
+ - negate,
+ - division,
+ - modulo (rem),
+ - shifts.
diff --git a/test/417-optimizing-arith-div/src/Main.java b/test/411-optimizing-arith/src/DivTest.java
index 68e89b3eb2..7696d0a806 100644
--- a/test/417-optimizing-arith-div/src/Main.java
+++ b/test/411-optimizing-arith/src/DivTest.java
@@ -16,7 +16,7 @@
// Note that $opt$ is a marker for the optimizing compiler to test
// it does compile the method.
-public class Main {
+public class DivTest {
public static void expectEquals(int expected, int result) {
if (expected != result) {
@@ -98,11 +98,7 @@ public class Main {
}
}
- public static void main(String[] args) {
- div();
- }
-
- public static void div() {
+ public static void main() {
divInt();
divLong();
divFloat();
diff --git a/test/411-optimizing-arith/src/Main.java b/test/411-optimizing-arith/src/Main.java
new file mode 100644
index 0000000000..e1a43d3b57
--- /dev/null
+++ b/test/411-optimizing-arith/src/Main.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 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[]) {
+ MulTest.main();
+ SubTest.main();
+ NegTest.main();
+ DivTest.main();
+ RemTest.main();
+ ShiftsTest.main();
+ }
+}
diff --git a/test/411-optimizing-arith-mul/src/Main.java b/test/411-optimizing-arith/src/MulTest.java
index 60e418e1e5..b9bffca0d1 100644
--- a/test/411-optimizing-arith-mul/src/Main.java
+++ b/test/411-optimizing-arith/src/MulTest.java
@@ -16,7 +16,7 @@
// Note that $opt$ is a marker for the optimizing compiler to test
// it does compile the method.
-public class Main {
+public class MulTest {
public static void expectEquals(int expected, int result) {
if (expected != result) {
@@ -72,11 +72,7 @@ public class Main {
}
}
- public static void main(String[] args) {
- mul();
- }
-
- public static void mul() {
+ public static void main() {
mulInt();
mulLong();
mulFloat();
@@ -129,9 +125,12 @@ public class Main {
expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(-2F, 3.40282346638528860e+38F));
expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(2F, Float.NEGATIVE_INFINITY));
expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(-2F, Float.NEGATIVE_INFINITY));
- expectEquals(Float.NEGATIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));
- expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
- expectEquals(Float.POSITIVE_INFINITY, $opt$Mul(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
+ expectEquals(Float.NEGATIVE_INFINITY,
+ $opt$Mul(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));
+ expectEquals(Float.POSITIVE_INFINITY,
+ $opt$Mul(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
+ expectEquals(Float.POSITIVE_INFINITY,
+ $opt$Mul(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
}
private static void mulDouble() {
diff --git a/test/415-optimizing-arith-neg/src/Main.java b/test/411-optimizing-arith/src/NegTest.java
index c53b639d40..83047269bb 100644
--- a/test/415-optimizing-arith-neg/src/Main.java
+++ b/test/411-optimizing-arith/src/NegTest.java
@@ -17,7 +17,7 @@
// Note that $opt$ is a marker for the optimizing compiler to test
// it does compile the method, and that $noinline$ is a marker to
// test that it does not inline it.
-public class Main {
+public class NegTest {
public static void assertEquals(int expected, int result) {
if (expected != result) {
@@ -67,7 +67,7 @@ public class Main {
}
}
- public static void main(String[] args) {
+ public static void main() {
negInt();
$opt$noinline$InplaceNegOneInt(1);
@@ -169,55 +169,29 @@ public class Main {
}
- static boolean doThrow = false;
-
private static void $opt$noinline$InplaceNegOneInt(int a) {
- if (doThrow) {
- // Try defeating inlining.
- throw new Error();
- }
a = -a;
assertEquals(-1, a);
}
private static void $opt$noinline$InplaceNegOneLong(long a) {
- if (doThrow) {
- // Try defeating inlining.
- throw new Error();
- }
a = -a;
assertEquals(-1L, a);
}
private static int $opt$noinline$NegInt(int a){
- if (doThrow) {
- // Try defeating inlining.
- throw new Error();
- }
return -a;
}
private static long $opt$noinline$NegLong(long a){
- if (doThrow) {
- // Try defeating inlining.
- throw new Error();
- }
return -a;
}
private static float $opt$noinline$NegFloat(float a){
- if (doThrow) {
- // Try defeating inlining.
- throw new Error();
- }
return -a;
}
private static double $opt$noinline$NegDouble(double a){
- if (doThrow) {
- // Try defeating inlining.
- throw new Error();
- }
return -a;
}
}
diff --git a/test/428-optimizing-arith-rem/src/Main.java b/test/411-optimizing-arith/src/RemTest.java
index 3f77318e6c..1b31f63569 100644
--- a/test/428-optimizing-arith-rem/src/Main.java
+++ b/test/411-optimizing-arith/src/RemTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-public class Main {
+public class RemTest {
- public static void main(String[] args) {
+ public static void main() {
remInt();
remLong();
}
diff --git a/test/431-optimizing-arith-shifts/src/Main.java b/test/411-optimizing-arith/src/ShiftsTest.java
index b7a112f6a3..139ff70bf0 100644
--- a/test/431-optimizing-arith-shifts/src/Main.java
+++ b/test/411-optimizing-arith/src/ShiftsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-public class Main {
+public class ShiftsTest {
public static void expectEquals(int expected, int result) {
if (expected != result) {
@@ -28,7 +28,7 @@ public class Main {
}
}
- public static void main(String[] args) {
+ public static void main() {
testShlInt();
testShlLong();
testShrInt();
diff --git a/test/414-optimizing-arith-sub/src/Main.java b/test/411-optimizing-arith/src/SubTest.java
index b4531cdfd4..9c9ea92f20 100644
--- a/test/414-optimizing-arith-sub/src/Main.java
+++ b/test/411-optimizing-arith/src/SubTest.java
@@ -16,7 +16,7 @@
// Note that $opt$ is a marker for the optimizing compiler to test
// it does compile the method.
-public class Main {
+public class SubTest {
public static void expectEquals(int expected, int result) {
if (expected != result) {
@@ -70,7 +70,7 @@ public class Main {
}
}
- public static void main(String[] args) {
+ public static void main() {
subInt();
subLong();
subFloat();
diff --git a/test/414-optimizing-arith-sub/expected.txt b/test/414-optimizing-arith-sub/expected.txt
deleted file mode 100644
index e69de29bb2..0000000000
--- a/test/414-optimizing-arith-sub/expected.txt
+++ /dev/null
diff --git a/test/414-optimizing-arith-sub/info.txt b/test/414-optimizing-arith-sub/info.txt
deleted file mode 100644
index 1eaa14887b..0000000000
--- a/test/414-optimizing-arith-sub/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Subtraction tests.
diff --git a/test/415-optimizing-arith-neg/expected.txt b/test/415-optimizing-arith-neg/expected.txt
deleted file mode 100644
index e69de29bb2..0000000000
--- a/test/415-optimizing-arith-neg/expected.txt
+++ /dev/null
diff --git a/test/415-optimizing-arith-neg/info.txt b/test/415-optimizing-arith-neg/info.txt
deleted file mode 100644
index 8494aad938..0000000000
--- a/test/415-optimizing-arith-neg/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for arithmetic negation operations.
diff --git a/test/417-optimizing-arith-div/expected.txt b/test/417-optimizing-arith-div/expected.txt
deleted file mode 100644
index e69de29bb2..0000000000
--- a/test/417-optimizing-arith-div/expected.txt
+++ /dev/null
diff --git a/test/417-optimizing-arith-div/info.txt b/test/417-optimizing-arith-div/info.txt
deleted file mode 100644
index 1374b0ffb3..0000000000
--- a/test/417-optimizing-arith-div/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for division operation.
diff --git a/test/428-optimizing-arith-rem/expected.txt b/test/428-optimizing-arith-rem/expected.txt
deleted file mode 100644
index e69de29bb2..0000000000
--- a/test/428-optimizing-arith-rem/expected.txt
+++ /dev/null
diff --git a/test/428-optimizing-arith-rem/info.txt b/test/428-optimizing-arith-rem/info.txt
deleted file mode 100644
index 3e37ffeee8..0000000000
--- a/test/428-optimizing-arith-rem/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for modulo (rem) operation.
diff --git a/test/431-optimizing-arith-shifts/expected.txt b/test/431-optimizing-arith-shifts/expected.txt
deleted file mode 100644
index e69de29bb2..0000000000
--- a/test/431-optimizing-arith-shifts/expected.txt
+++ /dev/null
diff --git a/test/431-optimizing-arith-shifts/info.txt b/test/431-optimizing-arith-shifts/info.txt
deleted file mode 100644
index 14ff264662..0000000000
--- a/test/431-optimizing-arith-shifts/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for shift operations.
diff --git a/test/646-checker-hadd-short/src/Main.java b/test/646-checker-hadd-short/src/Main.java
index 85c2fcaf22..c09da8125b 100644
--- a/test/646-checker-hadd-short/src/Main.java
+++ b/test/646-checker-hadd-short/src/Main.java
@@ -26,6 +26,10 @@ public class Main {
static short[] sB2 = new short[M];
static short[] sBo = new short[M];
+ private static int $inline$mone() {
+ return -1;
+ }
+
/// CHECK-START: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (before)
/// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
@@ -184,6 +188,35 @@ public class Main {
}
}
+ /// CHECK-START: void Main.rounding_halving_add_signed_alt3(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<M1:i\d+>> IntConstant -1 loop:none
+ /// CHECK-DAG: <<I9:i\d+>> IntConstant 9 loop:none
+ /// CHECK-DAG: <<M9:i\d+>> IntConstant -9 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Get1>>,<<I9>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Get2>>,<<M9>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add3:i\d+>> Add [<<Add1>>,<<Add2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Add3>>,<<M1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Sub>>,<<I1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed_alt3(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] packed_type:Int16 rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<HAdd>>] loop:<<Loop>> outer_loop:none
+ private static void rounding_halving_add_signed_alt3(short[] b1, short[] b2, short[] bo) {
+ int min_length = Math.min(bo.length, Math.min(b1.length, b2.length));
+ for (int i = 0; i < min_length; i++) {
+ // Computations that cancel to adding 1 also do not confuse recognition.
+ bo[i] = (short) (((b1[i] + 9) + (b2[i] - 9) - $inline$mone()) >> 1);
+ }
+ }
+
/// CHECK-START: void Main.rounding_halving_add_unsigned(short[], short[], short[]) instruction_simplifier (before)
/// CHECK-DAG: <<I1:i\d+>> IntConstant 1 loop:none
/// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
@@ -366,6 +399,11 @@ public class Main {
short e = (short) ((sB1[i] + sB2[i] + 1) >> 1);
expectEquals(e, sBo[i]);
}
+ rounding_halving_add_signed_alt3(sB1, sB2, sBo);
+ for (int i = 0; i < M; i++) {
+ short e = (short) ((sB1[i] + sB2[i] + 1) >> 1);
+ expectEquals(e, sBo[i]);
+ }
rounding_halving_add_unsigned(sB1, sB2, sBo);
for (int i = 0; i < M; i++) {
short e = (short) (((sB1[i] & 0xffff) + (sB2[i] & 0xffff) + 1) >> 1);
diff --git a/test/651-checker-byte-simd-minmax/expected.txt b/test/651-checker-byte-simd-minmax/expected.txt
deleted file mode 100644
index b0aad4deb5..0000000000
--- a/test/651-checker-byte-simd-minmax/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/651-checker-char-simd-minmax/expected.txt b/test/651-checker-char-simd-minmax/expected.txt
deleted file mode 100644
index b0aad4deb5..0000000000
--- a/test/651-checker-char-simd-minmax/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/651-checker-char-simd-minmax/info.txt b/test/651-checker-char-simd-minmax/info.txt
deleted file mode 100644
index 73af1242c0..0000000000
--- a/test/651-checker-char-simd-minmax/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Functional tests on min/max SIMD vectorization.
diff --git a/test/651-checker-double-simd-minmax/expected.txt b/test/651-checker-double-simd-minmax/expected.txt
deleted file mode 100644
index b0aad4deb5..0000000000
--- a/test/651-checker-double-simd-minmax/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/651-checker-double-simd-minmax/info.txt b/test/651-checker-double-simd-minmax/info.txt
deleted file mode 100644
index 73af1242c0..0000000000
--- a/test/651-checker-double-simd-minmax/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Functional tests on min/max SIMD vectorization.
diff --git a/test/651-checker-float-simd-minmax/expected.txt b/test/651-checker-float-simd-minmax/expected.txt
deleted file mode 100644
index b0aad4deb5..0000000000
--- a/test/651-checker-float-simd-minmax/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/651-checker-float-simd-minmax/info.txt b/test/651-checker-float-simd-minmax/info.txt
deleted file mode 100644
index 73af1242c0..0000000000
--- a/test/651-checker-float-simd-minmax/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Functional tests on min/max SIMD vectorization.
diff --git a/test/651-checker-int-simd-minmax/expected.txt b/test/651-checker-int-simd-minmax/expected.txt
deleted file mode 100644
index b0aad4deb5..0000000000
--- a/test/651-checker-int-simd-minmax/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/651-checker-int-simd-minmax/info.txt b/test/651-checker-int-simd-minmax/info.txt
deleted file mode 100644
index 73af1242c0..0000000000
--- a/test/651-checker-int-simd-minmax/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Functional tests on min/max SIMD vectorization.
diff --git a/test/651-checker-long-simd-minmax/expected.txt b/test/651-checker-long-simd-minmax/expected.txt
deleted file mode 100644
index b0aad4deb5..0000000000
--- a/test/651-checker-long-simd-minmax/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/651-checker-long-simd-minmax/info.txt b/test/651-checker-long-simd-minmax/info.txt
deleted file mode 100644
index 73af1242c0..0000000000
--- a/test/651-checker-long-simd-minmax/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Functional tests on min/max SIMD vectorization.
diff --git a/test/651-checker-short-simd-minmax/expected.txt b/test/651-checker-short-simd-minmax/expected.txt
deleted file mode 100644
index b0aad4deb5..0000000000
--- a/test/651-checker-short-simd-minmax/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-passed
diff --git a/test/651-checker-short-simd-minmax/info.txt b/test/651-checker-short-simd-minmax/info.txt
deleted file mode 100644
index 73af1242c0..0000000000
--- a/test/651-checker-short-simd-minmax/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Functional tests on min/max SIMD vectorization.
diff --git a/test/651-checker-simd-minmax/expected.txt b/test/651-checker-simd-minmax/expected.txt
new file mode 100644
index 0000000000..f362c45b77
--- /dev/null
+++ b/test/651-checker-simd-minmax/expected.txt
@@ -0,0 +1,7 @@
+ByteSimdMinMax passed
+CharSimdMinMax passed
+ShortSimdMinMax passed
+IntSimdMinMax passed
+LongSimdMinMax passed
+DoubleSimdMinMax passed
+FloatSimdMinMax passed
diff --git a/test/651-checker-byte-simd-minmax/info.txt b/test/651-checker-simd-minmax/info.txt
index 73af1242c0..73af1242c0 100644
--- a/test/651-checker-byte-simd-minmax/info.txt
+++ b/test/651-checker-simd-minmax/info.txt
diff --git a/test/651-checker-byte-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
index 4e667bbc6f..b9954947f7 100644
--- a/test/651-checker-byte-simd-minmax/src/Main.java
+++ b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
@@ -17,9 +17,9 @@
/**
* Tests for MIN/MAX vectorization.
*/
-public class Main {
+public class ByteSimdMinMax {
- /// CHECK-START: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (before)
+ /// CHECK-START: void ByteSimdMinMax.doitMin(byte[], byte[], byte[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -27,7 +27,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMin(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
@@ -39,7 +39,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMinUnsigned(byte[], byte[], byte[]) instruction_simplifier (before)
+ /// CHECK-START: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) instruction_simplifier (before)
/// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -50,7 +50,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (before)
+ /// CHECK-START: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:a\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:a\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -58,7 +58,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
@@ -70,7 +70,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (before)
+ /// CHECK-START: void ByteSimdMinMax.doitMax(byte[], byte[], byte[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -78,7 +78,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMax(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
@@ -90,7 +90,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMaxUnsigned(byte[], byte[], byte[]) instruction_simplifier (before)
+ /// CHECK-START: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) instruction_simplifier (before)
/// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -101,7 +101,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (before)
+ /// CHECK-START: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:a\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:a\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -109,7 +109,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
@@ -121,7 +121,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMin100(byte[], byte[]) loop_optimization (before)
+ /// CHECK-START: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (before)
/// CHECK-DAG: <<I100:i\d+>> IntConstant 100 loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get:b\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -129,7 +129,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(byte[], byte[]) loop_optimization (after)
+ /// CHECK-START-{ARM64,MIPS64}: void ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<I100:i\d+>> IntConstant 100 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>] loop:none
/// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
@@ -142,7 +142,7 @@ public class Main {
}
}
- public static void main(String[] args) {
+ public static void main() {
// Initialize cross-values for all possible values.
int total = 256 * 256;
byte[] x = new byte[total];
@@ -185,7 +185,7 @@ public class Main {
expectEquals(expected, x[i]);
}
- System.out.println("passed");
+ System.out.println("ByteSimdMinMax passed");
}
private static void expectEquals(byte expected, byte result) {
diff --git a/test/651-checker-char-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/CharSimdMinMax.java
index 520e10b6c1..30169c4591 100644
--- a/test/651-checker-char-simd-minmax/src/Main.java
+++ b/test/651-checker-simd-minmax/src/CharSimdMinMax.java
@@ -17,9 +17,9 @@
/**
* Tests for MIN/MAX vectorization.
*/
-public class Main {
+public class CharSimdMinMax {
- /// CHECK-START: void Main.doitMin(char[], char[], char[]) loop_optimization (before)
+ /// CHECK-START: void CharSimdMinMax.doitMin(char[], char[], char[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -27,7 +27,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void CharSimdMinMax.doitMin(char[], char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
@@ -39,7 +39,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMax(char[], char[], char[]) loop_optimization (before)
+ /// CHECK-START: void CharSimdMinMax.doitMax(char[], char[], char[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -47,7 +47,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void CharSimdMinMax.doitMax(char[], char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
@@ -59,7 +59,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMin100(char[], char[]) loop_optimization (before)
+ /// CHECK-START: void CharSimdMinMax.doitMin100(char[], char[]) loop_optimization (before)
/// CHECK-DAG: <<I100:i\d+>> IntConstant 100 loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -67,7 +67,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(char[], char[]) loop_optimization (after)
+ /// CHECK-START-{ARM64,MIPS64}: void CharSimdMinMax.doitMin100(char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<I100:i\d+>> IntConstant 100 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>] loop:none
/// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
@@ -80,7 +80,7 @@ public class Main {
}
}
- public static void main(String[] args) {
+ public static void main() {
char[] interesting = {
0x0000, 0x0001, 0x007f, 0x0080, 0x0081, 0x00ff,
0x0100, 0x0101, 0x017f, 0x0180, 0x0181, 0x01ff,
@@ -121,7 +121,7 @@ public class Main {
expectEquals(expected, x[i]);
}
- System.out.println("passed");
+ System.out.println("CharSimdMinMax passed");
}
private static void expectEquals(char expected, char result) {
diff --git a/test/651-checker-double-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/DoubleSimdMinMax.java
index 2eaf907167..da20594db8 100644
--- a/test/651-checker-double-simd-minmax/src/Main.java
+++ b/test/651-checker-simd-minmax/src/DoubleSimdMinMax.java
@@ -17,9 +17,9 @@
/**
* Tests for MIN/MAX vectorization.
*/
-public class Main {
+public class DoubleSimdMinMax {
- /// CHECK-START: void Main.doitMin(double[], double[], double[]) loop_optimization (before)
+ /// CHECK-START: void DoubleSimdMinMax.doitMin(double[], double[], double[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -29,7 +29,7 @@ public class Main {
// TODO x86: 0.0 vs -0.0?
// TODO MIPS64: min(x, NaN)?
//
- /// CHECK-START-ARM64: void Main.doitMin(double[], double[], double[]) loop_optimization (after)
+ /// CHECK-START-ARM64: void DoubleSimdMinMax.doitMin(double[], double[], double[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
@@ -41,7 +41,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMax(double[], double[], double[]) loop_optimization (before)
+ /// CHECK-START: void DoubleSimdMinMax.doitMax(double[], double[], double[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -51,7 +51,7 @@ public class Main {
// TODO x86: 0.0 vs -0.0?
// TODO MIPS64: max(x, NaN)?
//
- /// CHECK-START-ARM64: void Main.doitMax(double[], double[], double[]) loop_optimization (after)
+ /// CHECK-START-ARM64: void DoubleSimdMinMax.doitMax(double[], double[], double[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
@@ -63,7 +63,7 @@ public class Main {
}
}
- public static void main(String[] args) {
+ public static void main() {
double[] interesting = {
-0.0f,
+0.0f,
@@ -109,7 +109,7 @@ public class Main {
expectEquals(expected, x[i]);
}
- System.out.println("passed");
+ System.out.println("DoubleSimdMinMax passed");
}
private static void expectEquals(double expected, double result) {
diff --git a/test/651-checker-float-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/FloatSimdMinMax.java
index dc09dfc7cc..645081248a 100644
--- a/test/651-checker-float-simd-minmax/src/Main.java
+++ b/test/651-checker-simd-minmax/src/FloatSimdMinMax.java
@@ -17,9 +17,9 @@
/**
* Tests for MIN/MAX vectorization.
*/
-public class Main {
+public class FloatSimdMinMax {
- /// CHECK-START: void Main.doitMin(float[], float[], float[]) loop_optimization (before)
+ /// CHECK-START: void FloatSimdMinMax.doitMin(float[], float[], float[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:f\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:f\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -29,7 +29,7 @@ public class Main {
// TODO x86: 0.0 vs -0.0?
// TODO MIPS64: min(x, NaN)?
//
- /// CHECK-START-ARM64: void Main.doitMin(float[], float[], float[]) loop_optimization (after)
+ /// CHECK-START-ARM64: void FloatSimdMinMax.doitMin(float[], float[], float[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
@@ -41,7 +41,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMax(float[], float[], float[]) loop_optimization (before)
+ /// CHECK-START: void FloatSimdMinMax.doitMax(float[], float[], float[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:f\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:f\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -51,7 +51,7 @@ public class Main {
// TODO x86: 0.0 vs -0.0?
// TODO MIPS64: max(x, NaN)?
//
- /// CHECK-START-ARM64: void Main.doitMax(float[], float[], float[]) loop_optimization (after)
+ /// CHECK-START-ARM64: void FloatSimdMinMax.doitMax(float[], float[], float[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
@@ -63,7 +63,7 @@ public class Main {
}
}
- public static void main(String[] args) {
+ public static void main() {
float[] interesting = {
-0.0f,
+0.0f,
@@ -109,7 +109,7 @@ public class Main {
expectEquals(expected, x[i]);
}
- System.out.println("passed");
+ System.out.println("FloatSimdMinMax passed");
}
private static void expectEquals(float expected, float result) {
diff --git a/test/651-checker-int-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/IntSimdMinMax.java
index 82fad84d08..6373ae10eb 100644
--- a/test/651-checker-int-simd-minmax/src/Main.java
+++ b/test/651-checker-simd-minmax/src/IntSimdMinMax.java
@@ -17,16 +17,16 @@
/**
* Tests for MIN/MAX vectorization.
*/
-public class Main {
+public class IntSimdMinMax {
- /// CHECK-START: void Main.doitMin(int[], int[], int[]) loop_optimization (before)
+ /// CHECK-START: void IntSimdMinMax.doitMin(int[], int[], int[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:i\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:i\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Min:i\d+>> Min [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(int[], int[], int[]) loop_optimization (after)
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void IntSimdMinMax.doitMin(int[], int[], int[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Int32 loop:<<Loop>> outer_loop:none
@@ -38,14 +38,14 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMax(int[], int[], int[]) loop_optimization (before)
+ /// CHECK-START: void IntSimdMinMax.doitMax(int[], int[], int[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:i\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:i\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Max:i\d+>> Max [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(int[], int[], int[]) loop_optimization (after)
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void IntSimdMinMax.doitMax(int[], int[], int[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Int32 loop:<<Loop>> outer_loop:none
@@ -57,7 +57,7 @@ public class Main {
}
}
- public static void main(String[] args) {
+ public static void main() {
int[] interesting = {
0x00000000, 0x00000001, 0x00007fff, 0x00008000, 0x00008001, 0x0000ffff,
0x00010000, 0x00010001, 0x00017fff, 0x00018000, 0x00018001, 0x0001ffff,
@@ -93,7 +93,7 @@ public class Main {
expectEquals(expected, x[i]);
}
- System.out.println("passed");
+ System.out.println("IntSimdMinMax passed");
}
private static void expectEquals(int expected, int result) {
diff --git a/test/651-checker-long-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/LongSimdMinMax.java
index f52686e54c..bb0c6047ed 100644
--- a/test/651-checker-long-simd-minmax/src/Main.java
+++ b/test/651-checker-simd-minmax/src/LongSimdMinMax.java
@@ -17,9 +17,9 @@
/**
* Tests for MIN/MAX vectorization.
*/
-public class Main {
+public class LongSimdMinMax {
- /// CHECK-START: void Main.doitMin(long[], long[], long[]) loop_optimization (before)
+ /// CHECK-START: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:j\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:j\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -28,10 +28,10 @@ public class Main {
//
// Not directly supported for longs.
//
- /// CHECK-START-ARM64: void Main.doitMin(long[], long[], long[]) loop_optimization (after)
+ /// CHECK-START-ARM64: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (after)
/// CHECK-NOT: VecMin
//
- /// CHECK-START-MIPS64: void Main.doitMin(long[], long[], long[]) loop_optimization (after)
+ /// CHECK-START-MIPS64: void LongSimdMinMax.doitMin(long[], long[], long[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
@@ -44,7 +44,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMax(long[], long[], long[]) loop_optimization (before)
+ /// CHECK-START: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:j\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:j\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -53,10 +53,10 @@ public class Main {
//
// Not directly supported for longs.
//
- /// CHECK-START-ARM64: void Main.doitMax(long[], long[], long[]) loop_optimization (after)
+ /// CHECK-START-ARM64: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (after)
/// CHECK-NOT: VecMax
//
- /// CHECK-START-MIPS64: void Main.doitMax(long[], long[], long[]) loop_optimization (after)
+ /// CHECK-START-MIPS64: void LongSimdMinMax.doitMax(long[], long[], long[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
@@ -68,7 +68,7 @@ public class Main {
}
}
- public static void main(String[] args) {
+ public static void main() {
long[] interesting = {
0x0000000000000000L, 0x0000000000000001L, 0x000000007fffffffL,
0x0000000080000000L, 0x0000000080000001L, 0x00000000ffffffffL,
@@ -110,7 +110,7 @@ public class Main {
expectEquals(expected, x[i]);
}
- System.out.println("passed");
+ System.out.println("LongSimdMinMax passed");
}
private static void expectEquals(long expected, long result) {
diff --git a/test/651-checker-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/Main.java
new file mode 100644
index 0000000000..9134dd1edd
--- /dev/null
+++ b/test/651-checker-simd-minmax/src/Main.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 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) {
+ ByteSimdMinMax.main();
+ CharSimdMinMax.main();
+ ShortSimdMinMax.main();
+ IntSimdMinMax.main();
+ LongSimdMinMax.main();
+ DoubleSimdMinMax.main();
+ FloatSimdMinMax.main();
+ }
+}
diff --git a/test/651-checker-short-simd-minmax/src/Main.java b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
index 4300ca2951..aae78914d8 100644
--- a/test/651-checker-short-simd-minmax/src/Main.java
+++ b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
@@ -17,9 +17,9 @@
/**
* Tests for MIN/MAX vectorization.
*/
-public class Main {
+public class ShortSimdMinMax {
- /// CHECK-START: void Main.doitMin(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-START: void ShortSimdMinMax.doitMin(short[], short[], short[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -27,7 +27,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMin(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
@@ -39,7 +39,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMinUnsigned(short[], short[], short[]) instruction_simplifier (before)
+ /// CHECK-START: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) instruction_simplifier (before)
/// CHECK-DAG: <<IMAX:i\d+>> IntConstant 65535 loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -50,7 +50,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-START: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -58,7 +58,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinUnsigned(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
@@ -70,7 +70,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMax(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-START: void ShortSimdMinMax.doitMax(short[], short[], short[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -78,7 +78,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMax(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
@@ -90,7 +90,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMaxUnsigned(short[], short[], short[]) instruction_simplifier (before)
+ /// CHECK-START: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) instruction_simplifier (before)
/// CHECK-DAG: <<IMAX:i\d+>> IntConstant 65535 loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -101,7 +101,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-START: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) loop_optimization (before)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Get2:c\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -109,7 +109,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
@@ -121,7 +121,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.doitMin100(short[], short[]) loop_optimization (before)
+ /// CHECK-START: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (before)
/// CHECK-DAG: <<I100:i\d+>> IntConstant 100 loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get:s\d+>> ArrayGet loop:<<Loop>> outer_loop:none
@@ -129,7 +129,7 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
- /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(short[], short[]) loop_optimization (after)
+ /// CHECK-START-{ARM64,MIPS64}: void ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<I100:i\d+>> IntConstant 100 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I100>>] loop:none
/// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
@@ -142,7 +142,7 @@ public class Main {
}
}
- public static void main(String[] args) {
+ public static void main() {
short[] interesting = {
(short) 0x0000, (short) 0x0001, (short) 0x007f,
(short) 0x0080, (short) 0x0081, (short) 0x00ff,
@@ -199,7 +199,7 @@ public class Main {
expectEquals(expected, x[i]);
}
- System.out.println("passed");
+ System.out.println("ShortSimdMinMax passed");
}
private static void expectEquals(short expected, short result) {
diff --git a/test/660-checker-simd-sad-short/src/Main.java b/test/660-checker-simd-sad-short/src/Main.java
index 8a44d9ed12..77c9e53e0c 100644
--- a/test/660-checker-simd-sad-short/src/Main.java
+++ b/test/660-checker-simd-sad-short/src/Main.java
@@ -19,6 +19,10 @@
*/
public class Main {
+ private static int $inline$seven() {
+ return 7;
+ }
+
// TODO: lower precision still coming, b/64091002
private static short sadShort2Short(short[] s1, short[] s2) {
@@ -153,6 +157,102 @@ public class Main {
return sad;
}
+ /// CHECK-START: int Main.sadShort2IntConstant1(short[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Cons:i\d+>> IntConstant -7 loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Cons>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Intrin:i\d+>> Abs [<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant1(short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Cons:i\d+>> IntConstant 7 loop:none
+ /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none
+ /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<Cons>>] loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Rep>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none
+ private static int sadShort2IntConstant1(short[] s) {
+ int sad = 0;
+ for (int i = 0; i < s.length; i++) {
+ sad += Math.abs(s[i] - 7); // s[i] + -7
+ }
+ return sad;
+ }
+
+ /// CHECK-START: int Main.sadShort2IntConstant2(short[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Cons:i\d+>> IntConstant 7 loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Get1>>,<<Cons>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Intrin:i\d+>> Abs [<<Sub>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant2(short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Cons:i\d+>> IntConstant 7 loop:none
+ /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none
+ /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<Cons>>] loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Rep>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none
+ private static int sadShort2IntConstant2(short[] s) {
+ int sad = 0;
+ for (int i = 0; i < s.length; i++) {
+ sad += Math.abs(s[i] - $inline$seven()); // s[i] - 7
+ }
+ return sad;
+ }
+
+ /// CHECK-START: int Main.sadShort2IntConstant3(short[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Cons:i\d+>> IntConstant 7 loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Cons>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Intrin:i\d+>> Abs [<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi2>>,<<Intrin>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons1>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstant3(short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Cons:i\d+>> IntConstant -7 loop:none
+ /// CHECK-DAG: <<Cons8:i\d+>> IntConstant 8 loop:none
+ /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<Cons>>] loop:none
+ /// CHECK-DAG: <<Set:d\d+>> VecSetScalars [<<Cons0>>] loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi [<<Cons0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:d\d+>> Phi [<<Set>>,{{d\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Load1:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<SAD:d\d+>> VecSADAccumulate [<<Phi2>>,<<Load1>>,<<Rep>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi1>>,<<Cons8>>] loop:<<Loop>> outer_loop:none
+ private static int sadShort2IntConstant3(short[] s) {
+ int sad = 0;
+ for (int i = 0; i < s.length; i++) {
+ sad += Math.abs(s[i] + $inline$seven()); // hidden s[i] - (-7)
+ }
+ return sad;
+ }
+
/// CHECK-START: long Main.sadShort2Long(short[], short[]) loop_optimization (before)
/// CHECK-DAG: <<Cons0:i\d+>> IntConstant 0 loop:none
/// CHECK-DAG: <<Cons1:i\d+>> IntConstant 1 loop:none
@@ -243,6 +343,9 @@ public class Main {
expectEquals(65535, sadShort2IntAlt(s2, s1));
expectEquals(65535, sadShort2IntAlt2(s1, s2));
expectEquals(65535, sadShort2IntAlt2(s2, s1));
+ expectEquals(32880, sadShort2IntConstant1(s1));
+ expectEquals(32880, sadShort2IntConstant2(s1));
+ expectEquals(32866, sadShort2IntConstant3(s1));
expectEquals(65535L, sadShort2Long(s1, s2));
expectEquals(65535L, sadShort2Long(s2, s1));
expectEquals(65536L, sadShort2LongAt1(s1, s2));
@@ -279,6 +382,9 @@ public class Main {
expectEquals(1291788, sadShort2Int(s1, s2));
expectEquals(1291788, sadShort2IntAlt(s1, s2));
expectEquals(1291788, sadShort2IntAlt2(s1, s2));
+ expectEquals(823907, sadShort2IntConstant1(s1));
+ expectEquals(823907, sadShort2IntConstant2(s1));
+ expectEquals(823953, sadShort2IntConstant3(s1));
expectEquals(1291788L, sadShort2Long(s1, s2));
expectEquals(1291789L, sadShort2LongAt1(s1, s2));
diff --git a/test/678-checker-simd-saturation/src/Main.java b/test/678-checker-simd-saturation/src/Main.java
index d123cc2e25..decc691789 100644
--- a/test/678-checker-simd-saturation/src/Main.java
+++ b/test/678-checker-simd-saturation/src/Main.java
@@ -19,6 +19,14 @@
*/
public class Main {
+ static final int $inline$p15() {
+ return 15;
+ }
+
+ static final int $inline$m15() {
+ return -15;
+ }
+
//
// Direct min-max.
//
@@ -230,8 +238,8 @@ public class Main {
/// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSByte(byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Add:d\d+>> VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Sub:d\d+>> VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Sub>>] loop:<<Loop>> outer_loop:none
public static void satSubPConstSByte(byte[] a, byte[] b) {
int n = Math.min(a.length, b.length);
for (int i = 0; i < n; i++) {
@@ -242,8 +250,8 @@ public class Main {
/// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSByte(byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Add:d\d+>> VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Sub:d\d+>> VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Sub>>] loop:<<Loop>> outer_loop:none
public static void satSubNConstSByte(byte[] a, byte[] b) {
int n = Math.min(a.length, b.length);
for (int i = 0; i < n; i++) {
@@ -282,8 +290,8 @@ public class Main {
/// CHECK-START-{ARM,ARM64}: void Main.satSubPConstSShort(short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Add:d\d+>> VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Sub:d\d+>> VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Sub>>] loop:<<Loop>> outer_loop:none
public static void satSubPConstSShort(short[] a, short[] b) {
int n = Math.min(a.length, b.length);
for (int i = 0; i < n; i++) {
@@ -294,8 +302,8 @@ public class Main {
/// CHECK-START-{ARM,ARM64}: void Main.satSubNConstSShort(short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar loop:none
/// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Add:d\d+>> VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Sub:d\d+>> VecSaturationSub [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Sub>>] loop:<<Loop>> outer_loop:none
public static void satSubNConstSShort(short[] a, short[] b) {
int n = Math.min(a.length, b.length);
for (int i = 0; i < n; i++) {
@@ -304,7 +312,59 @@ public class Main {
}
//
- // Alternatives.
+ // Alternatives 8-bit clipping.
+ //
+
+ /// CHECK-START-{ARM,ARM64}: void Main.usatAddConst(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Add:d\d+>> VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none
+ public static void usatAddConst(byte[] a, byte[] b) {
+ int n = Math.min(a.length, b.length);
+ for (int i = 0; i < n; i++) {
+ b[i] = (byte) Math.min((a[i] & 0xff) + $inline$p15(), 255);
+ }
+ }
+
+ /// CHECK-START-{ARM,ARM64}: void Main.usatAddConstAlt(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Add:d\d+>> VecSaturationAdd [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none
+ public static void usatAddConstAlt(byte[] a, byte[] b) {
+ int n = Math.min(a.length, b.length);
+ for (int i = 0; i < n; i++) {
+ b[i] = (byte) Math.min((a[i] & 0xff) - $inline$m15(), 255);
+ }
+ }
+
+ /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Sub:d\d+>> VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Sub>>] loop:<<Loop>> outer_loop:none
+ public static void usatSubConst(byte[] a, byte[] b) {
+ int n = Math.min(a.length, b.length);
+ for (int i = 0; i < n; i++) {
+ b[i] = (byte) Math.max((a[i] & 0xff) - $inline$p15(), 0);
+ }
+ }
+
+ /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Sub:d\d+>> VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Sub>>] loop:<<Loop>> outer_loop:none
+ public static void usatSubConstAlt(byte[] a, byte[] b) {
+ int n = Math.min(a.length, b.length);
+ for (int i = 0; i < n; i++) {
+ b[i] = (byte) Math.max((a[i] & 0xff) + $inline$m15(), 0);
+ }
+ }
+
+ //
+ // Alternatives 16-bit clipping.
//
/// CHECK-START: void Main.satAlt1(short[], short[], short[]) loop_optimization (before)
@@ -442,6 +502,34 @@ public class Main {
}
}
+ /// CHECK-START-{ARM,ARM64}: void Main.usatSubConst(short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Sub:d\d+>> VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Sub>>] loop:<<Loop>> outer_loop:none
+ public static void usatSubConst(short[] a, short[] b) {
+ int n = Math.min(a.length, b.length);
+ for (int i = 0; i < n; i++) {
+ int t = a[i] & 0xffff;
+ int s = t - $inline$p15();
+ b[i] = (short)(s > 0 ? s : 0);
+ }
+ }
+
+ /// CHECK-START-{ARM,ARM64}: void Main.usatSubConstAlt(short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Get1:d\d+>> VecReplicateScalar loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Sub:d\d+>> VecSaturationSub [<<Get2>>,<<Get1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Sub>>] loop:<<Loop>> outer_loop:none
+ public static void usatSubConstAlt(short[] a, short[] b) {
+ int n = Math.min(a.length, b.length);
+ for (int i = 0; i < n; i++) {
+ int t = a[i] & 0xffff;
+ int s = t + $inline$m15();
+ b[i] = (short)(s > 0 ? s : 0);
+ }
+ }
+
//
// Test drivers.
//
@@ -503,6 +591,27 @@ public class Main {
byte e = (byte) Math.max(-15 - b1[i], -128);
expectEquals(e, out[i]);
}
+ // Alternatives.
+ usatAddConst(b1, out);
+ for (int i = 0; i < m; i++) {
+ byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255);
+ expectEquals(e, out[i]);
+ }
+ usatAddConstAlt(b1, out);
+ for (int i = 0; i < m; i++) {
+ byte e = (byte) Math.min((b1[i] & 0xff) + 15, 255);
+ expectEquals(e, out[i]);
+ }
+ usatSubConst(b1, out);
+ for (int i = 0; i < m; i++) {
+ byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0);
+ expectEquals(e, out[i]);
+ }
+ usatSubConstAlt(b1, out);
+ for (int i = 0; i < m; i++) {
+ byte e = (byte) Math.max((b1[i] & 0xff) - 15, 0);
+ expectEquals(e, out[i]);
+ }
}
private static void test16Bit() {
@@ -630,6 +739,16 @@ public class Main {
short e = (short) Math.max(Math.min(s1[i] + 15, 32767), -32752);
expectEquals(e, out[i]);
}
+ usatSubConst(s1, out);
+ for (int i = 0; i < m; i++) {
+ short e = (short) Math.max((s1[i] & 0xffff) - 15, 0);
+ expectEquals(e, out[i]);
+ }
+ usatSubConstAlt(s1, out);
+ for (int i = 0; i < m; i++) {
+ short e = (short) Math.max((s1[i] & 0xffff) - 15, 0);
+ expectEquals(e, out[i]);
+ }
}
public static void main(String[] args) {
diff --git a/test/680-checker-deopt-dex-pc-0/expected.txt b/test/680-checker-deopt-dex-pc-0/expected.txt
new file mode 100644
index 0000000000..805857dc65
--- /dev/null
+++ b/test/680-checker-deopt-dex-pc-0/expected.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+passed
diff --git a/test/680-checker-deopt-dex-pc-0/info.txt b/test/680-checker-deopt-dex-pc-0/info.txt
new file mode 100644
index 0000000000..8eae156b22
--- /dev/null
+++ b/test/680-checker-deopt-dex-pc-0/info.txt
@@ -0,0 +1,2 @@
+Regression test for deoptimization at dex pc 0 causing infinite recursion
+for JIT-at-first-use.
diff --git a/test/680-checker-deopt-dex-pc-0/src/Main.java b/test/680-checker-deopt-dex-pc-0/src/Main.java
new file mode 100644
index 0000000000..64a3cb3da7
--- /dev/null
+++ b/test/680-checker-deopt-dex-pc-0/src/Main.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 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 {
+ // We run this test for AOT to verify that there is a HDeoptimize with dex pc 0.
+ /// CHECK-START: int Main.$noinline$getInt(byte[], int) BCE (after)
+ /// CHECK: Deoptimize dex_pc:0
+ public static int $noinline$getInt(byte[] array, int offset) {
+ // The aget for `array[offset]` is at dex pc 0, so the Deoptimize
+ // from dynamic BCE shall also be at dex pc 0.
+ return ((array[offset ] & 0xFF) << 0) +
+ ((array[offset + 1] & 0xFF) << 8) +
+ ((array[offset + 2] & 0xFF) << 16) +
+ ((array[offset + 3] & 0xFF) << 24);
+ }
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ if (hasJit()) {
+ byte[] array = { 0, 1, 2, 3 };
+ ensureJitCompiled(Main.class, "$noinline$getInt");
+ if (!hasJitCompiledEntrypoint(Main.class, "$noinline$getInt")) {
+ throw new Error("Unexpected entrypoint!");
+ }
+ if ($noinline$getInt(array, 0) != 0x03020100) {
+ throw new Error();
+ }
+ try {
+ // The HDeoptimize at dex pc 0 was previously handled poorly as the dex pc 0
+ // was used to detect whether we entered the method. This meant that the
+ // instrumentation would have reported MethodEnteredEvent and we would have
+ // told JIT that the method was entered. With JIT-on-first-use we would also
+ // immediatelly recompile the method and run the compiled code leading to
+ // a an infinite deoptimization recursion, yielding StackOverflowError.
+ $noinline$getInt(array, 1);
+ } catch (ArrayIndexOutOfBoundsException ignored) {}
+ }
+ System.out.println("passed");
+ }
+
+ public static native boolean hasJit();
+ public native static boolean hasJitCompiledEntrypoint(Class<?> cls, String methodName);
+ public native static void ensureJitCompiled(Class<?> cls, String methodName);
+}
diff --git a/test/HiddenApiSignatures/Class1.java b/test/HiddenApiSignatures/Class1.java
new file mode 100644
index 0000000000..a9004dcd99
--- /dev/null
+++ b/test/HiddenApiSignatures/Class1.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package mypackage.packagea;
+
+public class Class1 {
+ public int field1;
+ public int field12;
+
+ public Class1() {
+ }
+
+ public void method1() {
+ }
+
+ public void method1(int i) {
+ }
+
+ public void method12() {
+ }
+
+} \ No newline at end of file
diff --git a/test/HiddenApiSignatures/Class12.java b/test/HiddenApiSignatures/Class12.java
new file mode 100644
index 0000000000..82b22e365f
--- /dev/null
+++ b/test/HiddenApiSignatures/Class12.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+package mypackage.packagea;
+
+public class Class12 {
+ public int field1;
+
+ public void method1() {
+ }
+} \ No newline at end of file
diff --git a/test/HiddenApiSignatures/Class2.java b/test/HiddenApiSignatures/Class2.java
new file mode 100644
index 0000000000..dc92b9cfd8
--- /dev/null
+++ b/test/HiddenApiSignatures/Class2.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package mypackage.packagea;
+
+public class Class2 {
+ public int field1;
+
+ public void method1() {
+ }
+
+ public void method1(int i) {
+ }
+} \ No newline at end of file
diff --git a/test/HiddenApiSignatures/Class3.java b/test/HiddenApiSignatures/Class3.java
new file mode 100644
index 0000000000..fbf04071a4
--- /dev/null
+++ b/test/HiddenApiSignatures/Class3.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package mypackage.packageb;
+
+public class Class3 {
+ public int field1;
+
+ public void method1() {
+ }
+
+ public void method1(int i) {
+ }
+} \ No newline at end of file
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index e9127a8101..86adb733a9 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -775,9 +775,6 @@ if [ "$HOST" = "n" ]; then
TMP_DIR_OPTION="-Djava.io.tmpdir=/data/local/tmp"
fi
-# We set DumpNativeStackOnSigQuit to false to avoid stressing libunwind.
-# b/27185632
-# b/24664297
dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \
$GDB_ARGS \
$FLAGS \
@@ -792,7 +789,6 @@ dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \
$DEBUGGER_OPTS \
$DALVIKVM_BOOT_OPT \
$TMP_DIR_OPTION \
- -XX:DumpNativeStackOnSigQuit:false \
-cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $MAIN $ARGS"
# Remove whitespace.
diff --git a/test/knownfailures.json b/test/knownfailures.json
index d390c4c049..6d8abe13b2 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -976,5 +976,11 @@
"tests": "677-fsi",
"variant": "no-dex2oat | no-image | no-prebuild | relocate-npatchoat | jvm",
"description": ["Test requires a successful dex2oat invocation"]
+ },
+ {
+ "tests": ["990-field-trace",
+ "991-field-trace-2"],
+ "variant": "gcstress & debug & target",
+ "description": ["Test can time out on gcstress with debug"]
}
]
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index b323ddc9cb..95e488dc4f 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -46,6 +46,10 @@ target_config = {
'art-jit' : {
'run-test' : ['--jit']
},
+ 'art-jit-on-first-use' : {
+ 'run-test' : ['--jit',
+ '--runtime-option=-Xjitthreshold:0']
+ },
'art-pictest' : {
'run-test' : ['--pictest',
'--optimizing']
@@ -66,6 +70,11 @@ target_config = {
'run-test' : ['--jit',
'--gcstress']
},
+ 'art-jit-on-first-use-gcstress' : {
+ 'run-test' : ['--jit',
+ '--gcstress',
+ '--runtime-option=-Xjitthreshold:0']
+ },
# TODO: Rename or repurpose this configuration as
# 'art-read-barrier-heap-poisoning' (b/62611253).
'art-read-barrier' : {
diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp
index 31ff682828..cbf62d9e9c 100644
--- a/tools/veridex/Android.bp
+++ b/tools/veridex/Android.bp
@@ -12,10 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-art_cc_binary {
+cc_binary {
name: "veridex",
host_supported: true,
srcs: [
+ "hidden_api.cc",
+ "hidden_api_finder.cc",
"resolver.cc",
"veridex.cc",
],
diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk
new file mode 100644
index 0000000000..4183054193
--- /dev/null
+++ b/tools/veridex/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2018 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+system_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex
+$(system_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000
+$(system_stub_dex): $(TOPDIR)prebuilts/sdk/system_current/android.jar | $(ZIP2ZIP) $(DX)
+ $(transform-classes-d8.jar-to-dex)
+
+
+oahl_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/oahl_dex_intermediates/classes.dex
+$(oahl_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000
+$(oahl_stub_dex): $(TOPDIR)prebuilts/sdk/org.apache.http.legacy/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX)
+ $(transform-classes-d8.jar-to-dex)
+
+.PHONY: appcompat
+
+appcompat: $(system_stub_dex) $(oahl_stub_dex) $(HOST_OUT_EXECUTABLES)/veridex \
+ ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-light-greylist.txt \
+ ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-dark-greylist.txt \
+ ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-blacklist.txt
diff --git a/tools/veridex/README.md b/tools/veridex/README.md
new file mode 100644
index 0000000000..0f91b08771
--- /dev/null
+++ b/tools/veridex/README.md
@@ -0,0 +1,14 @@
+appcompat.sh
+============
+
+Given an APK, finds API uses that fall into the blacklist/greylists APIs.
+
+NOTE: appcompat.sh is still under development. It can report
+API uses that do not execute at runtime, and reflection uses
+that do not exist. It can also miss on reflection uses.
+
+To build it:
+> make appcompat
+
+To run it:
+> ./art/tools/veridex/appcompat.sh test.apk
diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh
new file mode 100755
index 0000000000..f75aa4f0d0
--- /dev/null
+++ b/tools/veridex/appcompat.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 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.
+
+# We want to be at the root for simplifying the "out" detection
+# logic.
+if [ ! -d art ]; then
+ echo "Script needs to be run at the root of the android tree."
+ exit 1
+fi
+
+# Logic for setting out_dir from build/make/core/envsetup.mk:
+if [[ -z $OUT_DIR ]]; then
+ if [[ -z $OUT_DIR_COMMON_BASE ]]; then
+ OUT=out
+ else
+ OUT=${OUT_DIR_COMMON_BASE}/${PWD##*/}
+ fi
+else
+ OUT=${OUT_DIR}
+fi
+
+PACKAGING=${OUT}/target/common/obj/PACKAGING
+
+if [ -z "$ANDROID_HOST_OUT" ] ; then
+ ANDROID_HOST_OUT=${OUT}/host/linux-x86
+fi
+
+echo "NOTE: appcompat.sh is still under development. It can report"
+echo "API uses that do not execute at runtime, and reflection uses"
+echo "that do not exist. It can also miss on reflection uses."
+
+
+${ANDROID_HOST_OUT}/bin/veridex \
+ --core-stubs=${PACKAGING}/core_dex_intermediates/classes.dex:${PACKAGING}/oahl_dex_intermediates/classes.dex \
+ --blacklist=${PACKAGING}/hiddenapi-blacklist.txt \
+ --light-greylist=${PACKAGING}/hiddenapi-light-greylist.txt \
+ --dark-greylist=${PACKAGING}/hiddenapi-dark-greylist.txt \
+ --dex-file=$1
diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc
new file mode 100644
index 0000000000..93f921a25f
--- /dev/null
+++ b/tools/veridex/hidden_api.cc
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 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 "hidden_api.h"
+
+#include <fstream>
+#include <sstream>
+
+#include "dex/dex_file-inl.h"
+
+namespace art {
+
+std::string HiddenApi::GetApiMethodName(const DexFile& dex_file, uint32_t method_index) {
+ std::stringstream ss;
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(method_index);
+ ss << dex_file.StringByTypeIdx(method_id.class_idx_)
+ << "->"
+ << dex_file.GetMethodName(method_id)
+ << dex_file.GetMethodSignature(method_id).ToString();
+ return ss.str();
+}
+
+std::string HiddenApi::GetApiFieldName(const DexFile& dex_file, uint32_t field_index) {
+ std::stringstream ss;
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+ ss << dex_file.StringByTypeIdx(field_id.class_idx_)
+ << "->"
+ << dex_file.GetFieldName(field_id)
+ << ":"
+ << dex_file.GetFieldTypeDescriptor(field_id);
+ return ss.str();
+}
+
+void HiddenApi::FillList(const char* filename, std::set<std::string>& entries) {
+ if (filename == nullptr) {
+ return;
+ }
+ std::ifstream in(filename);
+ std::string str;
+ while (std::getline(in, str)) {
+ entries.insert(str);
+ size_t pos = str.find("->");
+ if (pos != std::string::npos) {
+ // Add the class name.
+ entries.insert(str.substr(0, pos));
+ pos = str.find('(');
+ if (pos != std::string::npos) {
+ // Add the class->method name (so stripping the signature).
+ entries.insert(str.substr(0, pos));
+ }
+ }
+ }
+}
+
+} // namespace art
diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h
new file mode 100644
index 0000000000..5893b8ae33
--- /dev/null
+++ b/tools/veridex/hidden_api.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 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_TOOLS_VERIDEX_HIDDEN_API_H_
+#define ART_TOOLS_VERIDEX_HIDDEN_API_H_
+
+#include "dex/hidden_api_access_flags.h"
+
+#include <ostream>
+#include <set>
+#include <string>
+
+namespace art {
+
+class DexFile;
+
+/**
+ * Helper class for logging if a method/field is in a hidden API list.
+ */
+class HiddenApi {
+ public:
+ HiddenApi(const char* blacklist, const char* dark_greylist, const char* light_greylist) {
+ FillList(light_greylist, light_greylist_);
+ FillList(dark_greylist, dark_greylist_);
+ FillList(blacklist, blacklist_);
+ }
+
+ HiddenApiAccessFlags::ApiList GetApiList(const std::string& name) const {
+ if (IsInList(name, blacklist_)) {
+ return HiddenApiAccessFlags::kBlacklist;
+ } else if (IsInList(name, dark_greylist_)) {
+ return HiddenApiAccessFlags::kDarkGreylist;
+ } else if (IsInList(name, light_greylist_)) {
+ return HiddenApiAccessFlags::kLightGreylist;
+ } else {
+ return HiddenApiAccessFlags::kWhitelist;
+ }
+ }
+
+ bool IsInRestrictionList(const std::string& name) const {
+ return GetApiList(name) != HiddenApiAccessFlags::kWhitelist;
+ }
+
+ static std::string GetApiMethodName(const DexFile& dex_file, uint32_t method_index);
+
+ static std::string GetApiFieldName(const DexFile& dex_file, uint32_t field_index);
+
+ private:
+ static bool IsInList(const std::string& name, const std::set<std::string>& list) {
+ return list.find(name) != list.end();
+ }
+
+ static void FillList(const char* filename, std::set<std::string>& entries);
+
+ std::set<std::string> blacklist_;
+ std::set<std::string> light_greylist_;
+ std::set<std::string> dark_greylist_;
+};
+
+} // namespace art
+
+#endif // ART_TOOLS_VERIDEX_HIDDEN_API_H_
diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc
new file mode 100644
index 0000000000..d611f78eed
--- /dev/null
+++ b/tools/veridex/hidden_api_finder.cc
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2018 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 "hidden_api_finder.h"
+
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex/dex_file.h"
+#include "dex/method_reference.h"
+#include "hidden_api.h"
+#include "resolver.h"
+#include "veridex.h"
+
+#include <iostream>
+
+namespace art {
+
+void HiddenApiFinder::CheckMethod(uint32_t method_id,
+ VeridexResolver* resolver,
+ MethodReference ref) {
+ // Cheap check that the method is resolved. If it is, we know it's not in
+ // a restricted list.
+ if (resolver->GetMethod(method_id) != nullptr) {
+ return;
+ }
+ std::string name = HiddenApi::GetApiMethodName(resolver->GetDexFile(), method_id);
+ if (hidden_api_.IsInRestrictionList(name)) {
+ method_locations_[name].push_back(ref);
+ }
+}
+
+void HiddenApiFinder::CheckField(uint32_t field_id,
+ VeridexResolver* resolver,
+ MethodReference ref) {
+ // Cheap check that the field is resolved. If it is, we know it's not in
+ // a restricted list.
+ if (resolver->GetField(field_id) != nullptr) {
+ return;
+ }
+ std::string name = HiddenApi::GetApiFieldName(resolver->GetDexFile(), field_id);
+ if (hidden_api_.IsInRestrictionList(name)) {
+ field_locations_[name].push_back(ref);
+ }
+}
+
+void HiddenApiFinder::CollectAccesses(VeridexResolver* resolver) {
+ const DexFile& dex_file = resolver->GetDexFile();
+ size_t class_def_count = dex_file.NumClassDefs();
+ for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+ const uint8_t* class_data = dex_file.GetClassData(class_def);
+ if (class_data == nullptr) {
+ // Empty class.
+ continue;
+ }
+ ClassDataItemIterator it(dex_file, class_data);
+ it.SkipAllFields();
+ for (; it.HasNextMethod(); it.Next()) {
+ const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+ if (code_item == nullptr) {
+ continue;
+ }
+ CodeItemDataAccessor code_item_accessor(dex_file, code_item);
+ for (const DexInstructionPcPair& inst : code_item_accessor) {
+ switch (inst->Opcode()) {
+ case Instruction::CONST_CLASS: {
+ dex::TypeIndex type_index(inst->VRegB_21c());
+ std::string name = dex_file.StringByTypeIdx(type_index);
+ // Only keep classes that are in a restriction list.
+ if (hidden_api_.IsInRestrictionList(name)) {
+ classes_.insert(name);
+ }
+ break;
+ }
+ case Instruction::CONST_STRING: {
+ dex::StringIndex string_index(inst->VRegB_21c());
+ std::string name = std::string(dex_file.StringDataByIdx(string_index));
+ // Cheap filtering on the string literal. We know it cannot be a field/method/class
+ // if it contains a space.
+ if (name.find(' ') == std::string::npos) {
+ // Class names at the Java level are of the form x.y.z, but the list encodes
+ // them of the form Lx/y/z;. Inner classes have '$' for both Java level class
+ // names in strings, and hidden API lists.
+ std::string str = name;
+ std::replace(str.begin(), str.end(), '.', '/');
+ str = "L" + str + ";";
+ // Note: we can query the lists directly, as HiddenApi added classes that own
+ // private methods and fields in them.
+ // We don't add class names to the `strings_` set as we know method/field names
+ // don't have '.' or '/'. All hidden API class names have a '/'.
+ if (hidden_api_.IsInRestrictionList(str)) {
+ classes_.insert(str);
+ } else if (hidden_api_.IsInRestrictionList(name)) {
+ // Could be something passed to JNI.
+ classes_.insert(name);
+ } else {
+ // We only keep track of the location for strings, as these will be the
+ // field/method names the user is interested in.
+ strings_.insert(name);
+ reflection_locations_[name].push_back(
+ MethodReference(&dex_file, it.GetMemberIndex()));
+ }
+ }
+ break;
+ }
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_SUPER:
+ case Instruction::INVOKE_VIRTUAL: {
+ CheckMethod(
+ inst->VRegB_35c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+ break;
+ }
+
+ case Instruction::INVOKE_DIRECT_RANGE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ case Instruction::INVOKE_STATIC_RANGE:
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_VIRTUAL_RANGE: {
+ CheckMethod(
+ inst->VRegB_3rc(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+ break;
+ }
+
+ case Instruction::IGET:
+ case Instruction::IGET_WIDE:
+ case Instruction::IGET_OBJECT:
+ case Instruction::IGET_BOOLEAN:
+ case Instruction::IGET_BYTE:
+ case Instruction::IGET_CHAR:
+ case Instruction::IGET_SHORT: {
+ CheckField(
+ inst->VRegC_22c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+ break;
+ }
+
+ case Instruction::IPUT:
+ case Instruction::IPUT_WIDE:
+ case Instruction::IPUT_OBJECT:
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BYTE:
+ case Instruction::IPUT_CHAR:
+ case Instruction::IPUT_SHORT: {
+ CheckField(
+ inst->VRegC_22c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+ break;
+ }
+
+ case Instruction::SGET:
+ case Instruction::SGET_WIDE:
+ case Instruction::SGET_OBJECT:
+ case Instruction::SGET_BOOLEAN:
+ case Instruction::SGET_BYTE:
+ case Instruction::SGET_CHAR:
+ case Instruction::SGET_SHORT: {
+ CheckField(
+ inst->VRegB_21c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+ break;
+ }
+
+ case Instruction::SPUT:
+ case Instruction::SPUT_WIDE:
+ case Instruction::SPUT_OBJECT:
+ case Instruction::SPUT_BOOLEAN:
+ case Instruction::SPUT_BYTE:
+ case Instruction::SPUT_CHAR:
+ case Instruction::SPUT_SHORT: {
+ CheckField(
+ inst->VRegB_21c(), resolver, MethodReference(&dex_file, it.GetMemberIndex()));
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+static std::string GetApiMethodName(MethodReference ref) {
+ return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index);
+}
+
+void HiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
+ for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
+ CollectAccesses(resolver.get());
+ }
+
+ Dump(std::cout);
+}
+
+void HiddenApiFinder::Dump(std::ostream& os) {
+ static const char* kPrefix = " ";
+ uint32_t count = 0;
+ uint32_t linking_count = method_locations_.size() + field_locations_.size();
+ uint32_t api_counts[4] = {0, 0, 0, 0};
+
+ // Dump methods from hidden APIs linked against.
+ for (const std::pair<std::string, std::vector<MethodReference>>& pair : method_locations_) {
+ HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first);
+ api_counts[api_list]++;
+ os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):";
+ os << std::endl;
+ for (const MethodReference& ref : pair.second) {
+ os << kPrefix << GetApiMethodName(ref) << std::endl;
+ }
+ os << std::endl;
+ }
+
+ // Dump fields from hidden APIs linked against.
+ for (const std::pair<std::string, std::vector<MethodReference>>& pair : field_locations_) {
+ HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(pair.first);
+ api_counts[api_list]++;
+ os << "#" << ++count << ": Linking " << api_list << " " << pair.first << " use(s):";
+ os << std::endl;
+ for (const MethodReference& ref : pair.second) {
+ os << kPrefix << GetApiMethodName(ref) << std::endl;
+ }
+ os << std::endl;
+ }
+
+ // Dump potential reflection uses.
+ for (const std::string& cls : classes_) {
+ for (const std::string& name : strings_) {
+ std::string full_name = cls + "->" + name;
+ HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
+ api_counts[api_list]++;
+ if (api_list != HiddenApiAccessFlags::kWhitelist) {
+ os << "#" << ++count << ": Reflection " << api_list << " " << full_name
+ << " potential use(s):";
+ os << std::endl;
+ for (const MethodReference& ref : reflection_locations_[name]) {
+ os << kPrefix << GetApiMethodName(ref) << std::endl;
+ }
+ os << std::endl;
+ }
+ }
+ }
+
+ os << count << " hidden API(s) used: "
+ << linking_count << " linked against, "
+ << count - linking_count << " potentially through reflection" << std::endl;
+ os << kPrefix << api_counts[HiddenApiAccessFlags::kBlacklist]
+ << " in blacklist" << std::endl;
+ os << kPrefix << api_counts[HiddenApiAccessFlags::kDarkGreylist]
+ << " in dark greylist" << std::endl;
+ os << kPrefix << api_counts[HiddenApiAccessFlags::kLightGreylist]
+ << " in light greylist" << std::endl;
+}
+
+} // namespace art
diff --git a/tools/veridex/hidden_api_finder.h b/tools/veridex/hidden_api_finder.h
new file mode 100644
index 0000000000..243079c187
--- /dev/null
+++ b/tools/veridex/hidden_api_finder.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 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_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_
+#define ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_
+
+#include "dex/method_reference.h"
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+
+namespace art {
+
+class HiddenApi;
+class VeridexResolver;
+
+/**
+ * Reports potential uses of hidden APIs from static linking and reflection.
+ */
+class HiddenApiFinder {
+ public:
+ explicit HiddenApiFinder(const HiddenApi& hidden_api) : hidden_api_(hidden_api) {}
+
+ // Iterate over the dex files associated with the passed resolvers to report
+ // hidden API uses.
+ void Run(const std::vector<std::unique_ptr<VeridexResolver>>& app_resolvers);
+
+ private:
+ void CollectAccesses(VeridexResolver* resolver);
+ void CheckMethod(uint32_t method_idx, VeridexResolver* resolver, MethodReference ref);
+ void CheckField(uint32_t field_idx, VeridexResolver* resolver, MethodReference ref);
+ void Dump(std::ostream& os);
+
+ const HiddenApi& hidden_api_;
+ std::set<std::string> classes_;
+ std::set<std::string> strings_;
+ std::map<std::string, std::vector<MethodReference>> reflection_locations_;
+ std::map<std::string, std::vector<MethodReference>> method_locations_;
+ std::map<std::string, std::vector<MethodReference>> field_locations_;
+};
+
+} // namespace art
+
+#endif // ART_TOOLS_VERIDEX_HIDDEN_API_FINDER_H_
diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc
index 82978215a6..13dda5c199 100644
--- a/tools/veridex/resolver.cc
+++ b/tools/veridex/resolver.cc
@@ -18,6 +18,7 @@
#include "dex/dex_file-inl.h"
#include "dex/primitive.h"
+#include "hidden_api.h"
#include "veridex.h"
namespace art {
diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h
index ae94dadb28..06c8aa70c5 100644
--- a/tools/veridex/resolver.h
+++ b/tools/veridex/resolver.h
@@ -22,6 +22,7 @@
namespace art {
+class HiddenApi;
class VeridexResolver;
/**
@@ -68,6 +69,11 @@ class VeridexResolver {
// Resolve all type_id/method_id/field_id.
void ResolveAll();
+ // The dex file this resolver is associated to.
+ const DexFile& GetDexFile() const {
+ return dex_file_;
+ }
+
private:
// Return the resolver where `kls` is from.
VeridexResolver* GetResolverOf(const VeriClass& kls) const;
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index 9287211a3c..16e9f0e55b 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -20,6 +20,8 @@
#include "dex/dex_file.h"
#include "dex/dex_file_loader.h"
+#include "hidden_api.h"
+#include "hidden_api_finder.h"
#include "resolver.h"
#include <sstream>
@@ -161,10 +163,10 @@ class Veridex {
std::vector<std::unique_ptr<VeridexResolver>> app_resolvers;
Resolve(app_dex_files, resolver_map, type_map, &app_resolvers);
- // Resolve all type_id/method_id/field_id of app dex files.
- for (const std::unique_ptr<VeridexResolver>& resolver : app_resolvers) {
- resolver->ResolveAll();
- }
+ // Find and log uses of hidden APIs.
+ HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist);
+ HiddenApiFinder api_finder(hidden_api);
+ api_finder.Run(app_resolvers);
return 0;
}