Merge "Implement cyclic region allocation in ART's region space."
diff --git a/Android.mk b/Android.mk
index 558986e..e4f4e74 100644
--- a/Android.mk
+++ b/Android.mk
@@ -67,6 +67,7 @@
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 b342abe..b483e5f 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -300,11 +300,13 @@
$(HOST_CORE_IMAGE_DEFAULT_64) \
$(HOST_CORE_IMAGE_DEFAULT_32) \
oatdumpd-host \
- oatdumpds-host
+ oatdumpds-host \
+ dexdump2-host
ART_GTEST_oatdump_test_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_DEFAULT_64) \
$(TARGET_CORE_IMAGE_DEFAULT_32) \
- oatdumpd-target
+ oatdumpd-target \
+ dexdump2-target
ART_GTEST_oatdump_image_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS)
ART_GTEST_oatdump_image_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS)
ART_GTEST_oatdump_app_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) \
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index f582341..c139fcf 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -20,6 +20,7 @@
#include "base/callee_save_type.h"
#include "base/enums.h"
#include "base/leb128.h"
+#include "base/malloc_arena_pool.h"
#include "class_linker.h"
#include "common_runtime_test.h"
#include "dex/code_item_accessors-inl.h"
@@ -67,7 +68,7 @@
fake_code_.push_back(0x70 | i);
}
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stack_maps(&allocator, kRuntimeISA);
diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc
index 236b5c0..11b0e2b 100644
--- a/compiler/jni/jni_cfi_test.cc
+++ b/compiler/jni/jni_cfi_test.cc
@@ -20,6 +20,7 @@
#include "arch/instruction_set.h"
#include "base/arena_allocator.h"
#include "base/enums.h"
+#include "base/malloc_arena_pool.h"
#include "cfi_test.h"
#include "gtest/gtest.h"
#include "jni/quick/calling_convention.h"
@@ -61,7 +62,7 @@
const bool is_synchronized = false;
const char* shorty = "IIFII";
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
std::unique_ptr<JniCallingConvention> jni_conv(
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index d001cfe..8cb1998 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -27,6 +27,8 @@
#include "base/enums.h"
#include "base/logging.h" // For VLOG.
#include "base/macros.h"
+#include "base/malloc_arena_pool.h"
+#include "base/memory_region.h"
#include "base/utils.h"
#include "calling_convention.h"
#include "class_linker.h"
@@ -36,7 +38,6 @@
#include "driver/compiler_options.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "jni_env_ext.h"
-#include "memory_region.h"
#include "thread.h"
#include "utils/arm/managed_register_arm.h"
#include "utils/arm64/managed_register_arm64.h"
@@ -174,7 +175,7 @@
}
}
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
// Calling conventions used to iterate over parameters to method
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
index 7875517..0056c50 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -21,6 +21,7 @@
#include "arch/arm/asm_support_arm.h"
#include "art_method.h"
#include "base/bit_utils.h"
+#include "base/malloc_arena_pool.h"
#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "linker/linker_patch.h"
@@ -355,7 +356,7 @@
}
std::vector<uint8_t> Thumb2RelativePatcher::CompileThunk(const ThunkKey& key) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
arm::ArmVIXLAssembler assembler(&allocator);
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index b268204..4bfe99b 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -20,6 +20,7 @@
#include "arch/arm64/instruction_set_features_arm64.h"
#include "art_method.h"
#include "base/bit_utils.h"
+#include "base/malloc_arena_pool.h"
#include "compiled_method-inl.h"
#include "driver/compiler_driver.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
@@ -511,7 +512,7 @@
}
std::vector<uint8_t> Arm64RelativePatcher::CompileThunk(const ThunkKey& key) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
arm64::Arm64Assembler assembler(&allocator);
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 3bd5e14..e1f680f 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -24,12 +24,12 @@
#include "base/bit_field.h"
#include "base/bit_utils.h"
#include "base/enums.h"
+#include "base/memory_region.h"
#include "dex/string_reference.h"
#include "dex/type_reference.h"
#include "globals.h"
#include "graph_visualizer.h"
#include "locations.h"
-#include "memory_region.h"
#include "nodes.h"
#include "optimizing_compiler_stats.h"
#include "read_barrier_option.h"
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 4053f55..82d1fda 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -6755,8 +6755,8 @@
return 0;
}
-// Interface case has 3 temps, one for holding the number of interfaces, one for the current
-// interface pointer, one for loading the current interface.
+// Interface case has 2 temps, one for holding the number of interfaces, one for the current
+// interface pointer, the current interface is compared in memory.
// The other checks have one temp for loading the object's class.
static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
@@ -7069,9 +7069,7 @@
} else {
locations->SetInAt(1, Location::Any());
}
- // Note that TypeCheckSlowPathX86 uses this "temp" register too.
- locations->AddTemp(Location::RequiresRegister());
- // When read barriers are enabled, we need an additional temporary register for some cases.
+ // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathX86.
locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
}
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 496d79d..322b0cf 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -6063,24 +6063,26 @@
CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
}
-static bool CheckCastTypeCheckNeedsATemporary(TypeCheckKind type_check_kind) {
- if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
- // We need a temporary for holding the iftable length.
- return true;
- }
- return kEmitCompilerReadBarrier &&
+// Temp is used for read barrier.
+static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
+ if (kEmitCompilerReadBarrier &&
!kUseBakerReadBarrier &&
(type_check_kind == TypeCheckKind::kAbstractClassCheck ||
type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
- type_check_kind == TypeCheckKind::kArrayObjectCheck);
+ type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
+ return 1;
+ }
+ return 0;
}
-static bool InstanceOfTypeCheckNeedsATemporary(TypeCheckKind type_check_kind) {
- return kEmitCompilerReadBarrier &&
- !kUseBakerReadBarrier &&
- (type_check_kind == TypeCheckKind::kAbstractClassCheck ||
- type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
- type_check_kind == TypeCheckKind::kArrayObjectCheck);
+// Interface case has 2 temps, one for holding the number of interfaces, one for the current
+// interface pointer, the current interface is compared in memory.
+// The other checks have one temp for loading the object's class.
+static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
+ if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
+ return 2;
+ }
+ return 1 + NumberOfInstanceOfTemps(type_check_kind);
}
void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) {
@@ -6121,11 +6123,7 @@
}
// Note that TypeCheckSlowPathX86_64 uses this "out" register too.
locations->SetOut(Location::RequiresRegister());
- // When read barriers are enabled, we need a temporary register for
- // some cases.
- if (InstanceOfTypeCheckNeedsATemporary(type_check_kind)) {
- locations->AddTemp(Location::RequiresRegister());
- }
+ locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
}
void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {
@@ -6136,9 +6134,9 @@
Location cls = locations->InAt(1);
Location out_loc = locations->Out();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
- Location maybe_temp_loc = InstanceOfTypeCheckNeedsATemporary(type_check_kind) ?
- locations->GetTemp(0) :
- Location::NoLocation();
+ const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
+ DCHECK_LE(num_temps, 1u);
+ Location maybe_temp_loc = (num_temps >= 1u) ? locations->GetTemp(0) : Location::NoLocation();
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
@@ -6401,14 +6399,8 @@
} else {
locations->SetInAt(1, Location::Any());
}
-
- // Note that TypeCheckSlowPathX86_64 uses this "temp" register too.
- locations->AddTemp(Location::RequiresRegister());
- // When read barriers are enabled, we need an additional temporary
- // register for some cases.
- if (CheckCastTypeCheckNeedsATemporary(type_check_kind)) {
- locations->AddTemp(Location::RequiresRegister());
- }
+ // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathX86.
+ locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
}
void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
@@ -6419,9 +6411,10 @@
Location cls = locations->InAt(1);
Location temp_loc = locations->GetTemp(0);
CpuRegister temp = temp_loc.AsRegister<CpuRegister>();
- Location maybe_temp2_loc = CheckCastTypeCheckNeedsATemporary(type_check_kind) ?
- locations->GetTemp(1) :
- Location::NoLocation();
+ const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
+ DCHECK_GE(num_temps, 1u);
+ DCHECK_LE(num_temps, 2u);
+ Location maybe_temp2_loc = (num_temps >= 2u) ? locations->GetTemp(1) : Location::NoLocation();
const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h
index 4a6c914..be26e67 100644
--- a/compiler/optimizing/data_type.h
+++ b/compiler/optimizing/data_type.h
@@ -210,6 +210,12 @@
static bool IsTypeConversionImplicit(Type input_type, Type result_type);
static bool IsTypeConversionImplicit(int64_t value, Type result_type);
+ static bool IsZeroExtension(Type input_type, Type result_type) {
+ return IsIntOrLongType(result_type) &&
+ IsUnsignedType(input_type) &&
+ Size(result_type) > Size(input_type);
+ }
+
static const char* PrettyDescriptor(Type type);
private:
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 676fe6b..d3cf956 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -67,7 +67,6 @@
bool TryCombineVecMultiplyAccumulate(HVecMul* mul);
void VisitShift(HBinaryOperation* shift);
-
void VisitEqual(HEqual* equal) OVERRIDE;
void VisitNotEqual(HNotEqual* equal) OVERRIDE;
void VisitBooleanNot(HBooleanNot* bool_not) OVERRIDE;
@@ -78,6 +77,7 @@
void VisitNullCheck(HNullCheck* instruction) OVERRIDE;
void VisitArrayLength(HArrayLength* instruction) OVERRIDE;
void VisitCheckCast(HCheckCast* instruction) OVERRIDE;
+ void VisitAbs(HAbs* instruction) OVERRIDE;
void VisitAdd(HAdd* instruction) OVERRIDE;
void VisitAnd(HAnd* instruction) OVERRIDE;
void VisitCondition(HCondition* instruction) OVERRIDE;
@@ -903,6 +903,30 @@
to_type == DataType::Type::kInt64);
}
+// Returns an acceptable substitution for "a" on the select
+// construct "a <cmp> b ? c : .." during MIN/MAX recognition.
+static HInstruction* AllowInMinMax(IfCondition cmp,
+ HInstruction* a,
+ HInstruction* b,
+ HInstruction* c) {
+ int64_t value = 0;
+ if (IsInt64AndGet(b, /*out*/ &value) &&
+ (((cmp == kCondLT || cmp == kCondLE) && c->IsMax()) ||
+ ((cmp == kCondGT || cmp == kCondGE) && c->IsMin()))) {
+ HConstant* other = c->AsBinaryOperation()->GetConstantRight();
+ if (other != nullptr && a == c->AsBinaryOperation()->GetLeastConstantLeft()) {
+ int64_t other_value = Int64FromConstant(other);
+ bool is_max = (cmp == kCondLT || cmp == kCondLE);
+ // Allow the max for a < 100 ? max(a, -100) : ..
+ // or the min for a > -100 ? min(a, 100) : ..
+ if (is_max ? (value >= other_value) : (value <= other_value)) {
+ return c;
+ }
+ }
+ }
+ return nullptr;
+}
+
void InstructionSimplifierVisitor::VisitSelect(HSelect* select) {
HInstruction* replace_with = nullptr;
HInstruction* condition = select->GetCondition();
@@ -946,9 +970,17 @@
DataType::Type t_type = true_value->GetType();
DataType::Type f_type = false_value->GetType();
// Here we have a <cmp> b ? true_value : false_value.
- // Test if both values are compatible integral types (resulting
- // MIN/MAX/ABS type will be int or long, like the condition).
+ // Test if both values are compatible integral types (resulting MIN/MAX/ABS
+ // type will be int or long, like the condition). Replacements are general,
+ // but assume conditions prefer constants on the right.
if (DataType::IsIntegralType(t_type) && DataType::Kind(t_type) == DataType::Kind(f_type)) {
+ // Allow a < 100 ? max(a, -100) : ..
+ // or a > -100 ? min(a, 100) : ..
+ // to use min/max instead of a to detect nested min/max expressions.
+ HInstruction* new_a = AllowInMinMax(cmp, a, b, true_value);
+ if (new_a != nullptr) {
+ a = new_a;
+ }
// Try to replace typical integral MIN/MAX/ABS constructs.
if ((cmp == kCondLT || cmp == kCondLE || cmp == kCondGT || cmp == kCondGE) &&
((a == true_value && b == false_value) ||
@@ -957,19 +989,16 @@
// or a > b ? a : b (MAX) or a > b ? b : a (MIN).
bool is_min = (cmp == kCondLT || cmp == kCondLE) == (a == true_value);
replace_with = NewIntegralMinMax(GetGraph()->GetAllocator(), a, b, select, is_min);
- } else if (true_value->IsNeg()) {
- HInstruction* negated = true_value->InputAt(0);
- if ((cmp == kCondLT || cmp == kCondLE) &&
- (a == negated && a == false_value && IsInt64Value(b, 0))) {
- // Found a < 0 ? -a : a which can be replaced by ABS(a).
- replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), false_value, select);
- }
- } else if (false_value->IsNeg()) {
- HInstruction* negated = false_value->InputAt(0);
- if ((cmp == kCondGT || cmp == kCondGE) &&
- (a == true_value && a == negated && IsInt64Value(b, 0))) {
- // Found a > 0 ? a : -a which can be replaced by ABS(a).
- replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), true_value, select);
+ } else if (((cmp == kCondLT || cmp == kCondLE) && true_value->IsNeg()) ||
+ ((cmp == kCondGT || cmp == kCondGE) && false_value->IsNeg())) {
+ bool negLeft = (cmp == kCondLT || cmp == kCondLE);
+ HInstruction* the_negated = negLeft ? true_value->InputAt(0) : false_value->InputAt(0);
+ HInstruction* not_negated = negLeft ? false_value : true_value;
+ if (a == the_negated && a == not_negated && IsInt64Value(b, 0)) {
+ // Found a < 0 ? -a : a
+ // or a > 0 ? a : -a
+ // which can be replaced by ABS(a).
+ replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), a, select);
}
} else if (true_value->IsSub() && false_value->IsSub()) {
HInstruction* true_sub1 = true_value->InputAt(0);
@@ -981,8 +1010,8 @@
((cmp == kCondLT || cmp == kCondLE) &&
(a == true_sub2 && b == true_sub1 && a == false_sub1 && b == false_sub2))) &&
AreLowerPrecisionArgs(t_type, a, b)) {
- // Found a > b ? a - b : b - a or
- // a < b ? b - a : a - b
+ // Found a > b ? a - b : b - a
+ // or a < b ? b - a : a - b
// which can be replaced by ABS(a - b) for lower precision operands a, b.
replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), true_value, select);
}
@@ -1241,6 +1270,17 @@
}
}
+void InstructionSimplifierVisitor::VisitAbs(HAbs* instruction) {
+ HInstruction* input = instruction->GetInput();
+ if (DataType::IsZeroExtension(input->GetType(), instruction->GetResultType())) {
+ // Zero extension from narrow to wide can never set sign bit in the wider
+ // operand, making the subsequent Abs redundant (e.g., abs(b & 0xff) for byte b).
+ instruction->ReplaceWith(input);
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ RecordSimplification();
+ }
+}
+
void InstructionSimplifierVisitor::VisitAdd(HAdd* instruction) {
HConstant* input_cst = instruction->GetConstantRight();
HInstruction* input_other = instruction->GetLeastConstantLeft();
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 81c0b509..c3d643a 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -2875,6 +2875,14 @@
__ Bind(&done);
}
+void IntrinsicLocationsBuilderARM64::VisitReachabilityFence(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+ locations->SetInAt(0, Location::Any());
+}
+
+void IntrinsicCodeGeneratorARM64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
+
UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index e61a0b0..29aecbc 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -3028,6 +3028,14 @@
}
}
+void IntrinsicLocationsBuilderARMVIXL::VisitReachabilityFence(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+ locations->SetInAt(0, Location::Any());
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
+
UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble) // Could be done by changing rounding mode, maybe?
UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure.
UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index bc1292b..ae248a3 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2693,6 +2693,14 @@
__ Bind(&done);
}
+void IntrinsicLocationsBuilderMIPS::VisitReachabilityFence(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+ locations->SetInAt(0, Location::Any());
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
+
// Unimplemented intrinsics.
UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil)
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index f429afd..9a9ae71 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -2354,6 +2354,14 @@
__ Bind(&done);
}
+void IntrinsicLocationsBuilderMIPS64::VisitReachabilityFence(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+ locations->SetInAt(0, Location::Any());
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
+
UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index c4f322b..f84a33b 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2928,6 +2928,13 @@
__ Bind(&done);
}
+void IntrinsicLocationsBuilderX86::VisitReachabilityFence(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+ locations->SetInAt(0, Location::Any());
+}
+
+void IntrinsicCodeGeneratorX86::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
UNIMPLEMENTED_INTRINSIC(X86, MathRoundDouble)
UNIMPLEMENTED_INTRINSIC(X86, ReferenceGetReferent)
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 437bc3d..7627dc9 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2737,6 +2737,14 @@
__ Bind(&done);
}
+void IntrinsicLocationsBuilderX86_64::VisitReachabilityFence(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+ locations->SetInAt(0, Location::Any());
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
+
UNIMPLEMENTED_INTRINSIC(X86_64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite)
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 1d83815..b41b659 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -153,6 +153,18 @@
return false;
}
}
+ // A MIN-MAX on narrower operands qualifies as well
+ // (returning the operator itself).
+ if (instruction->IsMin() || instruction->IsMax()) {
+ HBinaryOperation* min_max = instruction->AsBinaryOperation();
+ DCHECK(min_max->GetType() == DataType::Type::kInt32 ||
+ min_max->GetType() == DataType::Type::kInt64);
+ if (IsSignExtensionAndGet(min_max->InputAt(0), type, operand) &&
+ IsSignExtensionAndGet(min_max->InputAt(1), type, operand)) {
+ *operand = min_max;
+ return true;
+ }
+ }
return false;
}
@@ -216,6 +228,18 @@
return false;
}
}
+ // A MIN-MAX on narrower operands qualifies as well
+ // (returning the operator itself).
+ if (instruction->IsMin() || instruction->IsMax()) {
+ HBinaryOperation* min_max = instruction->AsBinaryOperation();
+ DCHECK(min_max->GetType() == DataType::Type::kInt32 ||
+ min_max->GetType() == DataType::Type::kInt64);
+ if (IsZeroExtensionAndGet(min_max->InputAt(0), type, operand) &&
+ IsZeroExtensionAndGet(min_max->InputAt(1), type, operand)) {
+ *operand = min_max;
+ return true;
+ }
+ }
return false;
}
@@ -227,6 +251,7 @@
/*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 +272,7 @@
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 +296,28 @@
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 +325,40 @@
*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 +410,8 @@
}
// 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 +423,7 @@
// Tighten the range for signed single clipping on constant.
if (!is_unsigned) {
int64_t c = 0;
- HInstruction* notused = nullptr;
- if (IsAddConst(clippee, ¬used, &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 +445,7 @@
}
// 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 +457,7 @@
// 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).
@@ -1521,8 +1553,7 @@
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;
@@ -2026,31 +2057,37 @@
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);
@@ -2101,8 +2138,7 @@
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) {
@@ -2124,8 +2160,7 @@
}
// 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);
@@ -2175,19 +2210,11 @@
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.
@@ -2220,8 +2247,7 @@
}
// 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);
}
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index e0a9cfb..9a26f2f 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -125,11 +125,6 @@
}
void Log() const {
- if (!kIsDebugBuild && !VLOG_IS_ON(compiler)) {
- // Log only in debug builds or if the compiler is verbose.
- return;
- }
-
uint32_t compiled_intrinsics = GetStat(MethodCompilationStat::kCompiledIntrinsic);
uint32_t compiled_native_stubs = GetStat(MethodCompilationStat::kCompiledNativeStub);
uint32_t bytecode_attempts =
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 6dcbadb..a9bc566 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -20,6 +20,7 @@
#include <memory>
#include <vector>
+#include "base/malloc_arena_pool.h"
#include "base/scoped_arena_allocator.h"
#include "builder.h"
#include "common_compiler_test.h"
@@ -97,7 +98,7 @@
ScopedArenaAllocator* GetScopedAllocator() { return &scoped_allocator_; }
private:
- ArenaPool pool_;
+ MallocArenaPool pool_;
ArenaAllocator allocator_;
ArenaStack arena_stack_;
ScopedArenaAllocator scoped_allocator_;
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index cb87cab..be35201 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/malloc_arena_pool.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
@@ -180,7 +181,7 @@
TYPED_TEST(ParallelMoveTest, Dependency) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
{
@@ -207,7 +208,7 @@
}
TYPED_TEST(ParallelMoveTest, Cycle) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
{
@@ -257,7 +258,7 @@
}
TYPED_TEST(ParallelMoveTest, ConstantLast) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
TypeParam resolver(&allocator);
HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
@@ -276,7 +277,7 @@
}
TYPED_TEST(ParallelMoveTest, Pairs) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
{
@@ -453,7 +454,7 @@
}
TYPED_TEST(ParallelMoveTest, MultiCycles) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
{
@@ -551,7 +552,7 @@
// Test that we do 64bits moves before 32bits moves.
TYPED_TEST(ParallelMoveTest, CyclesWith64BitsMoves) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
{
@@ -610,7 +611,7 @@
}
TYPED_TEST(ParallelMoveTest, CyclesWith64BitsMoves2) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
{
diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc
index 66e5142..3f52bdd 100644
--- a/compiler/optimizing/select_generator.cc
+++ b/compiler/optimizing/select_generator.cc
@@ -43,12 +43,16 @@
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* instruction = it.Current();
if (instruction->IsControlFlow()) {
- if (num_instructions > kMaxInstructionsInBranch) {
- return false;
- }
return instruction->IsGoto() || instruction->IsReturn();
} else if (instruction->CanBeMoved() && !instruction->HasSideEffects()) {
- num_instructions++;
+ if (instruction->IsSelect() &&
+ instruction->AsSelect()->GetCondition()->GetBlock() == block) {
+ // Count one HCondition and HSelect in the same block as a single instruction.
+ // This enables finding nested selects.
+ continue;
+ } else if (++num_instructions > kMaxInstructionsInBranch) {
+ return false; // bail as soon as we exceed number of allowed instructions
+ }
} else {
return false;
}
@@ -97,6 +101,7 @@
HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
DCHECK_NE(true_block, false_block);
+
if (!IsSimpleBlock(true_block) ||
!IsSimpleBlock(false_block) ||
!BlocksMergeTogether(true_block, false_block)) {
@@ -107,10 +112,10 @@
// If the branches are not empty, move instructions in front of the If.
// TODO(dbrazdil): This puts an instruction between If and its condition.
// Implement moving of conditions to first users if possible.
- if (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) {
+ while (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) {
true_block->GetFirstInstruction()->MoveBefore(if_instruction);
}
- if (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) {
+ while (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) {
false_block->GetFirstInstruction()->MoveBefore(if_instruction);
}
DCHECK(true_block->IsSingleGoto() || true_block->IsSingleReturn());
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 579aabd..268e9bd 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -19,9 +19,9 @@
#include "base/bit_vector-inl.h"
#include "base/hash_map.h"
+#include "base/memory_region.h"
#include "base/scoped_arena_containers.h"
#include "base/value_object.h"
-#include "memory_region.h"
#include "method_info.h"
#include "nodes.h"
#include "stack_map.h"
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index 7e517f3..e36c592 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -18,6 +18,7 @@
#include "art_method.h"
#include "base/arena_bit_vector.h"
+#include "base/malloc_arena_pool.h"
#include "stack_map_stream.h"
#include "gtest/gtest.h"
@@ -46,7 +47,7 @@
using Kind = DexRegisterLocation::Kind;
TEST(StackMapTest, Test1) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
@@ -128,7 +129,7 @@
}
TEST(StackMapTest, Test2) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
@@ -413,7 +414,7 @@
}
TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
@@ -508,7 +509,7 @@
}
TEST(StackMapTest, TestNonLiveDexRegisters) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
@@ -588,7 +589,7 @@
// StackMap::kNoDexRegisterMapSmallEncoding, and ensure we do
// not treat it as kNoDexRegisterMap.
TEST(StackMapTest, DexRegisterMapOffsetOverflow) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
@@ -652,7 +653,7 @@
}
TEST(StackMapTest, TestShareDexRegisterMap) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
@@ -711,7 +712,7 @@
}
TEST(StackMapTest, TestNoDexRegisterMap) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
@@ -761,7 +762,7 @@
}
TEST(StackMapTest, InlineTest) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
@@ -949,7 +950,7 @@
}
TEST(StackMapTest, TestDeduplicateStackMask) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
@@ -978,7 +979,7 @@
}
TEST(StackMapTest, TestInvokeInfo) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc
index 921d401..57360e7 100644
--- a/compiler/trampolines/trampoline_compiler.cc
+++ b/compiler/trampolines/trampoline_compiler.cc
@@ -17,6 +17,7 @@
#include "trampoline_compiler.h"
#include "base/arena_allocator.h"
+#include "base/malloc_arena_pool.h"
#include "jni_env_ext.h"
#ifdef ART_ENABLE_CODEGEN_arm
@@ -243,7 +244,7 @@
std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline64(InstructionSet isa,
EntryPointCallingConvention abi,
ThreadOffset64 offset) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
switch (isa) {
#ifdef ART_ENABLE_CODEGEN_arm64
@@ -269,7 +270,7 @@
std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline32(InstructionSet isa,
EntryPointCallingConvention abi,
ThreadOffset32 offset) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
switch (isa) {
#ifdef ART_ENABLE_CODEGEN_arm
diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc
index 944c64b..421c1b6 100644
--- a/compiler/utils/assembler.cc
+++ b/compiler/utils/assembler.cc
@@ -20,8 +20,8 @@
#include <vector>
#include "base/casts.h"
+#include "base/memory_region.h"
#include "globals.h"
-#include "memory_region.h"
namespace art {
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index 5b0cd6b..379a639 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -29,10 +29,10 @@
#include "base/array_ref.h"
#include "base/enums.h"
#include "base/macros.h"
+#include "base/memory_region.h"
#include "debug/dwarf/debug_frame_opcode_writer.h"
#include "label.h"
#include "managed_register.h"
-#include "memory_region.h"
#include "mips/constants_mips.h"
#include "offsets.h"
#include "x86/constants_x86.h"
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 0cb8bbb..7c800b3 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -26,6 +26,7 @@
#include <fstream>
#include <iterator>
+#include "base/malloc_arena_pool.h"
#include "assembler_test_base.h"
#include "common_runtime_test.h" // For ScratchFile
@@ -1606,7 +1607,7 @@
static constexpr size_t kWarnManyCombinationsThreshold = 500;
- ArenaPool pool_;
+ MallocArenaPool pool_;
std::unique_ptr<ArenaAllocator> allocator_;
std::unique_ptr<Ass> assembler_;
std::unique_ptr<AssemblerTestInfrastructure> test_helper_;
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 655d17d..053e202 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -27,6 +27,7 @@
#include "utils/arm/jni_macro_assembler_arm_vixl.h"
#include "base/hex_dump.h"
+#include "base/malloc_arena_pool.h"
#include "common_runtime_test.h"
namespace art {
@@ -169,7 +170,7 @@
public:
ArmVIXLAssemblerTest() : pool(), allocator(&pool), assembler(&allocator) { }
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator;
ArmVIXLJNIMacroAssembler assembler;
};
diff --git a/compiler/utils/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h
index 4bd323d..9915498 100644
--- a/compiler/utils/atomic_dex_ref_map-inl.h
+++ b/compiler/utils/atomic_dex_ref_map-inl.h
@@ -81,8 +81,7 @@
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/compiler/utils/jni_macro_assembler.cc b/compiler/utils/jni_macro_assembler.cc
index 3f7691b..0c34aa4f 100644
--- a/compiler/utils/jni_macro_assembler.cc
+++ b/compiler/utils/jni_macro_assembler.cc
@@ -38,8 +38,8 @@
#include "x86_64/jni_macro_assembler_x86_64.h"
#endif
#include "base/casts.h"
+#include "base/memory_region.h"
#include "globals.h"
-#include "memory_region.h"
namespace art {
diff --git a/compiler/utils/jni_macro_assembler_test.h b/compiler/utils/jni_macro_assembler_test.h
index 1aefc84..b70c18b 100644
--- a/compiler/utils/jni_macro_assembler_test.h
+++ b/compiler/utils/jni_macro_assembler_test.h
@@ -20,6 +20,7 @@
#include "jni_macro_assembler.h"
#include "assembler_test_base.h"
+#include "base/malloc_arena_pool.h"
#include "common_runtime_test.h" // For ScratchFile
#include <sys/stat.h>
@@ -139,7 +140,7 @@
test_helper_->Driver(*data, assembly_text, test_name);
}
- ArenaPool pool_;
+ MallocArenaPool pool_;
std::unique_ptr<ArenaAllocator> allocator_;
std::unique_ptr<Ass> assembler_;
std::unique_ptr<AssemblerTestInfrastructure> test_helper_;
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index b2ad490..dce5b95 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -18,9 +18,9 @@
#include "base/bit_utils.h"
#include "base/casts.h"
+#include "base/memory_region.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
-#include "memory_region.h"
#include "thread.h"
namespace art {
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 5a817fa..bb1bb82 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -18,9 +18,9 @@
#include "base/bit_utils.h"
#include "base/casts.h"
+#include "base/memory_region.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
-#include "memory_region.h"
#include "thread.h"
namespace art {
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 42c2541..86f9010 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -17,8 +17,8 @@
#include "assembler_x86.h"
#include "base/casts.h"
+#include "base/memory_region.h"
#include "entrypoints/quick/quick_entrypoints.h"
-#include "memory_region.h"
#include "thread.h"
namespace art {
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 8f72db7..cd007b3 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -17,13 +17,14 @@
#include "assembler_x86.h"
#include "base/arena_allocator.h"
+#include "base/malloc_arena_pool.h"
#include "base/stl_util.h"
#include "utils/assembler_test.h"
namespace art {
TEST(AssemblerX86, CreateBuffer) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
AssemblerBuffer buffer(&allocator);
AssemblerBuffer::EnsureCapacity ensured(&buffer);
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index c6e16e7..bd31561 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -17,8 +17,8 @@
#include "assembler_x86_64.h"
#include "base/casts.h"
+#include "base/memory_region.h"
#include "entrypoints/quick/quick_entrypoints.h"
-#include "memory_region.h"
#include "thread.h"
namespace art {
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 104e215..0589df5 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -21,6 +21,7 @@
#include <random>
#include "base/bit_utils.h"
+#include "base/malloc_arena_pool.h"
#include "base/stl_util.h"
#include "jni_macro_assembler_x86_64.h"
#include "utils/assembler_test.h"
@@ -29,7 +30,7 @@
namespace art {
TEST(AssemblerX86_64, CreateBuffer) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
AssemblerBuffer buffer(&allocator);
AssemblerBuffer::EnsureCapacity ensured(&buffer);
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
index 5766f9d..9486cb4 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
@@ -17,8 +17,8 @@
#include "jni_macro_assembler_x86_64.h"
#include "base/casts.h"
+#include "base/memory_region.h"
#include "entrypoints/quick/quick_entrypoints.h"
-#include "memory_region.h"
#include "thread.h"
namespace art {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 6950b93..e217769 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -404,6 +404,7 @@
UsageError(" Example: --very-large-app-threshold=100000000");
UsageError("");
UsageError(" --app-image-fd=<file-descriptor>: specify output file descriptor for app image.");
+ UsageError(" The image is non-empty only if a profile is passed in.");
UsageError(" Example: --app-image-fd=10");
UsageError("");
UsageError(" --app-image-file=<file-name>: specify a file name for app image.");
@@ -453,7 +454,9 @@
UsageError(" The image writer will group them together.");
UsageError("");
UsageError(" --compact-dex-level=none|fast: None avoids generating compact dex, fast");
- UsageError(" generates compact dex with low compile time.");
+ UsageError(" generates compact dex with low compile time. If speed-profile is specified as");
+ UsageError(" the compiler filter and the profile is not empty, the default compact dex");
+ UsageError(" level is always used.");
UsageError("");
UsageError(" --deduplicate-code=true|false: enable|disable code deduplication. Deduplicated");
UsageError(" code will have an arbitrary symbol tagged with [DEDUPED].");
@@ -1479,9 +1482,15 @@
}
void LoadClassProfileDescriptors() {
- if (profile_compilation_info_ != nullptr && IsImage()) {
- Runtime* runtime = Runtime::Current();
- CHECK(runtime != nullptr);
+ if (!IsImage()) {
+ return;
+ }
+ // If we don't have a profile, treat it as an empty set of classes. b/77340429
+ if (image_classes_ == nullptr) {
+ // May be non-null when --image-classes is passed in, in that case avoid clearing the list.
+ image_classes_.reset(new std::unordered_set<std::string>());
+ }
+ if (profile_compilation_info_ != nullptr) {
// Filter out class path classes since we don't want to include these in the image.
image_classes_.reset(
new std::unordered_set<std::string>(
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 0cd39ac..c890f8be 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -2093,4 +2093,36 @@
ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_;
}
+TEST_F(Dex2oatTest, AppImageNoProfile) {
+ ScratchFile app_image_file;
+ const std::string out_dir = GetScratchDir();
+ const std::string odex_location = out_dir + "/base.odex";
+ GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
+ odex_location,
+ CompilerFilter::Filter::kSpeedProfile,
+ { "--app-image-fd=" + std::to_string(app_image_file.GetFd()) },
+ true, // expect_success
+ false, // use_fd
+ [](const OatFile&) {});
+ // Open our generated oat file.
+ std::string error_msg;
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+ odex_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ odex_location.c_str(),
+ &error_msg));
+ ASSERT_TRUE(odex_file != nullptr);
+ ImageHeader header = {};
+ ASSERT_TRUE(app_image_file.GetFile()->PreadFully(
+ reinterpret_cast<void*>(&header),
+ sizeof(header),
+ /*offset*/ 0u)) << app_image_file.GetFile()->GetLength();
+ EXPECT_GT(header.GetImageSection(ImageHeader::kSectionObjects).Size(), 0u);
+ EXPECT_EQ(header.GetImageSection(ImageHeader::kSectionArtMethods).Size(), 0u);
+ EXPECT_EQ(header.GetImageSection(ImageHeader::kSectionArtFields).Size(), 0u);
+}
+
} // namespace art
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 65a4c5b..bcc5909 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -445,6 +445,11 @@
absolute_patch_locations_(),
profile_compilation_info_(info),
compact_dex_level_(compact_dex_level) {
+ // If we have a profile, always use at least the default compact dex level. The reason behind
+ // this is that CompactDex conversion is not more expensive than normal dexlayout.
+ if (info != nullptr && compact_dex_level_ == CompactDexLevel::kCompactDexLevelNone) {
+ compact_dex_level_ = kDefaultCompactDexLevel;
+ }
}
static bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location) {
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 3c61944..62157e1 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -20,13 +20,18 @@
host_supported: true,
srcs: [
"base/allocator.cc",
+ "base/arena_allocator.cc",
+ "base/arena_bit_vector.cc",
"base/bit_vector.cc",
"base/file_magic.cc",
"base/hex_dump.cc",
"base/logging.cc",
+ "base/malloc_arena_pool.cc",
+ "base/memory_region.cc",
"base/os_linux.cc",
"base/runtime_debug.cc",
"base/safe_copy.cc",
+ "base/scoped_arena_allocator.cc",
"base/scoped_flock.cc",
"base/time_utils.cc",
"base/unix_file/fd_file.cc",
@@ -90,6 +95,7 @@
"art_gtest_defaults",
],
srcs: [
+ "base/arena_allocator_test.cc",
"base/bit_field_test.cc",
"base/bit_string_test.cc",
"base/bit_struct_test.cc",
@@ -100,6 +106,7 @@
"base/histogram_test.cc",
"base/leb128_test.cc",
"base/logging_test.cc",
+ "base/memory_region_test.cc",
"base/safe_copy_test.cc",
"base/scoped_flock_test.cc",
"base/time_utils_test.cc",
diff --git a/libartbase/base/allocator.cc b/libartbase/base/allocator.cc
index 17da789..c7be4e0 100644
--- a/libartbase/base/allocator.cc
+++ b/libartbase/base/allocator.cc
@@ -76,7 +76,7 @@
// These globals are safe since they don't have any non-trivial destructors.
Atomic<size_t> g_bytes_used[kAllocatorTagCount];
-volatile size_t g_max_bytes_used[kAllocatorTagCount];
+Atomic<size_t> g_max_bytes_used[kAllocatorTagCount];
Atomic<uint64_t> g_total_bytes_used[kAllocatorTagCount];
void Dump(std::ostream& os) {
@@ -84,7 +84,7 @@
os << "Dumping native memory usage\n";
for (size_t i = 0; i < kAllocatorTagCount; ++i) {
uint64_t bytes_used = g_bytes_used[i].load(std::memory_order_relaxed);
- uint64_t max_bytes_used = g_max_bytes_used[i];
+ uint64_t max_bytes_used = g_max_bytes_used[i].load(std::memory_order_relaxed);
uint64_t total_bytes_used = g_total_bytes_used[i].load(std::memory_order_relaxed);
if (total_bytes_used != 0) {
os << static_cast<AllocatorTag>(i) << " active=" << bytes_used << " max="
diff --git a/libartbase/base/allocator.h b/libartbase/base/allocator.h
index 7ddbacf..662f78e 100644
--- a/libartbase/base/allocator.h
+++ b/libartbase/base/allocator.h
@@ -71,12 +71,14 @@
namespace TrackedAllocators {
+// We use memory_order_relaxed updates of the following counters. Values are treated as approximate
+// wherever concurrent updates are possible.
// Running count of number of bytes used for this kind of allocation. Increased by allocations,
// decreased by deallocations.
extern Atomic<size_t> g_bytes_used[kAllocatorTagCount];
// Largest value of bytes used seen.
-extern volatile size_t g_max_bytes_used[kAllocatorTagCount];
+extern Atomic<size_t> g_max_bytes_used[kAllocatorTagCount];
// Total number of bytes allocated of this kind.
extern Atomic<uint64_t> g_total_bytes_used[kAllocatorTagCount];
@@ -84,15 +86,17 @@
void Dump(std::ostream& os);
inline void RegisterAllocation(AllocatorTag tag, size_t bytes) {
- g_total_bytes_used[tag].fetch_add(bytes, std::memory_order_seq_cst);
- size_t new_bytes = g_bytes_used[tag].fetch_add(bytes, std::memory_order_seq_cst) + bytes;
- if (g_max_bytes_used[tag] < new_bytes) {
- g_max_bytes_used[tag] = new_bytes;
+ g_total_bytes_used[tag].fetch_add(bytes, std::memory_order_relaxed);
+ size_t new_bytes = g_bytes_used[tag].fetch_add(bytes, std::memory_order_relaxed) + bytes;
+ size_t max_bytes = g_max_bytes_used[tag].load(std::memory_order_relaxed);
+ while (max_bytes < new_bytes
+ && !g_max_bytes_used[tag].compare_exchange_weak(max_bytes /* updated */, new_bytes,
+ std::memory_order_relaxed)) {
}
}
inline void RegisterFree(AllocatorTag tag, size_t bytes) {
- g_bytes_used[tag].fetch_sub(bytes, std::memory_order_seq_cst);
+ g_bytes_used[tag].fetch_sub(bytes, std::memory_order_relaxed);
}
} // namespace TrackedAllocators
diff --git a/runtime/base/arena_allocator-inl.h b/libartbase/base/arena_allocator-inl.h
similarity index 86%
rename from runtime/base/arena_allocator-inl.h
rename to libartbase/base/arena_allocator-inl.h
index 0e43837..a03e9df 100644
--- a/runtime/base/arena_allocator-inl.h
+++ b/libartbase/base/arena_allocator-inl.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_BASE_ARENA_ALLOCATOR_INL_H_
-#define ART_RUNTIME_BASE_ARENA_ALLOCATOR_INL_H_
+#ifndef ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_INL_H_
+#define ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_INL_H_
#include "arena_allocator.h"
@@ -31,4 +31,4 @@
} // namespace arena_allocator
} // namespace art
-#endif // ART_RUNTIME_BASE_ARENA_ALLOCATOR_INL_H_
+#endif // ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_INL_H_
diff --git a/runtime/base/arena_allocator.cc b/libartbase/base/arena_allocator.cc
similarity index 67%
rename from runtime/base/arena_allocator.cc
rename to libartbase/base/arena_allocator.cc
index fe0f876..348a812 100644
--- a/runtime/base/arena_allocator.cc
+++ b/libartbase/base/arena_allocator.cc
@@ -25,11 +25,6 @@
#include <android-base/logging.h>
-#include "base/systrace.h"
-#include "mem_map.h"
-#include "mutex.h"
-#include "thread-current-inl.h"
-
namespace art {
constexpr size_t kMemoryToolRedZoneBytes = 8;
@@ -190,194 +185,6 @@
Arena::Arena() : bytes_allocated_(0), memory_(nullptr), size_(0), next_(nullptr) {
}
-class MallocArena FINAL : public Arena {
- public:
- explicit MallocArena(size_t size = arena_allocator::kArenaDefaultSize);
- virtual ~MallocArena();
- private:
- static constexpr size_t RequiredOverallocation() {
- return (alignof(std::max_align_t) < ArenaAllocator::kArenaAlignment)
- ? ArenaAllocator::kArenaAlignment - alignof(std::max_align_t)
- : 0u;
- }
-
- uint8_t* unaligned_memory_;
-};
-
-MallocArena::MallocArena(size_t size) {
- // We need to guarantee kArenaAlignment aligned allocation for the new arena.
- // TODO: Use std::aligned_alloc() when it becomes available with C++17.
- constexpr size_t overallocation = RequiredOverallocation();
- unaligned_memory_ = reinterpret_cast<uint8_t*>(calloc(1, size + overallocation));
- CHECK(unaligned_memory_ != nullptr); // Abort on OOM.
- DCHECK_ALIGNED(unaligned_memory_, alignof(std::max_align_t));
- if (overallocation == 0u) {
- memory_ = unaligned_memory_;
- } else {
- memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment);
- if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
- size_t head = memory_ - unaligned_memory_;
- size_t tail = overallocation - head;
- MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head);
- MEMORY_TOOL_MAKE_NOACCESS(memory_ + size, tail);
- }
- }
- DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
- size_ = size;
-}
-
-MallocArena::~MallocArena() {
- constexpr size_t overallocation = RequiredOverallocation();
- if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
- size_t head = memory_ - unaligned_memory_;
- size_t tail = overallocation - head;
- MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head);
- MEMORY_TOOL_MAKE_UNDEFINED(memory_ + size_, tail);
- }
- free(reinterpret_cast<void*>(unaligned_memory_));
-}
-
-class MemMapArena FINAL : public Arena {
- public:
- MemMapArena(size_t size, bool low_4gb, const char* name);
- virtual ~MemMapArena();
- void Release() OVERRIDE;
-
- private:
- std::unique_ptr<MemMap> map_;
-};
-
-MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) {
- // Round up to a full page as that's the smallest unit of allocation for mmap()
- // and we want to be able to use all memory that we actually allocate.
- size = RoundUp(size, kPageSize);
- std::string error_msg;
- map_.reset(MemMap::MapAnonymous(
- name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg));
- CHECK(map_.get() != nullptr) << error_msg;
- memory_ = map_->Begin();
- static_assert(ArenaAllocator::kArenaAlignment <= kPageSize,
- "Arena should not need stronger alignment than kPageSize.");
- DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
- size_ = map_->Size();
-}
-
-MemMapArena::~MemMapArena() {
- // Destroys MemMap via std::unique_ptr<>.
-}
-
-void MemMapArena::Release() {
- if (bytes_allocated_ > 0) {
- map_->MadviseDontNeedAndZero();
- bytes_allocated_ = 0;
- }
-}
-
-void Arena::Reset() {
- if (bytes_allocated_ > 0) {
- memset(Begin(), 0, bytes_allocated_);
- bytes_allocated_ = 0;
- }
-}
-
-ArenaPool::ArenaPool(bool use_malloc, bool low_4gb, const char* name)
- : use_malloc_(use_malloc),
- lock_("Arena pool lock", kArenaPoolLock),
- free_arenas_(nullptr),
- low_4gb_(low_4gb),
- name_(name) {
- if (low_4gb) {
- CHECK(!use_malloc) << "low4gb must use map implementation";
- }
- if (!use_malloc) {
- MemMap::Init();
- }
-}
-
-ArenaPool::~ArenaPool() {
- ReclaimMemory();
-}
-
-void ArenaPool::ReclaimMemory() {
- while (free_arenas_ != nullptr) {
- Arena* arena = free_arenas_;
- free_arenas_ = free_arenas_->next_;
- delete arena;
- }
-}
-
-void ArenaPool::LockReclaimMemory() {
- MutexLock lock(Thread::Current(), lock_);
- ReclaimMemory();
-}
-
-Arena* ArenaPool::AllocArena(size_t size) {
- Thread* self = Thread::Current();
- Arena* ret = nullptr;
- {
- MutexLock lock(self, lock_);
- if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) {
- ret = free_arenas_;
- free_arenas_ = free_arenas_->next_;
- }
- }
- if (ret == nullptr) {
- ret = use_malloc_ ? static_cast<Arena*>(new MallocArena(size)) :
- new MemMapArena(size, low_4gb_, name_);
- }
- ret->Reset();
- return ret;
-}
-
-void ArenaPool::TrimMaps() {
- if (!use_malloc_) {
- ScopedTrace trace(__PRETTY_FUNCTION__);
- // Doesn't work for malloc.
- MutexLock lock(Thread::Current(), lock_);
- for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
- arena->Release();
- }
- }
-}
-
-size_t ArenaPool::GetBytesAllocated() const {
- size_t total = 0;
- MutexLock lock(Thread::Current(), lock_);
- for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
- total += arena->GetBytesAllocated();
- }
- return total;
-}
-
-void ArenaPool::FreeArenaChain(Arena* first) {
- if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
- for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
- MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
- }
- }
-
- if (arena_allocator::kArenaAllocatorPreciseTracking) {
- // Do not reuse arenas when tracking.
- while (first != nullptr) {
- Arena* next = first->next_;
- delete first;
- first = next;
- }
- return;
- }
-
- if (first != nullptr) {
- Arena* last = first;
- while (last->next_ != nullptr) {
- last = last->next_;
- }
- Thread* self = Thread::Current();
- MutexLock lock(self, lock_);
- last->next_ = free_arenas_;
- free_arenas_ = first;
- }
-}
-
size_t ArenaAllocator::BytesAllocated() const {
return ArenaAllocatorStats::BytesAllocated();
}
diff --git a/runtime/base/arena_allocator.h b/libartbase/base/arena_allocator.h
similarity index 93%
rename from runtime/base/arena_allocator.h
rename to libartbase/base/arena_allocator.h
index 688f01b..3143fba 100644
--- a/runtime/base/arena_allocator.h
+++ b/libartbase/base/arena_allocator.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_
-#define ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_
+#ifndef ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_H_
+#define ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_H_
#include <stddef.h>
#include <stdint.h>
@@ -25,7 +25,6 @@
#include "base/dchecked_vector.h"
#include "base/macros.h"
#include "base/memory_tool.h"
-#include "mutex.h"
namespace art {
@@ -236,7 +235,8 @@
uint8_t* memory_;
size_t size_;
Arena* next_;
- friend class ArenaPool;
+ friend class MallocArenaPool;
+ friend class MemMapArenaPool;
friend class ArenaAllocator;
friend class ArenaStack;
friend class ScopedArenaAllocator;
@@ -250,25 +250,20 @@
class ArenaPool {
public:
- explicit ArenaPool(bool use_malloc = true,
- bool low_4gb = false,
- const char* name = "LinearAlloc");
- ~ArenaPool();
- Arena* AllocArena(size_t size) REQUIRES(!lock_);
- void FreeArenaChain(Arena* first) REQUIRES(!lock_);
- size_t GetBytesAllocated() const REQUIRES(!lock_);
- void ReclaimMemory() NO_THREAD_SAFETY_ANALYSIS;
- void LockReclaimMemory() REQUIRES(!lock_);
- // Trim the maps in arenas by madvising, used by JIT to reduce memory usage. This only works
- // use_malloc is false.
- void TrimMaps() REQUIRES(!lock_);
+ virtual ~ArenaPool() = default;
+
+ virtual Arena* AllocArena(size_t size) = 0;
+ virtual void FreeArenaChain(Arena* first) = 0;
+ virtual size_t GetBytesAllocated() const = 0;
+ virtual void ReclaimMemory() = 0;
+ virtual void LockReclaimMemory() = 0;
+ // Trim the maps in arenas by madvising, used by JIT to reduce memory usage.
+ virtual void TrimMaps() = 0;
+
+ protected:
+ ArenaPool() = default;
private:
- const bool use_malloc_;
- mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- Arena* free_arenas_ GUARDED_BY(lock_);
- const bool low_4gb_;
- const char* name_;
DISALLOW_COPY_AND_ASSIGN(ArenaPool);
};
@@ -428,4 +423,4 @@
} // namespace art
-#endif // ART_RUNTIME_BASE_ARENA_ALLOCATOR_H_
+#endif // ART_LIBARTBASE_BASE_ARENA_ALLOCATOR_H_
diff --git a/runtime/base/arena_allocator_test.cc b/libartbase/base/arena_allocator_test.cc
similarity index 95%
rename from runtime/base/arena_allocator_test.cc
rename to libartbase/base/arena_allocator_test.cc
index 68e26af..68e99f4 100644
--- a/runtime/base/arena_allocator_test.cc
+++ b/libartbase/base/arena_allocator_test.cc
@@ -16,6 +16,7 @@
#include "base/arena_allocator-inl.h"
#include "base/arena_bit_vector.h"
+#include "base/malloc_arena_pool.h"
#include "base/memory_tool.h"
#include "gtest/gtest.h"
@@ -33,7 +34,7 @@
};
TEST_F(ArenaAllocatorTest, Test) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
ArenaBitVector bv(&allocator, 10, true);
bv.SetBit(5);
@@ -44,7 +45,7 @@
TEST_F(ArenaAllocatorTest, MakeDefined) {
// Regression test to make sure we mark the allocated area defined.
- ArenaPool pool;
+ MallocArenaPool pool;
static constexpr size_t kSmallArraySize = 10;
static constexpr size_t kLargeArraySize = 50;
uint32_t* small_array;
@@ -71,7 +72,7 @@
}
{
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
// Note: Leaving some space for memory tool red zones.
void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 5 / 8);
@@ -80,7 +81,7 @@
ASSERT_EQ(1u, NumberOfArenas(&allocator));
}
{
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 13 / 16);
void* alloc2 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 11 / 16);
@@ -92,7 +93,7 @@
ASSERT_EQ(3u, NumberOfArenas(&allocator));
}
{
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 13 / 16);
void* alloc2 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 9 / 16);
@@ -105,7 +106,7 @@
ASSERT_EQ(2u, NumberOfArenas(&allocator));
}
{
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
void* alloc1 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 9 / 16);
void* alloc2 = allocator.Alloc(arena_allocator::kArenaDefaultSize * 13 / 16);
@@ -118,7 +119,7 @@
ASSERT_EQ(2u, NumberOfArenas(&allocator));
}
{
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
// Note: Leaving some space for memory tool red zones.
for (size_t i = 0; i != 15; ++i) {
@@ -133,7 +134,7 @@
}
TEST_F(ArenaAllocatorTest, AllocAlignment) {
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
for (size_t iterations = 0; iterations <= 10; ++iterations) {
for (size_t size = 1; size <= ArenaAllocator::kAlignment + 1; ++size) {
@@ -153,7 +154,7 @@
{
// Case 1: small aligned allocation, aligned extend inside arena.
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
const size_t original_size = ArenaAllocator::kAlignment * 2;
@@ -166,7 +167,7 @@
{
// Case 2: small aligned allocation, non-aligned extend inside arena.
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
const size_t original_size = ArenaAllocator::kAlignment * 2;
@@ -179,7 +180,7 @@
{
// Case 3: small non-aligned allocation, aligned extend inside arena.
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2);
@@ -192,7 +193,7 @@
{
// Case 4: small non-aligned allocation, aligned non-extend inside arena.
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2);
@@ -208,7 +209,7 @@
{
// Case 5: large allocation, aligned extend into next arena.
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
const size_t original_size = arena_allocator::kArenaDefaultSize -
@@ -222,7 +223,7 @@
{
// Case 6: large allocation, non-aligned extend into next arena.
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
const size_t original_size = arena_allocator::kArenaDefaultSize -
@@ -241,7 +242,7 @@
TEST_F(ArenaAllocatorTest, ReallocAlignment) {
{
// Case 1: small aligned allocation, aligned extend inside arena.
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
const size_t original_size = ArenaAllocator::kAlignment * 2;
@@ -258,7 +259,7 @@
{
// Case 2: small aligned allocation, non-aligned extend inside arena.
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
const size_t original_size = ArenaAllocator::kAlignment * 2;
@@ -275,7 +276,7 @@
{
// Case 3: small non-aligned allocation, aligned extend inside arena.
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2);
@@ -292,7 +293,7 @@
{
// Case 4: small non-aligned allocation, aligned non-extend inside arena.
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
const size_t original_size = ArenaAllocator::kAlignment * 2 + (ArenaAllocator::kAlignment / 2);
@@ -312,7 +313,7 @@
{
// Case 5: large allocation, aligned extend into next arena.
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
const size_t original_size = arena_allocator::kArenaDefaultSize -
@@ -330,7 +331,7 @@
{
// Case 6: large allocation, non-aligned extend into next arena.
- ArenaPool pool;
+ MallocArenaPool pool;
ArenaAllocator allocator(&pool);
const size_t original_size = arena_allocator::kArenaDefaultSize -
diff --git a/runtime/base/arena_bit_vector.cc b/libartbase/base/arena_bit_vector.cc
similarity index 100%
rename from runtime/base/arena_bit_vector.cc
rename to libartbase/base/arena_bit_vector.cc
diff --git a/runtime/base/arena_bit_vector.h b/libartbase/base/arena_bit_vector.h
similarity index 92%
rename from runtime/base/arena_bit_vector.h
rename to libartbase/base/arena_bit_vector.h
index ca1d5b1..2b2322e 100644
--- a/runtime/base/arena_bit_vector.h
+++ b/libartbase/base/arena_bit_vector.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_BASE_ARENA_BIT_VECTOR_H_
-#define ART_RUNTIME_BASE_ARENA_BIT_VECTOR_H_
+#ifndef ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_
+#define ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_
#include "base/arena_object.h"
#include "base/bit_vector.h"
@@ -55,4 +55,4 @@
} // namespace art
-#endif // ART_RUNTIME_BASE_ARENA_BIT_VECTOR_H_
+#endif // ART_LIBARTBASE_BASE_ARENA_BIT_VECTOR_H_
diff --git a/runtime/base/arena_containers.h b/libartbase/base/arena_containers.h
similarity index 97%
rename from runtime/base/arena_containers.h
rename to libartbase/base/arena_containers.h
index 4f572120..40cf23c 100644
--- a/runtime/base/arena_containers.h
+++ b/libartbase/base/arena_containers.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_BASE_ARENA_CONTAINERS_H_
-#define ART_RUNTIME_BASE_ARENA_CONTAINERS_H_
+#ifndef ART_LIBARTBASE_BASE_ARENA_CONTAINERS_H_
+#define ART_LIBARTBASE_BASE_ARENA_CONTAINERS_H_
#include <deque>
#include <queue>
@@ -241,4 +241,4 @@
} // namespace art
-#endif // ART_RUNTIME_BASE_ARENA_CONTAINERS_H_
+#endif // ART_LIBARTBASE_BASE_ARENA_CONTAINERS_H_
diff --git a/runtime/base/arena_object.h b/libartbase/base/arena_object.h
similarity index 93%
rename from runtime/base/arena_object.h
rename to libartbase/base/arena_object.h
index d01e346..de7cb64 100644
--- a/runtime/base/arena_object.h
+++ b/libartbase/base/arena_object.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_BASE_ARENA_OBJECT_H_
-#define ART_RUNTIME_BASE_ARENA_OBJECT_H_
+#ifndef ART_LIBARTBASE_BASE_ARENA_OBJECT_H_
+#define ART_LIBARTBASE_BASE_ARENA_OBJECT_H_
#include <android-base/logging.h>
@@ -69,4 +69,4 @@
} // namespace art
-#endif // ART_RUNTIME_BASE_ARENA_OBJECT_H_
+#endif // ART_LIBARTBASE_BASE_ARENA_OBJECT_H_
diff --git a/runtime/bit_memory_region.h b/libartbase/base/bit_memory_region.h
similarity index 93%
rename from runtime/bit_memory_region.h
rename to libartbase/base/bit_memory_region.h
index 3a696f1..f3926bc 100644
--- a/runtime/bit_memory_region.h
+++ b/libartbase/base/bit_memory_region.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_BIT_MEMORY_REGION_H_
-#define ART_RUNTIME_BIT_MEMORY_REGION_H_
+#ifndef ART_LIBARTBASE_BASE_BIT_MEMORY_REGION_H_
+#define ART_LIBARTBASE_BASE_BIT_MEMORY_REGION_H_
#include "memory_region.h"
@@ -70,4 +70,4 @@
} // namespace art
-#endif // ART_RUNTIME_BIT_MEMORY_REGION_H_
+#endif // ART_LIBARTBASE_BASE_BIT_MEMORY_REGION_H_
diff --git a/runtime/base/dumpable.h b/libartbase/base/dumpable.h
similarity index 63%
rename from runtime/base/dumpable.h
rename to libartbase/base/dumpable.h
index 9ef8d69..6621397 100644
--- a/runtime/base/dumpable.h
+++ b/libartbase/base/dumpable.h
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_BASE_DUMPABLE_H_
-#define ART_RUNTIME_BASE_DUMPABLE_H_
+#ifndef ART_LIBARTBASE_BASE_DUMPABLE_H_
+#define ART_LIBARTBASE_BASE_DUMPABLE_H_
#include <ostream>
#include "base/macros.h"
-#include "base/mutex.h"
namespace art {
@@ -51,27 +50,6 @@
return os;
}
-template<typename T>
-class MutatorLockedDumpable {
- public:
- explicit MutatorLockedDumpable(T& value) REQUIRES_SHARED(Locks::mutator_lock_) : value_(value) {}
-
- void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_) {
- value_.Dump(os);
- }
-
- private:
- const T& value_;
-
- DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable);
-};
-
-template<typename T>
-std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs)
- // TODO: should be REQUIRES_SHARED(Locks::mutator_lock_) however annotalysis
- // currently fails for this.
- NO_THREAD_SAFETY_ANALYSIS;
-
} // namespace art
-#endif // ART_RUNTIME_BASE_DUMPABLE_H_
+#endif // ART_LIBARTBASE_BASE_DUMPABLE_H_
diff --git a/libartbase/base/malloc_arena_pool.cc b/libartbase/base/malloc_arena_pool.cc
new file mode 100644
index 0000000..7df4aef
--- /dev/null
+++ b/libartbase/base/malloc_arena_pool.cc
@@ -0,0 +1,162 @@
+/*
+ * 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 "malloc_arena_pool.h"
+
+#include <sys/mman.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <iomanip>
+#include <numeric>
+
+#include <android-base/logging.h>
+#include "base/arena_allocator-inl.h"
+
+namespace art {
+
+class MallocArena FINAL : public Arena {
+ public:
+ explicit MallocArena(size_t size = arena_allocator::kArenaDefaultSize);
+ virtual ~MallocArena();
+ private:
+ static constexpr size_t RequiredOverallocation() {
+ return (alignof(std::max_align_t) < ArenaAllocator::kArenaAlignment)
+ ? ArenaAllocator::kArenaAlignment - alignof(std::max_align_t)
+ : 0u;
+ }
+
+ uint8_t* unaligned_memory_;
+};
+
+MallocArena::MallocArena(size_t size) {
+ // We need to guarantee kArenaAlignment aligned allocation for the new arena.
+ // TODO: Use std::aligned_alloc() when it becomes available with C++17.
+ constexpr size_t overallocation = RequiredOverallocation();
+ unaligned_memory_ = reinterpret_cast<uint8_t*>(calloc(1, size + overallocation));
+ CHECK(unaligned_memory_ != nullptr); // Abort on OOM.
+ DCHECK_ALIGNED(unaligned_memory_, alignof(std::max_align_t));
+ if (overallocation == 0u) {
+ memory_ = unaligned_memory_;
+ } else {
+ memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment);
+ if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+ size_t head = memory_ - unaligned_memory_;
+ size_t tail = overallocation - head;
+ MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head);
+ MEMORY_TOOL_MAKE_NOACCESS(memory_ + size, tail);
+ }
+ }
+ DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
+ size_ = size;
+}
+
+MallocArena::~MallocArena() {
+ constexpr size_t overallocation = RequiredOverallocation();
+ if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+ size_t head = memory_ - unaligned_memory_;
+ size_t tail = overallocation - head;
+ MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head);
+ MEMORY_TOOL_MAKE_UNDEFINED(memory_ + size_, tail);
+ }
+ free(reinterpret_cast<void*>(unaligned_memory_));
+}
+
+void Arena::Reset() {
+ if (bytes_allocated_ > 0) {
+ memset(Begin(), 0, bytes_allocated_);
+ bytes_allocated_ = 0;
+ }
+}
+
+MallocArenaPool::MallocArenaPool() : free_arenas_(nullptr) {
+}
+
+MallocArenaPool::~MallocArenaPool() {
+ ReclaimMemory();
+}
+
+void MallocArenaPool::ReclaimMemory() {
+ while (free_arenas_ != nullptr) {
+ Arena* arena = free_arenas_;
+ free_arenas_ = free_arenas_->next_;
+ delete arena;
+ }
+}
+
+void MallocArenaPool::LockReclaimMemory() {
+ std::lock_guard<std::mutex> lock(lock_);
+ ReclaimMemory();
+}
+
+Arena* MallocArenaPool::AllocArena(size_t size) {
+ Arena* ret = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) {
+ ret = free_arenas_;
+ free_arenas_ = free_arenas_->next_;
+ }
+ }
+ if (ret == nullptr) {
+ ret = new MallocArena(size);
+ }
+ ret->Reset();
+ return ret;
+}
+
+void MallocArenaPool::TrimMaps() {
+ // Nop, because there is no way to do madvise here.
+}
+
+size_t MallocArenaPool::GetBytesAllocated() const {
+ size_t total = 0;
+ std::lock_guard<std::mutex> lock(lock_);
+ for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
+ total += arena->GetBytesAllocated();
+ }
+ return total;
+}
+
+void MallocArenaPool::FreeArenaChain(Arena* first) {
+ if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+ for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
+ MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
+ }
+ }
+
+ if (arena_allocator::kArenaAllocatorPreciseTracking) {
+ // Do not reuse arenas when tracking.
+ while (first != nullptr) {
+ Arena* next = first->next_;
+ delete first;
+ first = next;
+ }
+ return;
+ }
+
+ if (first != nullptr) {
+ Arena* last = first;
+ while (last->next_ != nullptr) {
+ last = last->next_;
+ }
+ std::lock_guard<std::mutex> lock(lock_);
+ last->next_ = free_arenas_;
+ free_arenas_ = first;
+ }
+}
+
+} // namespace art
diff --git a/libartbase/base/malloc_arena_pool.h b/libartbase/base/malloc_arena_pool.h
new file mode 100644
index 0000000..8720189
--- /dev/null
+++ b/libartbase/base/malloc_arena_pool.h
@@ -0,0 +1,48 @@
+/*
+ * 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_LIBARTBASE_BASE_MALLOC_ARENA_POOL_H_
+#define ART_LIBARTBASE_BASE_MALLOC_ARENA_POOL_H_
+
+#include <mutex>
+
+#include "base/arena_allocator.h"
+
+namespace art {
+
+class MallocArenaPool FINAL : public ArenaPool {
+ public:
+ MallocArenaPool();
+ ~MallocArenaPool();
+ Arena* AllocArena(size_t size) OVERRIDE;
+ void FreeArenaChain(Arena* first) OVERRIDE;
+ size_t GetBytesAllocated() const OVERRIDE;
+ void ReclaimMemory() OVERRIDE;
+ void LockReclaimMemory() OVERRIDE;
+ // Is a nop for malloc pools.
+ void TrimMaps() OVERRIDE;
+
+ private:
+ Arena* free_arenas_;
+ // Use a std::mutex here as Arenas are at the bottom of the lock hierarchy when malloc is used.
+ mutable std::mutex lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(MallocArenaPool);
+};
+
+} // namespace art
+
+#endif // ART_LIBARTBASE_BASE_MALLOC_ARENA_POOL_H_
diff --git a/runtime/memory_region.cc b/libartbase/base/memory_region.cc
similarity index 100%
rename from runtime/memory_region.cc
rename to libartbase/base/memory_region.cc
diff --git a/runtime/memory_region.h b/libartbase/base/memory_region.h
similarity index 95%
rename from runtime/memory_region.h
rename to libartbase/base/memory_region.h
index 23e0aec..7add466 100644
--- a/runtime/memory_region.h
+++ b/libartbase/base/memory_region.h
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_MEMORY_REGION_H_
-#define ART_RUNTIME_MEMORY_REGION_H_
+#ifndef ART_LIBARTBASE_BASE_MEMORY_REGION_H_
+#define ART_LIBARTBASE_BASE_MEMORY_REGION_H_
#include <stdint.h>
#include <type_traits>
#include <android-base/logging.h>
-#include "arch/instruction_set.h"
#include "base/bit_utils.h"
#include "base/casts.h"
+#include "base/enums.h"
#include "base/macros.h"
#include "base/value_object.h"
#include "globals.h"
@@ -211,9 +211,8 @@
// Is `address` aligned on a machine word?
template<typename T> static constexpr bool IsWordAligned(const T* address) {
- // Word alignment in bytes.
- size_t kWordAlignment = static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA));
- return IsAlignedParam(address, kWordAlignment);
+ // Word alignment in bytes. Determined from pointer size.
+ return IsAligned<kRuntimePointerSize>(address);
}
void* pointer_;
@@ -222,4 +221,4 @@
} // namespace art
-#endif // ART_RUNTIME_MEMORY_REGION_H_
+#endif // ART_LIBARTBASE_BASE_MEMORY_REGION_H_
diff --git a/runtime/memory_region_test.cc b/libartbase/base/memory_region_test.cc
similarity index 100%
rename from runtime/memory_region_test.cc
rename to libartbase/base/memory_region_test.cc
diff --git a/runtime/base/scoped_arena_allocator.cc b/libartbase/base/scoped_arena_allocator.cc
similarity index 100%
rename from runtime/base/scoped_arena_allocator.cc
rename to libartbase/base/scoped_arena_allocator.cc
diff --git a/runtime/base/scoped_arena_allocator.h b/libartbase/base/scoped_arena_allocator.h
similarity index 96%
rename from runtime/base/scoped_arena_allocator.h
rename to libartbase/base/scoped_arena_allocator.h
index a253e2f..d5f6df8 100644
--- a/runtime/base/scoped_arena_allocator.h
+++ b/libartbase/base/scoped_arena_allocator.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_
-#define ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_
+#ifndef ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_
+#define ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_
#include <android-base/logging.h>
@@ -185,4 +185,4 @@
} // namespace art
-#endif // ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_
+#endif // ART_LIBARTBASE_BASE_SCOPED_ARENA_ALLOCATOR_H_
diff --git a/runtime/base/scoped_arena_containers.h b/libartbase/base/scoped_arena_containers.h
similarity index 97%
rename from runtime/base/scoped_arena_containers.h
rename to libartbase/base/scoped_arena_containers.h
index f8ee3f3..4df02b6 100644
--- a/runtime/base/scoped_arena_containers.h
+++ b/libartbase/base/scoped_arena_containers.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_BASE_SCOPED_ARENA_CONTAINERS_H_
-#define ART_RUNTIME_BASE_SCOPED_ARENA_CONTAINERS_H_
+#ifndef ART_LIBARTBASE_BASE_SCOPED_ARENA_CONTAINERS_H_
+#define ART_LIBARTBASE_BASE_SCOPED_ARENA_CONTAINERS_H_
#include <deque>
#include <queue>
@@ -272,4 +272,4 @@
} // namespace art
-#endif // ART_RUNTIME_BASE_SCOPED_ARENA_CONTAINERS_H_
+#endif // ART_LIBARTBASE_BASE_SCOPED_ARENA_CONTAINERS_H_
diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h
index 441b3c1..b62d044 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/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 433ed9a..3bff123 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1180,6 +1180,17 @@
}
}
+ // Update header for shared section.
+ uint32_t shared_section_offset = 0u;
+ uint32_t shared_section_size = 0u;
+ if (dex_file->IsCompactDexFile()) {
+ CompactDexFile::Header* const header =
+ reinterpret_cast<CompactDexFile::Header*>(const_cast<uint8_t*>(dex_file->Begin()));
+ shared_section_offset = header->data_off_;
+ shared_section_size = header->data_size_;
+ // The shared section will be serialized right after the dex file.
+ header->data_off_ = header->file_size_;
+ }
// Verify output directory exists
if (!OS::DirectoryExists(options_.export_dex_location_)) {
// TODO: Extend OS::DirectoryExists if symlink support is required
@@ -1226,16 +1237,22 @@
return false;
}
- bool success = false;
- success = file->WriteFully(dex_file->Begin(), fsize);
- // }
-
+ bool success = file->WriteFully(dex_file->Begin(), fsize);
if (!success) {
os << "Failed to write dex file";
file->Erase();
return false;
}
+ if (shared_section_size != 0) {
+ success = file->WriteFully(dex_file->Begin() + shared_section_offset, shared_section_size);
+ if (!success) {
+ os << "Failed to write shared data section";
+ file->Erase();
+ return false;
+ }
+ }
+
if (file->FlushCloseOrErase() != 0) {
os << "Flush and close failed";
return false;
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index 0034469..b4eddb9 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -72,9 +72,16 @@
}
TEST_F(OatDumpTest, TestExportDex) {
+ // Test is failing on target, b/77469384.
+ TEST_DISABLED_FOR_TARGET();
std::string error_msg;
ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly, &error_msg))
<< error_msg;
+ const std::string dex_location = tmp_dir_+ "/core-oj-hostdex.jar_export.dex";
+ const std::string dexdump2 = GetExecutableFilePath("dexdump2",
+ /*is_debug*/false,
+ /*is_static*/false);
+ ASSERT_TRUE(ForkAndExecAndWait({dexdump2, "-d", dex_location}, &error_msg)) << error_msg;
}
TEST_F(OatDumpTest, TestExportDexStatic) {
TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index fac0bb2..b85730d 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -70,20 +70,24 @@
kStatic, // oatdump(d)s, dex2oat(d)s
};
- // Returns path to the oatdump/dex2oat binary.
- std::string GetExecutableFilePath(Flavor flavor, const char* name) {
+ // Returns path to the oatdump/dex2oat/dexdump binary.
+ std::string GetExecutableFilePath(const char* name, bool is_debug, bool is_static) {
std::string root = GetTestAndroidRoot();
root += "/bin/";
root += name;
- if (kIsDebugBuild) {
+ if (is_debug) {
root += "d";
}
- if (flavor == kStatic) {
+ if (is_static) {
root += "s";
}
return root;
}
+ std::string GetExecutableFilePath(Flavor flavor, const char* name) {
+ return GetExecutableFilePath(name, kIsDebugBuild, flavor == kStatic);
+ }
+
enum Mode {
kModeOat,
kModeOatWithBootImage,
@@ -127,17 +131,7 @@
};
exec_argv.insert(exec_argv.end(), args.begin(), args.end());
- pid_t pid;
- int pipe_fd;
- bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg);
- if (result) {
- close(pipe_fd);
- int status = 0;
- if (waitpid(pid, &status, 0) != -1) {
- result = (status == 0);
- }
- }
- return result;
+ return ForkAndExecAndWait(exec_argv, error_msg);
}
// Run the test with custom arguments.
@@ -300,6 +294,21 @@
}
}
+ bool ForkAndExecAndWait(const std::vector<std::string>& exec_argv,
+ /*out*/ std::string* error_msg) {
+ pid_t pid;
+ int pipe_fd;
+ bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg);
+ if (result) {
+ close(pipe_fd);
+ int status = 0;
+ if (waitpid(pid, &status, 0) != -1) {
+ result = (status == 0);
+ }
+ }
+ return result;
+ }
+
std::string tmp_dir_;
private:
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
index 6d84ffa..a6f1207 100644
--- a/openjdkjvmti/deopt_manager.cc
+++ b/openjdkjvmti/deopt_manager.cc
@@ -53,20 +53,29 @@
namespace openjdkjvmti {
// TODO We should make this much more selective in the future so we only return true when we
-// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though
-// we can just assume that we care we are loaded at all.
-//
-// Even if we don't keep track of this at the method level we might want to keep track of it at the
-// level of enabled capabilities.
-bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(
- art::ArtMethod* method ATTRIBUTE_UNUSED) {
- return true;
+// actually care about the method at this time (ie active frames had locals changed). For now we
+// just assume that if anything has changed any frame's locals we care about all methods. If nothing
+// has we only care about methods with active breakpoints on them. In the future we should probably
+// rewrite all of this to instead do this at the ShadowFrame or thread granularity.
+bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(art::ArtMethod* method) {
+ // Non-java-debuggable runtimes we need to assume that any method might not be debuggable and
+ // therefore potentially being inspected (due to inlines). If we are debuggable we rely hard on
+ // inlining not being done since we don't keep track of which methods get inlined where and simply
+ // look to see if the method is breakpointed.
+ return !art::Runtime::Current()->IsJavaDebuggable() ||
+ manager_->HaveLocalsChanged() ||
+ manager_->MethodHasBreakpoints(method);
}
bool JvmtiMethodInspectionCallback::IsMethodSafeToJit(art::ArtMethod* method) {
return !manager_->MethodHasBreakpoints(method);
}
+bool JvmtiMethodInspectionCallback::MethodNeedsDebugVersion(
+ art::ArtMethod* method ATTRIBUTE_UNUSED) {
+ return true;
+}
+
DeoptManager::DeoptManager()
: deoptimization_status_lock_("JVMTI_DeoptimizationStatusLock",
static_cast<art::LockLevel>(
@@ -75,7 +84,10 @@
performing_deoptimization_(false),
global_deopt_count_(0),
deopter_count_(0),
- inspection_callback_(this) { }
+ breakpoint_status_lock_("JVMTI_BreakpointStatusLock",
+ static_cast<art::LockLevel>(art::LockLevel::kAbortLock + 1)),
+ inspection_callback_(this),
+ set_local_variable_called_(false) { }
void DeoptManager::Setup() {
art::ScopedThreadStateChange stsc(art::Thread::Current(),
@@ -121,14 +133,11 @@
}
bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) {
- art::MutexLock lk(art::Thread::Current(), deoptimization_status_lock_);
+ art::MutexLock lk(art::Thread::Current(), breakpoint_status_lock_);
return MethodHasBreakpointsLocked(method);
}
bool DeoptManager::MethodHasBreakpointsLocked(art::ArtMethod* method) {
- if (deopter_count_ == 0) {
- return false;
- }
auto elem = breakpoint_status_.find(method);
return elem != breakpoint_status_.end() && elem->second != 0;
}
@@ -158,18 +167,23 @@
art::ScopedThreadSuspension sts(self, art::kSuspended);
deoptimization_status_lock_.ExclusiveLock(self);
+ {
+ breakpoint_status_lock_.ExclusiveLock(self);
- DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request";
+ DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request";
- if (MethodHasBreakpointsLocked(method)) {
- // Don't need to do anything extra.
- breakpoint_status_[method]++;
- // Another thread might be deoptimizing the very method we just added new breakpoints for. Wait
- // for any deopts to finish before moving on.
- WaitForDeoptimizationToFinish(self);
- return;
+ if (MethodHasBreakpointsLocked(method)) {
+ // Don't need to do anything extra.
+ breakpoint_status_[method]++;
+ // Another thread might be deoptimizing the very method we just added new breakpoints for.
+ // Wait for any deopts to finish before moving on.
+ breakpoint_status_lock_.ExclusiveUnlock(self);
+ WaitForDeoptimizationToFinish(self);
+ return;
+ }
+ breakpoint_status_[method] = 1;
+ breakpoint_status_lock_.ExclusiveUnlock(self);
}
- breakpoint_status_[method] = 1;
auto instrumentation = art::Runtime::Current()->GetInstrumentation();
if (instrumentation->IsForcedInterpretOnly()) {
// We are already interpreting everything so no need to do anything.
@@ -196,17 +210,22 @@
// need but since that is very heavy we will instead just use a condition variable to make sure we
// don't race with ourselves.
deoptimization_status_lock_.ExclusiveLock(self);
+ bool is_last_breakpoint;
+ {
+ art::MutexLock mu(self, breakpoint_status_lock_);
- DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request";
- DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without "
- << "breakpoints present!";
+ DCHECK_GT(deopter_count_, 0u) << "unexpected deotpimization request";
+ DCHECK(MethodHasBreakpointsLocked(method)) << "Breakpoint on a method was removed without "
+ << "breakpoints present!";
+ breakpoint_status_[method] -= 1;
+ is_last_breakpoint = (breakpoint_status_[method] == 0);
+ }
auto instrumentation = art::Runtime::Current()->GetInstrumentation();
- breakpoint_status_[method] -= 1;
if (UNLIKELY(instrumentation->IsForcedInterpretOnly())) {
// We don't need to do anything since we are interpreting everything anyway.
deoptimization_status_lock_.ExclusiveUnlock(self);
return;
- } else if (breakpoint_status_[method] == 0) {
+ } else if (is_last_breakpoint) {
if (UNLIKELY(is_default)) {
RemoveDeoptimizeAllMethodsLocked(self);
} else {
diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h
index a495b68..6e991de 100644
--- a/openjdkjvmti/deopt_manager.h
+++ b/openjdkjvmti/deopt_manager.h
@@ -32,6 +32,7 @@
#ifndef ART_OPENJDKJVMTI_DEOPT_MANAGER_H_
#define ART_OPENJDKJVMTI_DEOPT_MANAGER_H_
+#include <atomic>
#include <unordered_map>
#include "jni.h"
@@ -62,6 +63,9 @@
bool IsMethodSafeToJit(art::ArtMethod* method)
OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_);
+ bool MethodNeedsDebugVersion(art::ArtMethod* method)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_);
+
private:
DeoptManager* manager_;
};
@@ -107,9 +111,17 @@
static DeoptManager* Get();
+ bool HaveLocalsChanged() const {
+ return set_local_variable_called_.load();
+ }
+
+ void SetLocalsUpdated() {
+ set_local_variable_called_.store(true);
+ }
+
private:
bool MethodHasBreakpointsLocked(art::ArtMethod* method)
- REQUIRES(deoptimization_status_lock_);
+ REQUIRES(breakpoint_status_lock_);
// Wait until nothing is currently in the middle of deoptimizing/undeoptimizing something. This is
// needed to ensure that everything is synchronized since threads need to drop the
@@ -156,13 +168,20 @@
// Number of users of deoptimization there currently are.
uint32_t deopter_count_ GUARDED_BY(deoptimization_status_lock_);
+ // A mutex that just protects the breakpoint-status map. This mutex should always be at the
+ // bottom of the lock hierarchy. Nothing more should be locked if we hold this.
+ art::Mutex breakpoint_status_lock_ ACQUIRED_BEFORE(art::Locks::abort_lock_);
// A map from methods to the number of breakpoints in them from all envs.
std::unordered_map<art::ArtMethod*, uint32_t> breakpoint_status_
- GUARDED_BY(deoptimization_status_lock_);
+ GUARDED_BY(breakpoint_status_lock_);
// The MethodInspectionCallback we use to tell the runtime if we care about particular methods.
JvmtiMethodInspectionCallback inspection_callback_;
+ // Set to true if anything calls SetLocalVariables on any thread since we need to be careful about
+ // OSR after this.
+ std::atomic<bool> set_local_variable_called_;
+
// Helper for setting up/tearing-down for deoptimization.
friend class ScopedDeoptimizationContext;
};
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index bf2e6cd..b83310d 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -915,6 +915,9 @@
if (depth < 0) {
return ERR(ILLEGAL_ARGUMENT);
}
+ // Make sure that we know not to do any OSR anymore.
+ // TODO We should really keep track of this at the Frame granularity.
+ DeoptManager::Get()->SetLocalsUpdated();
art::Thread* self = art::Thread::Current();
// Suspend JIT since it can get confused if we deoptimize methods getting jitted.
art::jit::ScopedJitSuspend suspend_jit;
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index 41a649b..4526be4 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -925,7 +925,9 @@
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 4736fd3..9271a05 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -32,12 +32,10 @@
"art_field.cc",
"art_method.cc",
"barrier.cc",
- "base/arena_allocator.cc",
- "base/arena_bit_vector.cc",
"base/file_utils.cc",
+ "base/mem_map_arena_pool.cc",
"base/mutex.cc",
"base/quasi_atomic.cc",
- "base/scoped_arena_allocator.cc",
"base/timing_logger.cc",
"cha.cc",
"check_jni.cc",
@@ -46,6 +44,7 @@
"class_table.cc",
"common_throws.cc",
"compiler_filter.cc",
+ "debug_print.cc",
"debugger.cc",
"dex/art_dex_file_loader.cc",
"dex/dex_file_annotations.cc",
@@ -87,6 +86,7 @@
"gc/space/zygote_space.cc",
"gc/task_processor.cc",
"gc/verification.cc",
+ "hidden_api.cc",
"hprof/hprof.cc",
"image.cc",
"index_bss_mapping.cc",
@@ -121,7 +121,6 @@
"linear_alloc.cc",
"managed_stack.cc",
"mem_map.cc",
- "memory_region.cc",
"method_handles.cc",
"mirror/array.cc",
"mirror/call_site.cc",
@@ -540,7 +539,6 @@
"arch/x86/instruction_set_features_x86_test.cc",
"arch/x86_64/instruction_set_features_x86_64_test.cc",
"barrier_test.cc",
- "base/arena_allocator_test.cc",
"base/file_utils_test.cc",
"base/mutex_test.cc",
"base/timing_logger_test.cc",
@@ -584,7 +582,6 @@
"java_vm_ext_test.cc",
"jit/profile_compilation_info_test.cc",
"mem_map_test.cc",
- "memory_region_test.cc",
"method_handles_test.cc",
"mirror/dex_cache_test.cc",
"mirror/method_type_test.cc",
diff --git a/runtime/base/dumpable-inl.h b/runtime/base/dumpable-inl.h
deleted file mode 100644
index 9d7fc39..0000000
--- a/runtime/base/dumpable-inl.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_BASE_DUMPABLE_INL_H_
-#define ART_RUNTIME_BASE_DUMPABLE_INL_H_
-
-#include "base/dumpable.h"
-#include "base/mutex.h"
-#include "thread-current-inl.h"
-
-namespace art {
-
-template<typename T>
-inline std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs) {
- Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
- rhs.Dump(os);
- return os;
-}
-
-} // namespace art
-
-#endif // ART_RUNTIME_BASE_DUMPABLE_INL_H_
diff --git a/runtime/base/mem_map_arena_pool.cc b/runtime/base/mem_map_arena_pool.cc
new file mode 100644
index 0000000..d5ea19b
--- /dev/null
+++ b/runtime/base/mem_map_arena_pool.cc
@@ -0,0 +1,155 @@
+/*
+ * 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 "mem_map_arena_pool.h"
+
+#include <sys/mman.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <iomanip>
+#include <numeric>
+
+#include <android-base/logging.h>
+
+#include "base/arena_allocator-inl.h"
+#include "base/systrace.h"
+#include "mem_map.h"
+
+namespace art {
+
+class MemMapArena FINAL : public Arena {
+ public:
+ MemMapArena(size_t size, bool low_4gb, const char* name);
+ virtual ~MemMapArena();
+ void Release() OVERRIDE;
+
+ private:
+ std::unique_ptr<MemMap> map_;
+};
+
+MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) {
+ // Round up to a full page as that's the smallest unit of allocation for mmap()
+ // and we want to be able to use all memory that we actually allocate.
+ size = RoundUp(size, kPageSize);
+ std::string error_msg;
+ map_.reset(MemMap::MapAnonymous(
+ name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg));
+ CHECK(map_.get() != nullptr) << error_msg;
+ memory_ = map_->Begin();
+ static_assert(ArenaAllocator::kArenaAlignment <= kPageSize,
+ "Arena should not need stronger alignment than kPageSize.");
+ DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
+ size_ = map_->Size();
+}
+
+MemMapArena::~MemMapArena() {
+ // Destroys MemMap via std::unique_ptr<>.
+}
+
+void MemMapArena::Release() {
+ if (bytes_allocated_ > 0) {
+ map_->MadviseDontNeedAndZero();
+ bytes_allocated_ = 0;
+ }
+}
+
+MemMapArenaPool::MemMapArenaPool(bool low_4gb, const char* name)
+ : low_4gb_(low_4gb),
+ name_(name),
+ free_arenas_(nullptr) {
+ MemMap::Init();
+}
+
+MemMapArenaPool::~MemMapArenaPool() {
+ ReclaimMemory();
+}
+
+void MemMapArenaPool::ReclaimMemory() {
+ while (free_arenas_ != nullptr) {
+ Arena* arena = free_arenas_;
+ free_arenas_ = free_arenas_->next_;
+ delete arena;
+ }
+}
+
+void MemMapArenaPool::LockReclaimMemory() {
+ std::lock_guard<std::mutex> lock(lock_);
+ ReclaimMemory();
+}
+
+Arena* MemMapArenaPool::AllocArena(size_t size) {
+ Arena* ret = nullptr;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) {
+ ret = free_arenas_;
+ free_arenas_ = free_arenas_->next_;
+ }
+ }
+ if (ret == nullptr) {
+ ret = new MemMapArena(size, low_4gb_, name_);
+ }
+ ret->Reset();
+ return ret;
+}
+
+void MemMapArenaPool::TrimMaps() {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
+ std::lock_guard<std::mutex> lock(lock_);
+ for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
+ arena->Release();
+ }
+}
+
+size_t MemMapArenaPool::GetBytesAllocated() const {
+ size_t total = 0;
+ std::lock_guard<std::mutex> lock(lock_);
+ for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
+ total += arena->GetBytesAllocated();
+ }
+ return total;
+}
+
+void MemMapArenaPool::FreeArenaChain(Arena* first) {
+ if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
+ for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
+ MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
+ }
+ }
+
+ if (arena_allocator::kArenaAllocatorPreciseTracking) {
+ // Do not reuse arenas when tracking.
+ while (first != nullptr) {
+ Arena* next = first->next_;
+ delete first;
+ first = next;
+ }
+ return;
+ }
+
+ if (first != nullptr) {
+ Arena* last = first;
+ while (last->next_ != nullptr) {
+ last = last->next_;
+ }
+ std::lock_guard<std::mutex> lock(lock_);
+ last->next_ = free_arenas_;
+ free_arenas_ = first;
+ }
+}
+
+} // namespace art
diff --git a/runtime/base/mem_map_arena_pool.h b/runtime/base/mem_map_arena_pool.h
new file mode 100644
index 0000000..24e150e
--- /dev/null
+++ b/runtime/base/mem_map_arena_pool.h
@@ -0,0 +1,49 @@
+/*
+ * 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_RUNTIME_BASE_MEM_MAP_ARENA_POOL_H_
+#define ART_RUNTIME_BASE_MEM_MAP_ARENA_POOL_H_
+
+#include "base/arena_allocator.h"
+
+namespace art {
+
+class MemMapArenaPool FINAL : public ArenaPool {
+ public:
+ explicit MemMapArenaPool(bool low_4gb = false, const char* name = "LinearAlloc");
+ virtual ~MemMapArenaPool();
+ Arena* AllocArena(size_t size) OVERRIDE;
+ void FreeArenaChain(Arena* first) OVERRIDE;
+ size_t GetBytesAllocated() const OVERRIDE;
+ void ReclaimMemory() OVERRIDE;
+ void LockReclaimMemory() OVERRIDE;
+ // Trim the maps in arenas by madvising, used by JIT to reduce memory usage.
+ void TrimMaps() OVERRIDE;
+
+ private:
+ const bool low_4gb_;
+ const char* name_;
+ Arena* free_arenas_;
+ // Use a std::mutex here as Arenas are second-from-the-bottom when using MemMaps, and MemMap
+ // itself uses std::mutex scoped to within an allocate/free only.
+ mutable std::mutex lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemMapArenaPool);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_MEM_MAP_ARENA_POOL_H_
diff --git a/runtime/base/mutator_locked_dumpable.h b/runtime/base/mutator_locked_dumpable.h
new file mode 100644
index 0000000..cf2199c
--- /dev/null
+++ b/runtime/base/mutator_locked_dumpable.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_
+#define ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_
+
+#include "base/mutex.h"
+#include "thread-current-inl.h"
+
+namespace art {
+
+template<typename T>
+class MutatorLockedDumpable {
+ public:
+ explicit MutatorLockedDumpable(T& value) REQUIRES_SHARED(Locks::mutator_lock_) : value_(value) {}
+
+ void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_) {
+ value_.Dump(os);
+ }
+
+ private:
+ const T& value_;
+
+ DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable);
+};
+
+// template<typename T>
+// std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs)
+// // TODO: should be REQUIRES_SHARED(Locks::mutator_lock_) however annotalysis
+// // currently fails for this.
+// NO_THREAD_SAFETY_ANALYSIS;
+
+template<typename T>
+inline std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs)
+ NO_THREAD_SAFETY_ANALYSIS {
+ Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+ rhs.Dump(os);
+ return os;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_MUTATOR_LOCKED_DUMPABLE_H_
diff --git a/runtime/base/quasi_atomic.h b/runtime/base/quasi_atomic.h
index 067d01d..0012f64 100644
--- a/runtime/base/quasi_atomic.h
+++ b/runtime/base/quasi_atomic.h
@@ -152,14 +152,6 @@
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 @@
#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 2110d04..5e69efe 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -53,6 +53,7 @@
#include "class_loader_utils.h"
#include "class_table-inl.h"
#include "compiler_callbacks.h"
+#include "debug_print.h"
#include "debugger.h"
#include "dex/descriptors_names.h"
#include "dex/dex_file-inl.h"
@@ -72,6 +73,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 +6180,7 @@
// 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;
}
@@ -7843,83 +7845,6 @@
return resolved;
}
-std::string DescribeSpace(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
- std::ostringstream oss;
- gc::Heap* heap = Runtime::Current()->GetHeap();
- gc::space::ContinuousSpace* cs = heap->FindContinuousSpaceFromAddress(klass.Ptr());
- if (cs != nullptr) {
- if (cs->IsImageSpace()) {
- oss << "image;" << cs->GetName() << ";" << cs->AsImageSpace()->GetImageFilename();
- } else {
- oss << "continuous;" << cs->GetName();
- }
- } else {
- gc::space::DiscontinuousSpace* ds =
- heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true);
- if (ds != nullptr) {
- oss << "discontinuous;" << ds->GetName();
- } else {
- oss << "invalid";
- }
- }
- return oss.str();
-}
-
-std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- std::ostringstream oss;
- uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor);
- ObjPtr<mirror::Class> path_class_loader =
- WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader);
- ObjPtr<mirror::Class> dex_class_loader =
- WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader);
- ObjPtr<mirror::Class> delegate_last_class_loader =
- WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
-
- // Print the class loader chain.
- bool found_class = false;
- const char* loader_separator = "";
- if (loader == nullptr) {
- oss << "BootClassLoader"; // This would be unexpected.
- }
- for (; loader != nullptr; loader = loader->GetParent()) {
- oss << loader_separator << loader->GetClass()->PrettyDescriptor();
- loader_separator = ";";
- // If we didn't find the interface yet, try to find it in the current class loader.
- if (!found_class) {
- ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader);
- ObjPtr<mirror::Class> klass =
- (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr;
- if (klass != nullptr) {
- found_class = true;
- oss << "[hit:" << DescribeSpace(klass) << "]";
- }
- }
-
- // For PathClassLoader, DexClassLoader or DelegateLastClassLoader
- // also dump the dex file locations.
- if (loader->GetClass() == path_class_loader ||
- loader->GetClass() == dex_class_loader ||
- loader->GetClass() == delegate_last_class_loader) {
- oss << "(";
- ScopedObjectAccessUnchecked soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> handle(hs.NewHandle(loader));
- const char* path_separator = "";
- VisitClassLoaderDexFiles(soa,
- handle,
- [&](const DexFile* dex_file) {
- oss << path_separator << dex_file->GetLocation();
- path_separator = ":";
- return true; // Continue with the next DexFile.
- });
- oss << ")";
- }
- }
-
- return oss.str();
-}
-
ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr<mirror::Class> klass,
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader,
@@ -8916,6 +8841,15 @@
}
}
+void ClassLinker::VisitAllocators(AllocatorVisitor* visitor) const {
+ for (const ClassLoaderData& data : class_loaders_) {
+ LinearAlloc* alloc = data.allocator;
+ if (alloc != nullptr && !visitor->Visit(alloc)) {
+ break;
+ }
+ }
+}
+
void ClassLinker::InsertDexFileInToClassLoader(ObjPtr<mirror::Object> dex_file,
ObjPtr<mirror::ClassLoader> class_loader) {
DCHECK(dex_file != nullptr);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 2f6b754..fa70f65 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -97,6 +97,14 @@
REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0;
};
+class AllocatorVisitor {
+ public:
+ virtual ~AllocatorVisitor() {}
+ // Return true to continue visiting.
+ virtual bool Visit(LinearAlloc* alloc)
+ REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0;
+};
+
class ClassLinker {
public:
// Well known mirror::Class roots accessed via GetClassRoot.
@@ -664,6 +672,11 @@
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Visit all of the allocators that belong to classloaders except boot classloader.
+ // This is used by 616-cha-unloading test to confirm memory reuse.
+ void VisitAllocators(AllocatorVisitor* visitor) const
+ REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+
// Throw the class initialization failure recorded when first trying to initialize the given
// class.
void ThrowEarlierClassFailure(ObjPtr<mirror::Class> c, bool wrap_in_no_class_def = false)
diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc
new file mode 100644
index 0000000..44a1836
--- /dev/null
+++ b/runtime/debug_print.cc
@@ -0,0 +1,113 @@
+/*
+ * 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 <sstream>
+
+#include "debug_print.h"
+
+#include "class_linker.h"
+#include "class_table.h"
+#include "class_loader_utils.h"
+#include "dex/utf.h"
+#include "gc/heap.h"
+#include "gc/space/space-inl.h"
+#include "mirror/class.h"
+#include "mirror/class_loader.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
+#include "well_known_classes.h"
+
+namespace art {
+
+std::string DescribeSpace(ObjPtr<mirror::Class> klass) {
+ std::ostringstream oss;
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ gc::space::ContinuousSpace* cs =
+ heap->FindContinuousSpaceFromObject(klass.Ptr(), /* fail_ok */ true);
+ if (cs != nullptr) {
+ if (cs->IsImageSpace()) {
+ oss << "image;" << cs->GetName() << ";" << cs->AsImageSpace()->GetImageFilename();
+ } else {
+ oss << "continuous;" << cs->GetName();
+ }
+ } else {
+ gc::space::DiscontinuousSpace* ds =
+ heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true);
+ if (ds != nullptr) {
+ oss << "discontinuous;" << ds->GetName();
+ } else {
+ oss << "invalid";
+ }
+ }
+ return oss.str();
+}
+
+std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor) {
+ std::ostringstream oss;
+ uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor);
+ ObjPtr<mirror::Class> path_class_loader =
+ WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader);
+ ObjPtr<mirror::Class> dex_class_loader =
+ WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader);
+ ObjPtr<mirror::Class> delegate_last_class_loader =
+ WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+
+ // Print the class loader chain.
+ bool found_class = false;
+ const char* loader_separator = "";
+ if (loader == nullptr) {
+ oss << "BootClassLoader"; // This would be unexpected.
+ }
+ for (; loader != nullptr; loader = loader->GetParent()) {
+ oss << loader_separator << loader->GetClass()->PrettyDescriptor();
+ loader_separator = ";";
+ // If we didn't find the class yet, try to find it in the current class loader.
+ if (!found_class) {
+ ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader);
+ ObjPtr<mirror::Class> klass =
+ (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr;
+ if (klass != nullptr) {
+ found_class = true;
+ oss << "[hit:" << DescribeSpace(klass) << "]";
+ }
+ }
+
+ // For PathClassLoader, DexClassLoader or DelegateLastClassLoader
+ // also dump the dex file locations.
+ if (loader->GetClass() == path_class_loader ||
+ loader->GetClass() == dex_class_loader ||
+ loader->GetClass() == delegate_last_class_loader) {
+ oss << "(";
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> handle(hs.NewHandle(loader));
+ const char* path_separator = "";
+ VisitClassLoaderDexFiles(soa,
+ handle,
+ [&](const DexFile* dex_file) {
+ oss << path_separator << dex_file->GetLocation();
+ path_separator = ":";
+ return true; // Continue with the next DexFile.
+ });
+ oss << ")";
+ }
+ }
+
+ return oss.str();
+}
+
+} // namespace art
diff --git a/runtime/debug_print.h b/runtime/debug_print.h
new file mode 100644
index 0000000..479c36a
--- /dev/null
+++ b/runtime/debug_print.h
@@ -0,0 +1,34 @@
+/*
+ * 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_RUNTIME_DEBUG_PRINT_H_
+#define ART_RUNTIME_DEBUG_PRINT_H_
+
+#include "base/mutex.h"
+#include "mirror/object.h"
+
+// Helper functions for printing extra information for certain hard to diagnose bugs.
+
+namespace art {
+
+std::string DescribeSpace(ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
+} // namespace art
+
+#endif // ART_RUNTIME_DEBUG_PRINT_H_
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 99a4c77..28659cb 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -364,6 +364,11 @@
return !Dbg::MethodHasAnyBreakpoints(m);
}
+bool DebuggerActiveMethodInspectionCallback::MethodNeedsDebugVersion(
+ ArtMethod* m ATTRIBUTE_UNUSED) {
+ return Dbg::IsDebuggerActive();
+}
+
void InternalDebuggerControlCallback::StartDebugger() {
// Release the mutator lock.
ScopedThreadStateChange stsc(art::Thread::Current(), kNative);
@@ -4354,9 +4359,11 @@
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 +4407,11 @@
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/debugger.h b/runtime/debugger.h
index 7401813..e1de991 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -56,6 +56,7 @@
struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback {
bool IsMethodBeingInspected(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
bool IsMethodSafeToJit(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+ bool MethodNeedsDebugVersion(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
};
struct DebuggerDdmCallback : public DdmCallback {
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 2284100..39429c5 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -19,6 +19,7 @@
#include "base/enums.h"
#include "callee_save_frame.h"
#include "common_throws.h"
+#include "debug_print.h"
#include "debugger.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_types.h"
@@ -1187,9 +1188,24 @@
}
}
+static void DumpB74410240ClassData(ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::string storage;
+ const char* descriptor = klass->GetDescriptor(&storage);
+ LOG(FATAL_WITHOUT_ABORT) << " " << DescribeLoaders(klass->GetClassLoader(), descriptor);
+ const OatDexFile* oat_dex_file = klass->GetDexFile().GetOatDexFile();
+ if (oat_dex_file != nullptr) {
+ const OatFile* oat_file = oat_dex_file->GetOatFile();
+ const char* dex2oat_cmdline =
+ oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
+ LOG(FATAL_WITHOUT_ABORT) << " OatFile: " << oat_file->GetLocation()
+ << "; " << (dex2oat_cmdline != nullptr ? dex2oat_cmdline : "<not recorded>");
+ }
+}
+
static void DumpB74410240DebugData(ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) {
// Mimick the search for the caller and dump some data while doing so.
- LOG(FATAL_WITHOUT_ABORT) << "Dumping debugging data for b/74410240.";
+ LOG(FATAL_WITHOUT_ABORT) << "Dumping debugging data, please attach a bugreport to b/74410240.";
constexpr CalleeSaveType type = CalleeSaveType::kSaveRefsAndArgs;
CHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type));
@@ -1227,6 +1243,7 @@
<< " dex pc: " << dex_pc
<< " dex file: " << outer_method->GetDexFile()->GetLocation()
<< " class table: " << class_linker->ClassTableForClassLoader(outer_method->GetClassLoader());
+ DumpB74410240ClassData(outer_method->GetDeclaringClass());
LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(outer_method, dex_pc);
ArtMethod* caller = outer_method;
@@ -1260,7 +1277,8 @@
<< " dex pc: " << dex_pc
<< " dex file: " << caller->GetDexFile()->GetLocation()
<< " class table: "
- << class_linker->ClassTableForClassLoader(outer_method->GetClassLoader());
+ << class_linker->ClassTableForClassLoader(caller->GetClassLoader());
+ DumpB74410240ClassData(caller->GetDeclaringClass());
LOG(FATAL_WITHOUT_ABORT) << " instruction: " << DumpInstruction(caller, dex_pc);
}
}
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 3015b10..671079b 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -37,7 +37,9 @@
// Static fault manger object accessed by signal handler.
FaultManager fault_manager;
-extern "C" __attribute__((visibility("default"))) void art_sigsegv_fault() {
+// This needs to be NO_INLINE since some debuggers do not read the inline-info to set a breakpoint
+// if it isn't.
+extern "C" NO_INLINE __attribute__((visibility("default"))) void art_sigsegv_fault() {
// Set a breakpoint here to be informed when a SIGSEGV is unhandled by ART.
VLOG(signals)<< "Caught unknown SIGSEGV in ART fault handler - chaining to next handler.";
}
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index bb5167f..0747c3c 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -2357,14 +2357,13 @@
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, ®ion_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 @@
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 @@
<< " 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 @@
// 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 @@
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 247e25c..f16138c 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3730,13 +3730,21 @@
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) {
@@ -3751,9 +3759,7 @@
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);
}
}
}
@@ -3762,9 +3768,7 @@
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 9af57d1..ef1c088 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1091,6 +1091,8 @@
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 0000000..f0b36a0
--- /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 0e6f159..cc6c146 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -19,7 +19,7 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
-#include "base/dumpable.h"
+#include "base/mutex.h"
#include "dex/hidden_api_access_flags.h"
#include "mirror/class-inl.h"
#include "reflection.h"
@@ -58,25 +58,6 @@
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) {
@@ -89,16 +70,7 @@
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
@@ -108,6 +80,8 @@
}
}
+// 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.
@@ -118,69 +92,45 @@
std::string tmp_;
public:
- explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_) {
- member_type_ = "field";
- signature_parts_ = {
- field->GetDeclaringClass()->GetDescriptor(&tmp_),
- "->",
- field->GetName(),
- ":",
- field->GetTypeDescriptor()
- };
- }
+ explicit MemberSignature(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_);
+ explicit MemberSignature(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
- explicit MemberSignature(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
- member_type_ = "method";
- signature_parts_ = {
- method->GetDeclaringClass()->GetDescriptor(&tmp_),
- "->",
- method->GetName(),
- method->GetSignature().ToString()
- };
- }
+ void Dump(std::ostream& os) const;
- const std::vector<std::string>& Parts() const {
- return signature_parts_;
- }
-
- void Dump(std::ostream& os) const {
- for (std::string part : signature_parts_) {
- os << part;
- }
- }
// 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 {
- 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 DoesPrefixMatch(const std::string& prefix) const;
- bool IsExempted(const std::vector<std::string>& exemptions) {
- for (const std::string& exemption : exemptions) {
- if (DoesPrefixMatch(exemption)) {
- return true;
- }
- }
- return false;
- }
+ bool IsExempted(const std::vector<std::string>& exemptions);
- void WarnAboutAccess(AccessMethod access_method, HiddenApiAccessFlags::ApiList list) {
- LOG(WARNING) << "Accessing hidden " << member_type_ << " " << Dumpable<MemberSignature>(*this)
- << " (" << list << ", " << access_method << ")";
- }
+ 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_) {
+ 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 the
// platform or not. Because different users of this function determine this
@@ -209,72 +159,13 @@
}
// Member is hidden and caller is not in the platform.
-
- // 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;
-}
-
-// 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/.
-inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loader,
- ObjPtr<mirror::DexCache> caller_dex_cache)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- 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();
- }
+ return detail::ShouldBlockAccessToMemberImpl(member, action, access_method);
}
inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return !caller.IsNull() && IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache());
+ return !caller.IsNull() &&
+ detail::IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache());
}
// Returns true if access to `member` should be denied to a caller loaded with
@@ -286,7 +177,7 @@
ObjPtr<mirror::DexCache> caller_dex_cache,
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
- bool caller_in_platform = IsCallerInPlatformDex(caller_class_loader, caller_dex_cache);
+ bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache);
return ShouldBlockAccessToMember(member,
/* thread */ nullptr,
[caller_in_platform] (Thread*) { return caller_in_platform; },
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index 190d2cb..5a31dd4 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -21,6 +21,8 @@
namespace art {
+using hiddenapi::detail::MemberSignature;
+
class HiddenApiTest : public CommonRuntimeTest {
protected:
void SetUp() OVERRIDE {
@@ -102,172 +104,172 @@
TEST_F(HiddenApiTest, CheckEverythingMatchesL) {
ScopedObjectAccess soa(self_);
std::string prefix("L");
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class3_field1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class3_method1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class3_field1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class3_method1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class3_method1_i_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class2_field1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class2_method1_i_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class12_field1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class12_method1_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
- ASSERT_TRUE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_i_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_method12_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_field12_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ 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(hiddenapi::MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_method1_).DoesPrefixMatch(prefix));
}
TEST_F(HiddenApiTest, CheckConstructorTrailingCharsNoMatch) {
ScopedObjectAccess soa(self_);
std::string prefix("Lmypackage/packagea/Class1;-><init>()Vfoo");
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_init_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_init_).DoesPrefixMatch(prefix));
}
TEST_F(HiddenApiTest, CheckFieldTrailingCharsNoMatch) {
ScopedObjectAccess soa(self_);
std::string prefix("Lmypackage/packagea/Class1;->field1:Ifoo");
- ASSERT_FALSE(hiddenapi::MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
+ ASSERT_FALSE(MemberSignature(class1_field1_).DoesPrefixMatch(prefix));
}
} // namespace art
diff --git a/runtime/image.cc b/runtime/image.cc
index f147078..316f7a5 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', '8', '\0' }; // R^3 Bitstring type check.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '9', '\0' }; // ReachabilityFence.
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 3b9cc0f..6143ba6 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -16,7 +16,7 @@
#include "indirect_reference_table-inl.h"
-#include "base/dumpable-inl.h"
+#include "base/mutator_locked_dumpable.h"
#include "base/systrace.h"
#include "base/utils.h"
#include "java_vm_ext.h"
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 84a148f..d7f33d5 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -139,10 +139,13 @@
bool Instrumentation::NeedDebugVersionFor(ArtMethod* method) const
REQUIRES_SHARED(Locks::mutator_lock_) {
- return Runtime::Current()->IsJavaDebuggable() &&
+ art::Runtime* runtime = Runtime::Current();
+ // If anything says we need the debug version or we are debuggable we will need the debug version
+ // of the method.
+ return (runtime->GetRuntimeCallbacks()->MethodNeedsDebugVersion(method) ||
+ runtime->IsJavaDebuggable()) &&
!method->IsNative() &&
- !method->IsProxyMethod() &&
- Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method);
+ !method->IsProxyMethod();
}
void Instrumentation::InstallStubsForMethod(ArtMethod* method) {
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 681a582..69dae31 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -91,7 +91,7 @@
// 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
@@ -399,6 +399,16 @@
VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetPlain)
VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetRelease)
+static ALWAYS_INLINE bool MterpReachabilityFence(ShadowFrame* shadow_frame ATTRIBUTE_UNUSED,
+ const Instruction* inst ATTRIBUTE_UNUSED,
+ uint16_t inst_data ATTRIBUTE_UNUSED,
+ JValue* result_register ATTRIBUTE_UNUSED)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Do nothing; Its only purpose is to keep the argument reference live
+ // at preceding suspend points. That's automatic in the interpreter.
+ return true;
+}
+
// Macro to help keep track of what's left to implement.
#define UNIMPLEMENTED_CASE(name) \
case Intrinsics::k##name: \
@@ -499,6 +509,7 @@
UNIMPLEMENTED_CASE(MemoryPokeIntNative /* (JI)V */)
UNIMPLEMENTED_CASE(MemoryPokeLongNative /* (JJ)V */)
UNIMPLEMENTED_CASE(MemoryPokeShortNative /* (JS)V */)
+ INTRINSIC_CASE(ReachabilityFence /* (Ljava/lang/Object;)V */)
INTRINSIC_CASE(StringCharAt)
INTRINSIC_CASE(StringCompareTo)
INTRINSIC_CASE(StringEquals)
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 4a2dd3b..4c7a97d 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1538,7 +1538,7 @@
}
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/intrinsics_list.h b/runtime/intrinsics_list.h
index da08793..2f91f5d 100644
--- a/runtime/intrinsics_list.h
+++ b/runtime/intrinsics_list.h
@@ -218,6 +218,7 @@
V(VarHandleReleaseFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "releaseFence", "()V") \
V(VarHandleLoadLoadFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "loadLoadFence", "()V") \
V(VarHandleStoreStoreFence, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "storeStoreFence", "()V") \
+ V(ReachabilityFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/ref/Reference;", "reachabilityFence", "(Ljava/lang/Object;)V") \
SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(V)
#endif // ART_RUNTIME_INTRINSICS_LIST_H_
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 5618b6e..de64fdd 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1539,7 +1539,7 @@
// 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);
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 1bbce4f..6bbe78f 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -34,9 +34,8 @@
#include "base/arena_allocator.h"
#include "base/dumpable.h"
-#include "base/file_utils.h"
#include "base/logging.h" // For VLOG.
-#include "base/mutex.h"
+#include "base/malloc_arena_pool.h"
#include "base/os.h"
#include "base/safe_map.h"
#include "base/scoped_flock.h"
@@ -90,7 +89,7 @@
}
ProfileCompilationInfo::ProfileCompilationInfo()
- : default_arena_pool_(/*use_malloc*/true, /*low_4gb*/false, "ProfileCompilationInfo"),
+ : default_arena_pool_(),
allocator_(&default_arena_pool_),
info_(allocator_.Adapter(kArenaAllocProfile)),
profile_key_map_(std::less<const std::string>(), allocator_.Adapter(kArenaAllocProfile)) {
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 6c56db9..f8334ce 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -23,8 +23,9 @@
#include "base/arena_containers.h"
#include "base/arena_object.h"
#include "base/atomic.h"
+#include "base/bit_memory_region.h"
+#include "base/malloc_arena_pool.h"
#include "base/safe_map.h"
-#include "bit_memory_region.h"
#include "dex/dex_cache_resolved_classes.h"
#include "dex/dex_file.h"
#include "dex/dex_file_types.h"
@@ -792,7 +793,7 @@
friend class ProfileAssistantTest;
friend class Dex2oatLayoutTest;
- ArenaPool default_arena_pool_;
+ MallocArenaPool default_arena_pool_;
ArenaAllocator allocator_;
// Vector containing the actual profile info.
diff --git a/runtime/linear_alloc.h b/runtime/linear_alloc.h
index 384b2e3..87086f2 100644
--- a/runtime/linear_alloc.h
+++ b/runtime/linear_alloc.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_LINEAR_ALLOC_H_
#include "base/arena_allocator.h"
+#include "base/mutex.h"
namespace art {
diff --git a/runtime/method_info.h b/runtime/method_info.h
index fe06256..b00ddc6 100644
--- a/runtime/method_info.h
+++ b/runtime/method_info.h
@@ -21,7 +21,7 @@
#include "base/leb128.h"
#include "base/macros.h"
-#include "memory_region.h"
+#include "base/memory_region.h"
namespace art {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index f0898f4..72b3179 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 e110763..f246d8b 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1101,7 +1101,7 @@
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_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index d9a5096..cf0a72a 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/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 25f984f..fb00ae3 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -116,7 +116,7 @@
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 @@
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 @@
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/runtime.cc b/runtime/runtime.cc
index 9a626ba..4068158 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -62,6 +62,8 @@
#include "base/dumpable.h"
#include "base/enums.h"
#include "base/file_utils.h"
+#include "base/malloc_arena_pool.h"
+#include "base/mem_map_arena_pool.h"
#include "base/memory_tool.h"
#include "base/mutex.h"
#include "base/os.h"
@@ -86,6 +88,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"
@@ -1331,13 +1334,17 @@
// Use MemMap arena pool for jit, malloc otherwise. Malloc arenas are faster to allocate but
// can't be trimmed as easily.
const bool use_malloc = IsAotCompiler();
- arena_pool_.reset(new ArenaPool(use_malloc, /* low_4gb */ false));
- jit_arena_pool_.reset(
- new ArenaPool(/* use_malloc */ false, /* low_4gb */ false, "CompilerMetadata"));
+ if (use_malloc) {
+ arena_pool_.reset(new MallocArenaPool());
+ jit_arena_pool_.reset(new MallocArenaPool());
+ } else {
+ arena_pool_.reset(new MemMapArenaPool(/* low_4gb */ false));
+ jit_arena_pool_.reset(new MemMapArenaPool(/* low_4gb */ false, "CompilerMetadata"));
+ }
if (IsAotCompiler() && Is64BitInstructionSet(kRuntimeISA)) {
// 4gb, no malloc. Explanation in header.
- low_4gb_arena_pool_.reset(new ArenaPool(/* use_malloc */ false, /* low_4gb */ true));
+ low_4gb_arena_pool_.reset(new MemMapArenaPool(/* low_4gb */ true));
}
linear_alloc_.reset(CreateLinearAlloc());
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index cd3c0b7..758917c 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -106,6 +106,15 @@
return false;
}
+bool RuntimeCallbacks::MethodNeedsDebugVersion(ArtMethod* m) {
+ for (MethodInspectionCallback* cb : method_inspection_callbacks_) {
+ if (cb->MethodNeedsDebugVersion(m)) {
+ return true;
+ }
+ }
+ return false;
+}
+
void RuntimeCallbacks::AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
thread_callbacks_.push_back(cb);
}
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index 24386ba..9f0410d 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -130,6 +130,10 @@
// Note that '!IsMethodSafeToJit(m) implies IsMethodBeingInspected(m)'. That is that if this
// method returns false IsMethodBeingInspected must return true.
virtual bool IsMethodSafeToJit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+ // Returns true if we expect the method to be debuggable but are not doing anything unusual with
+ // it currently.
+ virtual bool MethodNeedsDebugVersion(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
};
class RuntimeCallbacks {
@@ -198,6 +202,11 @@
// entrypoint should not be changed to JITed code.
bool IsMethodSafeToJit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns true if some MethodInspectionCallback indicates the method needs to use a debug
+ // version. This allows later code to set breakpoints or perform other actions that could be
+ // broken by some optimizations.
+ bool MethodNeedsDebugVersion(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
void AddMethodInspectionCallback(MethodInspectionCallback* cb)
REQUIRES_SHARED(Locks::mutator_lock_);
void RemoveMethodInspectionCallback(MethodInspectionCallback* cb)
diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc
index 59af918..eae2505 100644
--- a/runtime/runtime_common.cc
+++ b/runtime/runtime_common.cc
@@ -371,30 +371,11 @@
#pragma GCC diagnostic ignored "-Wframe-larger-than="
#endif
-void HandleUnexpectedSignalCommon(int signal_number,
- siginfo_t* info,
- void* raw_context,
- bool handle_timeout_signal,
- bool dump_on_stderr) {
- static bool handling_unexpected_signal = false;
- if (handling_unexpected_signal) {
- LogHelper::LogLineLowStack(__FILE__,
- __LINE__,
- ::android::base::FATAL_WITHOUT_ABORT,
- "HandleUnexpectedSignal reentered\n");
- if (handle_timeout_signal) {
- if (IsTimeoutSignal(signal_number)) {
- // Ignore a recursive timeout.
- return;
- }
- }
- _exit(1);
- }
- handling_unexpected_signal = true;
-
- gAborting++; // set before taking any locks
- MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_);
-
+static void HandleUnexpectedSignalCommonDump(int signal_number,
+ siginfo_t* info,
+ void* raw_context,
+ bool handle_timeout_signal,
+ bool dump_on_stderr) {
auto logger = [&](auto& stream) {
bool has_address = (signal_number == SIGILL || signal_number == SIGBUS ||
signal_number == SIGFPE || signal_number == SIGSEGV);
@@ -453,6 +434,71 @@
}
}
+void HandleUnexpectedSignalCommon(int signal_number,
+ siginfo_t* info,
+ void* raw_context,
+ bool handle_timeout_signal,
+ bool dump_on_stderr) {
+ // Local _static_ storing the currently handled signal (or -1).
+ static int handling_unexpected_signal = -1;
+
+ // Whether the dump code should be run under the unexpected-signal lock. For diagnostics we
+ // allow recursive unexpected-signals in certain cases - avoid a deadlock.
+ bool grab_lock = true;
+
+ if (handling_unexpected_signal != -1) {
+ LogHelper::LogLineLowStack(__FILE__,
+ __LINE__,
+ ::android::base::FATAL_WITHOUT_ABORT,
+ "HandleUnexpectedSignal reentered\n");
+ // Print the signal number. Don't use any standard functions, just some arithmetic. Just best
+ // effort, with a minimal buffer.
+ if (0 < signal_number && signal_number < 100) {
+ char buf[] = { ' ',
+ 'S',
+ static_cast<char>('0' + (signal_number / 10)),
+ static_cast<char>('0' + (signal_number % 10)),
+ '\n',
+ 0 };
+ LogHelper::LogLineLowStack(__FILE__,
+ __LINE__,
+ ::android::base::FATAL_WITHOUT_ABORT,
+ buf);
+ }
+ if (handle_timeout_signal) {
+ if (IsTimeoutSignal(signal_number)) {
+ // Ignore a recursive timeout.
+ return;
+ }
+ }
+ // If we were handling a timeout signal, try to go on. Otherwise hard-exit.
+ // This relies on the expectation that we'll only ever get one timeout signal.
+ if (!handle_timeout_signal || handling_unexpected_signal != GetTimeoutSignal()) {
+ _exit(1);
+ }
+ grab_lock = false; // The "outer" handling instance already holds the lock.
+ }
+ handling_unexpected_signal = signal_number;
+
+ gAborting++; // set before taking any locks
+
+ if (grab_lock) {
+ MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_);
+
+ HandleUnexpectedSignalCommonDump(signal_number,
+ info,
+ raw_context,
+ handle_timeout_signal,
+ dump_on_stderr);
+ } else {
+ HandleUnexpectedSignalCommonDump(signal_number,
+ info,
+ raw_context,
+ handle_timeout_signal,
+ dump_on_stderr);
+ }
+}
+
#if defined(__APPLE__)
#pragma GCC diagnostic pop
#endif
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index bde3462..3839764 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -20,12 +20,12 @@
#include <limits>
#include "arch/code_offset.h"
+#include "base/bit_memory_region.h"
#include "base/bit_utils.h"
#include "base/bit_vector.h"
#include "base/leb128.h"
-#include "bit_memory_region.h"
+#include "base/memory_region.h"
#include "dex/dex_file_types.h"
-#include "memory_region.h"
#include "method_info.h"
namespace art {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index b13d8ec..d17f409 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2881,6 +2881,17 @@
Handle<mirror::Class> h_aste_class(hs.NewHandle<mirror::Class>(
h_aste_array_class->GetComponentType()));
+
+ // Make sure the AnnotatedStackTraceElement.class is initialized, b/76208924 .
+ class_linker->EnsureInitialized(soa.Self(),
+ h_aste_class,
+ /* can_init_fields */ true,
+ /* can_init_parents */ true);
+ if (soa.Self()->IsExceptionPending()) {
+ // This should not fail in a healthy runtime.
+ return nullptr;
+ }
+
ArtField* stack_trace_element_field = h_aste_class->FindField(
soa.Self(), h_aste_class.Get(), "stackTraceElement", "Ljava/lang/StackTraceElement;");
DCHECK(stack_trace_element_field != nullptr);
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index ec4dc41..838d7f1 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -317,6 +317,7 @@
if (quickening_info.empty()) {
return ArrayRef<const uint8_t>();
}
+ CHECK_LT(dex_method_idx, dex_file.NumMethodIds());
const uint32_t quickening_offset =
GetQuickenInfoOffsetTable(dex_file, quickening_info).GetOffset(dex_method_idx);
if (quickening_offset == 0u) {
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index 18ad6b5..168eb7b 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -22,6 +22,7 @@
#include <android-base/logging.h>
+#include "base/mutex.h"
#include "base/safe_map.h"
#include "base/scoped_arena_containers.h"
diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java
index 51d4a81..be7ae4a 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -80,6 +81,7 @@
// the test fail (even when keeping the `null` assignment). b/76454261
FinalizerTest keepLive = wimp.get();
System.out.println("wimp: " + wimpString(wimp));
+ Reference.reachabilityFence(keepLive);
keepLive = null; // Clear the reference.
/* this will try to collect and finalize ft */
diff --git a/test/072-reachability-fence/expected.txt b/test/072-reachability-fence/expected.txt
new file mode 100644
index 0000000..fdd0d7b
--- /dev/null
+++ b/test/072-reachability-fence/expected.txt
@@ -0,0 +1,5 @@
+Starting
+Reference 0 was live.
+Reference 3 was live.
+Reference 4 was live.
+Finished
diff --git a/test/072-reachability-fence/info.txt b/test/072-reachability-fence/info.txt
new file mode 100644
index 0000000..21b6d6a
--- /dev/null
+++ b/test/072-reachability-fence/info.txt
@@ -0,0 +1,4 @@
+Check that reachabilityFence() prevents garbage collection of objects only referred to by a dead
+reference.
+
+This is not very convincing, since we currently usually keep such objects around anyway.
diff --git a/test/072-reachability-fence/src/Main.java b/test/072-reachability-fence/src/Main.java
new file mode 100644
index 0000000..ac1e131
--- /dev/null
+++ b/test/072-reachability-fence/src/Main.java
@@ -0,0 +1,61 @@
+/*
+ * 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 java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Starting");
+ WeakReference wrefs[] = new WeakReference[5];
+ String str0 = generateString("String", 0);
+ String str1 = generateString("String", 1);
+ String str2 = generateString("String", 2);
+ String str3 = generateString("String", 3);
+ String str4 = generateString("String", 4);
+ wrefs[0] = new WeakReference(str0);
+ wrefs[1] = new WeakReference(str1);
+ wrefs[2] = new WeakReference(str2);
+ wrefs[3] = new WeakReference(str3);
+ wrefs[4] = new WeakReference(str4);
+ // Clear a couple as a sanity check.
+ str1 = null;
+ str2 = null;
+ // str<n> dead here; in the future we will possibly reuse the registers.
+ // Give the compiler something to fill the registers with.
+ String str5 = generateString("String", 5);
+ String str6 = generateString("String", 6);
+ String str7 = generateString("String", 7);
+ String str8 = generateString("String", 8);
+ String str9 = generateString("String", 9);
+ Runtime.getRuntime().gc();
+ for (int i = 0; i < 5; ++i) {
+ if (wrefs[i].get() != null) {
+ System.out.println("Reference " + i + " was live.");
+ }
+ }
+ Reference.reachabilityFence(str0);
+ Reference.reachabilityFence(str1);
+ Reference.reachabilityFence(str2);
+ Reference.reachabilityFence(str3);
+ Reference.reachabilityFence(str4);
+ System.out.println("Finished");
+ }
+
+ private static String generateString(String base, int num) {
+ return base + num;
+ }
+}
diff --git a/test/1940-ddms-ext/check b/test/166-bad-interface-super/build
old mode 100755
new mode 100644
similarity index 64%
rename from test/1940-ddms-ext/check
rename to test/166-bad-interface-super/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/166-bad-interface-super/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/171-init-aste/expected.txt b/test/171-init-aste/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/171-init-aste/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/171-init-aste/info.txt b/test/171-init-aste/info.txt
new file mode 100644
index 0000000..201e8ad
--- /dev/null
+++ b/test/171-init-aste/info.txt
@@ -0,0 +1 @@
+Regression test for failure to initialize dalvik.system.AnnotatedStackTraceElement.
diff --git a/test/171-init-aste/src-art/Main.java b/test/171-init-aste/src-art/Main.java
new file mode 100644
index 0000000..9d36610
--- /dev/null
+++ b/test/171-init-aste/src-art/Main.java
@@ -0,0 +1,37 @@
+/*
+ * 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 java.lang.reflect.Method;
+import dalvik.system.AnnotatedStackTraceElement;
+
+public class Main {
+ public static void main(String args[]) throws Exception {
+ Class<?> vmStack = Class.forName("dalvik.system.VMStack");
+ Method getAnnotatedThreadStackTrace =
+ vmStack.getDeclaredMethod("getAnnotatedThreadStackTrace", Thread.class);
+ Object[] annotatedStackTrace =
+ (Object[]) getAnnotatedThreadStackTrace.invoke(null, Thread.currentThread());
+ AnnotatedStackTraceElement annotatedElement =
+ (AnnotatedStackTraceElement) annotatedStackTrace[0];
+ // This used to fail an assertion that the AnnotatedStackTraceElement.class
+ // is at least initializing (i.e. initializing, initialized or resolved-erroneous).
+ // Note: We cannot use reflection for this test because getDeclaredMethod() would
+ // initialize the class and hide the failure.
+ annotatedElement.getStackTraceElement();
+
+ System.out.println("passed");
+ }
+}
diff --git a/test/171-init-aste/src/Main.java b/test/171-init-aste/src/Main.java
new file mode 100644
index 0000000..4479cb4
--- /dev/null
+++ b/test/171-init-aste/src/Main.java
@@ -0,0 +1,24 @@
+/*
+ * 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 {
+ // Note: This file is used for the RI which does not support
+ // dalvik.system.AnnotatedStackTraceElement (see src-art/Main.java),
+ // so that we do not need an exclusion in known failures.
+ public static void main(String args[]) throws Exception {
+ System.out.println("passed");
+ }
+}
diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt
index cdb8f6a..a685891 100644
--- a/test/1935-get-set-current-frame-jit/expected.txt
+++ b/test/1935-get-set-current-frame-jit/expected.txt
@@ -1,7 +1,5 @@
JNI_OnLoad called
From GetLocalInt(), value is 42
-isInOsrCode? false
Value is '42'
Setting TARGET to 1337
-isInOsrCode? false
Value is '1337'
diff --git a/test/1935-get-set-current-frame-jit/run b/test/1935-get-set-current-frame-jit/run
index 51875a7..e569d08 100755
--- a/test/1935-get-set-current-frame-jit/run
+++ b/test/1935-get-set-current-frame-jit/run
@@ -14,5 +14,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Ask for stack traces to be dumped to a file rather than to stdout.
-./default-run "$@" --jvmti
+# Ensure the test is not subject to code collection
+./default-run "$@" --jvmti --runtime-option -Xjitinitialsize:32M
diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java
index 714a98a..378aaf7 100644
--- a/test/1935-get-set-current-frame-jit/src/Main.java
+++ b/test/1935-get-set-current-frame-jit/src/Main.java
@@ -21,6 +21,7 @@
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
+import java.time.Instant;
import java.util.concurrent.Semaphore;
import java.util.Arrays;
import java.util.Collection;
@@ -49,9 +50,11 @@
public static class IntRunner implements Runnable {
private volatile boolean continueBusyLoop;
private volatile boolean inBusyLoop;
- public IntRunner() {
+ private final boolean expectOsr;
+ public IntRunner(boolean expectOsr) {
this.continueBusyLoop = true;
this.inBusyLoop = false;
+ this.expectOsr = expectOsr;
}
public void run() {
int TARGET = 42;
@@ -59,14 +62,23 @@
while (continueBusyLoop) {
inBusyLoop = true;
}
- int i = 0;
- while (Main.isInterpreted() && i < 10000) {
- Main.ensureJitCompiled(IntRunner.class, "run");
- i++;
- }
- // We shouldn't be doing OSR since we are using JVMTI and the get/set prevents OSR.
+ // Wait up to 300 seconds for OSR to kick in if we expect it. If we don't give up after only
+ // 3 seconds.
+ Instant osrDeadline = Instant.now().plusSeconds(expectOsr ? 600 : 3);
+ do {
+ // Don't actually do anything here.
+ inBusyLoop = true;
+ } while (hasJit() && !Main.isInOsrCode("run") && osrDeadline.compareTo(Instant.now()) > 0);
+ // We shouldn't be doing OSR since we are using JVMTI and the set prevents OSR.
// Set local will also push us to interpreter but the get local may remain in compiled code.
- System.out.println("isInOsrCode? " + (hasJit() && Main.isInOsrCode("run")));
+ if (hasJit()) {
+ boolean inOsr = Main.isInOsrCode("run");
+ if (expectOsr && !inOsr) {
+ throw new Error("Expected to be in OSR but was not.");
+ } else if (!expectOsr && inOsr) {
+ throw new Error("Expected not to be in OSR but was.");
+ }
+ }
reportValue(TARGET);
}
public void waitForBusyLoopStart() { while (!inBusyLoop) {} }
@@ -78,7 +90,7 @@
public static void runGet() throws Exception {
Method target = IntRunner.class.getDeclaredMethod("run");
// Get Int
- IntRunner int_runner = new IntRunner();
+ IntRunner int_runner = new IntRunner(true);
Thread target_get = new Thread(int_runner, "GetLocalInt - Target");
target_get.start();
int_runner.waitForBusyLoopStart();
@@ -108,7 +120,7 @@
public static void runSet() throws Exception {
Method target = IntRunner.class.getDeclaredMethod("run");
// Set Int
- IntRunner int_runner = new IntRunner();
+ IntRunner int_runner = new IntRunner(false);
Thread target_set = new Thread(int_runner, "SetLocalInt - Target");
target_set.start();
int_runner.waitForBusyLoopStart();
@@ -157,7 +169,6 @@
throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
}
- public static native void ensureJitCompiled(Class k, String f);
public static native boolean isInterpreted();
public static native boolean isInOsrCode(String methodName);
public static native boolean hasJit();
diff --git a/test/1940-ddms-ext/expected_error.txt b/test/1940-ddms-ext/expected_error.txt
deleted file mode 100644
index 73883b4..0000000
--- 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 638c479..0000000
--- 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 1015551..0000000
--- 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
similarity index 100%
rename from test/411-optimizing-arith-mul/expected.txt
rename to 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 0000000..42be5d5
--- /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
similarity index 98%
rename from test/417-optimizing-arith-div/src/Main.java
rename to test/411-optimizing-arith/src/DivTest.java
index 68e89b3..7696d0a 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 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 0000000..e1a43d3
--- /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
similarity index 93%
rename from test/411-optimizing-arith-mul/src/Main.java
rename to test/411-optimizing-arith/src/MulTest.java
index 60e418e..b9bffca 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 static void main(String[] args) {
- mul();
- }
-
- public static void mul() {
+ public static void main() {
mulInt();
mulLong();
mulFloat();
@@ -129,9 +125,12 @@
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
similarity index 92%
rename from test/415-optimizing-arith-neg/src/Main.java
rename to test/411-optimizing-arith/src/NegTest.java
index c53b639..8304726 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 static void main(String[] args) {
+ public static void main() {
negInt();
$opt$noinline$InplaceNegOneInt(1);
@@ -169,55 +169,29 @@
}
- 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
similarity index 98%
rename from test/428-optimizing-arith-rem/src/Main.java
rename to test/411-optimizing-arith/src/RemTest.java
index 3f77318..1b31f63 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
similarity index 98%
rename from test/431-optimizing-arith-shifts/src/Main.java
rename to test/411-optimizing-arith/src/ShiftsTest.java
index b7a112f..139ff70 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 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
similarity index 98%
rename from test/414-optimizing-arith-sub/src/Main.java
rename to test/411-optimizing-arith/src/SubTest.java
index b4531cd..9c9ea92 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 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 e69de29..0000000
--- 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 1eaa148..0000000
--- 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 e69de29..0000000
--- 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 8494aad..0000000
--- 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 e69de29..0000000
--- 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 1374b0f..0000000
--- 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 e69de29..0000000
--- 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 3e37ffe..0000000
--- 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 e69de29..0000000
--- 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 14ff264..0000000
--- a/test/431-optimizing-arith-shifts/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Tests for shift operations.
diff --git a/test/1940-ddms-ext/check b/test/551-checker-shifter-operand/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/551-checker-shifter-operand/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/551-checker-shifter-operand/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/616-cha-unloading/cha_unload.cc b/test/616-cha-unloading/cha_unload.cc
index 4be3456..b17be6b 100644
--- a/test/616-cha-unloading/cha_unload.cc
+++ b/test/616-cha-unloading/cha_unload.cc
@@ -19,6 +19,7 @@
#include <iostream>
#include "art_method.h"
+#include "class_linker.h"
#include "jit/jit.h"
#include "linear_alloc.h"
#include "nativehelper/ScopedUtfChars.h"
@@ -29,6 +30,22 @@
namespace art {
namespace {
+class FindPointerAllocatorVisitor : public AllocatorVisitor {
+ public:
+ explicit FindPointerAllocatorVisitor(void* ptr) : is_found(false), ptr_(ptr) {}
+
+ bool Visit(LinearAlloc* alloc)
+ REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
+ is_found = alloc->Contains(ptr_);
+ return !is_found;
+ }
+
+ bool is_found;
+
+ private:
+ void* ptr_;
+};
+
extern "C" JNIEXPORT jlong JNICALL Java_Main_getArtMethod(JNIEnv* env,
jclass,
jobject java_method) {
@@ -40,13 +57,30 @@
extern "C" JNIEXPORT void JNICALL Java_Main_reuseArenaOfMethod(JNIEnv*,
jclass,
jlong art_method) {
- // Create a new allocation and use it to request a specified amount of arenas.
- // Hopefully one of them is a reused one, the one that covers the art_method pointer.
+ void* ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(art_method));
+
+ ReaderMutexLock mu(Thread::Current(), *Locks::mutator_lock_);
+ ReaderMutexLock mu2(Thread::Current(), *Locks::classlinker_classes_lock_);
+ // Check if the arena was already implicitly reused by boot classloader.
+ if (Runtime::Current()->GetLinearAlloc()->Contains(ptr)) {
+ return;
+ }
+
+ // Check if the arena was already implicitly reused by some other classloader.
+ FindPointerAllocatorVisitor visitor(ptr);
+ Runtime::Current()->GetClassLinker()->VisitAllocators(&visitor);
+ if (visitor.is_found) {
+ return;
+ }
+
+ // The arena was not reused yet. Do it explicitly.
+ // Create a new allocation and use it to request new arenas until one of them is
+ // a reused one that covers the art_method pointer.
std::unique_ptr<LinearAlloc> alloc(Runtime::Current()->CreateLinearAlloc());
do {
- // Ask for a byte - it's sufficient to get an arena and not have issues with size.
+ // Ask for a byte - it's sufficient to get an arena.
alloc->Alloc(Thread::Current(), 1);
- } while (!alloc->Contains(reinterpret_cast<void*>(static_cast<uintptr_t>(art_method))));
+ } while (!alloc->Contains(ptr));
}
} // namespace
diff --git a/test/1940-ddms-ext/check b/test/626-checker-arm64-scratch-register/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/626-checker-arm64-scratch-register/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/626-checker-arm64-scratch-register/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/645-checker-abs-simd/src/Main.java b/test/645-checker-abs-simd/src/Main.java
index 870a403..819304a 100644
--- a/test/645-checker-abs-simd/src/Main.java
+++ b/test/645-checker-abs-simd/src/Main.java
@@ -48,10 +48,7 @@
}
/// CHECK-START: void Main.doitChar(char[]) loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Abs loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ /// CHECK-NOT: Abs
//
/// CHECK-START: void Main.doitChar(char[]) loop_optimization (after)
/// CHECK-NOT: VecAbs
diff --git a/test/1940-ddms-ext/check b/test/646-checker-hadd-alt-char/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/646-checker-hadd-alt-char/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/646-checker-hadd-alt-char/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/646-checker-hadd-alt-short/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/646-checker-hadd-alt-short/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/646-checker-hadd-alt-short/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/646-checker-hadd-char/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/646-checker-hadd-char/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/646-checker-hadd-char/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/646-checker-hadd-short/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/646-checker-hadd-short/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/646-checker-hadd-short/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/646-checker-hadd-short/src/Main.java b/test/646-checker-hadd-short/src/Main.java
index 85c2fca..c09da81 100644
--- a/test/646-checker-hadd-short/src/Main.java
+++ b/test/646-checker-hadd-short/src/Main.java
@@ -26,6 +26,10 @@
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 @@
}
}
+ /// 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 @@
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/1940-ddms-ext/check b/test/651-checker-simd-minmax/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/651-checker-simd-minmax/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/651-checker-simd-minmax/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
index b995494..8dacd5d 100644
--- a/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
+++ b/test/651-checker-simd-minmax/src/ByteSimdMinMax.java
@@ -129,7 +129,7 @@
/// 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 ByteSimdMinMax.doitMin100(byte[], byte[]) loop_optimization (after)
+ /// CHECK-START-{ARM,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,6 +142,38 @@
}
}
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinMax(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<I11:i\d+>> IntConstant -11 loop:none
+ /// CHECK-DAG: <<I23:i\d+>> IntConstant 23 loop:none
+ /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>] loop:none
+ /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>] loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get>>,<<Rpl1>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Min>>,<<Rpl2>>] packed_type:Int8 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>> outer_loop:none
+ private static void doitMinMax(byte[] x, byte[] y) {
+ int n = Math.min(x.length, y.length);
+ for (int i = 0; i < n; i++) {
+ x[i] = (byte) Math.max(-11, Math.min(y[i], 23));
+ }
+ }
+
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void ByteSimdMinMax.doitMinMaxUnsigned(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<I11:i\d+>> IntConstant 11 loop:none
+ /// CHECK-DAG: <<I23:i\d+>> IntConstant 23 loop:none
+ /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>] loop:none
+ /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>] loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get>>,<<Rpl1>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Min>>,<<Rpl2>>] packed_type:Uint8 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>> outer_loop:none
+ private static void doitMinMaxUnsigned(byte[] x, byte[] y) {
+ int n = Math.min(x.length, y.length);
+ for (int i = 0; i < n; i++) {
+ x[i] = (byte) Math.max(11, Math.min(y[i] & 0xff, 23));
+ }
+ }
+
public static void main() {
// Initialize cross-values for all possible values.
int total = 256 * 256;
@@ -184,6 +216,18 @@
byte expected = (byte) Math.min(y[i], 100);
expectEquals(expected, x[i]);
}
+ doitMinMax(x, y);
+ for (int i = 0; i < total; i++) {
+ int s = y[i];
+ byte expected = (byte) (s < -11 ? -11 : (s > 23 ? 23 : s));
+ expectEquals(expected, x[i]);
+ }
+ doitMinMaxUnsigned(x, y);
+ for (int i = 0; i < total; i++) {
+ int u = y[i] & 0xff;
+ byte expected = (byte) (u < 11 ? 11 : (u > 23 ? 23 : u));
+ expectEquals(expected, x[i]);
+ }
System.out.println("ByteSimdMinMax passed");
}
diff --git a/test/651-checker-simd-minmax/src/ShortSimdMinMax.java b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
index aae7891..9075d80 100644
--- a/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
+++ b/test/651-checker-simd-minmax/src/ShortSimdMinMax.java
@@ -129,7 +129,7 @@
/// 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 ShortSimdMinMax.doitMin100(short[], short[]) loop_optimization (after)
+ /// CHECK-START-{ARM,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,6 +142,38 @@
}
}
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinMax(short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<I11:i\d+>> IntConstant -1111 loop:none
+ /// CHECK-DAG: <<I23:i\d+>> IntConstant 2323 loop:none
+ /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>] loop:none
+ /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>] loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get>>,<<Rpl1>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Min>>,<<Rpl2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>> outer_loop:none
+ private static void doitMinMax(short[] x, short[] y) {
+ int n = Math.min(x.length, y.length);
+ for (int i = 0; i < n; i++) {
+ x[i] = (short) Math.max(-1111, Math.min(y[i], 2323));
+ }
+ }
+
+ /// CHECK-START-{ARM,ARM64,MIPS64}: void ShortSimdMinMax.doitMinMaxUnsigned(short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<I11:i\d+>> IntConstant 1111 loop:none
+ /// CHECK-DAG: <<I23:i\d+>> IntConstant 2323 loop:none
+ /// CHECK-DAG: <<Rpl1:d\d+>> VecReplicateScalar [<<I23>>] loop:none
+ /// CHECK-DAG: <<Rpl2:d\d+>> VecReplicateScalar [<<I11>>] loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get>>,<<Rpl1>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Min>>,<<Rpl2>>] packed_type:Uint16 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<<Max>>] loop:<<Loop>> outer_loop:none
+ private static void doitMinMaxUnsigned(short[] x, short[] y) {
+ int n = Math.min(x.length, y.length);
+ for (int i = 0; i < n; i++) {
+ x[i] = (short) Math.max(1111, Math.min(y[i] & 0xffff, 2323));
+ }
+ }
+
public static void main() {
short[] interesting = {
(short) 0x0000, (short) 0x0001, (short) 0x007f,
@@ -198,6 +230,18 @@
short expected = (short) Math.min(y[i], 100);
expectEquals(expected, x[i]);
}
+ doitMinMax(x, y);
+ for (int i = 0; i < total; i++) {
+ int s = y[i];
+ short expected = (short) (s < -1111 ? -1111 : (s > 2323 ? 2323 : s));
+ expectEquals(expected, x[i]);
+ }
+ doitMinMaxUnsigned(x, y);
+ for (int i = 0; i < total; i++) {
+ int u = y[i] & 0xffff;
+ short expected = (short) (u < 1111 ? 1111 : (u > 2323 ? 2323 : u));
+ expectEquals(expected, x[i]);
+ }
System.out.println("ShortSimdMinMax passed");
}
diff --git a/test/1940-ddms-ext/check b/test/660-checker-simd-sad-byte/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/660-checker-simd-sad-byte/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/660-checker-simd-sad-byte/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/660-checker-simd-sad-char/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/660-checker-simd-sad-char/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/660-checker-simd-sad-char/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/660-checker-simd-sad-int/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/660-checker-simd-sad-int/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/660-checker-simd-sad-int/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/660-checker-simd-sad-short/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/660-checker-simd-sad-short/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/660-checker-simd-sad-short/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/660-checker-simd-sad-short/src/Main.java b/test/660-checker-simd-sad-short/src/Main.java
index 8a44d9e..77c9e53 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 @@
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 @@
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 @@
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/1940-ddms-ext/check b/test/660-checker-simd-sad-short2/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/660-checker-simd-sad-short2/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/660-checker-simd-sad-short2/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/660-checker-simd-sad-short3/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/660-checker-simd-sad-short3/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/660-checker-simd-sad-short3/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/661-checker-simd-reduc/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/661-checker-simd-reduc/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/661-checker-simd-reduc/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/672-checker-throw-method/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/672-checker-throw-method/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/672-checker-throw-method/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/673-checker-throw-vmethod/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/673-checker-throw-vmethod/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/673-checker-throw-vmethod/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/678-checker-simd-saturation/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/678-checker-simd-saturation/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/678-checker-simd-saturation/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/678-checker-simd-saturation/src/Main.java b/test/678-checker-simd-saturation/src/Main.java
index d123cc2..7a22ca1 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 @@
/// 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 @@
/// 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 @@
/// 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 @@
/// 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 @@
}
//
- // 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)
@@ -337,7 +397,22 @@
}
}
- // TODO: recognize the more common if-else too.
+ /// CHECK-START: void Main.satAlt2(short[], short[], short[]) loop_optimization (before)
+ /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -32768 loop:none
+ /// CHECK-DAG: <<Clp2:i\d+>> IntConstant 32767 loop:none
+ /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get2:s\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Max:i\d+>> Max [<<Add>>,<<Clp1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Min:i\d+>> Min [<<Max>>,<<Clp2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-{ARM,ARM64}: void Main.satAlt2(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:d\d+>> VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none
public static void satAlt2(short[] a, short[] b, short[] c) {
int n = Math.min(a.length, Math.min(b.length, c.length));
for (int i = 0; i < n; i++) {
@@ -351,7 +426,11 @@
}
}
- // TODO: recognize conditional too.
+ /// CHECK-START-{ARM,ARM64}: void Main.satAlt3(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:d\d+>> VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none
public static void satAlt3(short[] a, short[] b, short[] c) {
int n = Math.min(a.length, Math.min(b.length, c.length));
for (int i = 0; i < n; i++) {
@@ -442,6 +521,34 @@
}
}
+ /// 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 +610,27 @@
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 +758,16 @@
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/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java
index 38085bb..e330a53 100644
--- a/test/679-checker-minmax/src/Main.java
+++ b/test/679-checker-minmax/src/Main.java
@@ -19,6 +19,86 @@
*/
public class Main {
+ //
+ // Direct intrinsics.
+ //
+
+ /// CHECK-START: int Main.minI(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Min:i\d+>> InvokeStaticOrDirect [<<Par>>,<<Con>>] intrinsic:MathMinIntInt
+ /// CHECK-DAG: Return [<<Min>>]
+ //
+ /// CHECK-START: int Main.minI(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Min:i\d+>> Min [<<Par>>,<<Con>>]
+ /// CHECK-DAG: Return [<<Min>>]
+ //
+ /// CHECK-START: int Main.minI(int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ public static int minI(int a) {
+ return Math.min(a, 20);
+ }
+
+ /// CHECK-START: long Main.minL(long) instruction_simplifier (before)
+ /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
+ /// CHECK-DAG: <<Min:j\d+>> InvokeStaticOrDirect [<<Par>>,<<Con>>] intrinsic:MathMinLongLong
+ /// CHECK-DAG: Return [<<Min>>]
+ //
+ /// CHECK-START: long Main.minL(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
+ /// CHECK-DAG: <<Min:j\d+>> Min [<<Par>>,<<Con>>]
+ /// CHECK-DAG: Return [<<Min>>]
+ //
+ /// CHECK-START: long Main.minL(long) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ public static long minL(long a) {
+ return Math.min(a, 20L);
+ }
+
+ /// CHECK-START: int Main.maxI(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Max:i\d+>> InvokeStaticOrDirect [<<Par>>,<<Con>>] intrinsic:MathMaxIntInt
+ /// CHECK-DAG: Return [<<Max>>]
+ //
+ /// CHECK-START: int Main.maxI(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Con:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Max:i\d+>> Max [<<Par>>,<<Con>>]
+ /// CHECK-DAG: Return [<<Max>>]
+ //
+ /// CHECK-START: int Main.maxI(int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ public static int maxI(int a) {
+ return Math.max(a, 20);
+ }
+
+ /// CHECK-START: long Main.maxL(long) instruction_simplifier (before)
+ /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
+ /// CHECK-DAG: <<Max:j\d+>> InvokeStaticOrDirect [<<Par>>,<<Con>>] intrinsic:MathMaxLongLong
+ /// CHECK-DAG: Return [<<Max>>]
+ //
+ /// CHECK-START: long Main.maxL(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Con:j\d+>> LongConstant 20
+ /// CHECK-DAG: <<Max:j\d+>> Max [<<Par>>,<<Con>>]
+ /// CHECK-DAG: Return [<<Max>>]
+ //
+ /// CHECK-START: long Main.maxL(long) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ public static long maxL(long a) {
+ return Math.max(a, 20L);
+ }
+
+ //
+ // Different types.
+ //
+
/// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (before)
/// CHECK-DAG: <<Cnd:z\d+>> GreaterThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>]
/// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>]
@@ -229,7 +309,124 @@
return a >= b ? a : b;
}
+
+ //
+ // Complications.
+ //
+
+ // TODO: coming soon, under discussion
+ public static int min0(int[] a, int[] b) {
+ // Repeat of array references needs finding the common subexpressions
+ // prior to doing the select and min/max recognition.
+ return a[0] <= b[0] ? a[0] : b[0];
+ }
+
+ // TODO: coming soon, under discussion
+ public static int max0(int[] a, int[] b) {
+ // Repeat of array references needs finding the common subexpressions
+ // prior to doing the select and min/max recognition.
+ return a[0] >= b[0] ? a[0] : b[0];
+ }
+
+ /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+ /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+ /// CHECK-DAG: <<Cnd1:z\d+>> LessThanOrEqual [<<Par>>,<<P100>>]
+ /// CHECK-DAG: <<Sel1:i\d+>> Select [<<P100>>,<<Par>>,<<Cnd1>>]
+ /// CHECK-DAG: <<Cnd2:z\d+>> GreaterThanOrEqual [<<Sel1>>,<<M100>>]
+ /// CHECK-DAG: <<Sel2:i\d+>> Select [<<M100>>,<<Sel1>>,<<Cnd2>>]
+ /// CHECK-DAG: Return [<<Sel2>>]
+ //
+ /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+ /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+ /// CHECK-DAG: <<Min:i\d+>> Min [<<Par>>,<<P100>>]
+ /// CHECK-DAG: <<Max:i\d+>> Max [<<Min>>,<<M100>>]
+ /// CHECK-DAG: Return [<<Max>>]
+ //
+ /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (after)
+ /// CHECK-NOT: Select
+ public static int minmax1(int x) {
+ // Simple if-if gives clean select sequence.
+ if (x > 100) {
+ x = 100;
+ }
+ if (x < -100) {
+ x = -100;
+ }
+ return x;
+ }
+
+ /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+ /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+ /// CHECK-DAG: <<Cnd1:z\d+>> LessThanOrEqual [<<Par>>,<<P100>>]
+ /// CHECK-DAG: <<Cnd2:z\d+>> GreaterThanOrEqual [<<Par>>,<<M100>>]
+ /// CHECK-DAG: <<Sel1:i\d+>> Select [<<M100>>,<<Par>>,<<Cnd2>>]
+ /// CHECK-DAG: <<Sel2:i\d+>> Select [<<P100>>,<<Sel1>>,<<Cnd1>>]
+ /// CHECK-DAG: Return [<<Sel2>>]
+ //
+ /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+ /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+ /// CHECK-DAG: <<Max:i\d+>> Max [<<Par>>,<<M100>>]
+ /// CHECK-DAG: <<Min:i\d+>> Min [<<Max>>,<<P100>>]
+ /// CHECK-DAG: Return [<<Min>>]
+ //
+ /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (after)
+ /// CHECK-NOT: Select
+ public static int minmax2(int x) {
+ // Simple if-else requires inspecting bounds of resulting selects.
+ if (x > 100) {
+ x = 100;
+ } else if (x < -100) {
+ x = -100;
+ }
+ return x;
+ }
+
+ /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+ /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+ /// CHECK-DAG: <<Max:i\d+>> Max [<<Par>>,<<M100>>]
+ /// CHECK-DAG: <<Min:i\d+>> Min [<<Max>>,<<P100>>]
+ /// CHECK-DAG: Return [<<Min>>]
+ //
+ /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_inlining (after)
+ /// CHECK-NOT: Select
+ public static int minmax3(int x) {
+ return (x > 100) ? 100 : ((x < -100) ? -100 : x);
+ }
+
+ /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+ /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+ /// CHECK-DAG: <<Min:i\d+>> Min [<<Par>>,<<P100>>]
+ /// CHECK-DAG: <<Max:i\d+>> Max [<<Min>>,<<M100>>]
+ /// CHECK-DAG: Return [<<Max>>]
+ //
+ /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_inlining (after)
+ /// CHECK-NOT: Select
+ public static int minmax4(int x) {
+ return (x < -100) ? -100 : ((x > 100) ? 100 : x);
+ }
+
public static void main(String[] args) {
+ // Types.
+ expectEquals(10, minI(10));
+ expectEquals(20, minI(25));
+ expectEquals(10L, minL(10L));
+ expectEquals(20L, minL(25L));
+ expectEquals(20, maxI(10));
+ expectEquals(25, maxI(25));
+ expectEquals(20L, maxL(10L));
+ expectEquals(25L, maxL(25L));
expectEquals(10, min1(10, 20));
expectEquals(10, min2(10, 20));
expectEquals(10, min3(10, 20));
@@ -244,6 +441,23 @@
expectEquals(20, max5((short) 10, (short) 20));
expectEquals(20, max6((byte) 10, (byte) 20));
expectEquals(20L, max7(10L, 20L));
+ // Complications.
+ int[] a = { 10 };
+ int[] b = { 20 };
+ expectEquals(10, min0(a, b));
+ expectEquals(20, max0(a, b));
+ expectEquals(-100, minmax1(-200));
+ expectEquals(10, minmax1(10));
+ expectEquals(100, minmax1(200));
+ expectEquals(-100, minmax2(-200));
+ expectEquals(10, minmax2(10));
+ expectEquals(100, minmax2(200));
+ expectEquals(-100, minmax3(-200));
+ expectEquals(10, minmax3(10));
+ expectEquals(100, minmax3(200));
+ expectEquals(-100, minmax4(-200));
+ expectEquals(10, minmax4(10));
+ expectEquals(100, minmax4(200));
System.out.println("passed");
}
diff --git a/test/680-checker-deopt-dex-pc-0/src/Main.java b/test/680-checker-deopt-dex-pc-0/src/Main.java
index d5a6a90..64a3cb3 100644
--- a/test/680-checker-deopt-dex-pc-0/src/Main.java
+++ b/test/680-checker-deopt-dex-pc-0/src/Main.java
@@ -31,15 +31,12 @@
System.loadLibrary(args[0]);
if (hasJit()) {
byte[] array = { 0, 1, 2, 3 };
- while (!hasJitCompiledEntrypoint(Main.class, "$noinline$getInt")) {
- for (int i = 0; i < 10000; ++i) {
- if ($noinline$getInt(array, 0) != 0x03020100) {
- throw new Error();
- }
- }
- try {
- Thread.sleep(200);
- } catch (InterruptedException ignored) {}
+ 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
@@ -56,4 +53,5 @@
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/681-checker-abs/src/Main.java b/test/681-checker-abs/src/Main.java
index 8064b1d..d1ba7c6 100644
--- a/test/681-checker-abs/src/Main.java
+++ b/test/681-checker-abs/src/Main.java
@@ -19,6 +19,38 @@
*/
public class Main {
+ /// CHECK-START: int Main.absI(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Abs:i\d+>> InvokeStaticOrDirect [<<Par>>] intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Abs>>]
+ //
+ /// CHECK-START: int Main.absI(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>]
+ /// CHECK-DAG: Return [<<Abs>>]
+ //
+ /// CHECK-START: int Main.absI(int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ public static int absI(int a) {
+ return Math.abs(a);
+ }
+
+ /// CHECK-START: long Main.absL(long) instruction_simplifier (before)
+ /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Abs:j\d+>> InvokeStaticOrDirect [<<Par>>] intrinsic:MathAbsLong
+ /// CHECK-DAG: Return [<<Abs>>]
+ //
+ /// CHECK-START: long Main.absL(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Par:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Abs:j\d+>> Abs [<<Par>>]
+ /// CHECK-DAG: Return [<<Abs>>]
+ //
+ /// CHECK-START: long Main.absL(long) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ public static long absL(long a) {
+ return Math.abs(a);
+ }
+
/// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (before)
/// CHECK-DAG: <<Par:i\d+>> ParameterValue
/// CHECK-DAG: <<Zer:i\d+>> IntConstant 0
@@ -152,7 +184,74 @@
return a >= 0 ? a : -a;
}
+ //
+ // Nop zero extension.
+ //
+
+ /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (before)
+ /// CHECK-DAG: <<Par:b\d+>> ParameterValue
+ /// CHECK-DAG: <<Msk:i\d+>> IntConstant 255
+ /// CHECK-DAG: <<And:i\d+>> [<<Par>>,<<Msk>>]
+ /// CHECK-DAG: <<Abs:i\d+>> InvokeStaticOrDirect [<<And>>] intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Abs>>]
+ //
+ /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (after)
+ /// CHECK-DAG: <<Par:b\d+>> ParameterValue
+ /// CHECK-DAG: <<Cnv:a\d+>> TypeConversion [<<Par>>]
+ /// CHECK-DAG: Return [<<Cnv>>]
+ //
+ /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: Abs
+ public static int zabs1(byte a) {
+ return Math.abs(a & 0xff);
+ }
+
+ /// CHECK-START: int Main.zabs2(short) instruction_simplifier (before)
+ /// CHECK-DAG: <<Par:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Msk:i\d+>> IntConstant 65535
+ /// CHECK-DAG: <<And:i\d+>> [<<Msk>>,<<Par>>]
+ /// CHECK-DAG: <<Abs:i\d+>> InvokeStaticOrDirect [<<And>>] intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Abs>>]
+ //
+ /// CHECK-START: int Main.zabs2(short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Par:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Par>>]
+ /// CHECK-DAG: Return [<<Cnv>>]
+ //
+ /// CHECK-START: int Main.zabs2(short) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: Abs
+ public static int zabs2(short a) {
+ return Math.abs(a & 0xffff);
+ }
+
+ /// CHECK-START: int Main.zabs3(char) instruction_simplifier (before)
+ /// CHECK-DAG: <<Par:c\d+>> ParameterValue
+ /// CHECK-DAG: <<Abs:i\d+>> InvokeStaticOrDirect [<<Par>>] intrinsic:MathAbsInt
+ /// CHECK-DAG: Return [<<Abs>>]
+ //
+ /// CHECK-START: int Main.zabs3(char) instruction_simplifier (after)
+ /// CHECK-DAG: <<Par:c\d+>> ParameterValue
+ /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>]
+ /// CHECK-DAG: Return [<<Abs>>]
+ //
+ /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Par:c\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Par>>]
+ //
+ /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_inlining (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: Abs
+ public static int zabs3(char a) {
+ return Math.abs(a);
+ }
+
public static void main(String[] args) {
+ expectEquals(10, absI(-10));
+ expectEquals(20, absI(20));
+ expectEquals(10L, absL(-10L));
+ expectEquals(20L, absL(20L));
expectEquals(10, abs1(-10));
expectEquals(20, abs1(20));
expectEquals(10, abs2(-10));
@@ -167,6 +266,12 @@
expectEquals(20, abs6((byte) 20));
expectEquals(10L, abs7(-10L));
expectEquals(20L, abs7(20L));
+ expectEquals(1, zabs1((byte) 1));
+ expectEquals(0xff, zabs1((byte) -1));
+ expectEquals(1, zabs2((short) 1));
+ expectEquals(0xffff, zabs2((short) -1));
+ expectEquals(1, zabs3((char) 1));
+ expectEquals(0xffff, zabs3((char) -1));
System.out.println("passed");
}
diff --git a/test/1940-ddms-ext/check b/test/704-multiply-accumulate/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/704-multiply-accumulate/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/704-multiply-accumulate/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/1940-ddms-ext/check b/test/706-checker-scheduler/build
old mode 100755
new mode 100644
similarity index 64%
copy from test/1940-ddms-ext/check
copy to test/706-checker-scheduler/build
index 91966b4..d85147f
--- a/test/1940-ddms-ext/check
+++ b/test/706-checker-scheduler/build
@@ -1,12 +1,12 @@
#!/bin/bash
#
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright 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
+# 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,
@@ -14,8 +14,7 @@
# 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"
+# See b/65168732
+export USE_D8=false
-./default-check "$1" "$2.tmp"
+./default-build "$@"
diff --git a/test/712-varhandle-invocations/build b/test/712-varhandle-invocations/build
index 253765b..6d4429f 100755
--- a/test/712-varhandle-invocations/build
+++ b/test/712-varhandle-invocations/build
@@ -35,5 +35,8 @@
# Desugar is not happy with our Java 9 byte code, it shouldn't be necessary here anyway.
export USE_DESUGAR=false
+# See b/65168732
+export USE_D8=false
+
# Invoke default build with increased heap size for dx
./default-build "$@" --experimental var-handles --dx-vm-option -JXmx384m
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 6633958..f8bebdd 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -21,17 +21,12 @@
TEST_ART_RUN_TEST_DEPENDENCIES := \
$(HOST_OUT_EXECUTABLES)/dx \
$(HOST_OUT_EXECUTABLES)/d8 \
+ $(HOST_OUT_EXECUTABLES)/d8-compat-dx \
$(HOST_OUT_EXECUTABLES)/hiddenapi \
$(HOST_OUT_EXECUTABLES)/jasmin \
$(HOST_OUT_EXECUTABLES)/smali \
$(HOST_OUT_JAVA_LIBRARIES)/desugar.jar
-# Add d8 dependency, if enabled.
-ifeq ($(USE_D8),true)
-TEST_ART_RUN_TEST_DEPENDENCIES += \
- $(HOST_OUT_EXECUTABLES)/d8-compat-dx
-endif
-
# We need dex2oat and dalvikvm on the target as well as the core images (all images as we sync
# only once).
TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS)
diff --git a/test/etc/default-build b/test/etc/default-build
index 9de7294..dd55602 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -317,7 +317,7 @@
fi
local dexer="${DX}"
- if [ ${USE_D8} = "true" ]; then
+ if [[ "${USE_D8}" != "false" ]]; then
dexer="${ANDROID_HOST_OUT}/bin/d8-compat-dx"
fi
diff --git a/test/run-test b/test/run-test
index 5b43b52..5f85b08 100755
--- a/test/run-test
+++ b/test/run-test
@@ -45,7 +45,7 @@
export RUN="${progdir}/etc/run-test-jar"
export DEX_LOCATION=/data/run-test/${test_dir}
export NEED_DEX="true"
-export USE_D8="false"
+export USE_D8="true"
export USE_JACK="false"
export USE_DESUGAR="true"
export SMALI_ARGS=""
@@ -365,9 +365,6 @@
elif [ "x$1" = "x--build-only" ]; then
build_only="yes"
shift
- elif [ "x$1" = "x--build-with-d8" ]; then
- USE_D8="true"
- shift
elif [ "x$1" = "x--build-with-javac-dx" ]; then
USE_JACK="false"
shift
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index 5394991..7564f5a 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -71,9 +71,6 @@
# Compiling with jack? Possible values in (True, False, 'default')
ANDROID_COMPILE_WITH_JACK = _get_build_var_boolean('ANDROID_COMPILE_WITH_JACK', 'default')
-# Follow the build system's D8 usage.
-USE_D8_BY_DEFAULT = _get_build_var_boolean('USE_D8_BY_DEFAULT', False)
-
# Directory used for temporary test files on the host.
ART_HOST_TEST_DIR = tempfile.mkdtemp(prefix = 'test-art-')
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 734a600..88b509d 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -174,56 +174,37 @@
global _user_input_variants
global run_all_configs
+ # These are the default variant-options we will use if nothing in the group is specified.
+ default_variants = {
+ 'target': {'host', 'target'},
+ 'pictest': {'npictest'},
+ 'prebuild': {'prebuild'},
+ 'cdex_level': {'cdex-fast'},
+ 'jvmti': { 'no-jvmti'},
+ 'compiler': {'optimizing',
+ 'jit',
+ 'interpreter',
+ 'interp-ac',
+ 'speed-profile'},
+ 'relocate': {'no-relocate'},
+ 'trace': {'ntrace'},
+ 'gc': {'cms'},
+ 'jni': {'checkjni'},
+ 'image': {'picimage'},
+ 'pictest': {'pictest'},
+ 'debuggable': {'ndebuggable'},
+ 'run': {'debug'},
+ # address_sizes_target depends on the target so it is dealt with below.
+ }
+ # We want to pull these early since the full VARIANT_TYPE_DICT has a few additional ones we don't
+ # want to pick up if we pass --all.
+ default_variants_keys = default_variants.keys()
if run_all_configs:
- target_types = _user_input_variants['target']
- _user_input_variants = VARIANT_TYPE_DICT
- _user_input_variants['target'] = target_types
+ default_variants = VARIANT_TYPE_DICT
- if not _user_input_variants['target']:
- _user_input_variants['target'].add('host')
- _user_input_variants['target'].add('target')
-
- if not _user_input_variants['prebuild']: # Default
- _user_input_variants['prebuild'].add('prebuild')
-
- if not _user_input_variants['cdex_level']: # Default
- _user_input_variants['cdex_level'].add('cdex-fast')
-
- # By default only run without jvmti
- if not _user_input_variants['jvmti']:
- _user_input_variants['jvmti'].add('no-jvmti')
-
- # By default we run all 'compiler' variants.
- if not _user_input_variants['compiler'] and _user_input_variants['target'] != 'jvm':
- _user_input_variants['compiler'].add('optimizing')
- _user_input_variants['compiler'].add('jit')
- _user_input_variants['compiler'].add('interpreter')
- _user_input_variants['compiler'].add('interp-ac')
- _user_input_variants['compiler'].add('speed-profile')
-
- if not _user_input_variants['relocate']: # Default
- _user_input_variants['relocate'].add('no-relocate')
-
- if not _user_input_variants['trace']: # Default
- _user_input_variants['trace'].add('ntrace')
-
- if not _user_input_variants['gc']: # Default
- _user_input_variants['gc'].add('cms')
-
- if not _user_input_variants['jni']: # Default
- _user_input_variants['jni'].add('checkjni')
-
- if not _user_input_variants['image']: # Default
- _user_input_variants['image'].add('picimage')
-
- if not _user_input_variants['pictest']: # Default
- _user_input_variants['pictest'].add('npictest')
-
- if not _user_input_variants['debuggable']: # Default
- _user_input_variants['debuggable'].add('ndebuggable')
-
- if not _user_input_variants['run']: # Default
- _user_input_variants['run'].add('debug')
+ for key in default_variants_keys:
+ if not _user_input_variants[key]:
+ _user_input_variants[key] = default_variants[key]
_user_input_variants['address_sizes_target'] = collections.defaultdict(set)
if not _user_input_variants['address_sizes']:
@@ -504,9 +485,6 @@
elif env.ANDROID_COMPILE_WITH_JACK == False:
options_test += ' --build-with-javac-dx'
- if env.USE_D8_BY_DEFAULT == True:
- options_test += ' --build-with-d8'
-
# TODO(http://36039166): This is a temporary solution to
# fix build breakages.
options_test = (' --output-path %s') % (
diff --git a/tools/build/var_list b/tools/build/var_list
index 3727741..adcb066 100644
--- a/tools/build/var_list
+++ b/tools/build/var_list
@@ -34,5 +34,4 @@
HOST_OUT_EXECUTABLES
ANDROID_JAVA_TOOLCHAIN
ANDROID_COMPILE_WITH_JACK
-USE_D8_BY_DEFAULT
diff --git a/tools/libcore_network_failures.txt b/tools/libcore_network_failures.txt
new file mode 100644
index 0000000..e7e31db
--- /dev/null
+++ b/tools/libcore_network_failures.txt
@@ -0,0 +1,92 @@
+/*
+ * This file contains extra expectations for ART's buildbot regarding network tests.
+ * The script that uses this file is art/tools/run-libcore-tests.sh.
+ */
+
+[
+{
+ description: "Ignore failure of network-related tests on new devices running Android O",
+ result: EXEC_FAILED,
+ bug: 74725685,
+ modes: [device],
+ names: ["libcore.libcore.io.OsTest#test_byteBufferPositions_sendto_recvfrom_af_inet",
+ "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithFtpURLConnection",
+ "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithHttpURLConnection",
+ "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarFtpURLConnection",
+ "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarHttpURLConnection",
+ "libcore.libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithLoggingSocketHandler",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_40555",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_io_File",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_io_FileLjava_lang_String",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_io_InputStream",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_io_InputStreamLjava_lang_String",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_lang_Readable",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_lang_String",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_channels_ReadableByteChannel",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_channels_ReadableByteChannelLjava_lang_String",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_file_Path",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_file_PathLjava_lang_String",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_file_PathLjava_lang_String_Exception",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ConstructorLjava_nio_file_Path_Exception",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_close",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_delimiter",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_findInLine_LPattern",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_findInLine_LString",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_findInLine_LString_NPEs",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_findWithinHorizon_LPatternI",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNext",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBigDecimal",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBigInteger",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBigIntegerI",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBigIntegerI_cache",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextBoolean",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextByte",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextByteI",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextByteI_cache",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextDouble",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextFloat",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextInt",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextIntI",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextIntI_cache",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLPattern",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLString",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLine",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLine_sequence",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLong",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLongI",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextLongI_cache",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextShort",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextShortI",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_hasNextShortI_cache",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_ioException",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_locale",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_match",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_next",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextBigDecimal",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextBigInteger",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextBigIntegerI",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextBoolean",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextByte",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextByteI",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextDouble",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextFloat",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextInt",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextIntI",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextLPattern",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextLString",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextLine",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextLong",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextLongI",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextShort",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_nextShortI",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_radix",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_remove",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_skip_LPattern",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_skip_LString",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_toString",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_useDelimiter_LPattern",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_useDelimiter_String",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_useLocale_LLocale",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_useRadix_I"]
+}
+]
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index de07a47..21ddcbc 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -19,6 +19,16 @@
exit 1
fi
+# Prevent JDWP tests from running on the following devices running
+# Android O (they are failing because of a network-related issue), as
+# a workaround for b/74725685:
+# - FA7BN1A04406 (walleye device testing configuration aosp-poison/volantis-armv7-poison-debug)
+# - FA7BN1A04412 (walleye device testing configuration aosp-poison/volantis-armv8-poison-ndebug)
+# - FA7BN1A04433 (walleye device testing configuration aosp-poison/volantis-armv8-poison-debug)
+case "$ANDROID_SERIAL" in
+ (FA7BN1A04406|FA7BN1A04412|FA7BN1A04433) exit 0;;
+esac
+
source build/envsetup.sh >&/dev/null # for get_build_var, setpaths
setpaths # include platform prebuilt java, javac, etc in $PATH.
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 2b7c624..7f0383d 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -161,6 +161,16 @@
fi
fi
+# Disable network-related libcore tests that are failing on the following
+# devices running Android O, as a workaround for b/74725685:
+# - FA7BN1A04406 (walleye device testing configuration aosp-poison/volantis-armv7-poison-debug)
+# - FA7BN1A04412 (walleye device testing configuration aosp-poison/volantis-armv8-poison-ndebug)
+# - FA7BN1A04433 (walleye device testing configuration aosp-poison/volantis-armv8-poison-debug)
+case "$ANDROID_SERIAL" in
+ (FA7BN1A04406|FA7BN1A04412|FA7BN1A04433)
+ expectations="$expectations --expectations art/tools/libcore_network_failures.txt";;
+esac
+
# Run the tests using vogar.
echo "Running tests for the following test packages:"
echo ${working_packages[@]} | tr " " "\n"
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index 9373c69..5ce7f52 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -57,14 +57,19 @@
adb shell ifconfig lo up
adb shell ifconfig
-# When netd is running, some libcore and JDWP tests fail with this
-# exception (b/74725685):
+# Ensure netd is running, as otherwise the logcat would be spammed
+# with the following messages on devices running Android O:
#
-# android.system.ErrnoException: connect failed: EBADMSG (Not a data message)
+# E NetdConnector: Communications error: java.io.IOException: No such file or directory
+# E mDnsConnector: Communications error: java.io.IOException: No such file or directory
#
-# Turn it off to make these tests pass.
-echo -e "${green}Turning off netd${nc}"
-adb shell stop netd
+# Netd was initially disabled as an attempt to solve issues with
+# network-related libcore and JDWP tests failing on devices running
+# Android O (MR1) (see b/74725685). These tests are currently
+# disabled. When a better solution has been found, we should remove
+# the following lines.
+echo -e "${green}Turning on netd${nc}"
+adb shell start netd
adb shell getprop init.svc.netd
echo -e "${green}List properties${nc}"
diff --git a/tools/veridex/Android.bp b/tools/veridex/Android.bp
index ff181c8..570960c 100644
--- a/tools/veridex/Android.bp
+++ b/tools/veridex/Android.bp
@@ -12,11 +12,14 @@
// 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: [
+ "flow_analysis.cc",
"hidden_api.cc",
+ "hidden_api_finder.cc",
+ "precise_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 0000000..4183054
--- /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 0000000..f85a51b
--- /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 --dex-file=test.apk
diff --git a/tools/veridex/appcompat.sh b/tools/veridex/appcompat.sh
new file mode 100755
index 0000000..31a8654
--- /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 \
+ $@
diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc
new file mode 100644
index 0000000..abd0b9b
--- /dev/null
+++ b/tools/veridex/flow_analysis.cc
@@ -0,0 +1,602 @@
+/*
+ * 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 "flow_analysis.h"
+
+#include "dex/bytecode_utils.h"
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex/dex_file-inl.h"
+#include "dex/dex_file_exception_helpers.h"
+#include "resolver.h"
+#include "veridex.h"
+
+namespace art {
+
+
+void VeriFlowAnalysis::SetAsBranchTarget(uint32_t dex_pc) {
+ if (dex_registers_[dex_pc] == nullptr) {
+ dex_registers_[dex_pc].reset(
+ new std::vector<RegisterValue>(code_item_accessor_.RegistersSize()));
+ }
+}
+
+bool VeriFlowAnalysis::IsBranchTarget(uint32_t dex_pc) {
+ return dex_registers_[dex_pc] != nullptr;
+}
+
+bool VeriFlowAnalysis::MergeRegisterValues(uint32_t dex_pc) {
+ // TODO: Do the merging. Right now, just return that we should continue
+ // the iteration if the instruction has not been visited.
+ return !instruction_infos_[dex_pc].has_been_visited;
+}
+
+void VeriFlowAnalysis::SetVisited(uint32_t dex_pc) {
+ instruction_infos_[dex_pc].has_been_visited = true;
+}
+
+void VeriFlowAnalysis::FindBranches() {
+ SetAsBranchTarget(0);
+
+ if (code_item_accessor_.TriesSize() != 0) {
+ // TODO: We need to mark the range of dex pcs as flowing in the handlers.
+ /*
+ for (const DexFile::TryItem& try_item : code_item_accessor_.TryItems()) {
+ uint32_t dex_pc_start = try_item.start_addr_;
+ uint32_t dex_pc_end = dex_pc_start + try_item.insn_count_;
+ }
+ */
+
+ // Create branch targets for exception handlers.
+ const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData();
+ uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
+ for (uint32_t idx = 0; idx < handlers_size; ++idx) {
+ CatchHandlerIterator iterator(handlers_ptr);
+ for (; iterator.HasNext(); iterator.Next()) {
+ SetAsBranchTarget(iterator.GetHandlerAddress());
+ }
+ handlers_ptr = iterator.EndDataPointer();
+ }
+ }
+
+ // Iterate over all instructions and find branching instructions.
+ for (const DexInstructionPcPair& pair : code_item_accessor_) {
+ const uint32_t dex_pc = pair.DexPc();
+ const Instruction& instruction = pair.Inst();
+
+ if (instruction.IsBranch()) {
+ SetAsBranchTarget(dex_pc + instruction.GetTargetOffset());
+ } else if (instruction.IsSwitch()) {
+ DexSwitchTable table(instruction, dex_pc);
+ for (DexSwitchTableIterator s_it(table); !s_it.Done(); s_it.Advance()) {
+ SetAsBranchTarget(dex_pc + s_it.CurrentTargetOffset());
+ if (table.ShouldBuildDecisionTree() && !s_it.IsLast()) {
+ SetAsBranchTarget(s_it.GetDexPcForCurrentIndex());
+ }
+ }
+ }
+ }
+}
+
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register,
+ RegisterSource kind,
+ VeriClass* cls,
+ uint32_t source_id) {
+ current_registers_[dex_register] = RegisterValue(
+ kind, DexFileReference(&resolver_->GetDexFile(), source_id), cls);
+}
+
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const RegisterValue& value) {
+ current_registers_[dex_register] = value;
+}
+
+void VeriFlowAnalysis::UpdateRegister(uint32_t dex_register, const VeriClass* cls) {
+ current_registers_[dex_register] =
+ RegisterValue(RegisterSource::kNone, DexFileReference(nullptr, 0), cls);
+}
+
+const RegisterValue& VeriFlowAnalysis::GetRegister(uint32_t dex_register) {
+ return current_registers_[dex_register];
+}
+
+RegisterValue VeriFlowAnalysis::GetReturnType(uint32_t method_index) {
+ const DexFile& dex_file = resolver_->GetDexFile();
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(method_index);
+ const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(method_id);
+ VeriClass* cls = resolver_->GetVeriClass(proto_id.return_type_idx_);
+ return RegisterValue(RegisterSource::kMethod, DexFileReference(&dex_file, method_index), cls);
+}
+
+RegisterValue VeriFlowAnalysis::GetFieldType(uint32_t field_index) {
+ const DexFile& dex_file = resolver_->GetDexFile();
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+ VeriClass* cls = resolver_->GetVeriClass(field_id.type_idx_);
+ return RegisterValue(RegisterSource::kField, DexFileReference(&dex_file, field_index), cls);
+}
+
+void VeriFlowAnalysis::AnalyzeCode() {
+ std::vector<uint32_t> work_list;
+ work_list.push_back(0);
+ // Iterate over the code.
+ // When visiting unconditional branches (goto), move to that instruction.
+ // When visiting conditional branches, move to one destination, and put the other
+ // in the worklist.
+ while (!work_list.empty()) {
+ uint32_t dex_pc = work_list.back();
+ work_list.pop_back();
+ CHECK(IsBranchTarget(dex_pc));
+ current_registers_ = *dex_registers_[dex_pc].get();
+ while (true) {
+ const uint16_t* insns = code_item_accessor_.Insns() + dex_pc;
+ const Instruction& inst = *Instruction::At(insns);
+ ProcessDexInstruction(inst);
+ SetVisited(dex_pc);
+
+ int opcode_flags = Instruction::FlagsOf(inst.Opcode());
+ if ((opcode_flags & Instruction::kContinue) != 0) {
+ if ((opcode_flags & Instruction::kBranch) != 0) {
+ uint32_t branch_dex_pc = dex_pc + inst.GetTargetOffset();
+ if (MergeRegisterValues(branch_dex_pc)) {
+ work_list.push_back(branch_dex_pc);
+ }
+ }
+ dex_pc += inst.SizeInCodeUnits();
+ } else if ((opcode_flags & Instruction::kBranch) != 0) {
+ dex_pc += inst.GetTargetOffset();
+ DCHECK(IsBranchTarget(dex_pc));
+ } else {
+ break;
+ }
+
+ if (IsBranchTarget(dex_pc)) {
+ if (MergeRegisterValues(dex_pc)) {
+ current_registers_ = *dex_registers_[dex_pc].get();
+ } else {
+ break;
+ }
+ }
+ }
+ }
+}
+
+void VeriFlowAnalysis::ProcessDexInstruction(const Instruction& instruction) {
+ switch (instruction.Opcode()) {
+ case Instruction::CONST_4:
+ case Instruction::CONST_16:
+ case Instruction::CONST:
+ case Instruction::CONST_HIGH16: {
+ int32_t register_index = instruction.VRegA();
+ UpdateRegister(register_index, VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::CONST_WIDE_16:
+ case Instruction::CONST_WIDE_32:
+ case Instruction::CONST_WIDE:
+ case Instruction::CONST_WIDE_HIGH16: {
+ int32_t register_index = instruction.VRegA();
+ UpdateRegister(register_index, VeriClass::long_);
+ break;
+ }
+
+ case Instruction::MOVE:
+ case Instruction::MOVE_FROM16:
+ case Instruction::MOVE_16: {
+ UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB()));
+ break;
+ }
+
+ case Instruction::MOVE_WIDE:
+ case Instruction::MOVE_WIDE_FROM16:
+ case Instruction::MOVE_WIDE_16: {
+ UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB()));
+ break;
+ }
+
+ case Instruction::MOVE_OBJECT:
+ case Instruction::MOVE_OBJECT_16:
+ case Instruction::MOVE_OBJECT_FROM16: {
+ UpdateRegister(instruction.VRegA(), GetRegister(instruction.VRegB()));
+ break;
+ }
+ case Instruction::CONST_CLASS: {
+ UpdateRegister(instruction.VRegA_21c(),
+ RegisterSource::kClass,
+ VeriClass::class_,
+ instruction.VRegB_21c());
+ break;
+ }
+ case Instruction::CONST_STRING: {
+ UpdateRegister(instruction.VRegA_21c(),
+ RegisterSource::kString,
+ VeriClass::string_,
+ instruction.VRegB_21c());
+ break;
+ }
+
+ case Instruction::CONST_STRING_JUMBO: {
+ UpdateRegister(instruction.VRegA_31c(),
+ RegisterSource::kString,
+ VeriClass::string_,
+ instruction.VRegB_31c());
+ break;
+ }
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_SUPER:
+ case Instruction::INVOKE_VIRTUAL: {
+ VeriMethod method = resolver_->GetMethod(instruction.VRegB_35c());
+ uint32_t args[5];
+ instruction.GetVarArgs(args);
+ if (method == VeriClass::forName_) {
+ RegisterValue value = GetRegister(args[0]);
+ last_result_ = RegisterValue(
+ value.GetSource(), value.GetDexFileReference(), VeriClass::class_);
+ } else if (IsGetField(method)) {
+ RegisterValue cls = GetRegister(args[0]);
+ RegisterValue name = GetRegister(args[1]);
+ field_uses_.push_back(std::make_pair(cls, name));
+ last_result_ = GetReturnType(instruction.VRegB_35c());
+ } else if (IsGetMethod(method)) {
+ RegisterValue cls = GetRegister(args[0]);
+ RegisterValue name = GetRegister(args[1]);
+ method_uses_.push_back(std::make_pair(cls, name));
+ last_result_ = GetReturnType(instruction.VRegB_35c());
+ } else if (method == VeriClass::getClass_) {
+ RegisterValue obj = GetRegister(args[0]);
+ last_result_ = RegisterValue(
+ obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_);
+ } else {
+ last_result_ = GetReturnType(instruction.VRegB_35c());
+ }
+ 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: {
+ last_result_ = GetReturnType(instruction.VRegB_3rc());
+ break;
+ }
+
+ case Instruction::MOVE_RESULT:
+ case Instruction::MOVE_RESULT_WIDE:
+ case Instruction::MOVE_RESULT_OBJECT: {
+ UpdateRegister(instruction.VRegA(), last_result_);
+ break;
+ }
+ case Instruction::RETURN_VOID:
+ case Instruction::RETURN_OBJECT:
+ case Instruction::RETURN_WIDE:
+ case Instruction::RETURN: {
+ break;
+ }
+ #define IF_XX(cond) \
+ case Instruction::IF_##cond: break; \
+ case Instruction::IF_##cond##Z: break
+
+ IF_XX(EQ);
+ IF_XX(NE);
+ IF_XX(LT);
+ IF_XX(LE);
+ IF_XX(GT);
+ IF_XX(GE);
+
+ case Instruction::GOTO:
+ case Instruction::GOTO_16:
+ case Instruction::GOTO_32: {
+ break;
+ }
+ case Instruction::INVOKE_POLYMORPHIC: {
+ // TODO
+ break;
+ }
+
+ case Instruction::INVOKE_POLYMORPHIC_RANGE: {
+ // TODO
+ break;
+ }
+
+ case Instruction::NEG_INT:
+ case Instruction::NEG_LONG:
+ case Instruction::NEG_FLOAT:
+ case Instruction::NEG_DOUBLE:
+ case Instruction::NOT_INT:
+ case Instruction::NOT_LONG: {
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::INT_TO_LONG:
+ case Instruction::INT_TO_FLOAT:
+ case Instruction::INT_TO_DOUBLE:
+ case Instruction::LONG_TO_INT:
+ case Instruction::LONG_TO_FLOAT:
+ case Instruction::LONG_TO_DOUBLE:
+ case Instruction::FLOAT_TO_INT:
+ case Instruction::FLOAT_TO_LONG:
+ case Instruction::FLOAT_TO_DOUBLE:
+ case Instruction::DOUBLE_TO_INT:
+ case Instruction::DOUBLE_TO_LONG:
+ case Instruction::DOUBLE_TO_FLOAT:
+ case Instruction::INT_TO_BYTE:
+ case Instruction::INT_TO_SHORT:
+ case Instruction::INT_TO_CHAR: {
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::ADD_INT:
+ case Instruction::ADD_LONG:
+ case Instruction::ADD_DOUBLE:
+ case Instruction::ADD_FLOAT:
+ case Instruction::SUB_INT:
+ case Instruction::SUB_LONG:
+ case Instruction::SUB_FLOAT:
+ case Instruction::SUB_DOUBLE:
+ case Instruction::MUL_INT:
+ case Instruction::MUL_LONG:
+ case Instruction::MUL_FLOAT:
+ case Instruction::MUL_DOUBLE:
+ case Instruction::DIV_INT:
+ case Instruction::DIV_LONG:
+ case Instruction::DIV_FLOAT:
+ case Instruction::DIV_DOUBLE:
+ case Instruction::REM_INT:
+ case Instruction::REM_LONG:
+ case Instruction::REM_FLOAT:
+ case Instruction::REM_DOUBLE:
+ case Instruction::AND_INT:
+ case Instruction::AND_LONG:
+ case Instruction::SHL_INT:
+ case Instruction::SHL_LONG:
+ case Instruction::SHR_INT:
+ case Instruction::SHR_LONG:
+ case Instruction::USHR_INT:
+ case Instruction::USHR_LONG:
+ case Instruction::OR_INT:
+ case Instruction::OR_LONG:
+ case Instruction::XOR_INT:
+ case Instruction::XOR_LONG: {
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::ADD_INT_2ADDR:
+ case Instruction::ADD_LONG_2ADDR:
+ case Instruction::ADD_DOUBLE_2ADDR:
+ case Instruction::ADD_FLOAT_2ADDR:
+ case Instruction::SUB_INT_2ADDR:
+ case Instruction::SUB_LONG_2ADDR:
+ case Instruction::SUB_FLOAT_2ADDR:
+ case Instruction::SUB_DOUBLE_2ADDR:
+ case Instruction::MUL_INT_2ADDR:
+ case Instruction::MUL_LONG_2ADDR:
+ case Instruction::MUL_FLOAT_2ADDR:
+ case Instruction::MUL_DOUBLE_2ADDR:
+ case Instruction::DIV_INT_2ADDR:
+ case Instruction::DIV_LONG_2ADDR:
+ case Instruction::REM_INT_2ADDR:
+ case Instruction::REM_LONG_2ADDR:
+ case Instruction::REM_FLOAT_2ADDR:
+ case Instruction::REM_DOUBLE_2ADDR:
+ case Instruction::SHL_INT_2ADDR:
+ case Instruction::SHL_LONG_2ADDR:
+ case Instruction::SHR_INT_2ADDR:
+ case Instruction::SHR_LONG_2ADDR:
+ case Instruction::USHR_INT_2ADDR:
+ case Instruction::USHR_LONG_2ADDR:
+ case Instruction::DIV_FLOAT_2ADDR:
+ case Instruction::DIV_DOUBLE_2ADDR:
+ case Instruction::AND_INT_2ADDR:
+ case Instruction::AND_LONG_2ADDR:
+ case Instruction::OR_INT_2ADDR:
+ case Instruction::OR_LONG_2ADDR:
+ case Instruction::XOR_INT_2ADDR:
+ case Instruction::XOR_LONG_2ADDR: {
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::ADD_INT_LIT16:
+ case Instruction::AND_INT_LIT16:
+ case Instruction::OR_INT_LIT16:
+ case Instruction::XOR_INT_LIT16:
+ case Instruction::RSUB_INT:
+ case Instruction::MUL_INT_LIT16:
+ case Instruction::DIV_INT_LIT16:
+ case Instruction::REM_INT_LIT16: {
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::ADD_INT_LIT8:
+ case Instruction::AND_INT_LIT8:
+ case Instruction::OR_INT_LIT8:
+ case Instruction::XOR_INT_LIT8:
+ case Instruction::RSUB_INT_LIT8:
+ case Instruction::MUL_INT_LIT8:
+ case Instruction::DIV_INT_LIT8:
+ case Instruction::REM_INT_LIT8:
+ case Instruction::SHL_INT_LIT8:
+ case Instruction::SHR_INT_LIT8: {
+ case Instruction::USHR_INT_LIT8: {
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::NEW_INSTANCE: {
+ VeriClass* cls = resolver_->GetVeriClass(dex::TypeIndex(instruction.VRegB_21c()));
+ UpdateRegister(instruction.VRegA(), cls);
+ break;
+ }
+
+ case Instruction::NEW_ARRAY: {
+ dex::TypeIndex type_index(instruction.VRegC_22c());
+ VeriClass* cls = resolver_->GetVeriClass(type_index);
+ UpdateRegister(instruction.VRegA_22c(), cls);
+ break;
+ }
+
+ case Instruction::FILLED_NEW_ARRAY: {
+ dex::TypeIndex type_index(instruction.VRegB_35c());
+ VeriClass* cls = resolver_->GetVeriClass(type_index);
+ UpdateRegister(instruction.VRegA_22c(), cls);
+ break;
+ }
+
+ case Instruction::FILLED_NEW_ARRAY_RANGE: {
+ dex::TypeIndex type_index(instruction.VRegB_3rc());
+ uint32_t register_index = instruction.VRegC_3rc();
+ VeriClass* cls = resolver_->GetVeriClass(type_index);
+ UpdateRegister(register_index, cls);
+ break;
+ }
+
+ case Instruction::FILL_ARRAY_DATA: {
+ break;
+ }
+
+ case Instruction::CMP_LONG:
+ case Instruction::CMPG_FLOAT:
+ case Instruction::CMPG_DOUBLE:
+ case Instruction::CMPL_FLOAT:
+ case Instruction::CMPL_DOUBLE:
+ UpdateRegister(instruction.VRegA(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::NOP:
+ 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: {
+ UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c()));
+ 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: {
+ 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: {
+ UpdateRegister(instruction.VRegA_22c(), GetFieldType(instruction.VRegC_22c()));
+ 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: {
+ break;
+ }
+
+#define ARRAY_XX(kind, anticipated_type) \
+ case Instruction::AGET##kind: { \
+ UpdateRegister(instruction.VRegA_23x(), anticipated_type); \
+ break; \
+ } \
+ case Instruction::APUT##kind: { \
+ break; \
+ }
+
+ ARRAY_XX(, VeriClass::integer_);
+ ARRAY_XX(_WIDE, VeriClass::long_);
+ ARRAY_XX(_BOOLEAN, VeriClass::boolean_);
+ ARRAY_XX(_BYTE, VeriClass::byte_);
+ ARRAY_XX(_CHAR, VeriClass::char_);
+ ARRAY_XX(_SHORT, VeriClass::short_);
+
+ case Instruction::AGET_OBJECT: {
+ // TODO: take the component type.
+ UpdateRegister(instruction.VRegA_23x(), VeriClass::object_);
+ break;
+ }
+
+ case Instruction::APUT_OBJECT: {
+ break;
+ }
+
+ case Instruction::ARRAY_LENGTH: {
+ UpdateRegister(instruction.VRegA_12x(), VeriClass::integer_);
+ break;
+ }
+
+ case Instruction::MOVE_EXCEPTION: {
+ UpdateRegister(instruction.VRegA_11x(), VeriClass::throwable_);
+ break;
+ }
+
+ case Instruction::THROW: {
+ break;
+ }
+
+ case Instruction::INSTANCE_OF: {
+ uint8_t destination = instruction.VRegA_22c();
+ UpdateRegister(destination, VeriClass::boolean_);
+ break;
+ }
+
+ case Instruction::CHECK_CAST: {
+ uint8_t reference = instruction.VRegA_21c();
+ dex::TypeIndex type_index(instruction.VRegB_21c());
+ UpdateRegister(reference, resolver_->GetVeriClass(type_index));
+ break;
+ }
+
+ case Instruction::MONITOR_ENTER:
+ case Instruction::MONITOR_EXIT: {
+ break;
+ }
+
+ case Instruction::SPARSE_SWITCH:
+ case Instruction::PACKED_SWITCH:
+ break;
+
+ default:
+ break;
+ }
+}
+
+void VeriFlowAnalysis::Run() {
+ FindBranches();
+ AnalyzeCode();
+}
+
+} // namespace art
diff --git a/tools/veridex/flow_analysis.h b/tools/veridex/flow_analysis.h
new file mode 100644
index 0000000..c065fb8
--- /dev/null
+++ b/tools/veridex/flow_analysis.h
@@ -0,0 +1,147 @@
+/*
+ * 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_FLOW_ANALYSIS_H_
+#define ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
+
+#include "dex/code_item_accessors.h"
+#include "dex/dex_file_reference.h"
+#include "dex/method_reference.h"
+#include "veridex.h"
+
+namespace art {
+
+class VeridexClass;
+class VeridexResolver;
+
+/**
+ * The source where a dex register comes from.
+ */
+enum class RegisterSource {
+ kParameter,
+ kField,
+ kMethod,
+ kClass,
+ kString,
+ kNone
+};
+
+/**
+ * Abstract representation of a dex register value.
+ */
+class RegisterValue {
+ public:
+ RegisterValue() : source_(RegisterSource::kNone), reference_(nullptr, 0), type_(nullptr) {}
+ RegisterValue(RegisterSource source, DexFileReference reference, const VeriClass* type)
+ : source_(source), reference_(reference), type_(type) {}
+
+ RegisterSource GetSource() const { return source_; }
+ DexFileReference GetDexFileReference() const { return reference_; }
+ const VeriClass* GetType() const { return type_; }
+
+ const char* ToString() const {
+ switch (source_) {
+ case RegisterSource::kString:
+ return reference_.dex_file->StringDataByIdx(dex::StringIndex(reference_.index));
+ case RegisterSource::kClass:
+ return reference_.dex_file->StringByTypeIdx(dex::TypeIndex(reference_.index));
+ default:
+ return "<unknown>";
+ }
+ }
+
+ private:
+ RegisterSource source_;
+ DexFileReference reference_;
+ const VeriClass* type_;
+};
+
+struct InstructionInfo {
+ bool has_been_visited;
+};
+
+class VeriFlowAnalysis {
+ public:
+ VeriFlowAnalysis(VeridexResolver* resolver,
+ const CodeItemDataAccessor& code_item_accessor)
+ : resolver_(resolver),
+ code_item_accessor_(code_item_accessor),
+ dex_registers_(code_item_accessor.InsnsSizeInCodeUnits()),
+ instruction_infos_(code_item_accessor.InsnsSizeInCodeUnits()) {}
+
+ void Run();
+
+ const std::vector<std::pair<RegisterValue, RegisterValue>>& GetFieldUses() const {
+ return field_uses_;
+ }
+
+ const std::vector<std::pair<RegisterValue, RegisterValue>>& GetMethodUses() const {
+ return method_uses_;
+ }
+
+ private:
+ // Find all branches in the code.
+ void FindBranches();
+
+ // Analyze all non-deead instructions in the code.
+ void AnalyzeCode();
+
+ // Set the instruction at the given pc as a branch target.
+ void SetAsBranchTarget(uint32_t dex_pc);
+
+ // Whether the instruction at the given pc is a branch target.
+ bool IsBranchTarget(uint32_t dex_pc);
+
+ // Merge the register values at the given pc with `current_registers`.
+ // Return whether the register values have changed, and the instruction needs
+ // to be visited again.
+ bool MergeRegisterValues(uint32_t dex_pc);
+
+ void UpdateRegister(
+ uint32_t dex_register, RegisterSource kind, VeriClass* cls, uint32_t source_id);
+ void UpdateRegister(uint32_t dex_register, const RegisterValue& value);
+ void UpdateRegister(uint32_t dex_register, const VeriClass* cls);
+ const RegisterValue& GetRegister(uint32_t dex_register);
+ void ProcessDexInstruction(const Instruction& inst);
+ void SetVisited(uint32_t dex_pc);
+ RegisterValue GetReturnType(uint32_t method_index);
+ RegisterValue GetFieldType(uint32_t field_index);
+
+ VeridexResolver* resolver_;
+ const CodeItemDataAccessor& code_item_accessor_;
+
+ // Vector of register values for all branch targets.
+ std::vector<std::unique_ptr<std::vector<RegisterValue>>> dex_registers_;
+
+ // The current values of dex registers.
+ std::vector<RegisterValue> current_registers_;
+
+ // Information on each instruction useful for the analysis.
+ std::vector<InstructionInfo> instruction_infos_;
+
+ // The value of invoke instructions, to be fetched when visiting move-result.
+ RegisterValue last_result_;
+
+ // List of reflection field uses found.
+ std::vector<std::pair<RegisterValue, RegisterValue>> field_uses_;
+
+ // List of reflection method uses found.
+ std::vector<std::pair<RegisterValue, RegisterValue>> method_uses_;
+};
+
+} // namespace art
+
+#endif // ART_TOOLS_VERIDEX_FLOW_ANALYSIS_H_
diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc
index 33e499b..17fa1b8 100644
--- a/tools/veridex/hidden_api.cc
+++ b/tools/veridex/hidden_api.cc
@@ -44,17 +44,6 @@
return ss.str();
}
-bool HiddenApi::LogIfIn(const std::string& name,
- const std::set<std::string>& list,
- const std::string& log,
- const std::string& access_kind) {
- if (list.find(name) != list.end()) {
- LOG(WARNING) << std::string(log) << " usage found " << name << " (" << access_kind << ")";
- return true;
- }
- return false;
-}
-
void HiddenApi::FillList(const char* filename, std::set<std::string>& entries) {
if (filename == nullptr) {
return;
@@ -72,6 +61,11 @@
// Add the class->method name (so stripping the signature).
entries.insert(str.substr(0, pos));
}
+ pos = str.find(':');
+ if (pos != std::string::npos) {
+ // Add the class->field name (so stripping the type).
+ entries.insert(str.substr(0, pos));
+ }
}
}
}
diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h
index 282e7cf..4c67768 100644
--- a/tools/veridex/hidden_api.h
+++ b/tools/veridex/hidden_api.h
@@ -17,6 +17,10 @@
#ifndef ART_TOOLS_VERIDEX_HIDDEN_API_H_
#define ART_TOOLS_VERIDEX_HIDDEN_API_H_
+#include "dex/hidden_api_access_flags.h"
+#include "dex/method_reference.h"
+
+#include <ostream>
#include <set>
#include <string>
@@ -35,21 +39,34 @@
FillList(blacklist, blacklist_);
}
- bool LogIfInList(const std::string& name, const char* access_kind) const {
- return LogIfIn(name, blacklist_, "Blacklist", access_kind) ||
- LogIfIn(name, dark_greylist_, "Dark greylist", access_kind) ||
- LogIfIn(name, light_greylist_, "Light greylist", access_kind);
+ 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);
+ static std::string GetApiMethodName(MethodReference ref) {
+ return HiddenApi::GetApiMethodName(*ref.dex_file, ref.index);
+ }
+
private:
- static bool LogIfIn(const std::string& name,
- const std::set<std::string>& list,
- const std::string& log,
- const std::string& access_kind);
+ 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);
@@ -58,6 +75,13 @@
std::set<std::string> dark_greylist_;
};
+struct HiddenApiStats {
+ uint32_t count = 0;
+ uint32_t reflection_count = 0;
+ uint32_t linking_count = 0;
+ uint32_t api_counts[4] = { 0, 0, 0, 0 };
+};
+
} // 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 0000000..b9be618
--- /dev/null
+++ b/tools/veridex/hidden_api_finder.cc
@@ -0,0 +1,254 @@
+/*
+ * 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();
+ // Look at all types referenced in this dex file. Any of these
+ // types can lead to being used through reflection.
+ for (uint32_t i = 0; i < dex_file.NumTypeIds(); ++i) {
+ std::string name(dex_file.StringByTypeIdx(dex::TypeIndex(i)));
+ if (hidden_api_.IsInRestrictionList(name)) {
+ classes_.insert(name);
+ }
+ }
+ // Note: we collect strings constants only referenced in code items as the string table
+ // contains other kind of strings (eg types).
+ 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_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;
+ }
+ }
+ }
+ }
+}
+
+void HiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
+ for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
+ CollectAccesses(resolver.get());
+ }
+}
+
+void HiddenApiFinder::Dump(std::ostream& os,
+ HiddenApiStats* stats,
+ bool dump_reflection) {
+ static const char* kPrefix = " ";
+ stats->linking_count = method_locations_.size() + field_locations_.size();
+
+ // 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);
+ stats->api_counts[api_list]++;
+ os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
+ os << std::endl;
+ for (const MethodReference& ref : pair.second) {
+ os << kPrefix << HiddenApi::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);
+ stats->api_counts[api_list]++;
+ os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
+ os << std::endl;
+ for (const MethodReference& ref : pair.second) {
+ os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
+ }
+ os << std::endl;
+ }
+
+ if (dump_reflection) {
+ // 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);
+ stats->api_counts[api_list]++;
+ if (api_list != HiddenApiAccessFlags::kWhitelist) {
+ stats->reflection_count++;
+ os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
+ << " potential use(s):";
+ os << std::endl;
+ for (const MethodReference& ref : reflection_locations_[name]) {
+ os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
+ }
+ os << 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 0000000..f7d3dc8
--- /dev/null
+++ b/tools/veridex/hidden_api_finder.h
@@ -0,0 +1,61 @@
+/*
+ * 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;
+struct HiddenApiStats;
+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);
+
+ void Dump(std::ostream& os, HiddenApiStats* stats, bool dump_reflection);
+
+ 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);
+
+ 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/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc
new file mode 100644
index 0000000..2092af3
--- /dev/null
+++ b/tools/veridex/precise_hidden_api_finder.cc
@@ -0,0 +1,92 @@
+/*
+ * 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 "precise_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 "flow_analysis.h"
+#include "hidden_api.h"
+#include "resolver.h"
+#include "veridex.h"
+
+#include <iostream>
+
+namespace art {
+
+void PreciseHiddenApiFinder::Run(const std::vector<std::unique_ptr<VeridexResolver>>& resolvers) {
+ for (const std::unique_ptr<VeridexResolver>& resolver : resolvers) {
+ 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);
+ VeriFlowAnalysis ana(resolver.get(), code_item_accessor);
+ ana.Run();
+ if (!ana.GetFieldUses().empty()) {
+ field_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetFieldUses();
+ }
+ if (!ana.GetMethodUses().empty()) {
+ method_uses_[MethodReference(&dex_file, it.GetMemberIndex())] = ana.GetMethodUses();
+ }
+ }
+ }
+ }
+}
+
+void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) {
+ static const char* kPrefix = " ";
+ for (auto kinds : { field_uses_, method_uses_ }) {
+ for (auto it : kinds) {
+ MethodReference ref = it.first;
+ for (const std::pair<RegisterValue, RegisterValue>& info : it.second) {
+ if ((info.first.GetSource() == RegisterSource::kClass ||
+ info.first.GetSource() == RegisterSource::kString) &&
+ info.second.GetSource() == RegisterSource::kString) {
+ std::string cls(info.first.ToString());
+ std::string name(info.second.ToString());
+ std::string full_name = cls + "->" + name;
+ HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
+ stats->api_counts[api_list]++;
+ if (api_list != HiddenApiAccessFlags::kWhitelist) {
+ ++stats->reflection_count;
+ os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
+ << " use:";
+ os << std::endl;
+ os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
+ os << std::endl;
+ }
+ }
+ }
+ }
+ }
+}
+
+} // namespace art
diff --git a/tools/veridex/precise_hidden_api_finder.h b/tools/veridex/precise_hidden_api_finder.h
new file mode 100644
index 0000000..22744a6
--- /dev/null
+++ b/tools/veridex/precise_hidden_api_finder.h
@@ -0,0 +1,55 @@
+/*
+ * 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_PRECISE_HIDDEN_API_FINDER_H_
+#define ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_
+
+#include "dex/method_reference.h"
+#include "flow_analysis.h"
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+
+namespace art {
+
+class HiddenApi;
+struct HiddenApiStats;
+class VeridexResolver;
+
+/**
+ * Reports known uses of hidden APIs from reflection.
+ */
+class PreciseHiddenApiFinder {
+ public:
+ explicit PreciseHiddenApiFinder(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);
+
+ void Dump(std::ostream& os, HiddenApiStats* stats);
+
+ private:
+ const HiddenApi& hidden_api_;
+ std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> field_uses_;
+ std::map<MethodReference, std::vector<std::pair<RegisterValue, RegisterValue>>> method_uses_;
+};
+
+} // namespace art
+
+#endif // ART_TOOLS_VERIDEX_PRECISE_HIDDEN_API_FINDER_H_
diff --git a/tools/veridex/resolver.cc b/tools/veridex/resolver.cc
index 6ab872e..9113039 100644
--- a/tools/veridex/resolver.cc
+++ b/tools/veridex/resolver.cc
@@ -59,6 +59,14 @@
static bool HasSameNameAndSignature(const DexFile& dex_file,
const DexFile::MethodId& method_id,
const char* method_name,
+ const char* type) {
+ return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 &&
+ strcmp(type, dex_file.GetMethodSignature(method_id).ToString().c_str()) == 0;
+}
+
+static bool HasSameNameAndSignature(const DexFile& dex_file,
+ const DexFile::MethodId& method_id,
+ const char* method_name,
const Signature& signature) {
return strcmp(method_name, dex_file.GetMethodName(method_id)) == 0 &&
dex_file.GetMethodSignature(method_id) == signature;
@@ -241,6 +249,34 @@
return nullptr;
}
+VeriMethod VeridexResolver::LookupDeclaredMethodIn(const VeriClass& kls,
+ const char* method_name,
+ const char* type) const {
+ if (kls.IsPrimitive()) {
+ return nullptr;
+ }
+ if (kls.IsArray()) {
+ return nullptr;
+ }
+ VeridexResolver* resolver = GetResolverOf(kls);
+ const DexFile& other_dex_file = resolver->dex_file_;
+ const uint8_t* class_data = other_dex_file.GetClassData(*kls.GetClassDef());
+ if (class_data != nullptr) {
+ ClassDataItemIterator it(other_dex_file, class_data);
+ it.SkipAllFields();
+ for (; it.HasNextMethod(); it.Next()) {
+ const DexFile::MethodId& other_method_id = other_dex_file.GetMethodId(it.GetMemberIndex());
+ if (HasSameNameAndSignature(other_dex_file,
+ other_method_id,
+ method_name,
+ type)) {
+ return it.DataPointer();
+ }
+ }
+ }
+ return nullptr;
+}
+
VeriMethod VeridexResolver::GetMethod(uint32_t method_index) {
VeriMethod method_info = method_infos_[method_index];
if (method_info == nullptr) {
@@ -277,10 +313,8 @@
return field_info;
}
-void VeridexResolver::ResolveAll(const HiddenApi& hidden_api) {
+void VeridexResolver::ResolveAll() {
for (uint32_t i = 0; i < dex_file_.NumTypeIds(); ++i) {
- // Note: we don't look at HiddenApi for types, as the lists don't contain
- // classes.
if (GetVeriClass(dex::TypeIndex(i)) == nullptr) {
LOG(WARNING) << "Unresolved " << dex_file_.PrettyType(dex::TypeIndex(i));
}
@@ -288,17 +322,13 @@
for (uint32_t i = 0; i < dex_file_.NumMethodIds(); ++i) {
if (GetMethod(i) == nullptr) {
- if (!hidden_api.LogIfInList(HiddenApi::GetApiMethodName(dex_file_, i), "Linking")) {
- LOG(WARNING) << "Unresolved: " << dex_file_.PrettyMethod(i);
- }
+ LOG(WARNING) << "Unresolved: " << dex_file_.PrettyMethod(i);
}
}
for (uint32_t i = 0; i < dex_file_.NumFieldIds(); ++i) {
if (GetField(i) == nullptr) {
- if (!hidden_api.LogIfInList(HiddenApi::GetApiFieldName(dex_file_, i), "Linking")) {
- LOG(WARNING) << "Unresolved: " << dex_file_.PrettyField(i);
- }
+ LOG(WARNING) << "Unresolved: " << dex_file_.PrettyField(i);
}
}
}
diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h
index 82f6aae..52b15fe 100644
--- a/tools/veridex/resolver.h
+++ b/tools/veridex/resolver.h
@@ -66,9 +66,18 @@
const char* field_name,
const char* field_type);
- // Resolve all type_id/method_id/field_id. Log for unresolved
- // entities, or entities part of a hidden API list.
- void ResolveAll(const HiddenApi& hidden_api);
+ // Lookup a method declared in `kls`.
+ VeriMethod LookupDeclaredMethodIn(const VeriClass& kls,
+ const char* method_name,
+ const char* signature) const;
+
+ // 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.
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index c5203fe..6e72faa 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -21,6 +21,8 @@
#include "dex/dex_file.h"
#include "dex/dex_file_loader.h"
#include "hidden_api.h"
+#include "hidden_api_finder.h"
+#include "precise_hidden_api_finder.h"
#include "resolver.h"
#include <sstream>
@@ -46,8 +48,18 @@
VeriClass* VeriClass::double_ = &d_;
VeriClass* VeriClass::long_ = &j_;
VeriClass* VeriClass::void_ = &v_;
+
// Will be set after boot classpath has been resolved.
VeriClass* VeriClass::object_ = nullptr;
+VeriClass* VeriClass::class_ = nullptr;
+VeriClass* VeriClass::string_ = nullptr;
+VeriClass* VeriClass::throwable_ = nullptr;
+VeriMethod VeriClass::forName_ = nullptr;
+VeriMethod VeriClass::getField_ = nullptr;
+VeriMethod VeriClass::getDeclaredField_ = nullptr;
+VeriMethod VeriClass::getMethod_ = nullptr;
+VeriMethod VeriClass::getDeclaredMethod_ = nullptr;
+VeriMethod VeriClass::getClass_ = nullptr;
struct VeridexOptions {
const char* dex_file = nullptr;
@@ -55,6 +67,7 @@
const char* blacklist = nullptr;
const char* light_greylist = nullptr;
const char* dark_greylist = nullptr;
+ bool precise = true;
};
static const char* Substr(const char* str, int index) {
@@ -75,6 +88,7 @@
static const char* kBlacklistOption = "--blacklist=";
static const char* kDarkGreylistOption = "--dark-greylist=";
static const char* kLightGreylistOption = "--light-greylist=";
+ static const char* kImprecise = "--imprecise";
for (int i = 0; i < argc; ++i) {
if (StartsWith(argv[i], kDexFileOption)) {
@@ -87,6 +101,8 @@
options->dark_greylist = Substr(argv[i], strlen(kDarkGreylistOption));
} else if (StartsWith(argv[i], kLightGreylistOption)) {
options->light_greylist = Substr(argv[i], strlen(kLightGreylistOption));
+ } else if (strcmp(argv[i], kImprecise) == 0) {
+ options->precise = false;
}
}
}
@@ -156,22 +172,70 @@
std::vector<std::unique_ptr<VeridexResolver>> boot_resolvers;
Resolve(boot_dex_files, resolver_map, type_map, &boot_resolvers);
- // Now that boot classpath has been resolved, fill j.l.Object.
+ // Now that boot classpath has been resolved, fill classes and reflection
+ // methods.
VeriClass::object_ = type_map["Ljava/lang/Object;"];
+ VeriClass::class_ = type_map["Ljava/lang/Class;"];
+ VeriClass::string_ = type_map["Ljava/lang/String;"];
+ VeriClass::throwable_ = type_map["Ljava/lang/Throwable;"];
+ VeriClass::forName_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+ *VeriClass::class_, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
+ VeriClass::getField_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+ *VeriClass::class_, "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
+ VeriClass::getDeclaredField_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+ *VeriClass::class_, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
+ VeriClass::getMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+ *VeriClass::class_,
+ "getMethod",
+ "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
+ VeriClass::getDeclaredMethod_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+ *VeriClass::class_,
+ "getDeclaredMethod",
+ "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
+ VeriClass::getClass_ = boot_resolvers[0]->LookupDeclaredMethodIn(
+ *VeriClass::object_, "getClass", "()Ljava/lang/Class;");
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.
+ // Find and log uses of hidden APIs.
HiddenApi hidden_api(options.blacklist, options.dark_greylist, options.light_greylist);
- for (const std::unique_ptr<VeridexResolver>& resolver : app_resolvers) {
- resolver->ResolveAll(hidden_api);
+ HiddenApiStats stats;
+
+ HiddenApiFinder api_finder(hidden_api);
+ api_finder.Run(app_resolvers);
+ api_finder.Dump(std::cout, &stats, !options.precise);
+
+ if (options.precise) {
+ PreciseHiddenApiFinder precise_api_finder(hidden_api);
+ precise_api_finder.Run(app_resolvers);
+ precise_api_finder.Dump(std::cout, &stats);
+ }
+
+ DumpSummaryStats(std::cout, stats);
+
+ if (options.precise) {
+ std::cout << "To run an analysis that can give more reflection accesses, " << std::endl
+ << "but could include false positives, pass the --imprecise flag. " << std::endl;
}
return 0;
}
private:
+ static void DumpSummaryStats(std::ostream& os, const HiddenApiStats& stats) {
+ static const char* kPrefix = " ";
+ os << stats.count << " hidden API(s) used: "
+ << stats.linking_count << " linked against, "
+ << stats.reflection_count << " through reflection" << std::endl;
+ os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kBlacklist]
+ << " in blacklist" << std::endl;
+ os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kDarkGreylist]
+ << " in dark greylist" << std::endl;
+ os << kPrefix << stats.api_counts[HiddenApiAccessFlags::kLightGreylist]
+ << " in light greylist" << std::endl;
+ }
+
static bool Load(const std::string& filename,
std::string& content,
std::vector<std::unique_ptr<const DexFile>>* dex_files,
diff --git a/tools/veridex/veridex.h b/tools/veridex/veridex.h
index 0c928ab..75e4845 100644
--- a/tools/veridex/veridex.h
+++ b/tools/veridex/veridex.h
@@ -25,6 +25,18 @@
namespace art {
/**
+ * Abstraction for fields defined in dex files. Currently, that's a pointer into their
+ * `encoded_field` description.
+ */
+using VeriField = const uint8_t*;
+
+/**
+ * Abstraction for methods defined in dex files. Currently, that's a pointer into their
+ * `encoded_method` description.
+ */
+using VeriMethod = const uint8_t*;
+
+/**
* Abstraction for classes defined, or implicitly defined (for arrays and primitives)
* in dex files.
*/
@@ -52,6 +64,9 @@
const DexFile::ClassDef* GetClassDef() const { return class_def_; }
static VeriClass* object_;
+ static VeriClass* class_;
+ static VeriClass* string_;
+ static VeriClass* throwable_;
static VeriClass* boolean_;
static VeriClass* byte_;
static VeriClass* char_;
@@ -62,23 +77,26 @@
static VeriClass* long_;
static VeriClass* void_;
+ static VeriMethod forName_;
+ static VeriMethod getField_;
+ static VeriMethod getDeclaredField_;
+ static VeriMethod getMethod_;
+ static VeriMethod getDeclaredMethod_;
+ static VeriMethod getClass_;
+
private:
Primitive::Type kind_;
uint8_t dimensions_;
const DexFile::ClassDef* class_def_;
};
-/**
- * Abstraction for fields defined in dex files. Currently, that's a pointer into their
- * `encoded_field` description.
- */
-using VeriField = const uint8_t*;
+inline bool IsGetMethod(VeriMethod method) {
+ return method == VeriClass::getMethod_ || method == VeriClass::getDeclaredMethod_;
+}
-/**
- * Abstraction for methods defined in dex files. Currently, that's a pointer into their
- * `encoded_method` description.
- */
-using VeriMethod = const uint8_t*;
+inline bool IsGetField(VeriMethod method) {
+ return method == VeriClass::getField_ || method == VeriClass::getDeclaredField_;
+}
/**
* Map from name to VeriClass to quickly lookup classes.