From ec16f79a4d0aeff319bf52139a0c82de3080d73c Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Wed, 19 Aug 2015 15:04:01 +0100 Subject: [PATCH] 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 --- compiler/optimizing/builder.cc | 3 +- compiler/optimizing/graph_checker.cc | 39 +++++++++-------- compiler/optimizing/nodes.cc | 17 +++++--- compiler/optimizing/nodes.h | 84 ++++++++++++++++++++++++++++++------ compiler/optimizing/ssa_builder.cc | 7 +-- 5 files changed, 108 insertions(+), 42 deletions(-) diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 88414980b..c4ead5b03 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -306,7 +306,8 @@ void HGraphBuilder::CreateBlocksForTryCatch(const DexFile::CodeItem& code_item) 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 5406a0ccf..847d5a4e9 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -402,14 +402,14 @@ void SSAChecker::VisitBasicBlock(HBasicBlock* block) { } // 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 @@ void SSAChecker::VisitBasicBlock(HBasicBlock* block) { } 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 f2b63ae67..64c680c3f 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -339,7 +339,10 @@ void HGraph::ComputeTryBlockInformation() { // 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 @@ HBasicBlock* HBasicBlock::SplitAfter(HInstruction* cursor) { 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 f09e958d2..c4f64b4eb 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -555,6 +555,59 @@ class HLoopInformation : public ArenaObject { 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 { + 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 @@ class HBasicBlock : public ArenaObject { dex_pc_(dex_pc), lifetime_start_(kNoLifetime), lifetime_end_(kNoLifetime), - is_catch_block_(false) {} + try_catch_information_(nullptr) {} const GrowableArray& GetPredecessors() const { return predecessors_; @@ -853,14 +906,24 @@ class HBasicBlock : public ArenaObject { 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 @@ class HBasicBlock : public ArenaObject { 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 @@ class HBasicBlock : public ArenaObject { 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 @@ class HInstruction : public ArenaObject { 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 ff2e6ad82..561c3b496 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -567,9 +567,10 @@ void SsaBuilder::VisitInstruction(HInstruction* instruction) { } // 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* handler_locals = GetLocalsFor(it.Current()); for (size_t i = 0, e = current_locals_->Size(); i < e; ++i) { HInstruction* local_value = current_locals_->Get(i); -- 2.11.0