summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/graph_checker.cc6
-rw-r--r--compiler/optimizing/inliner.cc8
-rw-r--r--compiler/optimizing/nodes.h23
-rw-r--r--compiler/optimizing/optimizing_compiler.cc7
-rw-r--r--compiler/optimizing/primitive_type_propagation.cc2
-rw-r--r--compiler/optimizing/ssa_builder.cc227
-rw-r--r--compiler/optimizing/ssa_builder.h20
-rw-r--r--test/457-regs/expected.txt0
-rw-r--r--test/457-regs/info.txt1
-rw-r--r--test/457-regs/regs_jni.cc150
-rw-r--r--test/457-regs/smali/PhiLiveness.smali82
-rw-r--r--test/457-regs/src/Main.java45
-rw-r--r--test/Android.libarttest.mk3
-rw-r--r--test/Android.run-test.mk15
14 files changed, 556 insertions, 33 deletions
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index a7f1f74e27..76b9f4fe7e 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -362,6 +362,12 @@ void SSAChecker::VisitPhi(HPhi* phi) {
Primitive::PrettyDescriptor(phi->GetType())));
}
}
+ if (phi->GetType() != HPhi::ToPhiType(phi->GetType())) {
+ AddError(StringPrintf("Phi %d in block %d does not have an expected phi type: %s",
+ phi->GetId(),
+ phi->GetBlock()->GetBlockId(),
+ Primitive::PrettyDescriptor(phi->GetType())));
+ }
}
void SSAChecker::VisitIf(HIf* instruction) {
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index b34957a17e..e22f7ccbf1 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -124,8 +124,8 @@ bool HInliner::TryInline(HInvoke* invoke_instruction,
resolved_method->GetAccessFlags(),
nullptr);
- HGraph* callee_graph =
- new (graph_->GetArena()) HGraph(graph_->GetArena(), graph_->GetCurrentInstructionId());
+ HGraph* callee_graph = new (graph_->GetArena()) HGraph(
+ graph_->GetArena(), graph_->IsDebuggable(), graph_->GetCurrentInstructionId());
OptimizingCompilerStats inline_stats;
HGraphBuilder builder(callee_graph,
@@ -155,15 +155,11 @@ bool HInliner::TryInline(HInvoke* invoke_instruction,
}
// Run simple optimizations on the graph.
- SsaRedundantPhiElimination redundant_phi(callee_graph);
- SsaDeadPhiElimination dead_phi(callee_graph);
HDeadCodeElimination dce(callee_graph);
HConstantFolding fold(callee_graph);
InstructionSimplifier simplify(callee_graph, stats_);
HOptimization* optimizations[] = {
- &redundant_phi,
- &dead_phi,
&dce,
&fold,
&simplify,
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 8b56166610..942aa2374b 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -103,7 +103,7 @@ class HInstructionList {
// Control-flow graph of a method. Contains a list of basic blocks.
class HGraph : public ArenaObject<kArenaAllocMisc> {
public:
- HGraph(ArenaAllocator* arena, int start_instruction_id = 0)
+ HGraph(ArenaAllocator* arena, bool debuggable = false, int start_instruction_id = 0)
: arena_(arena),
blocks_(arena, kDefaultNumberOfBlocks),
reverse_post_order_(arena, kDefaultNumberOfBlocks),
@@ -114,6 +114,7 @@ class HGraph : public ArenaObject<kArenaAllocMisc> {
number_of_in_vregs_(0),
temporaries_vreg_slots_(0),
has_array_accesses_(false),
+ debuggable_(debuggable),
current_instruction_id_(start_instruction_id) {}
ArenaAllocator* GetArena() const { return arena_; }
@@ -208,6 +209,8 @@ class HGraph : public ArenaObject<kArenaAllocMisc> {
has_array_accesses_ = value;
}
+ bool IsDebuggable() const { return debuggable_; }
+
HNullConstant* GetNullConstant();
private:
@@ -248,6 +251,11 @@ class HGraph : public ArenaObject<kArenaAllocMisc> {
// Has array accesses. We can totally skip BCE if it's false.
bool has_array_accesses_;
+ // Indicates whether the graph should be compiled in a way that
+ // ensures full debuggability. If false, we can apply more
+ // aggressive optimizations that may limit the level of debugging.
+ const bool debuggable_;
+
// The current id to assign to a newly added instruction. See HInstruction.id_.
int32_t current_instruction_id_;
@@ -2498,6 +2506,19 @@ class HPhi : public HInstruction {
inputs_.SetSize(number_of_inputs);
}
+ // Returns a type equivalent to the given `type`, but that a `HPhi` can hold.
+ static Primitive::Type ToPhiType(Primitive::Type type) {
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ return Primitive::kPrimInt;
+ default:
+ return type;
+ }
+ }
+
size_t InputCount() const OVERRIDE { return inputs_.Size(); }
void AddInput(HInstruction* input);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index eb984248a9..3470595b2c 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -298,8 +298,6 @@ static void RunOptimizations(HGraph* graph,
const DexCompilationUnit& dex_compilation_unit,
PassInfoPrinter* pass_info_printer,
StackHandleScopeCollection* handles) {
- SsaRedundantPhiElimination redundant_phi(graph);
- SsaDeadPhiElimination dead_phi(graph);
HDeadCodeElimination dce(graph);
HConstantFolding fold1(graph);
InstructionSimplifier simplify1(graph, stats);
@@ -317,8 +315,6 @@ static void RunOptimizations(HGraph* graph,
IntrinsicsRecognizer intrinsics(graph, dex_compilation_unit.GetDexFile(), driver);
HOptimization* optimizations[] = {
- &redundant_phi,
- &dead_phi,
&intrinsics,
&dce,
&fold1,
@@ -461,7 +457,8 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
ArenaPool pool;
ArenaAllocator arena(&pool);
- HGraph* graph = new (&arena) HGraph(&arena);
+ HGraph* graph = new (&arena) HGraph(
+ &arena, compiler_driver->GetCompilerOptions().GetDebuggable());
// For testing purposes, we put a special marker on method names that should be compiled
// with this compiler. This makes sure we're not regressing.
diff --git a/compiler/optimizing/primitive_type_propagation.cc b/compiler/optimizing/primitive_type_propagation.cc
index fe23fcf326..c20c8a172d 100644
--- a/compiler/optimizing/primitive_type_propagation.cc
+++ b/compiler/optimizing/primitive_type_propagation.cc
@@ -33,7 +33,7 @@ static Primitive::Type MergeTypes(Primitive::Type existing, Primitive::Type new_
// to merge with a void type, we should use the existing one.
return new_type == Primitive::kPrimVoid
? existing
- : new_type;
+ : HPhi::ToPhiType(new_type);
}
}
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 3dc75059b2..1a8e784363 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -22,6 +22,158 @@
namespace art {
+/**
+ * A debuggable application may require to reviving phis, to ensure their
+ * associated DEX register is available to a debugger. This class implements
+ * the logic for statement (c) of the SsaBuilder (see ssa_builder.h). It
+ * also makes sure that phis with incompatible input types are not revived
+ * (statement (b) of the SsaBuilder).
+ *
+ * This phase must be run after detecting dead phis through the
+ * DeadPhiElimination phase, and before deleting the dead phis.
+ */
+class DeadPhiHandling : public ValueObject {
+ public:
+ explicit DeadPhiHandling(HGraph* graph)
+ : graph_(graph), worklist_(graph->GetArena(), kDefaultWorklistSize) {}
+
+ void Run();
+
+ private:
+ void VisitBasicBlock(HBasicBlock* block);
+ void ProcessWorklist();
+ void AddToWorklist(HPhi* phi);
+ void AddDependentInstructionsToWorklist(HPhi* phi);
+ bool UpdateType(HPhi* phi);
+
+ HGraph* const graph_;
+ GrowableArray<HPhi*> worklist_;
+
+ static constexpr size_t kDefaultWorklistSize = 8;
+
+ DISALLOW_COPY_AND_ASSIGN(DeadPhiHandling);
+};
+
+bool DeadPhiHandling::UpdateType(HPhi* phi) {
+ Primitive::Type existing = phi->GetType();
+ DCHECK(phi->IsLive());
+
+ bool conflict = false;
+ Primitive::Type new_type = existing;
+ for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
+ HInstruction* input = phi->InputAt(i);
+ if (input->IsPhi() && input->AsPhi()->IsDead()) {
+ // We are doing a reverse post order visit of the graph, reviving
+ // phis that have environment uses and updating their types. If an
+ // input is a phi, and it is dead (because its input types are
+ // conflicting), this phi must be marked dead as well.
+ conflict = true;
+ break;
+ }
+ Primitive::Type input_type = HPhi::ToPhiType(input->GetType());
+
+ // The only acceptable transitions are:
+ // - From void to typed: first time we update the type of this phi.
+ // - From int to reference (or reference to int): the phi has to change
+ // to reference type. If the integer input cannot be converted to a
+ // reference input, the phi will remain dead.
+ if (new_type == Primitive::kPrimVoid) {
+ new_type = input_type;
+ } else if (new_type == Primitive::kPrimNot && input_type == Primitive::kPrimInt) {
+ HInstruction* equivalent = SsaBuilder::GetReferenceTypeEquivalent(input);
+ if (equivalent == nullptr) {
+ conflict = true;
+ break;
+ } else {
+ phi->ReplaceInput(equivalent, i);
+ if (equivalent->IsPhi()) {
+ DCHECK_EQ(equivalent->GetType(), Primitive::kPrimNot);
+ // We created a new phi, but that phi has the same inputs as the old phi. We
+ // add it to the worklist to ensure its inputs can also be converted to reference.
+ // If not, it will remain dead, and the algorithm will make the current phi dead
+ // as well.
+ equivalent->AsPhi()->SetLive();
+ AddToWorklist(equivalent->AsPhi());
+ }
+ }
+ } else if (new_type == Primitive::kPrimInt && input_type == Primitive::kPrimNot) {
+ new_type = Primitive::kPrimNot;
+ // Start over, we may request reference equivalents for the inputs of the phi.
+ i = -1;
+ } else if (new_type != input_type) {
+ conflict = true;
+ break;
+ }
+ }
+
+ if (conflict) {
+ phi->SetType(Primitive::kPrimVoid);
+ phi->SetDead();
+ return true;
+ } else {
+ DCHECK(phi->IsLive());
+ phi->SetType(new_type);
+ return existing != new_type;
+ }
+}
+
+void DeadPhiHandling::VisitBasicBlock(HBasicBlock* block) {
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->AsPhi();
+ if (phi->IsDead() && phi->HasEnvironmentUses()) {
+ phi->SetLive();
+ if (block->IsLoopHeader()) {
+ // Give a type to the loop phi, to guarantee convergence of the algorithm.
+ phi->SetType(phi->InputAt(0)->GetType());
+ AddToWorklist(phi);
+ } else {
+ // Because we are doing a reverse post order visit, all inputs of
+ // this phi have been visited and therefore had their (initial) type set.
+ UpdateType(phi);
+ }
+ }
+ }
+}
+
+void DeadPhiHandling::ProcessWorklist() {
+ while (!worklist_.IsEmpty()) {
+ HPhi* instruction = worklist_.Pop();
+ // Note that the same equivalent phi can be added multiple times in the work list, if
+ // used by multiple phis. The first call to `UpdateType` will know whether the phi is
+ // dead or live.
+ if (instruction->IsLive() && UpdateType(instruction)) {
+ AddDependentInstructionsToWorklist(instruction);
+ }
+ }
+}
+
+void DeadPhiHandling::AddToWorklist(HPhi* instruction) {
+ DCHECK(instruction->IsLive());
+ worklist_.Add(instruction);
+}
+
+void DeadPhiHandling::AddDependentInstructionsToWorklist(HPhi* instruction) {
+ for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->GetUser()->AsPhi();
+ if (phi != nullptr && !phi->IsDead()) {
+ AddToWorklist(phi);
+ }
+ }
+}
+
+void DeadPhiHandling::Run() {
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ VisitBasicBlock(it.Current());
+ }
+ ProcessWorklist();
+}
+
+static bool IsPhiEquivalentOf(HInstruction* instruction, HPhi* phi) {
+ return instruction != nullptr
+ && instruction->IsPhi()
+ && instruction->AsPhi()->GetRegNumber() == phi->GetRegNumber();
+}
+
void SsaBuilder::BuildSsa() {
// 1) Visit in reverse post order. We need to have all predecessors of a block visited
// (with the exception of loops) in order to create the right environment for that
@@ -47,11 +199,9 @@ void SsaBuilder::BuildSsa() {
// our code generator will complain if the inputs of a phi do not have the same
// type. The marking allows the type propagation to know which phis it needs
// to handle. We mark but do not eliminate: the elimination will be done in
- // step 5).
- {
- SsaDeadPhiElimination dead_phis(GetGraph());
- dead_phis.MarkDeadPhis();
- }
+ // step 8).
+ SsaDeadPhiElimination dead_phis(GetGraph());
+ dead_phis.MarkDeadPhis();
// 4) Propagate types of phis. At this point, phis are typed void in the general
// case, or float/double/reference when we created an equivalent phi. So we
@@ -59,17 +209,58 @@ void SsaBuilder::BuildSsa() {
PrimitiveTypePropagation type_propagation(GetGraph());
type_propagation.Run();
- // 5) Step 4) changes inputs of phis which may lead to dead phis again. We re-run
- // the algorithm and this time elimimates them.
- // TODO: Make this work with debug info and reference liveness. We currently
- // eagerly remove phis used in environments.
- {
- SsaDeadPhiElimination dead_phis(GetGraph());
- dead_phis.Run();
+ // 5) Now that the graph is correclty typed, we can get rid of redundant phis.
+ // Note that we cannot do this phase before type propagation, otherwise
+ // we could get rid of phi equivalents, whose presence is a requirement for the
+ // type propagation phase. Note that this is to satisfy statement (a) of the
+ // SsaBuilder (see ssa_builder.h).
+ SsaRedundantPhiElimination redundant_phi(GetGraph());
+ redundant_phi.Run();
+
+ // 6) Make sure environments use the right phi "equivalent": a phi marked dead
+ // can have a phi equivalent that is not dead. We must therefore update
+ // all environment uses of the dead phi to use its equivalent. Note that there
+ // can be multiple phis for the same Dex register that are live (for example
+ // when merging constants), in which case it is OK for the environments
+ // to just reference one.
+ for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ for (HInstructionIterator it_phis(block->GetPhis()); !it_phis.Done(); it_phis.Advance()) {
+ HPhi* phi = it_phis.Current()->AsPhi();
+ // If the phi is not dead, or has no environment uses, there is nothing to do.
+ if (!phi->IsDead() || !phi->HasEnvironmentUses()) continue;
+ HInstruction* next = phi->GetNext();
+ if (!IsPhiEquivalentOf(next, phi)) continue;
+ if (next->AsPhi()->IsDead()) {
+ // If the phi equivalent is dead, check if there is another one.
+ next = next->GetNext();
+ if (!IsPhiEquivalentOf(next, phi)) continue;
+ // There can be at most two phi equivalents.
+ DCHECK(!IsPhiEquivalentOf(next->GetNext(), phi));
+ if (next->AsPhi()->IsDead()) continue;
+ }
+ // We found a live phi equivalent. Update the environment uses of `phi` with it.
+ phi->ReplaceWith(next);
+ }
}
- // 6) Clear locals.
- // TODO: Move this to a dead code eliminator phase.
+ // 7) Deal with phis to guarantee liveness of phis in case of a debuggable
+ // application. This is for satisfying statement (c) of the SsaBuilder
+ // (see ssa_builder.h).
+ if (GetGraph()->IsDebuggable()) {
+ DeadPhiHandling dead_phi_handler(GetGraph());
+ dead_phi_handler.Run();
+ }
+
+ // 8) Now that the right phis are used for the environments, and we
+ // have potentially revive dead phis in case of a debuggable application,
+ // we can eliminate phis we do not need. Regardless of the debuggable status,
+ // this phase is necessary for statement (b) of the SsaBuilder (see ssa_builder.h),
+ // as well as for the code generation, which does not deal with phis of conflicting
+ // input types.
+ dead_phis.EliminateDeadPhis();
+
+ // 9) Clear locals.
for (HInstructionIterator it(GetGraph()->GetEntryBlock()->GetInstructions());
!it.Done();
it.Advance()) {
@@ -257,12 +448,12 @@ HInstruction* SsaBuilder::GetFloatOrDoubleEquivalent(HInstruction* user,
}
HInstruction* SsaBuilder::GetReferenceTypeEquivalent(HInstruction* value) {
- if (value->IsIntConstant()) {
- DCHECK_EQ(value->AsIntConstant()->GetValue(), 0);
+ if (value->IsIntConstant() && value->AsIntConstant()->GetValue() == 0) {
return value->GetBlock()->GetGraph()->GetNullConstant();
- } else {
- DCHECK(value->IsPhi());
+ } else if (value->IsPhi()) {
return GetFloatDoubleOrReferenceEquivalentOfPhi(value->AsPhi(), Primitive::kPrimNot);
+ } else {
+ return nullptr;
}
}
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index f50da46040..b414fb2945 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -24,6 +24,26 @@ namespace art {
static constexpr int kDefaultNumberOfLoops = 2;
+/**
+ * Transforms a graph into SSA form. The liveness guarantees of
+ * this transformation are listed below. A DEX register
+ * being killed means its value at a given position in the code
+ * will not be available to its environment uses. A merge in the
+ * following text is materialized as a `HPhi`.
+ *
+ * (a) Dex registers that do not require merging (that is, they do not
+ * have different values at a join block) are available to all their
+ * environment uses.
+ *
+ * (b) Dex registers that require merging, and the merging gives
+ * incompatible types, will be killed for environment uses of that merge.
+ *
+ * (c) When the `debuggable` flag is passed to the compiler, Dex registers
+ * that require merging and have a proper type after the merge, are
+ * available to all their environment uses. If the `debuggable` flag
+ * is not set, values of Dex registers only used by environments
+ * are killed.
+ */
class SsaBuilder : public HGraphVisitor {
public:
explicit SsaBuilder(HGraph* graph)
diff --git a/test/457-regs/expected.txt b/test/457-regs/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/457-regs/expected.txt
diff --git a/test/457-regs/info.txt b/test/457-regs/info.txt
new file mode 100644
index 0000000000..d9500030bb
--- /dev/null
+++ b/test/457-regs/info.txt
@@ -0,0 +1 @@
+Tests debuggability of DEX registers.
diff --git a/test/457-regs/regs_jni.cc b/test/457-regs/regs_jni.cc
new file mode 100644
index 0000000000..ce701e898b
--- /dev/null
+++ b/test/457-regs/regs_jni.cc
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "arch/context.h"
+#include "jni.h"
+#include "mirror/art_method-inl.h"
+#include "scoped_thread_state_change.h"
+#include "stack.h"
+#include "thread.h"
+
+namespace art {
+
+namespace {
+
+class TestVisitor : public StackVisitor {
+ public:
+ TestVisitor(Thread* thread, Context* context)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : StackVisitor(thread, context) {}
+
+ bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* m = GetMethod();
+ std::string m_name(m->GetName());
+
+ if (m_name.compare("mergeOk") == 0) {
+ uint32_t value = 0;
+
+ CHECK(GetVReg(m, 0, kIntVReg, &value));
+ CHECK_EQ(value, 0u);
+
+ CHECK(GetVReg(m, 1, kIntVReg, &value));
+ CHECK_EQ(value, 1u);
+
+ CHECK(GetVReg(m, 2, kIntVReg, &value));
+ CHECK_EQ(value, 2u);
+
+ CHECK(GetVReg(m, 3, kIntVReg, &value));
+ CHECK_EQ(value, 1u);
+
+ CHECK(GetVReg(m, 4, kIntVReg, &value));
+ CHECK_EQ(value, 2u);
+ did_check_ = true;
+ } else if (m_name.compare("mergeNotOk") == 0) {
+ uint32_t value = 0;
+
+ CHECK(GetVReg(m, 0, kIntVReg, &value));
+ CHECK_EQ(value, 0u);
+
+ CHECK(GetVReg(m, 1, kIntVReg, &value));
+ CHECK_EQ(value, 1u);
+
+ bool success = GetVReg(m, 2, kIntVReg, &value);
+ if (m->IsOptimized(sizeof(void*))) CHECK(!success);
+
+ CHECK(GetVReg(m, 3, kReferenceVReg, &value));
+ CHECK_EQ(value, 1u);
+
+ CHECK(GetVReg(m, 4, kFloatVReg, &value));
+ uint32_t cast = bit_cast<float, uint32_t>(4.0f);
+ CHECK_EQ(value, cast);
+ did_check_ = true;
+ } else if (m_name.compare("phiEquivalent") == 0) {
+ uint32_t value = 0;
+
+ CHECK(GetVReg(m, 0, kIntVReg, &value));
+ // Quick doesn't like this one on x64.
+ CHECK_EQ(value, 0u);
+
+ CHECK(GetVReg(m, 1, kIntVReg, &value));
+ CHECK_EQ(value, 1u);
+
+ CHECK(GetVReg(m, 2, kFloatVReg, &value));
+ CHECK_EQ(value, 1u);
+
+ did_check_ = true;
+ } else if (m_name.compare("mergeReferences") == 0) {
+ uint32_t value = 0;
+
+ CHECK(GetVReg(m, 0, kIntVReg, &value));
+ CHECK_EQ(value, 0u);
+
+ CHECK(GetVReg(m, 1, kIntVReg, &value));
+ CHECK_EQ(value, 1u);
+
+ CHECK(GetVReg(m, 2, kReferenceVReg, &value));
+ CHECK_EQ(value, 0u);
+
+ CHECK(GetVReg(m, 3, kReferenceVReg, &value));
+ CHECK_NE(value, 0u);
+
+ did_check_ = true;
+ } else if (m_name.compare("phiAllEquivalents") == 0) {
+ uint32_t value = 0;
+
+ CHECK(GetVReg(m, 0, kIntVReg, &value));
+ CHECK_EQ(value, 0u);
+
+ CHECK(GetVReg(m, 1, kIntVReg, &value));
+ CHECK_EQ(value, 1u);
+
+ CHECK(GetVReg(m, 2, kReferenceVReg, &value));
+ CHECK_EQ(value, 0u);
+
+ did_check_ = true;
+ }
+
+ return true;
+ }
+
+ bool did_check_ = false;
+};
+
+extern "C" JNIEXPORT void JNICALL Java_PhiLiveness_regsNativeCall(
+ JNIEnv*, jclass value ATTRIBUTE_UNUSED) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<Context> context(Context::Create());
+ TestVisitor visitor(soa.Self(), context.get());
+ visitor.WalkStack();
+ CHECK(visitor.did_check_);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_PhiLiveness_regsNativeCallWithParameters(
+ JNIEnv*, jclass value ATTRIBUTE_UNUSED, jobject main, jint int_value, jfloat float_value) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<Context> context(Context::Create());
+ CHECK(soa.Decode<mirror::Object*>(main) == nullptr);
+ CHECK_EQ(int_value, 0);
+ int32_t cast = bit_cast<float, int32_t>(float_value);
+ CHECK_EQ(cast, 0);
+ TestVisitor visitor(soa.Self(), context.get());
+ visitor.WalkStack();
+ CHECK(visitor.did_check_);
+}
+
+} // namespace
+
+} // namespace art
diff --git a/test/457-regs/smali/PhiLiveness.smali b/test/457-regs/smali/PhiLiveness.smali
new file mode 100644
index 0000000000..c8a6773d44
--- /dev/null
+++ b/test/457-regs/smali/PhiLiveness.smali
@@ -0,0 +1,82 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LPhiLiveness;
+
+.super Ljava/lang/Object;
+
+.method public static mergeOk(ZB)V
+ .registers 5
+ const/4 v0, 0x0
+ const/4 v1, 0x1
+ move v2, v3
+ if-eq v1, v0, :else
+ move v2, v4
+ :else
+ invoke-static {}, LPhiLiveness;->regsNativeCall()V
+ return-void
+.end method
+
+.method public static mergeNotOk(ZF)V
+ .registers 5
+ const/4 v0, 0x0
+ const/4 v1, 0x1
+ move v2, v3
+ if-eq v1, v0, :else
+ move v2, v4
+ :else
+ invoke-static {}, LPhiLiveness;->regsNativeCall()V
+ return-void
+.end method
+
+.method public static mergeReferences(LMain;)V
+ .registers 4
+ const/4 v0, 0x0
+ const/4 v1, 0x1
+ move-object v2, p0
+ if-eq v1, v0, :else
+ move v2, v0
+ :else
+ invoke-static {}, LPhiLiveness;->regsNativeCall()V
+ return-void
+.end method
+
+.method public static phiEquivalent()F
+ .registers 5
+ const/4 v0, 0x0
+ const/4 v1, 0x1
+ move v2, v0
+ if-eq v1, v0, :else
+ move v2, v1
+ :else
+ invoke-static {}, LPhiLiveness;->regsNativeCall()V
+ return v2
+.end method
+
+.method public static phiAllEquivalents(LMain;)V
+ .registers 4
+ const/4 v0, 0x0
+ const/4 v1, 0x1
+ move v2, v0
+ if-eq v1, v0, :else
+ move v2, v0
+ :else
+ invoke-static {v2, v2, v2}, LPhiLiveness;->regsNativeCallWithParameters(LMain;IF)V
+ return-void
+.end method
+
+.method public static native regsNativeCall()V
+.end method
+.method public static native regsNativeCallWithParameters(LMain;IF)V
+.end method
diff --git a/test/457-regs/src/Main.java b/test/457-regs/src/Main.java
new file mode 100644
index 0000000000..0d82033195
--- /dev/null
+++ b/test/457-regs/src/Main.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("PhiLiveness");
+ Method m = c.getMethod("mergeOk", boolean.class, byte.class);
+ m.invoke(null, new Boolean(true), new Byte((byte)2));
+
+ m = c.getMethod("mergeNotOk", boolean.class, float.class);
+ m.invoke(null, new Boolean(true), new Float(4.0f));
+
+ m = c.getMethod("mergeReferences", Main.class);
+ m.invoke(null, new Main());
+
+ m = c.getMethod("phiEquivalent");
+ m.invoke(null);
+
+ m = c.getMethod("phiAllEquivalents", Main.class);
+ m.invoke(null, new Main());
+ }
+
+ static {
+ System.loadLibrary("arttest");
+ }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 75c5d72f18..f4bab3f9fd 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -29,7 +29,8 @@ LIBARTTEST_COMMON_SRC_FILES := \
117-nopatchoat/nopatchoat.cc \
118-noimage-dex2oat/noimage-dex2oat.cc \
454-get-vreg/get_vreg_jni.cc \
- 455-set-vreg/set_vreg_jni.cc
+ 455-set-vreg/set_vreg_jni.cc \
+ 457-regs/regs_jni.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ifdef TARGET_2ND_ARCH
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index da360567f5..740961e718 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -321,7 +321,8 @@ endif
TEST_ART_BROKEN_NDEBUG_TESTS :=
# Known broken tests for the default compiler (Quick).
-TEST_ART_BROKEN_DEFAULT_RUN_TESTS :=
+TEST_ART_BROKEN_DEFAULT_RUN_TESTS := \
+ 457-regs
ifneq (,$(filter default,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -377,6 +378,18 @@ endif
TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS :=
+# Tests that should fail when the optimizing compiler compiles them non-debuggable.
+TEST_ART_BROKEN_OPTIMIZING_NONDEBUGGABLE_RUN_TESTS := \
+ 457-regs \
+
+ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ optimizing,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),nondebuggable,$(TEST_ART_BROKEN_OPTIMIZING_NONDEBUGGABLE_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_OPTIMIZING_NONDEBUGGABLE_RUN_TESTS :=
+
# Clear variables ahead of appending to them when defining tests.
$(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))