summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/Android.gtest.mk11
-rw-r--r--compiler/driver/compiler_driver.cc22
-rw-r--r--compiler/image_writer.cc5
-rw-r--r--compiler/optimizing/bounds_check_elimination_test.cc2
-rw-r--r--compiler/optimizing/induction_var_range.cc27
-rw-r--r--compiler/optimizing/inliner.cc4
-rw-r--r--compiler/optimizing/instruction_builder.cc8
-rw-r--r--compiler/optimizing/instruction_simplifier.cc58
-rw-r--r--compiler/optimizing/instruction_simplifier.h8
-rw-r--r--compiler/optimizing/nodes.h3
-rw-r--r--compiler/optimizing/optimizing_compiler.cc10
-rw-r--r--compiler/optimizing/sharpening.cc30
-rw-r--r--compiler/optimizing/sharpening.h12
-rw-r--r--compiler/verifier_deps_test.cc23
-rw-r--r--profman/profile_assistant_test.cc39
-rw-r--r--profman/profman.cc16
-rw-r--r--runtime/class_linker.cc25
-rw-r--r--runtime/jit/jit_code_cache.cc6
-rw-r--r--runtime/jit/profile_compilation_info.cc79
-rw-r--r--runtime/jit/profile_compilation_info.h27
-rw-r--r--runtime/jit/profile_compilation_info_test.cc139
-rw-r--r--runtime/mirror/class.h6
-rw-r--r--test/618-checker-induction/src/Main.java18
-rw-r--r--test/623-checker-loop-regressions/src/Main.java45
-rw-r--r--test/641-checker-arraycopy/build24
-rw-r--r--test/641-checker-arraycopy/expected.txt0
-rw-r--r--test/641-checker-arraycopy/info.txt2
-rw-r--r--test/641-checker-arraycopy/src/Main.java83
-rw-r--r--test/641-iterations/expected.txt1
-rw-r--r--test/641-iterations/info.txt1
-rw-r--r--test/641-iterations/src/Main.java73
-rw-r--r--test/Android.run-test.mk10
-rw-r--r--test/ProfileTestMultiDex/Main.java4
-rw-r--r--test/VerifierDeps/MySub1SoftVerificationFailure.smali16
-rw-r--r--test/VerifierDeps/MySub2SoftVerificationFailure.smali16
-rw-r--r--test/VerifierDepsMulti/MySoftVerificationFailure.smali24
-rwxr-xr-xtest/etc/run-test-jar11
-rw-r--r--test/knownfailures.json7
38 files changed, 763 insertions, 132 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index b661e001c8..c27f8dbe4a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -75,8 +75,11 @@ $(ART_TEST_TARGET_GTEST_MainStripped_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX)
$(call dexpreopt-remove-classes.dex,$@)
ART_TEST_GTEST_VerifierDeps_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDeps/*.smali))
+ART_TEST_GTEST_VerifierDepsMulti_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDepsMulti/*.smali))
ART_TEST_HOST_GTEST_VerifierDeps_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifierDeps,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
ART_TEST_TARGET_GTEST_VerifierDeps_DEX := $(dir $(ART_TEST_TARGET_GTEST_Main_DEX))$(subst Main,VerifierDeps,$(basename $(notdir $(ART_TEST_TARGET_GTEST_Main_DEX))))$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
+ART_TEST_HOST_GTEST_VerifierDepsMulti_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifierDepsMulti,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
+ART_TEST_TARGET_GTEST_VerifierDepsMulti_DEX := $(dir $(ART_TEST_TARGET_GTEST_Main_DEX))$(subst Main,VerifierDepsMulti,$(basename $(notdir $(ART_TEST_TARGET_GTEST_Main_DEX))))$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
$(ART_TEST_HOST_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $(HOST_OUT_EXECUTABLES)/smali
$(HOST_OUT_EXECUTABLES)/smali --output=$@ $(filter %.smali,$^)
@@ -84,6 +87,12 @@ $(ART_TEST_HOST_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $(HO
$(ART_TEST_TARGET_GTEST_VerifierDeps_DEX): $(ART_TEST_GTEST_VerifierDeps_SRC) $(HOST_OUT_EXECUTABLES)/smali
$(HOST_OUT_EXECUTABLES)/smali --output=$@ $(filter %.smali,$^)
+$(ART_TEST_HOST_GTEST_VerifierDepsMulti_DEX): $(ART_TEST_GTEST_VerifierDepsMulti_SRC) $(HOST_OUT_EXECUTABLES)/smali
+ $(HOST_OUT_EXECUTABLES)/smali --output=$@ $(filter %.smali,$^)
+
+$(ART_TEST_TARGET_GTEST_VerifierDepsMulti_DEX): $(ART_TEST_GTEST_VerifierDepsMulti_SRC) $(HOST_OUT_EXECUTABLES)/smali
+ $(HOST_OUT_EXECUTABLES)/smali --output=$@ $(filter %.smali,$^)
+
# Dex file dependencies for each gtest.
ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
@@ -115,7 +124,7 @@ ART_GTEST_stub_test_DEX_DEPS := AllFields
ART_GTEST_transaction_test_DEX_DEPS := Transaction
ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup
ART_GTEST_unstarted_runtime_test_DEX_DEPS := Nested
-ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps MultiDex
+ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps VerifierDepsMulti MultiDex
ART_GTEST_dex_to_dex_decompiler_test_DEX_DEPS := VerifierDeps DexToDexDecompiler
# The elf writer test has dependencies on core.oat.
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index a5e4cb0877..057e3c9960 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -535,9 +535,8 @@ static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel(
if (klass->IsVerified()) {
// Class is verified so we can enable DEX-to-DEX compilation for performance.
return max_level;
- } else if (klass->IsCompileTimeVerified()) {
+ } else if (klass->ShouldVerifyAtRuntime()) {
// Class verification has soft-failed. Anyway, ensure at least correctness.
- DCHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
return optimizer::DexToDexCompilationLevel::kRequired;
} else {
// Class verification has failed: do not run DEX-to-DEX compilation.
@@ -964,7 +963,7 @@ static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader,
if (cls == nullptr) {
soa.Self()->ClearException();
} else if (&cls->GetDexFile() == dex_file) {
- DCHECK(cls->IsErroneous() || cls->IsVerified() || cls->IsCompileTimeVerified())
+ DCHECK(cls->IsErroneous() || cls->IsVerified() || cls->ShouldVerifyAtRuntime())
<< cls->PrettyClass()
<< " " << cls->GetStatus();
}
@@ -2160,6 +2159,14 @@ class VerifyClassVisitor : public CompilationVisitor {
LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor)
<< " because: " << error_msg;
manager_->GetCompiler()->SetHadHardVerifierFailure();
+ } else {
+ // Force a soft failure for the VerifierDeps. This is a sanity measure, as
+ // the vdex file already records that the class hasn't been resolved. It avoids
+ // trying to do future verification optimizations when processing the vdex file.
+ DCHECK(failure_kind == verifier::MethodVerifier::kSoftFailure ||
+ failure_kind == verifier::MethodVerifier::kNoFailure)
+ << failure_kind;
+ failure_kind = verifier::MethodVerifier::kSoftFailure;
}
} else if (!SkipClass(jclass_loader, dex_file, klass.Get())) {
CHECK(klass->IsResolved()) << klass->PrettyClass();
@@ -2172,7 +2179,7 @@ class VerifyClassVisitor : public CompilationVisitor {
manager_->GetCompiler()->SetHadHardVerifierFailure();
}
- CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous())
+ CHECK(klass->ShouldVerifyAtRuntime() || klass->IsVerified() || klass->IsErroneous())
<< klass->PrettyDescriptor() << ": state=" << klass->GetStatus();
// It is *very* problematic if there are verification errors in the boot classpath. For example,
@@ -2186,6 +2193,13 @@ class VerifyClassVisitor : public CompilationVisitor {
DCHECK(klass->IsVerified()) << "Boot classpath class " << klass->PrettyClass()
<< " failed to fully verify: state= " << klass->GetStatus();
}
+ if (klass->IsVerified()) {
+ DCHECK_EQ(failure_kind, verifier::MethodVerifier::kNoFailure);
+ } else if (klass->ShouldVerifyAtRuntime()) {
+ DCHECK_EQ(failure_kind, verifier::MethodVerifier::kSoftFailure);
+ } else {
+ DCHECK_EQ(failure_kind, verifier::MethodVerifier::kHardFailure);
+ }
}
} else {
// Make the skip a soft failure, essentially being considered as verify at runtime.
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 117d1131b5..65d82ed980 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -238,10 +238,11 @@ bool ImageWriter::Write(int image_fd,
case ImageHeader::kStorageModeLZ4: {
const size_t compressed_max_size = LZ4_compressBound(image_data_size);
compressed_data.reset(new char[compressed_max_size]);
- data_size = LZ4_compress(
+ data_size = LZ4_compress_default(
reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader),
&compressed_data[0],
- image_data_size);
+ image_data_size,
+ compressed_max_size);
break;
}
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index 5d58207511..cb6e14b2bd 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -43,7 +43,7 @@ class BoundsCheckEliminationTest : public testing::Test {
void RunBCE() {
graph_->BuildDominatorTree();
- InstructionSimplifier(graph_).Run();
+ InstructionSimplifier(graph_, /* codegen */ nullptr).Run();
SideEffectsAnalysis side_effects(graph_);
side_effects.Run();
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 1cd65c1c66..d6513c8e34 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -57,21 +57,27 @@ static bool IsIntAndGet(HInstruction* instruction, int64_t* value) {
return false;
}
-/** Returns b^e for b,e >= 1. Sets overflow if arithmetic wrap-around occurred. */
+/** Computes a * b for a,b > 0 (at least until first overflow happens). */
+static int64_t SafeMul(int64_t a, int64_t b, /*out*/ bool* overflow) {
+ if (a > 0 && b > 0 && a > (std::numeric_limits<int64_t>::max() / b)) {
+ *overflow = true;
+ }
+ return a * b;
+}
+
+/** Returns b^e for b,e > 0. Sets overflow if arithmetic wrap-around occurred. */
static int64_t IntPow(int64_t b, int64_t e, /*out*/ bool* overflow) {
- DCHECK_GE(b, 1);
- DCHECK_GE(e, 1);
+ DCHECK_LT(0, b);
+ DCHECK_LT(0, e);
int64_t pow = 1;
while (e) {
if (e & 1) {
- int64_t oldpow = pow;
- pow *= b;
- if (pow < oldpow) {
- *overflow = true;
- }
+ pow = SafeMul(pow, b, overflow);
}
e >>= 1;
- b *= b;
+ if (e) {
+ b = SafeMul(b, b, overflow);
+ }
}
return pow;
}
@@ -384,7 +390,8 @@ bool InductionVarRange::IsUnitStride(HInstruction* instruction,
HInductionVarAnalysis::InductionInfo* trip = nullptr;
if (HasInductionInfo(instruction, instruction, &loop, &info, &trip)) {
if (info->induction_class == HInductionVarAnalysis::kLinear &&
- info->op_b->operation == HInductionVarAnalysis::kFetch) {
+ info->op_b->operation == HInductionVarAnalysis::kFetch &&
+ !HInductionVarAnalysis::IsNarrowingLinear(info)) {
int64_t stride_value = 0;
if (IsConstant(info->op_a, kExact, &stride_value) && stride_value == 1) {
int64_t off_value = 0;
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 3e340908bf..e3926c58c4 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -549,7 +549,7 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver,
is_referrer,
invoke_instruction->GetDexPc(),
/* needs_access_check */ false);
- HLoadClass::LoadKind kind = HSharpening::SharpenClass(
+ HLoadClass::LoadKind kind = HSharpening::ComputeLoadClassKind(
load_class, codegen_, compiler_driver_, caller_compilation_unit_);
DCHECK(kind != HLoadClass::LoadKind::kInvalid)
<< "We should always be able to reference a class for inline caches";
@@ -1491,7 +1491,7 @@ size_t HInliner::RunOptimizations(HGraph* callee_graph,
HDeadCodeElimination dce(callee_graph, inline_stats_, "dead_code_elimination$inliner");
HConstantFolding fold(callee_graph, "constant_folding$inliner");
HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_, handles_);
- InstructionSimplifier simplify(callee_graph, inline_stats_);
+ InstructionSimplifier simplify(callee_graph, codegen_, inline_stats_);
IntrinsicsRecognizer intrinsics(callee_graph, inline_stats_);
HOptimization* optimizations[] = {
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index c60f6e5393..fe11433ae0 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1676,10 +1676,10 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index,
dex_pc,
needs_access_check);
- HLoadClass::LoadKind load_kind = HSharpening::SharpenClass(load_class,
- code_generator_,
- compiler_driver_,
- *dex_compilation_unit_);
+ HLoadClass::LoadKind load_kind = HSharpening::ComputeLoadClassKind(load_class,
+ code_generator_,
+ compiler_driver_,
+ *dex_compilation_unit_);
if (load_kind == HLoadClass::LoadKind::kInvalid) {
// We actually cannot reference this class, we're forced to bail.
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 35f59cb4a4..17421fc364 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -19,14 +19,18 @@
#include "escape.h"
#include "intrinsics.h"
#include "mirror/class-inl.h"
+#include "sharpening.h"
#include "scoped_thread_state_change-inl.h"
namespace art {
class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
public:
- InstructionSimplifierVisitor(HGraph* graph, OptimizingCompilerStats* stats)
+ InstructionSimplifierVisitor(HGraph* graph,
+ CodeGenerator* codegen,
+ OptimizingCompilerStats* stats)
: HGraphDelegateVisitor(graph),
+ codegen_(codegen),
stats_(stats) {}
void Run();
@@ -112,6 +116,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
void SimplifyAllocationIntrinsic(HInvoke* invoke);
void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind);
+ CodeGenerator* codegen_;
OptimizingCompilerStats* stats_;
bool simplification_occurred_ = false;
int simplifications_at_current_position_ = 0;
@@ -123,7 +128,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
};
void InstructionSimplifier::Run() {
- InstructionSimplifierVisitor visitor(graph_, stats_);
+ InstructionSimplifierVisitor visitor(graph_, codegen_, stats_);
visitor.Run();
}
@@ -1805,6 +1810,8 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction)
{
ScopedObjectAccess soa(Thread::Current());
+ Primitive::Type source_component_type = Primitive::kPrimVoid;
+ Primitive::Type destination_component_type = Primitive::kPrimVoid;
ReferenceTypeInfo destination_rti = destination->GetReferenceTypeInfo();
if (destination_rti.IsValid()) {
if (destination_rti.IsObjectArray()) {
@@ -1814,6 +1821,8 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction)
optimizations.SetDestinationIsTypedObjectArray();
}
if (destination_rti.IsPrimitiveArrayClass()) {
+ destination_component_type =
+ destination_rti.GetTypeHandle()->GetComponentType()->GetPrimitiveType();
optimizations.SetDestinationIsPrimitiveArray();
} else if (destination_rti.IsNonPrimitiveArrayClass()) {
optimizations.SetDestinationIsNonPrimitiveArray();
@@ -1826,10 +1835,55 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction)
}
if (source_rti.IsPrimitiveArrayClass()) {
optimizations.SetSourceIsPrimitiveArray();
+ source_component_type = source_rti.GetTypeHandle()->GetComponentType()->GetPrimitiveType();
} else if (source_rti.IsNonPrimitiveArrayClass()) {
optimizations.SetSourceIsNonPrimitiveArray();
}
}
+ // For primitive arrays, use their optimized ArtMethod implementations.
+ if ((source_component_type != Primitive::kPrimVoid) &&
+ (source_component_type == destination_component_type)) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ PointerSize image_size = class_linker->GetImagePointerSize();
+ HInvokeStaticOrDirect* invoke = instruction->AsInvokeStaticOrDirect();
+ mirror::Class* system = invoke->GetResolvedMethod()->GetDeclaringClass();
+ ArtMethod* method = nullptr;
+ switch (source_component_type) {
+ case Primitive::kPrimBoolean:
+ method = system->FindDeclaredDirectMethod("arraycopy", "([ZI[ZII)V", image_size);
+ break;
+ case Primitive::kPrimByte:
+ method = system->FindDeclaredDirectMethod("arraycopy", "([BI[BII)V", image_size);
+ break;
+ case Primitive::kPrimChar:
+ method = system->FindDeclaredDirectMethod("arraycopy", "([CI[CII)V", image_size);
+ break;
+ case Primitive::kPrimShort:
+ method = system->FindDeclaredDirectMethod("arraycopy", "([SI[SII)V", image_size);
+ break;
+ case Primitive::kPrimInt:
+ method = system->FindDeclaredDirectMethod("arraycopy", "([II[III)V", image_size);
+ break;
+ case Primitive::kPrimFloat:
+ method = system->FindDeclaredDirectMethod("arraycopy", "([FI[FII)V", image_size);
+ break;
+ case Primitive::kPrimLong:
+ method = system->FindDeclaredDirectMethod("arraycopy", "([JI[JII)V", image_size);
+ break;
+ case Primitive::kPrimDouble:
+ method = system->FindDeclaredDirectMethod("arraycopy", "([DI[DII)V", image_size);
+ break;
+ default:
+ LOG(FATAL) << "Unreachable";
+ }
+ DCHECK(method != nullptr);
+ invoke->SetResolvedMethod(method);
+ // Sharpen the new invoke. Note that we do not update the dex method index of
+ // the invoke, as we would need to look it up in the current dex file, and it
+ // is unlikely that it exists. The most usual situation for such typed
+ // arraycopy methods is a direct pointer to the boot image.
+ HSharpening::SharpenInvokeStaticOrDirect(invoke, codegen_);
+ }
}
}
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
index 7fe1067aa9..f7329a4a1f 100644
--- a/compiler/optimizing/instruction_simplifier.h
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -23,6 +23,8 @@
namespace art {
+class CodeGenerator;
+
/**
* Implements optimizations specific to each instruction.
*
@@ -36,15 +38,19 @@ namespace art {
class InstructionSimplifier : public HOptimization {
public:
explicit InstructionSimplifier(HGraph* graph,
+ CodeGenerator* codegen,
OptimizingCompilerStats* stats = nullptr,
const char* name = kInstructionSimplifierPassName)
- : HOptimization(graph, name, stats) {}
+ : HOptimization(graph, name, stats),
+ codegen_(codegen) {}
static constexpr const char* kInstructionSimplifierPassName = "instruction_simplifier";
void Run() OVERRIDE;
private:
+ CodeGenerator* codegen_;
+
DISALLOW_COPY_AND_ASSIGN(InstructionSimplifier);
};
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index c39aed2c6a..69ef9e178c 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3915,6 +3915,7 @@ class HInvoke : public HVariableInputSizeInstruction {
bool IsIntrinsic() const { return intrinsic_ != Intrinsics::kNone; }
ArtMethod* GetResolvedMethod() const { return resolved_method_; }
+ void SetResolvedMethod(ArtMethod* method) { resolved_method_ = method; }
DECLARE_ABSTRACT_INSTRUCTION(Invoke);
@@ -3957,7 +3958,7 @@ class HInvoke : public HVariableInputSizeInstruction {
}
uint32_t number_of_arguments_;
- ArtMethod* const resolved_method_;
+ ArtMethod* resolved_method_;
const uint32_t dex_method_index_;
Intrinsics intrinsic_;
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 607b9433ae..d6153b091c 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -507,7 +507,7 @@ static HOptimization* BuildOptimization(
} else if (opt_name == HInductionVarAnalysis::kInductionPassName) {
return new (arena) HInductionVarAnalysis(graph);
} else if (opt_name == InstructionSimplifier::kInstructionSimplifierPassName) {
- return new (arena) InstructionSimplifier(graph, stats, pass_name.c_str());
+ return new (arena) InstructionSimplifier(graph, codegen, stats, pass_name.c_str());
} else if (opt_name == IntrinsicsRecognizer::kIntrinsicsRecognizerPassName) {
return new (arena) IntrinsicsRecognizer(graph, stats);
} else if (opt_name == LICM::kLoopInvariantCodeMotionPassName) {
@@ -768,7 +768,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph,
HDeadCodeElimination* dce3 = new (arena) HDeadCodeElimination(
graph, stats, "dead_code_elimination$final");
HConstantFolding* fold1 = new (arena) HConstantFolding(graph, "constant_folding");
- InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, stats);
+ InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, codegen, stats);
HSelectGenerator* select_generator = new (arena) HSelectGenerator(graph, stats);
HConstantFolding* fold2 = new (arena) HConstantFolding(
graph, "constant_folding$after_inlining");
@@ -786,11 +786,11 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph,
HSharpening* sharpening = new (arena) HSharpening(
graph, codegen, dex_compilation_unit, driver, handles);
InstructionSimplifier* simplify2 = new (arena) InstructionSimplifier(
- graph, stats, "instruction_simplifier$after_inlining");
+ graph, codegen, stats, "instruction_simplifier$after_inlining");
InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier(
- graph, stats, "instruction_simplifier$after_bce");
+ graph, codegen, stats, "instruction_simplifier$after_bce");
InstructionSimplifier* simplify4 = new (arena) InstructionSimplifier(
- graph, stats, "instruction_simplifier$before_codegen");
+ graph, codegen, stats, "instruction_simplifier$before_codegen");
IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, stats);
CHAGuardOptimization* cha_guard = new (arena) CHAGuardOptimization(graph);
CodeSinking* code_sinking = new (arena) CodeSinking(graph, stats);
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index be400925d5..8f1827be69 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -41,7 +41,7 @@ void HSharpening::Run() {
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* instruction = it.Current();
if (instruction->IsInvokeStaticOrDirect()) {
- ProcessInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect());
+ SharpenInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect(), codegen_);
} else if (instruction->IsLoadString()) {
ProcessLoadString(instruction->AsLoadString());
}
@@ -70,7 +70,9 @@ static bool AOTCanEmbedMethod(ArtMethod* method, const CompilerOptions& options)
return IsInBootImage(method) && !options.GetCompilePic() && !options.GetIncludePatchInformation();
}
-void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+
+void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke,
+ CodeGenerator* codegen) {
if (invoke->IsStringInit()) {
// Not using the dex cache arrays. But we could still try to use a better dispatch...
// TODO: Use direct_method and direct_code for the appropriate StringFactory method.
@@ -97,12 +99,12 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
// We don't optimize for debuggable as it would prevent us from obsoleting the method in some
// situations.
- if (callee == codegen_->GetGraph()->GetArtMethod() && !codegen_->GetGraph()->IsDebuggable()) {
+ if (callee == codegen->GetGraph()->GetArtMethod() && !codegen->GetGraph()->IsDebuggable()) {
// Recursive call.
method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive;
code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf;
} else if (Runtime::Current()->UseJitCompilation() ||
- AOTCanEmbedMethod(callee, codegen_->GetCompilerOptions())) {
+ AOTCanEmbedMethod(callee, codegen->GetCompilerOptions())) {
// JIT or on-device AOT compilation referencing a boot image method.
// Use the method address directly.
method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress;
@@ -111,13 +113,17 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
} else {
// Use PC-relative access to the dex cache arrays.
method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative;
- DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()),
- &graph_->GetDexFile());
+ // Note: we use the invoke's graph instead of the codegen graph, which are
+ // different when inlining (the codegen graph is the most outer graph). The
+ // invoke's dex method index is relative to the dex file where the invoke's graph
+ // was built from.
+ DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen->GetInstructionSet()),
+ &invoke->GetBlock()->GetGraph()->GetDexFile());
method_load_data = layout.MethodOffset(invoke->GetDexMethodIndex());
code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
}
- if (graph_->IsDebuggable()) {
+ if (codegen->GetGraph()->IsDebuggable()) {
// For debuggable apps always use the code pointer from ArtMethod
// so that we don't circumvent instrumentation stubs if installed.
code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
@@ -127,14 +133,14 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
method_load_kind, code_ptr_location, method_load_data
};
HInvokeStaticOrDirect::DispatchInfo dispatch_info =
- codegen_->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke);
+ codegen->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke);
invoke->SetDispatchInfo(dispatch_info);
}
-HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class,
- CodeGenerator* codegen,
- CompilerDriver* compiler_driver,
- const DexCompilationUnit& dex_compilation_unit) {
+HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(HLoadClass* load_class,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver,
+ const DexCompilationUnit& dex_compilation_unit) {
Handle<mirror::Class> klass = load_class->GetClass();
DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCacheViaMethod ||
load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass)
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index 4240b2f339..10707c796f 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -48,14 +48,16 @@ class HSharpening : public HOptimization {
static constexpr const char* kSharpeningPassName = "sharpening";
// Used by the builder and the inliner.
- static HLoadClass::LoadKind SharpenClass(HLoadClass* load_class,
- CodeGenerator* codegen,
- CompilerDriver* compiler_driver,
- const DexCompilationUnit& dex_compilation_unit)
+ static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver,
+ const DexCompilationUnit& dex_compilation_unit)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Used by Sharpening and InstructionSimplifier.
+ static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, CodeGenerator* codegen);
+
private:
- void ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke);
void ProcessLoadString(HLoadString* load_string);
CodeGenerator* codegen_;
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index c892b25ed3..01c33591e5 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -246,9 +246,13 @@ class VerifierDepsTest : public CommonCompilerTest {
}
bool HasUnverifiedClass(const std::string& cls) {
- const DexFile::TypeId* type_id = primary_dex_file_->FindTypeId(cls.c_str());
+ return HasUnverifiedClass(cls, *primary_dex_file_);
+ }
+
+ bool HasUnverifiedClass(const std::string& cls, const DexFile& dex_file) {
+ const DexFile::TypeId* type_id = dex_file.FindTypeId(cls.c_str());
DCHECK(type_id != nullptr);
- dex::TypeIndex index = primary_dex_file_->GetIndexForTypeId(*type_id);
+ dex::TypeIndex index = dex_file.GetIndexForTypeId(*type_id);
for (const auto& dex_dep : verifier_deps_->dex_deps_) {
for (dex::TypeIndex entry : dex_dep.second->unverified_classes_) {
if (index == entry) {
@@ -1141,7 +1145,7 @@ TEST_F(VerifierDepsTest, UnverifiedClasses) {
// Test that a class with hard failure is recorded.
ASSERT_TRUE(HasUnverifiedClass("LMyVerificationFailure;"));
// Test that a class with unresolved super is recorded.
- ASSERT_FALSE(HasUnverifiedClass("LMyClassWithNoSuper;"));
+ ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuper;"));
// Test that a class with unresolved super and hard failure is recorded.
ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuperButFailures;"));
}
@@ -1511,5 +1515,18 @@ TEST_F(VerifierDepsTest, CompilerDriver) {
}
}
+TEST_F(VerifierDepsTest, MultiDexVerification) {
+ VerifyDexFile("VerifierDepsMulti");
+ ASSERT_EQ(NumberOfCompiledDexFiles(), 2u);
+
+ ASSERT_TRUE(HasUnverifiedClass("LMySoftVerificationFailure;", *dex_files_[1]));
+ ASSERT_TRUE(HasUnverifiedClass("LMySub1SoftVerificationFailure;", *dex_files_[0]));
+ ASSERT_TRUE(HasUnverifiedClass("LMySub2SoftVerificationFailure;", *dex_files_[0]));
+
+ std::vector<uint8_t> buffer;
+ verifier_deps_->Encode(dex_files_, &buffer);
+ ASSERT_FALSE(buffer.empty());
+}
+
} // namespace verifier
} // namespace art
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 5a758aedf4..52f3b52ee2 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -182,7 +182,8 @@ class ProfileAssistantTest : public CommonRuntimeTest {
void AssertInlineCaches(ArtMethod* method,
const std::set<mirror::Class*>& expected_clases,
const ProfileCompilationInfo& info,
- bool megamorphic)
+ bool is_megamorphic,
+ bool is_missing_types)
REQUIRES_SHARED(Locks::mutator_lock_) {
ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
ASSERT_TRUE(info.GetMethod(method->GetDexFile()->GetLocation(),
@@ -192,7 +193,8 @@ class ProfileAssistantTest : public CommonRuntimeTest {
ASSERT_EQ(pmi.inline_caches.size(), 1u);
ProfileCompilationInfo::DexPcData dex_pc_data = pmi.inline_caches.begin()->second;
- ASSERT_EQ(dex_pc_data.is_megamorphic, megamorphic);
+ ASSERT_EQ(dex_pc_data.is_megamorphic, is_megamorphic);
+ ASSERT_EQ(dex_pc_data.is_missing_types, is_missing_types);
ASSERT_EQ(expected_clases.size(), dex_pc_data.classes.size());
size_t found = 0;
for (mirror::Class* it : expected_clases) {
@@ -482,6 +484,7 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
"LTestInline;->inlineMonomorphic(LSuper;)I+LSubA;",
"LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;",
"LTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
+ "LTestInline;->inlineMissingTypes(LSuper;)I+missing_types",
"LTestInline;->noInlineCache(LSuper;)I"
};
std::string input_file_contents;
@@ -521,7 +524,11 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
ASSERT_TRUE(inline_monomorphic != nullptr);
std::set<mirror::Class*> expected_monomorphic;
expected_monomorphic.insert(sub_a);
- AssertInlineCaches(inline_monomorphic, expected_monomorphic, info, /*megamorphic*/ false);
+ AssertInlineCaches(inline_monomorphic,
+ expected_monomorphic,
+ info,
+ /*megamorphic*/false,
+ /*missing_types*/false);
}
{
@@ -534,7 +541,11 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
expected_polymorphic.insert(sub_a);
expected_polymorphic.insert(sub_b);
expected_polymorphic.insert(sub_c);
- AssertInlineCaches(inline_polymorhic, expected_polymorphic, info, /*megamorphic*/ false);
+ AssertInlineCaches(inline_polymorhic,
+ expected_polymorphic,
+ info,
+ /*megamorphic*/false,
+ /*missing_types*/false);
}
{
@@ -544,7 +555,25 @@ TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
"inlineMegamorphic");
ASSERT_TRUE(inline_megamorphic != nullptr);
std::set<mirror::Class*> expected_megamorphic;
- AssertInlineCaches(inline_megamorphic, expected_megamorphic, info, /*megamorphic*/ true);
+ AssertInlineCaches(inline_megamorphic,
+ expected_megamorphic,
+ info,
+ /*megamorphic*/true,
+ /*missing_types*/false);
+ }
+
+ {
+ // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
+ ArtMethod* inline_missing_types = GetVirtualMethod(class_loader,
+ "LTestInline;",
+ "inlineMissingTypes");
+ ASSERT_TRUE(inline_missing_types != nullptr);
+ std::set<mirror::Class*> expected_missing_Types;
+ AssertInlineCaches(inline_missing_types,
+ expected_missing_Types,
+ info,
+ /*megamorphic*/false,
+ /*missing_types*/true);
}
{
diff --git a/profman/profman.cc b/profman/profman.cc
index a99a0ea2da..f7316cc129 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -139,6 +139,7 @@ static constexpr uint16_t kDefaultTestProfileClassRatio = 5;
// Separators used when parsing human friendly representation of profiles.
static const std::string kMethodSep = "->";
+static const std::string kMissingTypesMarker = "missing_types";
static constexpr char kProfileParsingInlineChacheSep = '+';
static constexpr char kProfileParsingTypeSep = ',';
static constexpr char kProfileParsingFirstCharInSignature = '(';
@@ -624,8 +625,11 @@ class ProfMan FINAL {
// Process a line defining a class or a method and its inline caches.
// Upon success return true and add the class or the method info to profile.
- // The format of the method line is:
+ // The possible line formats are:
+ // "LJustTheCass;".
// "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;".
+ // "LTestInline;->inlineMissingTypes(LSuper;)I+missing_types".
+ // "LTestInline;->inlineNoInlineCaches(LSuper;)I".
// The method and classes are searched only in the given dex files.
bool ProcessLine(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
const std::string& line,
@@ -642,7 +646,7 @@ class ProfMan FINAL {
ProfileMethodInfo::ProfileClassReference class_ref;
if (!FindClass(dex_files, klass, &class_ref)) {
- LOG(ERROR) << "Could not find class: " << klass;
+ LOG(WARNING) << "Could not find class: " << klass;
return false;
}
@@ -664,10 +668,14 @@ class ProfMan FINAL {
std::vector<std::string> inline_cache_elems;
std::vector<std::string> method_elems;
+ bool is_missing_types = false;
Split(method_str, kProfileParsingInlineChacheSep, &method_elems);
if (method_elems.size() == 2) {
method_spec = method_elems[0];
- Split(method_elems[1], kProfileParsingTypeSep, &inline_cache_elems);
+ is_missing_types = method_elems[1] == kMissingTypesMarker;
+ if (!is_missing_types) {
+ Split(method_elems[1], kProfileParsingTypeSep, &inline_cache_elems);
+ }
} else if (method_elems.size() == 1) {
method_spec = method_elems[0];
} else {
@@ -689,7 +697,7 @@ class ProfMan FINAL {
}
}
std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches;
- inline_caches.emplace_back(dex_pc, classes);
+ inline_caches.emplace_back(dex_pc, is_missing_types, classes);
std::vector<ProfileMethodInfo> pmi;
pmi.emplace_back(class_ref.dex_file, method_index, inline_caches);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d232714338..b611aa2132 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3900,8 +3900,10 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self,
if (!supertype->IsVerified() && !supertype->IsErroneous()) {
VerifyClass(self, supertype);
}
- if (supertype->IsCompileTimeVerified()) {
- // Either we are verified or we soft failed and need to retry at runtime.
+
+ if (supertype->IsVerified() || supertype->ShouldVerifyAtRuntime()) {
+ // The supertype is either verified, or we soft failed at AOT time.
+ DCHECK(supertype->IsVerified() || Runtime::Current()->IsAotCompiler());
return true;
}
// If we got this far then we have a hard failure.
@@ -3967,13 +3969,16 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass(
return verifier::MethodVerifier::kHardFailure;
}
- // Don't attempt to re-verify if already sufficiently verified.
+ // Don't attempt to re-verify if already verified.
if (klass->IsVerified()) {
EnsureSkipAccessChecksMethods(klass, image_pointer_size_);
return verifier::MethodVerifier::kNoFailure;
}
- if (klass->IsCompileTimeVerified() && Runtime::Current()->IsAotCompiler()) {
- return verifier::MethodVerifier::kNoFailure;
+
+ // For AOT, don't attempt to re-verify if we have already found we should
+ // verify at runtime.
+ if (Runtime::Current()->IsAotCompiler() && klass->ShouldVerifyAtRuntime()) {
+ return verifier::MethodVerifier::kSoftFailure;
}
if (klass->GetStatus() == mirror::Class::kStatusResolved) {
@@ -4926,7 +4931,15 @@ bool ClassLinker::InitializeDefaultInterfaceRecursive(Thread* self,
// First we initialize all of iface's super-interfaces recursively.
for (size_t i = 0; i < num_direct_ifaces; i++) {
ObjPtr<mirror::Class> super_iface = mirror::Class::GetDirectInterface(self, iface.Get(), i);
- DCHECK(super_iface != nullptr);
+ if (UNLIKELY(super_iface == nullptr)) {
+ const char* iface_descriptor =
+ iface->GetDexFile().StringByTypeIdx(iface->GetDirectInterfaceTypeIdx(i));
+ LOG(FATAL) << "Check failed: super_iface != nullptr "
+ << "Debug data for bug 34839984: "
+ << iface->PrettyDescriptor() << " iface #" << i << " " << iface_descriptor
+ << " space: " << DescribeSpace(iface.Get())
+ << " loaders: " << DescribeLoaders(iface.Get(), iface_descriptor);
+ }
if (!super_iface->HasBeenRecursivelyInitialized()) {
// Recursive step
handle_super_iface.Assign(super_iface);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 62acedfb1b..8b2a2b4d13 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1262,6 +1262,7 @@ void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_loca
for (size_t i = 0; i < info->number_of_inline_caches_; ++i) {
std::vector<ProfileMethodInfo::ProfileClassReference> profile_classes;
const InlineCache& cache = info->cache_[i];
+ bool is_missing_types = false;
for (size_t k = 0; k < InlineCache::kIndividualCacheSize; k++) {
mirror::Class* cls = cache.classes_[k].Read();
if (cls == nullptr) {
@@ -1284,17 +1285,20 @@ void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_loca
}
if (!type_index.IsValid()) {
// Could be a proxy class or an array for which we couldn't find the type index.
+ is_missing_types = true;
continue;
}
if (ContainsElement(dex_base_locations, class_dex_file->GetBaseLocation())) {
// Only consider classes from the same apk (including multidex).
profile_classes.emplace_back(/*ProfileMethodInfo::ProfileClassReference*/
class_dex_file, type_index);
+ } else {
+ is_missing_types = true;
}
}
if (!profile_classes.empty()) {
inline_caches.emplace_back(/*ProfileMethodInfo::ProfileInlineCache*/
- cache.dex_pc_, profile_classes);
+ cache.dex_pc_, is_missing_types, profile_classes);
}
}
methods.emplace_back(/*ProfileMethodInfo*/
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 8c5bb0d775..b23a86313f 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -47,16 +47,19 @@ static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
// using the same test profile.
static constexpr bool kDebugIgnoreChecksum = false;
-static constexpr uint8_t kMegamorphicEncoding = 7;
+static constexpr uint8_t kIsMissingTypesEncoding = 6;
+static constexpr uint8_t kIsMegamorphicEncoding = 7;
static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t),
"InlineCache::kIndividualCacheSize does not have the expect type size");
-static_assert(InlineCache::kIndividualCacheSize < kMegamorphicEncoding,
+static_assert(InlineCache::kIndividualCacheSize < kIsMegamorphicEncoding,
+ "InlineCache::kIndividualCacheSize is larger than expected");
+static_assert(InlineCache::kIndividualCacheSize < kIsMissingTypesEncoding,
"InlineCache::kIndividualCacheSize is larger than expected");
void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
const dex::TypeIndex& type_idx) {
- if (is_megamorphic) {
+ if (is_megamorphic || is_missing_types) {
return;
}
classes.emplace(dex_profile_idx, type_idx);
@@ -207,7 +210,8 @@ static constexpr size_t kLineHeaderSize =
* Classes are grouped per their dex files and the line
* `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the
* mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...`
- * M stands for megamorphic and it's encoded as the byte kMegamorphicEncoding.
+ * M stands for megamorphic or missing types and it's encoded as either
+ * the byte kIsMegamorphicEncoding or kIsMissingTypesEncoding.
* When present, there will be no class ids following.
**/
bool ProfileCompilationInfo::Save(int fd) {
@@ -298,10 +302,19 @@ void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer
// Add the dex pc.
AddUintToBuffer(buffer, dex_pc);
- if (dex_pc_data.is_megamorphic) {
- // Add the megamorphic encoding if needed and continue.
- // If megamorphic, we don't add the rest of the classes.
- AddUintToBuffer(buffer, kMegamorphicEncoding);
+ // Add the megamorphic/missing_types encoding if needed and continue.
+ // In either cases we don't add any classes to the profiles and so there's
+ // no point to continue.
+ // TODO(calin): in case we miss types there is still value to add the
+ // rest of the classes. They can be added without bumping the profile version.
+ if (dex_pc_data.is_missing_types) {
+ DCHECK(!dex_pc_data.is_megamorphic); // at this point the megamorphic flag should not be set.
+ DCHECK_EQ(classes.size(), 0u);
+ AddUintToBuffer(buffer, kIsMissingTypesEncoding);
+ continue;
+ } else if (dex_pc_data.is_megamorphic) {
+ DCHECK_EQ(classes.size(), 0u);
+ AddUintToBuffer(buffer, kIsMegamorphicEncoding);
continue;
}
@@ -412,11 +425,21 @@ bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
for (const auto& pmi_inline_cache_it : pmi.inline_caches) {
uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first;
const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second;
- auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(pmi_ic_dex_pc);
+ DexPcData& dex_pc_data = inline_cache_it->second.FindOrAdd(pmi_ic_dex_pc)->second;
+ if (dex_pc_data.is_missing_types || dex_pc_data.is_megamorphic) {
+ // We are already megamorphic or we are missing types; no point in going forward.
+ continue;
+ }
+
+ if (pmi_ic_dex_pc_data.is_missing_types) {
+ dex_pc_data.SetIsMissingTypes();
+ continue;
+ }
if (pmi_ic_dex_pc_data.is_megamorphic) {
- dex_pc_data_it->second.SetMegamorphic();
+ dex_pc_data.SetIsMegamorphic();
continue;
}
+
for (const ClassReference& class_ref : pmi_ic_dex_pc_data.classes) {
const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index];
DexFileData* class_dex_data = GetOrAddDexFileData(
@@ -425,7 +448,7 @@ bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
if (class_dex_data == nullptr) { // checksum mismatch
return false;
}
- dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index);
+ dex_pc_data.AddClass(class_dex_data->profile_index, class_ref.type_index);
}
}
return true;
@@ -441,6 +464,11 @@ bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
auto inline_cache_it = data->method_map.FindOrAdd(pmi.dex_method_index);
for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) {
+ if (cache.is_missing_types) {
+ auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(cache.dex_pc);
+ dex_pc_data_it->second.SetIsMissingTypes();
+ continue;
+ }
for (const ProfileMethodInfo::ProfileClassReference& class_ref : cache.classes) {
DexFileData* class_dex_data = GetOrAddDexFileData(
GetProfileDexFileKey(class_ref.dex_file->GetLocation()),
@@ -449,6 +477,10 @@ bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
return false;
}
auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(cache.dex_pc);
+ if (dex_pc_data_it->second.is_missing_types) {
+ // Don't bother adding classes if we are missing types.
+ break;
+ }
dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index);
}
}
@@ -487,8 +519,12 @@ bool ProfileCompilationInfo::ReadInlineCache(SafeBuffer& buffer,
READ_UINT(uint16_t, buffer, dex_pc, error);
READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error);
auto dex_pc_data_it = inline_cache->FindOrAdd(dex_pc);
- if (dex_to_classes_map_size == kMegamorphicEncoding) {
- dex_pc_data_it->second.SetMegamorphic();
+ if (dex_to_classes_map_size == kIsMissingTypesEncoding) {
+ dex_pc_data_it->second.SetIsMissingTypes();
+ continue;
+ }
+ if (dex_to_classes_map_size == kIsMegamorphicEncoding) {
+ dex_pc_data_it->second.SetIsMegamorphic();
continue;
}
for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) {
@@ -835,8 +871,10 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) {
uint16_t other_dex_pc = other_ic_it.first;
const ClassSet& other_class_set = other_ic_it.second.classes;
auto class_set = method_it->second.FindOrAdd(other_dex_pc);
- if (other_ic_it.second.is_megamorphic) {
- class_set->second.SetMegamorphic();
+ if (other_ic_it.second.is_missing_types) {
+ class_set->second.SetIsMissingTypes();
+ } else if (other_ic_it.second.is_megamorphic) {
+ class_set->second.SetIsMegamorphic();
} else {
for (const auto& class_it : other_class_set) {
class_set->second.AddClass(dex_profile_index_remap.Get(
@@ -999,8 +1037,10 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>*
os << "[";
for (const auto& inline_cache_it : method_it.second) {
os << "{" << std::hex << inline_cache_it.first << std::dec << ":";
- if (inline_cache_it.second.is_megamorphic) {
- os << "M";
+ if (inline_cache_it.second.is_missing_types) {
+ os << "MT";
+ } else if (inline_cache_it.second.is_megamorphic) {
+ os << "MM";
} else {
for (const ClassReference& class_ref : inline_cache_it.second.classes) {
os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index)
@@ -1136,7 +1176,7 @@ bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
}
// We can't use a simple equality test because we need to match the dex files
- // of the inline caches which might have different profile indices.
+ // of the inline caches which might have different profile indexes.
for (const auto& inline_cache_it : inline_caches) {
uint16_t dex_pc = inline_cache_it.first;
const DexPcData dex_pc_data = inline_cache_it.second;
@@ -1145,7 +1185,8 @@ bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
return false;
}
const DexPcData& other_dex_pc_data = other_it->second;
- if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic) {
+ if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic ||
+ dex_pc_data.is_missing_types != other_dex_pc_data.is_missing_types) {
return false;
}
for (const ClassReference& class_ref : dex_pc_data.classes) {
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index f089dff898..6ad528c805 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -45,10 +45,13 @@ struct ProfileMethodInfo {
};
struct ProfileInlineCache {
- ProfileInlineCache(uint32_t pc, const std::vector<ProfileClassReference>& profile_classes)
- : dex_pc(pc), classes(profile_classes) {}
+ ProfileInlineCache(uint32_t pc,
+ bool missing_types,
+ const std::vector<ProfileClassReference>& profile_classes)
+ : dex_pc(pc), is_missing_types(missing_types), classes(profile_classes) {}
const uint32_t dex_pc;
+ const bool is_missing_types;
const std::vector<ProfileClassReference> classes;
};
@@ -134,18 +137,30 @@ class ProfileCompilationInfo {
// Encodes the actual inline cache for a given dex pc (whether or not the receiver is
// megamorphic and its possible types).
- // If the receiver is megamorphic the set of classes will be empty.
+ // If the receiver is megamorphic or is missing types the set of classes will be empty.
struct DexPcData {
- DexPcData() : is_megamorphic(false) {}
+ DexPcData() : is_missing_types(false), is_megamorphic(false) {}
void AddClass(uint16_t dex_profile_idx, const dex::TypeIndex& type_idx);
- void SetMegamorphic() {
+ void SetIsMegamorphic() {
+ if (is_missing_types) return;
is_megamorphic = true;
classes.clear();
}
+ void SetIsMissingTypes() {
+ is_megamorphic = false;
+ is_missing_types = true;
+ classes.clear();
+ }
bool operator==(const DexPcData& other) const {
- return is_megamorphic == other.is_megamorphic && classes == other.classes;
+ return is_megamorphic == other.is_megamorphic &&
+ is_missing_types == other.is_missing_types &&
+ classes == other.classes;
}
+ // Not all runtime types can be encoded in the profile. For example if the receiver
+ // type is in a dex file which is not tracked for profiling its type cannot be
+ // encoded. When types are missing this field will be set to true.
+ bool is_missing_types;
bool is_megamorphic;
ClassSet classes;
};
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 332280a0e3..5cd8e8fef5 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -108,26 +108,31 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
for (ArtMethod* method : methods) {
std::vector<ProfileMethodInfo::ProfileInlineCache> caches;
// Monomorphic
- for (uint16_t dex_pc = 0; dex_pc < 1; dex_pc++) {
+ for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
std::vector<ProfileMethodInfo::ProfileClassReference> classes;
classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0));
- caches.emplace_back(dex_pc, classes);
+ caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
}
// Polymorphic
- for (uint16_t dex_pc = 1; dex_pc < 2; dex_pc++) {
+ for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
std::vector<ProfileMethodInfo::ProfileClassReference> classes;
for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) {
classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
}
- caches.emplace_back(dex_pc, classes);
+ caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
}
// Megamorphic
- for (uint16_t dex_pc = 2; dex_pc < 3; dex_pc++) {
+ for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
std::vector<ProfileMethodInfo::ProfileClassReference> classes;
for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) {
classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
}
- caches.emplace_back(dex_pc, classes);
+ caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
+ }
+ // Missing types
+ for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
+ std::vector<ProfileMethodInfo::ProfileClassReference> classes;
+ caches.emplace_back(dex_pc, /*is_missing_types*/true, classes);
}
ProfileMethodInfo pmi(method->GetDexFile(), method->GetDexMethodIndex(), caches);
profile_methods.push_back(pmi);
@@ -148,12 +153,15 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi;
SafeMap<DexFile*, uint8_t> dex_map; // dex files to profile index
for (const auto& inline_cache : pmi.inline_caches) {
+ ProfileCompilationInfo::DexPcData& dex_pc_data =
+ offline_pmi.inline_caches.FindOrAdd(inline_cache.dex_pc)->second;
+ if (inline_cache.is_missing_types) {
+ dex_pc_data.SetIsMissingTypes();
+ }
for (const auto& class_ref : inline_cache.classes) {
uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file),
static_cast<uint8_t>(dex_map.size()))->second;
- offline_pmi.inline_caches
- .FindOrAdd(inline_cache.dex_pc)->second
- .AddClass(dex_profile_index, class_ref.type_index);
+ dex_pc_data.AddClass(dex_profile_index, class_ref.type_index);
if (dex_profile_index >= offline_pmi.dex_references.size()) {
// This is a new dex.
const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey(
@@ -170,18 +178,18 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() {
ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
- pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
- pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2);
- pmi.dex_references.emplace_back("dex_location3", /* checksum */ 3);
+ pmi.dex_references.emplace_back("dex_location1", /* checksum */1);
+ pmi.dex_references.emplace_back("dex_location2", /* checksum */2);
+ pmi.dex_references.emplace_back("dex_location3", /* checksum */3);
// Monomorphic
- for (uint16_t dex_pc = 0; dex_pc < 10; dex_pc++) {
+ for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
ProfileCompilationInfo::DexPcData dex_pc_data;
dex_pc_data.AddClass(0, dex::TypeIndex(0));
pmi.inline_caches.Put(dex_pc, dex_pc_data);
}
// Polymorphic
- for (uint16_t dex_pc = 10; dex_pc < 20; dex_pc++) {
+ for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
ProfileCompilationInfo::DexPcData dex_pc_data;
dex_pc_data.AddClass(0, dex::TypeIndex(0));
dex_pc_data.AddClass(1, dex::TypeIndex(1));
@@ -190,9 +198,15 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
pmi.inline_caches.Put(dex_pc, dex_pc_data);
}
// Megamorphic
- for (uint16_t dex_pc = 20; dex_pc < 30; dex_pc++) {
+ for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data;
+ dex_pc_data.SetIsMegamorphic();
+ pmi.inline_caches.Put(dex_pc, dex_pc_data);
+ }
+ // Missing types
+ for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
ProfileCompilationInfo::DexPcData dex_pc_data;
- dex_pc_data.is_megamorphic = true;
+ dex_pc_data.SetIsMissingTypes();
pmi.inline_caches.Put(dex_pc, dex_pc_data);
}
@@ -207,7 +221,13 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
}
}
- // Cannot sizeof the actual arrays so hardcode the values here.
+ void SetIsMissingTypes(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) {
+ for (auto it : pmi->inline_caches) {
+ it.second.SetIsMissingTypes();
+ }
+ }
+
+ // Cannot sizeof the actual arrays so hard code the values here.
// They should not change anyway.
static constexpr int kProfileMagicSize = 4;
static constexpr int kProfileVersionSize = 4;
@@ -530,6 +550,58 @@ TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) {
ASSERT_TRUE(loaded_pmi1 == pmi_extra);
}
+TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCaches) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
+
+ // Add methods with inline caches.
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
+ }
+
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Make some inline caches megamorphic and add them to the profile again.
+ ProfileCompilationInfo saved_info_extra;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo();
+ MakeMegamorphic(&pmi_extra);
+ for (uint16_t method_idx = 5; method_idx < 10; method_idx++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
+ }
+
+ // Mark all inline caches with missing types and add them to the profile again.
+ // This will verify that all inline caches (megamorphic or not) should be marked as missing types.
+ ProfileCompilationInfo::OfflineProfileMethodInfo missing_types = GetOfflineProfileMethodInfo();
+ SetIsMissingTypes(&missing_types);
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
+ }
+
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(saved_info_extra.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Merge the profiles so that we have the same view as the file.
+ ASSERT_TRUE(saved_info.MergeWith(saved_info_extra));
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+
+ ASSERT_TRUE(loaded_info.Equals(saved_info));
+
+ ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1;
+ ASSERT_TRUE(loaded_info.GetMethod("dex_location1",
+ /* checksum */ 1,
+ /* method_idx */ 3,
+ &loaded_pmi1));
+ ASSERT_TRUE(loaded_pmi1 == pmi_extra);
+}
+
TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
ScratchFile profile;
@@ -570,7 +642,7 @@ TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
}
}
-TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCahce) {
+TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCache) {
ScratchFile profile;
ProfileCompilationInfo info;
@@ -662,7 +734,7 @@ TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) {
ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
ProfileCompilationInfo::DexPcData dex_pc_data;
- dex_pc_data.is_megamorphic = true;
+ dex_pc_data.SetIsMegamorphic();
pmi.inline_caches.Put(/*dex_pc*/ 0, dex_pc_data);
ProfileCompilationInfo info_megamorphic;
@@ -686,4 +758,33 @@ TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) {
ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile)));
}
+TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCachesMerge) {
+ // Create an inline cache with missing types
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
+ pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
+ ProfileCompilationInfo::DexPcData dex_pc_data;
+ dex_pc_data.SetIsMissingTypes();
+ pmi.inline_caches.Put(/*dex_pc*/ 0, dex_pc_data);
+
+ ProfileCompilationInfo info_megamorphic;
+ ASSERT_TRUE(AddMethod("dex_location1",
+ /*checksum*/ 1,
+ /*method_idx*/ 0,
+ pmi,
+ &info_megamorphic));
+
+ // Create a profile with no inline caches (for the same method).
+ ProfileCompilationInfo info_no_inline_cache;
+ ASSERT_TRUE(AddMethod("dex_location1",
+ /*checksum*/ 1,
+ /*method_idx*/ 0,
+ &info_no_inline_cache));
+
+ // Merge the missing type cache into the empty one.
+ // Everything should be saved without errors.
+ ASSERT_TRUE(info_no_inline_cache.MergeWith(info_megamorphic));
+ ScratchFile profile;
+ ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile)));
+}
+
} // namespace art
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index d34f09c721..b68eedcb11 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -206,10 +206,10 @@ class MANAGED Class FINAL : public Object {
return status >= kStatusResolved || status == kStatusErrorResolved;
}
- // Returns true if the class was compile-time verified.
+ // Returns true if the class should be verified at runtime.
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- bool IsCompileTimeVerified() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetStatus<kVerifyFlags>() >= kStatusRetryVerificationAtRuntime;
+ bool ShouldVerifyAtRuntime() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetStatus<kVerifyFlags>() == kStatusRetryVerificationAtRuntime;
}
// Returns true if the class has been verified.
diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java
index ad3ff448d0..2d9daf1d43 100644
--- a/test/618-checker-induction/src/Main.java
+++ b/test/618-checker-induction/src/Main.java
@@ -21,6 +21,8 @@ public class Main {
static int[] a = new int[10];
+ static int[] novec = new int[20]; // to prevent vectorization
+
/// CHECK-START: void Main.deadSingleLoop() loop_optimization (before)
/// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
//
@@ -132,16 +134,18 @@ public class Main {
/// CHECK-START: void Main.deadInduction() loop_optimization (before)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
/// CHECK-START: void Main.deadInduction() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
static void deadInduction() {
int dead = 0;
for (int i = 0; i < a.length; i++) {
- a[i] = 1;
+ a[i] = novec[2 * i] + 1;
dead += 5;
}
}
@@ -151,17 +155,19 @@ public class Main {
/// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
/// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
/// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
/// CHECK-START: void Main.deadManyInduction() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
static void deadManyInduction() {
int dead1 = 0, dead2 = 1, dead3 = 3;
for (int i = 0; i < a.length; i++) {
dead1 += 5;
- a[i] = 2;
+ a[i] = novec[2 * i] + 2;
dead2 += 10;
dead3 += 100;
}
@@ -170,16 +176,18 @@ public class Main {
/// CHECK-START: void Main.deadSequence() loop_optimization (before)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
/// CHECK-START: void Main.deadSequence() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
static void deadSequence() {
int dead = 0;
for (int i = 0; i < a.length; i++) {
- a[i] = 3;
+ a[i] = novec[2 * i] + 3;
// Increment value defined inside loop,
// but sequence itself not used anywhere.
dead += i;
@@ -191,17 +199,19 @@ public class Main {
/// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-NOT: BoundsCheck
//
/// CHECK-START: void Main.deadCycleWithException(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none
static void deadCycleWithException(int k) {
int dead = 0;
for (int i = 0; i < a.length; i++) {
- a[i] = 4;
+ a[i] = novec[2 * i] + 4;
// Increment value of dead cycle may throw exception. Dynamic
// BCE takes care of the bounds check though, which enables
// removing the ArrayGet after removing the dead cycle.
diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java
index 7509d9b4f3..eee90ab285 100644
--- a/test/623-checker-loop-regressions/src/Main.java
+++ b/test/623-checker-loop-regressions/src/Main.java
@@ -213,6 +213,8 @@ public class Main {
/// CHECK-START: long Main.geoLongDivLastValue(long) instruction_simplifier$after_bce (after)
/// CHECK-DAG: <<Long:j\d+>> LongConstant 0 loop:none
/// CHECK-DAG: Return [<<Long>>] loop:none
+ //
+ // Tests overflow in the divisor (while updating intermediate result).
static long geoLongDivLastValue(long x) {
for (int i = 0; i < 10; i++) {
x /= 1081788608;
@@ -220,6 +222,26 @@ public class Main {
return x;
}
+ /// CHECK-START: long Main.geoLongDivLastValue() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: long Main.geoLongDivLastValue() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: long Main.geoLongDivLastValue() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Long:j\d+>> LongConstant 0 loop:none
+ /// CHECK-DAG: Return [<<Long>>] loop:none
+ //
+ // Tests overflow in the divisor (while updating base).
+ static long geoLongDivLastValue() {
+ long x = -1;
+ for (int i2 = 0; i2 < 2; i2++) {
+ x /= (Long.MAX_VALUE);
+ }
+ return x;
+ }
+
/// CHECK-START: long Main.geoLongMulLastValue(long) loop_optimization (before)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
@@ -239,6 +261,15 @@ public class Main {
return x;
}
+ // If vectorized, the narrowing subscript should not cause
+ // type inconsistencies in the synthesized code.
+ static void narrowingSubscript(float[] a) {
+ float val = 2.0f;
+ for (long i = 0; i < a.length; i++) {
+ a[(int) i] += val;
+ }
+ }
+
public static void main(String[] args) {
expectEquals(10, earlyExitFirst(-1));
for (int i = 0; i <= 10; i++) {
@@ -286,6 +317,8 @@ public class Main {
expectEquals(0L, geoLongDivLastValue(9223372036854775807L));
expectEquals(0L, geoLongDivLastValue(-9223372036854775808L));
+ expectEquals(0L, geoLongDivLastValue());
+
expectEquals( 0L, geoLongMulLastValue(0L));
expectEquals(-8070450532247928832L, geoLongMulLastValue(1L));
expectEquals( 2305843009213693952L, geoLongMulLastValue(2L));
@@ -296,6 +329,12 @@ public class Main {
expectEquals( 8070450532247928832L, geoLongMulLastValue(9223372036854775807L));
expectEquals( 0L, geoLongMulLastValue(-9223372036854775808L));
+ float[] a = new float[16];
+ narrowingSubscript(a);
+ for (int i = 0; i < 16; i++) {
+ expectEquals(2.0f, a[i]);
+ }
+
System.out.println("passed");
}
@@ -310,4 +349,10 @@ public class Main {
throw new Error("Expected: " + expected + ", found: " + result);
}
}
+
+ private static void expectEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
}
diff --git a/test/641-checker-arraycopy/build b/test/641-checker-arraycopy/build
new file mode 100644
index 0000000000..9abc61897a
--- /dev/null
+++ b/test/641-checker-arraycopy/build
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+# Don't use jack for this test, to ensure we don't use
+# the typed System.arraycopy versions directly.
+export USE_JACK=false
+
+./default-build
diff --git a/test/641-checker-arraycopy/expected.txt b/test/641-checker-arraycopy/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/641-checker-arraycopy/expected.txt
diff --git a/test/641-checker-arraycopy/info.txt b/test/641-checker-arraycopy/info.txt
new file mode 100644
index 0000000000..1a1111e145
--- /dev/null
+++ b/test/641-checker-arraycopy/info.txt
@@ -0,0 +1,2 @@
+Checker test for testing the arraycopy optimization in
+instruction simplifier.
diff --git a/test/641-checker-arraycopy/src/Main.java b/test/641-checker-arraycopy/src/Main.java
new file mode 100644
index 0000000000..f0fcf28d23
--- /dev/null
+++ b/test/641-checker-arraycopy/src/Main.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ // Note that this is testing we haven't intrinsified the byte[] arraycopy version.
+ // Once we eventually start doing it, we will need to re-adjust this test.
+
+ /// CHECK-START-X86: void Main.typedCopy(java.lang.Object, byte[]) disassembly (after)
+ /// CHECK: InvokeStaticOrDirect method_name:java.lang.System.arraycopy intrinsic:SystemArrayCopy
+ /// CHECK-NOT: call
+ /// CHECK: InvokeStaticOrDirect method_name:java.lang.System.arraycopy intrinsic:SystemArrayCopy
+ /// CHECK: call
+ /// CHECK: ReturnVoid
+ public static void typedCopy(Object o, byte[] foo) {
+ System.arraycopy(o, 1, o, 0, 1);
+ System.arraycopy(foo, 1, foo, 0, 1);
+ }
+
+ public static void untypedCopy(Object o, Object foo) {
+ System.arraycopy(o, 1, o, 0, 1);
+ System.arraycopy(foo, 1, foo, 0, 1);
+ }
+
+ // Test that we still do the optimization after inlining.
+
+ /// CHECK-START-X86: void Main.untypedCopyCaller(java.lang.Object, byte[]) disassembly (after)
+ /// CHECK: InvokeStaticOrDirect method_name:java.lang.System.arraycopy intrinsic:SystemArrayCopy
+ /// CHECK-NOT: call
+ /// CHECK: InvokeStaticOrDirect method_name:java.lang.System.arraycopy intrinsic:SystemArrayCopy
+ /// CHECK: call
+ /// CHECK: ReturnVoid
+ public static void untypedCopyCaller(Object o, byte[] array) {
+ untypedCopy(o, array);
+ }
+
+ public static void assertEquals(Object one, Object two) {
+ if (one != two) {
+ throw new Error("Expected " + one + ", got " + two);
+ }
+ }
+
+ public static void main(String[] args) {
+ // Simple sanity checks.
+ byte[] a = new byte[2];
+ Object[] o = new Object[2];
+
+ o[0] = a;
+ o[1] = o;
+ a[0] = 1;
+ a[1] = 2;
+
+ untypedCopyCaller(o, a);
+ assertEquals(o[0], o);
+ assertEquals(o[1], o);
+ assertEquals(a[0], (byte)2);
+ assertEquals(a[1], (byte)2);
+
+ o[0] = a;
+ o[1] = o;
+ a[0] = 1;
+ a[1] = 2;
+
+ typedCopy(o, a);
+ assertEquals(o[0], o);
+ assertEquals(o[1], o);
+ assertEquals(a[0], (byte)2);
+ assertEquals(a[1], (byte)2);
+ }
+}
diff --git a/test/641-iterations/expected.txt b/test/641-iterations/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/641-iterations/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/641-iterations/info.txt b/test/641-iterations/info.txt
new file mode 100644
index 0000000000..fd8059561c
--- /dev/null
+++ b/test/641-iterations/info.txt
@@ -0,0 +1 @@
+Tests on varying trip counts (to validate vector/cleanup loops).
diff --git a/test/641-iterations/src/Main.java b/test/641-iterations/src/Main.java
new file mode 100644
index 0000000000..6a27f80286
--- /dev/null
+++ b/test/641-iterations/src/Main.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Tests of varying trip counts. Focused on testing
+ * core and cleanup loop after vectorization.
+ */
+public class Main {
+
+ static int[] sA;
+
+ static void init() {
+ for (int i = 0; i < sA.length; i++)
+ sA[i] = 100;
+ }
+
+ static void doitTo(int n) {
+ for (int i = 0; i < n; i++)
+ sA[i] += 1;
+ }
+
+ static void doitFrom(int n) {
+ for (int i = n; i < sA.length; i++)
+ sA[i] += 1;
+ }
+
+ static void verify(int n) {
+ for (int i = 0; i < n; i++)
+ if (sA[i] != 101)
+ throw new Error("failed inside loop");
+ for (int i = n; i < sA.length; i++)
+ if (sA[i] != 100)
+ throw new Error("failed outside loop");
+ }
+
+ static void verify() {
+ for (int i = 0; i < sA.length; i++)
+ if (sA[i] != 101)
+ throw new Error("failed inside loop");
+ }
+
+ static void driver() {
+ for (int n = 0; n <= sA.length; n++) {
+ init();
+ doitTo(n);
+ verify(n);
+ doitFrom(n);
+ verify();
+ }
+ }
+
+ public static void main(String[] args) {
+ sA = new int[17];
+ driver();
+ sA = new int[32];
+ driver();
+ System.out.println("passed");
+ }
+}
+
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index bb0d51ec51..01eb14eda2 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -624,16 +624,18 @@ TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS :=
TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS :=
# Tests failing in non-Baker read barrier configurations with the Optimizing compiler (AOT).
-# 537: Expects an array copy to be intrinsified, but calling-on-slowpath intrinsics are not yet
+# 537 and 641: Expect an array copy to be intrinsified, but calling-on-slowpath intrinsics are not yet
# handled in non-Baker read barrier configurations.
TEST_ART_BROKEN_OPTIMIZING_NON_BAKER_READ_BARRIER_RUN_TESTS := \
- 537-checker-arraycopy
+ 537-checker-arraycopy \
+ 641-checker-arraycopy
# Tests failing in non-Baker read barrier configurations with JIT (Optimizing compiler).
-# 537: Expects an array copy to be intrinsified, but calling-on-slowpath intrinsics are not yet
+# 537 and 641: Expect an array copy to be intrinsified, but calling-on-slowpath intrinsics are not yet
# handled in non-Baker read barrier configurations.
TEST_ART_BROKEN_JIT_NON_BAKER_READ_BARRIER_RUN_TESTS := \
- 537-checker-arraycopy
+ 537-checker-arraycopy \
+ 641-checker-arraycopy
ifeq ($(ART_USE_READ_BARRIER),true)
ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
diff --git a/test/ProfileTestMultiDex/Main.java b/test/ProfileTestMultiDex/Main.java
index 73fbb00d25..a8ced544c9 100644
--- a/test/ProfileTestMultiDex/Main.java
+++ b/test/ProfileTestMultiDex/Main.java
@@ -39,6 +39,10 @@ class TestInline {
return s.getValue();
}
+ public int inlineMissingTypes(Super s) {
+ return s.getValue();
+ }
+
public int noInlineCache(Super s) {
return s.getValue();
}
diff --git a/test/VerifierDeps/MySub1SoftVerificationFailure.smali b/test/VerifierDeps/MySub1SoftVerificationFailure.smali
new file mode 100644
index 0000000000..8123394e87
--- /dev/null
+++ b/test/VerifierDeps/MySub1SoftVerificationFailure.smali
@@ -0,0 +1,16 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LMySub1SoftVerificationFailure;
+.super LMySoftVerificationFailure;
diff --git a/test/VerifierDeps/MySub2SoftVerificationFailure.smali b/test/VerifierDeps/MySub2SoftVerificationFailure.smali
new file mode 100644
index 0000000000..8d003236c8
--- /dev/null
+++ b/test/VerifierDeps/MySub2SoftVerificationFailure.smali
@@ -0,0 +1,16 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LMySub2SoftVerificationFailure;
+.super LMySoftVerificationFailure;
diff --git a/test/VerifierDepsMulti/MySoftVerificationFailure.smali b/test/VerifierDepsMulti/MySoftVerificationFailure.smali
new file mode 100644
index 0000000000..6b56a3b6dc
--- /dev/null
+++ b/test/VerifierDepsMulti/MySoftVerificationFailure.smali
@@ -0,0 +1,24 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LMySoftVerificationFailure;
+.super Ljava/lang/Object;
+
+.method public final foo()V
+ .registers 1
+ sget-object v0, LMySoftVerificationFailure;->error:LUnknownType;
+ throw v0
+.end method
+
+.field public static error:LUnknownType;
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index fb005984d0..1473149000 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -455,11 +455,12 @@ if [ "$RELOCATE" = "y" ]; then
else
FLAGS="$FLAGS -Xnorelocate"
COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xnorelocate"
- if [ "$HOST" = "y" ]; then
- # Increase ulimit to 128MB in case we are running hprof test,
- # or string append test with art-debug-gc.
- ulimit -S 128000 || exit 1
- fi
+fi
+
+if [ "$HOST" = "y" ]; then
+ # Increase ulimit to 128MB in case we are running hprof test,
+ # or string append test with art-debug-gc.
+ ulimit -S 128000 || exit 1
fi
if [ "$HOST" = "n" ]; then
diff --git a/test/knownfailures.json b/test/knownfailures.json
index f2f90353ac..50d70f18dc 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -297,7 +297,7 @@
"tests": ["000-nop",
"134-nodex2oat-nofallback",
"147-stripped-dex-fallback",
- "595-profile-saving"],
+ "595-profile-saving"],
"description": "The doesn't compile anything",
"env_vars": {"ART_TEST_BISECTION": "true"},
"variant": "optimizing | regalloc_gc"
@@ -322,7 +322,7 @@
},
{
"tests": ["115-native-bridge",
- "088-monitor-verification"],
+ "088-monitor-verification"],
"description": "The test assume they are always compiled.",
"env_vars": {"ART_TEST_BISECTION": "true"},
"variant": "optimizing | regalloc_gc"
@@ -335,7 +335,8 @@
"variant": "optimizing | regalloc_gc"
},
{
- "test": "537-checker-arraycopy",
+ "tests": ["537-checker-arraycopy",
+ "641-checker-arraycopy"],
"env_vars": {"ART_USE_READ_BARRIER": "true"},
"variant": "interpreter | optimizing | regalloc_gc | jit"
}