diff options
-rw-r--r-- | compiler/Android.mk | 2 | ||||
-rw-r--r-- | compiler/dex/bb_optimizations.cc | 121 | ||||
-rw-r--r-- | compiler/dex/bb_optimizations.h | 165 | ||||
-rw-r--r-- | compiler/dex/dataflow_iterator.h | 105 | ||||
-rw-r--r-- | compiler/dex/frontend.cc | 34 | ||||
-rw-r--r-- | compiler/dex/mir_dataflow.cc | 24 | ||||
-rw-r--r-- | compiler/dex/mir_graph.cc | 49 | ||||
-rw-r--r-- | compiler/dex/mir_graph.h | 67 | ||||
-rw-r--r-- | compiler/dex/mir_optimization.cc | 95 | ||||
-rw-r--r-- | compiler/dex/pass.h | 145 | ||||
-rw-r--r-- | compiler/dex/pass_driver.cc | 241 | ||||
-rw-r--r-- | compiler/dex/pass_driver.h | 101 | ||||
-rw-r--r-- | compiler/dex/ssa_transformation.cc | 42 |
13 files changed, 1006 insertions, 185 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index d9a573f69b..4340929e9b 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -54,6 +54,8 @@ LIBART_COMPILER_SRC_FILES := \ dex/dex_to_dex_compiler.cc \ dex/mir_dataflow.cc \ dex/mir_optimization.cc \ + dex/pass_driver.cc \ + dex/bb_optimizations.cc \ dex/frontend.cc \ dex/mir_graph.cc \ dex/mir_analysis.cc \ diff --git a/compiler/dex/bb_optimizations.cc b/compiler/dex/bb_optimizations.cc new file mode 100644 index 0000000000..f0130674b5 --- /dev/null +++ b/compiler/dex/bb_optimizations.cc @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2014 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 "bb_optimizations.h" +#include "dataflow_iterator.h" +#include "dataflow_iterator-inl.h" + +namespace art { + +/* + * Code Layout pass implementation start. + */ +bool CodeLayout::WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const { + cUnit->mir_graph->LayoutBlocks(bb); + // No need of repeating, so just return false. + return false; +} + +/* + * SSATransformation pass implementation start. + */ +bool SSATransformation::WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const { + cUnit->mir_graph->InsertPhiNodeOperands(bb); + // No need of repeating, so just return false. + return false; +} + +void SSATransformation::End(CompilationUnit *cUnit) const { + // Verify the dataflow information after the pass. + if (cUnit->enable_debug & (1 << kDebugVerifyDataflow)) { + cUnit->mir_graph->VerifyDataflow(); + } +} + +/* + * ConstantPropagation pass implementation start + */ +bool ConstantPropagation::WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const { + cUnit->mir_graph->DoConstantPropagation(bb); + // No need of repeating, so just return false. + return false; +} + +/* + * MethodUseCount pass implementation start. + */ +bool MethodUseCount::Gate(const CompilationUnit *cUnit) const { + // First initialize the data. + cUnit->mir_graph->InitializeMethodUses(); + + // Now check if the pass is to be ignored. + bool res = ((cUnit->disable_opt & (1 << kPromoteRegs)) == 0); + + return res; +} + +bool MethodUseCount::WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const { + cUnit->mir_graph->CountUses(bb); + // No need of repeating, so just return false. + return false; +} + +/* + * Null Check Elimination and Type Inference Initialization pass implementation start. + */ + +bool NullCheckEliminationAndTypeInferenceInit::Gate(const CompilationUnit *cUnit) const { + // First check the ssa register vector + cUnit->mir_graph->CheckSSARegisterVector(); + + // Did we disable the pass? + bool performInit = ((cUnit->disable_opt & (1 << kNullCheckElimination)) == 0); + + return performInit; +} + +bool NullCheckEliminationAndTypeInferenceInit::WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const { + cUnit->mir_graph->NullCheckEliminationInit(bb); + // No need of repeating, so just return false. + return false; +} + +/* + * BasicBlock Combine pass implementation start. + */ +bool BBCombine::WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const { + cUnit->mir_graph->CombineBlocks(bb); + + // No need of repeating, so just return false. + return false; +} + +/* + * BasicBlock Optimization pass implementation start. + */ +void BBOptimizations::Start(CompilationUnit *cUnit) const { + DCHECK_EQ(cUnit->num_compiler_temps, 0); + + /* + * This pass has a different ordering depEnding on the suppress exception, + * so do the pass here for now: + * - Later, the Start should just change the ordering and we can move the extended + * creation into the pass driver's main job with a new iterator + */ + cUnit->mir_graph->BasicBlockOptimization(); +} + +} // namespace art diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h new file mode 100644 index 0000000000..768b273510 --- /dev/null +++ b/compiler/dex/bb_optimizations.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_ +#define ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_ + +#include "compiler_internals.h" +#include "pass.h" + +namespace art { + +/** + * @class CodeLayout + * @brief Perform the code layout pass. + */ +class CodeLayout : public Pass { + public: + CodeLayout():Pass("CodeLayout", "2_post_layout_cfg") { + } + + void Start(CompilationUnit *cUnit) const { + cUnit->mir_graph->VerifyDataflow(); + } + + bool WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const; +}; + +/** + * @class SSATransformation + * @brief Perform an SSA representation pass on the CompilationUnit. + */ +class SSATransformation : public Pass { + public: + SSATransformation():Pass("SSATransformation", kPreOrderDFSTraversal, "3_post_ssa_cfg") { + } + + bool WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const; + + void Start(CompilationUnit *cUnit) const { + cUnit->mir_graph->InitializeSSATransformation(); + } + + void End(CompilationUnit *cUnit) const; +}; + +/** + * @class ConstantPropagation + * @brief Perform a constant propagation pass. + */ +class ConstantPropagation : public Pass { + public: + ConstantPropagation():Pass("ConstantPropagation") { + } + + bool WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const; + + void Start(CompilationUnit *cUnit) const { + cUnit->mir_graph->InitializeConstantPropagation(); + } +}; + +/** + * @class InitRegLocations + * @brief Initialize Register Locations. + */ +class InitRegLocations : public Pass { + public: + InitRegLocations():Pass("InitRegLocation") { + } + + void Start(CompilationUnit *cUnit) const { + cUnit->mir_graph->InitRegLocations(); + } +}; + +/** + * @class MethodUseCount + * @brief Count the register uses of the method + */ +class MethodUseCount : public Pass { + public: + MethodUseCount():Pass("UseCount") { + } + + bool WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const; + + bool Gate(const CompilationUnit *cUnit) const; +}; + +/** + * @class NullCheckEliminationAndTypeInferenceInit + * @brief Null check elimination and type inference initialization step. + */ +class NullCheckEliminationAndTypeInferenceInit : public Pass { + public: + NullCheckEliminationAndTypeInferenceInit():Pass("NCE_TypeInferenceInit") { + } + + bool WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const; + + bool Gate(const CompilationUnit *cUnit) const; +}; + +/** + * @class NullCheckEliminationAndTypeInference + * @brief Null check elimination and type inference. + */ +class NullCheckEliminationAndTypeInference : public Pass { + public: + NullCheckEliminationAndTypeInference():Pass("NCE_TypeInference", kRepeatingPreOrderDFSTraversal, "4_post_nce_cfg") { + } + + bool WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const { + return cUnit->mir_graph->EliminateNullChecksAndInferTypes(bb); + } +}; + +/** + * @class NullCheckEliminationAndTypeInference + * @brief Null check elimination and type inference. + */ +class BBCombine : public Pass { + public: + BBCombine():Pass("BBCombine", kPreOrderDFSTraversal, "5_post_bbcombine_cfg") { + } + + bool Gate(const CompilationUnit *cUnit) const { + return ((cUnit->disable_opt & (1 << kSuppressExceptionEdges)) != 0); + } + + bool WalkBasicBlocks(CompilationUnit *cUnit, BasicBlock *bb) const; +}; + +/** + * @class BasicBlock Optimizations + * @brief Any simple BasicBlock optimization can be put here. + */ +class BBOptimizations : public Pass { + public: + BBOptimizations():Pass("BBOptimizations", "5_post_bbo_cfg") { + } + + bool Gate(const CompilationUnit *cUnit) const { + return ((cUnit->disable_opt & (1 << kBBOpt)) == 0); + } + + void Start(CompilationUnit *cUnit) const; +}; + +} // namespace art + +#endif // ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_ diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h index 12f924e321..658a9b1c46 100644 --- a/compiler/dex/dataflow_iterator.h +++ b/compiler/dex/dataflow_iterator.h @@ -138,6 +138,21 @@ namespace art { return ForwardSingleNext(); } + + /** + * @brief Redefine the new operator to use the arena + * @param size actually unused, we use our own class size + * @param arena the arena to perform the actual allocation + * @return the pointer to the newly allocated object + */ + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(PreOrderDfsIterator), ArenaAllocator::kAllocGrowableBitMap); + } + + /** + * @brief Redefine delete to not actually delete anything since we are using the arena + */ + static void operator delete(void* p) {} }; /** @@ -169,6 +184,21 @@ namespace art { return ForwardRepeatNext(); } + + /** + * @brief Redefine the new operator to use the arena + * @param size actually unused, we use our own class size + * @param arena the arena to perform the actual allocation + * @return the pointer to the newly allocated object + */ + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(RepeatingPreOrderDfsIterator), ArenaAllocator::kAllocGrowableBitMap); + } + + /** + * @brief Redefine delete to not actually delete anything since we are using the arena + */ + static void operator delete(void* p) {} }; /** @@ -200,6 +230,21 @@ namespace art { return ForwardRepeatNext(); } + + /** + * @brief Redefine the new operator to use the arena + * @param size actually unused, we use our own class size + * @param arena the arena to perform the actual allocation + * @return the pointer to the newly allocated object + */ + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(RepeatingPostOrderDfsIterator), ArenaAllocator::kAllocGrowableBitMap); + } + + /** + * @brief Redefine delete to not actually delete anything since we are using the arena + */ + static void operator delete(void* p) {} }; /** @@ -230,6 +275,21 @@ namespace art { return ReverseSingleNext(); } + + /** + * @brief Redefine the new operator to use the arena + * @param size actually unused, we use our own class size + * @param arena the arena to perform the actual allocation + * @return the pointer to the newly allocated object + */ + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(ReversePostOrderDfsIterator), ArenaAllocator::kAllocGrowableBitMap); + } + + /** + * @brief Redefine delete to not actually delete anything since we are using the arena + */ + static void operator delete(void* p) {} }; /** @@ -261,6 +321,21 @@ namespace art { return ReverseRepeatNext(); } + + /** + * @brief Redefine the new operator to use the arena + * @param size actually unused, we use our own class size + * @param arena the arena to perform the actual allocation + * @return the pointer to the newly allocated object + */ + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(RepeatingReversePostOrderDfsIterator), ArenaAllocator::kAllocGrowableBitMap); + } + + /** + * @brief Redefine delete to not actually delete anything since we are using the arena + */ + static void operator delete(void* p) {} }; /** @@ -291,6 +366,21 @@ namespace art { return ForwardSingleNext(); } + + /** + * @brief Redefine the new operator to use the arena + * @param size actually unused, we use our own class size + * @param arena the arena to perform the actual allocation + * @return the pointer to the newly allocated object + */ + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(PostOrderDOMIterator), ArenaAllocator::kAllocGrowableBitMap); + } + + /** + * @brief Redefine delete to not actually delete anything since we are using the arena + */ + static void operator delete(void* p) {} }; /** @@ -323,6 +413,21 @@ namespace art { */ virtual BasicBlock* Next(bool had_change = false) ALWAYS_INLINE; + /** + * @brief Redefine the new operator to use the arena + * @param size actually unused, we use our own class size + * @param arena the arena to perform the actual allocation + * @return the pointer to the newly allocated object + */ + static void* operator new(size_t size, ArenaAllocator* arena) { + return arena->Alloc(sizeof(AllNodesIterator), ArenaAllocator::kAllocGrowableBitMap); + } + + /** + * @brief Redefine delete to not actually delete anything since we are using the arena + */ + static void operator delete(void* p) {} + private: GrowableArray<BasicBlock*>::Iterator* all_nodes_iterator_; /**< @brief The list of all the nodes */ }; diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index adfbf2f3b5..f5bb85a910 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -21,6 +21,7 @@ #include "dataflow_iterator-inl.h" #include "leb128.h" #include "mirror/object.h" +#include "pass_driver.h" #include "runtime.h" #include "backend.h" #include "base/logging.h" @@ -251,36 +252,9 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler, } #endif - /* Do a code layout pass */ - cu.NewTimingSplit("MIROpt:CodeLayout"); - cu.mir_graph->CodeLayout(); - - /* Perform SSA transformation for the whole method */ - cu.NewTimingSplit("MIROpt:SSATransform"); - cu.mir_graph->SSATransformation(); - - /* Do constant propagation */ - cu.NewTimingSplit("MIROpt:ConstantProp"); - cu.mir_graph->PropagateConstants(); - - cu.NewTimingSplit("MIROpt:InitRegLoc"); - cu.mir_graph->InitRegLocations(); - - /* Count uses */ - cu.NewTimingSplit("MIROpt:UseCount"); - cu.mir_graph->MethodUseCount(); - - /* Perform null check elimination and type inference*/ - cu.NewTimingSplit("MIROpt:NCE_TypeInference"); - cu.mir_graph->NullCheckEliminationAndTypeInference(); - - /* Combine basic blocks where possible */ - cu.NewTimingSplit("MIROpt:BBCombine"); - cu.mir_graph->BasicBlockCombine(); - - /* Do some basic block optimizations */ - cu.NewTimingSplit("MIROpt:BBOpt"); - cu.mir_graph->BasicBlockOptimization(); + /* Create the pass driver and launch it */ + PassDriver driver(&cu); + driver.Launch(); if (cu.enable_debug & (1 << kDebugDumpCheckStats)) { cu.mir_graph->DumpCheckStats(); diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index 728d48ad70..c235448f50 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -1235,9 +1235,9 @@ bool MIRGraph::InvokeUsesMethodStar(MIR* mir) { * counts explicitly used s_regs. A later phase will add implicit * counts for things such as Method*, null-checked references, etc. */ -bool MIRGraph::CountUses(struct BasicBlock* bb) { +void MIRGraph::CountUses(struct BasicBlock* bb) { if (bb->block_type != kDalvikByteCode) { - return false; + return; } // Each level of nesting adds *100 to count, up to 3 levels deep. uint32_t depth = std::min(3U, static_cast<uint32_t>(bb->nesting_depth)); @@ -1269,26 +1269,6 @@ bool MIRGraph::CountUses(struct BasicBlock* bb) { } } } - return false; -} - -void MIRGraph::MethodUseCount() { - // Now that we know, resize the lists. - int num_ssa_regs = GetNumSSARegs(); - use_counts_.Resize(num_ssa_regs + 32); - raw_use_counts_.Resize(num_ssa_regs + 32); - // Initialize list - for (int i = 0; i < num_ssa_regs; i++) { - use_counts_.Insert(0); - raw_use_counts_.Insert(0); - } - if (cu_->disable_opt & (1 << kPromoteRegs)) { - return; - } - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { - CountUses(bb); - } } /* Verify if all the successor is connected with all the claimed predecessors */ diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index b1c0b2867a..8d1653fc6c 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -1147,4 +1147,53 @@ BasicBlock* MIRGraph::NewMemBB(BBType block_type, int block_id) { return bb; } +void MIRGraph::InitializeConstantPropagation() { + is_constant_v_ = new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false); + constant_values_ = static_cast<int*>(arena_->Alloc(sizeof(int) * GetNumSSARegs(), ArenaAllocator::kAllocDFInfo)); +} + +void MIRGraph::InitializeMethodUses() { + // The gate starts by initializing the use counts + int num_ssa_regs = GetNumSSARegs(); + use_counts_.Resize(num_ssa_regs + 32); + raw_use_counts_.Resize(num_ssa_regs + 32); + // Initialize list + for (int i = 0; i < num_ssa_regs; i++) { + use_counts_.Insert(0); + raw_use_counts_.Insert(0); + } +} + +void MIRGraph::InitializeSSATransformation() { + /* Compute the DFS order */ + ComputeDFSOrders(); + + /* Compute the dominator info */ + ComputeDominators(); + + /* Allocate data structures in preparation for SSA conversion */ + CompilerInitializeSSAConversion(); + + /* Find out the "Dalvik reg def x block" relation */ + ComputeDefBlockMatrix(); + + /* Insert phi nodes to dominance frontiers for all variables */ + InsertPhiNodes(); + + /* Rename register names by local defs and phi nodes */ + ClearAllVisitedFlags(); + DoDFSPreOrderSSARename(GetEntryBlock()); + + /* + * Shared temp bit vector used by each block to count the number of defs + * from all the predecessor blocks. + */ + temp_ssa_register_v_ = + new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false, kBitMapTempSSARegisterV); +} + +void MIRGraph::CheckSSARegisterVector() { + DCHECK(temp_ssa_register_v_ != nullptr); +} + } // namespace art diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index d080e392cd..b68e6997ae 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -615,17 +615,12 @@ class MIRGraph { return opcode >= static_cast<int>(kMirOpFirst); } - void BasicBlockCombine(); - void CodeLayout(); void DumpCheckStats(); - void PropagateConstants(); MIR* FindMoveResult(BasicBlock* bb, MIR* mir); int SRegToVReg(int ssa_reg) const; void VerifyDataflow(); - void MethodUseCount(); - void SSATransformation(); void CheckForDominanceFrontier(BasicBlock* dom_bb, const BasicBlock* succ_bb); - void NullCheckEliminationAndTypeInference(); + bool EliminateNullChecksAndInferTypes(BasicBlock *bb); /* * Type inference handling helpers. Because Dalvik's bytecode is not fully typed, * we have to do some work to figure out the sreg type. For some operations it is @@ -670,6 +665,58 @@ class MIRGraph { BasicBlock* NextDominatedBlock(BasicBlock* bb); bool LayoutBlocks(BasicBlock* bb); + /** + * @brief Perform the initial preparation for the Method Uses. + */ + void InitializeMethodUses(); + + /** + * @brief Perform the initial preparation for the Constant Propagation. + */ + void InitializeConstantPropagation(); + + /** + * @brief Perform the initial preparation for the SSA Transformation. + */ + void InitializeSSATransformation(); + + /** + * @brief Insert a the operands for the Phi nodes. + * @param bb the considered BasicBlock. + * @return true + */ + bool InsertPhiNodeOperands(BasicBlock* bb); + + /** + * @brief Perform constant propagation on a BasicBlock. + * @param bb the considered BasicBlock. + */ + void DoConstantPropagation(BasicBlock* bb); + + /** + * @brief Count the uses in the BasicBlock + * @param bb the BasicBlock + */ + void CountUses(struct BasicBlock* bb); + + /** + * @brief Initialize the data structures with Null Check data + * @param bb the considered BasicBlock + */ + void NullCheckEliminationInit(BasicBlock* bb); + + /** + * @brief Check if the temporary ssa register vector is allocated + */ + void CheckSSARegisterVector(); + + /** + * @brief Combine BasicBlocks + * @param the BasicBlock we are considering + */ + void CombineBlocks(BasicBlock* bb); + + void ClearAllVisitedFlags(); /* * IsDebugBuild sanity check: keep track of the Dex PCs for catch entries so that later on * we can verify that all catch entries have native PC entries. @@ -715,8 +762,6 @@ class MIRGraph { void DataFlowSSAFormat35C(MIR* mir); void DataFlowSSAFormat3RC(MIR* mir); bool FindLocalLiveIn(BasicBlock* bb); - void ClearAllVisitedFlags(); - bool CountUses(struct BasicBlock* bb); bool InferTypeAndSize(BasicBlock* bb, MIR* mir, bool changed); bool VerifyPredInfo(BasicBlock* bb); BasicBlock* NeedsVisit(BasicBlock* bb); @@ -733,8 +778,6 @@ class MIRGraph { void SetConstantWide(int ssa_reg, int64_t value); int GetSSAUseCount(int s_reg); bool BasicBlockOpt(BasicBlock* bb); - bool EliminateNullChecksAndInferTypes(BasicBlock* bb); - void NullCheckEliminationInit(BasicBlock* bb); bool BuildExtendedBBList(struct BasicBlock* bb); bool FillDefBlockMatrix(BasicBlock* bb); void InitializeDominationInfo(BasicBlock* bb); @@ -742,11 +785,9 @@ class MIRGraph { bool ComputeBlockDominators(BasicBlock* bb); bool SetDominators(BasicBlock* bb); bool ComputeBlockLiveIns(BasicBlock* bb); - bool InsertPhiNodeOperands(BasicBlock* bb); bool ComputeDominanceFrontier(BasicBlock* bb); - void DoConstantPropogation(BasicBlock* bb); + void CountChecks(BasicBlock* bb); - bool CombineBlocks(BasicBlock* bb); void AnalyzeBlock(BasicBlock* bb, struct MethodStats* stats); bool ComputeSkipCompilation(struct MethodStats* stats, bool skip_default); diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index 5d83991001..ee9f28e184 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -36,7 +36,7 @@ void MIRGraph::SetConstantWide(int ssa_reg, int64_t value) { constant_values_[ssa_reg + 1] = High32Bits(value); } -void MIRGraph::DoConstantPropogation(BasicBlock* bb) { +void MIRGraph::DoConstantPropagation(BasicBlock* bb) { MIR* mir; for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { @@ -92,16 +92,6 @@ void MIRGraph::DoConstantPropogation(BasicBlock* bb) { /* TODO: implement code to handle arithmetic operations */ } -void MIRGraph::PropagateConstants() { - is_constant_v_ = new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false); - constant_values_ = static_cast<int*>(arena_->Alloc(sizeof(int) * GetNumSSARegs(), - ArenaAllocator::kAllocDFInfo)); - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { - DoConstantPropogation(bb); - } -} - /* Advance to next strictly dominated MIR node in an extended basic block */ MIR* MIRGraph::AdvanceMIR(BasicBlock** p_bb, MIR* mir) { BasicBlock* bb = *p_bb; @@ -557,7 +547,7 @@ bool MIRGraph::LayoutBlocks(BasicBlock* bb) { } /* Combine any basic blocks terminated by instructions that we now know can't throw */ -bool MIRGraph::CombineBlocks(struct BasicBlock* bb) { +void MIRGraph::CombineBlocks(struct BasicBlock* bb) { // Loop here to allow combining a sequence of blocks while (true) { // Check termination conditions @@ -621,14 +611,13 @@ bool MIRGraph::CombineBlocks(struct BasicBlock* bb) { // Now, loop back and see if we can keep going } - return false; } /* * Eliminate unnecessary null checks for a basic block. Also, while we're doing * an iterative walk go ahead and perform type and size inference. */ -bool MIRGraph::EliminateNullChecksAndInferTypes(struct BasicBlock* bb) { +bool MIRGraph::EliminateNullChecksAndInferTypes(BasicBlock* bb) { if (bb->data_flow_info == NULL) return false; bool infer_changed = false; bool do_nce = ((cu_->disable_opt & (1 << kNullCheckElimination)) == 0); @@ -810,49 +799,6 @@ bool MIRGraph::EliminateNullChecksAndInferTypes(struct BasicBlock* bb) { return infer_changed | nce_changed; } -void MIRGraph::NullCheckEliminationAndTypeInference() { - DCHECK(temp_ssa_register_v_ != NULL); - if ((cu_->disable_opt & (1 << kNullCheckElimination)) == 0) { - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { - NullCheckEliminationInit(bb); - } - } - RepeatingPreOrderDfsIterator iter2(this); - bool change = false; - for (BasicBlock* bb = iter2.Next(change); bb != NULL; bb = iter2.Next(change)) { - change = EliminateNullChecksAndInferTypes(bb); - } - if (cu_->enable_debug & (1 << kDebugDumpCFG)) { - DumpCFG("/sdcard/4_post_nce_cfg/", false); - } -} - -void MIRGraph::BasicBlockCombine() { - if ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) { - PreOrderDfsIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { - CombineBlocks(bb); - } - if (cu_->enable_debug & (1 << kDebugDumpCFG)) { - DumpCFG("/sdcard/5_post_bbcombine_cfg/", false); - } - } -} - -void MIRGraph::CodeLayout() { - if (cu_->enable_debug & (1 << kDebugVerifyDataflow)) { - VerifyDataflow(); - } - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { - LayoutBlocks(bb); - } - if (cu_->enable_debug & (1 << kDebugDumpCFG)) { - DumpCFG("/sdcard/2_post_layout_cfg/", true); - } -} - void MIRGraph::DumpCheckStats() { Checkstats* stats = static_cast<Checkstats*>(arena_->Alloc(sizeof(Checkstats), ArenaAllocator::kAllocDFInfo)); @@ -909,29 +855,22 @@ bool MIRGraph::BuildExtendedBBList(struct BasicBlock* bb) { return false; // Not iterative - return value will be ignored } - void MIRGraph::BasicBlockOptimization() { - if (!(cu_->disable_opt & (1 << kBBOpt))) { - DCHECK_EQ(cu_->num_compiler_temps, 0); - if ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) { - ClearAllVisitedFlags(); - PreOrderDfsIterator iter2(this); - for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) { - BuildExtendedBBList(bb); - } - // Perform extended basic block optimizations. - for (unsigned int i = 0; i < extended_basic_blocks_.size(); i++) { - BasicBlockOpt(GetBasicBlock(extended_basic_blocks_[i])); - } - } else { - PreOrderDfsIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { - BasicBlockOpt(bb); - } + if ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) { + ClearAllVisitedFlags(); + PreOrderDfsIterator iter2(this); + for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) { + BuildExtendedBBList(bb); + } + // Perform extended basic block optimizations. + for (unsigned int i = 0; i < extended_basic_blocks_.size(); i++) { + BasicBlockOpt(GetBasicBlock(extended_basic_blocks_[i])); + } + } else { + PreOrderDfsIterator iter(this); + for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) { + BasicBlockOpt(bb); } - } - if (cu_->enable_debug & (1 << kDebugDumpCFG)) { - DumpCFG("/sdcard/6_post_bbo_cfg/", false); } } diff --git a/compiler/dex/pass.h b/compiler/dex/pass.h new file mode 100644 index 0000000000..c52ddf5f27 --- /dev/null +++ b/compiler/dex/pass.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef ART_COMPILER_DEX_PASS_H_ +#define ART_COMPILER_DEX_PASS_H_ + +#include <string> + +namespace art { + +// Forward declarations. +class BasicBlock; +class CompilationUnit; +class Pass; + +/** + * @brief OptimizationFlag is an enumeration to perform certain tasks for a given pass. + * @details Each enum should be a power of 2 to be correctly used. + */ +enum OptimizationFlag { +}; + +enum DataFlowAnalysisMode { + kAllNodes = 0, /**< @brief All nodes. */ + kPreOrderDFSTraversal, /**< @brief Depth-First-Search / Pre-Order. */ + kRepeatingPreOrderDFSTraversal, /**< @brief Depth-First-Search / Repeating Pre-Order. */ + kReversePostOrderDFSTraversal, /**< @brief Depth-First-Search / Reverse Post-Order. */ + kRepeatingPostOrderDFSTraversal, /**< @brief Depth-First-Search / Repeating Post-Order. */ + kRepeatingReversePostOrderDFSTraversal, /**< @brief Depth-First-Search / Repeating Reverse Post-Order. */ + kPostOrderDOMTraversal, /**< @brief Dominator tree / Post-Order. */ +}; + +/** + * @class Pass + * @brief Pass is the Pass structure for the optimizations. + * @details The following structure has the different optimization passes that we are going to do. + */ +class Pass { + public: + Pass(const char *name, DataFlowAnalysisMode type, bool freed, const unsigned int f, const char *dump): pass_name_(name), traversal_type_(type), flags_(f), dump_cfg_folder_(dump) { + } + + Pass(const char *name, const char *dump): pass_name_(name), traversal_type_(kAllNodes), flags_(0), dump_cfg_folder_(dump) { + } + + explicit Pass(const char *name):pass_name_(name), traversal_type_(kAllNodes), flags_(0), dump_cfg_folder_("") { + } + + Pass(const char *name, DataFlowAnalysisMode type, const char *dump):pass_name_(name), traversal_type_(type), flags_(false), dump_cfg_folder_(dump) { + } + + virtual ~Pass() {} + + virtual const char* GetName() const { + return pass_name_; + } + + virtual DataFlowAnalysisMode GetTraversal() const { + return traversal_type_; + } + + virtual bool GetFlag(OptimizationFlag flag) const { + return (flags_ & flag); + } + + const char* GetDumpCFGFolder() const {return dump_cfg_folder_;} + + /** + * @brief Gate for the pass: determines whether to execute the pass or not considering a CompilationUnit + * @param c_unit the CompilationUnit. + * @return whether or not to execute the pass + */ + virtual bool Gate(const CompilationUnit *c_unit) const { + // Unused parameter. + UNUSED(c_unit); + + // Base class says yes. + return true; + } + + /** + * @brief Start of the pass: called before the WalkBasicBlocks function + * @param c_unit the considered CompilationUnit. + */ + virtual void Start(CompilationUnit *c_unit) const { + // Unused parameter. + UNUSED(c_unit); + } + + /** + * @brief End of the pass: called after the WalkBasicBlocks function + * @param c_unit the considered CompilationUnit. + */ + virtual void End(CompilationUnit *c_unit) const { + // Unused parameter. + UNUSED(c_unit); + } + + /** + * @brief Actually walk the BasicBlocks following a particular traversal type. + * @param c_unit the CompilationUnit. + * @param bb the BasicBlock. + * @return whether or not there is a change when walking the BasicBlock + */ + virtual bool WalkBasicBlocks(CompilationUnit *c_unit, BasicBlock *bb) const { + // Unused parameters. + UNUSED(c_unit); + UNUSED(bb); + + // BasicBlock did not change. + return false; + } + + protected: + /** @brief The pass name: used for searching for a pass when running a particular pass or debugging. */ + const char* const pass_name_; + + /** @brief Type of traversal: determines the order to execute the pass on the BasicBlocks. */ + const DataFlowAnalysisMode traversal_type_; + + /** @brief Flags for additional directives: used to determine if a particular clean-up is necessary post pass. */ + const unsigned int flags_; + + /** @brief CFG Dump Folder: what sub-folder to use for dumping the CFGs post pass. */ + const char* const dump_cfg_folder_; + + private: + // In order to make the all passes not copy-friendly. + DISALLOW_COPY_AND_ASSIGN(Pass); +}; +} // namespace art +#endif // ART_COMPILER_DEX_PASS_H_ diff --git a/compiler/dex/pass_driver.cc b/compiler/dex/pass_driver.cc new file mode 100644 index 0000000000..b20d7283af --- /dev/null +++ b/compiler/dex/pass_driver.cc @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2014 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 <dlfcn.h> + +#include "bb_optimizations.h" +#include "compiler_internals.h" +#include "dataflow_iterator.h" +#include "dataflow_iterator-inl.h" +#include "pass.h" +#include "pass_driver.h" + +namespace art { + +PassDriver::PassDriver(CompilationUnit* const cu, bool create_default_passes) : cu_(cu) { + dump_cfg_folder_ = "/sdcard/"; + + // If need be, create the default passes. + if (create_default_passes == true) { + CreatePasses(); + } +} + +PassDriver::~PassDriver() { + // Clear the map: done to remove any chance of having a pointer after freeing below + pass_map_.clear(); +} + +void PassDriver::InsertPass(Pass* new_pass, bool warn_override) { + assert(new_pass != 0); + + // Get name here to not do it all over the method. + const std::string& name = new_pass->GetName(); + + // Do we want to warn the user about squashing a pass? + if (warn_override == false) { + SafeMap<std::string, Pass* >::iterator it = pass_map_.find(name); + + if (it != pass_map_.end()) { + LOG(INFO) << "Pass name " << name << " already used, overwriting pass"; + } + } + + // Now add to map and list. + pass_map_.Put(name, new_pass); + pass_list_.push_back(new_pass); +} + +void PassDriver::CreatePasses() { + /* + * Create the pass list: + * - These passes are immutable and are shared across the threads: + * - This is achieved via: + * - The UniquePtr used here. + * - DISALLOW_COPY_AND_ASSIGN in the base Pass class. + * + * Advantage is that there will be no race conditions here. + * Disadvantage is the passes can't change their internal states depending on CompilationUnit: + * - This is not yet an issue: no current pass would require it. + */ + static UniquePtr<Pass> *passes[] = { + new UniquePtr<Pass>(new CodeLayout()), + new UniquePtr<Pass>(new SSATransformation()), + new UniquePtr<Pass>(new ConstantPropagation()), + new UniquePtr<Pass>(new InitRegLocations()), + new UniquePtr<Pass>(new MethodUseCount()), + new UniquePtr<Pass>(new NullCheckEliminationAndTypeInferenceInit()), + new UniquePtr<Pass>(new NullCheckEliminationAndTypeInference()), + new UniquePtr<Pass>(new BBCombine()), + new UniquePtr<Pass>(new BBOptimizations()), + }; + + // Get number of elements in the array. + unsigned int nbr = (sizeof(passes) / sizeof(passes[0])); + + // Insert each pass into the map and into the list via the InsertPass method: + // - Map is used for the lookup + // - List is used for the pass walk + for (unsigned int i = 0; i < nbr; i++) { + InsertPass(passes[i]->get()); + } +} + +void PassDriver::HandlePassFlag(CompilationUnit* c_unit, Pass* pass) { + // Unused parameters for the moment. + UNUSED(c_unit); + UNUSED(pass); +} + +void PassDriver::DispatchPass(CompilationUnit* c_unit, Pass* curPass) { + DataflowIterator* iterator = 0; + + LOG(DEBUG) << "Dispatching " << curPass->GetName(); + + MIRGraph* mir_graph = c_unit->mir_graph.get(); + ArenaAllocator *arena = &(c_unit->arena); + + // Let us start by getting the right iterator. + DataFlowAnalysisMode mode = curPass->GetTraversal(); + + switch (mode) { + case kPreOrderDFSTraversal: + iterator = new (arena) PreOrderDfsIterator(mir_graph); + break; + case kRepeatingPreOrderDFSTraversal: + iterator = new (arena) RepeatingPreOrderDfsIterator(mir_graph); + break; + case kRepeatingPostOrderDFSTraversal: + iterator = new (arena) RepeatingPostOrderDfsIterator(mir_graph); + break; + case kReversePostOrderDFSTraversal: + iterator = new (arena) ReversePostOrderDfsIterator(mir_graph); + break; + case kRepeatingReversePostOrderDFSTraversal: + iterator = new (arena) RepeatingReversePostOrderDfsIterator(mir_graph); + break; + case kPostOrderDOMTraversal: + iterator = new (arena) PostOrderDOMIterator(mir_graph); + break; + case kAllNodes: + iterator = new (arena) AllNodesIterator(mir_graph); + break; + default: + LOG(DEBUG) << "Iterator mode not handled in dispatcher: " << mode; + return; + } + + // Paranoid: Check the iterator before walking the BasicBlocks. + assert(iterator != 0); + + bool change = false; + for (BasicBlock *bb = iterator->Next(change); bb != 0; bb = iterator->Next(change)) { + change = curPass->WalkBasicBlocks(c_unit, bb); + } +} + +void PassDriver::ApplyPass(CompilationUnit* c_unit, Pass* curPass) { + curPass->Start(c_unit); + DispatchPass(c_unit, curPass); + curPass->End(c_unit); +} + +bool PassDriver::RunPass(CompilationUnit* c_unit, Pass* curPass, bool time_split) { + // Paranoid: c_unit or curPass cannot be 0, and the pass should have a name. + if (c_unit == 0 || curPass == 0 || (strcmp(curPass->GetName(), "") == 0)) { + return false; + } + + // Do we perform a time split + if (time_split == true) { + std::string name = "MIROpt:"; + name += curPass->GetName(); + c_unit->NewTimingSplit(name.c_str()); + } + + // Check the pass gate first. + bool shouldApplyPass = curPass->Gate(c_unit); + + if (shouldApplyPass == true) { + // Applying the pass: first start, doWork, and end calls. + ApplyPass(c_unit, curPass); + + // Clean up if need be. + HandlePassFlag(c_unit, curPass); + + // Do we want to log it? + if ((c_unit->enable_debug& (1 << kDebugDumpCFG)) != 0) { + // Do we have a pass folder? + const std::string& passFolder = curPass->GetDumpCFGFolder(); + + if (passFolder != "") { + // Create directory prefix. + std::string prefix = GetDumpCFGFolder(); + prefix += passFolder; + prefix += "/"; + + c_unit->mir_graph->DumpCFG(prefix.c_str(), false); + } + } + } + + // If the pass gate passed, we can declare success. + return shouldApplyPass; +} + +bool PassDriver::RunPass(CompilationUnit* c_unit, const std::string& pass_name) { + // Paranoid: c_unit cannot be 0 and we need a pass name. + if (c_unit == 0 || pass_name == "") { + return false; + } + + Pass* curPass = GetPass(pass_name); + + if (curPass != 0) { + return RunPass(c_unit, curPass); + } + + // Return false, we did not find the pass. + return false; +} + +void PassDriver::Launch() { + for (std::list<Pass* >::iterator it = pass_list_.begin(); it != pass_list_.end(); it++) { + Pass* curPass = *it; + RunPass(cu_, curPass, true); + } +} + +void PassDriver::PrintPassNames() const { + LOG(INFO) << "Loop Passes are:"; + + for (std::list<Pass* >::const_iterator it = pass_list_.begin(); it != pass_list_.end(); it++) { + const Pass* curPass = *it; + LOG(INFO) << "\t-" << curPass->GetName(); + } +} + +Pass* PassDriver::GetPass(const std::string& name) const { + SafeMap<std::string, Pass*>::const_iterator it = pass_map_.find(name); + + if (it != pass_map_.end()) { + return it->second; + } + + return 0; +} + +} // namespace art diff --git a/compiler/dex/pass_driver.h b/compiler/dex/pass_driver.h new file mode 100644 index 0000000000..29da3519bd --- /dev/null +++ b/compiler/dex/pass_driver.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef ART_COMPILER_DEX_PASS_DRIVER_H_ +#define ART_COMPILER_DEX_PASS_DRIVER_H_ + +#include <list> +#include "pass.h" +#include "safe_map.h" + +// Forward Declarations. +class CompilationUnit; +class Pass; + +namespace art { + +/** + * @class PassDriver + * @brief PassDriver is the wrapper around all Pass instances in order to execute them from the Middle-End + */ +class PassDriver { + public: + explicit PassDriver(CompilationUnit* const cu, bool create_default_passes = true); + + ~PassDriver(); + + /** + * @brief Insert a Pass: can warn if multiple passes have the same name. + * @param new_pass the new Pass to insert in the map and list. + * @param warn_override warn if the name of the Pass is already used. + */ + void InsertPass(Pass *new_pass, bool warn_override = true); + + /** + * @brief Run a pass using the name as key. + * @param c_unit the considered CompilationUnit. + * @param pass_name the Pass name. + * @return whether the pass was applied. + */ + bool RunPass(CompilationUnit* c_unit, const std::string& pass_name); + + /** + * @brief Run a pass using the Pass itself. + * @param time_split do we want a time split request(default: false)? + * @return whether the pass was applied. + */ + bool RunPass(CompilationUnit* c_unit, Pass* pass, bool time_split = false); + + void Launch(); + + void HandlePassFlag(CompilationUnit* c_unit, Pass* pass); + + /** + * @brief Apply a patch: perform start/work/end functions. + */ + void ApplyPass(CompilationUnit* c_unit, Pass* pass); + + /** + * @brief Dispatch a patch: walk the BasicBlocks depending on the traversal mode + */ + void DispatchPass(CompilationUnit* c_unit, Pass* pass); + + void PrintPassNames() const; + + Pass* GetPass(const std::string& name) const; + + const char *GetDumpCFGFolder() const { + return dump_cfg_folder_; + } + + protected: + void CreatePasses(); + + /** @brief The Pass Map: contains name -> pass for quick lookup. */ + SafeMap<std::string, Pass*> pass_map_; + + /** @brief List of passes: provides the order to execute the passes. */ + std::list<Pass*> pass_list_; + + /** @brief The CompilationUnit on which to execute the passes on. */ + CompilationUnit* const cu_; + + /** @brief Dump CFG base folder: where is the base folder for dumping CFGs. */ + const char* dump_cfg_folder_; +}; + +} // namespace art +#endif // ART_COMPILER_DEX_PASS_DRIVER_H_ diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index 0d8bd07f40..502df1e9e9 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -669,46 +669,4 @@ void MIRGraph::DoDFSPreOrderSSARename(BasicBlock* block) { return; } -/* Perform SSA transformation for the whole method */ -void MIRGraph::SSATransformation() { - /* Compute the DFS order */ - ComputeDFSOrders(); - - /* Compute the dominator info */ - ComputeDominators(); - - /* Allocate data structures in preparation for SSA conversion */ - CompilerInitializeSSAConversion(); - - /* Find out the "Dalvik reg def x block" relation */ - ComputeDefBlockMatrix(); - - /* Insert phi nodes to dominance frontiers for all variables */ - InsertPhiNodes(); - - /* Rename register names by local defs and phi nodes */ - ClearAllVisitedFlags(); - DoDFSPreOrderSSARename(GetEntryBlock()); - - /* - * Shared temp bit vector used by each block to count the number of defs - * from all the predecessor blocks. - */ - temp_ssa_register_v_ = - new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false, kBitMapTempSSARegisterV); - - /* Insert phi-operands with latest SSA names from predecessor blocks */ - PreOrderDfsIterator iter2(this); - for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) { - InsertPhiNodeOperands(bb); - } - - if (cu_->enable_debug & (1 << kDebugDumpCFG)) { - DumpCFG("/sdcard/3_post_ssa_cfg/", false); - } - if (cu_->enable_debug & (1 << kDebugVerifyDataflow)) { - VerifyDataflow(); - } -} - } // namespace art |