Merge "ART: Fix .bss index lookup in wrong dex file."
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 19abcd1..3699d66 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -322,6 +322,7 @@
"linker/linker_patch_test.cc",
"linker/output_stream_test.cc",
"optimizing/bounds_check_elimination_test.cc",
+ "optimizing/cloner_test.cc",
"optimizing/data_type_test.cc",
"optimizing/dominator_test.cc",
"optimizing/find_loops_test.cc",
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index 37c2d32..d599994 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -35,6 +35,7 @@
#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/class.h"
+#include "oat_file.h"
namespace art {
namespace debug {
@@ -49,7 +50,8 @@
std::vector<const char*> names;
if (mi->code_item != nullptr) {
DCHECK(mi->dex_file != nullptr);
- const uint8_t* stream = mi->dex_file->GetDebugInfoStream(mi->code_item);
+ uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*mi->dex_file, mi->code_item);
+ const uint8_t* stream = mi->dex_file->GetDebugInfoStream(debug_info_offset);
if (stream != nullptr) {
DecodeUnsignedLeb128(&stream); // line.
uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
@@ -257,7 +259,9 @@
// Write local variables.
LocalInfos local_infos;
+ uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex, dex_code);
if (dex->DecodeDebugLocalInfo(dex_code,
+ debug_info_offset,
is_static,
mi->dex_method_index,
LocalInfoCallback,
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index 6e72b46..943e03a 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -26,6 +26,7 @@
#include "debug/src_map_elem.h"
#include "dex_file-inl.h"
#include "linker/elf_builder.h"
+#include "oat_file.h"
#include "stack_map.h"
namespace art {
@@ -158,7 +159,9 @@
PositionInfos dex2line_map;
DCHECK(mi->dex_file != nullptr);
const DexFile* dex = mi->dex_file;
- if (!dex->DecodeDebugPositionInfo(mi->code_item, PositionInfoCallback, &dex2line_map)) {
+ uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex, mi->code_item);
+ if (!dex->DecodeDebugPositionInfo(
+ mi->code_item, debug_info_offset, PositionInfoCallback, &dex2line_map)) {
continue;
}
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 726401d..e4dd544 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2025,28 +2025,19 @@
ClassReference ref(manager_->GetDexFile(), class_def_index);
manager_->GetCompiler()->RecordClassStatus(ref, klass->GetStatus());
- // It is *very* problematic if there are verification errors in the boot classpath.
- // For example, we rely on things working OK without verification when the decryption dialog
- // is brought up. So abort in a debug build if we find this violated.
+ // It is *very* problematic if there are resolution errors in the boot classpath.
+ //
+ // It is also bad if classes fail verification. For example, we rely on things working
+ // OK without verification when the decryption dialog is brought up. It is thus highly
+ // recommended to compile the boot classpath with
+ // --abort-on-hard-verifier-error --abort-on-soft-verifier-error
+ // which is the default build system configuration.
if (kIsDebugBuild) {
if (manager_->GetCompiler()->GetCompilerOptions().IsBootImage()) {
- if (!klass->IsVerified()) {
- // Re-run verification to get all failure messages if it soft-failed.
- if (!klass->IsErroneous()) {
- gLogVerbosity.verifier = true;
- // Note: We can't call ClassLinker::VerifyClass, as it will elide the second
- // verification.
- Runtime* runtime = Runtime::Current();
- std::string v_error;
- verifier::MethodVerifier::VerifyClass(soa.Self(),
- klass.Get(),
- runtime->GetCompilerCallbacks(),
- runtime->IsAotCompiler(),
- verifier::HardFailLogMode::kLogInternalFatal,
- &v_error);
- }
+ if (!klass->IsResolved() || klass->IsErroneous()) {
LOG(FATAL) << "Boot classpath class " << klass->PrettyClass()
- << " failed to fully verify: state= " << klass->GetStatus();
+ << " failed to resolve/is erroneous: state= " << klass->GetStatus();
+ UNREACHABLE();
}
}
if (klass->IsVerified()) {
diff --git a/compiler/optimizing/cloner_test.cc b/compiler/optimizing/cloner_test.cc
new file mode 100644
index 0000000..d34dd81
--- /dev/null
+++ b/compiler/optimizing/cloner_test.cc
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#include "graph_checker.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+// This class provides methods and helpers for testing various cloning and copying routines:
+// individual instruction cloning and cloning of the more coarse-grain structures.
+class ClonerTest : public OptimizingUnitTest {
+ public:
+ ClonerTest()
+ : graph_(CreateGraph()), entry_block_(nullptr), exit_block_(nullptr), parameter_(nullptr) {}
+
+ void CreateBasicLoopControlFlow(/* out */ HBasicBlock** header_p,
+ /* out */ HBasicBlock** body_p) {
+ entry_block_ = new (GetAllocator()) HBasicBlock(graph_);
+ graph_->AddBlock(entry_block_);
+ graph_->SetEntryBlock(entry_block_);
+
+ HBasicBlock* loop_preheader = new (GetAllocator()) HBasicBlock(graph_);
+ HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph_);
+ HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph_);
+ HBasicBlock* loop_exit = new (GetAllocator()) HBasicBlock(graph_);
+
+ graph_->AddBlock(loop_preheader);
+ graph_->AddBlock(loop_header);
+ graph_->AddBlock(loop_body);
+ graph_->AddBlock(loop_exit);
+
+ exit_block_ = new (GetAllocator()) HBasicBlock(graph_);
+ graph_->AddBlock(exit_block_);
+ graph_->SetExitBlock(exit_block_);
+
+ entry_block_->AddSuccessor(loop_preheader);
+ loop_preheader->AddSuccessor(loop_header);
+ // Loop exit first to have a proper exit condition/target for HIf.
+ loop_header->AddSuccessor(loop_exit);
+ loop_header->AddSuccessor(loop_body);
+ loop_body->AddSuccessor(loop_header);
+ loop_exit->AddSuccessor(exit_block_);
+
+ *header_p = loop_header;
+ *body_p = loop_body;
+
+ parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(0),
+ 0,
+ DataType::Type::kInt32);
+ entry_block_->AddInstruction(parameter_);
+ loop_exit->AddInstruction(new (GetAllocator()) HReturnVoid());
+ exit_block_->AddInstruction(new (GetAllocator()) HExit());
+ }
+
+ void CreateBasicLoopDataFlow(HBasicBlock* loop_header, HBasicBlock* loop_body) {
+ uint32_t dex_pc = 0;
+
+ // Entry block.
+ HIntConstant* const_0 = graph_->GetIntConstant(0);
+ HIntConstant* const_1 = graph_->GetIntConstant(1);
+ HIntConstant* const_128 = graph_->GetIntConstant(128);
+
+ // Header block.
+ HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
+ HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck();
+
+ loop_header->AddPhi(phi);
+ loop_header->AddInstruction(suspend_check);
+ loop_header->AddInstruction(new (GetAllocator()) HGreaterThanOrEqual(phi, const_128));
+ loop_header->AddInstruction(new (GetAllocator()) HIf(parameter_));
+
+ // Loop body block.
+ HInstruction* null_check = new (GetAllocator()) HNullCheck(parameter_, dex_pc);
+ HInstruction* array_length = new (GetAllocator()) HArrayLength(null_check, dex_pc);
+ HInstruction* bounds_check = new (GetAllocator()) HBoundsCheck(phi, array_length, dex_pc);
+ HInstruction* array_get =
+ new (GetAllocator()) HArrayGet(null_check, bounds_check, DataType::Type::kInt32, dex_pc);
+ HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, array_get, const_1);
+ HInstruction* array_set =
+ new (GetAllocator()) HArraySet(null_check, bounds_check, add, DataType::Type::kInt32, dex_pc);
+ HInstruction* induction_inc = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, const_1);
+
+ loop_body->AddInstruction(null_check);
+ loop_body->AddInstruction(array_length);
+ loop_body->AddInstruction(bounds_check);
+ loop_body->AddInstruction(array_get);
+ loop_body->AddInstruction(add);
+ loop_body->AddInstruction(array_set);
+ loop_body->AddInstruction(induction_inc);
+ loop_body->AddInstruction(new (GetAllocator()) HGoto());
+
+ phi->AddInput(const_0);
+ phi->AddInput(induction_inc);
+
+ graph_->SetHasBoundsChecks(true);
+
+ // Adjust HEnvironment for each instruction which require that.
+ ArenaVector<HInstruction*> current_locals({phi, const_128, parameter_},
+ GetAllocator()->Adapter(kArenaAllocInstruction));
+
+ HEnvironment* env = ManuallyBuildEnvFor(suspend_check, ¤t_locals);
+ null_check->CopyEnvironmentFrom(env);
+ bounds_check->CopyEnvironmentFrom(env);
+ }
+
+ HEnvironment* ManuallyBuildEnvFor(HInstruction* instruction,
+ ArenaVector<HInstruction*>* current_locals) {
+ HEnvironment* environment = new (GetAllocator()) HEnvironment(
+ (GetAllocator()),
+ current_locals->size(),
+ graph_->GetArtMethod(),
+ instruction->GetDexPc(),
+ instruction);
+
+ environment->CopyFrom(ArrayRef<HInstruction* const>(*current_locals));
+ instruction->SetRawEnvironment(environment);
+ return environment;
+ }
+
+ bool CheckGraph() {
+ GraphChecker checker(graph_);
+ checker.Run();
+ if (!checker.IsValid()) {
+ for (const std::string& error : checker.GetErrors()) {
+ std::cout << error << std::endl;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ HGraph* graph_;
+
+ HBasicBlock* entry_block_;
+ HBasicBlock* exit_block_;
+
+ HInstruction* parameter_;
+};
+
+TEST_F(ClonerTest, IndividualInstrCloner) {
+ HBasicBlock* header = nullptr;
+ HBasicBlock* loop_body = nullptr;
+
+ CreateBasicLoopControlFlow(&header, &loop_body);
+ CreateBasicLoopDataFlow(header, loop_body);
+ graph_->BuildDominatorTree();
+ ASSERT_TRUE(CheckGraph());
+
+ HSuspendCheck* old_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
+ CloneAndReplaceInstructionVisitor visitor(graph_);
+ // Do instruction cloning and replacement twice with different visiting order.
+
+ visitor.VisitInsertionOrder();
+ size_t instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount();
+ EXPECT_EQ(instr_replaced_by_clones_count, 12u);
+ EXPECT_TRUE(CheckGraph());
+
+ visitor.VisitReversePostOrder();
+ instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount();
+ EXPECT_EQ(instr_replaced_by_clones_count, 24u);
+ EXPECT_TRUE(CheckGraph());
+
+ HSuspendCheck* new_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
+ EXPECT_NE(new_suspend_check, old_suspend_check);
+ EXPECT_NE(new_suspend_check, nullptr);
+}
+
+} // namespace art
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index ad29ba5..d270c6a 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -93,6 +93,136 @@
}
}
+/**
+ * Returns true if loop is guarded by "a cmp b" on entry.
+ */
+static bool IsGuardedBy(HLoopInformation* loop,
+ IfCondition cmp,
+ HInstruction* a,
+ HInstruction* b) {
+ // Chase back through straightline code to the first potential
+ // block that has a control dependence.
+ // guard: if (x) bypass
+ // |
+ // entry: straightline code
+ // |
+ // preheader
+ // |
+ // header
+ HBasicBlock* guard = loop->GetPreHeader();
+ HBasicBlock* entry = loop->GetHeader();
+ while (guard->GetPredecessors().size() == 1 &&
+ guard->GetSuccessors().size() == 1) {
+ entry = guard;
+ guard = guard->GetSinglePredecessor();
+ }
+ // Find guard.
+ HInstruction* control = guard->GetLastInstruction();
+ if (!control->IsIf()) {
+ return false;
+ }
+ HIf* ifs = control->AsIf();
+ HInstruction* if_expr = ifs->InputAt(0);
+ if (if_expr->IsCondition()) {
+ IfCondition other_cmp = ifs->IfTrueSuccessor() == entry
+ ? if_expr->AsCondition()->GetCondition()
+ : if_expr->AsCondition()->GetOppositeCondition();
+ if (if_expr->InputAt(0) == a && if_expr->InputAt(1) == b) {
+ return cmp == other_cmp;
+ } else if (if_expr->InputAt(1) == a && if_expr->InputAt(0) == b) {
+ switch (cmp) {
+ case kCondLT: return other_cmp == kCondGT;
+ case kCondLE: return other_cmp == kCondGE;
+ case kCondGT: return other_cmp == kCondLT;
+ case kCondGE: return other_cmp == kCondLE;
+ default: LOG(FATAL) << "unexpected cmp: " << cmp;
+ }
+ }
+ }
+ return false;
+}
+
+/* Finds first loop header phi use. */
+HInstruction* FindFirstLoopHeaderPhiUse(HLoopInformation* loop, HInstruction* instruction) {
+ for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
+ if (use.GetUser()->GetBlock() == loop->GetHeader() &&
+ use.GetUser()->IsPhi() &&
+ use.GetUser()->InputAt(1) == instruction) {
+ return use.GetUser();
+ }
+ }
+ return nullptr;
+}
+
+/**
+ * Relinks the Phi structure after break-loop rewriting.
+ */
+bool FixOutsideUse(HLoopInformation* loop,
+ HInstruction* instruction,
+ HInstruction* replacement,
+ bool rewrite) {
+ // Deal with regular uses.
+ const HUseList<HInstruction*>& uses = instruction->GetUses();
+ for (auto it = uses.begin(), end = uses.end(); it != end; ) {
+ HInstruction* user = it->GetUser();
+ size_t index = it->GetIndex();
+ ++it; // increment prior to potential removal
+ if (user->GetBlock()->GetLoopInformation() != loop) {
+ if (replacement == nullptr) {
+ return false;
+ } else if (rewrite) {
+ user->ReplaceInput(replacement, index);
+ }
+ }
+ }
+ // Deal with environment uses.
+ const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
+ for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) {
+ HEnvironment* user = it->GetUser();
+ size_t index = it->GetIndex();
+ ++it; // increment prior to potential removal
+ if (user->GetHolder()->GetBlock()->GetLoopInformation() != loop) {
+ if (replacement == nullptr) {
+ return false;
+ } else if (rewrite) {
+ user->RemoveAsUserOfInput(index);
+ user->SetRawEnvAt(index, replacement);
+ replacement->AddEnvUseAt(user, index);
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * Test and rewrite the loop body of a break-loop. Returns true on success.
+ */
+bool RewriteBreakLoopBody(HLoopInformation* loop,
+ HBasicBlock* body,
+ HInstruction* cond,
+ HInstruction* index,
+ HInstruction* upper,
+ bool rewrite) {
+ // Deal with Phis. Outside use prohibited, except for index (which gets exit value).
+ for (HInstructionIterator it(loop->GetHeader()->GetPhis()); !it.Done(); it.Advance()) {
+ HInstruction* exit_value = it.Current() == index ? upper : nullptr;
+ if (!FixOutsideUse(loop, it.Current(), exit_value, rewrite)) {
+ return false;
+ }
+ }
+ // Deal with other statements in header.
+ for (HInstruction* m = cond->GetPrevious(), *p = nullptr; m && !m->IsSuspendCheck(); m = p) {
+ p = m->GetPrevious();
+ if (rewrite) {
+ m->MoveBefore(body->GetFirstInstruction(), false);
+ }
+ if (!FixOutsideUse(loop, m, FindFirstLoopHeaderPhiUse(loop, m), rewrite)) {
+ return false;
+ }
+ }
+ return true;
+}
+
//
// Class methods.
//
@@ -754,6 +884,10 @@
return nullptr;
}
+//
+// Loop trip count analysis methods.
+//
+
void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) {
HInstruction* control = loop->GetHeader()->GetLastInstruction();
if (control->IsIf()) {
@@ -774,15 +908,16 @@
if (a == nullptr || b == nullptr) {
return; // Loop control is not a sequence.
} else if (if_true->GetLoopInformation() != loop && if_false->GetLoopInformation() == loop) {
- VisitCondition(loop, a, b, type, condition->GetOppositeCondition());
+ VisitCondition(loop, if_false, a, b, type, condition->GetOppositeCondition());
} else if (if_true->GetLoopInformation() == loop && if_false->GetLoopInformation() != loop) {
- VisitCondition(loop, a, b, type, condition->GetCondition());
+ VisitCondition(loop, if_true, a, b, type, condition->GetCondition());
}
}
}
}
void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop,
+ HBasicBlock* body,
InductionInfo* a,
InductionInfo* b,
DataType::Type type,
@@ -790,11 +925,11 @@
if (a->induction_class == kInvariant && b->induction_class == kLinear) {
// Swap condition if induction is at right-hand-side (e.g. U > i is same as i < U).
switch (cmp) {
- case kCondLT: VisitCondition(loop, b, a, type, kCondGT); break;
- case kCondLE: VisitCondition(loop, b, a, type, kCondGE); break;
- case kCondGT: VisitCondition(loop, b, a, type, kCondLT); break;
- case kCondGE: VisitCondition(loop, b, a, type, kCondLE); break;
- case kCondNE: VisitCondition(loop, b, a, type, kCondNE); break;
+ case kCondLT: VisitCondition(loop, body, b, a, type, kCondGT); break;
+ case kCondLE: VisitCondition(loop, body, b, a, type, kCondGE); break;
+ case kCondGT: VisitCondition(loop, body, b, a, type, kCondLT); break;
+ case kCondGE: VisitCondition(loop, body, b, a, type, kCondLE); break;
+ case kCondNE: VisitCondition(loop, body, b, a, type, kCondNE); break;
default: break;
}
} else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
@@ -802,24 +937,30 @@
InductionInfo* lower_expr = a->op_b;
InductionInfo* upper_expr = b;
InductionInfo* stride_expr = a->op_a;
- // Constant stride?
+ // Test for constant stride and integral condition.
int64_t stride_value = 0;
if (!IsExact(stride_expr, &stride_value)) {
- return;
+ return; // unknown stride
+ } else if (type != DataType::Type::kInt32 && type != DataType::Type::kInt64) {
+ return; // not integral
}
- // Rewrite condition i != U into strict end condition i < U or i > U if this end condition
- // is reached exactly (tested by verifying if the loop has a unit stride and the non-strict
- // condition would be always taken).
+ // Since loops with a i != U condition will not be normalized by the method below, first
+ // try to rewrite a break-loop with terminating condition i != U into an equivalent loop
+ // with non-strict end condition i <= U or i >= U if such a rewriting is possible and safe.
+ if (cmp == kCondNE && RewriteBreakLoop(loop, body, stride_value, type)) {
+ cmp = stride_value > 0 ? kCondLE : kCondGE;
+ }
+ // If this rewriting failed, try to rewrite condition i != U into strict end condition i < U
+ // or i > U if this end condition is reached exactly (tested by verifying if the loop has a
+ // unit stride and the non-strict condition would be always taken).
if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLE)) ||
(stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) {
cmp = stride_value > 0 ? kCondLT : kCondGT;
}
- // Only accept integral condition. A mismatch between the type of condition and the induction
- // is only allowed if the, necessarily narrower, induction range fits the narrower control.
- if (type != DataType::Type::kInt32 && type != DataType::Type::kInt64) {
- return; // not integral
- } else if (type != a->type &&
- !FitsNarrowerControl(lower_expr, upper_expr, stride_value, a->type, cmp)) {
+ // A mismatch between the type of condition and the induction is only allowed if the,
+ // necessarily narrower, induction range fits the narrower control.
+ if (type != a->type &&
+ !FitsNarrowerControl(lower_expr, upper_expr, stride_value, a->type, cmp)) {
return; // mismatched type
}
// Normalize a linear loop control with a nonzero stride:
@@ -984,6 +1125,69 @@
IsAtMost(upper_expr, &value) && value <= max;
}
+bool HInductionVarAnalysis::RewriteBreakLoop(HLoopInformation* loop,
+ HBasicBlock* body,
+ int64_t stride_value,
+ DataType::Type type) {
+ // Only accept unit stride.
+ if (std::abs(stride_value) != 1) {
+ return false;
+ }
+ // Simple terminating i != U condition, used nowhere else.
+ HIf* ifs = loop->GetHeader()->GetLastInstruction()->AsIf();
+ HInstruction* cond = ifs->InputAt(0);
+ if (ifs->GetPrevious() != cond || !cond->HasOnlyOneNonEnvironmentUse()) {
+ return false;
+ }
+ int c = LookupInfo(loop, cond->InputAt(0))->induction_class == kLinear ? 0 : 1;
+ HInstruction* index = cond->InputAt(c);
+ HInstruction* upper = cond->InputAt(1 - c);
+ // Safe to rewrite into i <= U?
+ IfCondition cmp = stride_value > 0 ? kCondLE : kCondGE;
+ if (!index->IsPhi() || !IsFinite(LookupInfo(loop, upper), stride_value, type, cmp)) {
+ return false;
+ }
+ // Body consists of update to index i only, used nowhere else.
+ if (body->GetSuccessors().size() != 1 ||
+ body->GetSingleSuccessor() != loop->GetHeader() ||
+ !body->GetPhis().IsEmpty() ||
+ body->GetInstructions().IsEmpty() ||
+ body->GetFirstInstruction() != index->InputAt(1) ||
+ !body->GetFirstInstruction()->HasOnlyOneNonEnvironmentUse() ||
+ !body->GetFirstInstruction()->GetNext()->IsGoto()) {
+ return false;
+ }
+ // Always taken or guarded by enclosing condition.
+ if (!IsTaken(LookupInfo(loop, index)->op_b, LookupInfo(loop, upper), cmp) &&
+ !IsGuardedBy(loop, cmp, index->InputAt(0), upper)) {
+ return false;
+ }
+ // Test if break-loop body can be written, and do so on success.
+ if (RewriteBreakLoopBody(loop, body, cond, index, upper, /*rewrite*/ false)) {
+ RewriteBreakLoopBody(loop, body, cond, index, upper, /*rewrite*/ true);
+ } else {
+ return false;
+ }
+ // Rewrite condition in HIR.
+ if (ifs->IfTrueSuccessor() != body) {
+ cmp = (cmp == kCondLE) ? kCondGT : kCondLT;
+ }
+ HInstruction* rep = nullptr;
+ switch (cmp) {
+ case kCondLT: rep = new (graph_->GetAllocator()) HLessThan(index, upper); break;
+ case kCondGT: rep = new (graph_->GetAllocator()) HGreaterThan(index, upper); break;
+ case kCondLE: rep = new (graph_->GetAllocator()) HLessThanOrEqual(index, upper); break;
+ case kCondGE: rep = new (graph_->GetAllocator()) HGreaterThanOrEqual(index, upper); break;
+ default: LOG(FATAL) << cmp; UNREACHABLE();
+ }
+ loop->GetHeader()->ReplaceAndRemoveInstructionWith(cond, rep);
+ return true;
+}
+
+//
+// Helper methods.
+//
+
void HInductionVarAnalysis::AssignInfo(HLoopInformation* loop,
HInstruction* instruction,
InductionInfo* info) {
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 8737b89..acad77d 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -195,9 +195,14 @@
HInstruction* entry_phi,
HTypeConversion* conversion);
+ //
+ // Loop trip count analysis methods.
+ //
+
// Trip count information.
void VisitControl(HLoopInformation* loop);
void VisitCondition(HLoopInformation* loop,
+ HBasicBlock* body,
InductionInfo* a,
InductionInfo* b,
DataType::Type type,
@@ -219,6 +224,14 @@
int64_t stride_value,
DataType::Type type,
IfCondition cmp);
+ bool RewriteBreakLoop(HLoopInformation* loop,
+ HBasicBlock* body,
+ int64_t stride_value,
+ DataType::Type type);
+
+ //
+ // Helper methods.
+ //
// Assign and lookup.
void AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info);
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 61840cc..978d0c2 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -29,6 +29,7 @@
#include "driver/compiler_options.h"
#include "imtable-inl.h"
#include "mirror/dex_cache.h"
+#include "oat_file.h"
#include "optimizing_compiler_stats.h"
#include "quicken_info.h"
#include "scoped_thread_state_change-inl.h"
@@ -447,7 +448,8 @@
/* expandable */ false,
kArenaAllocGraphBuilder);
locations->ClearAllBits();
- dex_file_->DecodeDebugPositionInfo(code_item_, Callback::Position, locations);
+ uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file_, code_item_);
+ dex_file_->DecodeDebugPositionInfo(code_item_, debug_info_offset, Callback::Position, locations);
// Instruction-specific tweaks.
IterationRange<DexInstructionIterator> instructions = code_item_->Instructions();
for (const DexInstructionPcPair& inst : instructions) {
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index fbfee12..4c18e16 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -27,6 +27,10 @@
namespace art {
+// Whether to run an exhaustive test of individual HInstructions cloning when each instruction
+// is replaced with its copy if it is clonable.
+static constexpr bool kTestInstructionClonerExhaustively = false;
+
class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
public:
InstructionSimplifierVisitor(HGraph* graph,
@@ -130,6 +134,11 @@
};
void InstructionSimplifier::Run() {
+ if (kTestInstructionClonerExhaustively) {
+ CloneAndReplaceInstructionVisitor visitor(graph_);
+ visitor.VisitReversePostOrder();
+ }
+
InstructionSimplifierVisitor visitor(graph_, codegen_, compiler_driver_, stats_);
visitor.Run();
}
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index fff61f5..fa580d9 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -845,6 +845,13 @@
DCHECK(!instruction->HasEnvironment());
}
+void HBasicBlock::ReplaceAndRemovePhiWith(HPhi* initial, HPhi* replacement) {
+ DCHECK(initial->GetBlock() == this);
+ InsertPhiAfter(replacement, initial);
+ initial->ReplaceWith(replacement);
+ RemovePhi(initial);
+}
+
void HBasicBlock::ReplaceAndRemoveInstructionWith(HInstruction* initial,
HInstruction* replacement) {
DCHECK(initial->GetBlock() == this);
@@ -2907,6 +2914,28 @@
env_uses_.clear();
}
+HInstruction* ReplaceInstrOrPhiByClone(HInstruction* instr) {
+ HInstruction* clone = instr->Clone(instr->GetBlock()->GetGraph()->GetAllocator());
+ HBasicBlock* block = instr->GetBlock();
+
+ if (instr->IsPhi()) {
+ HPhi* phi = instr->AsPhi();
+ DCHECK(!phi->HasEnvironment());
+ HPhi* phi_clone = clone->AsPhi();
+ block->ReplaceAndRemovePhiWith(phi, phi_clone);
+ } else {
+ block->ReplaceAndRemoveInstructionWith(instr, clone);
+ if (instr->HasEnvironment()) {
+ clone->CopyEnvironmentFrom(instr->GetEnvironment());
+ HLoopInformation* loop_info = block->GetLoopInformation();
+ if (instr->IsSuspendCheck() && loop_info != nullptr) {
+ loop_info->SetSuspendCheck(clone->AsSuspendCheck());
+ }
+ }
+ }
+ return clone;
+}
+
// Returns an instruction with the opposite Boolean value from 'cond'.
HInstruction* HGraph::InsertOppositeCondition(HInstruction* cond, HInstruction* cursor) {
ArenaAllocator* allocator = GetAllocator();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 6672901..66d5bfe 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1160,6 +1160,8 @@
// Insert `instruction` before/after an existing instruction `cursor`.
void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor);
void InsertInstructionAfter(HInstruction* instruction, HInstruction* cursor);
+ // Replace phi `initial` with `replacement` within this block.
+ void ReplaceAndRemovePhiWith(HPhi* initial, HPhi* replacement);
// Replace instruction `initial` with `replacement` within this block.
void ReplaceAndRemoveInstructionWith(HInstruction* initial,
HInstruction* replacement);
@@ -1480,18 +1482,31 @@
#undef FORWARD_DECLARATION
#define DECLARE_INSTRUCTION(type) \
+ private: \
+ H##type& operator=(const H##type&) = delete; \
+ public: \
InstructionKind GetKindInternal() const OVERRIDE { return k##type; } \
const char* DebugName() const OVERRIDE { return #type; } \
bool InstructionTypeEquals(const HInstruction* other) const OVERRIDE { \
return other->Is##type(); \
} \
+ HInstruction* Clone(ArenaAllocator* arena) const OVERRIDE { \
+ DCHECK(IsClonable()); \
+ return new (arena) H##type(*this->As##type()); \
+ } \
void Accept(HGraphVisitor* visitor) OVERRIDE
#define DECLARE_ABSTRACT_INSTRUCTION(type) \
+ private: \
+ H##type& operator=(const H##type&) = delete; \
+ public: \
bool Is##type() const { return As##type() != nullptr; } \
const H##type* As##type() const { return this; } \
H##type* As##type() { return this; }
+#define DEFAULT_COPY_CONSTRUCTOR(type) \
+ explicit H##type(const H##type& other) = default;
+
template <typename T>
class HUseListNode : public ArenaObject<kArenaAllocUseListNode>,
public IntrusiveForwardListNode<HUseListNode<T>> {
@@ -2182,6 +2197,25 @@
FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
#undef INSTRUCTION_TYPE_CHECK
+ // Return a clone of the instruction if it is clonable (shallow copy by default, custom copy
+ // if a custom copy-constructor is provided for a particular type). If IsClonable() is false for
+ // the instruction then the behaviour of this function is undefined.
+ //
+ // Note: It is semantically valid to create a clone of the instruction only until
+ // prepare_for_register_allocator phase as lifetime, intervals and codegen info are not
+ // copied.
+ //
+ // Note: HEnvironment and some other fields are not copied and are set to default values, see
+ // 'explicit HInstruction(const HInstruction& other)' for details.
+ virtual HInstruction* Clone(ArenaAllocator* arena ATTRIBUTE_UNUSED) const {
+ LOG(FATAL) << "Cloning is not implemented for the instruction " <<
+ DebugName() << " " << GetId();
+ UNREACHABLE();
+ }
+
+ // Return whether instruction can be cloned (copied).
+ virtual bool IsClonable() const { return false; }
+
// Returns whether the instruction can be moved within the graph.
// TODO: this method is used by LICM and GVN with possibly different
// meanings? split and rename?
@@ -2298,6 +2332,30 @@
packed_fields_ = BitFieldType::Update(value, packed_fields_);
}
+ // Copy construction for the instruction (used for Clone function).
+ //
+ // Fields (e.g. lifetime, intervals and codegen info) associated with phases starting from
+ // prepare_for_register_allocator are not copied (set to default values).
+ //
+ // Copy constructors must be provided for every HInstruction type; default copy constructor is
+ // fine for most of them. However for some of the instructions a custom copy constructor must be
+ // specified (when instruction has non-trivially copyable fields and must have a special behaviour
+ // for copying them).
+ explicit HInstruction(const HInstruction& other)
+ : previous_(nullptr),
+ next_(nullptr),
+ block_(nullptr),
+ dex_pc_(other.dex_pc_),
+ id_(-1),
+ ssa_index_(-1),
+ packed_fields_(other.packed_fields_),
+ environment_(nullptr),
+ locations_(nullptr),
+ live_interval_(nullptr),
+ lifetime_position_(kNoLifetime),
+ side_effects_(other.side_effects_),
+ reference_type_handle_(other.reference_type_handle_) {}
+
private:
void FixUpUserRecordsAfterUseInsertion(HUseList<HInstruction*>::iterator fixup_end) {
auto before_use_node = uses_.before_begin();
@@ -2387,8 +2445,6 @@
friend class HEnvironment;
friend class HGraph;
friend class HInstructionList;
-
- DISALLOW_COPY_AND_ASSIGN(HInstruction);
};
std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs);
@@ -2484,10 +2540,9 @@
: HInstruction(side_effects, dex_pc),
inputs_(number_of_inputs, allocator->Adapter(kind)) {}
- ArenaVector<HUserRecord<HInstruction*>> inputs_;
+ DEFAULT_COPY_CONSTRUCTOR(VariableInputSizeInstruction);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVariableInputSizeInstruction);
+ ArenaVector<HUserRecord<HInstruction*>> inputs_;
};
template<size_t N>
@@ -2502,6 +2557,9 @@
return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
}
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction<N>);
+
private:
std::array<HUserRecord<HInstruction*>, N> inputs_;
@@ -2522,6 +2580,9 @@
return ArrayRef<HUserRecord<HInstruction*>>();
}
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction<0>);
+
private:
friend class SsaBuilder;
};
@@ -2547,6 +2608,7 @@
static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits,
"Too many packed fields.");
using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>;
+ DEFAULT_COPY_CONSTRUCTOR(Expression<N>);
};
// Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow
@@ -2560,8 +2622,8 @@
DECLARE_INSTRUCTION(ReturnVoid);
- private:
- DISALLOW_COPY_AND_ASSIGN(HReturnVoid);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(ReturnVoid);
};
// Represents dex's RETURN opcodes. A HReturn is a control flow
@@ -2577,8 +2639,8 @@
DECLARE_INSTRUCTION(Return);
- private:
- DISALLOW_COPY_AND_ASSIGN(HReturn);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Return);
};
class HPhi FINAL : public HVariableInputSizeInstruction {
@@ -2604,6 +2666,8 @@
SetPackedFlag<kFlagCanBeNull>(true);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
// Returns a type equivalent to the given `type`, but that a `HPhi` can hold.
static DataType::Type ToPhiType(DataType::Type type) {
return DataType::Kind(type);
@@ -2666,6 +2730,9 @@
DECLARE_INSTRUCTION(Phi);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Phi);
+
private:
static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
static constexpr size_t kFieldTypeSize =
@@ -2677,8 +2744,6 @@
using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>;
const uint32_t reg_number_;
-
- DISALLOW_COPY_AND_ASSIGN(HPhi);
};
// The exit instruction is the only instruction of the exit block.
@@ -2692,8 +2757,8 @@
DECLARE_INSTRUCTION(Exit);
- private:
- DISALLOW_COPY_AND_ASSIGN(HExit);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Exit);
};
// Jumps from one block to another.
@@ -2701,6 +2766,7 @@
public:
explicit HGoto(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {}
+ bool IsClonable() const OVERRIDE { return true; }
bool IsControlFlow() const OVERRIDE { return true; }
HBasicBlock* GetSuccessor() const {
@@ -2709,8 +2775,8 @@
DECLARE_INSTRUCTION(Goto);
- private:
- DISALLOW_COPY_AND_ASSIGN(HGoto);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Goto);
};
class HConstant : public HExpression<0> {
@@ -2733,8 +2799,8 @@
DECLARE_ABSTRACT_INSTRUCTION(Constant);
- private:
- DISALLOW_COPY_AND_ASSIGN(HConstant);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Constant);
};
class HNullConstant FINAL : public HConstant {
@@ -2752,12 +2818,14 @@
DECLARE_INSTRUCTION(NullConstant);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(NullConstant);
+
private:
explicit HNullConstant(uint32_t dex_pc = kNoDexPc)
: HConstant(DataType::Type::kReference, dex_pc) {}
friend class HGraph;
- DISALLOW_COPY_AND_ASSIGN(HNullConstant);
};
// Constants of the type int. Those can be from Dex instructions, or
@@ -2789,6 +2857,9 @@
DECLARE_INSTRUCTION(IntConstant);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(IntConstant);
+
private:
explicit HIntConstant(int32_t value, uint32_t dex_pc = kNoDexPc)
: HConstant(DataType::Type::kInt32, dex_pc), value_(value) {}
@@ -2800,7 +2871,6 @@
friend class HGraph;
ART_FRIEND_TEST(GraphTest, InsertInstructionBefore);
ART_FRIEND_TYPED_TEST(ParallelMoveTest, ConstantLast);
- DISALLOW_COPY_AND_ASSIGN(HIntConstant);
};
class HLongConstant FINAL : public HConstant {
@@ -2823,6 +2893,9 @@
DECLARE_INSTRUCTION(LongConstant);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(LongConstant);
+
private:
explicit HLongConstant(int64_t value, uint32_t dex_pc = kNoDexPc)
: HConstant(DataType::Type::kInt64, dex_pc), value_(value) {}
@@ -2830,7 +2903,6 @@
const int64_t value_;
friend class HGraph;
- DISALLOW_COPY_AND_ASSIGN(HLongConstant);
};
class HFloatConstant FINAL : public HConstant {
@@ -2872,6 +2944,9 @@
DECLARE_INSTRUCTION(FloatConstant);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(FloatConstant);
+
private:
explicit HFloatConstant(float value, uint32_t dex_pc = kNoDexPc)
: HConstant(DataType::Type::kFloat32, dex_pc), value_(value) {}
@@ -2883,7 +2958,6 @@
// Only the SsaBuilder and HGraph can create floating-point constants.
friend class SsaBuilder;
friend class HGraph;
- DISALLOW_COPY_AND_ASSIGN(HFloatConstant);
};
class HDoubleConstant FINAL : public HConstant {
@@ -2923,6 +2997,9 @@
DECLARE_INSTRUCTION(DoubleConstant);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(DoubleConstant);
+
private:
explicit HDoubleConstant(double value, uint32_t dex_pc = kNoDexPc)
: HConstant(DataType::Type::kFloat64, dex_pc), value_(value) {}
@@ -2934,7 +3011,6 @@
// Only the SsaBuilder and HGraph can create floating-point constants.
friend class SsaBuilder;
friend class HGraph;
- DISALLOW_COPY_AND_ASSIGN(HDoubleConstant);
};
// Conditional branch. A block ending with an HIf instruction must have
@@ -2946,6 +3022,7 @@
SetRawInputAt(0, input);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool IsControlFlow() const OVERRIDE { return true; }
HBasicBlock* IfTrueSuccessor() const {
@@ -2958,8 +3035,8 @@
DECLARE_INSTRUCTION(If);
- private:
- DISALLOW_COPY_AND_ASSIGN(HIf);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(If);
};
@@ -3012,6 +3089,9 @@
DECLARE_INSTRUCTION(TryBoundary);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(TryBoundary);
+
private:
static constexpr size_t kFieldBoundaryKind = kNumberOfGenericPackedBits;
static constexpr size_t kFieldBoundaryKindSize =
@@ -3021,8 +3101,6 @@
static_assert(kNumberOfTryBoundaryPackedBits <= kMaxNumberOfPackedBits,
"Too many packed fields.");
using BoundaryKindField = BitField<BoundaryKind, kFieldBoundaryKind, kFieldBoundaryKindSize>;
-
- DISALLOW_COPY_AND_ASSIGN(HTryBoundary);
};
// Deoptimize to interpreter, upon checking a condition.
@@ -3045,6 +3123,8 @@
SetRawInputAt(0, cond);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
// Use this constructor when the `HDeoptimize` guards an instruction, and any user
// that relies on the deoptimization to pass should have its input be the `HDeoptimize`
// instead of `guard`.
@@ -3098,6 +3178,9 @@
DECLARE_INSTRUCTION(Deoptimize);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Deoptimize);
+
private:
static constexpr size_t kFieldCanBeMoved = kNumberOfGenericPackedBits;
static constexpr size_t kFieldDeoptimizeKind = kNumberOfGenericPackedBits + 1;
@@ -3109,8 +3192,6 @@
"Too many packed fields.");
using DeoptimizeKindField =
BitField<DeoptimizationKind, kFieldDeoptimizeKind, kFieldDeoptimizeKindSize>;
-
- DISALLOW_COPY_AND_ASSIGN(HDeoptimize);
};
// Represents a should_deoptimize flag. Currently used for CHA-based devirtualization.
@@ -3136,8 +3217,8 @@
DECLARE_INSTRUCTION(ShouldDeoptimizeFlag);
- private:
- DISALLOW_COPY_AND_ASSIGN(HShouldDeoptimizeFlag);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(ShouldDeoptimizeFlag);
};
// Represents the ArtMethod that was passed as a first argument to
@@ -3150,8 +3231,8 @@
DECLARE_INSTRUCTION(CurrentMethod);
- private:
- DISALLOW_COPY_AND_ASSIGN(HCurrentMethod);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(CurrentMethod);
};
// Fetches an ArtMethod from the virtual table or the interface method table
@@ -3174,6 +3255,7 @@
SetRawInputAt(0, cls);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
return other->AsClassTableGet()->GetIndex() == index_ &&
@@ -3185,6 +3267,9 @@
DECLARE_INSTRUCTION(ClassTableGet);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(ClassTableGet);
+
private:
static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits;
static constexpr size_t kFieldTableKindSize =
@@ -3196,8 +3281,6 @@
// The index of the ArtMethod in the table.
const size_t index_;
-
- DISALLOW_COPY_AND_ASSIGN(HClassTableGet);
};
// PackedSwitch (jump table). A block ending with a PackedSwitch instruction will
@@ -3215,6 +3298,8 @@
SetRawInputAt(0, input);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
bool IsControlFlow() const OVERRIDE { return true; }
int32_t GetStartValue() const { return start_value_; }
@@ -3227,11 +3312,12 @@
}
DECLARE_INSTRUCTION(PackedSwitch);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(PackedSwitch);
+
private:
const int32_t start_value_;
const uint32_t num_entries_;
-
- DISALLOW_COPY_AND_ASSIGN(HPackedSwitch);
};
class HUnaryOperation : public HExpression<1> {
@@ -3241,6 +3327,9 @@
SetRawInputAt(0, input);
}
+ // All of the UnaryOperation instructions are clonable.
+ bool IsClonable() const OVERRIDE { return true; }
+
HInstruction* GetInput() const { return InputAt(0); }
DataType::Type GetResultType() const { return GetType(); }
@@ -3262,8 +3351,8 @@
DECLARE_ABSTRACT_INSTRUCTION(UnaryOperation);
- private:
- DISALLOW_COPY_AND_ASSIGN(HUnaryOperation);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(UnaryOperation);
};
class HBinaryOperation : public HExpression<2> {
@@ -3278,6 +3367,9 @@
SetRawInputAt(1, right);
}
+ // All of the BinaryOperation instructions are clonable.
+ bool IsClonable() const OVERRIDE { return true; }
+
HInstruction* GetLeft() const { return InputAt(0); }
HInstruction* GetRight() const { return InputAt(1); }
DataType::Type GetResultType() const { return GetType(); }
@@ -3352,8 +3444,8 @@
DECLARE_ABSTRACT_INSTRUCTION(BinaryOperation);
- private:
- DISALLOW_COPY_AND_ASSIGN(HBinaryOperation);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(BinaryOperation);
};
// The comparison bias applies for floating point operations and indicates how NaN
@@ -3443,8 +3535,7 @@
return GetBlock()->GetGraph()->GetIntConstant(value, dex_pc);
}
- private:
- DISALLOW_COPY_AND_ASSIGN(HCondition);
+ DEFAULT_COPY_CONSTRUCTOR(Condition);
};
// Instruction to check if two inputs are equal to each other.
@@ -3486,10 +3577,11 @@
return kCondNE;
}
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Equal);
+
private:
template <typename T> static bool Compute(T x, T y) { return x == y; }
-
- DISALLOW_COPY_AND_ASSIGN(HEqual);
};
class HNotEqual FINAL : public HCondition {
@@ -3529,10 +3621,11 @@
return kCondEQ;
}
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(NotEqual);
+
private:
template <typename T> static bool Compute(T x, T y) { return x != y; }
-
- DISALLOW_COPY_AND_ASSIGN(HNotEqual);
};
class HLessThan FINAL : public HCondition {
@@ -3566,10 +3659,11 @@
return kCondGE;
}
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(LessThan);
+
private:
template <typename T> static bool Compute(T x, T y) { return x < y; }
-
- DISALLOW_COPY_AND_ASSIGN(HLessThan);
};
class HLessThanOrEqual FINAL : public HCondition {
@@ -3603,10 +3697,11 @@
return kCondGT;
}
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(LessThanOrEqual);
+
private:
template <typename T> static bool Compute(T x, T y) { return x <= y; }
-
- DISALLOW_COPY_AND_ASSIGN(HLessThanOrEqual);
};
class HGreaterThan FINAL : public HCondition {
@@ -3640,10 +3735,11 @@
return kCondLE;
}
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(GreaterThan);
+
private:
template <typename T> static bool Compute(T x, T y) { return x > y; }
-
- DISALLOW_COPY_AND_ASSIGN(HGreaterThan);
};
class HGreaterThanOrEqual FINAL : public HCondition {
@@ -3677,10 +3773,11 @@
return kCondLT;
}
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(GreaterThanOrEqual);
+
private:
template <typename T> static bool Compute(T x, T y) { return x >= y; }
-
- DISALLOW_COPY_AND_ASSIGN(HGreaterThanOrEqual);
};
class HBelow FINAL : public HCondition {
@@ -3715,12 +3812,13 @@
return kCondAE;
}
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Below);
+
private:
template <typename T> static bool Compute(T x, T y) {
return MakeUnsigned(x) < MakeUnsigned(y);
}
-
- DISALLOW_COPY_AND_ASSIGN(HBelow);
};
class HBelowOrEqual FINAL : public HCondition {
@@ -3755,12 +3853,13 @@
return kCondA;
}
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(BelowOrEqual);
+
private:
template <typename T> static bool Compute(T x, T y) {
return MakeUnsigned(x) <= MakeUnsigned(y);
}
-
- DISALLOW_COPY_AND_ASSIGN(HBelowOrEqual);
};
class HAbove FINAL : public HCondition {
@@ -3795,12 +3894,13 @@
return kCondBE;
}
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Above);
+
private:
template <typename T> static bool Compute(T x, T y) {
return MakeUnsigned(x) > MakeUnsigned(y);
}
-
- DISALLOW_COPY_AND_ASSIGN(HAbove);
};
class HAboveOrEqual FINAL : public HCondition {
@@ -3835,12 +3935,13 @@
return kCondB;
}
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(AboveOrEqual);
+
private:
template <typename T> static bool Compute(T x, T y) {
return MakeUnsigned(x) >= MakeUnsigned(y);
}
-
- DISALLOW_COPY_AND_ASSIGN(HAboveOrEqual);
};
// Instruction to check how two inputs compare to each other.
@@ -3930,8 +4031,7 @@
return GetBlock()->GetGraph()->GetIntConstant(value, dex_pc);
}
- private:
- DISALLOW_COPY_AND_ASSIGN(HCompare);
+ DEFAULT_COPY_CONSTRUCTOR(Compare);
};
class HNewInstance FINAL : public HExpression<1> {
@@ -3950,6 +4050,8 @@
SetRawInputAt(0, cls);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
dex::TypeIndex GetTypeIndex() const { return type_index_; }
const DexFile& GetDexFile() const { return dex_file_; }
@@ -3986,6 +4088,9 @@
DECLARE_INSTRUCTION(NewInstance);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(NewInstance);
+
private:
static constexpr size_t kFlagFinalizable = kNumberOfExpressionPackedBits;
static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1;
@@ -3995,8 +4100,6 @@
const dex::TypeIndex type_index_;
const DexFile& dex_file_;
QuickEntrypointEnum entrypoint_;
-
- DISALLOW_COPY_AND_ASSIGN(HNewInstance);
};
enum IntrinsicNeedsEnvironmentOrCache {
@@ -4114,6 +4217,8 @@
SetPackedFlag<kFlagCanThrow>(true);
}
+ DEFAULT_COPY_CONSTRUCTOR(Invoke);
+
uint32_t number_of_arguments_;
ArtMethod* resolved_method_;
const uint32_t dex_method_index_;
@@ -4121,9 +4226,6 @@
// A magic word holding optimizations for intrinsics. See intrinsics.h.
uint32_t intrinsic_optimizations_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(HInvoke);
};
class HInvokeUnresolved FINAL : public HInvoke {
@@ -4144,10 +4246,12 @@
invoke_type) {
}
+ bool IsClonable() const OVERRIDE { return true; }
+
DECLARE_INSTRUCTION(InvokeUnresolved);
- private:
- DISALLOW_COPY_AND_ASSIGN(HInvokeUnresolved);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(InvokeUnresolved);
};
class HInvokePolymorphic FINAL : public HInvoke {
@@ -4166,10 +4270,12 @@
nullptr,
kVirtual) {}
+ bool IsClonable() const OVERRIDE { return true; }
+
DECLARE_INSTRUCTION(InvokePolymorphic);
- private:
- DISALLOW_COPY_AND_ASSIGN(HInvokePolymorphic);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(InvokePolymorphic);
};
class HInvokeStaticOrDirect FINAL : public HInvoke {
@@ -4256,6 +4362,8 @@
SetPackedField<ClinitCheckRequirementField>(clinit_check_requirement);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
void SetDispatchInfo(const DispatchInfo& dispatch_info) {
bool had_current_method_input = HasCurrentMethodInput();
bool needs_current_method_input = NeedsCurrentMethodInput(dispatch_info.method_load_kind);
@@ -4401,6 +4509,9 @@
DECLARE_INSTRUCTION(InvokeStaticOrDirect);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(InvokeStaticOrDirect);
+
private:
static constexpr size_t kFieldClinitCheckRequirement = kNumberOfInvokePackedBits;
static constexpr size_t kFieldClinitCheckRequirementSize =
@@ -4416,8 +4527,6 @@
// Cached values of the resolved method, to avoid needing the mutator lock.
MethodReference target_method_;
DispatchInfo dispatch_info_;
-
- DISALLOW_COPY_AND_ASSIGN(HInvokeStaticOrDirect);
};
std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind rhs);
std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckRequirement rhs);
@@ -4441,6 +4550,8 @@
kVirtual),
vtable_index_(vtable_index) {}
+ bool IsClonable() const OVERRIDE { return true; }
+
bool CanBeNull() const OVERRIDE {
switch (GetIntrinsic()) {
case Intrinsics::kThreadCurrentThread:
@@ -4463,11 +4574,12 @@
DECLARE_INSTRUCTION(InvokeVirtual);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(InvokeVirtual);
+
private:
// Cached value of the resolved method, to avoid needing the mutator lock.
const uint32_t vtable_index_;
-
- DISALLOW_COPY_AND_ASSIGN(HInvokeVirtual);
};
class HInvokeInterface FINAL : public HInvoke {
@@ -4489,6 +4601,8 @@
kInterface),
imt_index_(imt_index) {}
+ bool IsClonable() const OVERRIDE { return true; }
+
bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
// TODO: Add implicit null checks in intrinsics.
return (obj == InputAt(0)) && !GetLocations()->Intrinsified();
@@ -4504,11 +4618,12 @@
DECLARE_INSTRUCTION(InvokeInterface);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(InvokeInterface);
+
private:
// Cached value of the resolved method, to avoid needing the mutator lock.
const uint32_t imt_index_;
-
- DISALLOW_COPY_AND_ASSIGN(HInvokeInterface);
};
class HNeg FINAL : public HUnaryOperation {
@@ -4535,8 +4650,8 @@
DECLARE_INSTRUCTION(Neg);
- private:
- DISALLOW_COPY_AND_ASSIGN(HNeg);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Neg);
};
class HNewArray FINAL : public HExpression<2> {
@@ -4547,6 +4662,8 @@
SetRawInputAt(1, length);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
// Calls runtime so needs an environment.
bool NeedsEnvironment() const OVERRIDE { return true; }
@@ -4566,8 +4683,8 @@
DECLARE_INSTRUCTION(NewArray);
- private:
- DISALLOW_COPY_AND_ASSIGN(HNewArray);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(NewArray);
};
class HAdd FINAL : public HBinaryOperation {
@@ -4601,8 +4718,8 @@
DECLARE_INSTRUCTION(Add);
- private:
- DISALLOW_COPY_AND_ASSIGN(HAdd);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Add);
};
class HSub FINAL : public HBinaryOperation {
@@ -4634,8 +4751,8 @@
DECLARE_INSTRUCTION(Sub);
- private:
- DISALLOW_COPY_AND_ASSIGN(HSub);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Sub);
};
class HMul FINAL : public HBinaryOperation {
@@ -4669,8 +4786,8 @@
DECLARE_INSTRUCTION(Mul);
- private:
- DISALLOW_COPY_AND_ASSIGN(HMul);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Mul);
};
class HDiv FINAL : public HBinaryOperation {
@@ -4716,8 +4833,8 @@
DECLARE_INSTRUCTION(Div);
- private:
- DISALLOW_COPY_AND_ASSIGN(HDiv);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Div);
};
class HRem FINAL : public HBinaryOperation {
@@ -4763,8 +4880,8 @@
DECLARE_INSTRUCTION(Rem);
- private:
- DISALLOW_COPY_AND_ASSIGN(HRem);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Rem);
};
class HDivZeroCheck FINAL : public HExpression<1> {
@@ -4789,8 +4906,8 @@
DECLARE_INSTRUCTION(DivZeroCheck);
- private:
- DISALLOW_COPY_AND_ASSIGN(HDivZeroCheck);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(DivZeroCheck);
};
class HShl FINAL : public HBinaryOperation {
@@ -4835,8 +4952,8 @@
DECLARE_INSTRUCTION(Shl);
- private:
- DISALLOW_COPY_AND_ASSIGN(HShl);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Shl);
};
class HShr FINAL : public HBinaryOperation {
@@ -4881,8 +4998,8 @@
DECLARE_INSTRUCTION(Shr);
- private:
- DISALLOW_COPY_AND_ASSIGN(HShr);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Shr);
};
class HUShr FINAL : public HBinaryOperation {
@@ -4929,8 +5046,8 @@
DECLARE_INSTRUCTION(UShr);
- private:
- DISALLOW_COPY_AND_ASSIGN(HUShr);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(UShr);
};
class HAnd FINAL : public HBinaryOperation {
@@ -4966,8 +5083,8 @@
DECLARE_INSTRUCTION(And);
- private:
- DISALLOW_COPY_AND_ASSIGN(HAnd);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(And);
};
class HOr FINAL : public HBinaryOperation {
@@ -5003,8 +5120,8 @@
DECLARE_INSTRUCTION(Or);
- private:
- DISALLOW_COPY_AND_ASSIGN(HOr);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Or);
};
class HXor FINAL : public HBinaryOperation {
@@ -5040,8 +5157,8 @@
DECLARE_INSTRUCTION(Xor);
- private:
- DISALLOW_COPY_AND_ASSIGN(HXor);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Xor);
};
class HRor FINAL : public HBinaryOperation {
@@ -5091,8 +5208,8 @@
DECLARE_INSTRUCTION(Ror);
- private:
- DISALLOW_COPY_AND_ASSIGN(HRor);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Ror);
};
// The value of a parameter in this method. Its location depends on
@@ -5122,6 +5239,9 @@
DECLARE_INSTRUCTION(ParameterValue);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(ParameterValue);
+
private:
// Whether or not the parameter value corresponds to 'this' argument.
static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits;
@@ -5135,8 +5255,6 @@
// The index of this parameter in the parameters list. Must be less
// than HGraph::number_of_in_vregs_.
const uint8_t index_;
-
- DISALLOW_COPY_AND_ASSIGN(HParameterValue);
};
class HNot FINAL : public HUnaryOperation {
@@ -5168,8 +5286,8 @@
DECLARE_INSTRUCTION(Not);
- private:
- DISALLOW_COPY_AND_ASSIGN(HNot);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Not);
};
class HBooleanNot FINAL : public HUnaryOperation {
@@ -5205,8 +5323,8 @@
DECLARE_INSTRUCTION(BooleanNot);
- private:
- DISALLOW_COPY_AND_ASSIGN(HBooleanNot);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(BooleanNot);
};
class HTypeConversion FINAL : public HExpression<1> {
@@ -5234,8 +5352,8 @@
DECLARE_INSTRUCTION(TypeConversion);
- private:
- DISALLOW_COPY_AND_ASSIGN(HTypeConversion);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(TypeConversion);
};
static constexpr uint32_t kNoRegNumber = -1;
@@ -5249,6 +5367,7 @@
SetRawInputAt(0, value);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
return true;
@@ -5260,11 +5379,10 @@
bool CanBeNull() const OVERRIDE { return false; }
-
DECLARE_INSTRUCTION(NullCheck);
- private:
- DISALLOW_COPY_AND_ASSIGN(HNullCheck);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(NullCheck);
};
// Embeds an ArtField and all the information required by the compiler. We cache
@@ -5326,6 +5444,7 @@
SetRawInputAt(0, value);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return !IsVolatile(); }
bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
@@ -5355,10 +5474,11 @@
DECLARE_INSTRUCTION(InstanceFieldGet);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(InstanceFieldGet);
+
private:
const FieldInfo field_info_;
-
- DISALLOW_COPY_AND_ASSIGN(HInstanceFieldGet);
};
class HInstanceFieldSet FINAL : public HTemplateInstruction<2> {
@@ -5386,6 +5506,8 @@
SetRawInputAt(1, value);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
return (obj == InputAt(0)) && art::CanDoImplicitNullCheckOn(GetFieldOffset().Uint32Value());
}
@@ -5400,6 +5522,9 @@
DECLARE_INSTRUCTION(InstanceFieldSet);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(InstanceFieldSet);
+
private:
static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits;
static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagValueCanBeNull + 1;
@@ -5407,8 +5532,6 @@
"Too many packed fields.");
const FieldInfo field_info_;
-
- DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet);
};
class HArrayGet FINAL : public HExpression<2> {
@@ -5436,6 +5559,7 @@
SetRawInputAt(1, index);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
return true;
@@ -5485,6 +5609,9 @@
DECLARE_INSTRUCTION(ArrayGet);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(ArrayGet);
+
private:
// We treat a String as an array, creating the HArrayGet from String.charAt()
// intrinsic in the instruction simplifier. We can always determine whether
@@ -5495,8 +5622,6 @@
static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1;
static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits,
"Too many packed fields.");
-
- DISALLOW_COPY_AND_ASSIGN(HArrayGet);
};
class HArraySet FINAL : public HTemplateInstruction<3> {
@@ -5530,6 +5655,8 @@
SetRawInputAt(2, value);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
bool NeedsEnvironment() const OVERRIDE {
// We call a runtime method to throw ArrayStoreException.
return NeedsTypeCheck();
@@ -5595,6 +5722,9 @@
DECLARE_INSTRUCTION(ArraySet);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(ArraySet);
+
private:
static constexpr size_t kFieldExpectedComponentType = kNumberOfGenericPackedBits;
static constexpr size_t kFieldExpectedComponentTypeSize =
@@ -5610,8 +5740,6 @@
static_assert(kNumberOfArraySetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
using ExpectedComponentTypeField =
BitField<DataType::Type, kFieldExpectedComponentType, kFieldExpectedComponentTypeSize>;
-
- DISALLOW_COPY_AND_ASSIGN(HArraySet);
};
class HArrayLength FINAL : public HExpression<1> {
@@ -5624,6 +5752,7 @@
SetRawInputAt(0, array);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
return true;
@@ -5636,6 +5765,9 @@
DECLARE_INSTRUCTION(ArrayLength);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(ArrayLength);
+
private:
// We treat a String as an array, creating the HArrayLength from String.length()
// or String.isEmpty() intrinsic in the instruction simplifier. We can always
@@ -5646,8 +5778,6 @@
static constexpr size_t kNumberOfArrayLengthPackedBits = kFlagIsStringLength + 1;
static_assert(kNumberOfArrayLengthPackedBits <= HInstruction::kMaxNumberOfPackedBits,
"Too many packed fields.");
-
- DISALLOW_COPY_AND_ASSIGN(HArrayLength);
};
class HBoundsCheck FINAL : public HExpression<2> {
@@ -5665,6 +5795,7 @@
SetRawInputAt(1, length);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
return true;
@@ -5680,10 +5811,11 @@
DECLARE_INSTRUCTION(BoundsCheck);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(BoundsCheck);
+
private:
static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits;
-
- DISALLOW_COPY_AND_ASSIGN(HBoundsCheck);
};
class HSuspendCheck FINAL : public HTemplateInstruction<0> {
@@ -5691,6 +5823,8 @@
explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc)
: HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) {}
+ bool IsClonable() const OVERRIDE { return true; }
+
bool NeedsEnvironment() const OVERRIDE {
return true;
}
@@ -5700,12 +5834,13 @@
DECLARE_INSTRUCTION(SuspendCheck);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(SuspendCheck);
+
private:
// Only used for code generation, in order to share the same slow path between back edges
// of a same loop.
SlowPathCode* slow_path_;
-
- DISALLOW_COPY_AND_ASSIGN(HSuspendCheck);
};
// Pseudo-instruction which provides the native debugger with mapping information.
@@ -5721,8 +5856,8 @@
DECLARE_INSTRUCTION(NativeDebugInfo);
- private:
- DISALLOW_COPY_AND_ASSIGN(HNativeDebugInfo);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(NativeDebugInfo);
};
/**
@@ -5788,6 +5923,8 @@
SetPackedFlag<kFlagGenerateClInitCheck>(false);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
void SetLoadKind(LoadKind load_kind);
LoadKind GetLoadKind() const {
@@ -5879,6 +6016,9 @@
DECLARE_INSTRUCTION(LoadClass);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(LoadClass);
+
private:
static constexpr size_t kFlagNeedsAccessCheck = kNumberOfGenericPackedBits;
static constexpr size_t kFlagIsInBootImage = kFlagNeedsAccessCheck + 1;
@@ -5918,8 +6058,6 @@
Handle<mirror::Class> klass_;
ReferenceTypeInfo loaded_class_rti_;
-
- DISALLOW_COPY_AND_ASSIGN(HLoadClass);
};
std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs);
@@ -5977,6 +6115,8 @@
SetPackedField<LoadKindField>(LoadKind::kRuntimeCall);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
void SetLoadKind(LoadKind load_kind);
LoadKind GetLoadKind() const {
@@ -6043,6 +6183,9 @@
DECLARE_INSTRUCTION(LoadString);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(LoadString);
+
private:
static constexpr size_t kFieldLoadKind = kNumberOfGenericPackedBits;
static constexpr size_t kFieldLoadKindSize =
@@ -6062,8 +6205,6 @@
const DexFile& dex_file_;
Handle<mirror::String> string_;
-
- DISALLOW_COPY_AND_ASSIGN(HLoadString);
};
std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs);
@@ -6095,6 +6236,7 @@
SetRawInputAt(0, constant);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
return true;
@@ -6114,8 +6256,9 @@
DECLARE_INSTRUCTION(ClinitCheck);
- private:
- DISALLOW_COPY_AND_ASSIGN(HClinitCheck);
+
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(ClinitCheck);
};
class HStaticFieldGet FINAL : public HExpression<1> {
@@ -6141,6 +6284,7 @@
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return !IsVolatile(); }
bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
@@ -6166,10 +6310,11 @@
DECLARE_INSTRUCTION(StaticFieldGet);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(StaticFieldGet);
+
private:
const FieldInfo field_info_;
-
- DISALLOW_COPY_AND_ASSIGN(HStaticFieldGet);
};
class HStaticFieldSet FINAL : public HTemplateInstruction<2> {
@@ -6197,6 +6342,7 @@
SetRawInputAt(1, value);
}
+ bool IsClonable() const OVERRIDE { return true; }
const FieldInfo& GetFieldInfo() const { return field_info_; }
MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
DataType::Type GetFieldType() const { return field_info_.GetFieldType(); }
@@ -6208,6 +6354,9 @@
DECLARE_INSTRUCTION(StaticFieldSet);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(StaticFieldSet);
+
private:
static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits;
static constexpr size_t kNumberOfStaticFieldSetPackedBits = kFlagValueCanBeNull + 1;
@@ -6215,8 +6364,6 @@
"Too many packed fields.");
const FieldInfo field_info_;
-
- DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet);
};
class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> {
@@ -6230,6 +6377,7 @@
SetRawInputAt(0, obj);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool NeedsEnvironment() const OVERRIDE { return true; }
bool CanThrow() const OVERRIDE { return true; }
@@ -6238,10 +6386,11 @@
DECLARE_INSTRUCTION(UnresolvedInstanceFieldGet);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(UnresolvedInstanceFieldGet);
+
private:
const uint32_t field_index_;
-
- DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldGet);
};
class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> {
@@ -6259,6 +6408,7 @@
SetRawInputAt(1, value);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool NeedsEnvironment() const OVERRIDE { return true; }
bool CanThrow() const OVERRIDE { return true; }
@@ -6267,6 +6417,9 @@
DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(UnresolvedInstanceFieldSet);
+
private:
static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits;
static constexpr size_t kFieldFieldTypeSize =
@@ -6278,8 +6431,6 @@
using FieldTypeField = BitField<DataType::Type, kFieldFieldType, kFieldFieldTypeSize>;
const uint32_t field_index_;
-
- DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet);
};
class HUnresolvedStaticFieldGet FINAL : public HExpression<0> {
@@ -6291,6 +6442,7 @@
field_index_(field_index) {
}
+ bool IsClonable() const OVERRIDE { return true; }
bool NeedsEnvironment() const OVERRIDE { return true; }
bool CanThrow() const OVERRIDE { return true; }
@@ -6299,10 +6451,11 @@
DECLARE_INSTRUCTION(UnresolvedStaticFieldGet);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(UnresolvedStaticFieldGet);
+
private:
const uint32_t field_index_;
-
- DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldGet);
};
class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> {
@@ -6318,6 +6471,7 @@
SetRawInputAt(0, value);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool NeedsEnvironment() const OVERRIDE { return true; }
bool CanThrow() const OVERRIDE { return true; }
@@ -6326,6 +6480,9 @@
DECLARE_INSTRUCTION(UnresolvedStaticFieldSet);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(UnresolvedStaticFieldSet);
+
private:
static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits;
static constexpr size_t kFieldFieldTypeSize =
@@ -6337,8 +6494,6 @@
using FieldTypeField = BitField<DataType::Type, kFieldFieldType, kFieldFieldTypeSize>;
const uint32_t field_index_;
-
- DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet);
};
// Implement the move-exception DEX instruction.
@@ -6351,8 +6506,8 @@
DECLARE_INSTRUCTION(LoadException);
- private:
- DISALLOW_COPY_AND_ASSIGN(HLoadException);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(LoadException);
};
// Implicit part of move-exception which clears thread-local exception storage.
@@ -6364,8 +6519,8 @@
DECLARE_INSTRUCTION(ClearException);
- private:
- DISALLOW_COPY_AND_ASSIGN(HClearException);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(ClearException);
};
class HThrow FINAL : public HTemplateInstruction<1> {
@@ -6381,11 +6536,10 @@
bool CanThrow() const OVERRIDE { return true; }
-
DECLARE_INSTRUCTION(Throw);
- private:
- DISALLOW_COPY_AND_ASSIGN(HThrow);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Throw);
};
/**
@@ -6420,6 +6574,7 @@
SetRawInputAt(1, constant);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
@@ -6447,6 +6602,9 @@
DECLARE_INSTRUCTION(InstanceOf);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(InstanceOf);
+
private:
static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits;
static constexpr size_t kFieldTypeCheckKindSize =
@@ -6455,8 +6613,6 @@
static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1;
static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
-
- DISALLOW_COPY_AND_ASSIGN(HInstanceOf);
};
class HBoundType FINAL : public HExpression<1> {
@@ -6470,6 +6626,8 @@
SetRawInputAt(0, input);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
// {Get,Set}Upper* should only be used in reference type propagation.
const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; }
bool GetUpperCanBeNull() const { return GetPackedFlag<kFlagUpperCanBeNull>(); }
@@ -6484,6 +6642,9 @@
DECLARE_INSTRUCTION(BoundType);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(BoundType);
+
private:
// Represents the top constraint that can_be_null_ cannot exceed (i.e. if this
// is false then CanBeNull() cannot be true).
@@ -6499,8 +6660,6 @@
// // uper_bound_ will be ClassX
// }
ReferenceTypeInfo upper_bound_;
-
- DISALLOW_COPY_AND_ASSIGN(HBoundType);
};
class HCheckCast FINAL : public HTemplateInstruction<2> {
@@ -6516,6 +6675,7 @@
SetRawInputAt(1, constant);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
@@ -6536,6 +6696,9 @@
DECLARE_INSTRUCTION(CheckCast);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(CheckCast);
+
private:
static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits;
static constexpr size_t kFieldTypeCheckKindSize =
@@ -6544,8 +6707,6 @@
static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1;
static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
-
- DISALLOW_COPY_AND_ASSIGN(HCheckCast);
};
/**
@@ -6582,10 +6743,15 @@
SetPackedField<BarrierKindField>(barrier_kind);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
MemBarrierKind GetBarrierKind() { return GetPackedField<BarrierKindField>(); }
DECLARE_INSTRUCTION(MemoryBarrier);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(MemoryBarrier);
+
private:
static constexpr size_t kFieldBarrierKind = HInstruction::kNumberOfGenericPackedBits;
static constexpr size_t kFieldBarrierKindSize =
@@ -6595,8 +6761,6 @@
static_assert(kNumberOfMemoryBarrierPackedBits <= kMaxNumberOfPackedBits,
"Too many packed fields.");
using BarrierKindField = BitField<MemBarrierKind, kFieldBarrierKind, kFieldBarrierKindSize>;
-
- DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier);
};
// A constructor fence orders all prior stores to fields that could be accessed via a final field of
@@ -6747,8 +6911,8 @@
DECLARE_INSTRUCTION(ConstructorFence);
- private:
- DISALLOW_COPY_AND_ASSIGN(HConstructorFence);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(ConstructorFence);
};
class HMonitorOperation FINAL : public HTemplateInstruction<1> {
@@ -6782,6 +6946,9 @@
DECLARE_INSTRUCTION(MonitorOperation);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(MonitorOperation);
+
private:
static constexpr size_t kFieldOperationKind = HInstruction::kNumberOfGenericPackedBits;
static constexpr size_t kFieldOperationKindSize =
@@ -6791,9 +6958,6 @@
static_assert(kNumberOfMonitorOperationPackedBits <= HInstruction::kMaxNumberOfPackedBits,
"Too many packed fields.");
using OperationKindField = BitField<OperationKind, kFieldOperationKind, kFieldOperationKindSize>;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(HMonitorOperation);
};
class HSelect FINAL : public HExpression<3> {
@@ -6814,6 +6978,7 @@
SetRawInputAt(2, condition);
}
+ bool IsClonable() const OVERRIDE { return true; }
HInstruction* GetFalseValue() const { return InputAt(0); }
HInstruction* GetTrueValue() const { return InputAt(1); }
HInstruction* GetCondition() const { return InputAt(2); }
@@ -6829,8 +6994,8 @@
DECLARE_INSTRUCTION(Select);
- private:
- DISALLOW_COPY_AND_ASSIGN(HSelect);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(Select);
};
class MoveOperands : public ArenaObject<kArenaAllocMoveOperands> {
@@ -6961,10 +7126,11 @@
DECLARE_INSTRUCTION(ParallelMove);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(ParallelMove);
+
private:
ArenaVector<MoveOperands> moves_;
-
- DISALLOW_COPY_AND_ASSIGN(HParallelMove);
};
// This instruction computes an intermediate address pointing in the 'middle' of an object. The
@@ -6983,6 +7149,7 @@
SetRawInputAt(1, offset);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
return true;
@@ -6994,8 +7161,8 @@
DECLARE_INSTRUCTION(IntermediateAddress);
- private:
- DISALLOW_COPY_AND_ASSIGN(HIntermediateAddress);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(IntermediateAddress);
};
@@ -7070,6 +7237,33 @@
DISALLOW_COPY_AND_ASSIGN(HGraphDelegateVisitor);
};
+// Create a clone of the instruction, insert it into the graph; replace the old one with a new
+// and remove the old instruction.
+HInstruction* ReplaceInstrOrPhiByClone(HInstruction* instr);
+
+// Create a clone for each clonable instructions/phis and replace the original with the clone.
+//
+// Used for testing individual instruction cloner.
+class CloneAndReplaceInstructionVisitor : public HGraphDelegateVisitor {
+ public:
+ explicit CloneAndReplaceInstructionVisitor(HGraph* graph)
+ : HGraphDelegateVisitor(graph), instr_replaced_by_clones_count(0) {}
+
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ if (instruction->IsClonable()) {
+ ReplaceInstrOrPhiByClone(instruction);
+ instr_replaced_by_clones_count++;
+ }
+ }
+
+ size_t GetInstrReplacedByClonesCount() const { return instr_replaced_by_clones_count; }
+
+ private:
+ size_t instr_replaced_by_clones_count;
+
+ DISALLOW_COPY_AND_ASSIGN(CloneAndReplaceInstructionVisitor);
+};
+
// Iterator over the blocks that art part of the loop. Includes blocks part
// of an inner loop. The order in which the blocks are iterated is on their
// block id.
diff --git a/compiler/optimizing/nodes_mips.h b/compiler/optimizing/nodes_mips.h
index ef388c3..2c0595e 100644
--- a/compiler/optimizing/nodes_mips.h
+++ b/compiler/optimizing/nodes_mips.h
@@ -30,8 +30,8 @@
DECLARE_INSTRUCTION(MipsComputeBaseMethodAddress);
- private:
- DISALLOW_COPY_AND_ASSIGN(HMipsComputeBaseMethodAddress);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(MipsComputeBaseMethodAddress);
};
// Mips version of HPackedSwitch that holds a pointer to the base method address.
@@ -62,11 +62,12 @@
DECLARE_INSTRUCTION(MipsPackedSwitch);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(MipsPackedSwitch);
+
private:
const int32_t start_value_;
const int32_t num_entries_;
-
- DISALLOW_COPY_AND_ASSIGN(HMipsPackedSwitch);
};
// This instruction computes part of the array access offset (index offset).
@@ -105,8 +106,8 @@
DECLARE_INSTRUCTION(IntermediateArrayAddressIndex);
- private:
- DISALLOW_COPY_AND_ASSIGN(HIntermediateArrayAddressIndex);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(IntermediateArrayAddressIndex);
};
} // namespace art
diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h
index 7b4f5f7..e837f1e 100644
--- a/compiler/optimizing/nodes_shared.h
+++ b/compiler/optimizing/nodes_shared.h
@@ -38,6 +38,8 @@
SetRawInputAt(kInputMulRightIndex, mul_right);
}
+ bool IsClonable() const OVERRIDE { return true; }
+
static constexpr int kInputAccumulatorIndex = 0;
static constexpr int kInputMulLeftIndex = 1;
static constexpr int kInputMulRightIndex = 2;
@@ -51,11 +53,12 @@
DECLARE_INSTRUCTION(MultiplyAccumulate);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(MultiplyAccumulate);
+
private:
// Indicates if this is a MADD or MSUB.
const InstructionKind op_kind_;
-
- DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate);
};
class HBitwiseNegatedRight FINAL : public HBinaryOperation {
@@ -111,11 +114,12 @@
DECLARE_INSTRUCTION(BitwiseNegatedRight);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(BitwiseNegatedRight);
+
private:
// Specifies the bitwise operation, which will be then negated.
const InstructionKind op_kind_;
-
- DISALLOW_COPY_AND_ASSIGN(HBitwiseNegatedRight);
};
// This instruction computes part of the array access offset (data and index offset).
@@ -145,6 +149,7 @@
SetRawInputAt(2, shift);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
return true;
@@ -157,8 +162,8 @@
DECLARE_INSTRUCTION(IntermediateAddressIndex);
- private:
- DISALLOW_COPY_AND_ASSIGN(HIntermediateAddressIndex);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(IntermediateAddressIndex);
};
class HDataProcWithShifterOp FINAL : public HExpression<2> {
@@ -198,6 +203,7 @@
SetRawInputAt(1, right);
}
+ bool IsClonable() const OVERRIDE { return true; }
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(const HInstruction* other_instr) const OVERRIDE {
const HDataProcWithShifterOp* other = other_instr->AsDataProcWithShifterOp();
@@ -225,14 +231,15 @@
DECLARE_INSTRUCTION(DataProcWithShifterOp);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(DataProcWithShifterOp);
+
private:
InstructionKind instr_kind_;
OpKind op_kind_;
int shift_amount_;
friend std::ostream& operator<<(std::ostream& os, OpKind op);
-
- DISALLOW_COPY_AND_ASSIGN(HDataProcWithShifterOp);
};
std::ostream& operator<<(std::ostream& os, const HDataProcWithShifterOp::OpKind op);
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 17540b9..59d5b9f 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -161,10 +161,10 @@
static_assert(kNumberOfVectorOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>;
+ DEFAULT_COPY_CONSTRUCTOR(VecOperation);
+
private:
const size_t vector_length_;
-
- DISALLOW_COPY_AND_ASSIGN(HVecOperation);
};
// Abstraction of a unary vector operation.
@@ -188,8 +188,8 @@
DECLARE_ABSTRACT_INSTRUCTION(VecUnaryOperation);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecUnaryOperation);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecUnaryOperation);
};
// Abstraction of a binary vector operation.
@@ -216,8 +216,8 @@
DECLARE_ABSTRACT_INSTRUCTION(VecBinaryOperation);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecBinaryOperation);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecBinaryOperation);
};
// Abstraction of a vector operation that references memory, with an alignment.
@@ -255,10 +255,11 @@
DECLARE_ABSTRACT_INSTRUCTION(VecMemoryOperation);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecMemoryOperation);
+
private:
Alignment alignment_;
-
- DISALLOW_COPY_AND_ASSIGN(HVecMemoryOperation);
};
// Packed type consistency checker ("same vector length" integral types may mix freely).
@@ -296,8 +297,8 @@
DECLARE_INSTRUCTION(VecReplicateScalar);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecReplicateScalar);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecReplicateScalar);
};
// Extracts a particular scalar from the given vector,
@@ -329,8 +330,8 @@
DECLARE_INSTRUCTION(VecExtractScalar);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecExtractScalar);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecExtractScalar);
};
// Reduces the given vector into the first element as sum/min/max,
@@ -367,10 +368,11 @@
DECLARE_INSTRUCTION(VecReduce);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecReduce);
+
private:
const ReductionKind kind_;
-
- DISALLOW_COPY_AND_ASSIGN(HVecReduce);
};
// Converts every component in the vector,
@@ -394,8 +396,8 @@
DECLARE_INSTRUCTION(VecCnv);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecCnv);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecCnv);
};
// Negates every component in the vector,
@@ -415,8 +417,8 @@
DECLARE_INSTRUCTION(VecNeg);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecNeg);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecNeg);
};
// Takes absolute value of every component in the vector,
@@ -437,8 +439,8 @@
DECLARE_INSTRUCTION(VecAbs);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecAbs);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecAbs);
};
// Bitwise- or boolean-nots every component in the vector,
@@ -459,8 +461,8 @@
DECLARE_INSTRUCTION(VecNot);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecNot);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecNot);
};
//
@@ -486,8 +488,8 @@
DECLARE_INSTRUCTION(VecAdd);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecAdd);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecAdd);
};
// Performs halving add on every component in the two vectors, viz.
@@ -531,14 +533,15 @@
DECLARE_INSTRUCTION(VecHalvingAdd);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecHalvingAdd);
+
private:
// Additional packed bits.
static constexpr size_t kFieldHAddIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits;
static constexpr size_t kFieldHAddIsRounded = kFieldHAddIsUnsigned + 1;
static constexpr size_t kNumberOfHAddPackedBits = kFieldHAddIsRounded + 1;
static_assert(kNumberOfHAddPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
-
- DISALLOW_COPY_AND_ASSIGN(HVecHalvingAdd);
};
// Subtracts every component in the two vectors,
@@ -560,8 +563,8 @@
DECLARE_INSTRUCTION(VecSub);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecSub);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecSub);
};
// Multiplies every component in the two vectors,
@@ -583,8 +586,8 @@
DECLARE_INSTRUCTION(VecMul);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecMul);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecMul);
};
// Divides every component in the two vectors,
@@ -606,8 +609,8 @@
DECLARE_INSTRUCTION(VecDiv);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecDiv);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecDiv);
};
// Takes minimum of every component in the two vectors,
@@ -645,13 +648,14 @@
DECLARE_INSTRUCTION(VecMin);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecMin);
+
private:
// Additional packed bits.
static constexpr size_t kFieldMinOpIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits;
static constexpr size_t kNumberOfMinOpPackedBits = kFieldMinOpIsUnsigned + 1;
static_assert(kNumberOfMinOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
-
- DISALLOW_COPY_AND_ASSIGN(HVecMin);
};
// Takes maximum of every component in the two vectors,
@@ -689,13 +693,14 @@
DECLARE_INSTRUCTION(VecMax);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecMax);
+
private:
// Additional packed bits.
static constexpr size_t kFieldMaxOpIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits;
static constexpr size_t kNumberOfMaxOpPackedBits = kFieldMaxOpIsUnsigned + 1;
static_assert(kNumberOfMaxOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
-
- DISALLOW_COPY_AND_ASSIGN(HVecMax);
};
// Bitwise-ands every component in the two vectors,
@@ -716,8 +721,8 @@
DECLARE_INSTRUCTION(VecAnd);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecAnd);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecAnd);
};
// Bitwise-and-nots every component in the two vectors,
@@ -738,8 +743,8 @@
DECLARE_INSTRUCTION(VecAndNot);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecAndNot);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecAndNot);
};
// Bitwise-ors every component in the two vectors,
@@ -760,8 +765,8 @@
DECLARE_INSTRUCTION(VecOr);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecOr);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecOr);
};
// Bitwise-xors every component in the two vectors,
@@ -782,8 +787,8 @@
DECLARE_INSTRUCTION(VecXor);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecXor);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecXor);
};
// Logically shifts every component in the vector left by the given distance,
@@ -804,8 +809,8 @@
DECLARE_INSTRUCTION(VecShl);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecShl);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecShl);
};
// Arithmetically shifts every component in the vector right by the given distance,
@@ -826,8 +831,8 @@
DECLARE_INSTRUCTION(VecShr);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecShr);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecShr);
};
// Logically shifts every component in the vector right by the given distance,
@@ -848,8 +853,8 @@
DECLARE_INSTRUCTION(VecUShr);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecUShr);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecUShr);
};
//
@@ -885,8 +890,8 @@
DECLARE_INSTRUCTION(VecSetScalars);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecSetScalars);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecSetScalars);
};
// Multiplies every component in the two vectors, adds the result vector to the accumulator vector,
@@ -929,11 +934,12 @@
DECLARE_INSTRUCTION(VecMultiplyAccumulate);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecMultiplyAccumulate);
+
private:
// Indicates if this is a MADD or MSUB.
const InstructionKind op_kind_;
-
- DISALLOW_COPY_AND_ASSIGN(HVecMultiplyAccumulate);
};
// Takes the absolute difference of two vectors, and adds the results to
@@ -968,8 +974,8 @@
DECLARE_INSTRUCTION(VecSADAccumulate);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecSADAccumulate);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecSADAccumulate);
};
// Loads a vector from memory, viz. load(mem, 1)
@@ -1007,13 +1013,14 @@
DECLARE_INSTRUCTION(VecLoad);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecLoad);
+
private:
// Additional packed bits.
static constexpr size_t kFieldIsStringCharAt = HVecOperation::kNumberOfVectorOpPackedBits;
static constexpr size_t kNumberOfVecLoadPackedBits = kFieldIsStringCharAt + 1;
static_assert(kNumberOfVecLoadPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
-
- DISALLOW_COPY_AND_ASSIGN(HVecLoad);
};
// Stores a vector to memory, viz. store(m, 1, [x1, .. , xn] )
@@ -1045,8 +1052,8 @@
DECLARE_INSTRUCTION(VecStore);
- private:
- DISALLOW_COPY_AND_ASSIGN(HVecStore);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(VecStore)
};
} // namespace art
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index 22e92ea..6326065 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -30,8 +30,8 @@
DECLARE_INSTRUCTION(X86ComputeBaseMethodAddress);
- private:
- DISALLOW_COPY_AND_ASSIGN(HX86ComputeBaseMethodAddress);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(X86ComputeBaseMethodAddress);
};
// Load a constant value from the constant table.
@@ -54,8 +54,8 @@
DECLARE_INSTRUCTION(X86LoadFromConstantTable);
- private:
- DISALLOW_COPY_AND_ASSIGN(HX86LoadFromConstantTable);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(X86LoadFromConstantTable);
};
// Version of HNeg with access to the constant table for FP types.
@@ -77,8 +77,8 @@
DECLARE_INSTRUCTION(X86FPNeg);
- private:
- DISALLOW_COPY_AND_ASSIGN(HX86FPNeg);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(X86FPNeg);
};
// X86 version of HPackedSwitch that holds a pointer to the base method address.
@@ -113,11 +113,12 @@
DECLARE_INSTRUCTION(X86PackedSwitch);
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(X86PackedSwitch);
+
private:
const int32_t start_value_;
const int32_t num_entries_;
-
- DISALLOW_COPY_AND_ASSIGN(HX86PackedSwitch);
};
} // namespace art
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 84ccaa0..a7af193 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -1202,9 +1202,11 @@
// Positions and locals table in the debug info.
bool is_static = (flags & kAccStatic) != 0;
fprintf(gOutFile, " positions : \n");
- pDexFile->DecodeDebugPositionInfo(pCode, dumpPositionsCb, nullptr);
+ uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode);
+ pDexFile->DecodeDebugPositionInfo(pCode, debug_info_offset, dumpPositionsCb, nullptr);
fprintf(gOutFile, " locals : \n");
- pDexFile->DecodeDebugLocalInfo(pCode, is_static, idx, dumpLocalsCb, nullptr);
+ pDexFile->DecodeDebugLocalInfo(
+ pCode, debug_info_offset, is_static, idx, dumpLocalsCb, nullptr);
}
/*
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index a8ba950..2af579c 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -570,16 +570,17 @@
uint32_t tries_size = disk_code_item.tries_size_;
// TODO: Calculate the size of the debug info.
- const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(&disk_code_item);
+ uint32_t debug_info_offset = dex_file.GetDebugInfoOffset(&disk_code_item);
+ const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(debug_info_offset);
DebugInfoItem* debug_info = nullptr;
if (debug_info_stream != nullptr) {
- debug_info = debug_info_items_map_.GetExistingObject(disk_code_item.debug_info_off_);
+ debug_info = debug_info_items_map_.GetExistingObject(debug_info_offset);
if (debug_info == nullptr) {
uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream);
uint8_t* debug_info_buffer = new uint8_t[debug_info_size];
memcpy(debug_info_buffer, debug_info_stream, debug_info_size);
debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer);
- AddItem(debug_info_items_map_, debug_info_items_, debug_info, disk_code_item.debug_info_off_);
+ AddItem(debug_info_items_map_, debug_info_items_, debug_info, debug_info_offset);
}
}
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 61a4eae..8421774 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -396,6 +396,14 @@
eagerly_assign_offsets_ = eagerly_assign_offsets;
}
+ void SetLinkData(std::vector<uint8_t>&& link_data) {
+ link_data_ = std::move(link_data);
+ }
+
+ const std::vector<uint8_t>& LinkData() const {
+ return link_data_;
+ }
+
private:
EncodedValue* ReadEncodedValue(const DexFile& dex_file, const uint8_t** data);
EncodedValue* ReadEncodedValue(const DexFile& dex_file,
@@ -452,6 +460,9 @@
uint32_t map_list_offset_ = 0;
+ // Link data.
+ std::vector<uint8_t> link_data_;
+
// If we eagerly assign offsets during IR building or later after layout. Must be false if
// changing the layout is enabled.
bool eagerly_assign_offsets_;
diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc
index 924dfe0..1fd963f 100644
--- a/dexlayout/dex_ir_builder.cc
+++ b/dexlayout/dex_ir_builder.cc
@@ -80,6 +80,11 @@
// Sort the vectors by the map order (same order as the file).
collections.SortVectorsByMapOrder();
+ // Load the link data if it exists.
+ collections.SetLinkData(std::vector<uint8_t>(
+ dex_file.Begin() + dex_file.GetHeader().link_off_,
+ dex_file.Begin() + dex_file.GetHeader().link_off_ + dex_file.GetHeader().link_size_));
+
return header;
}
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index c85bca0..1fac235 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -878,7 +878,15 @@
}
}
- // TODO: Write link data?
+ // Write link data if it exists.
+ const std::vector<uint8_t>& link_data = collection.LinkData();
+ if (link_data.size() > 0) {
+ CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size()));
+ if (compute_offsets_) {
+ header_->SetLinkOffset(offset);
+ }
+ offset += Write(&link_data[0], link_data.size(), header_->LinkOffset());
+ }
// Write header last.
if (compute_offsets_) {
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index c4f7acc..f994fd6 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -732,4 +732,46 @@
dexlayout_args));
}
+// Test that link data is written out (or at least the header is updated).
+TEST_F(DexLayoutTest, LinkData) {
+ TEST_DISABLED_FOR_TARGET();
+ ScratchFile temp_dex;
+ size_t file_size = 0;
+ MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("ManyMethods"), [&] (DexFile* dex) {
+ DexFile::Header& header = const_cast<DexFile::Header&>(dex->GetHeader());
+ header.link_off_ = header.file_size_;
+ header.link_size_ = 16 * KB;
+ header.file_size_ += header.link_size_;
+ file_size = header.file_size_;
+ });
+ TEMP_FAILURE_RETRY(temp_dex.GetFile()->SetLength(file_size));
+
+ std::string error_msg;
+
+ ScratchFile tmp_file;
+ const std::string& tmp_name = tmp_file.GetFilename();
+ size_t tmp_last_slash = tmp_name.rfind('/');
+ std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
+ ScratchFile profile_file;
+
+ std::vector<std::string> dexlayout_args =
+ { "-i",
+ "-v",
+ "-w", tmp_dir,
+ "-o", tmp_name,
+ "-p", profile_file.GetFilename(),
+ temp_dex.GetFilename()
+ };
+ // -v makes sure that the layout did not corrupt the dex file.
+ ASSERT_TRUE(DexLayoutExec(&temp_dex,
+ /*dex_filename*/ nullptr,
+ &profile_file,
+ dexlayout_args));
+
+ std::string output_dex = temp_dex.GetFilename() + ".new";
+ std::vector<std::string> rm_exec_argv =
+ { "/bin/rm", output_dex };
+ ASSERT_TRUE(::art::Exec(rm_exec_argv, &error_msg));
+}
+
} // namespace art
diff --git a/dexlist/Android.bp b/dexlist/Android.bp
index 03943bf..8ecff42 100644
--- a/dexlist/Android.bp
+++ b/dexlist/Android.bp
@@ -17,7 +17,7 @@
host_supported: true,
srcs: ["dexlist.cc"],
cflags: ["-Wall", "-Werror"],
- shared_libs: ["libart"],
+ shared_libs: ["libart", "libbase"],
}
art_cc_test {
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
index e3ca59c..3bd903d 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -120,7 +120,8 @@
// Find the first line.
int firstLine = -1;
- pDexFile->DecodeDebugPositionInfo(pCode, positionsCb, &firstLine);
+ uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode);
+ pDexFile->DecodeDebugPositionInfo(pCode, debug_info_offset, positionsCb, &firstLine);
// Method signature.
const Signature signature = pDexFile->GetMethodSignature(pMethodId);
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index cf93bf0..448ce41 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -48,6 +48,7 @@
#include "mirror/object_array-inl.h"
#include "modifiers.h"
#include "nativehelper/scoped_local_ref.h"
+#include "oat_file.h"
#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
@@ -259,7 +260,9 @@
};
LocalVariableContext context(env);
+ uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item);
if (!dex_file->DecodeDebugLocalInfo(code_item,
+ debug_info_offset,
art_method->IsStatic(),
art_method->GetDexMethodIndex(),
LocalVariableContext::Callback,
@@ -480,7 +483,9 @@
}
LineNumberContext context;
- bool success = dex_file->DecodeDebugPositionInfo(code_item, CollectLineNumbers, &context);
+ uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item);
+ bool success = dex_file->DecodeDebugPositionInfo(
+ code_item, debug_info_offset, CollectLineNumbers, &context);
if (!success) {
return ERR(ABSENT_INFORMATION);
}
@@ -648,7 +653,9 @@
};
GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type);
+ uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item);
if (!dex_file->DecodeDebugLocalInfo(code_item,
+ debug_info_offset,
method->IsStatic(),
method->GetDexMethodIndex(),
GetLocalVariableInfoContext::Callback,
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 613e4fe..1dcd935 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -58,6 +58,7 @@
#include "mirror/throwable.h"
#include "nativehelper/scoped_local_ref.h"
#include "nativehelper/scoped_primitive_array.h"
+#include "oat_file.h"
#include "obj_ptr-inl.h"
#include "reflection.h"
#include "safe_map.h"
@@ -345,7 +346,14 @@
Dbg::DbgClassLoadCallback Dbg::class_load_callback_;
void DebuggerDdmCallback::DdmPublishChunk(uint32_t type, const ArrayRef<const uint8_t>& data) {
- Dbg::DdmSendChunk(type, data);
+ if (gJdwpState == nullptr) {
+ VLOG(jdwp) << "Debugger thread not active, ignoring DDM send: " << type;
+ } else {
+ iovec vec[1];
+ vec[0].iov_base = reinterpret_cast<void*>(const_cast<uint8_t*>(data.data()));
+ vec[0].iov_len = data.size();
+ gJdwpState->DdmSendChunkV(type, vec, 1);
+ }
}
bool DebuggerActiveMethodInspectionCallback::IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) {
@@ -1673,7 +1681,9 @@
context.pReply = pReply;
if (code_item != nullptr) {
- m->GetDexFile()->DecodeDebugPositionInfo(code_item, DebugCallbackContext::Callback, &context);
+ uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item);
+ m->GetDexFile()->DecodeDebugPositionInfo(
+ code_item, debug_info_offset, DebugCallbackContext::Callback, &context);
}
JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
@@ -1730,9 +1740,10 @@
const DexFile::CodeItem* code_item = m->GetCodeItem();
if (code_item != nullptr) {
+ uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item);
m->GetDexFile()->DecodeDebugLocalInfo(
- code_item, m->IsStatic(), m->GetDexMethodIndex(), DebugCallbackContext::Callback,
- &context);
+ code_item, debug_info_offset, m->IsStatic(), m->GetDexMethodIndex(),
+ DebugCallbackContext::Callback, &context);
}
JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count);
@@ -3879,7 +3890,9 @@
if (m != nullptr && !m->IsNative()) {
const DexFile::CodeItem* const code_item = m->GetCodeItem();
DebugCallbackContext context(single_step_control, line_number, code_item);
- m->GetDexFile()->DecodeDebugPositionInfo(code_item, DebugCallbackContext::Callback, &context);
+ uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item);
+ m->GetDexFile()->DecodeDebugPositionInfo(
+ code_item, debug_info_offset, DebugCallbackContext::Callback, &context);
}
// Activate single-step in the thread.
@@ -4458,10 +4471,11 @@
return;
}
+ RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks();
if (type == CHUNK_TYPE("THDE")) {
uint8_t buf[4];
JDWP::Set4BE(&buf[0], t->GetThreadId());
- Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf);
+ cb->DdmPublishChunk(CHUNK_TYPE("THDE"), ArrayRef<const uint8_t>(buf));
} else {
CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type;
ScopedObjectAccessUnchecked soa(Thread::Current());
@@ -4480,7 +4494,7 @@
JDWP::AppendUtf16BE(bytes, chars, char_count);
}
CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2);
- Dbg::DdmSendChunk(type, bytes);
+ cb->DdmPublishChunk(type, ArrayRef<const uint8_t>(bytes));
}
}
@@ -4523,30 +4537,6 @@
Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE"));
}
-void Dbg::DdmSendChunk(uint32_t type, const ArrayRef<const uint8_t>& data) {
- DdmSendChunk(type, data.size(), data.data());
-}
-
-void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) {
- CHECK(buf != nullptr);
- iovec vec[1];
- vec[0].iov_base = reinterpret_cast<void*>(const_cast<uint8_t*>(buf));
- vec[0].iov_len = byte_count;
- Dbg::DdmSendChunkV(type, vec, 1);
-}
-
-void Dbg::DdmSendChunk(uint32_t type, const std::vector<uint8_t>& bytes) {
- DdmSendChunk(type, bytes.size(), &bytes[0]);
-}
-
-void Dbg::DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count) {
- if (gJdwpState == nullptr) {
- VLOG(jdwp) << "Debugger thread not active, ignoring DDM send: " << type;
- } else {
- gJdwpState->DdmSendChunkV(type, iov, iov_count);
- }
-}
-
JDWP::JdwpState* Dbg::GetJdwpState() {
return gJdwpState;
}
@@ -4624,7 +4614,8 @@
JDWP::Append4BE(bytes, heap->GetBytesAllocated());
JDWP::Append4BE(bytes, heap->GetObjectsAllocated());
CHECK_EQ(bytes.size(), 4U + (heap_count * (4 + 8 + 1 + 4 + 4 + 4 + 4)));
- Dbg::DdmSendChunk(CHUNK_TYPE("HPIF"), bytes);
+ Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(CHUNK_TYPE("HPIF"),
+ ArrayRef<const uint8_t>(bytes));
}
enum HpsgSolidity {
@@ -4710,7 +4701,8 @@
CHECK_LE(pieceLenField_, p_);
JDWP::Set4BE(pieceLenField_, totalAllocationUnits_);
- Dbg::DdmSendChunk(type_, p_ - &buf_[0], &buf_[0]);
+ ArrayRef<const uint8_t> out(&buf_[0], p_ - &buf_[0]);
+ Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(type_, out);
Reset();
}
@@ -4892,6 +4884,7 @@
if (when == HPSG_WHEN_NEVER) {
return;
}
+ RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks();
// Figure out what kind of chunks we'll be sending.
CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS)
<< static_cast<int>(what);
@@ -4899,7 +4892,8 @@
// First, send a heap start chunk.
uint8_t heap_id[4];
JDWP::Set4BE(&heap_id[0], 1); // Heap id (bogus; we only have one heap).
- Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id);
+ cb->DdmPublishChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"),
+ ArrayRef<const uint8_t>(heap_id));
Thread* self = Thread::Current();
Locks::mutator_lock_->AssertSharedHeld(self);
@@ -4958,7 +4952,8 @@
}
// Finally, send a heap end chunk.
- Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id);
+ cb->DdmPublishChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"),
+ ArrayRef<const uint8_t>(heap_id));
}
void Dbg::SetAllocTrackingEnabled(bool enable) {
diff --git a/runtime/debugger.h b/runtime/debugger.h
index c3184e8..d5bad8d 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -662,14 +662,6 @@
static bool DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen);
static void DdmConnected() REQUIRES_SHARED(Locks::mutator_lock_);
static void DdmDisconnected() REQUIRES_SHARED(Locks::mutator_lock_);
- static void DdmSendChunk(uint32_t type, const ArrayRef<const uint8_t>& bytes)
- REQUIRES_SHARED(Locks::mutator_lock_);
- static void DdmSendChunk(uint32_t type, const std::vector<uint8_t>& bytes)
- REQUIRES_SHARED(Locks::mutator_lock_);
- static void DdmSendChunk(uint32_t type, size_t len, const uint8_t* buf)
- REQUIRES_SHARED(Locks::mutator_lock_);
- static void DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count)
- REQUIRES_SHARED(Locks::mutator_lock_);
// Visit breakpoint roots, used to prevent unloading of methods with breakpoints.
static void VisitRoots(RootVisitor* visitor)
diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h
index c926a0d..1880968 100644
--- a/runtime/dex_file-inl.h
+++ b/runtime/dex_file-inl.h
@@ -386,6 +386,7 @@
template<typename NewLocalCallback>
bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item,
+ uint32_t debug_info_offset,
bool is_static,
uint32_t method_idx,
NewLocalCallback new_local_callback,
@@ -398,7 +399,7 @@
for (; it.HasNext(); it.Next()) {
arg_descriptors.push_back(it.GetDescriptor());
}
- return DecodeDebugLocalInfo(GetDebugInfoStream(code_item),
+ return DecodeDebugLocalInfo(GetDebugInfoStream(debug_info_offset),
GetLocation(),
GetMethodDeclaringClassDescriptor(GetMethodId(method_idx)),
arg_descriptors,
@@ -488,12 +489,13 @@
template<typename DexDebugNewPosition>
bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item,
+ uint32_t debug_info_offset,
DexDebugNewPosition position_functor,
void* context) const {
if (code_item == nullptr) {
return false;
}
- return DecodeDebugPositionInfo(GetDebugInfoStream(code_item),
+ return DecodeDebugPositionInfo(GetDebugInfoStream(debug_info_offset),
[this](uint32_t idx) {
return StringDataByIdx(dex::StringIndex(idx));
},
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index cdefb23..2166ed1 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -698,6 +698,15 @@
return reinterpret_cast<const CodeItem*>(addr);
}
+ uint32_t GetDebugInfoOffset(const CodeItem* code_item) const {
+ if (code_item == nullptr) {
+ return 0;
+ }
+ CHECK(oat_dex_file_ == nullptr)
+ << "Should only use GetDebugInfoOffset in a non runtime setup";
+ return code_item->debug_info_off_;
+ }
+
const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const;
// Returns the number of prototype identifiers in the .dex file.
@@ -775,11 +784,10 @@
static int32_t FindCatchHandlerOffset(const CodeItem &code_item, uint32_t address);
// Get the pointer to the start of the debugging data
- const uint8_t* GetDebugInfoStream(const CodeItem* code_item) const {
+ const uint8_t* GetDebugInfoStream(uint32_t debug_info_off) const {
// Check that the offset is in bounds.
// Note that although the specification says that 0 should be used if there
// is no debug information, some applications incorrectly use 0xFFFFFFFF.
- const uint32_t debug_info_off = code_item->debug_info_off_;
return (debug_info_off == 0 || debug_info_off >= size_) ? nullptr : begin_ + debug_info_off;
}
@@ -936,6 +944,7 @@
void* context);
template<typename NewLocalCallback>
bool DecodeDebugLocalInfo(const CodeItem* code_item,
+ uint32_t debug_info_offset,
bool is_static,
uint32_t method_idx,
NewLocalCallback new_local,
@@ -949,6 +958,7 @@
void* context);
template<typename DexDebugNewPosition>
bool DecodeDebugPositionInfo(const CodeItem* code_item,
+ uint32_t debug_info_offset,
DexDebugNewPosition position_functor,
void* context) const;
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 27060ae..b44bd51 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -28,6 +28,7 @@
#include "jvalue-inl.h"
#include "mirror/field.h"
#include "mirror/method.h"
+#include "oat_file.h"
#include "reflection.h"
#include "thread.h"
#include "well_known_classes.h"
@@ -1571,7 +1572,9 @@
// A method with no line number info should return -1
DexFile::LineNumFromPcContext context(rel_pc, -1);
- dex_file->DecodeDebugPositionInfo(code_item, DexFile::LineNumForPcCb, &context);
+ uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file, code_item);
+ dex_file->DecodeDebugPositionInfo(
+ code_item, debug_info_offset, DexFile::LineNumForPcCb, &context);
return context.line_num_;
}
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index c963f6e..14c36b4 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -730,7 +730,8 @@
kRawDexDebugInfoLocalNullType, tmp.GetFilename().c_str(), 0xf25f2b38U, true);
const DexFile::ClassDef& class_def = raw->GetClassDef(0);
const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, 1));
- ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item, true, 1, Callback, nullptr));
+ uint32_t debug_info_offset = raw->GetDebugInfoOffset(code_item);
+ ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item, debug_info_offset, true, 1, Callback, nullptr));
}
} // namespace art
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index fbac348..5f54d5d 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1499,6 +1499,14 @@
}
}
+uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file ATTRIBUTE_UNUSED,
+ const DexFile::CodeItem* code_item) {
+ if (code_item == nullptr) {
+ return 0;
+ }
+ return code_item->debug_info_off_;
+}
+
const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location,
const uint32_t* dex_location_checksum,
std::string* error_msg) const {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index d06cf1b..36a4d7b 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -109,11 +109,15 @@
static OatFile* OpenWritable(File* file, const std::string& location,
const char* abs_dex_location,
std::string* error_msg);
- // Opens an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE.
+ // Open an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE.
static OatFile* OpenReadable(File* file, const std::string& location,
const char* abs_dex_location,
std::string* error_msg);
+ // Return the debug info offset of the code item `item` located in `dex_file`.
+ static uint32_t GetDebugInfoOffset(const DexFile& dex_file,
+ const DexFile::CodeItem* item);
+
virtual ~OatFile();
bool IsExecutable() const {
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 4b5a761..a113ab5 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -413,42 +413,40 @@
sampling_pthread_ = 0U;
}
- {
+ if (the_trace != nullptr) {
+ stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0;
+ if (finish_tracing) {
+ the_trace->FinishTracing();
+ }
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseInstrumentation,
gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa(__FUNCTION__);
- if (the_trace != nullptr) {
- stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0;
- if (finish_tracing) {
- the_trace->FinishTracing();
- }
- if (the_trace->trace_mode_ == TraceMode::kSampling) {
- MutexLock mu(self, *Locks::thread_list_lock_);
- runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
- } else {
- runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
- runtime->GetInstrumentation()->RemoveListener(
- the_trace, instrumentation::Instrumentation::kMethodEntered |
- instrumentation::Instrumentation::kMethodExited |
- instrumentation::Instrumentation::kMethodUnwind);
- }
- if (the_trace->trace_file_.get() != nullptr) {
- // Do not try to erase, so flush and close explicitly.
- if (flush_file) {
- if (the_trace->trace_file_->Flush() != 0) {
- PLOG(WARNING) << "Could not flush trace file.";
- }
- } else {
- the_trace->trace_file_->MarkUnchecked(); // Do not trigger guard.
- }
- if (the_trace->trace_file_->Close() != 0) {
- PLOG(ERROR) << "Could not close trace file.";
- }
- }
- delete the_trace;
+ if (the_trace->trace_mode_ == TraceMode::kSampling) {
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
+ } else {
+ runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
+ runtime->GetInstrumentation()->RemoveListener(
+ the_trace, instrumentation::Instrumentation::kMethodEntered |
+ instrumentation::Instrumentation::kMethodExited |
+ instrumentation::Instrumentation::kMethodUnwind);
}
+ if (the_trace->trace_file_.get() != nullptr) {
+ // Do not try to erase, so flush and close explicitly.
+ if (flush_file) {
+ if (the_trace->trace_file_->Flush() != 0) {
+ PLOG(WARNING) << "Could not flush trace file.";
+ }
+ } else {
+ the_trace->trace_file_->MarkUnchecked(); // Do not trigger guard.
+ }
+ if (the_trace->trace_file_->Close() != 0) {
+ PLOG(ERROR) << "Could not close trace file.";
+ }
+ }
+ delete the_trace;
}
if (stop_alloc_counting) {
// Can be racy since SetStatsEnabled is not guarded by any locks.
@@ -717,12 +715,12 @@
FlushBuf();
} else {
if (trace_file_.get() == nullptr) {
- iovec iov[2];
- iov[0].iov_base = reinterpret_cast<void*>(const_cast<char*>(header.c_str()));
- iov[0].iov_len = header.length();
- iov[1].iov_base = buf_.get();
- iov[1].iov_len = final_offset;
- Dbg::DdmSendChunkV(CHUNK_TYPE("MPSE"), iov, 2);
+ std::vector<uint8_t> data;
+ data.resize(header.length() + final_offset);
+ memcpy(data.data(), header.c_str(), header.length());
+ memcpy(data.data() + header.length(), buf_.get(), final_offset);
+ Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(CHUNK_TYPE("MPSE"),
+ ArrayRef<const uint8_t>(data));
const bool kDumpTraceInfo = false;
if (kDumpTraceInfo) {
LOG(INFO) << "Trace sent:\n" << header;
diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt
index cf4ad50..62d3b7b 100644
--- a/test/1940-ddms-ext/expected.txt
+++ b/test/1940-ddms-ext/expected.txt
@@ -5,3 +5,6 @@
JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37])
Sending chunk: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16])
Chunk published: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16])
+Saw expected thread events.
+Expected chunk type published: 1213221190
+Expected chunk type published: 1297109829
diff --git a/test/1940-ddms-ext/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java
index f0ee710..9f79eae 100644
--- a/test/1940-ddms-ext/src-art/art/Test1940.java
+++ b/test/1940-ddms-ext/src-art/art/Test1940.java
@@ -17,6 +17,7 @@
package art;
import org.apache.harmony.dalvik.ddmc.*;
+import dalvik.system.VMDebug;
import java.lang.reflect.Method;
import java.util.Arrays;
@@ -30,6 +31,12 @@
public static final int MY_DDMS_TYPE = 0xDEADBEEF;
public static final int MY_DDMS_RESPONSE_TYPE = 0xFADE7357;
+ public static final boolean PRINT_ALL_CHUNKS = false;
+
+ public static interface DdmHandler {
+ public void HandleChunk(int type, byte[] data);
+ }
+
public static final class TestError extends Error {
public TestError(String s) { super(s); }
}
@@ -69,11 +76,38 @@
public static final ChunkHandler SINGLE_HANDLER = new MyDdmHandler();
+ public static DdmHandler CURRENT_HANDLER;
+
public static void HandlePublish(int type, byte[] data) {
- System.out.println("Chunk published: " + printChunk(new Chunk(type, data, 0, data.length)));
+ if (PRINT_ALL_CHUNKS) {
+ System.out.println(
+ "Unknown Chunk published: " + printChunk(new Chunk(type, data, 0, data.length)));
+ }
+ CURRENT_HANDLER.HandleChunk(type, data);
+ }
+
+ // TYPE Thread Create
+ public static final int TYPE_THCR = 0x54484352;
+ // Type Thread name
+ public static final int TYPE_THNM = 0x54484E4D;
+ // Type Thread death.
+ public static final int TYPE_THDE = 0x54484445;
+ // Type Heap info
+ public static final int TYPE_HPIF = 0x48504946;
+ // Type Trace Results
+ public static final int TYPE_MPSE = 0x4D505345;
+
+ public static boolean IsFromThread(Thread t, byte[] data) {
+ // DDMS always puts the thread-id as the first 4 bytes.
+ ByteBuffer b = ByteBuffer.wrap(data);
+ b.order(ByteOrder.BIG_ENDIAN);
+ return b.getInt() == (int) t.getId();
}
public static void run() throws Exception {
+ CURRENT_HANDLER = (type, data) -> {
+ System.out.println("Chunk published: " + printChunk(new Chunk(type, data, 0, data.length)));
+ };
initializeTest(
Test1940.class,
Test1940.class.getDeclaredMethod("HandlePublish", Integer.TYPE, new byte[0].getClass()));
@@ -90,8 +124,70 @@
MY_DDMS_TYPE, new byte[] { 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, 0, 8);
System.out.println("Sending chunk: " + printChunk(c));
DdmServer.sendChunk(c);
+
+ // Test thread chunks are sent.
+ final boolean[] types_seen = new boolean[] { false, false, false };
+ CURRENT_HANDLER = (type, cdata) -> {
+ switch (type) {
+ case TYPE_THCR:
+ types_seen[0] = true;
+ break;
+ case TYPE_THNM:
+ types_seen[1] = true;
+ break;
+ case TYPE_THDE:
+ types_seen[2] = true;
+ break;
+ default:
+ // We don't want to print other types.
+ break;
+ }
+ };
+ DdmVmInternal.threadNotify(true);
+ final Thread thr = new Thread(() -> { return; }, "THREAD");
+ thr.start();
+ thr.join();
+ DdmVmInternal.threadNotify(false);
+ // Make sure we saw at least one of Thread-create, Thread name, & thread death.
+ if (!types_seen[0] || !types_seen[1] || !types_seen[2]) {
+ System.out.println("Didn't see expected chunks for thread creation! got: " +
+ Arrays.toString(types_seen));
+ } else {
+ System.out.println("Saw expected thread events.");
+ }
+
+ // Test heap chunks are sent.
+ CURRENT_HANDLER = (type, cdata) -> {
+ // The actual data is far to noisy for this test as it includes information about global heap
+ // state.
+ if (type == TYPE_HPIF) {
+ System.out.println("Expected chunk type published: " + type);
+ }
+ };
+ final int HPIF_WHEN_NOW = 1;
+ if (!DdmVmInternal.heapInfoNotify(HPIF_WHEN_NOW)) {
+ System.out.println("Unexpected failure for heapInfoNotify!");
+ }
+
+ // method Tracing
+ CURRENT_HANDLER = (type, cdata) -> {
+ // This chunk includes timing and thread information so we just check the type.
+ if (type == TYPE_MPSE) {
+ System.out.println("Expected chunk type published: " + type);
+ }
+ };
+ VMDebug.startMethodTracingDdms(/*size: default*/0,
+ /*flags: none*/ 0,
+ /*sampling*/ false,
+ /*interval*/ 0);
+ doNothing();
+ doNothing();
+ doNothing();
+ doNothing();
+ VMDebug.stopMethodTracing();
}
+ private static void doNothing() {}
private static Chunk processChunk(byte[] val) {
return processChunk(new Chunk(MY_DDMS_TYPE, val, 0, val.length));
}
diff --git a/test/669-checker-break/expected.txt b/test/669-checker-break/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/669-checker-break/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/669-checker-break/info.txt b/test/669-checker-break/info.txt
new file mode 100644
index 0000000..3408b3b
--- /dev/null
+++ b/test/669-checker-break/info.txt
@@ -0,0 +1 @@
+Test optimizations of "break" loops.
diff --git a/test/669-checker-break/src/Main.java b/test/669-checker-break/src/Main.java
new file mode 100644
index 0000000..e59061b
--- /dev/null
+++ b/test/669-checker-break/src/Main.java
@@ -0,0 +1,328 @@
+/*
+ * 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 for optimizations of break-loops, i.e. loops that break
+ * out of a while-true loop when the end condition is satisfied.
+ * In particular, the tests focus on break-loops that can be
+ * rewritten into regular countable loops (this may improve certain
+ * loops generated by the Kotlin compiler for inclusive ranges).
+ */
+public class Main {
+
+ /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (before)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<NE:z\d+>> NotEqual [{{i\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (after)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: If [<<LE>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: int Main.breakLoop(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Four:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none
+ /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<One>>] loop:none
+ /// CHECK-DAG: VecStore [<<Nil>>,<<Phi:i\d+>>,<<Rep>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi>>,<<Four>>] loop:<<Loop>> outer_loop:none
+ static int breakLoop(int[] a) {
+ int l = 0;
+ int u = a.length - 1;
+ int i = l;
+ if (l <= u) {
+ while (true) {
+ a[i] = 1;
+ if (i == u) break;
+ i++;
+ }
+ }
+ return i;
+ }
+
+ /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (before)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<MOne:i\d+>> IntConstant -1 loop:none
+ /// CHECK-DAG: <<Two:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [{{i\d+}},<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<Two>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<NE:z\d+>> NotEqual [<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<MOne>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (after)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<MOne:i\d+>> IntConstant -1 loop:none
+ /// CHECK-DAG: <<Two:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [{{i\d+}},<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<GE:z\d+>> GreaterThanOrEqual [<<Phi>>,<<Zero>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: If [<<GE>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<Two>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<MOne>>] loop:<<Loop>> outer_loop:none
+ static int breakLoopDown(int[] a) {
+ int l = 0;
+ int u = a.length - 1;
+ int i = u;
+ if (u >= l) {
+ while (true) {
+ a[i] = 2;
+ if (i == l) break;
+ i--;
+ }
+ }
+ return i;
+ }
+
+ /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (before)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Three:i\d+>> IntConstant 3 loop:none
+ /// CHECK-DAG: <<L1:i\d+>> IntConstant 2147483631 loop:none
+ /// CHECK-DAG: <<L2:i\d+>> IntConstant 2147483646 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<L1>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Phi>>,<<L1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Sub>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<Three>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<NE:z\d+>> NotEqual [<<Phi>>,<<L2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (after)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Three:i\d+>> IntConstant 3 loop:none
+ /// CHECK-DAG: <<L1:i\d+>> IntConstant 2147483631 loop:none
+ /// CHECK-DAG: <<L2:i\d+>> IntConstant 2147483646 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<L1>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Phi>>,<<L2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Phi>>,<<L1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Sub>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<Three>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: int Main.breakLoopSafeConst(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Three:i\d+>> IntConstant 3 loop:none
+ /// CHECK-DAG: <<Four:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Rep:d\d+>> VecReplicateScalar [<<Three>>] loop:none
+ /// CHECK-DAG: VecStore [<<Par>>,<<Phi:i\d+>>,<<Rep>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Add [<<Phi>>,<<Four>>] loop:<<Loop>> outer_loop:none
+ static int breakLoopSafeConst(int[] a) {
+ int l = Integer.MAX_VALUE - 16;
+ int u = Integer.MAX_VALUE - 1;
+ int i = l;
+ if (l <= u) { // will be removed by simplifier
+ while (true) {
+ a[i - l] = 3;
+ if (i == u) break;
+ i++;
+ }
+ }
+ return i;
+ }
+
+ /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (before)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Four:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<L1:i\d+>> IntConstant 2147483632 loop:none
+ /// CHECK-DAG: <<L2:i\d+>> IntConstant 2147483647 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<L1>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Phi>>,<<L1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Sub>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<Four>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<NE:z\d+>> NotEqual [<<Phi>>,<<L2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (after)
+ /// CHECK-DAG: NotEqual
+ /// CHECK-NOT: LessThanOrEqual
+ static int breakLoopUnsafeConst(int[] a) {
+ int l = Integer.MAX_VALUE - 15;
+ int u = Integer.MAX_VALUE;
+ int i = l;
+ if (l <= u) { // will be removed by simplifier
+ while (true) {
+ a[i - l] = 4;
+ if (i == u) break; // rewriting exit not safe!
+ i++;
+ }
+ }
+ return i;
+ }
+
+ /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (before)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Five:i\d+>> IntConstant 5 loop:none
+ /// CHECK-DAG: <<M123:i\d+>> IntConstant -123 loop:none
+ /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Wrap:i\d+>> Phi [<<M123>>,<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Nil>>,<<Bnd>>,<<Five>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<NE:z\d+>> NotEqual [{{i\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Comb:i\d+>> Phi [<<M123>>,<<Wrap>>] loop:none
+ /// CHECK-DAG: Return [<<Comb>>] loop:none
+ //
+ /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (after)
+ /// CHECK-DAG: NotEqual
+ /// CHECK-NOT: LessThanOrEqual
+ static int breakLoopNastyPhi(int[] a) {
+ int l = 0;
+ int u = a.length - 1;
+ int x = -123;
+ if (l <= u) {
+ int i = l;
+ while (true) {
+ a[i] = 5;
+ if (i == u) break;
+ x = i;
+ i++;
+ }
+ }
+ return x; // keep another phi live
+ }
+
+ /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (before)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Red:i\d+>> Phi [<<Zero>>,<<RedI:i\d+>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Nil>>,<<Bnd>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<RedI>> Add [<<Red>>,<<Get>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<NE:z\d+>> NotEqual [{{i\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Comb:i\d+>> Phi [<<Zero>>,<<RedI>>] loop:none
+ /// CHECK-DAG: Return [<<Comb>>] loop:none
+ //
+ /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (after)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Nil:l\d+>> NullCheck [<<Par>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Zero>>,<<AddI:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Red:i\d+>> Phi [<<Zero>>,<<RedI:i\d+>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: If [<<LE>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Bnd:i\d+>> BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet [<<Nil>>,<<Bnd>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<RedI>> Add [<<Red>>,<<Get>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<AddI>> Add [<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Comb:i\d+>> Phi [<<Zero>>,<<Red>>] loop:none
+ /// CHECK-DAG: Return [<<Comb>>] loop:none
+ //
+ /// CHECK-START-ARM64: int Main.breakLoopReduction(int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Par:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Exp:d\d+>> VecSetScalars [<<Zero>>] loop:none
+ /// CHECK-DAG: <<VPhi:d\d+>> Phi [<<Exp>>,<<VAdd:d\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<VLoad:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<VAdd>> VecAdd [<<VPhi>>,<<VLoad>>] loop:<<Loop>> outer_loop:none
+ static int breakLoopReduction(int[] a) {
+ int l = 0;
+ int u = a.length - 1;
+ int x = 0;
+ if (l <= u) {
+ int i = l;
+ while (true) {
+ x += a[i];
+ if (i == u) break;
+ i++;
+ }
+ }
+ return x;
+ }
+
+ //
+ // Test driver.
+ //
+
+ public static void main(String[] args) {
+ int[] a = new int[100];
+
+ expectEquals(99, breakLoop(a));
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(1, a[i]);
+ }
+
+ expectEquals(0, breakLoopDown(a));
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(2, a[i]);
+ }
+
+ expectEquals(Integer.MAX_VALUE - 1, breakLoopSafeConst(a));
+ for (int i = 0; i < a.length; i++) {
+ int e = i < 16 ? 3 : 2;
+ expectEquals(e, a[i]);
+ }
+
+ expectEquals(Integer.MAX_VALUE, breakLoopUnsafeConst(a));
+ for (int i = 0; i < a.length; i++) {
+ int e = i < 16 ? 4 : 2;
+ expectEquals(e, a[i]);
+ }
+
+ expectEquals(98, breakLoopNastyPhi(a));
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(5, a[i]);
+ }
+
+ expectEquals(500, breakLoopReduction(a));
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index c6b6574..5b2ebf5 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -202,6 +202,12 @@
"bug": "http://b/34369284"
},
{
+ "tests": "1940-ddms-ext",
+ "description": ["Test expects to be able to start tracing but we cannot",
+ "do that if tracing is already ongoing."],
+ "variant": "trace | stream"
+ },
+ {
"tests": "137-cfi",
"description": ["This test unrolls and expects managed frames, but",
"tracing means we run the interpreter."],
diff --git a/tools/libjdwp_art_failures.txt b/tools/libjdwp_art_failures.txt
index fd711bb..abcc728 100644
--- a/tools/libjdwp_art_failures.txt
+++ b/tools/libjdwp_art_failures.txt
@@ -103,5 +103,11 @@
result: EXEC_FAILED,
bug: 69169846,
name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001"
+},
+{
+ description: "Test crashes",
+ result: EXEC_FAILED,
+ bug: 69591477,
+ name: "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest#testExit001"
}
]
diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/libjdwp_oj_art_failures.txt
index 3d06bcf..e1cc831 100644
--- a/tools/libjdwp_oj_art_failures.txt
+++ b/tools/libjdwp_oj_art_failures.txt
@@ -73,5 +73,11 @@
result: EXEC_FAILED,
bug: 69169846,
name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001"
+},
+{
+ description: "Test crashes",
+ result: EXEC_FAILED,
+ bug: 69591477,
+ name: "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest#testExit001"
}
]