diff options
-rw-r--r-- | compiler/optimizing/graph_checker.cc | 6 | ||||
-rw-r--r-- | compiler/optimizing/inliner.cc | 8 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 23 | ||||
-rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 7 | ||||
-rw-r--r-- | compiler/optimizing/primitive_type_propagation.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/ssa_builder.cc | 227 | ||||
-rw-r--r-- | compiler/optimizing/ssa_builder.h | 20 | ||||
-rw-r--r-- | test/457-regs/expected.txt | 0 | ||||
-rw-r--r-- | test/457-regs/info.txt | 1 | ||||
-rw-r--r-- | test/457-regs/regs_jni.cc | 150 | ||||
-rw-r--r-- | test/457-regs/smali/PhiLiveness.smali | 82 | ||||
-rw-r--r-- | test/457-regs/src/Main.java | 45 | ||||
-rw-r--r-- | test/Android.libarttest.mk | 3 | ||||
-rw-r--r-- | test/Android.run-test.mk | 15 |
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 :=)) |