ART: Refactor try/catch block info, store exception type
This patch replaces HBasicBlock fields storing try/catch info with a
single TryCatchInformation data structure, saving memory for the
majority of non-try/catch blocks. It also changes builder to store
the exception type for catch blocks.
Change-Id: Ib3e43f7db247e6915d67c267fc62410420e230c9
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 8841498..c4ead5b 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -306,7 +306,8 @@
for (; iterator.HasNext(); iterator.Next()) {
uint32_t address = iterator.GetHandlerAddress();
HBasicBlock* block = FindOrCreateBlockStartingAt(address);
- block->SetIsCatchBlock();
+ block->SetTryCatchInformation(
+ new (arena_) TryCatchInformation(iterator.GetHandlerTypeIndex(), *dex_file_));
}
handlers_ptr = iterator.EndDataPointer();
}
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 5406a0c..847d5a4 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -402,14 +402,14 @@
}
// Ensure try membership information is consistent.
- HTryBoundary* try_entry = block->GetTryEntry();
if (block->IsCatchBlock()) {
- if (try_entry != nullptr) {
+ if (block->IsTryBlock()) {
+ const HTryBoundary& try_entry = block->GetTryCatchInformation()->GetTryEntry();
AddError(StringPrintf("Catch blocks should not be try blocks but catch block %d "
"has try entry %s:%d.",
block->GetBlockId(),
- try_entry->DebugName(),
- try_entry->GetId()));
+ try_entry.DebugName(),
+ try_entry.GetId()));
}
if (block->IsLoopHeader()) {
@@ -419,29 +419,30 @@
} else {
for (size_t i = 0; i < block->GetPredecessors().Size(); ++i) {
HBasicBlock* predecessor = block->GetPredecessors().Get(i);
- HTryBoundary* incoming_try_entry = predecessor->ComputeTryEntryOfSuccessors();
- if (try_entry == nullptr) {
- if (incoming_try_entry != nullptr) {
- AddError(StringPrintf("Block %d has no try entry but try entry %s:%d follows "
+ const HTryBoundary* incoming_try_entry = predecessor->ComputeTryEntryOfSuccessors();
+ if (block->IsTryBlock()) {
+ const HTryBoundary& stored_try_entry = block->GetTryCatchInformation()->GetTryEntry();
+ if (incoming_try_entry == nullptr) {
+ AddError(StringPrintf("Block %d has try entry %s:%d but no try entry follows "
"from predecessor %d.",
block->GetBlockId(),
+ stored_try_entry.DebugName(),
+ stored_try_entry.GetId(),
+ predecessor->GetBlockId()));
+ } else if (!incoming_try_entry->HasSameExceptionHandlersAs(stored_try_entry)) {
+ AddError(StringPrintf("Block %d has try entry %s:%d which is not consistent "
+ "with %s:%d that follows from predecessor %d.",
+ block->GetBlockId(),
+ stored_try_entry.DebugName(),
+ stored_try_entry.GetId(),
incoming_try_entry->DebugName(),
incoming_try_entry->GetId(),
predecessor->GetBlockId()));
}
- } else if (incoming_try_entry == nullptr) {
- AddError(StringPrintf("Block %d has try entry %s:%d but no try entry follows "
+ } else if (incoming_try_entry != nullptr) {
+ AddError(StringPrintf("Block %d is not a try block but try entry %s:%d follows "
"from predecessor %d.",
block->GetBlockId(),
- try_entry->DebugName(),
- try_entry->GetId(),
- predecessor->GetBlockId()));
- } else if (!incoming_try_entry->HasSameExceptionHandlersAs(*try_entry)) {
- AddError(StringPrintf("Block %d has try entry %s:%d which is not consistent "
- "with %s:%d that follows from predecessor %d.",
- block->GetBlockId(),
- try_entry->DebugName(),
- try_entry->GetId(),
incoming_try_entry->DebugName(),
incoming_try_entry->GetId(),
predecessor->GetBlockId()));
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index f2b63ae..64c680c 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -339,7 +339,10 @@
// been visited already and had its try membership set.
HBasicBlock* first_predecessor = block->GetPredecessors().Get(0);
DCHECK(!block->IsLoopHeader() || !block->GetLoopInformation()->IsBackEdge(*first_predecessor));
- block->SetTryEntry(first_predecessor->ComputeTryEntryOfSuccessors());
+ const HTryBoundary* try_entry = first_predecessor->ComputeTryEntryOfSuccessors();
+ if (try_entry != nullptr) {
+ block->SetTryCatchInformation(new (arena_) TryCatchInformation(*try_entry));
+ }
}
}
@@ -1164,19 +1167,21 @@
return new_block;
}
-HTryBoundary* HBasicBlock::ComputeTryEntryOfSuccessors() const {
+const HTryBoundary* HBasicBlock::ComputeTryEntryOfSuccessors() const {
if (EndsWithTryBoundary()) {
HTryBoundary* try_boundary = GetLastInstruction()->AsTryBoundary();
if (try_boundary->IsEntry()) {
- DCHECK(try_entry_ == nullptr);
+ DCHECK(!IsTryBlock());
return try_boundary;
} else {
- DCHECK(try_entry_ != nullptr);
- DCHECK(try_entry_->HasSameExceptionHandlersAs(*try_boundary));
+ DCHECK(IsTryBlock());
+ DCHECK(try_catch_information_->GetTryEntry().HasSameExceptionHandlersAs(*try_boundary));
return nullptr;
}
+ } else if (IsTryBlock()) {
+ return &try_catch_information_->GetTryEntry();
} else {
- return try_entry_;
+ return nullptr;
}
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index f09e958..c4f64b4 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -555,6 +555,59 @@
DISALLOW_COPY_AND_ASSIGN(HLoopInformation);
};
+// Stores try/catch information for basic blocks.
+// Note that HGraph is constructed so that catch blocks cannot simultaneously
+// be try blocks.
+class TryCatchInformation : public ArenaObject<kArenaAllocMisc> {
+ public:
+ // Try block information constructor.
+ explicit TryCatchInformation(const HTryBoundary& try_entry)
+ : try_entry_(&try_entry),
+ catch_dex_file_(nullptr),
+ catch_type_index_(DexFile::kDexNoIndex16) {
+ DCHECK(try_entry_ != nullptr);
+ }
+
+ // Catch block information constructor.
+ TryCatchInformation(uint16_t catch_type_index, const DexFile& dex_file)
+ : try_entry_(nullptr),
+ catch_dex_file_(&dex_file),
+ catch_type_index_(catch_type_index) {}
+
+ bool IsTryBlock() const { return try_entry_ != nullptr; }
+
+ const HTryBoundary& GetTryEntry() const {
+ DCHECK(IsTryBlock());
+ return *try_entry_;
+ }
+
+ bool IsCatchBlock() const { return catch_dex_file_ != nullptr; }
+
+ bool IsCatchAllTypeIndex() const {
+ DCHECK(IsCatchBlock());
+ return catch_type_index_ == DexFile::kDexNoIndex16;
+ }
+
+ uint16_t GetCatchTypeIndex() const {
+ DCHECK(IsCatchBlock());
+ return catch_type_index_;
+ }
+
+ const DexFile& GetCatchDexFile() const {
+ DCHECK(IsCatchBlock());
+ return *catch_dex_file_;
+ }
+
+ private:
+ // One of possibly several TryBoundary instructions entering the block's try.
+ // Only set for try blocks.
+ const HTryBoundary* try_entry_;
+
+ // Exception type information. Only set for catch blocks.
+ const DexFile* catch_dex_file_;
+ const uint16_t catch_type_index_;
+};
+
static constexpr size_t kNoLifetime = -1;
static constexpr uint32_t kNoDexPc = -1;
@@ -575,7 +628,7 @@
dex_pc_(dex_pc),
lifetime_start_(kNoLifetime),
lifetime_end_(kNoLifetime),
- is_catch_block_(false) {}
+ try_catch_information_(nullptr) {}
const GrowableArray<HBasicBlock*>& GetPredecessors() const {
return predecessors_;
@@ -853,14 +906,24 @@
bool IsInLoop() const { return loop_information_ != nullptr; }
- HTryBoundary* GetTryEntry() const { return try_entry_; }
- void SetTryEntry(HTryBoundary* try_entry) { try_entry_ = try_entry; }
- bool IsInTry() const { return try_entry_ != nullptr; }
+ TryCatchInformation* GetTryCatchInformation() const { return try_catch_information_; }
+
+ void SetTryCatchInformation(TryCatchInformation* try_catch_information) {
+ try_catch_information_ = try_catch_information;
+ }
+
+ bool IsTryBlock() const {
+ return try_catch_information_ != nullptr && try_catch_information_->IsTryBlock();
+ }
+
+ bool IsCatchBlock() const {
+ return try_catch_information_ != nullptr && try_catch_information_->IsCatchBlock();
+ }
// Returns the try entry that this block's successors should have. They will
// be in the same try, unless the block ends in a try boundary. In that case,
// the appropriate try entry will be returned.
- HTryBoundary* ComputeTryEntryOfSuccessors() const;
+ const HTryBoundary* ComputeTryEntryOfSuccessors() const;
// Returns whether this block dominates the blocked passed as parameter.
bool Dominates(HBasicBlock* block) const;
@@ -873,9 +936,6 @@
uint32_t GetDexPc() const { return dex_pc_; }
- bool IsCatchBlock() const { return is_catch_block_; }
- void SetIsCatchBlock() { is_catch_block_ = true; }
-
bool EndsWithControlFlowInstruction() const;
bool EndsWithIf() const;
bool EndsWithTryBoundary() const;
@@ -895,11 +955,7 @@
const uint32_t dex_pc_;
size_t lifetime_start_;
size_t lifetime_end_;
- bool is_catch_block_;
-
- // If this block is in a try block, `try_entry_` stores one of, possibly
- // several, TryBoundary instructions entering it.
- HTryBoundary* try_entry_;
+ TryCatchInformation* try_catch_information_;
friend class HGraph;
friend class HInstruction;
@@ -1676,7 +1732,9 @@
UNREACHABLE();
}
virtual bool IsControlFlow() const { return false; }
+
virtual bool CanThrow() const { return false; }
+ bool CanThrowIntoCatchBlock() const { return CanThrow() && block_->IsTryBlock(); }
bool HasSideEffects() const { return side_effects_.HasSideEffects(); }
bool DoesAnyWrite() const { return side_effects_.DoesAnyWrite(); }
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index ff2e6ad..561c3b4 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -567,9 +567,10 @@
}
// If in a try block, propagate values of locals into catch blocks.
- if (instruction->GetBlock()->IsInTry() && instruction->CanThrow()) {
- HTryBoundary* try_block = instruction->GetBlock()->GetTryEntry();
- for (HExceptionHandlerIterator it(*try_block); !it.Done(); it.Advance()) {
+ if (instruction->CanThrowIntoCatchBlock()) {
+ const HTryBoundary& try_entry =
+ instruction->GetBlock()->GetTryCatchInformation()->GetTryEntry();
+ for (HExceptionHandlerIterator it(try_entry); !it.Done(); it.Advance()) {
GrowableArray<HInstruction*>* handler_locals = GetLocalsFor(it.Current());
for (size_t i = 0, e = current_locals_->Size(); i < e; ++i) {
HInstruction* local_value = current_locals_->Get(i);