diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 33242f1c5de0cf6f840cf22e6acac285e29d7245..426c3cac64e3f7bfc3db034ee38d965dfc80e6db 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -247,12 +247,6 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ runtime/reflection_test.cc \ compiler/compiled_method_test.cc \ compiler/debug/dwarf/dwarf_test.cc \ - compiler/dex/gvn_dead_code_elimination_test.cc \ - compiler/dex/global_value_numbering_test.cc \ - compiler/dex/local_value_numbering_test.cc \ - compiler/dex/mir_graph_test.cc \ - compiler/dex/mir_optimization_test.cc \ - compiler/dex/type_inference_test.cc \ compiler/driver/compiled_method_storage_test.cc \ compiler/driver/compiler_driver_test.cc \ compiler/elf_writer_test.cc \ @@ -284,7 +278,6 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/utils/test_dex_file_builder_test.cc \ COMPILER_GTEST_COMMON_SRC_FILES_all := \ - compiler/dex/quick/quick_cfi_test.cc \ compiler/jni/jni_cfi_test.cc \ compiler/optimizing/codegen_test.cc \ compiler/optimizing/constant_folding_test.cc \ @@ -374,7 +367,6 @@ COMPILER_GTEST_HOST_SRC_FILES_mips64 := \ COMPILER_GTEST_HOST_SRC_FILES_x86 := \ $(COMPILER_GTEST_COMMON_SRC_FILES_x86) \ - compiler/dex/quick/x86/quick_assemble_x86_test.cc \ compiler/utils/x86/assembler_x86_test.cc \ COMPILER_GTEST_HOST_SRC_FILES_x86_64 := \ diff --git a/compiler/Android.mk b/compiler/Android.mk index 11ee6dd3a1b887c948ff9f322a78abd02200ec09..f12f00743b43c5739bd5665d129b73afae5378a2 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -21,40 +21,12 @@ include art/build/Android.common_build.mk LIBART_COMPILER_SRC_FILES := \ compiled_method.cc \ debug/elf_debug_writer.cc \ - dex/global_value_numbering.cc \ - dex/gvn_dead_code_elimination.cc \ - dex/local_value_numbering.cc \ - dex/type_inference.cc \ - dex/quick/codegen_util.cc \ - dex/quick/dex_file_method_inliner.cc \ - dex/quick/dex_file_to_method_inliner_map.cc \ - dex/quick/gen_common.cc \ - dex/quick/gen_invoke.cc \ - dex/quick/gen_loadstore.cc \ - dex/quick/lazy_debug_frame_opcode_writer.cc \ - dex/quick/local_optimizations.cc \ - dex/quick/mir_to_lir.cc \ - dex/quick/quick_compiler.cc \ - dex/quick/ralloc_util.cc \ - dex/quick/resource_mask.cc \ dex/dex_to_dex_compiler.cc \ - dex/bb_optimizations.cc \ - dex/compiler_ir.cc \ - dex/mir_analysis.cc \ - dex/mir_dataflow.cc \ - dex/mir_field_info.cc \ - dex/mir_graph.cc \ - dex/mir_method_info.cc \ - dex/mir_optimization.cc \ - dex/post_opt_passes.cc \ - dex/pass_driver_me_opts.cc \ - dex/pass_driver_me_post_opt.cc \ - dex/pass_manager.cc \ - dex/ssa_transformation.cc \ dex/verified_method.cc \ dex/verification_results.cc \ - dex/vreg_analysis.cc \ dex/quick_compiler_callbacks.cc \ + dex/quick/dex_file_method_inliner.cc \ + dex/quick/dex_file_to_method_inliner_map.cc \ driver/compiled_method_storage.cc \ driver/compiler_driver.cc \ driver/compiler_options.cc \ @@ -111,12 +83,6 @@ LIBART_COMPILER_SRC_FILES := \ oat_writer.cc LIBART_COMPILER_SRC_FILES_arm := \ - dex/quick/arm/assemble_arm.cc \ - dex/quick/arm/call_arm.cc \ - dex/quick/arm/fp_arm.cc \ - dex/quick/arm/int_arm.cc \ - dex/quick/arm/target_arm.cc \ - dex/quick/arm/utility_arm.cc \ jni/quick/arm/calling_convention_arm.cc \ linker/arm/relative_patcher_arm_base.cc \ linker/arm/relative_patcher_thumb2.cc \ @@ -133,12 +99,6 @@ LIBART_COMPILER_SRC_FILES_arm := \ # 32bit one. LIBART_COMPILER_SRC_FILES_arm64 := \ $(LIBART_COMPILER_SRC_FILES_arm) \ - dex/quick/arm64/assemble_arm64.cc \ - dex/quick/arm64/call_arm64.cc \ - dex/quick/arm64/fp_arm64.cc \ - dex/quick/arm64/int_arm64.cc \ - dex/quick/arm64/target_arm64.cc \ - dex/quick/arm64/utility_arm64.cc \ jni/quick/arm64/calling_convention_arm64.cc \ linker/arm64/relative_patcher_arm64.cc \ optimizing/code_generator_arm64.cc \ @@ -150,12 +110,6 @@ LIBART_COMPILER_SRC_FILES_arm64 := \ utils/arm64/managed_register_arm64.cc \ LIBART_COMPILER_SRC_FILES_mips := \ - dex/quick/mips/assemble_mips.cc \ - dex/quick/mips/call_mips.cc \ - dex/quick/mips/fp_mips.cc \ - dex/quick/mips/int_mips.cc \ - dex/quick/mips/target_mips.cc \ - dex/quick/mips/utility_mips.cc \ jni/quick/mips/calling_convention_mips.cc \ optimizing/code_generator_mips.cc \ optimizing/intrinsics_mips.cc \ @@ -172,12 +126,6 @@ LIBART_COMPILER_SRC_FILES_mips64 := \ LIBART_COMPILER_SRC_FILES_x86 := \ - dex/quick/x86/assemble_x86.cc \ - dex/quick/x86/call_x86.cc \ - dex/quick/x86/fp_x86.cc \ - dex/quick/x86/int_x86.cc \ - dex/quick/x86/target_x86.cc \ - dex/quick/x86/utility_x86.cc \ jni/quick/x86/calling_convention_x86.cc \ linker/x86/relative_patcher_x86.cc \ linker/x86/relative_patcher_x86_base.cc \ @@ -200,26 +148,20 @@ LIBART_COMPILER_SRC_FILES_x86_64 := \ LIBART_COMPILER_CFLAGS := LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \ - dex/quick/resource_mask.h \ dex/compiler_enums.h \ dex/dex_to_dex_compiler.h \ - dex/global_value_numbering.h \ - dex/pass_me.h \ driver/compiler_driver.h \ driver/compiler_options.h \ image_writer.h \ optimizing/locations.h LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_arm := \ - dex/quick/arm/arm_lir.h \ utils/arm/constants_arm.h LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_arm64 := \ - $(LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_arm) \ - dex/quick/arm64/arm64_lir.h + $(LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_arm) LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_mips := \ - dex/quick/mips/mips_lir.h \ utils/mips/assembler_mips.h LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_mips64 := \ diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 6075cd6fbe2c6731cbc739098fa7d617b143c350..6483ef63b12aeef8e5620062c4a7585999969396 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -21,7 +21,6 @@ #include "art_method.h" #include "class_linker.h" #include "compiled_method.h" -#include "dex/pass_manager.h" #include "dex/quick_compiler_callbacks.h" #include "dex/quick/dex_file_to_method_inliner_map.h" #include "dex/verification_results.h" diff --git a/compiler/compiler.cc b/compiler/compiler.cc index 223affad82ccce8e660951afb5c4e3a914411aea..16263177d8cfbd67c69c09ee628350818074e736 100644 --- a/compiler/compiler.cc +++ b/compiler/compiler.cc @@ -17,7 +17,6 @@ #include "compiler.h" #include "base/logging.h" -#include "dex/quick/quick_compiler_factory.h" #include "driver/compiler_driver.h" #include "optimizing/optimizing_compiler.h" #include "utils.h" @@ -27,8 +26,7 @@ namespace art { Compiler* Compiler::Create(CompilerDriver* driver, Compiler::Kind kind) { switch (kind) { case kQuick: - return CreateQuickCompiler(driver); - + // TODO: Remove Quick in options. case kOptimizing: return CreateOptimizingCompiler(driver); diff --git a/compiler/dex/bb_optimizations.cc b/compiler/dex/bb_optimizations.cc deleted file mode 100644 index 11a7e44f9852c139357753176d8a2fdbfd6e2f8e..0000000000000000000000000000000000000000 --- a/compiler/dex/bb_optimizations.cc +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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::Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - c_unit->mir_graph->LayoutBlocks(bb); - // No need of repeating, so just return false. - return false; -} - -/* - * BasicBlock Combine pass implementation start. - */ -bool BBCombine::Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - c_unit->mir_graph->CombineBlocks(bb); - - // No need of repeating, so just return false. - return false; -} - -/* - * MethodUseCount pass implementation start. - */ -bool MethodUseCount::Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - // First initialize the data. - c_unit->mir_graph->InitializeMethodUses(); - - // Now check if the pass is to be ignored. - bool res = ((c_unit->disable_opt & (1 << kPromoteRegs)) == 0); - - return res; -} - -bool MethodUseCount::Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - c_unit->mir_graph->CountUses(bb); - // No need of repeating, so just return false. - return false; -} - -} // namespace art diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h deleted file mode 100644 index 02d532798c633658427ec204466a254bccc6f5da..0000000000000000000000000000000000000000 --- a/compiler/dex/bb_optimizations.h +++ /dev/null @@ -1,452 +0,0 @@ -/* - * 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 "base/casts.h" -#include "compiler_ir.h" -#include "dex_flags.h" -#include "pass_me.h" -#include "mir_graph.h" - -namespace art { - -/** - * @class String Change - * @brief Converts calls to String. to StringFactory instead. - */ -class StringChange : public PassME { - public: - StringChange() : PassME("StringChange", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->StringChange(); - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->HasInvokes(); - } -}; - -/** - * @class CacheFieldLoweringInfo - * @brief Cache the lowering info for fields used by IGET/IPUT/SGET/SPUT insns. - */ -class CacheFieldLoweringInfo : public PassME { - public: - CacheFieldLoweringInfo() : PassME("CacheFieldLoweringInfo", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->DoCacheFieldLoweringInfo(); - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->HasFieldAccess(); - } -}; - -/** - * @class CacheMethodLoweringInfo - * @brief Cache the lowering info for methods called by INVOKEs. - */ -class CacheMethodLoweringInfo : public PassME { - public: - CacheMethodLoweringInfo() : PassME("CacheMethodLoweringInfo", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->DoCacheMethodLoweringInfo(); - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->HasInvokes(); - } -}; - -/** - * @class SpecialMethodInliner - * @brief Performs method inlining pass on special kinds of methods. - * @details Special methods are methods that fall in one of the following categories: - * empty, instance getter, instance setter, argument return, and constant return. - */ -class SpecialMethodInliner : public PassME { - public: - SpecialMethodInliner() : PassME("SpecialMethodInliner") { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->InlineSpecialMethodsGate(); - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->InlineSpecialMethodsStart(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - c_unit->mir_graph->InlineSpecialMethods(bb); - // No need of repeating, so just return false. - return false; - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->InlineSpecialMethodsEnd(); - } -}; - -/** - * @class CodeLayout - * @brief Perform the code layout pass. - */ -class CodeLayout : public PassME { - public: - CodeLayout() : PassME("CodeLayout", kAllNodes, kOptimizationBasicBlockChange, "2_post_layout_cfg") { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->VerifyDataflow(); - c_unit->mir_graph->ClearAllVisitedFlags(); - } - - bool Worker(PassDataHolder* data) const; -}; - -/** - * @class NullCheckElimination - * @brief Null check elimination pass. - */ -class NullCheckElimination : public PassME { - public: - NullCheckElimination() - : PassME("NCE", kRepeatingPreOrderDFSTraversal, "3_post_nce_cfg") { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->EliminateNullChecksGate(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - return c_unit->mir_graph->EliminateNullChecks(bb); - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->EliminateNullChecksEnd(); - } -}; - -class ClassInitCheckElimination : public PassME { - public: - ClassInitCheckElimination() - : PassME("ClInitCheckElimination", kRepeatingPreOrderDFSTraversal) { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->EliminateClassInitChecksGate(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - return c_unit->mir_graph->EliminateClassInitChecks(bb); - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->EliminateClassInitChecksEnd(); - } -}; - -/** - * @class GlobalValueNumberingPass - * @brief Performs the global value numbering pass. - */ -class GlobalValueNumberingPass : public PassME { - public: - GlobalValueNumberingPass() - : PassME("GVN", kLoopRepeatingTopologicalSortTraversal, "4_post_gvn_cfg") { - } - - bool Gate(const PassDataHolder* data) const OVERRIDE { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->ApplyGlobalValueNumberingGate(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - return c_unit->mir_graph->ApplyGlobalValueNumbering(bb); - } - - void End(PassDataHolder* data) const OVERRIDE { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->ApplyGlobalValueNumberingEnd(); - } -}; - -/** - * @class DeadCodeEliminationPass - * @brief Performs the GVN-based dead code elimination pass. - */ -class DeadCodeEliminationPass : public PassME { - public: - DeadCodeEliminationPass() : PassME("DCE", kPreOrderDFSTraversal, "4_post_dce_cfg") { - } - - bool Gate(const PassDataHolder* data) const OVERRIDE { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->EliminateDeadCodeGate(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - return c_unit->mir_graph->EliminateDeadCode(bb); - } - - void End(PassDataHolder* data) const OVERRIDE { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->EliminateDeadCodeEnd(); - } -}; - -/** - * @class GlobalValueNumberingCleanupPass - * @brief Performs the cleanup after global value numbering pass and the dependent - * dead code elimination pass that needs the GVN data. - */ -class GlobalValueNumberingCleanupPass : public PassME { - public: - GlobalValueNumberingCleanupPass() - : PassME("GVNCleanup", kNoNodes, "") { - } - - void Start(PassDataHolder* data) const OVERRIDE { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->GlobalValueNumberingCleanup(); - } -}; - -/** - * @class BBCombine - * @brief Perform the basic block combination pass. - */ -class BBCombine : public PassME { - public: - BBCombine() : PassME("BBCombine", kPreOrderDFSTraversal, "5_post_bbcombine_cfg") { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->HasTryCatchBlocks() || - ((c_unit->disable_opt & (1 << kSuppressExceptionEdges)) != 0); - } - - bool Worker(PassDataHolder* data) const; -}; - -/** - * @class ConstantPropagation - * @brief Perform a constant propagation pass. - */ -class ConstantPropagation : public PassME { - public: - ConstantPropagation() : PassME("ConstantPropagation") { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->InitializeConstantPropagation(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = down_cast(data)->bb; - DCHECK(bb != nullptr); - c_unit->mir_graph->DoConstantPropagation(bb); - // No need of repeating, so just return false. - return false; - } -}; - -/** - * @class MethodUseCount - * @brief Count the register uses of the method - */ -class MethodUseCount : public PassME { - public: - MethodUseCount() : PassME("UseCount") { - } - - bool Worker(PassDataHolder* data) const; - - bool Gate(const PassDataHolder* data) const; -}; - -/** - * @class BasicBlock Optimizations - * @brief Any simple BasicBlock optimization can be put here. - */ -class BBOptimizations : public PassME { - public: - BBOptimizations() - : PassME("BBOptimizations", kNoNodes, kOptimizationBasicBlockChange, "5_post_bbo_cfg") { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return ((c_unit->disable_opt & (1 << kBBOpt)) == 0); - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->BasicBlockOptimizationStart(); - - /* - * 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 - */ - c_unit->mir_graph->BasicBlockOptimization(); - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->BasicBlockOptimizationEnd(); - down_cast(data)->dirty = !c_unit->mir_graph->DfsOrdersUpToDate(); - } -}; - -/** - * @class SuspendCheckElimination - * @brief Any simple BasicBlock optimization can be put here. - */ -class SuspendCheckElimination : public PassME { - public: - SuspendCheckElimination() - : PassME("SuspendCheckElimination", kTopologicalSortTraversal, "6_post_sce_cfg") { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->EliminateSuspendChecksGate(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - return c_unit->mir_graph->EliminateSuspendChecks(bb); - } -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_ diff --git a/compiler/dex/compiler_ir.cc b/compiler/dex/compiler_ir.cc deleted file mode 100644 index 6e1853bbd7d333cd033f23e0722e7d4606de9d5b..0000000000000000000000000000000000000000 --- a/compiler/dex/compiler_ir.cc +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 "compiler_ir.h" - -#include "arch/instruction_set_features.h" -#include "base/dumpable.h" -#include "dex_flags.h" -#include "dex/quick/mir_to_lir.h" -#include "driver/compiler_driver.h" -#include "mir_graph.h" -#include "utils.h" - -namespace art { - -CompilationUnit::CompilationUnit(ArenaPool* pool, InstructionSet isa, CompilerDriver* driver, - ClassLinker* linker) - : compiler_driver(driver), - class_linker(linker), - dex_file(nullptr), - class_loader(nullptr), - class_def_idx(0), - method_idx(0), - access_flags(0), - invoke_type(kDirect), - shorty(nullptr), - disable_opt(0), - enable_debug(0), - verbose(false), - instruction_set(isa), - target64(Is64BitInstructionSet(isa)), - arena(pool), - arena_stack(pool), - mir_graph(nullptr), - cg(nullptr), - timings("QuickCompiler", true, false), - print_pass(false) { -} - -CompilationUnit::~CompilationUnit() { - overridden_pass_options.clear(); -} - -void CompilationUnit::StartTimingSplit(const char* label) { - if (compiler_driver->GetDumpPasses()) { - timings.StartTiming(label); - } -} - -void CompilationUnit::NewTimingSplit(const char* label) { - if (compiler_driver->GetDumpPasses()) { - timings.EndTiming(); - timings.StartTiming(label); - } -} - -void CompilationUnit::EndTiming() { - if (compiler_driver->GetDumpPasses()) { - timings.EndTiming(); - if (enable_debug & (1 << kDebugTimings)) { - LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file); - LOG(INFO) << Dumpable(timings); - } - } -} - -} // namespace art diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h deleted file mode 100644 index 5203355d0645e9f1d10baa0d0239ffe8d57b80fe..0000000000000000000000000000000000000000 --- a/compiler/dex/compiler_ir.h +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2011 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_COMPILER_IR_H_ -#define ART_COMPILER_DEX_COMPILER_IR_H_ - -#include "jni.h" -#include -#include - -#include "arch/instruction_set.h" -#include "base/arena_allocator.h" -#include "base/scoped_arena_allocator.h" -#include "base/timing_logger.h" -#include "invoke_type.h" -#include "safe_map.h" - -namespace art { - -class ClassLinker; -class CompilerDriver; -class DexFile; -class Mir2Lir; -class MIRGraph; - -constexpr size_t kOptionStringMaxLength = 2048; - -/** - * Structure abstracting pass option values, which can be of type string or integer. - */ -struct OptionContent { - OptionContent(const OptionContent& option) : - type(option.type), container(option.container, option.type) {} - - explicit OptionContent(const char* value) : - type(kString), container(value) {} - - explicit OptionContent(int value) : - type(kInteger), container(value) {} - - explicit OptionContent(int64_t value) : - type(kInteger), container(value) {} - - ~OptionContent() { - if (type == kString) { - container.StringDelete(); - } - } - - /** - * Allows for a transparent display of the option content. - */ - friend std::ostream& operator<<(std::ostream& out, const OptionContent& option) { - if (option.type == kString) { - out << option.container.s; - } else { - out << option.container.i; - } - - return out; - } - - inline const char* GetString() const { - return container.s; - } - - inline int64_t GetInteger() const { - return container.i; - } - - /** - * @brief Used to compare a string option value to a given @p value. - * @details Will return whether the internal string option is equal to - * the parameter @p value. It will return false if the type of the - * object is not a string. - * @param value The string to compare to. - * @return Returns whether the internal string option is equal to the - * parameter @p value. - */ - inline bool Equals(const char* value) const { - DCHECK(value != nullptr); - if (type != kString) { - return false; - } - return !strncmp(container.s, value, kOptionStringMaxLength); - } - - /** - * @brief Used to compare an integer option value to a given @p value. - * @details Will return whether the internal integer option is equal to - * the parameter @p value. It will return false if the type of the - * object is not an integer. - * @param value The integer to compare to. - * @return Returns whether the internal integer option is equal to the - * parameter @p value. - */ - inline bool Equals(int64_t value) const { - if (type != kInteger) { - return false; - } - return container.i == value; - } - - /** - * Describes the type of parameters allowed as option values. - */ - enum OptionType { - kString = 0, - kInteger - }; - - OptionType type; - - private: - /** - * Union containing the option value of either type. - */ - union OptionContainer { - OptionContainer(const OptionContainer& c, OptionType t) { - if (t == kString) { - DCHECK(c.s != nullptr); - s = strndup(c.s, kOptionStringMaxLength); - } else { - i = c.i; - } - } - - explicit OptionContainer(const char* value) { - DCHECK(value != nullptr); - s = strndup(value, kOptionStringMaxLength); - } - - explicit OptionContainer(int64_t value) : i(value) {} - ~OptionContainer() {} - - void StringDelete() { - if (s != nullptr) { - free(s); - } - } - - char* s; - int64_t i; - }; - - OptionContainer container; -}; - -struct CompilationUnit { - CompilationUnit(ArenaPool* pool, InstructionSet isa, CompilerDriver* driver, ClassLinker* linker); - ~CompilationUnit(); - - void StartTimingSplit(const char* label); - void NewTimingSplit(const char* label); - void EndTiming(); - - /* - * Fields needed/generated by common frontend and generally used throughout - * the compiler. - */ - CompilerDriver* const compiler_driver; - ClassLinker* const class_linker; // Linker to resolve fields and methods. - const DexFile* dex_file; // DexFile containing the method being compiled. - jobject class_loader; // compiling method's class loader. - uint16_t class_def_idx; // compiling method's defining class definition index. - uint32_t method_idx; // compiling method's index into method_ids of DexFile. - uint32_t access_flags; // compiling method's access flags. - InvokeType invoke_type; // compiling method's invocation type. - const char* shorty; // compiling method's shorty. - uint32_t disable_opt; // opt_control_vector flags. - uint32_t enable_debug; // debugControlVector flags. - bool verbose; - const InstructionSet instruction_set; - const bool target64; - - // TODO: move memory management to mir_graph, or just switch to using standard containers. - ArenaAllocator arena; - ArenaStack arena_stack; // Arenas for ScopedArenaAllocator. - - std::unique_ptr mir_graph; // MIR container. - std::unique_ptr cg; // Target-specific codegen. - TimingLogger timings; - bool print_pass; // Do we want to print a pass or not? - - /** - * @brief Holds pass options for current pass being applied to compilation unit. - * @details This is updated for every pass to contain the overridden pass options - * that were specified by user. The pass itself will check this to see if the - * default settings have been changed. The key is simply the option string without - * the pass name. - */ - SafeMap overridden_pass_options; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_COMPILER_IR_H_ diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h deleted file mode 100644 index e9402e39e525f216c8294d7da53e1cec272d917c..0000000000000000000000000000000000000000 --- a/compiler/dex/dataflow_iterator-inl.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2013 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_DATAFLOW_ITERATOR_INL_H_ -#define ART_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_ - -#include "dataflow_iterator.h" - -namespace art { - -// Single forward pass over the nodes. -inline BasicBlock* DataflowIterator::ForwardSingleNext() { - BasicBlock* res = nullptr; - - // Are we not yet at the end? - if (idx_ < end_idx_) { - // Get the next index. - BasicBlockId bb_id = (*block_id_list_)[idx_]; - res = mir_graph_->GetBasicBlock(bb_id); - idx_++; - } - - return res; -} - -// Repeat full forward passes over all nodes until no change occurs during a complete pass. -inline BasicBlock* DataflowIterator::ForwardRepeatNext() { - BasicBlock* res = nullptr; - - // Are we at the end and have we changed something? - if ((idx_ >= end_idx_) && changed_ == true) { - // Reset the index. - idx_ = start_idx_; - repeats_++; - changed_ = false; - } - - // Are we not yet at the end? - if (idx_ < end_idx_) { - // Get the BasicBlockId. - BasicBlockId bb_id = (*block_id_list_)[idx_]; - res = mir_graph_->GetBasicBlock(bb_id); - idx_++; - } - - return res; -} - -// Single reverse pass over the nodes. -inline BasicBlock* DataflowIterator::ReverseSingleNext() { - BasicBlock* res = nullptr; - - // Are we not yet at the end? - if (idx_ >= 0) { - // Get the BasicBlockId. - BasicBlockId bb_id = (*block_id_list_)[idx_]; - res = mir_graph_->GetBasicBlock(bb_id); - idx_--; - } - - return res; -} - -// Repeat full backwards passes over all nodes until no change occurs during a complete pass. -inline BasicBlock* DataflowIterator::ReverseRepeatNext() { - BasicBlock* res = nullptr; - - // Are we done and we changed something during the last iteration? - if ((idx_ < 0) && changed_) { - // Reset the index. - idx_ = start_idx_; - repeats_++; - changed_ = false; - } - - // Are we not yet done? - if (idx_ >= 0) { - // Get the BasicBlockId. - BasicBlockId bb_id = (*block_id_list_)[idx_]; - res = mir_graph_->GetBasicBlock(bb_id); - idx_--; - } - - return res; -} - -// AllNodes uses the existing block list, and should be considered unordered. -inline BasicBlock* AllNodesIterator::Next(bool had_change) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - BasicBlock* res = nullptr; - while (idx_ != end_idx_) { - BasicBlock* bb = mir_graph_->GetBlockList()[idx_++]; - DCHECK(bb != nullptr); - if (!bb->hidden) { - res = bb; - break; - } - } - - return res; -} - -inline BasicBlock* TopologicalSortIterator::Next(bool had_change) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - while (loop_head_stack_->size() != 0u && - (*loop_ends_)[loop_head_stack_->back().first] == idx_) { - loop_head_stack_->pop_back(); - } - - if (idx_ == end_idx_) { - return nullptr; - } - - // Get next block and return it. - BasicBlockId idx = idx_; - idx_ += 1; - BasicBlock* bb = mir_graph_->GetBasicBlock((*block_id_list_)[idx]); - DCHECK(bb != nullptr); - if ((*loop_ends_)[idx] != 0u) { - loop_head_stack_->push_back(std::make_pair(idx, false)); // Not recalculating. - } - return bb; -} - -inline BasicBlock* LoopRepeatingTopologicalSortIterator::Next(bool had_change) { - if (idx_ != 0) { - // Mark last processed block visited. - BasicBlock* bb = mir_graph_->GetBasicBlock((*block_id_list_)[idx_ - 1]); - bb->visited = true; - if (had_change) { - // If we had a change we need to revisit the children. - ChildBlockIterator iter(bb, mir_graph_); - for (BasicBlock* child_bb = iter.Next(); child_bb != nullptr; child_bb = iter.Next()) { - child_bb->visited = false; - } - } - } - - while (true) { - // Pop loops we have left and check if we need to recalculate one of them. - // NOTE: We need to do this even if idx_ == end_idx_. - while (loop_head_stack_->size() != 0u && - (*loop_ends_)[loop_head_stack_->back().first] == idx_) { - auto top = loop_head_stack_->back(); - uint16_t loop_head_idx = top.first; - bool recalculated = top.second; - loop_head_stack_->pop_back(); - BasicBlock* loop_head = mir_graph_->GetBasicBlock((*block_id_list_)[loop_head_idx]); - DCHECK(loop_head != nullptr); - if (!recalculated || !loop_head->visited) { - // Recalculating this loop. - loop_head_stack_->push_back(std::make_pair(loop_head_idx, true)); - idx_ = loop_head_idx + 1; - return loop_head; - } - } - - if (idx_ == end_idx_) { - return nullptr; - } - - // Get next block and return it if unvisited. - BasicBlockId idx = idx_; - idx_ += 1; - BasicBlock* bb = mir_graph_->GetBasicBlock((*block_id_list_)[idx]); - DCHECK(bb != nullptr); - if ((*loop_ends_)[idx] != 0u) { - // If bb->visited is false, the loop needs to be processed from scratch. - // Otherwise we mark it as recalculating; for a natural loop we will not - // need to recalculate any block in the loop anyway, and for unnatural - // loops we will recalculate the loop head only if one of its predecessors - // actually changes. - bool recalculating = bb->visited; - loop_head_stack_->push_back(std::make_pair(idx, recalculating)); - } - if (!bb->visited) { - return bb; - } - } -} - -} // namespace art - -#endif // ART_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_ diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h deleted file mode 100644 index 097c2a40b437aa57fdf71062f30580662138cf05..0000000000000000000000000000000000000000 --- a/compiler/dex/dataflow_iterator.h +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright (C) 2013 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_DATAFLOW_ITERATOR_H_ -#define ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_ - -#include "base/logging.h" -#include "mir_graph.h" - -namespace art { - - /* - * This class supports iterating over lists of basic blocks in various - * interesting orders. Note that for efficiency, the visit orders have been pre-computed. - * The order itself will not change during the iteration. However, for some uses, - * auxiliary data associated with the basic blocks may be changed during the iteration, - * necessitating another pass over the list. If this behavior is required, use the - * "Repeating" variant. For the repeating variant, the caller must tell the iterator - * whether a change has been made that necessitates another pass. Note that calling Next(true) - * does not affect the iteration order or short-circuit the current pass - it simply tells - * the iterator that once it has finished walking through the block list it should reset and - * do another full pass through the list. - */ - /** - * @class DataflowIterator - * @brief The main iterator class, all other iterators derive of this one to define an iteration order. - */ - class DataflowIterator { - public: - virtual ~DataflowIterator() {} - - /** - * @brief How many times have we repeated the iterator across the BasicBlocks? - * @return the number of iteration repetitions. - */ - int32_t GetRepeatCount() { return repeats_; } - - /** - * @brief Has the user of the iterator reported a change yet? - * @details Does not mean there was or not a change, it is only whether the user passed a true to the Next function call. - * @return whether the user of the iterator reported a change yet. - */ - int32_t GetChanged() { return changed_; } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) = 0; - - protected: - /** - * @param mir_graph the MIRGraph we are interested in. - * @param start_idx the first index we want to iterate across. - * @param end_idx the last index we want to iterate (not included). - */ - DataflowIterator(MIRGraph* mir_graph, int32_t start_idx, int32_t end_idx) - : mir_graph_(mir_graph), - start_idx_(start_idx), - end_idx_(end_idx), - block_id_list_(nullptr), - idx_(0), - repeats_(0), - changed_(false) {} - - /** - * @brief Get the next BasicBlock iterating forward. - * @return the next BasicBlock iterating forward. - */ - virtual BasicBlock* ForwardSingleNext() ALWAYS_INLINE; - - /** - * @brief Get the next BasicBlock iterating backward. - * @return the next BasicBlock iterating backward. - */ - virtual BasicBlock* ReverseSingleNext() ALWAYS_INLINE; - - /** - * @brief Get the next BasicBlock iterating forward, restart if a BasicBlock was reported changed during the last iteration. - * @return the next BasicBlock iterating forward, with chance of repeating the iteration. - */ - virtual BasicBlock* ForwardRepeatNext() ALWAYS_INLINE; - - /** - * @brief Get the next BasicBlock iterating backward, restart if a BasicBlock was reported changed during the last iteration. - * @return the next BasicBlock iterating backward, with chance of repeating the iteration. - */ - virtual BasicBlock* ReverseRepeatNext() ALWAYS_INLINE; - - MIRGraph* const mir_graph_; /**< @brief the MIRGraph */ - const int32_t start_idx_; /**< @brief the start index for the iteration */ - const int32_t end_idx_; /**< @brief the last index for the iteration */ - const ArenaVector* block_id_list_; /**< @brief the list of BasicBlocks we want to iterate on */ - int32_t idx_; /**< @brief Current index for the iterator */ - int32_t repeats_; /**< @brief Number of repeats over the iteration */ - bool changed_; /**< @brief Has something changed during the current iteration? */ - }; // DataflowIterator - - /** - * @class PreOrderDfsIterator - * @brief Used to perform a Pre-order Depth-First-Search Iteration of a MIRGraph. - */ - class PreOrderDfsIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit PreOrderDfsIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { - // Extra setup for the PreOrderDfsIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetDfsOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ForwardSingleNext(); - } - }; - - /** - * @class RepeatingPreOrderDfsIterator - * @brief Used to perform a Repeating Pre-order Depth-First-Search Iteration of a MIRGraph. - * @details If there is a change during an iteration, the iteration starts over at the end of the iteration. - */ - class RepeatingPreOrderDfsIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit RepeatingPreOrderDfsIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { - // Extra setup for the RepeatingPreOrderDfsIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetDfsOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ForwardRepeatNext(); - } - }; - - /** - * @class RepeatingPostOrderDfsIterator - * @brief Used to perform a Repeating Post-order Depth-First-Search Iteration of a MIRGraph. - * @details If there is a change during an iteration, the iteration starts over at the end of the iteration. - */ - class RepeatingPostOrderDfsIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit RepeatingPostOrderDfsIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { - // Extra setup for the RepeatingPostOrderDfsIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetDfsPostOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ForwardRepeatNext(); - } - }; - - /** - * @class ReversePostOrderDfsIterator - * @brief Used to perform a Reverse Post-order Depth-First-Search Iteration of a MIRGraph. - */ - class ReversePostOrderDfsIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit ReversePostOrderDfsIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) { - // Extra setup for the ReversePostOrderDfsIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetDfsPostOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ReverseSingleNext(); - } - }; - - /** - * @class ReversePostOrderDfsIterator - * @brief Used to perform a Repeating Reverse Post-order Depth-First-Search Iteration of a MIRGraph. - * @details If there is a change during an iteration, the iteration starts over at the end of the iteration. - */ - class RepeatingReversePostOrderDfsIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit RepeatingReversePostOrderDfsIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) { - // Extra setup for the RepeatingReversePostOrderDfsIterator - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetDfsPostOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ReverseRepeatNext(); - } - }; - - /** - * @class PostOrderDOMIterator - * @brief Used to perform a Post-order Domination Iteration of a MIRGraph. - */ - class PostOrderDOMIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit PostOrderDOMIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { - // Extra setup for thePostOrderDOMIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetDomPostOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ForwardSingleNext(); - } - }; - - /** - * @class AllNodesIterator - * @brief Used to perform an iteration on all the BasicBlocks a MIRGraph. - */ - class AllNodesIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit AllNodesIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetBlockList().size()) { - } - - /** - * @brief Resetting the iterator. - */ - void Reset() { - idx_ = 0; - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) ALWAYS_INLINE; - }; - - /** - * @class TopologicalSortIterator - * @brief Used to perform a Topological Sort Iteration of a MIRGraph. - */ - class TopologicalSortIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit TopologicalSortIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()), - loop_ends_(&mir_graph->GetTopologicalSortOrderLoopEnds()), - loop_head_stack_(mir_graph_->GetTopologicalSortOrderLoopHeadStack()) { - // Extra setup for TopologicalSortIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetTopologicalSortOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) OVERRIDE; - - private: - const ArenaVector* const loop_ends_; - ArenaVector>* const loop_head_stack_; - }; - - /** - * @class LoopRepeatingTopologicalSortIterator - * @brief Used to perform a Topological Sort Iteration of a MIRGraph, repeating loops as needed. - * @details The iterator uses the visited flags to keep track of the blocks that need - * recalculation and keeps a stack of loop heads in the MIRGraph. At the end of the loop - * it returns back to the loop head if it needs to be recalculated. Due to the use of - * the visited flags and the loop head stack in the MIRGraph, it's not possible to use - * two iterators at the same time or modify this data during iteration (though inspection - * of this data is allowed and sometimes even expected). - * - * NOTE: This iterator is not suitable for passes that need to propagate changes to - * predecessors, such as type inferrence. - */ - class LoopRepeatingTopologicalSortIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit LoopRepeatingTopologicalSortIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()), - loop_ends_(&mir_graph->GetTopologicalSortOrderLoopEnds()), - loop_head_stack_(mir_graph_->GetTopologicalSortOrderLoopHeadStack()) { - // Extra setup for RepeatingTopologicalSortIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetTopologicalSortOrder(); - // Clear visited flags and check that the loop head stack is empty. - mir_graph->ClearAllVisitedFlags(); - DCHECK_EQ(loop_head_stack_->size(), 0u); - } - - ~LoopRepeatingTopologicalSortIterator() { - DCHECK_EQ(loop_head_stack_->size(), 0u); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) OVERRIDE; - - private: - const ArenaVector* const loop_ends_; - ArenaVector>* const loop_head_stack_; - }; - -} // namespace art - -#endif // ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_ diff --git a/compiler/dex/dex_flags.h b/compiler/dex/dex_flags.h deleted file mode 100644 index e8eb40ccd284600599bc4136ef95e0d62b12414b..0000000000000000000000000000000000000000 --- a/compiler/dex/dex_flags.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_DEX_FLAGS_H_ -#define ART_COMPILER_DEX_DEX_FLAGS_H_ - -namespace art { - -// Suppress optimization if corresponding bit set. -enum OptControlVector { - kLoadStoreElimination = 0, - kLoadHoisting, - kSuppressLoads, - kNullCheckElimination, - kClassInitCheckElimination, - kGlobalValueNumbering, - kGvnDeadCodeElimination, - kLocalValueNumbering, - kPromoteRegs, - kTrackLiveTemps, - kSafeOptimizations, - kBBOpt, - kSuspendCheckElimination, - kMatch, - kPromoteCompilerTemps, - kBranchFusing, - kSuppressExceptionEdges, - kSuppressMethodInlining, -}; - -// Force code generation paths for testing. -enum DebugControlVector { - kDebugVerbose, - kDebugDumpCFG, - kDebugSlowFieldPath, - kDebugSlowInvokePath, - kDebugSlowStringPath, - kDebugSlowTypePath, - kDebugSlowestFieldPath, - kDebugSlowestStringPath, - kDebugExerciseResolveMethod, - kDebugVerifyDataflow, - kDebugShowMemoryUsage, - kDebugShowNops, - kDebugCountOpcodes, - kDebugDumpCheckStats, - kDebugShowSummaryMemoryUsage, - kDebugShowFilterStats, - kDebugTimings, - kDebugCodegenDump -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_DEX_FLAGS_H_ diff --git a/compiler/dex/dex_types.h b/compiler/dex/dex_types.h deleted file mode 100644 index f485c1c345de8f12c553bbb7a56bb68e7b3ed608..0000000000000000000000000000000000000000 --- a/compiler/dex/dex_types.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2011 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_DEX_TYPES_H_ -#define ART_COMPILER_DEX_DEX_TYPES_H_ - -namespace art { - -typedef uint32_t DexOffset; // Dex offset in code units. -typedef uint16_t NarrowDexOffset; // For use in structs, Dex offsets range from 0 .. 0xffff. - -} // namespace art - -#endif // ART_COMPILER_DEX_DEX_TYPES_H_ diff --git a/compiler/dex/global_value_numbering.cc b/compiler/dex/global_value_numbering.cc deleted file mode 100644 index 94ba4fad2ad8973a8edb39aef75a7f6663d3fc3d..0000000000000000000000000000000000000000 --- a/compiler/dex/global_value_numbering.cc +++ /dev/null @@ -1,237 +0,0 @@ -/* - * 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 "global_value_numbering.h" - -#include "base/bit_vector-inl.h" -#include "base/stl_util.h" -#include "local_value_numbering.h" - -namespace art { - -GlobalValueNumbering::GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator, - Mode mode) - : cu_(cu), - mir_graph_(cu->mir_graph.get()), - allocator_(allocator), - bbs_processed_(0u), - max_bbs_to_process_(kMaxBbsToProcessMultiplyFactor * mir_graph_->GetNumReachableBlocks()), - last_value_(kNullValue), - modifications_allowed_(true), - mode_(mode), - global_value_map_(std::less(), allocator->Adapter()), - array_location_map_(ArrayLocationComparator(), allocator->Adapter()), - array_location_reverse_map_(allocator->Adapter()), - ref_set_map_(std::less(), allocator->Adapter()), - lvns_(mir_graph_->GetNumBlocks(), nullptr, allocator->Adapter()), - work_lvn_(nullptr), - merge_lvns_(allocator->Adapter()) { -} - -GlobalValueNumbering::~GlobalValueNumbering() { - STLDeleteElements(&lvns_); -} - -LocalValueNumbering* GlobalValueNumbering::PrepareBasicBlock(BasicBlock* bb, - ScopedArenaAllocator* allocator) { - if (UNLIKELY(!Good())) { - return nullptr; - } - if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) { - DCHECK(bb->first_mir_insn == nullptr); - return nullptr; - } - if (mode_ == kModeGvn && UNLIKELY(bbs_processed_ == max_bbs_to_process_)) { - // If we're still trying to converge, stop now. Otherwise, proceed to apply optimizations. - last_value_ = kNoValue; // Make bad. - return nullptr; - } - if (mode_ == kModeGvnPostProcessing && - mir_graph_->GetTopologicalSortOrderLoopHeadStack()->empty()) { - // Modifications outside loops are performed during the main phase. - return nullptr; - } - if (allocator == nullptr) { - allocator = allocator_; - } - DCHECK(work_lvn_.get() == nullptr); - work_lvn_.reset(new (allocator) LocalValueNumbering(this, bb->id, allocator)); - if (bb->block_type == kEntryBlock) { - work_lvn_->PrepareEntryBlock(); - DCHECK(bb->first_mir_insn == nullptr); // modifications_allowed_ is irrelevant. - } else { - // To avoid repeated allocation on the ArenaStack, reuse a single vector kept as a member. - DCHECK(merge_lvns_.empty()); - // If we're running the full GVN, the RepeatingTopologicalSortIterator keeps the loop - // head stack in the MIRGraph up to date and for a loop head we need to check whether - // we're making the initial computation and need to merge only preceding blocks in the - // topological order, or we're recalculating a loop head and need to merge all incoming - // LVNs. When we're not at a loop head (including having an empty loop head stack) all - // predecessors should be preceding blocks and we shall merge all of them anyway. - bool use_all_predecessors = true; - uint16_t loop_head_idx = 0u; // Used only if !use_all_predecessors. - if (mode_ == kModeGvn && mir_graph_->GetTopologicalSortOrderLoopHeadStack()->size() != 0) { - // Full GVN inside a loop, see if we're at the loop head for the first time. - modifications_allowed_ = false; - auto top = mir_graph_->GetTopologicalSortOrderLoopHeadStack()->back(); - loop_head_idx = top.first; - bool recalculating = top.second; - use_all_predecessors = recalculating || - loop_head_idx != mir_graph_->GetTopologicalSortOrderIndexes()[bb->id]; - } else { - modifications_allowed_ = true; - } - for (BasicBlockId pred_id : bb->predecessors) { - DCHECK_NE(pred_id, NullBasicBlockId); - if (lvns_[pred_id] != nullptr && - (use_all_predecessors || - mir_graph_->GetTopologicalSortOrderIndexes()[pred_id] < loop_head_idx)) { - merge_lvns_.push_back(lvns_[pred_id]); - } - } - // Determine merge type. - LocalValueNumbering::MergeType merge_type = LocalValueNumbering::kNormalMerge; - if (bb->catch_entry) { - merge_type = LocalValueNumbering::kCatchMerge; - } else if (bb->last_mir_insn != nullptr && - IsInstructionReturn(bb->last_mir_insn->dalvikInsn.opcode) && - bb->GetFirstNonPhiInsn() == bb->last_mir_insn) { - merge_type = LocalValueNumbering::kReturnMerge; - } - // At least one predecessor must have been processed before this bb. - CHECK(!merge_lvns_.empty()); - if (merge_lvns_.size() == 1u) { - work_lvn_->MergeOne(*merge_lvns_[0], merge_type); - } else { - work_lvn_->Merge(merge_type); - } - } - return work_lvn_.get(); -} - -bool GlobalValueNumbering::FinishBasicBlock(BasicBlock* bb) { - DCHECK(work_lvn_ != nullptr); - DCHECK_EQ(bb->id, work_lvn_->Id()); - ++bbs_processed_; - merge_lvns_.clear(); - - bool change = false; - if (mode_ == kModeGvn) { - change = (lvns_[bb->id] == nullptr) || !lvns_[bb->id]->Equals(*work_lvn_); - // In GVN mode, keep the latest LVN even if Equals() indicates no change. This is - // to keep the correct values of fields that do not contribute to Equals() as long - // as they depend only on predecessor LVNs' fields that do contribute to Equals(). - // Currently, that's LVN::merge_map_ used by LVN::GetStartingVregValueNumberImpl(). - std::unique_ptr old_lvn(lvns_[bb->id]); - lvns_[bb->id] = work_lvn_.release(); - } else { - DCHECK_EQ(mode_, kModeGvnPostProcessing); // kModeLvn doesn't use FinishBasicBlock(). - DCHECK(lvns_[bb->id] != nullptr); - DCHECK(lvns_[bb->id]->Equals(*work_lvn_)); - work_lvn_.reset(); - } - return change; -} - -uint16_t GlobalValueNumbering::GetArrayLocation(uint16_t base, uint16_t index) { - auto cmp = array_location_map_.key_comp(); - ArrayLocation key = { base, index }; - auto lb = array_location_map_.lower_bound(key); - if (lb != array_location_map_.end() && !cmp(key, lb->first)) { - return lb->second; - } - uint16_t location = static_cast(array_location_reverse_map_.size()); - DCHECK_EQ(location, array_location_reverse_map_.size()); // No overflow. - auto it = array_location_map_.PutBefore(lb, key, location); - array_location_reverse_map_.push_back(&*it); - return location; -} - -bool GlobalValueNumbering::NullCheckedInAllPredecessors( - const ScopedArenaVector& merge_names) const { - // Implicit parameters: - // - *work_lvn_: the LVN for which we're checking predecessors. - // - merge_lvns_: the predecessor LVNs. - DCHECK_EQ(merge_lvns_.size(), merge_names.size()); - for (size_t i = 0, size = merge_lvns_.size(); i != size; ++i) { - const LocalValueNumbering* pred_lvn = merge_lvns_[i]; - uint16_t value_name = merge_names[i]; - if (!pred_lvn->IsValueNullChecked(value_name)) { - // Check if the predecessor has an IF_EQZ/IF_NEZ as the last insn. - const BasicBlock* pred_bb = mir_graph_->GetBasicBlock(pred_lvn->Id()); - if (!HasNullCheckLastInsn(pred_bb, work_lvn_->Id())) { - return false; - } - // IF_EQZ/IF_NEZ checks some sreg, see if that sreg contains the value_name. - int s_reg = pred_bb->last_mir_insn->ssa_rep->uses[0]; - if (pred_lvn->GetSregValue(s_reg) != value_name) { - return false; - } - } - } - return true; -} - -bool GlobalValueNumbering::DivZeroCheckedInAllPredecessors( - const ScopedArenaVector& merge_names) const { - // Implicit parameters: - // - *work_lvn_: the LVN for which we're checking predecessors. - // - merge_lvns_: the predecessor LVNs. - DCHECK_EQ(merge_lvns_.size(), merge_names.size()); - for (size_t i = 0, size = merge_lvns_.size(); i != size; ++i) { - const LocalValueNumbering* pred_lvn = merge_lvns_[i]; - uint16_t value_name = merge_names[i]; - if (!pred_lvn->IsValueDivZeroChecked(value_name)) { - return false; - } - } - return true; -} - -bool GlobalValueNumbering::IsBlockEnteredOnTrue(uint16_t cond, BasicBlockId bb_id) { - DCHECK_NE(cond, kNoValue); - BasicBlock* bb = mir_graph_->GetBasicBlock(bb_id); - if (bb->predecessors.size() == 1u) { - BasicBlockId pred_id = bb->predecessors[0]; - BasicBlock* pred_bb = mir_graph_->GetBasicBlock(pred_id); - if (pred_bb->BranchesToSuccessorOnlyIfNotZero(bb_id)) { - DCHECK(lvns_[pred_id] != nullptr); - uint16_t operand = lvns_[pred_id]->GetSregValue(pred_bb->last_mir_insn->ssa_rep->uses[0]); - if (operand == cond) { - return true; - } - } - } - return false; -} - -bool GlobalValueNumbering::IsTrueInBlock(uint16_t cond, BasicBlockId bb_id) { - // We're not doing proper value propagation, so just see if the condition is used - // with if-nez/if-eqz to branch/fall-through to this bb or one of its dominators. - DCHECK_NE(cond, kNoValue); - if (IsBlockEnteredOnTrue(cond, bb_id)) { - return true; - } - BasicBlock* bb = mir_graph_->GetBasicBlock(bb_id); - for (uint32_t dom_id : bb->dominators->Indexes()) { - if (IsBlockEnteredOnTrue(cond, dom_id)) { - return true; - } - } - return false; -} - -} // namespace art diff --git a/compiler/dex/global_value_numbering.h b/compiler/dex/global_value_numbering.h deleted file mode 100644 index c514f75dcc744aba61f7e388c2b558d6ff51f741..0000000000000000000000000000000000000000 --- a/compiler/dex/global_value_numbering.h +++ /dev/null @@ -1,301 +0,0 @@ -/* - * 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_GLOBAL_VALUE_NUMBERING_H_ -#define ART_COMPILER_DEX_GLOBAL_VALUE_NUMBERING_H_ - -#include "base/arena_object.h" -#include "base/logging.h" -#include "base/macros.h" -#include "mir_graph.h" -#include "compiler_ir.h" -#include "dex_flags.h" - -namespace art { - -class LocalValueNumbering; -class MirFieldInfo; - -class GlobalValueNumbering : public DeletableArenaObject { - public: - static constexpr uint16_t kNoValue = 0xffffu; - static constexpr uint16_t kNullValue = 1u; - - enum Mode { - kModeGvn, - kModeGvnPostProcessing, - kModeLvn - }; - - static bool Skip(CompilationUnit* cu) { - return (cu->disable_opt & (1u << kGlobalValueNumbering)) != 0u || - cu->mir_graph->GetMaxNestedLoops() > kMaxAllowedNestedLoops; - } - - // Instance and static field id map is held by MIRGraph to avoid multiple recalculations - // when doing LVN. - template // Container of MirIFieldLoweringInfo or MirSFieldLoweringInfo. - static uint16_t* PrepareGvnFieldIds(ScopedArenaAllocator* allocator, - const Container& field_infos); - - GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator, Mode mode); - ~GlobalValueNumbering(); - - CompilationUnit* GetCompilationUnit() const { - return cu_; - } - - MIRGraph* GetMirGraph() const { - return mir_graph_; - } - - // Prepare LVN for the basic block. - LocalValueNumbering* PrepareBasicBlock(BasicBlock* bb, - ScopedArenaAllocator* allocator = nullptr); - - // Finish processing the basic block. - bool FinishBasicBlock(BasicBlock* bb); - - // Checks that the value names didn't overflow. - bool Good() const { - return last_value_ < kNoValue; - } - - // Allow modifications. - void StartPostProcessing(); - - bool CanModify() const { - return modifications_allowed_ && Good(); - } - - // Retrieve the LVN with GVN results for a given BasicBlock. - const LocalValueNumbering* GetLvn(BasicBlockId bb_id) const; - - private: - // Allocate a new value name. - uint16_t NewValueName(); - - // Key is concatenation of opcode, operand1, operand2 and modifier, value is value name. - typedef ScopedArenaSafeMap ValueMap; - - static uint64_t BuildKey(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) { - return (static_cast(op) << 48 | static_cast(operand1) << 32 | - static_cast(operand2) << 16 | static_cast(modifier)); - } - - // Look up a value in the global value map, adding a new entry if there was none before. - uint16_t LookupValue(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) { - uint16_t res; - uint64_t key = BuildKey(op, operand1, operand2, modifier); - auto lb = global_value_map_.lower_bound(key); - if (lb != global_value_map_.end() && lb->first == key) { - res = lb->second; - } else { - res = NewValueName(); - global_value_map_.PutBefore(lb, key, res); - } - return res; - } - - // Look up a value in the global value map, don't add a new entry if there was none before. - uint16_t FindValue(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) const { - uint16_t res; - uint64_t key = BuildKey(op, operand1, operand2, modifier); - auto lb = global_value_map_.lower_bound(key); - if (lb != global_value_map_.end() && lb->first == key) { - res = lb->second; - } else { - res = kNoValue; - } - return res; - } - - // Get an instance field id. - uint16_t GetIFieldId(MIR* mir) { - return GetMirGraph()->GetGvnIFieldId(mir); - } - - // Get a static field id. - uint16_t GetSFieldId(MIR* mir) { - return GetMirGraph()->GetGvnSFieldId(mir); - } - - // Get an instance field type based on field id. - uint16_t GetIFieldType(uint16_t field_id) { - return static_cast(GetMirGraph()->GetIFieldLoweringInfo(field_id).MemAccessType()); - } - - // Get a static field type based on field id. - uint16_t GetSFieldType(uint16_t field_id) { - return static_cast(GetMirGraph()->GetSFieldLoweringInfo(field_id).MemAccessType()); - } - - struct ArrayLocation { - uint16_t base; - uint16_t index; - }; - - struct ArrayLocationComparator { - bool operator()(const ArrayLocation& lhs, const ArrayLocation& rhs) const { - if (lhs.base != rhs.base) { - return lhs.base < rhs.base; - } - return lhs.index < rhs.index; - } - }; - - typedef ScopedArenaSafeMap ArrayLocationMap; - - // Get an array location. - uint16_t GetArrayLocation(uint16_t base, uint16_t index); - - // Get the array base from an array location. - uint16_t GetArrayLocationBase(uint16_t location) const { - return array_location_reverse_map_[location]->first.base; - } - - // Get the array index from an array location. - uint16_t GetArrayLocationIndex(uint16_t location) const { - return array_location_reverse_map_[location]->first.index; - } - - // A set of value names. - typedef ScopedArenaSet ValueNameSet; - - // A map from a set of references to the set id. - typedef ScopedArenaSafeMap RefSetIdMap; - - uint16_t GetRefSetId(const ValueNameSet& ref_set) { - uint16_t res = kNoValue; - auto lb = ref_set_map_.lower_bound(ref_set); - if (lb != ref_set_map_.end() && !ref_set_map_.key_comp()(ref_set, lb->first)) { - res = lb->second; - } else { - res = NewValueName(); - ref_set_map_.PutBefore(lb, ref_set, res); - } - return res; - } - - const BasicBlock* GetBasicBlock(uint16_t bb_id) const { - return mir_graph_->GetBasicBlock(bb_id); - } - - static bool HasNullCheckLastInsn(const BasicBlock* pred_bb, BasicBlockId succ_id) { - return pred_bb->BranchesToSuccessorOnlyIfNotZero(succ_id); - } - - bool NullCheckedInAllPredecessors(const ScopedArenaVector& merge_names) const; - - bool DivZeroCheckedInAllPredecessors(const ScopedArenaVector& merge_names) const; - - bool IsBlockEnteredOnTrue(uint16_t cond, BasicBlockId bb_id); - bool IsTrueInBlock(uint16_t cond, BasicBlockId bb_id); - - ScopedArenaAllocator* Allocator() const { - return allocator_; - } - - CompilationUnit* const cu_; - MIRGraph* const mir_graph_; - ScopedArenaAllocator* const allocator_; - - // The maximum number of nested loops that we accept for GVN. - static constexpr size_t kMaxAllowedNestedLoops = 6u; - - // The number of BBs that we need to process grows exponentially with the number - // of nested loops. Don't allow excessive processing for too many nested loops or - // otherwise expensive methods. - static constexpr uint32_t kMaxBbsToProcessMultiplyFactor = 20u; - - uint32_t bbs_processed_; - uint32_t max_bbs_to_process_; // Doesn't apply after the main GVN has converged. - - // We have 32-bit last_value_ so that we can detect when we run out of value names, see Good(). - // We usually don't check Good() until the end of LVN unless we're about to modify code. - uint32_t last_value_; - - // Marks whether code modifications are allowed. The initial GVN is done without code - // modifications to settle the value names. Afterwards, we allow modifications and rerun - // LVN once for each BasicBlock. - bool modifications_allowed_; - - // Specifies the mode of operation. - Mode mode_; - - ValueMap global_value_map_; - ArrayLocationMap array_location_map_; - ScopedArenaVector array_location_reverse_map_; - RefSetIdMap ref_set_map_; - - ScopedArenaVector lvns_; // Owning. - std::unique_ptr work_lvn_; - ScopedArenaVector merge_lvns_; // Not owning. - - friend class LocalValueNumbering; - friend class GlobalValueNumberingTest; - - DISALLOW_COPY_AND_ASSIGN(GlobalValueNumbering); -}; -std::ostream& operator<<(std::ostream& os, const GlobalValueNumbering::Mode& rhs); - -inline const LocalValueNumbering* GlobalValueNumbering::GetLvn(BasicBlockId bb_id) const { - DCHECK_EQ(mode_, kModeGvnPostProcessing); - DCHECK_LT(bb_id, lvns_.size()); - DCHECK(lvns_[bb_id] != nullptr); - return lvns_[bb_id]; -} - -inline void GlobalValueNumbering::StartPostProcessing() { - DCHECK(Good()); - DCHECK_EQ(mode_, kModeGvn); - mode_ = kModeGvnPostProcessing; -} - -inline uint16_t GlobalValueNumbering::NewValueName() { - DCHECK_NE(mode_, kModeGvnPostProcessing); - ++last_value_; - return last_value_; -} - -template // Container of MirIFieldLoweringInfo or MirSFieldLoweringInfo. -uint16_t* GlobalValueNumbering::PrepareGvnFieldIds(ScopedArenaAllocator* allocator, - const Container& field_infos) { - size_t size = field_infos.size(); - uint16_t* field_ids = allocator->AllocArray(size, kArenaAllocMisc); - for (size_t i = 0u; i != size; ++i) { - size_t idx = i; - const MirFieldInfo& cur_info = field_infos[i]; - if (cur_info.IsResolved()) { - for (size_t j = 0; j != i; ++j) { - const MirFieldInfo& prev_info = field_infos[j]; - if (prev_info.IsResolved() && - prev_info.DeclaringDexFile() == cur_info.DeclaringDexFile() && - prev_info.DeclaringFieldIndex() == cur_info.DeclaringFieldIndex()) { - DCHECK_EQ(cur_info.MemAccessType(), prev_info.MemAccessType()); - idx = j; - break; - } - } - } - field_ids[i] = idx; - } - return field_ids; -} - -} // namespace art - -#endif // ART_COMPILER_DEX_GLOBAL_VALUE_NUMBERING_H_ diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc deleted file mode 100644 index 7d647e5c3b46abe13384e4720cd2f7c848b0b10c..0000000000000000000000000000000000000000 --- a/compiler/dex/global_value_numbering_test.cc +++ /dev/null @@ -1,2428 +0,0 @@ -/* - * 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 "base/logging.h" -#include "dataflow_iterator-inl.h" -#include "dex/mir_field_info.h" -#include "global_value_numbering.h" -#include "local_value_numbering.h" -#include "gtest/gtest.h" - -namespace art { - -class GlobalValueNumberingTest : public testing::Test { - protected: - static constexpr uint16_t kNoValue = GlobalValueNumbering::kNoValue; - - struct IFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_field_idx; - bool is_volatile; - DexMemAccessType type; - }; - - struct SFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_field_idx; - bool is_volatile; - DexMemAccessType type; - }; - - struct BBDef { - static constexpr size_t kMaxSuccessors = 4; - static constexpr size_t kMaxPredecessors = 4; - - BBType type; - size_t num_successors; - BasicBlockId successors[kMaxPredecessors]; - size_t num_predecessors; - BasicBlockId predecessors[kMaxPredecessors]; - }; - - struct MIRDef { - static constexpr size_t kMaxSsaDefs = 2; - static constexpr size_t kMaxSsaUses = 4; - - BasicBlockId bbid; - Instruction::Code opcode; - int64_t value; - uint32_t field_info; - size_t num_uses; - int32_t uses[kMaxSsaUses]; - size_t num_defs; - int32_t defs[kMaxSsaDefs]; - }; - -#define DEF_SUCC0() \ - 0u, { } -#define DEF_SUCC1(s1) \ - 1u, { s1 } -#define DEF_SUCC2(s1, s2) \ - 2u, { s1, s2 } -#define DEF_SUCC3(s1, s2, s3) \ - 3u, { s1, s2, s3 } -#define DEF_SUCC4(s1, s2, s3, s4) \ - 4u, { s1, s2, s3, s4 } -#define DEF_PRED0() \ - 0u, { } -#define DEF_PRED1(p1) \ - 1u, { p1 } -#define DEF_PRED2(p1, p2) \ - 2u, { p1, p2 } -#define DEF_PRED3(p1, p2, p3) \ - 3u, { p1, p2, p3 } -#define DEF_PRED4(p1, p2, p3, p4) \ - 4u, { p1, p2, p3, p4 } -#define DEF_BB(type, succ, pred) \ - { type, succ, pred } - -#define DEF_CONST(bb, opcode, reg, value) \ - { bb, opcode, value, 0u, 0, { }, 1, { reg } } -#define DEF_CONST_WIDE(bb, opcode, reg, value) \ - { bb, opcode, value, 0u, 0, { }, 2, { reg, reg + 1 } } -#define DEF_CONST_STRING(bb, opcode, reg, index) \ - { bb, opcode, index, 0u, 0, { }, 1, { reg } } -#define DEF_IGET(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 1, { obj }, 1, { reg } } -#define DEF_IGET_WIDE(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 1, { obj }, 2, { reg, reg + 1 } } -#define DEF_IPUT(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 2, { reg, obj }, 0, { } } -#define DEF_IPUT_WIDE(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 3, { reg, reg + 1, obj }, 0, { } } -#define DEF_SGET(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 0, { }, 1, { reg } } -#define DEF_SGET_WIDE(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 0, { }, 2, { reg, reg + 1 } } -#define DEF_SPUT(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 1, { reg }, 0, { } } -#define DEF_SPUT_WIDE(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 2, { reg, reg + 1 }, 0, { } } -#define DEF_AGET(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 2, { obj, idx }, 1, { reg } } -#define DEF_AGET_WIDE(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 2, { obj, idx }, 2, { reg, reg + 1 } } -#define DEF_APUT(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 3, { reg, obj, idx }, 0, { } } -#define DEF_APUT_WIDE(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 4, { reg, reg + 1, obj, idx }, 0, { } } -#define DEF_INVOKE1(bb, opcode, reg) \ - { bb, opcode, 0u, 0u, 1, { reg }, 0, { } } -#define DEF_UNIQUE_REF(bb, opcode, reg) \ - { bb, opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ... -#define DEF_IFZ(bb, opcode, reg) \ - { bb, opcode, 0u, 0u, 1, { reg }, 0, { } } -#define DEF_MOVE(bb, opcode, reg, src) \ - { bb, opcode, 0u, 0u, 1, { src }, 1, { reg } } -#define DEF_MOVE_WIDE(bb, opcode, reg, src) \ - { bb, opcode, 0u, 0u, 2, { src, src + 1 }, 2, { reg, reg + 1 } } -#define DEF_PHI2(bb, reg, src1, src2) \ - { bb, static_cast(kMirOpPhi), 0, 0u, 2u, { src1, src2 }, 1, { reg } } -#define DEF_BINOP(bb, opcode, result, src1, src2) \ - { bb, opcode, 0u, 0u, 2, { src1, src2 }, 1, { result } } -#define DEF_UNOP(bb, opcode, result, src) DEF_MOVE(bb, opcode, result, src) - - void DoPrepareIFields(const IFieldDef* defs, size_t count) { - cu_.mir_graph->ifield_lowering_infos_.clear(); - cu_.mir_graph->ifield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const IFieldDef* def = &defs[i]; - MirIFieldLoweringInfo field_info(def->field_idx, def->type, false); - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ &= ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile); - } - cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareIFields(const IFieldDef (&defs)[count]) { - DoPrepareIFields(defs, count); - } - - void DoPrepareSFields(const SFieldDef* defs, size_t count) { - cu_.mir_graph->sfield_lowering_infos_.clear(); - cu_.mir_graph->sfield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const SFieldDef* def = &defs[i]; - MirSFieldLoweringInfo field_info(def->field_idx, def->type); - // Mark even unresolved fields as initialized. - field_info.flags_ |= MirSFieldLoweringInfo::kFlagClassIsInitialized; - // NOTE: MirSFieldLoweringInfo::kFlagClassIsInDexCache isn't used by GVN. - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ &= ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile); - } - cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareSFields(const SFieldDef (&defs)[count]) { - DoPrepareSFields(defs, count); - } - - void DoPrepareBasicBlocks(const BBDef* defs, size_t count) { - cu_.mir_graph->block_id_map_.clear(); - cu_.mir_graph->block_list_.clear(); - ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block. - ASSERT_EQ(kNullBlock, defs[0].type); - ASSERT_EQ(kEntryBlock, defs[1].type); - ASSERT_EQ(kExitBlock, defs[2].type); - for (size_t i = 0u; i != count; ++i) { - const BBDef* def = &defs[i]; - BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type); - if (def->num_successors <= 2) { - bb->successor_block_list_type = kNotUsed; - bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u; - bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u; - } else { - bb->successor_block_list_type = kPackedSwitch; - bb->fall_through = 0u; - bb->taken = 0u; - bb->successor_blocks.reserve(def->num_successors); - for (size_t j = 0u; j != def->num_successors; ++j) { - SuccessorBlockInfo* successor_block_info = - static_cast(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessors)); - successor_block_info->block = j; - successor_block_info->key = 0u; // Not used by class init check elimination. - bb->successor_blocks.push_back(successor_block_info); - } - } - bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors); - if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) { - bb->data_flow_info = static_cast( - cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo)); - bb->data_flow_info->live_in_v = live_in_v_; - } - } - ASSERT_EQ(count, cu_.mir_graph->block_list_.size()); - cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1]; - ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type); - cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2]; - ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type); - } - - template - void PrepareBasicBlocks(const BBDef (&defs)[count]) { - DoPrepareBasicBlocks(defs, count); - } - - void DoPrepareMIRs(const MIRDef* defs, size_t count) { - mir_count_ = count; - mirs_ = cu_.arena.AllocArray(count, kArenaAllocMIR); - ssa_reps_.resize(count); - for (size_t i = 0u; i != count; ++i) { - const MIRDef* def = &defs[i]; - MIR* mir = &mirs_[i]; - ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size()); - BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid]; - bb->AppendMIR(mir); - mir->dalvikInsn.opcode = def->opcode; - mir->dalvikInsn.vB = static_cast(def->value); - mir->dalvikInsn.vB_wide = def->value; - if (IsInstructionIGetOrIPut(def->opcode)) { - ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size()); - mir->meta.ifield_lowering_info = def->field_info; - ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_info].MemAccessType(), - IGetOrIPutMemAccessType(def->opcode)); - } else if (IsInstructionSGetOrSPut(def->opcode)) { - ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size()); - mir->meta.sfield_lowering_info = def->field_info; - ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_info].MemAccessType(), - SGetOrSPutMemAccessType(def->opcode)); - } else if (def->opcode == static_cast(kMirOpPhi)) { - mir->meta.phi_incoming = - allocator_->AllocArray(def->num_uses, kArenaAllocDFInfo); - ASSERT_EQ(def->num_uses, bb->predecessors.size()); - std::copy(bb->predecessors.begin(), bb->predecessors.end(), mir->meta.phi_incoming); - } - mir->ssa_rep = &ssa_reps_[i]; - mir->ssa_rep->num_uses = def->num_uses; - mir->ssa_rep->uses = const_cast(def->uses); // Not modified by LVN. - mir->ssa_rep->num_defs = def->num_defs; - mir->ssa_rep->defs = const_cast(def->defs); // Not modified by LVN. - mir->dalvikInsn.opcode = def->opcode; - mir->offset = i; // LVN uses offset only for debug output - mir->optimization_flags = 0u; - } - DexFile::CodeItem* code_item = static_cast( - cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc)); - code_item->insns_size_in_code_units_ = 2u * count; - cu_.mir_graph->current_code_item_ = code_item; - } - - template - void PrepareMIRs(const MIRDef (&defs)[count]) { - DoPrepareMIRs(defs, count); - } - - void DoPrepareVregToSsaMapExit(BasicBlockId bb_id, const int32_t* map, size_t count) { - BasicBlock* bb = cu_.mir_graph->GetBasicBlock(bb_id); - ASSERT_TRUE(bb != nullptr); - ASSERT_TRUE(bb->data_flow_info != nullptr); - bb->data_flow_info->vreg_to_ssa_map_exit = - cu_.arena.AllocArray(count, kArenaAllocDFInfo); - std::copy_n(map, count, bb->data_flow_info->vreg_to_ssa_map_exit); - } - - template - void PrepareVregToSsaMapExit(BasicBlockId bb_id, const int32_t (&map)[count]) { - DoPrepareVregToSsaMapExit(bb_id, map, count); - } - - template - void MarkAsWideSRegs(const int32_t (&sregs)[count]) { - for (int32_t sreg : sregs) { - cu_.mir_graph->reg_location_[sreg].wide = true; - cu_.mir_graph->reg_location_[sreg + 1].wide = true; - cu_.mir_graph->reg_location_[sreg + 1].high_word = true; - } - } - - void PerformGVN() { - DoPerformGVN(); - } - - void PerformPreOrderDfsGVN() { - DoPerformGVN(); - } - - template - void DoPerformGVN() { - cu_.mir_graph->SSATransformationStart(); - cu_.mir_graph->ComputeDFSOrders(); - cu_.mir_graph->ComputeDominators(); - cu_.mir_graph->ComputeTopologicalSortOrder(); - cu_.mir_graph->SSATransformationEnd(); - cu_.mir_graph->temp_.gvn.ifield_ids = GlobalValueNumbering::PrepareGvnFieldIds( - allocator_.get(), cu_.mir_graph->ifield_lowering_infos_); - cu_.mir_graph->temp_.gvn.sfield_ids = GlobalValueNumbering::PrepareGvnFieldIds( - allocator_.get(), cu_.mir_graph->sfield_lowering_infos_); - ASSERT_TRUE(gvn_ == nullptr); - gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(), - GlobalValueNumbering::kModeGvn)); - value_names_.resize(mir_count_, 0xffffu); - IteratorType iterator(cu_.mir_graph.get()); - bool change = false; - for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { - LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - value_names_[mir - mirs_] = lvn->GetValueNumber(mir); - } - } - change = (lvn != nullptr) && gvn_->FinishBasicBlock(bb); - ASSERT_TRUE(gvn_->Good()); - } - } - - void PerformGVNCodeModifications() { - ASSERT_TRUE(gvn_ != nullptr); - ASSERT_TRUE(gvn_->Good()); - gvn_->StartPostProcessing(); - TopologicalSortIterator iterator(cu_.mir_graph.get()); - for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { - LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - uint16_t value_name = lvn->GetValueNumber(mir); - ASSERT_EQ(value_name, value_names_[mir - mirs_]); - } - } - bool change = (lvn != nullptr) && gvn_->FinishBasicBlock(bb); - ASSERT_FALSE(change); - ASSERT_TRUE(gvn_->Good()); - } - } - - GlobalValueNumberingTest() - : pool_(), - cu_(&pool_, kRuntimeISA, nullptr, nullptr), - mir_count_(0u), - mirs_(nullptr), - ssa_reps_(), - allocator_(), - gvn_(), - value_names_(), - live_in_v_(new (&cu_.arena) ArenaBitVector(&cu_.arena, kMaxSsaRegs, false)) { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); - cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test. - allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); - // By default, the zero-initialized reg_location_[.] with ref == false tells LVN that - // 0 constants are integral, not references, and the values are all narrow. - // Nothing else is used by LVN/GVN. Tests can override the default values as needed. - cu_.mir_graph->reg_location_ = - cu_.arena.AllocArray(kMaxSsaRegs, kArenaAllocRegAlloc); - cu_.mir_graph->num_ssa_regs_ = kMaxSsaRegs; - // Bind all possible sregs to live vregs for test purposes. - live_in_v_->SetInitialBits(kMaxSsaRegs); - cu_.mir_graph->ssa_base_vregs_.reserve(kMaxSsaRegs); - cu_.mir_graph->ssa_subscripts_.reserve(kMaxSsaRegs); - for (unsigned int i = 0; i < kMaxSsaRegs; i++) { - cu_.mir_graph->ssa_base_vregs_.push_back(i); - cu_.mir_graph->ssa_subscripts_.push_back(0); - } - // Set shorty for a void-returning method without arguments. - cu_.shorty = "V"; - } - - static constexpr size_t kMaxSsaRegs = 16384u; - - ArenaPool pool_; - CompilationUnit cu_; - size_t mir_count_; - MIR* mirs_; - std::vector ssa_reps_; - std::unique_ptr allocator_; - std::unique_ptr gvn_; - std::vector value_names_; - ArenaBitVector* live_in_v_; -}; - -constexpr uint16_t GlobalValueNumberingTest::kNoValue; - -class GlobalValueNumberingTestDiamond : public GlobalValueNumberingTest { - public: - GlobalValueNumberingTestDiamond(); - - private: - static const BBDef kDiamondBbs[]; -}; - -const GlobalValueNumberingTest::BBDef GlobalValueNumberingTestDiamond::kDiamondBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // Block #3, top of the diamond. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Block #4, left side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Block #5, right side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // Block #6, bottom. -}; - -GlobalValueNumberingTestDiamond::GlobalValueNumberingTestDiamond() - : GlobalValueNumberingTest() { - PrepareBasicBlocks(kDiamondBbs); -} - -class GlobalValueNumberingTestLoop : public GlobalValueNumberingTest { - public: - GlobalValueNumberingTestLoop(); - - private: - static const BBDef kLoopBbs[]; -}; - -const GlobalValueNumberingTest::BBDef GlobalValueNumberingTestLoop::kLoopBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), -}; - -GlobalValueNumberingTestLoop::GlobalValueNumberingTestLoop() - : GlobalValueNumberingTest() { - PrepareBasicBlocks(kLoopBbs); -} - -class GlobalValueNumberingTestCatch : public GlobalValueNumberingTest { - public: - GlobalValueNumberingTestCatch(); - - private: - static const BBDef kCatchBbs[]; -}; - -const GlobalValueNumberingTest::BBDef GlobalValueNumberingTestCatch::kCatchBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), // The top. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // The throwing insn. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Catch handler. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // The merged block. -}; - -GlobalValueNumberingTestCatch::GlobalValueNumberingTestCatch() - : GlobalValueNumberingTest() { - PrepareBasicBlocks(kCatchBbs); - // Mark catch handler. - BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u); - catch_handler->catch_entry = true; - // Add successor block info to the check block. - BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u); - check_bb->successor_block_list_type = kCatch; - SuccessorBlockInfo* successor_block_info = reinterpret_cast - (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); - successor_block_info->block = catch_handler->id; - check_bb->successor_blocks.push_back(successor_block_info); -} - -class GlobalValueNumberingTestTwoConsecutiveLoops : public GlobalValueNumberingTest { - public: - GlobalValueNumberingTestTwoConsecutiveLoops(); - - private: - static const BBDef kTwoConsecutiveLoopsBbs[]; -}; - -const GlobalValueNumberingTest::BBDef -GlobalValueNumberingTestTwoConsecutiveLoops::kTwoConsecutiveLoopsBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(9)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 6), DEF_PRED2(3, 5)), // "taken" skips over the loop. - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(4)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED2(6, 8)), // "taken" skips over the loop. - DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(7)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(7)), -}; - -GlobalValueNumberingTestTwoConsecutiveLoops::GlobalValueNumberingTestTwoConsecutiveLoops() - : GlobalValueNumberingTest() { - PrepareBasicBlocks(kTwoConsecutiveLoopsBbs); -} - -class GlobalValueNumberingTestTwoNestedLoops : public GlobalValueNumberingTest { - public: - GlobalValueNumberingTestTwoNestedLoops(); - - private: - static const BBDef kTwoNestedLoopsBbs[]; -}; - -const GlobalValueNumberingTest::BBDef -GlobalValueNumberingTestTwoNestedLoops::kTwoNestedLoopsBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(8)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 8), DEF_PRED2(3, 7)), // "taken" skips over the loop. - DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)), // "taken" skips over the loop. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), -}; - -GlobalValueNumberingTestTwoNestedLoops::GlobalValueNumberingTestTwoNestedLoops() - : GlobalValueNumberingTest() { - PrepareBasicBlocks(kTwoNestedLoopsBbs); -} - -TEST_F(GlobalValueNumberingTestDiamond, NonAliasingIFields) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessWord }, - { 4u, 1u, 4u, false, kDexMemAccessShort }, - { 5u, 1u, 5u, false, kDexMemAccessChar }, - { 6u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - { 7u, 1u, 7u, false, kDexMemAccessWord }, - { 8u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved. - { 9u, 1u, 9u, false, kDexMemAccessWord }, - { 10u, 1u, 10u, false, kDexMemAccessWord }, - { 11u, 1u, 11u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 100u), - DEF_IGET(3, Instruction::IGET, 1u, 100u, 0u), - DEF_IGET(6, Instruction::IGET, 2u, 100u, 0u), // Same as at the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 200u), - DEF_IGET(4, Instruction::IGET, 4u, 200u, 1u), - DEF_IGET(6, Instruction::IGET, 5u, 200u, 1u), // Same as at the left side. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 300u), - DEF_IGET(3, Instruction::IGET, 7u, 300u, 2u), - DEF_CONST(5, Instruction::CONST, 8u, 1000), - DEF_IPUT(5, Instruction::IPUT, 8u, 300u, 2u), - DEF_IGET(6, Instruction::IGET, 10u, 300u, 2u), // Differs from the top and the CONST. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 400u), - DEF_IGET(3, Instruction::IGET, 12u, 400u, 3u), - DEF_CONST(3, Instruction::CONST, 13u, 2000), - DEF_IPUT(4, Instruction::IPUT, 13u, 400u, 3u), - DEF_IPUT(5, Instruction::IPUT, 13u, 400u, 3u), - DEF_IGET(6, Instruction::IGET, 16u, 400u, 3u), // Differs from the top, equals the CONST. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 500u), - DEF_IGET(3, Instruction::IGET_SHORT, 18u, 500u, 4u), - DEF_IGET(3, Instruction::IGET_CHAR, 19u, 500u, 5u), - DEF_IPUT(4, Instruction::IPUT_SHORT, 20u, 500u, 6u), // Clobbers field #4, not #5. - DEF_IGET(6, Instruction::IGET_SHORT, 21u, 500u, 4u), // Differs from the top. - DEF_IGET(6, Instruction::IGET_CHAR, 22u, 500u, 5u), // Same as the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 600u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 601u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 602u), - DEF_IGET(3, Instruction::IGET, 26u, 600u, 7u), - DEF_IGET(3, Instruction::IGET, 27u, 601u, 7u), - DEF_IPUT(4, Instruction::IPUT, 28u, 602u, 8u), // Doesn't clobber field #7 for other refs. - DEF_IGET(6, Instruction::IGET, 29u, 600u, 7u), // Same as the top. - DEF_IGET(6, Instruction::IGET, 30u, 601u, 7u), // Same as the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 700u), - DEF_CONST(4, Instruction::CONST, 32u, 3000), - DEF_IPUT(4, Instruction::IPUT, 32u, 700u, 9u), - DEF_IPUT(4, Instruction::IPUT, 32u, 700u, 10u), - DEF_CONST(5, Instruction::CONST, 35u, 3001), - DEF_IPUT(5, Instruction::IPUT, 35u, 700u, 9u), - DEF_IPUT(5, Instruction::IPUT, 35u, 700u, 10u), - DEF_IGET(6, Instruction::IGET, 38u, 700u, 9u), - DEF_IGET(6, Instruction::IGET, 39u, 700u, 10u), // Same value as read from field #9. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 800u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 801u), - DEF_CONST(4, Instruction::CONST, 42u, 3000), - DEF_IPUT(4, Instruction::IPUT, 42u, 800u, 11u), - DEF_IPUT(4, Instruction::IPUT, 42u, 801u, 11u), - DEF_CONST(5, Instruction::CONST, 45u, 3001), - DEF_IPUT(5, Instruction::IPUT, 45u, 800u, 11u), - DEF_IPUT(5, Instruction::IPUT, 45u, 801u, 11u), - DEF_IGET(6, Instruction::IGET, 48u, 800u, 11u), - DEF_IGET(6, Instruction::IGET, 49u, 801u, 11u), // Same value as read from ref 46u. - - // Invoke doesn't interfere with non-aliasing refs. There's one test above where a reference - // escapes in the left BB (we let a reference escape if we use it to store to an unresolved - // field) and the INVOKE in the right BB shouldn't interfere with that either. - DEF_INVOKE1(5, Instruction::INVOKE_STATIC, 48u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[1], value_names_[2]); - - EXPECT_EQ(value_names_[4], value_names_[5]); - - EXPECT_NE(value_names_[7], value_names_[10]); - EXPECT_NE(value_names_[8], value_names_[10]); - - EXPECT_NE(value_names_[12], value_names_[16]); - EXPECT_EQ(value_names_[13], value_names_[16]); - - EXPECT_NE(value_names_[18], value_names_[21]); - EXPECT_EQ(value_names_[19], value_names_[22]); - - EXPECT_EQ(value_names_[26], value_names_[29]); - EXPECT_EQ(value_names_[27], value_names_[30]); - - EXPECT_EQ(value_names_[38], value_names_[39]); - - EXPECT_EQ(value_names_[48], value_names_[49]); -} - -TEST_F(GlobalValueNumberingTestDiamond, AliasingIFieldsSingleObject) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessWord }, - { 4u, 1u, 4u, false, kDexMemAccessShort }, - { 5u, 1u, 5u, false, kDexMemAccessChar }, - { 6u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - { 7u, 1u, 7u, false, kDexMemAccessWord }, - { 8u, 1u, 8u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_IGET(3, Instruction::IGET, 0u, 100u, 0u), - DEF_IGET(6, Instruction::IGET, 1u, 100u, 0u), // Same as at the top. - - DEF_IGET(4, Instruction::IGET, 2u, 100u, 1u), - DEF_IGET(6, Instruction::IGET, 3u, 100u, 1u), // Same as at the left side. - - DEF_IGET(3, Instruction::IGET, 4u, 100u, 2u), - DEF_CONST(5, Instruction::CONST, 5u, 1000), - DEF_IPUT(5, Instruction::IPUT, 5u, 100u, 2u), - DEF_IGET(6, Instruction::IGET, 7u, 100u, 2u), // Differs from the top and the CONST. - - DEF_IGET(3, Instruction::IGET, 8u, 100u, 3u), - DEF_CONST(3, Instruction::CONST, 9u, 2000), - DEF_IPUT(4, Instruction::IPUT, 9u, 100u, 3u), - DEF_IPUT(5, Instruction::IPUT, 9u, 100u, 3u), - DEF_IGET(6, Instruction::IGET, 12u, 100u, 3u), // Differs from the top, equals the CONST. - - DEF_IGET(3, Instruction::IGET_SHORT, 13u, 100u, 4u), - DEF_IGET(3, Instruction::IGET_CHAR, 14u, 100u, 5u), - DEF_IPUT(4, Instruction::IPUT_SHORT, 15u, 100u, 6u), // Clobbers field #4, not #5. - DEF_IGET(6, Instruction::IGET_SHORT, 16u, 100u, 4u), // Differs from the top. - DEF_IGET(6, Instruction::IGET_CHAR, 17u, 100u, 5u), // Same as the top. - - DEF_CONST(4, Instruction::CONST, 18u, 3000), - DEF_IPUT(4, Instruction::IPUT, 18u, 100u, 7u), - DEF_IPUT(4, Instruction::IPUT, 18u, 100u, 8u), - DEF_CONST(5, Instruction::CONST, 21u, 3001), - DEF_IPUT(5, Instruction::IPUT, 21u, 100u, 7u), - DEF_IPUT(5, Instruction::IPUT, 21u, 100u, 8u), - DEF_IGET(6, Instruction::IGET, 24u, 100u, 7u), - DEF_IGET(6, Instruction::IGET, 25u, 100u, 8u), // Same value as read from field #7. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - - EXPECT_EQ(value_names_[2], value_names_[3]); - - EXPECT_NE(value_names_[4], value_names_[7]); - EXPECT_NE(value_names_[5], value_names_[7]); - - EXPECT_NE(value_names_[8], value_names_[12]); - EXPECT_EQ(value_names_[9], value_names_[12]); - - EXPECT_NE(value_names_[13], value_names_[16]); - EXPECT_EQ(value_names_[14], value_names_[17]); - - EXPECT_EQ(value_names_[24], value_names_[25]); -} - -TEST_F(GlobalValueNumberingTestDiamond, AliasingIFieldsTwoObjects) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessWord }, - { 4u, 1u, 4u, false, kDexMemAccessShort }, - { 5u, 1u, 5u, false, kDexMemAccessChar }, - { 6u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - { 7u, 1u, 7u, false, kDexMemAccessWord }, - { 8u, 1u, 8u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_IGET(3, Instruction::IGET, 0u, 100u, 0u), - DEF_IPUT(4, Instruction::IPUT, 1u, 101u, 0u), // May alias with the IGET at the top. - DEF_IGET(6, Instruction::IGET, 2u, 100u, 0u), // Differs from the top. - - DEF_IGET(3, Instruction::IGET, 3u, 100u, 1u), - DEF_IPUT(5, Instruction::IPUT, 3u, 101u, 1u), // If aliasing, stores the same value. - DEF_IGET(6, Instruction::IGET, 5u, 100u, 1u), // Same as the top. - - DEF_IGET(3, Instruction::IGET, 6u, 100u, 2u), - DEF_CONST(5, Instruction::CONST, 7u, 1000), - DEF_IPUT(5, Instruction::IPUT, 7u, 101u, 2u), - DEF_IGET(6, Instruction::IGET, 9u, 100u, 2u), // Differs from the top and the CONST. - - DEF_IGET(3, Instruction::IGET, 10u, 100u, 3u), - DEF_CONST(3, Instruction::CONST, 11u, 2000), - DEF_IPUT(4, Instruction::IPUT, 11u, 101u, 3u), - DEF_IPUT(5, Instruction::IPUT, 11u, 101u, 3u), - DEF_IGET(6, Instruction::IGET, 14u, 100u, 3u), // Differs from the top and the CONST. - - DEF_IGET(3, Instruction::IGET_SHORT, 15u, 100u, 4u), - DEF_IGET(3, Instruction::IGET_CHAR, 16u, 100u, 5u), - DEF_IPUT(4, Instruction::IPUT_SHORT, 17u, 101u, 6u), // Clobbers field #4, not #5. - DEF_IGET(6, Instruction::IGET_SHORT, 18u, 100u, 4u), // Differs from the top. - DEF_IGET(6, Instruction::IGET_CHAR, 19u, 100u, 5u), // Same as the top. - - DEF_CONST(4, Instruction::CONST, 20u, 3000), - DEF_IPUT(4, Instruction::IPUT, 20u, 100u, 7u), - DEF_IPUT(4, Instruction::IPUT, 20u, 101u, 8u), - DEF_CONST(5, Instruction::CONST, 23u, 3001), - DEF_IPUT(5, Instruction::IPUT, 23u, 100u, 7u), - DEF_IPUT(5, Instruction::IPUT, 23u, 101u, 8u), - DEF_IGET(6, Instruction::IGET, 26u, 100u, 7u), - DEF_IGET(6, Instruction::IGET, 27u, 101u, 8u), // Same value as read from field #7. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[2]); - - EXPECT_EQ(value_names_[3], value_names_[5]); - - EXPECT_NE(value_names_[6], value_names_[9]); - EXPECT_NE(value_names_[7], value_names_[9]); - - EXPECT_NE(value_names_[10], value_names_[14]); - EXPECT_NE(value_names_[10], value_names_[14]); - - EXPECT_NE(value_names_[15], value_names_[18]); - EXPECT_EQ(value_names_[16], value_names_[19]); - - EXPECT_EQ(value_names_[26], value_names_[27]); -} - -TEST_F(GlobalValueNumberingTestDiamond, SFields) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessWord }, - { 4u, 1u, 4u, false, kDexMemAccessShort }, - { 5u, 1u, 5u, false, kDexMemAccessChar }, - { 6u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - { 7u, 1u, 7u, false, kDexMemAccessWord }, - { 8u, 1u, 8u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_SGET(3, Instruction::SGET, 0u, 0u), - DEF_SGET(6, Instruction::SGET, 1u, 0u), // Same as at the top. - - DEF_SGET(4, Instruction::SGET, 2u, 1u), - DEF_SGET(6, Instruction::SGET, 3u, 1u), // Same as at the left side. - - DEF_SGET(3, Instruction::SGET, 4u, 2u), - DEF_CONST(5, Instruction::CONST, 5u, 100), - DEF_SPUT(5, Instruction::SPUT, 5u, 2u), - DEF_SGET(6, Instruction::SGET, 7u, 2u), // Differs from the top and the CONST. - - DEF_SGET(3, Instruction::SGET, 8u, 3u), - DEF_CONST(3, Instruction::CONST, 9u, 200), - DEF_SPUT(4, Instruction::SPUT, 9u, 3u), - DEF_SPUT(5, Instruction::SPUT, 9u, 3u), - DEF_SGET(6, Instruction::SGET, 12u, 3u), // Differs from the top, equals the CONST. - - DEF_SGET(3, Instruction::SGET_SHORT, 13u, 4u), - DEF_SGET(3, Instruction::SGET_CHAR, 14u, 5u), - DEF_SPUT(4, Instruction::SPUT_SHORT, 15u, 6u), // Clobbers field #4, not #5. - DEF_SGET(6, Instruction::SGET_SHORT, 16u, 4u), // Differs from the top. - DEF_SGET(6, Instruction::SGET_CHAR, 17u, 5u), // Same as the top. - - DEF_CONST(4, Instruction::CONST, 18u, 300), - DEF_SPUT(4, Instruction::SPUT, 18u, 7u), - DEF_SPUT(4, Instruction::SPUT, 18u, 8u), - DEF_CONST(5, Instruction::CONST, 21u, 301), - DEF_SPUT(5, Instruction::SPUT, 21u, 7u), - DEF_SPUT(5, Instruction::SPUT, 21u, 8u), - DEF_SGET(6, Instruction::SGET, 24u, 7u), - DEF_SGET(6, Instruction::SGET, 25u, 8u), // Same value as read from field #7. - }; - - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - - EXPECT_EQ(value_names_[2], value_names_[3]); - - EXPECT_NE(value_names_[4], value_names_[7]); - EXPECT_NE(value_names_[5], value_names_[7]); - - EXPECT_NE(value_names_[8], value_names_[12]); - EXPECT_EQ(value_names_[9], value_names_[12]); - - EXPECT_NE(value_names_[13], value_names_[16]); - EXPECT_EQ(value_names_[14], value_names_[17]); - - EXPECT_EQ(value_names_[24], value_names_[25]); -} - -TEST_F(GlobalValueNumberingTestDiamond, NonAliasingArrays) { - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 100u), - DEF_AGET(3, Instruction::AGET, 1u, 100u, 101u), - DEF_AGET(6, Instruction::AGET, 2u, 100u, 101u), // Same as at the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 200u), - DEF_IGET(4, Instruction::AGET, 4u, 200u, 201u), - DEF_IGET(6, Instruction::AGET, 5u, 200u, 201u), // Same as at the left side. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 300u), - DEF_AGET(3, Instruction::AGET, 7u, 300u, 301u), - DEF_CONST(5, Instruction::CONST, 8u, 1000), - DEF_APUT(5, Instruction::APUT, 8u, 300u, 301u), - DEF_AGET(6, Instruction::AGET, 10u, 300u, 301u), // Differs from the top and the CONST. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 400u), - DEF_AGET(3, Instruction::AGET, 12u, 400u, 401u), - DEF_CONST(3, Instruction::CONST, 13u, 2000), - DEF_APUT(4, Instruction::APUT, 13u, 400u, 401u), - DEF_APUT(5, Instruction::APUT, 13u, 400u, 401u), - DEF_AGET(6, Instruction::AGET, 16u, 400u, 401u), // Differs from the top, equals the CONST. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 500u), - DEF_AGET(3, Instruction::AGET, 18u, 500u, 501u), - DEF_APUT(4, Instruction::APUT, 19u, 500u, 502u), // Clobbers value at index 501u. - DEF_AGET(6, Instruction::AGET, 20u, 500u, 501u), // Differs from the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 600u), - DEF_CONST(4, Instruction::CONST, 22u, 3000), - DEF_APUT(4, Instruction::APUT, 22u, 600u, 601u), - DEF_APUT(4, Instruction::APUT, 22u, 600u, 602u), - DEF_CONST(5, Instruction::CONST, 25u, 3001), - DEF_APUT(5, Instruction::APUT, 25u, 600u, 601u), - DEF_APUT(5, Instruction::APUT, 25u, 600u, 602u), - DEF_AGET(6, Instruction::AGET, 28u, 600u, 601u), - DEF_AGET(6, Instruction::AGET, 29u, 600u, 602u), // Same value as read from index 601u. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 700u), - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 701u), - DEF_AGET(3, Instruction::AGET, 32u, 700u, 702u), - DEF_APUT(4, Instruction::APUT, 33u, 701u, 702u), // Doesn't interfere with unrelated array. - DEF_AGET(6, Instruction::AGET, 34u, 700u, 702u), // Same value as at the top. - }; - - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[1], value_names_[2]); - - EXPECT_EQ(value_names_[4], value_names_[5]); - - EXPECT_NE(value_names_[7], value_names_[10]); - EXPECT_NE(value_names_[8], value_names_[10]); - - EXPECT_NE(value_names_[12], value_names_[16]); - EXPECT_EQ(value_names_[13], value_names_[16]); - - EXPECT_NE(value_names_[18], value_names_[20]); - - EXPECT_NE(value_names_[28], value_names_[22]); - EXPECT_NE(value_names_[28], value_names_[25]); - EXPECT_EQ(value_names_[28], value_names_[29]); - - EXPECT_EQ(value_names_[32], value_names_[34]); -} - -TEST_F(GlobalValueNumberingTestDiamond, AliasingArrays) { - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - // NOTE: We're also testing that these tests really do not interfere with each other. - - DEF_AGET(3, Instruction::AGET_BOOLEAN, 0u, 100u, 101u), - DEF_AGET(6, Instruction::AGET_BOOLEAN, 1u, 100u, 101u), // Same as at the top. - - DEF_IGET(4, Instruction::AGET_OBJECT, 2u, 200u, 201u), - DEF_IGET(6, Instruction::AGET_OBJECT, 3u, 200u, 201u), // Same as at the left side. - - DEF_AGET(3, Instruction::AGET_WIDE, 4u, 300u, 301u), - DEF_CONST(5, Instruction::CONST_WIDE, 6u, 1000), - DEF_APUT(5, Instruction::APUT_WIDE, 6u, 300u, 301u), - DEF_AGET(6, Instruction::AGET_WIDE, 8u, 300u, 301u), // Differs from the top and the CONST. - - DEF_AGET(3, Instruction::AGET_SHORT, 10u, 400u, 401u), - DEF_CONST(3, Instruction::CONST, 11u, 2000), - DEF_APUT(4, Instruction::APUT_SHORT, 11u, 400u, 401u), - DEF_APUT(5, Instruction::APUT_SHORT, 11u, 400u, 401u), - DEF_AGET(6, Instruction::AGET_SHORT, 12u, 400u, 401u), // Differs from the top, == CONST. - - DEF_AGET(3, Instruction::AGET_CHAR, 13u, 500u, 501u), - DEF_APUT(4, Instruction::APUT_CHAR, 14u, 500u, 502u), // Clobbers value at index 501u. - DEF_AGET(6, Instruction::AGET_CHAR, 15u, 500u, 501u), // Differs from the top. - - DEF_AGET(3, Instruction::AGET_BYTE, 16u, 600u, 602u), - DEF_APUT(4, Instruction::APUT_BYTE, 17u, 601u, 602u), // Clobbers values in array 600u. - DEF_AGET(6, Instruction::AGET_BYTE, 18u, 600u, 602u), // Differs from the top. - - DEF_CONST(4, Instruction::CONST, 19u, 3000), - DEF_APUT(4, Instruction::APUT, 19u, 700u, 701u), - DEF_APUT(4, Instruction::APUT, 19u, 700u, 702u), - DEF_CONST(5, Instruction::CONST, 22u, 3001), - DEF_APUT(5, Instruction::APUT, 22u, 700u, 701u), - DEF_APUT(5, Instruction::APUT, 22u, 700u, 702u), - DEF_AGET(6, Instruction::AGET, 25u, 700u, 701u), - DEF_AGET(6, Instruction::AGET, 26u, 700u, 702u), // Same value as read from index 601u. - }; - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 4, 6, 8 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - - EXPECT_EQ(value_names_[2], value_names_[3]); - - EXPECT_NE(value_names_[4], value_names_[7]); - EXPECT_NE(value_names_[5], value_names_[7]); - - EXPECT_NE(value_names_[8], value_names_[12]); - EXPECT_EQ(value_names_[9], value_names_[12]); - - EXPECT_NE(value_names_[13], value_names_[15]); - - EXPECT_NE(value_names_[16], value_names_[18]); - - EXPECT_NE(value_names_[25], value_names_[19]); - EXPECT_NE(value_names_[25], value_names_[22]); - EXPECT_EQ(value_names_[25], value_names_[26]); -} - -TEST_F(GlobalValueNumberingTestDiamond, Phi) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000), - DEF_CONST(4, Instruction::CONST, 1u, 2000), - DEF_CONST(5, Instruction::CONST, 2u, 3000), - DEF_MOVE(4, Instruction::MOVE, 3u, 0u), - DEF_MOVE(4, Instruction::MOVE, 4u, 1u), - DEF_MOVE(5, Instruction::MOVE, 5u, 0u), - DEF_MOVE(5, Instruction::MOVE, 6u, 2u), - DEF_PHI2(6, 7u, 3u, 5u), // Same as CONST 0u (1000). - DEF_PHI2(6, 8u, 3u, 0u), // Same as CONST 0u (1000). - DEF_PHI2(6, 9u, 0u, 5u), // Same as CONST 0u (1000). - DEF_PHI2(6, 10u, 4u, 5u), // Merge 1u (2000) and 0u (1000). - DEF_PHI2(6, 11u, 1u, 5u), // Merge 1u (2000) and 0u (1000). - DEF_PHI2(6, 12u, 4u, 0u), // Merge 1u (2000) and 0u (1000). - DEF_PHI2(6, 13u, 1u, 0u), // Merge 1u (2000) and 0u (1000). - DEF_PHI2(6, 14u, 3u, 6u), // Merge 0u (1000) and 2u (3000). - DEF_PHI2(6, 15u, 0u, 6u), // Merge 0u (1000) and 2u (3000). - DEF_PHI2(6, 16u, 3u, 2u), // Merge 0u (1000) and 2u (3000). - DEF_PHI2(6, 17u, 0u, 2u), // Merge 0u (1000) and 2u (3000). - DEF_PHI2(6, 18u, 4u, 6u), // Merge 1u (2000) and 2u (3000). - DEF_PHI2(6, 19u, 1u, 6u), // Merge 1u (2000) and 2u (3000). - DEF_PHI2(6, 20u, 4u, 2u), // Merge 1u (2000) and 2u (3000). - DEF_PHI2(6, 21u, 1u, 2u), // Merge 1u (2000) and 2u (3000). - }; - - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[7]); - EXPECT_EQ(value_names_[0], value_names_[8]); - EXPECT_EQ(value_names_[0], value_names_[9]); - EXPECT_NE(value_names_[10], value_names_[0]); - EXPECT_NE(value_names_[10], value_names_[1]); - EXPECT_NE(value_names_[10], value_names_[2]); - EXPECT_EQ(value_names_[10], value_names_[11]); - EXPECT_EQ(value_names_[10], value_names_[12]); - EXPECT_EQ(value_names_[10], value_names_[13]); - EXPECT_NE(value_names_[14], value_names_[0]); - EXPECT_NE(value_names_[14], value_names_[1]); - EXPECT_NE(value_names_[14], value_names_[2]); - EXPECT_NE(value_names_[14], value_names_[10]); - EXPECT_EQ(value_names_[14], value_names_[15]); - EXPECT_EQ(value_names_[14], value_names_[16]); - EXPECT_EQ(value_names_[14], value_names_[17]); - EXPECT_NE(value_names_[18], value_names_[0]); - EXPECT_NE(value_names_[18], value_names_[1]); - EXPECT_NE(value_names_[18], value_names_[2]); - EXPECT_NE(value_names_[18], value_names_[10]); - EXPECT_NE(value_names_[18], value_names_[14]); - EXPECT_EQ(value_names_[18], value_names_[19]); - EXPECT_EQ(value_names_[18], value_names_[20]); - EXPECT_EQ(value_names_[18], value_names_[21]); -} - -TEST_F(GlobalValueNumberingTestDiamond, PhiWide) { - static const MIRDef mirs[] = { - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000), - DEF_CONST_WIDE(4, Instruction::CONST_WIDE, 2u, 2000), - DEF_CONST_WIDE(5, Instruction::CONST_WIDE, 4u, 3000), - DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 6u, 0u), - DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 8u, 2u), - DEF_MOVE_WIDE(5, Instruction::MOVE_WIDE, 10u, 0u), - DEF_MOVE_WIDE(5, Instruction::MOVE_WIDE, 12u, 4u), - DEF_PHI2(6, 14u, 6u, 10u), // Same as CONST_WIDE 0u (1000). - DEF_PHI2(6, 15u, 7u, 11u), // Same as CONST_WIDE 0u (1000), high word. - DEF_PHI2(6, 16u, 6u, 0u), // Same as CONST_WIDE 0u (1000). - DEF_PHI2(6, 17u, 7u, 1u), // Same as CONST_WIDE 0u (1000), high word. - DEF_PHI2(6, 18u, 0u, 10u), // Same as CONST_WIDE 0u (1000). - DEF_PHI2(6, 19u, 1u, 11u), // Same as CONST_WIDE 0u (1000), high word. - DEF_PHI2(6, 20u, 8u, 10u), // Merge 2u (2000) and 0u (1000). - DEF_PHI2(6, 21u, 9u, 11u), // Merge 2u (2000) and 0u (1000), high word. - DEF_PHI2(6, 22u, 2u, 10u), // Merge 2u (2000) and 0u (1000). - DEF_PHI2(6, 23u, 3u, 11u), // Merge 2u (2000) and 0u (1000), high word. - DEF_PHI2(6, 24u, 8u, 0u), // Merge 2u (2000) and 0u (1000). - DEF_PHI2(6, 25u, 9u, 1u), // Merge 2u (2000) and 0u (1000), high word. - DEF_PHI2(6, 26u, 2u, 0u), // Merge 2u (2000) and 0u (1000). - DEF_PHI2(6, 27u, 5u, 1u), // Merge 2u (2000) and 0u (1000), high word. - DEF_PHI2(6, 28u, 6u, 12u), // Merge 0u (1000) and 4u (3000). - DEF_PHI2(6, 29u, 7u, 13u), // Merge 0u (1000) and 4u (3000), high word. - DEF_PHI2(6, 30u, 0u, 12u), // Merge 0u (1000) and 4u (3000). - DEF_PHI2(6, 31u, 1u, 13u), // Merge 0u (1000) and 4u (3000), high word. - DEF_PHI2(6, 32u, 6u, 4u), // Merge 0u (1000) and 4u (3000). - DEF_PHI2(6, 33u, 7u, 5u), // Merge 0u (1000) and 4u (3000), high word. - DEF_PHI2(6, 34u, 0u, 4u), // Merge 0u (1000) and 4u (3000). - DEF_PHI2(6, 35u, 1u, 5u), // Merge 0u (1000) and 4u (3000), high word. - DEF_PHI2(6, 36u, 8u, 12u), // Merge 2u (2000) and 4u (3000). - DEF_PHI2(6, 37u, 9u, 13u), // Merge 2u (2000) and 4u (3000), high word. - DEF_PHI2(6, 38u, 2u, 12u), // Merge 2u (2000) and 4u (3000). - DEF_PHI2(6, 39u, 3u, 13u), // Merge 2u (2000) and 4u (3000), high word. - DEF_PHI2(6, 40u, 8u, 4u), // Merge 2u (2000) and 4u (3000). - DEF_PHI2(6, 41u, 9u, 5u), // Merge 2u (2000) and 4u (3000), high word. - DEF_PHI2(6, 42u, 2u, 4u), // Merge 2u (2000) and 4u (3000). - DEF_PHI2(6, 43u, 3u, 5u), // Merge 2u (2000) and 4u (3000), high word. - }; - - PrepareMIRs(mirs); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - if ((mirs_[i].ssa_rep->defs[0] % 2) == 0) { - const int32_t wide_sregs[] = { mirs_[i].ssa_rep->defs[0] }; - MarkAsWideSRegs(wide_sregs); - } - } - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[7]); - EXPECT_EQ(value_names_[0], value_names_[9]); - EXPECT_EQ(value_names_[0], value_names_[11]); - EXPECT_NE(value_names_[13], value_names_[0]); - EXPECT_NE(value_names_[13], value_names_[1]); - EXPECT_NE(value_names_[13], value_names_[2]); - EXPECT_EQ(value_names_[13], value_names_[15]); - EXPECT_EQ(value_names_[13], value_names_[17]); - EXPECT_EQ(value_names_[13], value_names_[19]); - EXPECT_NE(value_names_[21], value_names_[0]); - EXPECT_NE(value_names_[21], value_names_[1]); - EXPECT_NE(value_names_[21], value_names_[2]); - EXPECT_NE(value_names_[21], value_names_[13]); - EXPECT_EQ(value_names_[21], value_names_[23]); - EXPECT_EQ(value_names_[21], value_names_[25]); - EXPECT_EQ(value_names_[21], value_names_[27]); - EXPECT_NE(value_names_[29], value_names_[0]); - EXPECT_NE(value_names_[29], value_names_[1]); - EXPECT_NE(value_names_[29], value_names_[2]); - EXPECT_NE(value_names_[29], value_names_[13]); - EXPECT_NE(value_names_[29], value_names_[21]); - EXPECT_EQ(value_names_[29], value_names_[31]); - EXPECT_EQ(value_names_[29], value_names_[33]); - EXPECT_EQ(value_names_[29], value_names_[35]); - // High words should get kNoValue. - EXPECT_EQ(value_names_[8], kNoValue); - EXPECT_EQ(value_names_[10], kNoValue); - EXPECT_EQ(value_names_[12], kNoValue); - EXPECT_EQ(value_names_[14], kNoValue); - EXPECT_EQ(value_names_[16], kNoValue); - EXPECT_EQ(value_names_[18], kNoValue); - EXPECT_EQ(value_names_[20], kNoValue); - EXPECT_EQ(value_names_[22], kNoValue); - EXPECT_EQ(value_names_[24], kNoValue); - EXPECT_EQ(value_names_[26], kNoValue); - EXPECT_EQ(value_names_[28], kNoValue); - EXPECT_EQ(value_names_[30], kNoValue); - EXPECT_EQ(value_names_[32], kNoValue); - EXPECT_EQ(value_names_[34], kNoValue); - EXPECT_EQ(value_names_[36], kNoValue); -} - -TEST_F(GlobalValueNumberingTestLoop, NonAliasingIFields) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessWord }, - { 4u, 1u, 4u, false, kDexMemAccessWord }, - { 5u, 1u, 5u, false, kDexMemAccessShort }, - { 6u, 1u, 6u, false, kDexMemAccessChar }, - { 7u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - { 8u, 1u, 8u, false, kDexMemAccessWord }, - { 9u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved. - { 10u, 1u, 10u, false, kDexMemAccessWord }, - { 11u, 1u, 11u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 100u), - DEF_IGET(3, Instruction::IGET, 1u, 100u, 0u), - DEF_IGET(4, Instruction::IGET, 2u, 100u, 0u), // Same as at the top. - DEF_IGET(5, Instruction::IGET, 3u, 100u, 0u), // Same as at the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 200u), - DEF_IGET(3, Instruction::IGET, 5u, 200u, 1u), - DEF_IGET(4, Instruction::IGET, 6u, 200u, 1u), // Differs from top... - DEF_IPUT(4, Instruction::IPUT, 7u, 200u, 1u), // Because of this IPUT. - DEF_IGET(5, Instruction::IGET, 8u, 200u, 1u), // Differs from top and the loop IGET. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 300u), - DEF_IGET(3, Instruction::IGET, 10u, 300u, 2u), - DEF_IPUT(4, Instruction::IPUT, 11u, 300u, 2u), // Because of this IPUT... - DEF_IGET(4, Instruction::IGET, 12u, 300u, 2u), // Differs from top. - DEF_IGET(5, Instruction::IGET, 13u, 300u, 2u), // Differs from top but same as the loop IGET. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 400u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 401u), - DEF_CONST(3, Instruction::CONST, 16u, 3000), - DEF_IPUT(3, Instruction::IPUT, 16u, 400u, 3u), - DEF_IPUT(3, Instruction::IPUT, 16u, 400u, 4u), - DEF_IPUT(3, Instruction::IPUT, 16u, 401u, 3u), - DEF_IGET(4, Instruction::IGET, 20u, 400u, 3u), // Differs from 16u and 23u. - DEF_IGET(4, Instruction::IGET, 21u, 400u, 4u), // Same as 20u. - DEF_IGET(4, Instruction::IGET, 22u, 401u, 3u), // Same as 20u. - DEF_CONST(4, Instruction::CONST, 23u, 4000), - DEF_IPUT(4, Instruction::IPUT, 23u, 400u, 3u), - DEF_IPUT(4, Instruction::IPUT, 23u, 400u, 4u), - DEF_IPUT(4, Instruction::IPUT, 23u, 401u, 3u), - DEF_IGET(5, Instruction::IGET, 27u, 400u, 3u), // Differs from 16u and 20u... - DEF_IGET(5, Instruction::IGET, 28u, 400u, 4u), // and same as the CONST 23u - DEF_IGET(5, Instruction::IGET, 29u, 400u, 4u), // and same as the CONST 23u. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 500u), - DEF_IGET(3, Instruction::IGET_SHORT, 31u, 500u, 5u), - DEF_IGET(3, Instruction::IGET_CHAR, 32u, 500u, 6u), - DEF_IPUT(4, Instruction::IPUT_SHORT, 33u, 500u, 7u), // Clobbers field #5, not #6. - DEF_IGET(5, Instruction::IGET_SHORT, 34u, 500u, 5u), // Differs from the top. - DEF_IGET(5, Instruction::IGET_CHAR, 35u, 500u, 6u), // Same as the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 600u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 601u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 602u), - DEF_IGET(3, Instruction::IGET, 39u, 600u, 8u), - DEF_IGET(3, Instruction::IGET, 40u, 601u, 8u), - DEF_IPUT(4, Instruction::IPUT, 41u, 602u, 9u), // Doesn't clobber field #8 for other refs. - DEF_IGET(5, Instruction::IGET, 42u, 600u, 8u), // Same as the top. - DEF_IGET(5, Instruction::IGET, 43u, 601u, 8u), // Same as the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 700u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 701u), - DEF_CONST(3, Instruction::CONST, 46u, 3000), - DEF_IPUT(3, Instruction::IPUT, 46u, 700u, 10u), - DEF_IPUT(3, Instruction::IPUT, 46u, 700u, 11u), - DEF_IPUT(3, Instruction::IPUT, 46u, 701u, 10u), - DEF_IGET(4, Instruction::IGET, 50u, 700u, 10u), // Differs from the CONSTs 46u and 53u. - DEF_IGET(4, Instruction::IGET, 51u, 700u, 11u), // Same as 50u. - DEF_IGET(4, Instruction::IGET, 52u, 701u, 10u), // Same as 50u. - DEF_CONST(4, Instruction::CONST, 53u, 3001), - DEF_IPUT(4, Instruction::IPUT, 53u, 700u, 10u), - DEF_IPUT(4, Instruction::IPUT, 53u, 700u, 11u), - DEF_IPUT(4, Instruction::IPUT, 53u, 701u, 10u), - DEF_IGET(5, Instruction::IGET, 57u, 700u, 10u), // Same as the CONST 53u. - DEF_IGET(5, Instruction::IGET, 58u, 700u, 11u), // Same as the CONST 53u. - DEF_IGET(5, Instruction::IGET, 59u, 701u, 10u), // Same as the CONST 53u. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[1], value_names_[2]); - EXPECT_EQ(value_names_[1], value_names_[3]); - - EXPECT_NE(value_names_[5], value_names_[6]); - EXPECT_NE(value_names_[5], value_names_[7]); - EXPECT_NE(value_names_[6], value_names_[7]); - - EXPECT_NE(value_names_[10], value_names_[12]); - EXPECT_EQ(value_names_[12], value_names_[13]); - - EXPECT_NE(value_names_[20], value_names_[16]); - EXPECT_NE(value_names_[20], value_names_[23]); - EXPECT_EQ(value_names_[20], value_names_[21]); - EXPECT_EQ(value_names_[20], value_names_[22]); - EXPECT_NE(value_names_[27], value_names_[16]); - EXPECT_NE(value_names_[27], value_names_[20]); - EXPECT_EQ(value_names_[27], value_names_[28]); - EXPECT_EQ(value_names_[27], value_names_[29]); - - EXPECT_NE(value_names_[31], value_names_[34]); - EXPECT_EQ(value_names_[32], value_names_[35]); - - EXPECT_EQ(value_names_[39], value_names_[42]); - EXPECT_EQ(value_names_[40], value_names_[43]); - - EXPECT_NE(value_names_[50], value_names_[46]); - EXPECT_NE(value_names_[50], value_names_[53]); - EXPECT_EQ(value_names_[50], value_names_[51]); - EXPECT_EQ(value_names_[50], value_names_[52]); - EXPECT_EQ(value_names_[57], value_names_[53]); - EXPECT_EQ(value_names_[58], value_names_[53]); - EXPECT_EQ(value_names_[59], value_names_[53]); -} - -TEST_F(GlobalValueNumberingTestLoop, AliasingIFieldsSingleObject) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessWord }, - { 4u, 1u, 4u, false, kDexMemAccessWord }, - { 5u, 1u, 5u, false, kDexMemAccessShort }, - { 6u, 1u, 6u, false, kDexMemAccessChar }, - { 7u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_IGET(3, Instruction::IGET, 0u, 100u, 0u), - DEF_IGET(4, Instruction::IGET, 1u, 100u, 0u), // Same as at the top. - DEF_IGET(5, Instruction::IGET, 2u, 100u, 0u), // Same as at the top. - - DEF_IGET(3, Instruction::IGET, 3u, 100u, 1u), - DEF_IGET(4, Instruction::IGET, 4u, 100u, 1u), // Differs from top... - DEF_IPUT(4, Instruction::IPUT, 5u, 100u, 1u), // Because of this IPUT. - DEF_IGET(5, Instruction::IGET, 6u, 100u, 1u), // Differs from top and the loop IGET. - - DEF_IGET(3, Instruction::IGET, 7u, 100u, 2u), - DEF_IPUT(4, Instruction::IPUT, 8u, 100u, 2u), // Because of this IPUT... - DEF_IGET(4, Instruction::IGET, 9u, 100u, 2u), // Differs from top. - DEF_IGET(5, Instruction::IGET, 10u, 100u, 2u), // Differs from top but same as the loop IGET. - - DEF_CONST(3, Instruction::CONST, 11u, 3000), - DEF_IPUT(3, Instruction::IPUT, 11u, 100u, 3u), - DEF_IPUT(3, Instruction::IPUT, 11u, 100u, 4u), - DEF_IGET(4, Instruction::IGET, 14u, 100u, 3u), // Differs from 11u and 16u. - DEF_IGET(4, Instruction::IGET, 15u, 100u, 4u), // Same as 14u. - DEF_CONST(4, Instruction::CONST, 16u, 4000), - DEF_IPUT(4, Instruction::IPUT, 16u, 100u, 3u), - DEF_IPUT(4, Instruction::IPUT, 16u, 100u, 4u), - DEF_IGET(5, Instruction::IGET, 19u, 100u, 3u), // Differs from 11u and 14u... - DEF_IGET(5, Instruction::IGET, 20u, 100u, 4u), // and same as the CONST 16u. - - DEF_IGET(3, Instruction::IGET_SHORT, 21u, 100u, 5u), - DEF_IGET(3, Instruction::IGET_CHAR, 22u, 100u, 6u), - DEF_IPUT(4, Instruction::IPUT_SHORT, 23u, 100u, 7u), // Clobbers field #5, not #6. - DEF_IGET(5, Instruction::IGET_SHORT, 24u, 100u, 5u), // Differs from the top. - DEF_IGET(5, Instruction::IGET_CHAR, 25u, 100u, 6u), // Same as the top. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - EXPECT_NE(value_names_[3], value_names_[4]); - EXPECT_NE(value_names_[3], value_names_[6]); - EXPECT_NE(value_names_[4], value_names_[6]); - - EXPECT_NE(value_names_[7], value_names_[9]); - EXPECT_EQ(value_names_[9], value_names_[10]); - - EXPECT_NE(value_names_[14], value_names_[11]); - EXPECT_NE(value_names_[14], value_names_[16]); - EXPECT_EQ(value_names_[14], value_names_[15]); - EXPECT_NE(value_names_[19], value_names_[11]); - EXPECT_NE(value_names_[19], value_names_[14]); - EXPECT_EQ(value_names_[19], value_names_[16]); - EXPECT_EQ(value_names_[19], value_names_[20]); - - EXPECT_NE(value_names_[21], value_names_[24]); - EXPECT_EQ(value_names_[22], value_names_[25]); -} - -TEST_F(GlobalValueNumberingTestLoop, AliasingIFieldsTwoObjects) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessShort }, - { 4u, 1u, 4u, false, kDexMemAccessChar }, - { 5u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - { 6u, 1u, 6u, false, kDexMemAccessWord }, - { 7u, 1u, 7u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_IGET(3, Instruction::IGET, 0u, 100u, 0u), - DEF_IPUT(4, Instruction::IPUT, 1u, 101u, 0u), // May alias with the IGET at the top. - DEF_IGET(5, Instruction::IGET, 2u, 100u, 0u), // Differs from the top. - - DEF_IGET(3, Instruction::IGET, 3u, 100u, 1u), - DEF_IPUT(4, Instruction::IPUT, 3u, 101u, 1u), // If aliasing, stores the same value. - DEF_IGET(5, Instruction::IGET, 5u, 100u, 1u), // Same as the top. - - DEF_IGET(3, Instruction::IGET, 6u, 100u, 2u), - DEF_CONST(4, Instruction::CONST, 7u, 1000), - DEF_IPUT(4, Instruction::IPUT, 7u, 101u, 2u), - DEF_IGET(5, Instruction::IGET, 9u, 100u, 2u), // Differs from the top and the CONST. - - DEF_IGET(3, Instruction::IGET_SHORT, 10u, 100u, 3u), - DEF_IGET(3, Instruction::IGET_CHAR, 11u, 100u, 4u), - DEF_IPUT(4, Instruction::IPUT_SHORT, 12u, 101u, 5u), // Clobbers field #3, not #4. - DEF_IGET(5, Instruction::IGET_SHORT, 13u, 100u, 3u), // Differs from the top. - DEF_IGET(5, Instruction::IGET_CHAR, 14u, 100u, 4u), // Same as the top. - - DEF_CONST(3, Instruction::CONST, 15u, 3000), - DEF_IPUT(3, Instruction::IPUT, 15u, 100u, 6u), - DEF_IPUT(3, Instruction::IPUT, 15u, 100u, 7u), - DEF_IPUT(3, Instruction::IPUT, 15u, 101u, 6u), - DEF_IGET(4, Instruction::IGET, 19u, 100u, 6u), // Differs from CONSTs 15u and 22u. - DEF_IGET(4, Instruction::IGET, 20u, 100u, 7u), // Same value as 19u. - DEF_IGET(4, Instruction::IGET, 21u, 101u, 6u), // Same value as read from field #7. - DEF_CONST(4, Instruction::CONST, 22u, 3001), - DEF_IPUT(4, Instruction::IPUT, 22u, 100u, 6u), - DEF_IPUT(4, Instruction::IPUT, 22u, 100u, 7u), - DEF_IPUT(4, Instruction::IPUT, 22u, 101u, 6u), - DEF_IGET(5, Instruction::IGET, 26u, 100u, 6u), // Same as CONST 22u. - DEF_IGET(5, Instruction::IGET, 27u, 100u, 7u), // Same as CONST 22u. - DEF_IGET(5, Instruction::IGET, 28u, 101u, 6u), // Same as CONST 22u. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[2]); - - EXPECT_EQ(value_names_[3], value_names_[5]); - - EXPECT_NE(value_names_[6], value_names_[9]); - EXPECT_NE(value_names_[7], value_names_[9]); - - EXPECT_NE(value_names_[10], value_names_[13]); - EXPECT_EQ(value_names_[11], value_names_[14]); - - EXPECT_NE(value_names_[19], value_names_[15]); - EXPECT_NE(value_names_[19], value_names_[22]); - EXPECT_EQ(value_names_[22], value_names_[26]); - EXPECT_EQ(value_names_[22], value_names_[27]); - EXPECT_EQ(value_names_[22], value_names_[28]); -} - -TEST_F(GlobalValueNumberingTestLoop, IFieldToBaseDependency) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // For the IGET that loads sreg 3u using base 2u, the following IPUT creates a dependency - // from the field value to the base. However, this dependency does not result in an - // infinite loop since the merge of the field value for base 0u gets assigned a value name - // based only on the base 0u, not on the actual value, and breaks the dependency cycle. - DEF_IGET(3, Instruction::IGET, 0u, 100u, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_IGET(4, Instruction::IGET, 2u, 0u, 0u), - DEF_IGET(4, Instruction::IGET, 3u, 2u, 0u), - DEF_IPUT(4, Instruction::IPUT, 3u, 0u, 0u), - DEF_IGET(5, Instruction::IGET, 5u, 0u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[1], value_names_[2]); - EXPECT_EQ(value_names_[3], value_names_[5]); -} - -TEST_F(GlobalValueNumberingTestLoop, SFields) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_SGET(3, Instruction::SGET, 0u, 0u), - DEF_SGET(4, Instruction::SGET, 1u, 0u), // Same as at the top. - DEF_SGET(5, Instruction::SGET, 2u, 0u), // Same as at the top. - - DEF_SGET(3, Instruction::SGET, 3u, 1u), - DEF_SGET(4, Instruction::SGET, 4u, 1u), // Differs from top... - DEF_SPUT(4, Instruction::SPUT, 5u, 1u), // Because of this SPUT. - DEF_SGET(5, Instruction::SGET, 6u, 1u), // Differs from top and the loop SGET. - - DEF_SGET(3, Instruction::SGET, 7u, 2u), - DEF_SPUT(4, Instruction::SPUT, 8u, 2u), // Because of this SPUT... - DEF_SGET(4, Instruction::SGET, 9u, 2u), // Differs from top. - DEF_SGET(5, Instruction::SGET, 10u, 2u), // Differs from top but same as the loop SGET. - }; - - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - EXPECT_NE(value_names_[3], value_names_[4]); - EXPECT_NE(value_names_[3], value_names_[6]); - EXPECT_NE(value_names_[4], value_names_[5]); - - EXPECT_NE(value_names_[7], value_names_[9]); - EXPECT_EQ(value_names_[9], value_names_[10]); -} - -TEST_F(GlobalValueNumberingTestLoop, NonAliasingArrays) { - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 100u), - DEF_AGET(3, Instruction::AGET, 1u, 100u, 101u), - DEF_AGET(4, Instruction::AGET, 2u, 100u, 101u), // Same as at the top. - DEF_AGET(5, Instruction::AGET, 3u, 100u, 101u), // Same as at the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 200u), - DEF_AGET(3, Instruction::AGET, 5u, 200u, 201u), - DEF_AGET(4, Instruction::AGET, 6u, 200u, 201u), // Differs from top... - DEF_APUT(4, Instruction::APUT, 7u, 200u, 201u), // Because of this IPUT. - DEF_AGET(5, Instruction::AGET, 8u, 200u, 201u), // Differs from top and the loop AGET. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 300u), - DEF_AGET(3, Instruction::AGET, 10u, 300u, 301u), - DEF_APUT(4, Instruction::APUT, 11u, 300u, 301u), // Because of this IPUT... - DEF_AGET(4, Instruction::AGET, 12u, 300u, 301u), // Differs from top. - DEF_AGET(5, Instruction::AGET, 13u, 300u, 301u), // Differs from top but == the loop AGET. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 400u), - DEF_CONST(3, Instruction::CONST, 15u, 3000), - DEF_APUT(3, Instruction::APUT, 15u, 400u, 401u), - DEF_APUT(3, Instruction::APUT, 15u, 400u, 402u), - DEF_AGET(4, Instruction::AGET, 18u, 400u, 401u), // Differs from 15u and 20u. - DEF_AGET(4, Instruction::AGET, 19u, 400u, 402u), // Same as 18u. - DEF_CONST(4, Instruction::CONST, 20u, 4000), - DEF_APUT(4, Instruction::APUT, 20u, 400u, 401u), - DEF_APUT(4, Instruction::APUT, 20u, 400u, 402u), - DEF_AGET(5, Instruction::AGET, 23u, 400u, 401u), // Differs from 15u and 18u... - DEF_AGET(5, Instruction::AGET, 24u, 400u, 402u), // and same as the CONST 20u. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 500u), - DEF_AGET(3, Instruction::AGET, 26u, 500u, 501u), - DEF_APUT(4, Instruction::APUT, 27u, 500u, 502u), // Clobbers element at index 501u. - DEF_AGET(5, Instruction::AGET, 28u, 500u, 501u), // Differs from the top. - }; - - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[1], value_names_[2]); - EXPECT_EQ(value_names_[1], value_names_[3]); - - EXPECT_NE(value_names_[5], value_names_[6]); - EXPECT_NE(value_names_[5], value_names_[8]); - EXPECT_NE(value_names_[6], value_names_[8]); - - EXPECT_NE(value_names_[10], value_names_[12]); - EXPECT_EQ(value_names_[12], value_names_[13]); - - EXPECT_NE(value_names_[18], value_names_[15]); - EXPECT_NE(value_names_[18], value_names_[20]); - EXPECT_EQ(value_names_[18], value_names_[19]); - EXPECT_NE(value_names_[23], value_names_[15]); - EXPECT_NE(value_names_[23], value_names_[18]); - EXPECT_EQ(value_names_[23], value_names_[20]); - EXPECT_EQ(value_names_[23], value_names_[24]); - - EXPECT_NE(value_names_[26], value_names_[28]); -} - -TEST_F(GlobalValueNumberingTestLoop, AliasingArrays) { - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_AGET(3, Instruction::AGET_WIDE, 0u, 100u, 101u), - DEF_AGET(4, Instruction::AGET_WIDE, 2u, 100u, 101u), // Same as at the top. - DEF_AGET(5, Instruction::AGET_WIDE, 4u, 100u, 101u), // Same as at the top. - - DEF_AGET(3, Instruction::AGET_BYTE, 6u, 200u, 201u), - DEF_AGET(4, Instruction::AGET_BYTE, 7u, 200u, 201u), // Differs from top... - DEF_APUT(4, Instruction::APUT_BYTE, 8u, 200u, 201u), // Because of this IPUT. - DEF_AGET(5, Instruction::AGET_BYTE, 9u, 200u, 201u), // Differs from top and the loop AGET. - - DEF_AGET(3, Instruction::AGET, 10u, 300u, 301u), - DEF_APUT(4, Instruction::APUT, 11u, 300u, 301u), // Because of this IPUT... - DEF_AGET(4, Instruction::AGET, 12u, 300u, 301u), // Differs from top. - DEF_AGET(5, Instruction::AGET, 13u, 300u, 301u), // Differs from top but == the loop AGET. - - DEF_CONST(3, Instruction::CONST, 14u, 3000), - DEF_APUT(3, Instruction::APUT_CHAR, 14u, 400u, 401u), - DEF_APUT(3, Instruction::APUT_CHAR, 14u, 400u, 402u), - DEF_AGET(4, Instruction::AGET_CHAR, 15u, 400u, 401u), // Differs from 11u and 16u. - DEF_AGET(4, Instruction::AGET_CHAR, 16u, 400u, 402u), // Same as 14u. - DEF_CONST(4, Instruction::CONST, 17u, 4000), - DEF_APUT(4, Instruction::APUT_CHAR, 17u, 400u, 401u), - DEF_APUT(4, Instruction::APUT_CHAR, 17u, 400u, 402u), - DEF_AGET(5, Instruction::AGET_CHAR, 19u, 400u, 401u), // Differs from 11u and 14u... - DEF_AGET(5, Instruction::AGET_CHAR, 20u, 400u, 402u), // and same as the CONST 16u. - - DEF_AGET(3, Instruction::AGET_SHORT, 21u, 500u, 501u), - DEF_APUT(4, Instruction::APUT_SHORT, 22u, 500u, 502u), // Clobbers element at index 501u. - DEF_AGET(5, Instruction::AGET_SHORT, 23u, 500u, 501u), // Differs from the top. - - DEF_AGET(3, Instruction::AGET_OBJECT, 24u, 600u, 601u), - DEF_APUT(4, Instruction::APUT_OBJECT, 25u, 601u, 602u), // Clobbers 600u/601u. - DEF_AGET(5, Instruction::AGET_OBJECT, 26u, 600u, 601u), // Differs from the top. - - DEF_AGET(3, Instruction::AGET_BOOLEAN, 27u, 700u, 701u), - DEF_APUT(4, Instruction::APUT_BOOLEAN, 27u, 701u, 702u), // Storing the same value. - DEF_AGET(5, Instruction::AGET_BOOLEAN, 29u, 700u, 701u), // Differs from the top. - }; - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 0, 2, 4 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - EXPECT_NE(value_names_[3], value_names_[4]); - EXPECT_NE(value_names_[3], value_names_[6]); - EXPECT_NE(value_names_[4], value_names_[6]); - - EXPECT_NE(value_names_[7], value_names_[9]); - EXPECT_EQ(value_names_[9], value_names_[10]); - - EXPECT_NE(value_names_[14], value_names_[11]); - EXPECT_NE(value_names_[14], value_names_[16]); - EXPECT_EQ(value_names_[14], value_names_[15]); - EXPECT_NE(value_names_[19], value_names_[11]); - EXPECT_NE(value_names_[19], value_names_[14]); - EXPECT_EQ(value_names_[19], value_names_[16]); - EXPECT_EQ(value_names_[19], value_names_[20]); - - EXPECT_NE(value_names_[21], value_names_[23]); - - EXPECT_NE(value_names_[24], value_names_[26]); - - EXPECT_EQ(value_names_[27], value_names_[29]); -} - -TEST_F(GlobalValueNumberingTestLoop, Phi) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000), - DEF_PHI2(4, 1u, 0u, 6u), // Merge CONST 0u (1000) with the same. - DEF_PHI2(4, 2u, 0u, 7u), // Merge CONST 0u (1000) with the Phi itself. - DEF_PHI2(4, 3u, 0u, 8u), // Merge CONST 0u (1000) and CONST 4u (2000). - DEF_PHI2(4, 4u, 0u, 9u), // Merge CONST 0u (1000) and Phi 3u. - DEF_CONST(4, Instruction::CONST, 5u, 2000), - DEF_MOVE(4, Instruction::MOVE, 6u, 0u), - DEF_MOVE(4, Instruction::MOVE, 7u, 2u), - DEF_MOVE(4, Instruction::MOVE, 8u, 5u), - DEF_MOVE(4, Instruction::MOVE, 9u, 3u), - }; - - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[1], value_names_[0]); - EXPECT_EQ(value_names_[2], value_names_[0]); - - EXPECT_NE(value_names_[3], value_names_[0]); - EXPECT_NE(value_names_[3], value_names_[5]); - EXPECT_NE(value_names_[4], value_names_[0]); - EXPECT_NE(value_names_[4], value_names_[5]); - EXPECT_NE(value_names_[4], value_names_[3]); -} - -TEST_F(GlobalValueNumberingTestLoop, IFieldLoopVariable) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 0), - DEF_IPUT(3, Instruction::IPUT, 0u, 100u, 0u), - DEF_IGET(4, Instruction::IGET, 2u, 100u, 0u), - DEF_BINOP(4, Instruction::ADD_INT, 3u, 2u, 101u), - DEF_IPUT(4, Instruction::IPUT, 3u, 100u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[2], value_names_[0]); - EXPECT_NE(value_names_[3], value_names_[0]); - EXPECT_NE(value_names_[3], value_names_[2]); - - - // Set up vreg_to_ssa_map_exit for prologue and loop and set post-processing mode - // as needed for GetStartingVregValueNumber(). - const int32_t prologue_vreg_to_ssa_map_exit[] = { 0 }; - const int32_t loop_vreg_to_ssa_map_exit[] = { 3 }; - PrepareVregToSsaMapExit(3, prologue_vreg_to_ssa_map_exit); - PrepareVregToSsaMapExit(4, loop_vreg_to_ssa_map_exit); - gvn_->StartPostProcessing(); - - // Check that vreg 0 has the same value number as the result of IGET 2u. - const LocalValueNumbering* loop = gvn_->GetLvn(4); - EXPECT_EQ(value_names_[2], loop->GetStartingVregValueNumber(0)); -} - -TEST_F(GlobalValueNumberingTestCatch, IFields) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 200u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 201u), - DEF_IGET(3, Instruction::IGET, 2u, 100u, 0u), - DEF_IGET(3, Instruction::IGET, 3u, 200u, 0u), - DEF_IGET(3, Instruction::IGET, 4u, 201u, 0u), - DEF_INVOKE1(4, Instruction::INVOKE_STATIC, 201u), // Clobbering catch, 201u escapes. - DEF_IGET(4, Instruction::IGET, 6u, 100u, 0u), // Differs from IGET 2u. - DEF_IPUT(4, Instruction::IPUT, 6u, 100u, 1u), - DEF_IPUT(4, Instruction::IPUT, 6u, 101u, 0u), - DEF_IPUT(4, Instruction::IPUT, 6u, 200u, 0u), - DEF_IGET(5, Instruction::IGET, 10u, 100u, 0u), // Differs from IGETs 2u and 6u. - DEF_IGET(5, Instruction::IGET, 11u, 200u, 0u), // Same as the top. - DEF_IGET(5, Instruction::IGET, 12u, 201u, 0u), // Differs from the top, 201u escaped. - DEF_IPUT(5, Instruction::IPUT, 10u, 100u, 1u), - DEF_IPUT(5, Instruction::IPUT, 10u, 101u, 0u), - DEF_IPUT(5, Instruction::IPUT, 10u, 200u, 0u), - DEF_IGET(6, Instruction::IGET, 16u, 100u, 0u), // Differs from IGETs 2u, 6u and 10u. - DEF_IGET(6, Instruction::IGET, 17u, 100u, 1u), // Same as IGET 16u. - DEF_IGET(6, Instruction::IGET, 18u, 101u, 0u), // Same as IGET 16u. - DEF_IGET(6, Instruction::IGET, 19u, 200u, 0u), // Same as IGET 16u. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[2], value_names_[6]); - EXPECT_NE(value_names_[2], value_names_[10]); - EXPECT_NE(value_names_[6], value_names_[10]); - EXPECT_EQ(value_names_[3], value_names_[11]); - EXPECT_NE(value_names_[4], value_names_[12]); - - EXPECT_NE(value_names_[2], value_names_[16]); - EXPECT_NE(value_names_[6], value_names_[16]); - EXPECT_NE(value_names_[10], value_names_[16]); - EXPECT_EQ(value_names_[16], value_names_[17]); - EXPECT_EQ(value_names_[16], value_names_[18]); - EXPECT_EQ(value_names_[16], value_names_[19]); -} - -TEST_F(GlobalValueNumberingTestCatch, SFields) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_SGET(3, Instruction::SGET, 0u, 0u), - DEF_INVOKE1(4, Instruction::INVOKE_STATIC, 100u), // Clobbering catch. - DEF_SGET(4, Instruction::SGET, 2u, 0u), // Differs from SGET 0u. - DEF_SPUT(4, Instruction::SPUT, 2u, 1u), - DEF_SGET(5, Instruction::SGET, 4u, 0u), // Differs from SGETs 0u and 2u. - DEF_SPUT(5, Instruction::SPUT, 4u, 1u), - DEF_SGET(6, Instruction::SGET, 6u, 0u), // Differs from SGETs 0u, 2u and 4u. - DEF_SGET(6, Instruction::SGET, 7u, 1u), // Same as field #1. - }; - - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_NE(value_names_[0], value_names_[4]); - EXPECT_NE(value_names_[2], value_names_[4]); - EXPECT_NE(value_names_[0], value_names_[6]); - EXPECT_NE(value_names_[2], value_names_[6]); - EXPECT_NE(value_names_[4], value_names_[6]); - EXPECT_EQ(value_names_[6], value_names_[7]); -} - -TEST_F(GlobalValueNumberingTestCatch, Arrays) { - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 200u), - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 201u), - DEF_AGET(3, Instruction::AGET, 2u, 100u, 101u), - DEF_AGET(3, Instruction::AGET, 3u, 200u, 202u), - DEF_AGET(3, Instruction::AGET, 4u, 200u, 203u), - DEF_AGET(3, Instruction::AGET, 5u, 201u, 202u), - DEF_AGET(3, Instruction::AGET, 6u, 201u, 203u), - DEF_INVOKE1(4, Instruction::INVOKE_STATIC, 201u), // Clobbering catch, 201u escapes. - DEF_AGET(4, Instruction::AGET, 8u, 100u, 101u), // Differs from AGET 2u. - DEF_APUT(4, Instruction::APUT, 8u, 100u, 102u), - DEF_APUT(4, Instruction::APUT, 8u, 200u, 202u), - DEF_APUT(4, Instruction::APUT, 8u, 200u, 203u), - DEF_APUT(4, Instruction::APUT, 8u, 201u, 202u), - DEF_APUT(4, Instruction::APUT, 8u, 201u, 203u), - DEF_AGET(5, Instruction::AGET, 14u, 100u, 101u), // Differs from AGETs 2u and 8u. - DEF_AGET(5, Instruction::AGET, 15u, 200u, 202u), // Same as AGET 3u. - DEF_AGET(5, Instruction::AGET, 16u, 200u, 203u), // Same as AGET 4u. - DEF_AGET(5, Instruction::AGET, 17u, 201u, 202u), // Differs from AGET 5u. - DEF_AGET(5, Instruction::AGET, 18u, 201u, 203u), // Differs from AGET 6u. - DEF_APUT(5, Instruction::APUT, 14u, 100u, 102u), - DEF_APUT(5, Instruction::APUT, 14u, 200u, 202u), - DEF_APUT(5, Instruction::APUT, 14u, 200u, 203u), - DEF_APUT(5, Instruction::APUT, 14u, 201u, 202u), - DEF_APUT(5, Instruction::APUT, 14u, 201u, 203u), - DEF_AGET(6, Instruction::AGET, 24u, 100u, 101u), // Differs from AGETs 2u, 8u and 14u. - DEF_AGET(6, Instruction::AGET, 25u, 100u, 101u), // Same as AGET 24u. - DEF_AGET(6, Instruction::AGET, 26u, 200u, 202u), // Same as AGET 24u. - DEF_AGET(6, Instruction::AGET, 27u, 200u, 203u), // Same as AGET 24u. - DEF_AGET(6, Instruction::AGET, 28u, 201u, 202u), // Same as AGET 24u. - DEF_AGET(6, Instruction::AGET, 29u, 201u, 203u), // Same as AGET 24u. - }; - - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[2], value_names_[8]); - EXPECT_NE(value_names_[2], value_names_[14]); - EXPECT_NE(value_names_[8], value_names_[14]); - EXPECT_EQ(value_names_[3], value_names_[15]); - EXPECT_EQ(value_names_[4], value_names_[16]); - EXPECT_NE(value_names_[5], value_names_[17]); - EXPECT_NE(value_names_[6], value_names_[18]); - EXPECT_NE(value_names_[2], value_names_[24]); - EXPECT_NE(value_names_[8], value_names_[24]); - EXPECT_NE(value_names_[14], value_names_[24]); - EXPECT_EQ(value_names_[24], value_names_[25]); - EXPECT_EQ(value_names_[24], value_names_[26]); - EXPECT_EQ(value_names_[24], value_names_[27]); - EXPECT_EQ(value_names_[24], value_names_[28]); - EXPECT_EQ(value_names_[24], value_names_[29]); -} - -TEST_F(GlobalValueNumberingTestCatch, Phi) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000), - DEF_CONST(3, Instruction::CONST, 1u, 2000), - DEF_MOVE(3, Instruction::MOVE, 2u, 1u), - DEF_INVOKE1(4, Instruction::INVOKE_STATIC, 100u), // Clobbering catch. - DEF_CONST(5, Instruction::CONST, 4u, 1000), - DEF_CONST(5, Instruction::CONST, 5u, 3000), - DEF_MOVE(5, Instruction::MOVE, 6u, 5u), - DEF_PHI2(6, 7u, 0u, 4u), - DEF_PHI2(6, 8u, 0u, 5u), - DEF_PHI2(6, 9u, 0u, 6u), - DEF_PHI2(6, 10u, 1u, 4u), - DEF_PHI2(6, 11u, 1u, 5u), - DEF_PHI2(6, 12u, 1u, 6u), - DEF_PHI2(6, 13u, 2u, 4u), - DEF_PHI2(6, 14u, 2u, 5u), - DEF_PHI2(6, 15u, 2u, 6u), - }; - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - ASSERT_EQ(value_names_[4], value_names_[0]); // Both CONSTs are 1000. - EXPECT_EQ(value_names_[7], value_names_[0]); // Merging CONST 0u and CONST 4u, both 1000. - EXPECT_NE(value_names_[8], value_names_[0]); - EXPECT_NE(value_names_[8], value_names_[5]); - EXPECT_EQ(value_names_[9], value_names_[8]); - EXPECT_NE(value_names_[10], value_names_[1]); - EXPECT_NE(value_names_[10], value_names_[4]); - EXPECT_NE(value_names_[10], value_names_[8]); - EXPECT_NE(value_names_[11], value_names_[1]); - EXPECT_NE(value_names_[11], value_names_[5]); - EXPECT_NE(value_names_[11], value_names_[8]); - EXPECT_NE(value_names_[11], value_names_[10]); - EXPECT_EQ(value_names_[12], value_names_[11]); - EXPECT_EQ(value_names_[13], value_names_[10]); - EXPECT_EQ(value_names_[14], value_names_[11]); - EXPECT_EQ(value_names_[15], value_names_[11]); -} - -TEST_F(GlobalValueNumberingTest, NullCheckIFields) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, // Object. - { 1u, 1u, 1u, false, kDexMemAccessObject }, // Object. - }; - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // 4 is fall-through, 5 is taken. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(3, 4)), - }; - static const MIRDef mirs[] = { - DEF_IGET(3, Instruction::IGET_OBJECT, 0u, 100u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 100u, 1u), - DEF_IGET(3, Instruction::IGET_OBJECT, 2u, 101u, 0u), - DEF_IFZ(3, Instruction::IF_NEZ, 0u), // Null-check for field #0 for taken. - DEF_UNIQUE_REF(4, Instruction::NEW_ARRAY, 4u), - DEF_IPUT(4, Instruction::IPUT_OBJECT, 4u, 100u, 0u), - DEF_IPUT(4, Instruction::IPUT_OBJECT, 4u, 100u, 1u), - DEF_IPUT(4, Instruction::IPUT_OBJECT, 4u, 101u, 0u), - DEF_IGET(5, Instruction::IGET_OBJECT, 8u, 100u, 0u), // 100u/#0, IF_NEZ/NEW_ARRAY. - DEF_IGET(5, Instruction::IGET_OBJECT, 9u, 100u, 1u), // 100u/#1, -/NEW_ARRAY. - DEF_IGET(5, Instruction::IGET_OBJECT, 10u, 101u, 0u), // 101u/#0, -/NEW_ARRAY. - DEF_CONST(5, Instruction::CONST, 11u, 0), - DEF_AGET(5, Instruction::AGET, 12u, 8u, 11u), // Null-check eliminated. - DEF_AGET(5, Instruction::AGET, 13u, 9u, 11u), // Null-check kept. - DEF_AGET(5, Instruction::AGET, 14u, 10u, 11u), // Null-check kept. - }; - static const bool expected_ignore_null_check[] = { - false, true, false, false, // BB #3; unimportant. - false, true, true, true, // BB #4; unimportant. - true, true, true, false, true, false, false, // BB #5; only the last three are important. - }; - - PrepareIFields(ifields); - PrepareBasicBlocks(bbs); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(GlobalValueNumberingTest, NullCheckSFields) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - { 1u, 1u, 1u, false, kDexMemAccessObject }, - }; - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // 4 is fall-through, 5 is taken. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(3, 4)), - }; - static const MIRDef mirs[] = { - DEF_SGET(3, Instruction::SGET_OBJECT, 0u, 0u), - DEF_SGET(3, Instruction::SGET_OBJECT, 1u, 1u), - DEF_IFZ(3, Instruction::IF_NEZ, 0u), // Null-check for field #0 for taken. - DEF_UNIQUE_REF(4, Instruction::NEW_ARRAY, 3u), - DEF_SPUT(4, Instruction::SPUT_OBJECT, 3u, 0u), - DEF_SPUT(4, Instruction::SPUT_OBJECT, 3u, 1u), - DEF_SGET(5, Instruction::SGET_OBJECT, 6u, 0u), // Field #0 is null-checked, IF_NEZ/NEW_ARRAY. - DEF_SGET(5, Instruction::SGET_OBJECT, 7u, 1u), // Field #1 is not null-checked, -/NEW_ARRAY. - DEF_CONST(5, Instruction::CONST, 8u, 0), - DEF_AGET(5, Instruction::AGET, 9u, 6u, 8u), // Null-check eliminated. - DEF_AGET(5, Instruction::AGET, 10u, 7u, 8u), // Null-check kept. - }; - static const bool expected_ignore_null_check[] = { - false, false, false, false, false, false, false, false, false, true, false - }; - - PrepareSFields(sfields); - PrepareBasicBlocks(bbs); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(GlobalValueNumberingTest, NullCheckArrays) { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // 4 is fall-through, 5 is taken. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(3, 4)), - }; - static const MIRDef mirs[] = { - DEF_AGET(3, Instruction::AGET_OBJECT, 0u, 100u, 102u), - DEF_AGET(3, Instruction::AGET_OBJECT, 1u, 100u, 103u), - DEF_AGET(3, Instruction::AGET_OBJECT, 2u, 101u, 102u), - DEF_IFZ(3, Instruction::IF_NEZ, 0u), // Null-check for field #0 for taken. - DEF_UNIQUE_REF(4, Instruction::NEW_ARRAY, 4u), - DEF_APUT(4, Instruction::APUT_OBJECT, 4u, 100u, 102u), - DEF_APUT(4, Instruction::APUT_OBJECT, 4u, 100u, 103u), - DEF_APUT(4, Instruction::APUT_OBJECT, 4u, 101u, 102u), - DEF_AGET(5, Instruction::AGET_OBJECT, 8u, 100u, 102u), // Null-checked, IF_NEZ/NEW_ARRAY. - DEF_AGET(5, Instruction::AGET_OBJECT, 9u, 100u, 103u), // Not null-checked, -/NEW_ARRAY. - DEF_AGET(5, Instruction::AGET_OBJECT, 10u, 101u, 102u), // Not null-checked, -/NEW_ARRAY. - DEF_CONST(5, Instruction::CONST, 11u, 0), - DEF_AGET(5, Instruction::AGET, 12u, 8u, 11u), // Null-check eliminated. - DEF_AGET(5, Instruction::AGET, 13u, 9u, 11u), // Null-check kept. - DEF_AGET(5, Instruction::AGET, 14u, 10u, 11u), // Null-check kept. - }; - static const bool expected_ignore_null_check[] = { - false, true, false, false, // BB #3; unimportant. - false, true, true, true, // BB #4; unimportant. - true, true, true, false, true, false, false, // BB #5; only the last three are important. - }; - - PrepareBasicBlocks(bbs); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(GlobalValueNumberingTestDiamond, RangeCheckArrays) { - // NOTE: We don't merge range checks when we merge value names for Phis or memory locations. - static const MIRDef mirs[] = { - DEF_AGET(4, Instruction::AGET, 0u, 100u, 101u), - DEF_AGET(5, Instruction::AGET, 1u, 100u, 101u), - DEF_APUT(6, Instruction::APUT, 2u, 100u, 101u), - - DEF_AGET(4, Instruction::AGET, 3u, 200u, 201u), - DEF_AGET(5, Instruction::AGET, 4u, 200u, 202u), - DEF_APUT(6, Instruction::APUT, 5u, 200u, 201u), - - DEF_AGET(4, Instruction::AGET, 6u, 300u, 302u), - DEF_AGET(5, Instruction::AGET, 7u, 301u, 302u), - DEF_APUT(6, Instruction::APUT, 8u, 300u, 302u), - }; - static const bool expected_ignore_null_check[] = { - false, false, true, - false, false, true, - false, false, false, - }; - static const bool expected_ignore_range_check[] = { - false, false, true, - false, false, false, - false, false, false, - }; - - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - ASSERT_EQ(arraysize(expected_ignore_range_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - EXPECT_EQ(expected_ignore_range_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0) << i; - } -} - -TEST_F(GlobalValueNumberingTestDiamond, MergeSameValueInDifferentMemoryLocations) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 100u), - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 200u), - DEF_CONST(4, Instruction::CONST, 2u, 1000), - DEF_IPUT(4, Instruction::IPUT, 2u, 100u, 0u), - DEF_IPUT(4, Instruction::IPUT, 2u, 100u, 1u), - DEF_IPUT(4, Instruction::IPUT, 2u, 101u, 0u), - DEF_APUT(4, Instruction::APUT, 2u, 200u, 202u), - DEF_APUT(4, Instruction::APUT, 2u, 200u, 203u), - DEF_APUT(4, Instruction::APUT, 2u, 201u, 202u), - DEF_APUT(4, Instruction::APUT, 2u, 201u, 203u), - DEF_SPUT(4, Instruction::SPUT, 2u, 0u), - DEF_SPUT(4, Instruction::SPUT, 2u, 1u), - DEF_CONST(5, Instruction::CONST, 12u, 2000), - DEF_IPUT(5, Instruction::IPUT, 12u, 100u, 0u), - DEF_IPUT(5, Instruction::IPUT, 12u, 100u, 1u), - DEF_IPUT(5, Instruction::IPUT, 12u, 101u, 0u), - DEF_APUT(5, Instruction::APUT, 12u, 200u, 202u), - DEF_APUT(5, Instruction::APUT, 12u, 200u, 203u), - DEF_APUT(5, Instruction::APUT, 12u, 201u, 202u), - DEF_APUT(5, Instruction::APUT, 12u, 201u, 203u), - DEF_SPUT(5, Instruction::SPUT, 12u, 0u), - DEF_SPUT(5, Instruction::SPUT, 12u, 1u), - DEF_PHI2(6, 22u, 2u, 12u), - DEF_IGET(6, Instruction::IGET, 23u, 100u, 0u), - DEF_IGET(6, Instruction::IGET, 24u, 100u, 1u), - DEF_IGET(6, Instruction::IGET, 25u, 101u, 0u), - DEF_AGET(6, Instruction::AGET, 26u, 200u, 202u), - DEF_AGET(6, Instruction::AGET, 27u, 200u, 203u), - DEF_AGET(6, Instruction::AGET, 28u, 201u, 202u), - DEF_AGET(6, Instruction::AGET, 29u, 201u, 203u), - DEF_SGET(6, Instruction::SGET, 30u, 0u), - DEF_SGET(6, Instruction::SGET, 31u, 1u), - }; - PrepareIFields(ifields); - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[2], value_names_[12]); - EXPECT_NE(value_names_[2], value_names_[22]); - EXPECT_NE(value_names_[12], value_names_[22]); - for (size_t i = 23; i != arraysize(mirs); ++i) { - EXPECT_EQ(value_names_[22], value_names_[i]) << i; - } -} - -TEST_F(GlobalValueNumberingTest, InfiniteLocationLoop) { - // This is a pattern that lead to an infinite loop during the GVN development. This has been - // fixed by rewriting the merging of AliasingValues to merge only locations read from or - // written to in each incoming LVN rather than merging all locations read from or written to - // in any incoming LVN. It also showed up only when the GVN used the DFS ordering instead of - // the "topological" ordering but, since the "topological" ordering is not really topological - // when there are cycles and an optimizing Java compiler (or a tool like proguard) could - // theoretically create any sort of flow graph, this could have shown up in real code. - // - // While we were merging all the locations: - // The first time the Phi evaluates to the same value name as CONST 0u. After the second - // evaluation, when the BB #9 has been processed, the Phi receives its own value name. - // However, the index from the first evaluation keeps disappearing and reappearing in the - // LVN's aliasing_array_value_map_'s load_value_map for BBs #9, #4, #5, #7 because of the - // DFS ordering of LVN evaluation. - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - }; - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(4)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 2), DEF_PRED2(3, 9)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED1(4)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(9), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(9), DEF_PRED1(7)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED3(6, 7, 8)), - }; - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 0), - DEF_PHI2(4, 1u, 0u, 10u), - DEF_INVOKE1(6, Instruction::INVOKE_STATIC, 100u), - DEF_IGET(6, Instruction::IGET_OBJECT, 3u, 100u, 0u), - DEF_CONST(6, Instruction::CONST, 4u, 1000), - DEF_APUT(6, Instruction::APUT, 4u, 3u, 1u), // Index is Phi 1u. - DEF_INVOKE1(8, Instruction::INVOKE_STATIC, 100u), - DEF_IGET(8, Instruction::IGET_OBJECT, 7u, 100u, 0u), - DEF_CONST(8, Instruction::CONST, 8u, 2000), - DEF_APUT(8, Instruction::APUT, 9u, 7u, 1u), // Index is Phi 1u. - DEF_CONST(9, Instruction::CONST, 10u, 3000), - }; - PrepareIFields(ifields); - PrepareBasicBlocks(bbs); - PrepareMIRs(mirs); - // Using DFS order for this test. The GVN result should not depend on the used ordering - // once the GVN actually converges. But creating a test for this convergence issue with - // the topological ordering could be a very challenging task. - PerformPreOrderDfsGVN(); -} - -TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, IFieldAndPhi) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - }; - static const MIRDef mirs[] = { - DEF_MOVE(3, Instruction::MOVE_OBJECT, 0u, 100u), - DEF_IPUT(3, Instruction::IPUT_OBJECT, 0u, 200u, 0u), - DEF_PHI2(4, 2u, 0u, 3u), - DEF_MOVE(5, Instruction::MOVE_OBJECT, 3u, 300u), - DEF_IPUT(5, Instruction::IPUT_OBJECT, 3u, 200u, 0u), - DEF_MOVE(6, Instruction::MOVE_OBJECT, 5u, 2u), - DEF_IGET(6, Instruction::IGET_OBJECT, 6u, 200u, 0u), - DEF_MOVE(7, Instruction::MOVE_OBJECT, 7u, 5u), - DEF_IGET(7, Instruction::IGET_OBJECT, 8u, 200u, 0u), - DEF_MOVE(8, Instruction::MOVE_OBJECT, 9u, 5u), - DEF_IGET(8, Instruction::IGET_OBJECT, 10u, 200u, 0u), - DEF_MOVE(9, Instruction::MOVE_OBJECT, 11u, 5u), - DEF_IGET(9, Instruction::IGET_OBJECT, 12u, 200u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[3]); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_NE(value_names_[3], value_names_[2]); - EXPECT_EQ(value_names_[2], value_names_[5]); - EXPECT_EQ(value_names_[5], value_names_[6]); - EXPECT_EQ(value_names_[5], value_names_[7]); - EXPECT_EQ(value_names_[5], value_names_[8]); - EXPECT_EQ(value_names_[5], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[5], value_names_[11]); - EXPECT_EQ(value_names_[5], value_names_[12]); -} - -TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, NullCheck) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - }; - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - }; - static const MIRDef mirs[] = { - DEF_MOVE(3, Instruction::MOVE_OBJECT, 0u, 100u), - DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 200u, 0u), - DEF_SGET(3, Instruction::SGET_OBJECT, 2u, 0u), - DEF_AGET(3, Instruction::AGET_OBJECT, 3u, 300u, 201u), - DEF_PHI2(4, 4u, 0u, 8u), - DEF_IGET(5, Instruction::IGET_OBJECT, 5u, 200u, 0u), - DEF_SGET(5, Instruction::SGET_OBJECT, 6u, 0u), - DEF_AGET(5, Instruction::AGET_OBJECT, 7u, 300u, 201u), - DEF_MOVE(5, Instruction::MOVE_OBJECT, 8u, 400u), - DEF_IPUT(5, Instruction::IPUT_OBJECT, 4u, 200u, 0u), // PUT the Phi 4u. - DEF_SPUT(5, Instruction::SPUT_OBJECT, 4u, 0u), // PUT the Phi 4u. - DEF_APUT(5, Instruction::APUT_OBJECT, 4u, 300u, 201u), // PUT the Phi 4u. - DEF_MOVE(6, Instruction::MOVE_OBJECT, 12u, 4u), - DEF_IGET(6, Instruction::IGET_OBJECT, 13u, 200u, 0u), - DEF_SGET(6, Instruction::SGET_OBJECT, 14u, 0u), - DEF_AGET(6, Instruction::AGET_OBJECT, 15u, 300u, 201u), - DEF_AGET(6, Instruction::AGET_OBJECT, 16u, 12u, 600u), - DEF_AGET(6, Instruction::AGET_OBJECT, 17u, 13u, 600u), - DEF_AGET(6, Instruction::AGET_OBJECT, 18u, 14u, 600u), - DEF_AGET(6, Instruction::AGET_OBJECT, 19u, 15u, 600u), - DEF_MOVE(8, Instruction::MOVE_OBJECT, 20u, 12u), - DEF_IGET(8, Instruction::IGET_OBJECT, 21u, 200u, 0u), - DEF_SGET(8, Instruction::SGET_OBJECT, 22u, 0u), - DEF_AGET(8, Instruction::AGET_OBJECT, 23u, 300u, 201u), - DEF_AGET(8, Instruction::AGET_OBJECT, 24u, 12u, 600u), - DEF_AGET(8, Instruction::AGET_OBJECT, 25u, 13u, 600u), - DEF_AGET(8, Instruction::AGET_OBJECT, 26u, 14u, 600u), - DEF_AGET(8, Instruction::AGET_OBJECT, 27u, 15u, 600u), - DEF_MOVE(9, Instruction::MOVE_OBJECT, 28u, 12u), - DEF_IGET(9, Instruction::IGET_OBJECT, 29u, 200u, 0u), - DEF_SGET(9, Instruction::SGET_OBJECT, 30u, 0u), - DEF_AGET(9, Instruction::AGET_OBJECT, 31u, 300u, 201u), - DEF_AGET(9, Instruction::AGET_OBJECT, 32u, 12u, 600u), - DEF_AGET(9, Instruction::AGET_OBJECT, 33u, 13u, 600u), - DEF_AGET(9, Instruction::AGET_OBJECT, 34u, 14u, 600u), - DEF_AGET(9, Instruction::AGET_OBJECT, 35u, 15u, 600u), - }; - static const bool expected_ignore_null_check[] = { - false, false, false, false, // BB #3. - false, true, false, true, false, true, false, true, // BBs #4 and #5. - false, true, false, true, false, false, false, false, // BB #6. - false, true, false, true, true, true, true, true, // BB #7. - false, true, false, true, true, true, true, true, // BB #8. - }; - static const bool expected_ignore_range_check[] = { - false, false, false, false, // BB #3. - false, false, false, true, false, false, false, true, // BBs #4 and #5. - false, false, false, true, false, false, false, false, // BB #6. - false, false, false, true, true, true, true, true, // BB #7. - false, false, false, true, true, true, true, true, // BB #8. - }; - - PrepareIFields(ifields); - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[4]); - EXPECT_NE(value_names_[1], value_names_[5]); - EXPECT_NE(value_names_[2], value_names_[6]); - EXPECT_NE(value_names_[3], value_names_[7]); - EXPECT_NE(value_names_[4], value_names_[8]); - EXPECT_EQ(value_names_[4], value_names_[12]); - EXPECT_EQ(value_names_[5], value_names_[13]); - EXPECT_EQ(value_names_[6], value_names_[14]); - EXPECT_EQ(value_names_[7], value_names_[15]); - EXPECT_EQ(value_names_[12], value_names_[20]); - EXPECT_EQ(value_names_[13], value_names_[21]); - EXPECT_EQ(value_names_[14], value_names_[22]); - EXPECT_EQ(value_names_[15], value_names_[23]); - EXPECT_EQ(value_names_[12], value_names_[28]); - EXPECT_EQ(value_names_[13], value_names_[29]); - EXPECT_EQ(value_names_[14], value_names_[30]); - EXPECT_EQ(value_names_[15], value_names_[31]); - PerformGVNCodeModifications(); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - EXPECT_EQ(expected_ignore_range_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0) << i; - } -} - -TEST_F(GlobalValueNumberingTestTwoNestedLoops, IFieldAndPhi) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - }; - static const MIRDef mirs[] = { - DEF_MOVE(3, Instruction::MOVE_OBJECT, 0u, 100u), - DEF_IPUT(3, Instruction::IPUT_OBJECT, 0u, 200u, 0u), - DEF_PHI2(4, 2u, 0u, 11u), - DEF_MOVE(4, Instruction::MOVE_OBJECT, 3u, 2u), - DEF_IGET(4, Instruction::IGET_OBJECT, 4u, 200u, 0u), - DEF_MOVE(5, Instruction::MOVE_OBJECT, 5u, 3u), - DEF_IGET(5, Instruction::IGET_OBJECT, 6u, 200u, 0u), - DEF_MOVE(6, Instruction::MOVE_OBJECT, 7u, 3u), - DEF_IGET(6, Instruction::IGET_OBJECT, 8u, 200u, 0u), - DEF_MOVE(7, Instruction::MOVE_OBJECT, 9u, 3u), - DEF_IGET(7, Instruction::IGET_OBJECT, 10u, 200u, 0u), - DEF_MOVE(7, Instruction::MOVE_OBJECT, 11u, 300u), - DEF_IPUT(7, Instruction::IPUT_OBJECT, 11u, 200u, 0u), - DEF_MOVE(8, Instruction::MOVE_OBJECT, 13u, 3u), - DEF_IGET(8, Instruction::IGET_OBJECT, 14u, 200u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[11]); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_NE(value_names_[11], value_names_[2]); - EXPECT_EQ(value_names_[2], value_names_[3]); - EXPECT_EQ(value_names_[3], value_names_[4]); - EXPECT_EQ(value_names_[3], value_names_[5]); - EXPECT_EQ(value_names_[3], value_names_[6]); - EXPECT_EQ(value_names_[3], value_names_[7]); - EXPECT_EQ(value_names_[3], value_names_[8]); - EXPECT_EQ(value_names_[3], value_names_[9]); - EXPECT_EQ(value_names_[3], value_names_[10]); - EXPECT_EQ(value_names_[3], value_names_[13]); - EXPECT_EQ(value_names_[3], value_names_[14]); -} - -TEST_F(GlobalValueNumberingTest, NormalPathToCatchEntry) { - // When there's an empty catch block, all the exception paths lead to the next block in - // the normal path and we can also have normal "taken" or "fall-through" branches to that - // path. Check that LocalValueNumbering::PruneNonAliasingRefsForCatch() can handle it. - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(3, 4)), - }; - static const MIRDef mirs[] = { - DEF_INVOKE1(4, Instruction::INVOKE_STATIC, 100u), - }; - PrepareBasicBlocks(bbs); - BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u); - catch_handler->catch_entry = true; - // Add successor block info to the check block. - BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u); - check_bb->successor_block_list_type = kCatch; - SuccessorBlockInfo* successor_block_info = reinterpret_cast - (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); - successor_block_info->block = catch_handler->id; - check_bb->successor_blocks.push_back(successor_block_info); - BasicBlock* merge_block = cu_.mir_graph->GetBasicBlock(4u); - std::swap(merge_block->taken, merge_block->fall_through); - PrepareMIRs(mirs); - PerformGVN(); -} - -TEST_F(GlobalValueNumberingTestDiamond, DivZeroCheckDiamond) { - static const MIRDef mirs[] = { - DEF_BINOP(3u, Instruction::DIV_INT, 1u, 20u, 21u), - DEF_BINOP(3u, Instruction::DIV_INT, 2u, 24u, 21u), - DEF_BINOP(3u, Instruction::DIV_INT, 3u, 20u, 23u), - DEF_BINOP(4u, Instruction::DIV_INT, 4u, 24u, 22u), - DEF_BINOP(4u, Instruction::DIV_INT, 9u, 24u, 25u), - DEF_BINOP(5u, Instruction::DIV_INT, 5u, 24u, 21u), - DEF_BINOP(5u, Instruction::DIV_INT, 10u, 24u, 26u), - DEF_PHI2(6u, 27u, 25u, 26u), - DEF_BINOP(6u, Instruction::DIV_INT, 12u, 20u, 27u), - DEF_BINOP(6u, Instruction::DIV_INT, 6u, 24u, 21u), - DEF_BINOP(6u, Instruction::DIV_INT, 7u, 20u, 23u), - DEF_BINOP(6u, Instruction::DIV_INT, 8u, 20u, 22u), - }; - - static const bool expected_ignore_div_zero_check[] = { - false, // New divisor seen. - true, // Eliminated since it has first divisor as first one. - false, // New divisor seen. - false, // New divisor seen. - false, // New divisor seen. - true, // Eliminated in dominating block. - false, // New divisor seen. - false, // Phi node. - true, // Eliminated on both sides of diamond and merged via phi. - true, // Eliminated in dominating block. - true, // Eliminated in dominating block. - false, // Only eliminated on one path of diamond. - }; - - PrepareMIRs(mirs); - PerformGVN(); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_div_zero_check), mir_count_); - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = expected_ignore_div_zero_check[i] ? MIR_IGNORE_DIV_ZERO_CHECK : 0u; - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(GlobalValueNumberingTestDiamond, CheckCastDiamond) { - static const MIRDef mirs[] = { - DEF_UNOP(3u, Instruction::INSTANCE_OF, 0u, 100u), - DEF_UNOP(3u, Instruction::INSTANCE_OF, 1u, 200u), - DEF_IFZ(3u, Instruction::IF_NEZ, 0u), - DEF_INVOKE1(4u, Instruction::CHECK_CAST, 100u), - DEF_INVOKE1(5u, Instruction::CHECK_CAST, 100u), - DEF_INVOKE1(5u, Instruction::CHECK_CAST, 200u), - DEF_INVOKE1(5u, Instruction::CHECK_CAST, 100u), - DEF_INVOKE1(6u, Instruction::CHECK_CAST, 100u), - }; - - static const bool expected_ignore_check_cast[] = { - false, // instance-of - false, // instance-of - false, // if-nez - false, // Not eliminated, fall-through branch. - true, // Eliminated. - false, // Not eliminated, different value. - false, // Not eliminated, different type. - false, // Not eliminated, bottom block. - }; - - PrepareMIRs(mirs); - mirs_[0].dalvikInsn.vC = 1234; // type for instance-of - mirs_[1].dalvikInsn.vC = 1234; // type for instance-of - mirs_[3].dalvikInsn.vB = 1234; // type for check-cast - mirs_[4].dalvikInsn.vB = 1234; // type for check-cast - mirs_[5].dalvikInsn.vB = 1234; // type for check-cast - mirs_[6].dalvikInsn.vB = 4321; // type for check-cast - mirs_[7].dalvikInsn.vB = 1234; // type for check-cast - PerformGVN(); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_check_cast), mir_count_); - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = expected_ignore_check_cast[i] ? MIR_IGNORE_CHECK_CAST : 0u; - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(GlobalValueNumberingTest, CheckCastDominators) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // Block #3, top of the diamond. - DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(3)), // Block #4, left side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Block #5, right side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(5)), // Block #6, right side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 6)), // Block #7, bottom. - }; - static const MIRDef mirs[] = { - DEF_UNOP(3u, Instruction::INSTANCE_OF, 0u, 100u), - DEF_UNOP(3u, Instruction::INSTANCE_OF, 1u, 200u), - DEF_IFZ(3u, Instruction::IF_NEZ, 0u), - DEF_INVOKE1(4u, Instruction::CHECK_CAST, 100u), - DEF_INVOKE1(6u, Instruction::CHECK_CAST, 100u), - DEF_INVOKE1(6u, Instruction::CHECK_CAST, 200u), - DEF_INVOKE1(6u, Instruction::CHECK_CAST, 100u), - DEF_INVOKE1(7u, Instruction::CHECK_CAST, 100u), - }; - - static const bool expected_ignore_check_cast[] = { - false, // instance-of - false, // instance-of - false, // if-nez - false, // Not eliminated, fall-through branch. - true, // Eliminated. - false, // Not eliminated, different value. - false, // Not eliminated, different type. - false, // Not eliminated, bottom block. - }; - - PrepareBasicBlocks(bbs); - PrepareMIRs(mirs); - mirs_[0].dalvikInsn.vC = 1234; // type for instance-of - mirs_[1].dalvikInsn.vC = 1234; // type for instance-of - mirs_[3].dalvikInsn.vB = 1234; // type for check-cast - mirs_[4].dalvikInsn.vB = 1234; // type for check-cast - mirs_[5].dalvikInsn.vB = 1234; // type for check-cast - mirs_[6].dalvikInsn.vB = 4321; // type for check-cast - mirs_[7].dalvikInsn.vB = 1234; // type for check-cast - PerformGVN(); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_check_cast), mir_count_); - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = expected_ignore_check_cast[i] ? MIR_IGNORE_CHECK_CAST : 0u; - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -} // namespace art diff --git a/compiler/dex/gvn_dead_code_elimination.cc b/compiler/dex/gvn_dead_code_elimination.cc deleted file mode 100644 index 445859cc78decc6958a4e7ad1817de50d0833987..0000000000000000000000000000000000000000 --- a/compiler/dex/gvn_dead_code_elimination.cc +++ /dev/null @@ -1,1473 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "gvn_dead_code_elimination.h" - -#include "base/arena_bit_vector.h" -#include "base/bit_vector-inl.h" -#include "base/macros.h" -#include "base/allocator.h" -#include "compiler_enums.h" -#include "dataflow_iterator-inl.h" -#include "dex_instruction.h" -#include "dex/mir_graph.h" -#include "local_value_numbering.h" - -namespace art { - -constexpr uint16_t GvnDeadCodeElimination::kNoValue; -constexpr uint16_t GvnDeadCodeElimination::kNPos; - -inline uint16_t GvnDeadCodeElimination::MIRData::PrevChange(int v_reg) const { - DCHECK(has_def); - DCHECK(v_reg == vreg_def || v_reg == vreg_def + 1); - return (v_reg == vreg_def) ? prev_value.change : prev_value_high.change; -} - -inline void GvnDeadCodeElimination::MIRData::SetPrevChange(int v_reg, uint16_t change) { - DCHECK(has_def); - DCHECK(v_reg == vreg_def || v_reg == vreg_def + 1); - if (v_reg == vreg_def) { - prev_value.change = change; - } else { - prev_value_high.change = change; - } -} - -inline void GvnDeadCodeElimination::MIRData::RemovePrevChange(int v_reg, MIRData* prev_data) { - DCHECK_NE(PrevChange(v_reg), kNPos); - DCHECK(v_reg == prev_data->vreg_def || v_reg == prev_data->vreg_def + 1); - if (vreg_def == v_reg) { - if (prev_data->vreg_def == v_reg) { - prev_value = prev_data->prev_value; - low_def_over_high_word = prev_data->low_def_over_high_word; - } else { - prev_value = prev_data->prev_value_high; - low_def_over_high_word = !prev_data->high_def_over_low_word; - } - } else { - if (prev_data->vreg_def == v_reg) { - prev_value_high = prev_data->prev_value; - high_def_over_low_word = !prev_data->low_def_over_high_word; - } else { - prev_value_high = prev_data->prev_value_high; - high_def_over_low_word = prev_data->high_def_over_low_word; - } - } -} - -GvnDeadCodeElimination::VRegChains::VRegChains(uint32_t num_vregs, ScopedArenaAllocator* alloc) - : num_vregs_(num_vregs), - vreg_data_(alloc->AllocArray(num_vregs, kArenaAllocMisc)), - vreg_high_words_(false, Allocator::GetNoopAllocator(), - BitVector::BitsToWords(num_vregs), - alloc->AllocArray(BitVector::BitsToWords(num_vregs))), - mir_data_(alloc->Adapter()) { - mir_data_.reserve(100); -} - -inline void GvnDeadCodeElimination::VRegChains::Reset() { - DCHECK(mir_data_.empty()); - std::fill_n(vreg_data_, num_vregs_, VRegValue()); - vreg_high_words_.ClearAllBits(); -} - -void GvnDeadCodeElimination::VRegChains::AddMIRWithDef(MIR* mir, int v_reg, bool wide, - uint16_t new_value) { - uint16_t pos = mir_data_.size(); - mir_data_.emplace_back(mir); - MIRData* data = &mir_data_.back(); - data->has_def = true; - data->wide_def = wide; - data->vreg_def = v_reg; - - DCHECK_LT(static_cast(v_reg), num_vregs_); - data->prev_value = vreg_data_[v_reg]; - data->low_def_over_high_word = - (vreg_data_[v_reg].change != kNPos) - ? GetMIRData(vreg_data_[v_reg].change)->vreg_def + 1 == v_reg - : vreg_high_words_.IsBitSet(v_reg); - vreg_data_[v_reg].value = new_value; - vreg_data_[v_reg].change = pos; - vreg_high_words_.ClearBit(v_reg); - - if (wide) { - DCHECK_LT(static_cast(v_reg + 1), num_vregs_); - data->prev_value_high = vreg_data_[v_reg + 1]; - data->high_def_over_low_word = - (vreg_data_[v_reg + 1].change != kNPos) - ? GetMIRData(vreg_data_[v_reg + 1].change)->vreg_def == v_reg + 1 - : !vreg_high_words_.IsBitSet(v_reg + 1); - vreg_data_[v_reg + 1].value = new_value; - vreg_data_[v_reg + 1].change = pos; - vreg_high_words_.SetBit(v_reg + 1); - } -} - -inline void GvnDeadCodeElimination::VRegChains::AddMIRWithoutDef(MIR* mir) { - mir_data_.emplace_back(mir); -} - -void GvnDeadCodeElimination::VRegChains::RemoveLastMIRData() { - MIRData* data = LastMIRData(); - if (data->has_def) { - DCHECK_EQ(vreg_data_[data->vreg_def].change, NumMIRs() - 1u); - vreg_data_[data->vreg_def] = data->prev_value; - DCHECK(!vreg_high_words_.IsBitSet(data->vreg_def)); - if (data->low_def_over_high_word) { - vreg_high_words_.SetBit(data->vreg_def); - } - if (data->wide_def) { - DCHECK_EQ(vreg_data_[data->vreg_def + 1].change, NumMIRs() - 1u); - vreg_data_[data->vreg_def + 1] = data->prev_value_high; - DCHECK(vreg_high_words_.IsBitSet(data->vreg_def + 1)); - if (data->high_def_over_low_word) { - vreg_high_words_.ClearBit(data->vreg_def + 1); - } - } - } - mir_data_.pop_back(); -} - -void GvnDeadCodeElimination::VRegChains::RemoveTrailingNops() { - // There's at least one NOP to drop. There may be more. - MIRData* last_data = LastMIRData(); - DCHECK(!last_data->must_keep && !last_data->has_def); - do { - DCHECK_EQ(static_cast(last_data->mir->dalvikInsn.opcode), static_cast(kMirOpNop)); - mir_data_.pop_back(); - if (mir_data_.empty()) { - break; - } - last_data = LastMIRData(); - } while (!last_data->must_keep && !last_data->has_def); -} - -inline size_t GvnDeadCodeElimination::VRegChains::NumMIRs() const { - return mir_data_.size(); -} - -inline GvnDeadCodeElimination::MIRData* GvnDeadCodeElimination::VRegChains::GetMIRData(size_t pos) { - DCHECK_LT(pos, mir_data_.size()); - return &mir_data_[pos]; -} - -inline GvnDeadCodeElimination::MIRData* GvnDeadCodeElimination::VRegChains::LastMIRData() { - DCHECK(!mir_data_.empty()); - return &mir_data_.back(); -} - -uint32_t GvnDeadCodeElimination::VRegChains::NumVRegs() const { - return num_vregs_; -} - -void GvnDeadCodeElimination::VRegChains::InsertInitialValueHigh(int v_reg, uint16_t value) { - DCHECK_NE(value, kNoValue); - DCHECK_LT(static_cast(v_reg), num_vregs_); - uint16_t change = vreg_data_[v_reg].change; - if (change == kNPos) { - vreg_data_[v_reg].value = value; - vreg_high_words_.SetBit(v_reg); - } else { - while (true) { - MIRData* data = &mir_data_[change]; - DCHECK(data->vreg_def == v_reg || data->vreg_def + 1 == v_reg); - if (data->vreg_def == v_reg) { // Low word, use prev_value. - if (data->prev_value.change == kNPos) { - DCHECK_EQ(data->prev_value.value, kNoValue); - data->prev_value.value = value; - data->low_def_over_high_word = true; - break; - } - change = data->prev_value.change; - } else { // High word, use prev_value_high. - if (data->prev_value_high.change == kNPos) { - DCHECK_EQ(data->prev_value_high.value, kNoValue); - data->prev_value_high.value = value; - break; - } - change = data->prev_value_high.change; - } - } - } -} - -void GvnDeadCodeElimination::VRegChains::UpdateInitialVRegValue(int v_reg, bool wide, - const LocalValueNumbering* lvn) { - DCHECK_LT(static_cast(v_reg), num_vregs_); - if (!wide) { - if (vreg_data_[v_reg].value == kNoValue) { - uint16_t old_value = lvn->GetStartingVregValueNumber(v_reg); - if (old_value == kNoValue) { - // Maybe there was a wide value in v_reg before. Do not check for wide value in v_reg-1, - // that will be done only if we see a definition of v_reg-1, otherwise it's unnecessary. - old_value = lvn->GetStartingVregValueNumberWide(v_reg); - if (old_value != kNoValue) { - InsertInitialValueHigh(v_reg + 1, old_value); - } - } - vreg_data_[v_reg].value = old_value; - DCHECK(!vreg_high_words_.IsBitSet(v_reg)); // Keep marked as low word. - } - } else { - DCHECK_LT(static_cast(v_reg + 1), num_vregs_); - bool check_high = true; - if (vreg_data_[v_reg].value == kNoValue) { - uint16_t old_value = lvn->GetStartingVregValueNumberWide(v_reg); - if (old_value != kNoValue) { - InsertInitialValueHigh(v_reg + 1, old_value); - check_high = false; // High word has been processed. - } else { - // Maybe there was a narrow value before. Do not check for wide value in v_reg-1, - // that will be done only if we see a definition of v_reg-1, otherwise it's unnecessary. - old_value = lvn->GetStartingVregValueNumber(v_reg); - } - vreg_data_[v_reg].value = old_value; - DCHECK(!vreg_high_words_.IsBitSet(v_reg)); // Keep marked as low word. - } - if (check_high && vreg_data_[v_reg + 1].value == kNoValue) { - uint16_t old_value = lvn->GetStartingVregValueNumber(v_reg + 1); - if (old_value == kNoValue && static_cast(v_reg + 2) < num_vregs_) { - // Maybe there was a wide value before. - old_value = lvn->GetStartingVregValueNumberWide(v_reg + 1); - if (old_value != kNoValue) { - InsertInitialValueHigh(v_reg + 2, old_value); - } - } - vreg_data_[v_reg + 1].value = old_value; - DCHECK(!vreg_high_words_.IsBitSet(v_reg + 1)); // Keep marked as low word. - } - } -} - -inline uint16_t GvnDeadCodeElimination::VRegChains::LastChange(int v_reg) { - DCHECK_LT(static_cast(v_reg), num_vregs_); - return vreg_data_[v_reg].change; -} - -inline uint16_t GvnDeadCodeElimination::VRegChains::CurrentValue(int v_reg) { - DCHECK_LT(static_cast(v_reg), num_vregs_); - return vreg_data_[v_reg].value; -} - -uint16_t GvnDeadCodeElimination::VRegChains::FindKillHead(int v_reg, uint16_t cutoff) { - uint16_t current_value = this->CurrentValue(v_reg); - DCHECK_NE(current_value, kNoValue); - uint16_t change = LastChange(v_reg); - DCHECK_LT(change, mir_data_.size()); - DCHECK_GE(change, cutoff); - bool match_high_word = (mir_data_[change].vreg_def != v_reg); - do { - MIRData* data = &mir_data_[change]; - DCHECK(data->vreg_def == v_reg || data->vreg_def + 1 == v_reg); - if (data->vreg_def == v_reg) { // Low word, use prev_value. - if (data->prev_value.value == current_value && - match_high_word == data->low_def_over_high_word) { - break; - } - change = data->prev_value.change; - } else { // High word, use prev_value_high. - if (data->prev_value_high.value == current_value && - match_high_word != data->high_def_over_low_word) { - break; - } - change = data->prev_value_high.change; - } - if (change < cutoff) { - change = kNPos; - } - } while (change != kNPos); - return change; -} - -uint16_t GvnDeadCodeElimination::VRegChains::FindFirstChangeAfter(int v_reg, - uint16_t change) const { - DCHECK_LT(static_cast(v_reg), num_vregs_); - DCHECK_LT(change, mir_data_.size()); - uint16_t result = kNPos; - uint16_t search_change = vreg_data_[v_reg].change; - while (search_change != kNPos && search_change > change) { - result = search_change; - search_change = mir_data_[search_change].PrevChange(v_reg); - } - return result; -} - -void GvnDeadCodeElimination::VRegChains::ReplaceChange(uint16_t old_change, uint16_t new_change) { - const MIRData* old_data = GetMIRData(old_change); - DCHECK(old_data->has_def); - int count = old_data->wide_def ? 2 : 1; - for (int v_reg = old_data->vreg_def, end = old_data->vreg_def + count; v_reg != end; ++v_reg) { - uint16_t next_change = FindFirstChangeAfter(v_reg, old_change); - if (next_change == kNPos) { - DCHECK_EQ(vreg_data_[v_reg].change, old_change); - vreg_data_[v_reg].change = new_change; - DCHECK_EQ(vreg_high_words_.IsBitSet(v_reg), v_reg == old_data->vreg_def + 1); - // No change in vreg_high_words_. - } else { - DCHECK_EQ(mir_data_[next_change].PrevChange(v_reg), old_change); - mir_data_[next_change].SetPrevChange(v_reg, new_change); - } - } -} - -void GvnDeadCodeElimination::VRegChains::RemoveChange(uint16_t change) { - MIRData* data = &mir_data_[change]; - DCHECK(data->has_def); - int count = data->wide_def ? 2 : 1; - for (int v_reg = data->vreg_def, end = data->vreg_def + count; v_reg != end; ++v_reg) { - uint16_t next_change = FindFirstChangeAfter(v_reg, change); - if (next_change == kNPos) { - DCHECK_EQ(vreg_data_[v_reg].change, change); - vreg_data_[v_reg] = (data->vreg_def == v_reg) ? data->prev_value : data->prev_value_high; - DCHECK_EQ(vreg_high_words_.IsBitSet(v_reg), v_reg == data->vreg_def + 1); - if (data->vreg_def == v_reg && data->low_def_over_high_word) { - vreg_high_words_.SetBit(v_reg); - } else if (data->vreg_def != v_reg && data->high_def_over_low_word) { - vreg_high_words_.ClearBit(v_reg); - } - } else { - DCHECK_EQ(mir_data_[next_change].PrevChange(v_reg), change); - mir_data_[next_change].RemovePrevChange(v_reg, data); - } - } -} - -inline bool GvnDeadCodeElimination::VRegChains::IsTopChange(uint16_t change) const { - DCHECK_LT(change, mir_data_.size()); - const MIRData* data = &mir_data_[change]; - DCHECK(data->has_def); - DCHECK_LT(data->wide_def ? data->vreg_def + 1u : data->vreg_def, num_vregs_); - return vreg_data_[data->vreg_def].change == change && - (!data->wide_def || vreg_data_[data->vreg_def + 1u].change == change); -} - -bool GvnDeadCodeElimination::VRegChains::IsSRegUsed(uint16_t first_change, uint16_t last_change, - int s_reg) const { - DCHECK_LE(first_change, last_change); - DCHECK_LE(last_change, mir_data_.size()); - for (size_t c = first_change; c != last_change; ++c) { - SSARepresentation* ssa_rep = mir_data_[c].mir->ssa_rep; - for (int i = 0; i != ssa_rep->num_uses; ++i) { - if (ssa_rep->uses[i] == s_reg) { - return true; - } - } - } - return false; -} - -bool GvnDeadCodeElimination::VRegChains::IsVRegUsed(uint16_t first_change, uint16_t last_change, - int v_reg, MIRGraph* mir_graph) const { - DCHECK_LE(first_change, last_change); - DCHECK_LE(last_change, mir_data_.size()); - for (size_t c = first_change; c != last_change; ++c) { - SSARepresentation* ssa_rep = mir_data_[c].mir->ssa_rep; - for (int i = 0; i != ssa_rep->num_uses; ++i) { - if (mir_graph->SRegToVReg(ssa_rep->uses[i]) == v_reg) { - return true; - } - } - } - return false; -} - -void GvnDeadCodeElimination::VRegChains::RenameSRegUses(uint16_t first_change, uint16_t last_change, - int old_s_reg, int new_s_reg, bool wide) { - for (size_t c = first_change; c != last_change; ++c) { - SSARepresentation* ssa_rep = mir_data_[c].mir->ssa_rep; - for (int i = 0; i != ssa_rep->num_uses; ++i) { - if (ssa_rep->uses[i] == old_s_reg) { - ssa_rep->uses[i] = new_s_reg; - if (wide) { - ++i; - DCHECK_LT(i, ssa_rep->num_uses); - ssa_rep->uses[i] = new_s_reg + 1; - } - } - } - } -} - -void GvnDeadCodeElimination::VRegChains::RenameVRegUses(uint16_t first_change, uint16_t last_change, - int old_s_reg, int old_v_reg, - int new_s_reg, int new_v_reg) { - for (size_t c = first_change; c != last_change; ++c) { - MIR* mir = mir_data_[c].mir; - if (IsInstructionBinOp2Addr(mir->dalvikInsn.opcode) && - mir->ssa_rep->uses[0] == old_s_reg && old_v_reg != new_v_reg) { - // Rewrite binop_2ADDR with plain binop before doing the register rename. - ChangeBinOp2AddrToPlainBinOp(mir); - } - uint64_t df_attr = MIRGraph::GetDataFlowAttributes(mir); - size_t use = 0u; -#define REPLACE_VREG(REG) \ - if ((df_attr & DF_U##REG) != 0) { \ - if (mir->ssa_rep->uses[use] == old_s_reg) { \ - DCHECK_EQ(mir->dalvikInsn.v##REG, static_cast(old_v_reg)); \ - mir->dalvikInsn.v##REG = new_v_reg; \ - mir->ssa_rep->uses[use] = new_s_reg; \ - if ((df_attr & DF_##REG##_WIDE) != 0) { \ - DCHECK_EQ(mir->ssa_rep->uses[use + 1], old_s_reg + 1); \ - mir->ssa_rep->uses[use + 1] = new_s_reg + 1; \ - } \ - } \ - use += ((df_attr & DF_##REG##_WIDE) != 0) ? 2 : 1; \ - } - REPLACE_VREG(A) - REPLACE_VREG(B) - REPLACE_VREG(C) -#undef REPLACE_VREG - // We may encounter an out-of-order Phi which we need to ignore, otherwise we should - // only be asked to rename registers specified by DF_UA, DF_UB and DF_UC. - DCHECK_EQ(use, - static_cast(mir->dalvikInsn.opcode) == kMirOpPhi - ? 0u - : static_cast(mir->ssa_rep->num_uses)); - } -} - -GvnDeadCodeElimination::GvnDeadCodeElimination(const GlobalValueNumbering* gvn, - ScopedArenaAllocator* alloc) - : gvn_(gvn), - mir_graph_(gvn_->GetMirGraph()), - vreg_chains_(mir_graph_->GetNumOfCodeAndTempVRs(), alloc), - bb_(nullptr), - lvn_(nullptr), - no_uses_all_since_(0u), - unused_vregs_(new (alloc) ArenaBitVector(alloc, vreg_chains_.NumVRegs(), false)), - vregs_to_kill_(new (alloc) ArenaBitVector(alloc, vreg_chains_.NumVRegs(), false)), - kill_heads_(alloc->AllocArray(vreg_chains_.NumVRegs(), kArenaAllocMisc)), - changes_to_kill_(alloc->Adapter()), - dependent_vregs_(new (alloc) ArenaBitVector(alloc, vreg_chains_.NumVRegs(), false)) { - changes_to_kill_.reserve(16u); -} - -void GvnDeadCodeElimination::Apply(BasicBlock* bb) { - bb_ = bb; - lvn_ = gvn_->GetLvn(bb->id); - - RecordPass(); - BackwardPass(); - - DCHECK_EQ(no_uses_all_since_, 0u); - lvn_ = nullptr; - bb_ = nullptr; -} - -void GvnDeadCodeElimination::RecordPass() { - // Record MIRs with vreg definition data, eliminate single instructions. - vreg_chains_.Reset(); - DCHECK_EQ(no_uses_all_since_, 0u); - for (MIR* mir = bb_->first_mir_insn; mir != nullptr; mir = mir->next) { - if (RecordMIR(mir)) { - RecordPassTryToKillOverwrittenMoveOrMoveSrc(); - RecordPassTryToKillLastMIR(); - } - } -} - -void GvnDeadCodeElimination::BackwardPass() { - // Now process MIRs in reverse order, trying to eliminate them. - unused_vregs_->ClearAllBits(); // Implicitly depend on all vregs at the end of BB. - while (vreg_chains_.NumMIRs() != 0u) { - if (BackwardPassTryToKillLastMIR()) { - continue; - } - BackwardPassProcessLastMIR(); - } -} - -void GvnDeadCodeElimination::KillMIR(MIRData* data) { - DCHECK(!data->must_keep); - DCHECK(!data->uses_all_vregs); - DCHECK(data->has_def); - DCHECK(data->mir->ssa_rep->num_defs == 1 || data->mir->ssa_rep->num_defs == 2); - - KillMIR(data->mir); - data->has_def = false; - data->is_move = false; - data->is_move_src = false; -} - -void GvnDeadCodeElimination::KillMIR(MIR* mir) { - mir->dalvikInsn.opcode = static_cast(kMirOpNop); - mir->ssa_rep->num_uses = 0; - mir->ssa_rep->num_defs = 0; -} - -void GvnDeadCodeElimination::ChangeBinOp2AddrToPlainBinOp(MIR* mir) { - mir->dalvikInsn.vC = mir->dalvikInsn.vB; - mir->dalvikInsn.vB = mir->dalvikInsn.vA; - mir->dalvikInsn.opcode = static_cast( - mir->dalvikInsn.opcode - Instruction::ADD_INT_2ADDR + Instruction::ADD_INT); -} - -MIR* GvnDeadCodeElimination::CreatePhi(int s_reg) { - int v_reg = mir_graph_->SRegToVReg(s_reg); - MIR* phi = mir_graph_->NewMIR(); - phi->dalvikInsn.opcode = static_cast(kMirOpPhi); - phi->dalvikInsn.vA = v_reg; - phi->offset = bb_->start_offset; - phi->m_unit_index = 0; // Arbitrarily assign all Phi nodes to outermost method. - - phi->ssa_rep = static_cast(mir_graph_->GetArena()->Alloc( - sizeof(SSARepresentation), kArenaAllocDFInfo)); - - mir_graph_->AllocateSSADefData(phi, 1); - phi->ssa_rep->defs[0] = s_reg; - - size_t num_uses = bb_->predecessors.size(); - mir_graph_->AllocateSSAUseData(phi, num_uses); - size_t idx = 0u; - for (BasicBlockId pred_id : bb_->predecessors) { - BasicBlock* pred_bb = mir_graph_->GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - phi->ssa_rep->uses[idx] = pred_bb->data_flow_info->vreg_to_ssa_map_exit[v_reg]; - DCHECK_NE(phi->ssa_rep->uses[idx], INVALID_SREG); - idx++; - } - - phi->meta.phi_incoming = static_cast(mir_graph_->GetArena()->Alloc( - sizeof(BasicBlockId) * num_uses, kArenaAllocDFInfo)); - std::copy(bb_->predecessors.begin(), bb_->predecessors.end(), phi->meta.phi_incoming); - bb_->PrependMIR(phi); - return phi; -} - -MIR* GvnDeadCodeElimination::RenameSRegDefOrCreatePhi(uint16_t def_change, uint16_t last_change, - MIR* mir_to_kill) { - DCHECK(mir_to_kill->ssa_rep->num_defs == 1 || mir_to_kill->ssa_rep->num_defs == 2); - bool wide = (mir_to_kill->ssa_rep->num_defs != 1); - int new_s_reg = mir_to_kill->ssa_rep->defs[0]; - - // Just before we kill mir_to_kill, we need to replace the previous SSA reg assigned to the - // same dalvik reg to keep consistency with subsequent instructions. However, if there's no - // defining MIR for that dalvik reg, the preserved values must come from its predecessors - // and we need to create a new Phi (a degenerate Phi if there's only a single predecessor). - if (def_change == kNPos) { - if (wide) { - DCHECK_EQ(new_s_reg + 1, mir_to_kill->ssa_rep->defs[1]); - DCHECK_EQ(mir_graph_->SRegToVReg(new_s_reg) + 1, mir_graph_->SRegToVReg(new_s_reg + 1)); - CreatePhi(new_s_reg + 1); // High word Phi. - } - MIR* phi = CreatePhi(new_s_reg); - // If this is a degenerate Phi with all inputs being the same SSA reg, we need to its uses. - DCHECK_NE(phi->ssa_rep->num_uses, 0u); - int old_s_reg = phi->ssa_rep->uses[0]; - bool all_same = true; - for (size_t i = 1u, num = phi->ssa_rep->num_uses; i != num; ++i) { - if (phi->ssa_rep->uses[i] != old_s_reg) { - all_same = false; - break; - } - } - if (all_same) { - vreg_chains_.RenameSRegUses(0u, last_change, old_s_reg, new_s_reg, wide); - } - return phi; - } else { - DCHECK_LT(def_change, last_change); - DCHECK_LE(last_change, vreg_chains_.NumMIRs()); - MIRData* def_data = vreg_chains_.GetMIRData(def_change); - DCHECK(def_data->has_def); - int old_s_reg = def_data->mir->ssa_rep->defs[0]; - DCHECK_NE(old_s_reg, new_s_reg); - DCHECK_EQ(mir_graph_->SRegToVReg(old_s_reg), mir_graph_->SRegToVReg(new_s_reg)); - def_data->mir->ssa_rep->defs[0] = new_s_reg; - if (wide) { - if (static_cast(def_data->mir->dalvikInsn.opcode) == kMirOpPhi) { - // Currently the high word Phi is always located after the low word Phi. - MIR* phi_high = def_data->mir->next; - DCHECK(phi_high != nullptr && static_cast(phi_high->dalvikInsn.opcode) == kMirOpPhi); - DCHECK_EQ(phi_high->ssa_rep->defs[0], old_s_reg + 1); - phi_high->ssa_rep->defs[0] = new_s_reg + 1; - } else { - DCHECK_EQ(def_data->mir->ssa_rep->defs[1], old_s_reg + 1); - def_data->mir->ssa_rep->defs[1] = new_s_reg + 1; - } - } - vreg_chains_.RenameSRegUses(def_change + 1u, last_change, old_s_reg, new_s_reg, wide); - return nullptr; - } -} - - -void GvnDeadCodeElimination::BackwardPassProcessLastMIR() { - MIRData* data = vreg_chains_.LastMIRData(); - if (data->uses_all_vregs) { - DCHECK(data->must_keep); - unused_vregs_->ClearAllBits(); - DCHECK_EQ(no_uses_all_since_, vreg_chains_.NumMIRs()); - --no_uses_all_since_; - while (no_uses_all_since_ != 0u && - !vreg_chains_.GetMIRData(no_uses_all_since_ - 1u)->uses_all_vregs) { - --no_uses_all_since_; - } - } else { - if (data->has_def) { - unused_vregs_->SetBit(data->vreg_def); - if (data->wide_def) { - unused_vregs_->SetBit(data->vreg_def + 1); - } - } - for (int i = 0, num_uses = data->mir->ssa_rep->num_uses; i != num_uses; ++i) { - int v_reg = mir_graph_->SRegToVReg(data->mir->ssa_rep->uses[i]); - unused_vregs_->ClearBit(v_reg); - } - } - vreg_chains_.RemoveLastMIRData(); -} - -void GvnDeadCodeElimination::RecordPassKillMoveByRenamingSrcDef(uint16_t src_change, - uint16_t move_change) { - DCHECK_LT(src_change, move_change); - MIRData* src_data = vreg_chains_.GetMIRData(src_change); - MIRData* move_data = vreg_chains_.GetMIRData(move_change); - DCHECK(src_data->is_move_src); - DCHECK_EQ(src_data->wide_def, move_data->wide_def); - DCHECK(move_data->prev_value.change == kNPos || move_data->prev_value.change <= src_change); - DCHECK(!move_data->wide_def || move_data->prev_value_high.change == kNPos || - move_data->prev_value_high.change <= src_change); - - int old_s_reg = src_data->mir->ssa_rep->defs[0]; - // NOTE: old_s_reg may differ from move_data->mir->ssa_rep->uses[0]; value names must match. - int new_s_reg = move_data->mir->ssa_rep->defs[0]; - DCHECK_NE(old_s_reg, new_s_reg); - - if (IsInstructionBinOp2Addr(src_data->mir->dalvikInsn.opcode) && - src_data->vreg_def != move_data->vreg_def) { - // Rewrite binop_2ADDR with plain binop before doing the register rename. - ChangeBinOp2AddrToPlainBinOp(src_data->mir); - } - // Remove src_change from the vreg chain(s). - vreg_chains_.RemoveChange(src_change); - // Replace the move_change with the src_change, copying all necessary data. - src_data->is_move_src = move_data->is_move_src; - src_data->low_def_over_high_word = move_data->low_def_over_high_word; - src_data->high_def_over_low_word = move_data->high_def_over_low_word; - src_data->vreg_def = move_data->vreg_def; - src_data->prev_value = move_data->prev_value; - src_data->prev_value_high = move_data->prev_value_high; - src_data->mir->dalvikInsn.vA = move_data->vreg_def; - src_data->mir->ssa_rep->defs[0] = new_s_reg; - if (move_data->wide_def) { - DCHECK_EQ(src_data->mir->ssa_rep->defs[1], old_s_reg + 1); - src_data->mir->ssa_rep->defs[1] = new_s_reg + 1; - } - vreg_chains_.ReplaceChange(move_change, src_change); - - // Rename uses and kill the move. - vreg_chains_.RenameVRegUses(src_change + 1u, vreg_chains_.NumMIRs(), - old_s_reg, mir_graph_->SRegToVReg(old_s_reg), - new_s_reg, mir_graph_->SRegToVReg(new_s_reg)); - KillMIR(move_data); -} - -void GvnDeadCodeElimination::RecordPassTryToKillOverwrittenMoveOrMoveSrc(uint16_t check_change) { - MIRData* data = vreg_chains_.GetMIRData(check_change); - DCHECK(data->is_move || data->is_move_src); - int32_t dest_s_reg = data->mir->ssa_rep->defs[0]; - - if (data->is_move) { - // Check if source vreg has changed since the MOVE. - int32_t src_s_reg = data->mir->ssa_rep->uses[0]; - uint32_t src_v_reg = mir_graph_->SRegToVReg(src_s_reg); - uint16_t src_change = vreg_chains_.FindFirstChangeAfter(src_v_reg, check_change); - bool wide = data->wide_def; - if (wide) { - uint16_t src_change_high = vreg_chains_.FindFirstChangeAfter(src_v_reg + 1, check_change); - if (src_change_high != kNPos && (src_change == kNPos || src_change_high < src_change)) { - src_change = src_change_high; - } - } - if (src_change == kNPos || - !vreg_chains_.IsSRegUsed(src_change + 1u, vreg_chains_.NumMIRs(), dest_s_reg)) { - // We can simply change all uses of dest to src. - size_t rename_end = (src_change != kNPos) ? src_change + 1u : vreg_chains_.NumMIRs(); - vreg_chains_.RenameVRegUses(check_change + 1u, rename_end, - dest_s_reg, mir_graph_->SRegToVReg(dest_s_reg), - src_s_reg, mir_graph_->SRegToVReg(src_s_reg)); - - // Now, remove the MOVE from the vreg chain(s) and kill it. - vreg_chains_.RemoveChange(check_change); - KillMIR(data); - return; - } - } - - if (data->is_move_src) { - // Try to find a MOVE to a vreg that wasn't changed since check_change. - uint16_t value_name = - data->wide_def ? lvn_->GetSregValueWide(dest_s_reg) : lvn_->GetSregValue(dest_s_reg); - uint32_t dest_v_reg = mir_graph_->SRegToVReg(dest_s_reg); - for (size_t c = check_change + 1u, size = vreg_chains_.NumMIRs(); c != size; ++c) { - MIRData* d = vreg_chains_.GetMIRData(c); - if (d->is_move && d->wide_def == data->wide_def && - (d->prev_value.change == kNPos || d->prev_value.change <= check_change) && - (!d->wide_def || - d->prev_value_high.change == kNPos || d->prev_value_high.change <= check_change)) { - // Compare value names to find move to move. - int32_t src_s_reg = d->mir->ssa_rep->uses[0]; - uint16_t src_name = - (d->wide_def ? lvn_->GetSregValueWide(src_s_reg) : lvn_->GetSregValue(src_s_reg)); - if (value_name == src_name) { - // Check if the move's destination vreg is unused between check_change and the move. - uint32_t new_dest_v_reg = mir_graph_->SRegToVReg(d->mir->ssa_rep->defs[0]); - if (!vreg_chains_.IsVRegUsed(check_change + 1u, c, new_dest_v_reg, mir_graph_) && - (!d->wide_def || - !vreg_chains_.IsVRegUsed(check_change + 1u, c, new_dest_v_reg + 1, mir_graph_))) { - // If the move's destination vreg changed, check if the vreg we're trying - // to rename is unused after that change. - uint16_t dest_change = vreg_chains_.FindFirstChangeAfter(new_dest_v_reg, c); - if (d->wide_def) { - uint16_t dest_change_high = vreg_chains_.FindFirstChangeAfter(new_dest_v_reg + 1, c); - if (dest_change_high != kNPos && - (dest_change == kNPos || dest_change_high < dest_change)) { - dest_change = dest_change_high; - } - } - if (dest_change == kNPos || - !vreg_chains_.IsVRegUsed(dest_change + 1u, size, dest_v_reg, mir_graph_)) { - RecordPassKillMoveByRenamingSrcDef(check_change, c); - return; - } - } - } - } - } - } -} - -void GvnDeadCodeElimination::RecordPassTryToKillOverwrittenMoveOrMoveSrc() { - // Check if we're overwriting a the result of a move or the definition of a source of a move. - // For MOVE_WIDE, we may be overwriting partially; if that's the case, check that the other - // word wasn't previously overwritten - we would have tried to rename back then. - MIRData* data = vreg_chains_.LastMIRData(); - if (!data->has_def) { - return; - } - // NOTE: Instructions such as new-array implicitly use all vregs (if they throw) but they can - // define a move source which can be renamed. Therefore we allow the checked change to be the - // change before no_uses_all_since_. This has no effect on moves as they never use all vregs. - if (data->prev_value.change != kNPos && data->prev_value.change + 1u >= no_uses_all_since_) { - MIRData* check_data = vreg_chains_.GetMIRData(data->prev_value.change); - bool try_to_kill = false; - if (!check_data->is_move && !check_data->is_move_src) { - DCHECK(!try_to_kill); - } else if (!check_data->wide_def) { - // Narrow move; always fully overwritten by the last MIR. - try_to_kill = true; - } else if (data->low_def_over_high_word) { - // Overwriting only the high word; is the low word still valid? - DCHECK_EQ(check_data->vreg_def + 1u, data->vreg_def); - if (vreg_chains_.LastChange(check_data->vreg_def) == data->prev_value.change) { - try_to_kill = true; - } - } else if (!data->wide_def) { - // Overwriting only the low word, is the high word still valid? - if (vreg_chains_.LastChange(data->vreg_def + 1) == data->prev_value.change) { - try_to_kill = true; - } - } else { - // Overwriting both words; was the high word still from the same move? - if (data->prev_value_high.change == data->prev_value.change) { - try_to_kill = true; - } - } - if (try_to_kill) { - RecordPassTryToKillOverwrittenMoveOrMoveSrc(data->prev_value.change); - } - } - if (data->wide_def && data->high_def_over_low_word && - data->prev_value_high.change != kNPos && - data->prev_value_high.change + 1u >= no_uses_all_since_) { - MIRData* check_data = vreg_chains_.GetMIRData(data->prev_value_high.change); - bool try_to_kill = false; - if (!check_data->is_move && !check_data->is_move_src) { - DCHECK(!try_to_kill); - } else if (!check_data->wide_def) { - // Narrow move; always fully overwritten by the last MIR. - try_to_kill = true; - } else if (vreg_chains_.LastChange(check_data->vreg_def + 1) == - data->prev_value_high.change) { - // High word is still valid. - try_to_kill = true; - } - if (try_to_kill) { - RecordPassTryToKillOverwrittenMoveOrMoveSrc(data->prev_value_high.change); - } - } -} - -void GvnDeadCodeElimination::RecordPassTryToKillLastMIR() { - MIRData* last_data = vreg_chains_.LastMIRData(); - if (last_data->must_keep) { - return; - } - if (UNLIKELY(!last_data->has_def)) { - // Must be an eliminated MOVE. Drop its data and data of all eliminated MIRs before it. - vreg_chains_.RemoveTrailingNops(); - return; - } - - // Try to kill a sequence of consecutive definitions of the same vreg. Allow mixing - // wide and non-wide defs; consider high word dead if low word has been overwritten. - uint16_t current_value = vreg_chains_.CurrentValue(last_data->vreg_def); - uint16_t change = vreg_chains_.NumMIRs() - 1u; - MIRData* data = last_data; - while (data->prev_value.value != current_value) { - --change; - if (data->prev_value.change == kNPos || data->prev_value.change != change) { - return; - } - data = vreg_chains_.GetMIRData(data->prev_value.change); - if (data->must_keep || !data->has_def || data->vreg_def != last_data->vreg_def) { - return; - } - } - - bool wide = last_data->wide_def; - if (wide) { - // Check that the low word is valid. - if (data->low_def_over_high_word) { - return; - } - // Check that the high word is valid. - MIRData* high_data = data; - if (!high_data->wide_def) { - uint16_t high_change = vreg_chains_.FindFirstChangeAfter(data->vreg_def + 1, change); - DCHECK_NE(high_change, kNPos); - high_data = vreg_chains_.GetMIRData(high_change); - DCHECK_EQ(high_data->vreg_def, data->vreg_def); - } - if (high_data->prev_value_high.value != current_value || high_data->high_def_over_low_word) { - return; - } - } - - MIR* phi = RenameSRegDefOrCreatePhi(data->prev_value.change, change, last_data->mir); - for (size_t i = 0, count = vreg_chains_.NumMIRs() - change; i != count; ++i) { - KillMIR(vreg_chains_.LastMIRData()->mir); - vreg_chains_.RemoveLastMIRData(); - } - if (phi != nullptr) { - // Though the Phi has been added to the beginning, we can put the MIRData at the end. - vreg_chains_.AddMIRWithDef(phi, phi->dalvikInsn.vA, wide, current_value); - // Reset the previous value to avoid eventually eliminating the Phi itself (unless unused). - last_data = vreg_chains_.LastMIRData(); - last_data->prev_value.value = kNoValue; - last_data->prev_value_high.value = kNoValue; - } -} - -uint16_t GvnDeadCodeElimination::FindChangesToKill(uint16_t first_change, uint16_t last_change) { - // Process dependencies for changes in range [first_change, last_change) and record all - // changes that we need to kill. Return kNPos if there's a dependent change that must be - // kept unconditionally; otherwise the end of the range processed before encountering - // a change that defines a dalvik reg that we need to keep (last_change on full success). - changes_to_kill_.clear(); - dependent_vregs_->ClearAllBits(); - for (size_t change = first_change; change != last_change; ++change) { - MIRData* data = vreg_chains_.GetMIRData(change); - DCHECK(!data->uses_all_vregs); - bool must_not_depend = data->must_keep; - bool depends = false; - // Check if the MIR defines a vreg we're trying to eliminate. - if (data->has_def && vregs_to_kill_->IsBitSet(data->vreg_def)) { - if (change < kill_heads_[data->vreg_def]) { - must_not_depend = true; - } else { - depends = true; - } - } - if (data->has_def && data->wide_def && vregs_to_kill_->IsBitSet(data->vreg_def + 1)) { - if (change < kill_heads_[data->vreg_def + 1]) { - must_not_depend = true; - } else { - depends = true; - } - } - if (!depends) { - // Check for dependency through SSA reg uses. - SSARepresentation* ssa_rep = data->mir->ssa_rep; - for (int i = 0; i != ssa_rep->num_uses; ++i) { - if (dependent_vregs_->IsBitSet(mir_graph_->SRegToVReg(ssa_rep->uses[i]))) { - depends = true; - break; - } - } - } - // Now check if we can eliminate the insn if we need to. - if (depends && must_not_depend) { - return kNPos; - } - if (depends && data->has_def && - vreg_chains_.IsTopChange(change) && !vregs_to_kill_->IsBitSet(data->vreg_def) && - !unused_vregs_->IsBitSet(data->vreg_def) && - (!data->wide_def || !unused_vregs_->IsBitSet(data->vreg_def + 1))) { - // This is a top change but neither unnecessary nor one of the top kill changes. - return change; - } - // Finally, update the data. - if (depends) { - changes_to_kill_.push_back(change); - if (data->has_def) { - dependent_vregs_->SetBit(data->vreg_def); - if (data->wide_def) { - dependent_vregs_->SetBit(data->vreg_def + 1); - } - } - } else { - if (data->has_def) { - dependent_vregs_->ClearBit(data->vreg_def); - if (data->wide_def) { - dependent_vregs_->ClearBit(data->vreg_def + 1); - } - } - } - } - return last_change; -} - -void GvnDeadCodeElimination::BackwardPassTryToKillRevertVRegs() { -} - -bool GvnDeadCodeElimination::BackwardPassTryToKillLastMIR() { - MIRData* last_data = vreg_chains_.LastMIRData(); - if (last_data->must_keep) { - return false; - } - DCHECK(!last_data->uses_all_vregs); - if (!last_data->has_def) { - // Previously eliminated. - DCHECK_EQ(static_cast(last_data->mir->dalvikInsn.opcode), static_cast(kMirOpNop)); - vreg_chains_.RemoveTrailingNops(); - return true; - } - if (unused_vregs_->IsBitSet(last_data->vreg_def) || - (last_data->wide_def && unused_vregs_->IsBitSet(last_data->vreg_def + 1))) { - if (last_data->wide_def) { - // For wide defs, one of the vregs may still be considered needed, fix that. - unused_vregs_->SetBit(last_data->vreg_def); - unused_vregs_->SetBit(last_data->vreg_def + 1); - } - KillMIR(last_data->mir); - vreg_chains_.RemoveLastMIRData(); - return true; - } - - vregs_to_kill_->ClearAllBits(); - size_t num_mirs = vreg_chains_.NumMIRs(); - DCHECK_NE(num_mirs, 0u); - uint16_t kill_change = num_mirs - 1u; - uint16_t start = num_mirs; - size_t num_killed_top_changes = 0u; - while (num_killed_top_changes != kMaxNumTopChangesToKill && - kill_change != kNPos && kill_change != num_mirs) { - ++num_killed_top_changes; - - DCHECK(vreg_chains_.IsTopChange(kill_change)); - MIRData* data = vreg_chains_.GetMIRData(kill_change); - int count = data->wide_def ? 2 : 1; - for (int v_reg = data->vreg_def, end = data->vreg_def + count; v_reg != end; ++v_reg) { - uint16_t kill_head = vreg_chains_.FindKillHead(v_reg, no_uses_all_since_); - if (kill_head == kNPos) { - return false; - } - kill_heads_[v_reg] = kill_head; - vregs_to_kill_->SetBit(v_reg); - start = std::min(start, kill_head); - } - DCHECK_LT(start, vreg_chains_.NumMIRs()); - - kill_change = FindChangesToKill(start, num_mirs); - } - - if (kill_change != num_mirs) { - return false; - } - - // Kill all MIRs marked as dependent. - for (uint32_t v_reg : vregs_to_kill_->Indexes()) { - // Rename s_regs or create Phi only once for each MIR (only for low word). - MIRData* data = vreg_chains_.GetMIRData(vreg_chains_.LastChange(v_reg)); - DCHECK(data->has_def); - if (data->vreg_def == v_reg) { - MIRData* kill_head_data = vreg_chains_.GetMIRData(kill_heads_[v_reg]); - RenameSRegDefOrCreatePhi(kill_head_data->PrevChange(v_reg), num_mirs, data->mir); - } else { - DCHECK_EQ(data->vreg_def + 1u, v_reg); - DCHECK_EQ(vreg_chains_.GetMIRData(kill_heads_[v_reg - 1u])->PrevChange(v_reg - 1u), - vreg_chains_.GetMIRData(kill_heads_[v_reg])->PrevChange(v_reg)); - } - } - for (auto it = changes_to_kill_.rbegin(), end = changes_to_kill_.rend(); it != end; ++it) { - MIRData* data = vreg_chains_.GetMIRData(*it); - DCHECK(!data->must_keep); - DCHECK(data->has_def); - vreg_chains_.RemoveChange(*it); - KillMIR(data); - } - - // Each dependent register not in vregs_to_kill_ is either already marked unused or - // it's one word of a wide register where the other word has been overwritten. - unused_vregs_->UnionIfNotIn(dependent_vregs_, vregs_to_kill_); - - vreg_chains_.RemoveTrailingNops(); - return true; -} - -bool GvnDeadCodeElimination::RecordMIR(MIR* mir) { - bool must_keep = false; - bool uses_all_vregs = false; - bool is_move = false; - uint16_t opcode = mir->dalvikInsn.opcode; - switch (opcode) { - case kMirOpPhi: { - // Determine if this Phi is merging wide regs. - RegLocation raw_dest = gvn_->GetMirGraph()->GetRawDest(mir); - if (raw_dest.high_word) { - // This is the high part of a wide reg. Ignore the Phi. - return false; - } - bool wide = raw_dest.wide; - // Record the value. - DCHECK_EQ(mir->ssa_rep->num_defs, 1); - int s_reg = mir->ssa_rep->defs[0]; - uint16_t new_value = wide ? lvn_->GetSregValueWide(s_reg) : lvn_->GetSregValue(s_reg); - - int v_reg = mir_graph_->SRegToVReg(s_reg); - DCHECK_EQ(vreg_chains_.CurrentValue(v_reg), kNoValue); // No previous def for v_reg. - if (wide) { - DCHECK_EQ(vreg_chains_.CurrentValue(v_reg + 1), kNoValue); - } - vreg_chains_.AddMIRWithDef(mir, v_reg, wide, new_value); - return true; // Avoid the common processing. - } - - case kMirOpNop: - case Instruction::NOP: - // Don't record NOPs. - return false; - - case kMirOpCheck: - must_keep = true; - uses_all_vregs = true; - break; - - case Instruction::RETURN_VOID: - case Instruction::RETURN: - case Instruction::RETURN_OBJECT: - case Instruction::RETURN_WIDE: - case Instruction::GOTO: - case Instruction::GOTO_16: - case Instruction::GOTO_32: - case Instruction::PACKED_SWITCH: - case Instruction::SPARSE_SWITCH: - case Instruction::IF_EQ: - case Instruction::IF_NE: - case Instruction::IF_LT: - case Instruction::IF_GE: - case Instruction::IF_GT: - case Instruction::IF_LE: - case Instruction::IF_EQZ: - case Instruction::IF_NEZ: - case Instruction::IF_LTZ: - case Instruction::IF_GEZ: - case Instruction::IF_GTZ: - case Instruction::IF_LEZ: - case kMirOpFusedCmplFloat: - case kMirOpFusedCmpgFloat: - case kMirOpFusedCmplDouble: - case kMirOpFusedCmpgDouble: - case kMirOpFusedCmpLong: - must_keep = true; - uses_all_vregs = true; // Keep the implicit dependencies on all vregs. - break; - - case Instruction::CONST_CLASS: - case Instruction::CONST_STRING: - case Instruction::CONST_STRING_JUMBO: - // NOTE: While we're currently treating CONST_CLASS, CONST_STRING and CONST_STRING_JUMBO - // as throwing but we could conceivably try and eliminate those exceptions if we're - // retrieving the class/string repeatedly. - must_keep = true; - uses_all_vregs = true; - break; - - case Instruction::MONITOR_ENTER: - case Instruction::MONITOR_EXIT: - // We can actually try and optimize across the acquire operation of MONITOR_ENTER, - // the value names provided by GVN reflect the possible changes to memory visibility. - // NOTE: In ART, MONITOR_ENTER and MONITOR_EXIT can throw only NPE. - must_keep = true; - uses_all_vregs = (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0; - break; - - case Instruction::INVOKE_DIRECT: - case Instruction::INVOKE_DIRECT_RANGE: - case Instruction::INVOKE_VIRTUAL: - case Instruction::INVOKE_VIRTUAL_RANGE: - case Instruction::INVOKE_SUPER: - case Instruction::INVOKE_SUPER_RANGE: - case Instruction::INVOKE_INTERFACE: - case Instruction::INVOKE_INTERFACE_RANGE: - case Instruction::INVOKE_STATIC: - case Instruction::INVOKE_STATIC_RANGE: - case Instruction::THROW: - case Instruction::FILLED_NEW_ARRAY: - case Instruction::FILLED_NEW_ARRAY_RANGE: - case Instruction::FILL_ARRAY_DATA: - must_keep = true; - uses_all_vregs = true; - break; - - case Instruction::NEW_INSTANCE: - case Instruction::NEW_ARRAY: - must_keep = true; - uses_all_vregs = true; - break; - - case Instruction::CHECK_CAST: - DCHECK_EQ(mir->ssa_rep->num_uses, 1); - must_keep = true; // Keep for type information even if MIR_IGNORE_CHECK_CAST. - uses_all_vregs = (mir->optimization_flags & MIR_IGNORE_CHECK_CAST) == 0; - break; - - case kMirOpNullCheck: - DCHECK_EQ(mir->ssa_rep->num_uses, 1); - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) { - mir->ssa_rep->num_uses = 0; - mir->dalvikInsn.opcode = static_cast(kMirOpNop); - return false; - } - must_keep = true; - uses_all_vregs = true; - break; - - case Instruction::MOVE_RESULT: - case Instruction::MOVE_RESULT_OBJECT: - case Instruction::MOVE_RESULT_WIDE: - break; - - case Instruction::INSTANCE_OF: - break; - - case Instruction::MOVE_EXCEPTION: - must_keep = true; - break; - - case kMirOpCopy: - case Instruction::MOVE: - case Instruction::MOVE_FROM16: - case Instruction::MOVE_16: - case Instruction::MOVE_WIDE: - case Instruction::MOVE_WIDE_FROM16: - case Instruction::MOVE_WIDE_16: - case Instruction::MOVE_OBJECT: - case Instruction::MOVE_OBJECT_FROM16: - case Instruction::MOVE_OBJECT_16: { - is_move = true; - // If the MIR defining src vreg is known, allow renaming all uses of src vreg to dest vreg - // while updating the defining MIR to directly define dest vreg. However, changing Phi's - // def this way doesn't work without changing MIRs in other BBs. - int src_v_reg = mir_graph_->SRegToVReg(mir->ssa_rep->uses[0]); - int src_change = vreg_chains_.LastChange(src_v_reg); - if (src_change != kNPos) { - MIRData* src_data = vreg_chains_.GetMIRData(src_change); - if (static_cast(src_data->mir->dalvikInsn.opcode) != kMirOpPhi) { - src_data->is_move_src = true; - } - } - break; - } - - case Instruction::CONST_4: - case Instruction::CONST_16: - case Instruction::CONST: - case Instruction::CONST_HIGH16: - case Instruction::CONST_WIDE_16: - case Instruction::CONST_WIDE_32: - case Instruction::CONST_WIDE: - case Instruction::CONST_WIDE_HIGH16: - case Instruction::CMPL_FLOAT: - case Instruction::CMPG_FLOAT: - case Instruction::CMPL_DOUBLE: - case Instruction::CMPG_DOUBLE: - case Instruction::CMP_LONG: - case Instruction::NEG_INT: - case Instruction::NOT_INT: - case Instruction::NEG_LONG: - case Instruction::NOT_LONG: - case Instruction::NEG_FLOAT: - case Instruction::NEG_DOUBLE: - case Instruction::INT_TO_LONG: - case Instruction::INT_TO_FLOAT: - case Instruction::INT_TO_DOUBLE: - case Instruction::LONG_TO_INT: - case Instruction::LONG_TO_FLOAT: - case Instruction::LONG_TO_DOUBLE: - case Instruction::FLOAT_TO_INT: - case Instruction::FLOAT_TO_LONG: - case Instruction::FLOAT_TO_DOUBLE: - case Instruction::DOUBLE_TO_INT: - case Instruction::DOUBLE_TO_LONG: - case Instruction::DOUBLE_TO_FLOAT: - case Instruction::INT_TO_BYTE: - case Instruction::INT_TO_CHAR: - case Instruction::INT_TO_SHORT: - case Instruction::ADD_INT: - case Instruction::SUB_INT: - case Instruction::MUL_INT: - case Instruction::AND_INT: - case Instruction::OR_INT: - case Instruction::XOR_INT: - case Instruction::SHL_INT: - case Instruction::SHR_INT: - case Instruction::USHR_INT: - case Instruction::ADD_LONG: - case Instruction::SUB_LONG: - case Instruction::MUL_LONG: - case Instruction::AND_LONG: - case Instruction::OR_LONG: - case Instruction::XOR_LONG: - case Instruction::SHL_LONG: - case Instruction::SHR_LONG: - case Instruction::USHR_LONG: - case Instruction::ADD_FLOAT: - case Instruction::SUB_FLOAT: - case Instruction::MUL_FLOAT: - case Instruction::DIV_FLOAT: - case Instruction::REM_FLOAT: - case Instruction::ADD_DOUBLE: - case Instruction::SUB_DOUBLE: - case Instruction::MUL_DOUBLE: - case Instruction::DIV_DOUBLE: - case Instruction::REM_DOUBLE: - case Instruction::ADD_INT_2ADDR: - case Instruction::SUB_INT_2ADDR: - case Instruction::MUL_INT_2ADDR: - case Instruction::AND_INT_2ADDR: - case Instruction::OR_INT_2ADDR: - case Instruction::XOR_INT_2ADDR: - case Instruction::SHL_INT_2ADDR: - case Instruction::SHR_INT_2ADDR: - case Instruction::USHR_INT_2ADDR: - case Instruction::ADD_LONG_2ADDR: - case Instruction::SUB_LONG_2ADDR: - case Instruction::MUL_LONG_2ADDR: - case Instruction::AND_LONG_2ADDR: - case Instruction::OR_LONG_2ADDR: - case Instruction::XOR_LONG_2ADDR: - case Instruction::SHL_LONG_2ADDR: - case Instruction::SHR_LONG_2ADDR: - case Instruction::USHR_LONG_2ADDR: - case Instruction::ADD_FLOAT_2ADDR: - case Instruction::SUB_FLOAT_2ADDR: - case Instruction::MUL_FLOAT_2ADDR: - case Instruction::DIV_FLOAT_2ADDR: - case Instruction::REM_FLOAT_2ADDR: - case Instruction::ADD_DOUBLE_2ADDR: - case Instruction::SUB_DOUBLE_2ADDR: - case Instruction::MUL_DOUBLE_2ADDR: - case Instruction::DIV_DOUBLE_2ADDR: - case Instruction::REM_DOUBLE_2ADDR: - case Instruction::ADD_INT_LIT16: - case Instruction::RSUB_INT: - case Instruction::MUL_INT_LIT16: - case Instruction::AND_INT_LIT16: - case Instruction::OR_INT_LIT16: - case Instruction::XOR_INT_LIT16: - case Instruction::ADD_INT_LIT8: - case Instruction::RSUB_INT_LIT8: - case Instruction::MUL_INT_LIT8: - case Instruction::AND_INT_LIT8: - case Instruction::OR_INT_LIT8: - case Instruction::XOR_INT_LIT8: - case Instruction::SHL_INT_LIT8: - case Instruction::SHR_INT_LIT8: - case Instruction::USHR_INT_LIT8: - break; - - case Instruction::DIV_INT: - case Instruction::REM_INT: - case Instruction::DIV_LONG: - case Instruction::REM_LONG: - case Instruction::DIV_INT_2ADDR: - case Instruction::REM_INT_2ADDR: - case Instruction::DIV_LONG_2ADDR: - case Instruction::REM_LONG_2ADDR: - if ((mir->optimization_flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) { - must_keep = true; - uses_all_vregs = true; - } - break; - - case Instruction::DIV_INT_LIT16: - case Instruction::REM_INT_LIT16: - case Instruction::DIV_INT_LIT8: - case Instruction::REM_INT_LIT8: - if (mir->dalvikInsn.vC == 0) { // Explicit division by 0? - must_keep = true; - uses_all_vregs = true; - } - break; - - case Instruction::ARRAY_LENGTH: - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0) { - must_keep = true; - uses_all_vregs = true; - } - break; - - case Instruction::AGET_OBJECT: - case Instruction::AGET: - case Instruction::AGET_WIDE: - case Instruction::AGET_BOOLEAN: - case Instruction::AGET_BYTE: - case Instruction::AGET_CHAR: - case Instruction::AGET_SHORT: - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0 || - (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) == 0) { - must_keep = true; - uses_all_vregs = true; - } - break; - - case Instruction::APUT_OBJECT: - case Instruction::APUT: - case Instruction::APUT_WIDE: - case Instruction::APUT_BYTE: - case Instruction::APUT_BOOLEAN: - case Instruction::APUT_SHORT: - case Instruction::APUT_CHAR: - must_keep = true; - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0 || - (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) == 0) { - uses_all_vregs = true; - } - break; - - case Instruction::IGET_OBJECT: - case Instruction::IGET: - case Instruction::IGET_WIDE: - case Instruction::IGET_BOOLEAN: - case Instruction::IGET_BYTE: - case Instruction::IGET_CHAR: - case Instruction::IGET_SHORT: { - const MirIFieldLoweringInfo& info = mir_graph_->GetIFieldLoweringInfo(mir); - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0 || - !info.IsResolved() || !info.FastGet()) { - must_keep = true; - uses_all_vregs = true; - } else if (info.IsVolatile()) { - must_keep = true; - } - break; - } - - case Instruction::IPUT_OBJECT: - case Instruction::IPUT: - case Instruction::IPUT_WIDE: - case Instruction::IPUT_BOOLEAN: - case Instruction::IPUT_BYTE: - case Instruction::IPUT_CHAR: - case Instruction::IPUT_SHORT: { - must_keep = true; - const MirIFieldLoweringInfo& info = mir_graph_->GetIFieldLoweringInfo(mir); - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0 || - !info.IsResolved() || !info.FastPut()) { - uses_all_vregs = true; - } - break; - } - - case Instruction::SGET_OBJECT: - case Instruction::SGET: - case Instruction::SGET_WIDE: - case Instruction::SGET_BOOLEAN: - case Instruction::SGET_BYTE: - case Instruction::SGET_CHAR: - case Instruction::SGET_SHORT: { - const MirSFieldLoweringInfo& info = mir_graph_->GetSFieldLoweringInfo(mir); - if ((mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0 || - !info.IsResolved() || !info.FastGet()) { - must_keep = true; - uses_all_vregs = true; - } else if (info.IsVolatile()) { - must_keep = true; - } - break; - } - - case Instruction::SPUT_OBJECT: - case Instruction::SPUT: - case Instruction::SPUT_WIDE: - case Instruction::SPUT_BOOLEAN: - case Instruction::SPUT_BYTE: - case Instruction::SPUT_CHAR: - case Instruction::SPUT_SHORT: { - must_keep = true; - const MirSFieldLoweringInfo& info = mir_graph_->GetSFieldLoweringInfo(mir); - if ((mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0 || - !info.IsResolved() || !info.FastPut()) { - uses_all_vregs = true; - } - break; - } - - default: - LOG(FATAL) << "Unexpected opcode: " << opcode; - UNREACHABLE(); - } - - if (mir->ssa_rep->num_defs != 0) { - DCHECK(mir->ssa_rep->num_defs == 1 || mir->ssa_rep->num_defs == 2); - bool wide = (mir->ssa_rep->num_defs == 2); - int s_reg = mir->ssa_rep->defs[0]; - int v_reg = mir_graph_->SRegToVReg(s_reg); - uint16_t new_value = wide ? lvn_->GetSregValueWide(s_reg) : lvn_->GetSregValue(s_reg); - DCHECK_NE(new_value, kNoValue); - - vreg_chains_.UpdateInitialVRegValue(v_reg, wide, lvn_); - vreg_chains_.AddMIRWithDef(mir, v_reg, wide, new_value); - if (is_move) { - // Allow renaming all uses of dest vreg to src vreg. - vreg_chains_.LastMIRData()->is_move = true; - } - } else { - vreg_chains_.AddMIRWithoutDef(mir); - DCHECK(!is_move) << opcode; - } - - if (must_keep) { - MIRData* last_data = vreg_chains_.LastMIRData(); - last_data->must_keep = true; - if (uses_all_vregs) { - last_data->uses_all_vregs = true; - no_uses_all_since_ = vreg_chains_.NumMIRs(); - } - } else { - DCHECK_NE(mir->ssa_rep->num_defs, 0) << opcode; - DCHECK(!uses_all_vregs) << opcode; - } - return true; -} - -} // namespace art diff --git a/compiler/dex/gvn_dead_code_elimination.h b/compiler/dex/gvn_dead_code_elimination.h deleted file mode 100644 index 06022db5012592d97fcddaa1ab75ca213f97ce4f..0000000000000000000000000000000000000000 --- a/compiler/dex/gvn_dead_code_elimination.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_GVN_DEAD_CODE_ELIMINATION_H_ -#define ART_COMPILER_DEX_GVN_DEAD_CODE_ELIMINATION_H_ - -#include "base/arena_object.h" -#include "base/scoped_arena_containers.h" -#include "global_value_numbering.h" - -namespace art { - -class ArenaBitVector; -class BasicBlock; -class LocalValueNumbering; -class MIR; -class MIRGraph; - -/** - * @class DeadCodeElimination - * @details Eliminate dead code based on the results of global value numbering. - * Also get rid of MOVE insns when we can use the source instead of destination - * without affecting the vreg values at safepoints; this is useful in methods - * with a large number of vregs that frequently move values to and from low vregs - * to accommodate insns that can work only with the low 16 or 256 vregs. - */ -class GvnDeadCodeElimination : public DeletableArenaObject { - public: - GvnDeadCodeElimination(const GlobalValueNumbering* gvn, ScopedArenaAllocator* alloc); - - // Apply the DCE to a basic block. - void Apply(BasicBlock* bb); - - private: - static constexpr uint16_t kNoValue = GlobalValueNumbering::kNoValue; - static constexpr uint16_t kNPos = 0xffffu; - static constexpr size_t kMaxNumTopChangesToKill = 2; - - struct VRegValue { - VRegValue() : value(kNoValue), change(kNPos) { } - - // Value name as reported by GVN, kNoValue if not available. - uint16_t value; - // Index of the change in mir_data_ that defined the value, kNPos if initial value for the BB. - uint16_t change; - }; - - struct MIRData { - explicit MIRData(MIR* m) - : mir(m), uses_all_vregs(false), must_keep(false), is_move(false), is_move_src(false), - has_def(false), wide_def(false), - low_def_over_high_word(false), high_def_over_low_word(false), vreg_def(0u), - prev_value(), prev_value_high() { - } - - uint16_t PrevChange(int v_reg) const; - void SetPrevChange(int v_reg, uint16_t change); - void RemovePrevChange(int v_reg, MIRData* prev_data); - - MIR* mir; - bool uses_all_vregs : 1; // If mir uses all vregs, uses in mir->ssa_rep are irrelevant. - bool must_keep : 1; - bool is_move : 1; - bool is_move_src : 1; - bool has_def : 1; - bool wide_def : 1; - bool low_def_over_high_word : 1; - bool high_def_over_low_word : 1; - uint16_t vreg_def; - VRegValue prev_value; - VRegValue prev_value_high; // For wide defs. - }; - - class VRegChains { - public: - VRegChains(uint32_t num_vregs, ScopedArenaAllocator* alloc); - - void Reset(); - - void AddMIRWithDef(MIR* mir, int v_reg, bool wide, uint16_t new_value); - void AddMIRWithoutDef(MIR* mir); - void RemoveLastMIRData(); - void RemoveTrailingNops(); - - size_t NumMIRs() const; - MIRData* GetMIRData(size_t pos); - MIRData* LastMIRData(); - - uint32_t NumVRegs() const; - void InsertInitialValueHigh(int v_reg, uint16_t value); - void UpdateInitialVRegValue(int v_reg, bool wide, const LocalValueNumbering* lvn); - uint16_t LastChange(int v_reg); - uint16_t CurrentValue(int v_reg); - - uint16_t FindKillHead(int v_reg, uint16_t cutoff); - uint16_t FindFirstChangeAfter(int v_reg, uint16_t change) const; - void ReplaceChange(uint16_t old_change, uint16_t new_change); - void RemoveChange(uint16_t change); - bool IsTopChange(uint16_t change) const; - bool IsSRegUsed(uint16_t first_change, uint16_t last_change, int s_reg) const; - bool IsVRegUsed(uint16_t first_change, uint16_t last_change, int v_reg, - MIRGraph* mir_graph) const; - void RenameSRegUses(uint16_t first_change, uint16_t last_change, - int old_s_reg, int new_s_reg, bool wide); - void RenameVRegUses(uint16_t first_change, uint16_t last_change, - int old_s_reg, int old_v_reg, int new_s_reg, int new_v_reg); - - private: - const uint32_t num_vregs_; - VRegValue* const vreg_data_; - BitVector vreg_high_words_; - ScopedArenaVector mir_data_; - }; - - void RecordPass(); - void BackwardPass(); - - void KillMIR(MIRData* data); - static void KillMIR(MIR* mir); - static void ChangeBinOp2AddrToPlainBinOp(MIR* mir); - MIR* CreatePhi(int s_reg); - MIR* RenameSRegDefOrCreatePhi(uint16_t def_change, uint16_t last_change, MIR* mir_to_kill); - - // Update state variables going backwards through a MIR. - void BackwardPassProcessLastMIR(); - - uint16_t FindChangesToKill(uint16_t first_change, uint16_t last_change); - void BackwardPassTryToKillRevertVRegs(); - bool BackwardPassTryToKillLastMIR(); - - void RecordPassKillMoveByRenamingSrcDef(uint16_t src_change, uint16_t move_change); - void RecordPassTryToKillOverwrittenMoveOrMoveSrc(uint16_t check_change); - void RecordPassTryToKillOverwrittenMoveOrMoveSrc(); - void RecordPassTryToKillLastMIR(); - - bool RecordMIR(MIR* mir); - - const GlobalValueNumbering* const gvn_; - MIRGraph* const mir_graph_; - - VRegChains vreg_chains_; - BasicBlock* bb_; - const LocalValueNumbering* lvn_; - size_t no_uses_all_since_; // The change index after the last change with uses_all_vregs set. - - // Data used when processing MIRs in reverse order. - ArenaBitVector* unused_vregs_; // vregs that are not needed later. - ArenaBitVector* vregs_to_kill_; // vregs that revert to a previous value. - uint16_t* kill_heads_; // For each vreg in vregs_to_kill_, the first change to kill. - ScopedArenaVector changes_to_kill_; - ArenaBitVector* dependent_vregs_; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_GVN_DEAD_CODE_ELIMINATION_H_ diff --git a/compiler/dex/gvn_dead_code_elimination_test.cc b/compiler/dex/gvn_dead_code_elimination_test.cc deleted file mode 100644 index 22fb835b707e4e881459ca70bf9a836351fc21c9..0000000000000000000000000000000000000000 --- a/compiler/dex/gvn_dead_code_elimination_test.cc +++ /dev/null @@ -1,2201 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "dataflow_iterator-inl.h" -#include "dex/mir_field_info.h" -#include "global_value_numbering.h" -#include "gvn_dead_code_elimination.h" -#include "local_value_numbering.h" -#include "gtest/gtest.h" - -namespace art { - -class GvnDeadCodeEliminationTest : public testing::Test { - protected: - static constexpr uint16_t kNoValue = GlobalValueNumbering::kNoValue; - - struct IFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_field_idx; - bool is_volatile; - DexMemAccessType type; - }; - - struct SFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_field_idx; - bool is_volatile; - DexMemAccessType type; - }; - - struct BBDef { - static constexpr size_t kMaxSuccessors = 4; - static constexpr size_t kMaxPredecessors = 4; - - BBType type; - size_t num_successors; - BasicBlockId successors[kMaxPredecessors]; - size_t num_predecessors; - BasicBlockId predecessors[kMaxPredecessors]; - }; - - struct MIRDef { - static constexpr size_t kMaxSsaDefs = 2; - static constexpr size_t kMaxSsaUses = 4; - - BasicBlockId bbid; - Instruction::Code opcode; - int64_t value; - uint32_t field_info; - size_t num_uses; - int32_t uses[kMaxSsaUses]; - size_t num_defs; - int32_t defs[kMaxSsaDefs]; - }; - -#define DEF_SUCC0() \ - 0u, { } -#define DEF_SUCC1(s1) \ - 1u, { s1 } -#define DEF_SUCC2(s1, s2) \ - 2u, { s1, s2 } -#define DEF_SUCC3(s1, s2, s3) \ - 3u, { s1, s2, s3 } -#define DEF_SUCC4(s1, s2, s3, s4) \ - 4u, { s1, s2, s3, s4 } -#define DEF_PRED0() \ - 0u, { } -#define DEF_PRED1(p1) \ - 1u, { p1 } -#define DEF_PRED2(p1, p2) \ - 2u, { p1, p2 } -#define DEF_PRED3(p1, p2, p3) \ - 3u, { p1, p2, p3 } -#define DEF_PRED4(p1, p2, p3, p4) \ - 4u, { p1, p2, p3, p4 } -#define DEF_BB(type, succ, pred) \ - { type, succ, pred } - -#define DEF_CONST(bb, opcode, reg, value) \ - { bb, opcode, value, 0u, 0, { }, 1, { reg } } -#define DEF_CONST_WIDE(bb, opcode, reg, value) \ - { bb, opcode, value, 0u, 0, { }, 2, { reg, reg + 1 } } -#define DEF_CONST_STRING(bb, opcode, reg, index) \ - { bb, opcode, index, 0u, 0, { }, 1, { reg } } -#define DEF_IGET(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 1, { obj }, 1, { reg } } -#define DEF_IGET_WIDE(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 1, { obj }, 2, { reg, reg + 1 } } -#define DEF_IPUT(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 2, { reg, obj }, 0, { } } -#define DEF_IPUT_WIDE(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 3, { reg, reg + 1, obj }, 0, { } } -#define DEF_SGET(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 0, { }, 1, { reg } } -#define DEF_SGET_WIDE(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 0, { }, 2, { reg, reg + 1 } } -#define DEF_SPUT(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 1, { reg }, 0, { } } -#define DEF_SPUT_WIDE(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 2, { reg, reg + 1 }, 0, { } } -#define DEF_AGET(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 2, { obj, idx }, 1, { reg } } -#define DEF_AGET_WIDE(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 2, { obj, idx }, 2, { reg, reg + 1 } } -#define DEF_APUT(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 3, { reg, obj, idx }, 0, { } } -#define DEF_APUT_WIDE(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 4, { reg, reg + 1, obj, idx }, 0, { } } -#define DEF_INVOKE1(bb, opcode, reg) \ - { bb, opcode, 0u, 0u, 1, { reg }, 0, { } } -#define DEF_UNIQUE_REF(bb, opcode, reg) \ - { bb, opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ... -#define DEF_IFZ(bb, opcode, reg) \ - { bb, opcode, 0u, 0u, 1, { reg }, 0, { } } -#define DEF_MOVE(bb, opcode, reg, src) \ - { bb, opcode, 0u, 0u, 1, { src }, 1, { reg } } -#define DEF_MOVE_WIDE(bb, opcode, reg, src) \ - { bb, opcode, 0u, 0u, 2, { src, src + 1 }, 2, { reg, reg + 1 } } -#define DEF_PHI2(bb, reg, src1, src2) \ - { bb, static_cast(kMirOpPhi), 0, 0u, 2u, { src1, src2 }, 1, { reg } } -#define DEF_UNOP(bb, opcode, result, src1) \ - { bb, opcode, 0u, 0u, 1, { src1 }, 1, { result } } -#define DEF_BINOP(bb, opcode, result, src1, src2) \ - { bb, opcode, 0u, 0u, 2, { src1, src2 }, 1, { result } } -#define DEF_BINOP_WIDE(bb, opcode, result, src1, src2) \ - { bb, opcode, 0u, 0u, 4, { src1, src1 + 1, src2, src2 + 1 }, 2, { result, result + 1 } } - - void DoPrepareIFields(const IFieldDef* defs, size_t count) { - cu_.mir_graph->ifield_lowering_infos_.clear(); - cu_.mir_graph->ifield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const IFieldDef* def = &defs[i]; - MirIFieldLoweringInfo field_info(def->field_idx, def->type, false); - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ = - MirIFieldLoweringInfo::kFlagFastGet | MirIFieldLoweringInfo::kFlagFastPut | - (field_info.flags_ & ~(def->is_volatile ? 0u : MirIFieldLoweringInfo::kFlagIsVolatile)); - } - cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareIFields(const IFieldDef (&defs)[count]) { - DoPrepareIFields(defs, count); - } - - void DoPrepareSFields(const SFieldDef* defs, size_t count) { - cu_.mir_graph->sfield_lowering_infos_.clear(); - cu_.mir_graph->sfield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const SFieldDef* def = &defs[i]; - MirSFieldLoweringInfo field_info(def->field_idx, def->type); - // Mark even unresolved fields as initialized. - field_info.flags_ |= MirSFieldLoweringInfo::kFlagClassIsInitialized; - // NOTE: MirSFieldLoweringInfo::kFlagClassIsInDexCache isn't used by GVN. - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ = - MirSFieldLoweringInfo::kFlagFastGet | MirSFieldLoweringInfo::kFlagFastPut | - (field_info.flags_ & ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile)); - } - cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareSFields(const SFieldDef (&defs)[count]) { - DoPrepareSFields(defs, count); - } - - void DoPrepareBasicBlocks(const BBDef* defs, size_t count) { - cu_.mir_graph->block_id_map_.clear(); - cu_.mir_graph->block_list_.clear(); - ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block. - ASSERT_EQ(kNullBlock, defs[0].type); - ASSERT_EQ(kEntryBlock, defs[1].type); - ASSERT_EQ(kExitBlock, defs[2].type); - for (size_t i = 0u; i != count; ++i) { - const BBDef* def = &defs[i]; - BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type); - if (def->num_successors <= 2) { - bb->successor_block_list_type = kNotUsed; - bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u; - bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u; - } else { - bb->successor_block_list_type = kPackedSwitch; - bb->fall_through = 0u; - bb->taken = 0u; - bb->successor_blocks.reserve(def->num_successors); - for (size_t j = 0u; j != def->num_successors; ++j) { - SuccessorBlockInfo* successor_block_info = - static_cast(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessors)); - successor_block_info->block = j; - successor_block_info->key = 0u; // Not used by class init check elimination. - bb->successor_blocks.push_back(successor_block_info); - } - } - bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors); - if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) { - bb->data_flow_info = static_cast( - cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo)); - bb->data_flow_info->live_in_v = live_in_v_; - bb->data_flow_info->vreg_to_ssa_map_exit = nullptr; - } - } - ASSERT_EQ(count, cu_.mir_graph->block_list_.size()); - cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1]; - ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type); - cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2]; - ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type); - } - - template - void PrepareBasicBlocks(const BBDef (&defs)[count]) { - DoPrepareBasicBlocks(defs, count); - } - - int SRegToVReg(int32_t s_reg, bool wide) { - int v_reg = cu_.mir_graph->SRegToVReg(s_reg); - CHECK_LT(static_cast(v_reg), num_vregs_); - if (wide) { - CHECK_LT(static_cast(v_reg + 1), num_vregs_); - } - return v_reg; - } - - int SRegToVReg(int32_t* uses, size_t* use, bool wide) { - int v_reg = SRegToVReg(uses[*use], wide); - if (wide) { - CHECK_EQ(uses[*use] + 1, uses[*use + 1]); - *use += 2u; - } else { - *use += 1u; - } - return v_reg; - } - - void DoPrepareMIRs(const MIRDef* defs, size_t count) { - mir_count_ = count; - mirs_ = reinterpret_cast(cu_.arena.Alloc(sizeof(MIR) * count, kArenaAllocMIR)); - ssa_reps_.resize(count); - for (size_t i = 0u; i != count; ++i) { - const MIRDef* def = &defs[i]; - MIR* mir = &mirs_[i]; - ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size()); - BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid]; - bb->AppendMIR(mir); - mir->dalvikInsn.opcode = def->opcode; - mir->dalvikInsn.vB = static_cast(def->value); - mir->dalvikInsn.vB_wide = def->value; - if (IsInstructionIGetOrIPut(def->opcode)) { - ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size()); - mir->meta.ifield_lowering_info = def->field_info; - ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_info].MemAccessType(), - IGetOrIPutMemAccessType(def->opcode)); - } else if (IsInstructionSGetOrSPut(def->opcode)) { - ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size()); - mir->meta.sfield_lowering_info = def->field_info; - ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_info].MemAccessType(), - SGetOrSPutMemAccessType(def->opcode)); - } else if (def->opcode == static_cast(kMirOpPhi)) { - mir->meta.phi_incoming = - allocator_->AllocArray(def->num_uses, kArenaAllocDFInfo); - ASSERT_EQ(def->num_uses, bb->predecessors.size()); - std::copy(bb->predecessors.begin(), bb->predecessors.end(), mir->meta.phi_incoming); - } - mir->ssa_rep = &ssa_reps_[i]; - cu_.mir_graph->AllocateSSAUseData(mir, def->num_uses); - std::copy_n(def->uses, def->num_uses, mir->ssa_rep->uses); - // Keep mir->ssa_rep->fp_use[.] zero-initialized (false). Not used by DCE, only copied. - cu_.mir_graph->AllocateSSADefData(mir, def->num_defs); - std::copy_n(def->defs, def->num_defs, mir->ssa_rep->defs); - // Keep mir->ssa_rep->fp_def[.] zero-initialized (false). Not used by DCE, only copied. - mir->dalvikInsn.opcode = def->opcode; - mir->offset = i; // LVN uses offset only for debug output - mir->optimization_flags = 0u; - uint64_t df_attrs = MIRGraph::GetDataFlowAttributes(mir); - if ((df_attrs & DF_DA) != 0) { - CHECK_NE(def->num_defs, 0u); - mir->dalvikInsn.vA = SRegToVReg(def->defs[0], (df_attrs & DF_A_WIDE) != 0); - bb->data_flow_info->vreg_to_ssa_map_exit[mir->dalvikInsn.vA] = def->defs[0]; - if ((df_attrs & DF_A_WIDE) != 0) { - CHECK_EQ(def->defs[0] + 1, def->defs[1]); - bb->data_flow_info->vreg_to_ssa_map_exit[mir->dalvikInsn.vA + 1u] = def->defs[0] + 1; - } - } - if ((df_attrs & (DF_UA | DF_UB | DF_UC)) != 0) { - size_t use = 0; - if ((df_attrs & DF_UA) != 0) { - mir->dalvikInsn.vA = SRegToVReg(mir->ssa_rep->uses, &use, (df_attrs & DF_A_WIDE) != 0); - } - if ((df_attrs & DF_UB) != 0) { - mir->dalvikInsn.vB = SRegToVReg(mir->ssa_rep->uses, &use, (df_attrs & DF_B_WIDE) != 0); - } - if ((df_attrs & DF_UC) != 0) { - mir->dalvikInsn.vC = SRegToVReg(mir->ssa_rep->uses, &use, (df_attrs & DF_C_WIDE) != 0); - } - DCHECK_EQ(def->num_uses, use); - } - } - DexFile::CodeItem* code_item = static_cast( - cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc)); - code_item->insns_size_in_code_units_ = 2u * count; - code_item->registers_size_ = kMaxVRegs; - cu_.mir_graph->current_code_item_ = code_item; - } - - template - void PrepareMIRs(const MIRDef (&defs)[count]) { - DoPrepareMIRs(defs, count); - } - - template - void PrepareSRegToVRegMap(const int (&map)[count]) { - cu_.mir_graph->ssa_base_vregs_.assign(map, map + count); - num_vregs_ = *std::max_element(map, map + count) + 1u; - AllNodesIterator iterator(cu_.mir_graph.get()); - for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { - if (bb->data_flow_info != nullptr) { - bb->data_flow_info->vreg_to_ssa_map_exit = static_cast( - cu_.arena.Alloc(sizeof(int32_t) * num_vregs_, kArenaAllocDFInfo)); - std::fill_n(bb->data_flow_info->vreg_to_ssa_map_exit, num_vregs_, INVALID_SREG); - } - } - } - - void PerformGVN() { - cu_.mir_graph->SSATransformationStart(); - cu_.mir_graph->ComputeDFSOrders(); - cu_.mir_graph->ComputeDominators(); - cu_.mir_graph->ComputeTopologicalSortOrder(); - cu_.mir_graph->SSATransformationEnd(); - cu_.mir_graph->temp_.gvn.ifield_ids = GlobalValueNumbering::PrepareGvnFieldIds( - allocator_.get(), cu_.mir_graph->ifield_lowering_infos_); - cu_.mir_graph->temp_.gvn.sfield_ids = GlobalValueNumbering::PrepareGvnFieldIds( - allocator_.get(), cu_.mir_graph->sfield_lowering_infos_); - ASSERT_TRUE(gvn_ == nullptr); - gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(), - GlobalValueNumbering::kModeGvn)); - value_names_.resize(mir_count_, 0xffffu); - LoopRepeatingTopologicalSortIterator iterator(cu_.mir_graph.get()); - bool change = false; - for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { - LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - value_names_[mir - mirs_] = lvn->GetValueNumber(mir); - } - } - change = (lvn != nullptr) && gvn_->FinishBasicBlock(bb); - ASSERT_TRUE(gvn_->Good()); - } - } - - void PerformGVNCodeModifications() { - ASSERT_TRUE(gvn_ != nullptr); - ASSERT_TRUE(gvn_->Good()); - gvn_->StartPostProcessing(); - TopologicalSortIterator iterator(cu_.mir_graph.get()); - for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { - LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - uint16_t value_name = lvn->GetValueNumber(mir); - ASSERT_EQ(value_name, value_names_[mir - mirs_]); - } - } - bool change = (lvn != nullptr) && gvn_->FinishBasicBlock(bb); - ASSERT_FALSE(change); - ASSERT_TRUE(gvn_->Good()); - } - } - - void FillVregToSsaRegExitMaps() { - // Fill in vreg_to_ssa_map_exit for each BB. - PreOrderDfsIterator iterator(cu_.mir_graph.get()); - for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { - if (bb->block_type == kDalvikByteCode) { - CHECK(!bb->predecessors.empty()); - BasicBlock* pred_bb = cu_.mir_graph->GetBasicBlock(bb->predecessors[0]); - for (size_t v_reg = 0; v_reg != num_vregs_; ++v_reg) { - if (bb->data_flow_info->vreg_to_ssa_map_exit[v_reg] == INVALID_SREG) { - bb->data_flow_info->vreg_to_ssa_map_exit[v_reg] = - pred_bb->data_flow_info->vreg_to_ssa_map_exit[v_reg]; - } - } - } - } - } - - template - void MarkAsWideSRegs(const int32_t (&sregs)[count]) { - for (int32_t sreg : sregs) { - cu_.mir_graph->reg_location_[sreg].wide = true; - cu_.mir_graph->reg_location_[sreg + 1].wide = true; - cu_.mir_graph->reg_location_[sreg + 1].high_word = true; - } - } - - void PerformDCE() { - FillVregToSsaRegExitMaps(); - cu_.mir_graph->GetNumOfCodeAndTempVRs(); - dce_.reset(new (allocator_.get()) GvnDeadCodeElimination(gvn_.get(), allocator_.get())); - PreOrderDfsIterator iterator(cu_.mir_graph.get()); - for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { - if (bb->block_type == kDalvikByteCode) { - dce_->Apply(bb); - } - } - } - - void PerformGVN_DCE() { - PerformGVN(); - PerformGVNCodeModifications(); // Eliminate null/range checks. - PerformDCE(); - } - - template - void ExpectValueNamesNE(const size_t (&indexes)[count]) { - for (size_t i1 = 0; i1 != count; ++i1) { - size_t idx1 = indexes[i1]; - for (size_t i2 = i1 + 1; i2 != count; ++i2) { - size_t idx2 = indexes[i2]; - EXPECT_NE(value_names_[idx1], value_names_[idx2]) << idx1 << " " << idx2; - } - } - } - - template - void ExpectNoNullCheck(const size_t (&indexes)[count]) { - for (size_t i = 0; i != count; ++i) { - size_t idx = indexes[i]; - EXPECT_EQ(MIR_IGNORE_NULL_CHECK, mirs_[idx].optimization_flags & MIR_IGNORE_NULL_CHECK) - << idx; - } - size_t num_no_null_ck = 0u; - for (size_t i = 0; i != mir_count_; ++i) { - if ((mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) { - ++num_no_null_ck; - } - } - EXPECT_EQ(count, num_no_null_ck); - } - - GvnDeadCodeEliminationTest() - : pool_(), - cu_(&pool_, kRuntimeISA, nullptr, nullptr), - num_vregs_(0u), - mir_count_(0u), - mirs_(nullptr), - ssa_reps_(), - allocator_(), - gvn_(), - dce_(), - value_names_(), - live_in_v_(new (&cu_.arena) ArenaBitVector(&cu_.arena, kMaxSsaRegs, false)) { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); - cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test. - allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); - // By default, the zero-initialized reg_location_[.] with ref == false tells LVN that - // 0 constants are integral, not references, and the values are all narrow. - // Nothing else is used by LVN/GVN. Tests can override the default values as needed. - cu_.mir_graph->reg_location_ = static_cast(cu_.arena.Alloc( - kMaxSsaRegs * sizeof(cu_.mir_graph->reg_location_[0]), kArenaAllocRegAlloc)); - cu_.mir_graph->num_ssa_regs_ = kMaxSsaRegs; - // Bind all possible sregs to live vregs for test purposes. - live_in_v_->SetInitialBits(kMaxSsaRegs); - cu_.mir_graph->ssa_base_vregs_.reserve(kMaxSsaRegs); - cu_.mir_graph->ssa_subscripts_.reserve(kMaxSsaRegs); - for (unsigned int i = 0; i < kMaxSsaRegs; i++) { - cu_.mir_graph->ssa_base_vregs_.push_back(i); - cu_.mir_graph->ssa_subscripts_.push_back(0); - } - // Set shorty for a void-returning method without arguments. - cu_.shorty = "V"; - } - - static constexpr size_t kMaxSsaRegs = 16384u; - static constexpr size_t kMaxVRegs = 256u; - - ArenaPool pool_; - CompilationUnit cu_; - size_t num_vregs_; - size_t mir_count_; - MIR* mirs_; - std::vector ssa_reps_; - std::unique_ptr allocator_; - std::unique_ptr gvn_; - std::unique_ptr dce_; - std::vector value_names_; - ArenaBitVector* live_in_v_; -}; - -constexpr uint16_t GvnDeadCodeEliminationTest::kNoValue; - -class GvnDeadCodeEliminationTestSimple : public GvnDeadCodeEliminationTest { - public: - GvnDeadCodeEliminationTestSimple(); - - private: - static const BBDef kSimpleBbs[]; -}; - -const GvnDeadCodeEliminationTest::BBDef GvnDeadCodeEliminationTestSimple::kSimpleBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)), -}; - -GvnDeadCodeEliminationTestSimple::GvnDeadCodeEliminationTestSimple() - : GvnDeadCodeEliminationTest() { - PrepareBasicBlocks(kSimpleBbs); -} - -class GvnDeadCodeEliminationTestDiamond : public GvnDeadCodeEliminationTest { - public: - GvnDeadCodeEliminationTestDiamond(); - - private: - static const BBDef kDiamondBbs[]; -}; - -const GvnDeadCodeEliminationTest::BBDef GvnDeadCodeEliminationTestDiamond::kDiamondBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // Block #3, top of the diamond. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Block #4, left side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Block #5, right side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // Block #6, bottom. -}; - -GvnDeadCodeEliminationTestDiamond::GvnDeadCodeEliminationTestDiamond() - : GvnDeadCodeEliminationTest() { - PrepareBasicBlocks(kDiamondBbs); -} - -class GvnDeadCodeEliminationTestLoop : public GvnDeadCodeEliminationTest { - public: - GvnDeadCodeEliminationTestLoop(); - - private: - static const BBDef kLoopBbs[]; -}; - -const GvnDeadCodeEliminationTest::BBDef GvnDeadCodeEliminationTestLoop::kLoopBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), -}; - -GvnDeadCodeEliminationTestLoop::GvnDeadCodeEliminationTestLoop() - : GvnDeadCodeEliminationTest() { - PrepareBasicBlocks(kLoopBbs); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename1) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 2u, 0u), - DEF_IGET(3, Instruction::IGET, 3u, 2u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 3 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[2]); - - const size_t no_null_ck_indexes[] = { 1, 3 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the IGET uses the s_reg 0, v_reg 0, defined by mirs_[0]. - ASSERT_EQ(1, mirs_[3].ssa_rep->num_uses); - EXPECT_EQ(0, mirs_[3].ssa_rep->uses[0]); - EXPECT_EQ(0u, mirs_[3].dalvikInsn.vB); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename2) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 2u, 0u), - DEF_IGET(3, Instruction::IGET, 3u, 2u, 1u), - DEF_CONST(3, Instruction::CONST, 4u, 1000), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 3, 4 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[2]); - - const size_t no_null_ck_indexes[] = { 1, 3 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, true, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the IGET uses the s_reg 0, v_reg 0, defined by mirs_[0]. - ASSERT_EQ(1, mirs_[3].ssa_rep->num_uses); - EXPECT_EQ(0, mirs_[3].ssa_rep->uses[0]); - EXPECT_EQ(0u, mirs_[3].dalvikInsn.vB); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename3) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 2u, 0u), - DEF_IGET(3, Instruction::IGET, 3u, 2u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 3 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[2]); - - const size_t no_null_ck_indexes[] = { 1, 3 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the NEW_INSTANCE defines the s_reg 2, v_reg 2, originally defined by the move. - ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); - EXPECT_EQ(2, mirs_[0].ssa_rep->defs[0]); - EXPECT_EQ(2u, mirs_[0].dalvikInsn.vA); - // Check that the first IGET is using the s_reg 2, v_reg 2. - ASSERT_EQ(1, mirs_[1].ssa_rep->num_uses); - EXPECT_EQ(2, mirs_[1].ssa_rep->uses[0]); - EXPECT_EQ(2u, mirs_[1].dalvikInsn.vB); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename4) { - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 1u, 0u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 2u, 1u), - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 3u, 1000u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 0, 1 /* high word */ }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 3 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 3 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - static const bool eliminated[] = { - false, true, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the NEW_INSTANCE defines the s_reg 2, v_reg 2, originally defined by the move 2u. - ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); - EXPECT_EQ(2, mirs_[0].ssa_rep->defs[0]); - EXPECT_EQ(2u, mirs_[0].dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename5) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 2u, 1u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 3u, 0u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 4u, 3u), - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 5u, 1000u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 3, 0, 1 /* high word */ }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 5 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 5 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[3]); - EXPECT_EQ(value_names_[0], value_names_[4]); - - static const bool eliminated[] = { - false, false, false, true, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the NEW_INSTANCE defines the s_reg 4, v_reg 3, originally defined by the move 4u. - ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); - EXPECT_EQ(4, mirs_[0].ssa_rep->defs[0]); - EXPECT_EQ(3u, mirs_[0].dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename6) { - static const MIRDef mirs[] = { - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000u), - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 2u, 0u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1 /* high word */, 1, 2 /* high word */ }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 0, 2 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - - static const bool eliminated[] = { - false, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the CONST_WIDE defines the s_reg 2, v_reg 1, originally defined by the move 2u. - ASSERT_EQ(2, mirs_[0].ssa_rep->num_defs); - EXPECT_EQ(2, mirs_[0].ssa_rep->defs[0]); - EXPECT_EQ(3, mirs_[0].ssa_rep->defs[1]); - EXPECT_EQ(1u, mirs_[0].dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename7) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_MOVE(3, Instruction::MOVE, 1u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 2u, 0u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_EQ(value_names_[0], value_names_[1]); - - static const bool eliminated[] = { - false, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the CONST defines the s_reg 1, v_reg 1, originally defined by the move 1u. - ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); - EXPECT_EQ(1, mirs_[0].ssa_rep->defs[0]); - EXPECT_EQ(1u, mirs_[0].dalvikInsn.vA); - // Check that the ADD_INT inputs are both s_reg1, vreg 1. - ASSERT_EQ(2, mirs_[2].ssa_rep->num_uses); - EXPECT_EQ(1, mirs_[2].ssa_rep->uses[0]); - EXPECT_EQ(1, mirs_[2].ssa_rep->uses[1]); - EXPECT_EQ(1u, mirs_[2].dalvikInsn.vB); - EXPECT_EQ(1u, mirs_[2].dalvikInsn.vC); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename8) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_MOVE(3, Instruction::MOVE, 1u, 0u), - DEF_BINOP(3, Instruction::ADD_INT_2ADDR, 2u, 0u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_EQ(value_names_[0], value_names_[1]); - - static const bool eliminated[] = { - false, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the CONST defines the s_reg 1, v_reg 1, originally defined by the move 1u. - ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); - EXPECT_EQ(1, mirs_[0].ssa_rep->defs[0]); - EXPECT_EQ(1u, mirs_[0].dalvikInsn.vA); - // Check that the ADD_INT_2ADDR was replaced by ADD_INT and inputs are both s_reg 1, vreg 1. - EXPECT_EQ(Instruction::ADD_INT, mirs_[2].dalvikInsn.opcode); - ASSERT_EQ(2, mirs_[2].ssa_rep->num_uses); - EXPECT_EQ(1, mirs_[2].ssa_rep->uses[0]); - EXPECT_EQ(1, mirs_[2].ssa_rep->uses[1]); - EXPECT_EQ(1u, mirs_[2].dalvikInsn.vB); - EXPECT_EQ(1u, mirs_[2].dalvikInsn.vC); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename9) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_BINOP(3, Instruction::ADD_INT_2ADDR, 1u, 0u, 0u), - DEF_MOVE(3, Instruction::MOVE, 2u, 1u), - DEF_CONST(3, Instruction::CONST, 3u, 3000u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 0, 1, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 3 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[1], value_names_[2]); - - static const bool eliminated[] = { - false, false, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the ADD_INT_2ADDR was replaced by ADD_INT and output is in s_reg 2, vreg 1. - EXPECT_EQ(Instruction::ADD_INT, mirs_[1].dalvikInsn.opcode); - ASSERT_EQ(2, mirs_[1].ssa_rep->num_uses); - EXPECT_EQ(0, mirs_[1].ssa_rep->uses[0]); - EXPECT_EQ(0, mirs_[1].ssa_rep->uses[1]); - EXPECT_EQ(0u, mirs_[1].dalvikInsn.vB); - EXPECT_EQ(0u, mirs_[1].dalvikInsn.vC); - ASSERT_EQ(1, mirs_[1].ssa_rep->num_defs); - EXPECT_EQ(2, mirs_[1].ssa_rep->defs[0]); - EXPECT_EQ(1u, mirs_[1].dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, NoRename1) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 2u, 1u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 3u, 0u), - DEF_CONST(3, Instruction::CONST, 4u, 1000), - DEF_IGET(3, Instruction::IGET, 5u, 3u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 0, 1 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 4, 5 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[3]); - - const size_t no_null_ck_indexes[] = { 1, 5 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, NoRename2) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 2u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 3u, 0u), - DEF_CONST(3, Instruction::CONST, 4u, 1000), - DEF_IGET(3, Instruction::IGET, 5u, 3u, 1u), - DEF_CONST(3, Instruction::CONST, 6u, 2000), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 2, 0, 3, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 4, 5, 6 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[3]); - - const size_t no_null_ck_indexes[] = { 1, 5 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, NoRename3) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_IGET(3, Instruction::IGET, 2u, 0u, 2u), - DEF_BINOP(3, Instruction::ADD_INT, 3u, 1u, 2u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 4u, 0u), - DEF_IGET(3, Instruction::IGET, 5u, 4u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 2, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 5 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[4]); - - const size_t no_null_ck_indexes[] = { 1, 2, 5 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, NoRename4) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 1u), - DEF_CONST(3, Instruction::CONST, 2u, 100u), - DEF_CONST(3, Instruction::CONST, 3u, 200u), - DEF_BINOP(3, Instruction::OR_INT_2ADDR, 4u, 2u, 3u), // 3. Find definition of the move src. - DEF_MOVE(3, Instruction::MOVE, 5u, 0u), // 4. Uses move dest vreg. - DEF_MOVE(3, Instruction::MOVE, 6u, 4u), // 2. Find overwritten move src. - DEF_CONST(3, Instruction::CONST, 7u, 2000u), // 1. Overwrites 4u, look for moves. - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 2, 4, 0, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 7 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[5]); - EXPECT_EQ(value_names_[4], value_names_[6]); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Simple1) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - { 1u, 1u, 1u, false, kDexMemAccessObject }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 0u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 2u, 1u, 1u), - DEF_IGET(3, Instruction::IGET, 3u, 2u, 2u), - DEF_IGET(3, Instruction::IGET_OBJECT, 4u, 0u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 5u, 4u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 1, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[1]); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_NE(value_names_[0], value_names_[3]); - EXPECT_NE(value_names_[1], value_names_[2]); - EXPECT_NE(value_names_[1], value_names_[3]); - EXPECT_NE(value_names_[2], value_names_[3]); - EXPECT_EQ(value_names_[1], value_names_[4]); - EXPECT_EQ(value_names_[2], value_names_[5]); - - EXPECT_EQ(MIR_IGNORE_NULL_CHECK, mirs_[4].optimization_flags & MIR_IGNORE_NULL_CHECK); - EXPECT_EQ(MIR_IGNORE_NULL_CHECK, mirs_[5].optimization_flags & MIR_IGNORE_NULL_CHECK); - - static const bool eliminated[] = { - false, false, false, false, true, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[1].ssa_rep->num_defs); - EXPECT_EQ(4, mirs_[1].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[1].ssa_rep->num_uses); - EXPECT_EQ(0, mirs_[1].ssa_rep->uses[0]); - ASSERT_EQ(1, mirs_[2].ssa_rep->num_defs); - EXPECT_EQ(5, mirs_[2].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[2].ssa_rep->num_uses); - EXPECT_EQ(4, mirs_[2].ssa_rep->uses[0]); - ASSERT_EQ(1, mirs_[3].ssa_rep->num_defs); - EXPECT_EQ(3, mirs_[3].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[3].ssa_rep->num_uses); - EXPECT_EQ(5, mirs_[3].ssa_rep->uses[0]); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Simple2) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_IGET(3, Instruction::IGET, 2u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT_2ADDR, 3u, 2u, 1u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 4u, 3u), - DEF_IGET(3, Instruction::IGET, 5u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT_2ADDR, 6u, 5u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 2, 3, 2, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[2], value_names_[5]); - EXPECT_EQ(value_names_[3], value_names_[6]); - - const size_t no_null_ck_indexes[] = { 2, 5 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, true, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[3].ssa_rep->num_defs); - EXPECT_EQ(6, mirs_[3].ssa_rep->defs[0]); - ASSERT_EQ(2, mirs_[3].ssa_rep->num_uses); - EXPECT_EQ(2, mirs_[3].ssa_rep->uses[0]); - EXPECT_EQ(1, mirs_[3].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[4].ssa_rep->num_defs); - EXPECT_EQ(4, mirs_[4].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[4].ssa_rep->num_uses); - EXPECT_EQ(6, mirs_[4].ssa_rep->uses[0]); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Simple3) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_CONST(3, Instruction::CONST, 2u, 2000), - DEF_CONST(3, Instruction::CONST, 3u, 3000), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), // Simple elimination of ADD+MUL - DEF_BINOP(3, Instruction::SUB_INT, 12u, 11u, 3u), // allows simple elimination of IGET+SUB. - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 5, 4, 6, 4, 5, 5, 4 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[4], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[6], value_names_[11]); - EXPECT_EQ(value_names_[7], value_names_[12]); - - const size_t no_null_ck_indexes[] = { 4, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false, false, true, true, true, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[6].ssa_rep->num_defs); - EXPECT_EQ(11, mirs_[6].ssa_rep->defs[0]); // 6 -> 11 - ASSERT_EQ(2, mirs_[6].ssa_rep->num_uses); - EXPECT_EQ(5, mirs_[6].ssa_rep->uses[0]); - EXPECT_EQ(2, mirs_[6].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[7].ssa_rep->num_defs); - EXPECT_EQ(12, mirs_[7].ssa_rep->defs[0]); // 7 -> 12 - ASSERT_EQ(2, mirs_[7].ssa_rep->num_uses); - EXPECT_EQ(11, mirs_[7].ssa_rep->uses[0]); // 6 -> 11 - EXPECT_EQ(3, mirs_[7].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_defs); - EXPECT_EQ(8, mirs_[8].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_uses); - EXPECT_EQ(12, mirs_[8].ssa_rep->uses[0]); // 7 -> 12 -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Simple4) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 1u, INT64_C(1)), - DEF_BINOP(3, Instruction::LONG_TO_FLOAT, 3u, 1u, 2u), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 5u, 4u), - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 6u, INT64_C(1)), - DEF_BINOP(3, Instruction::LONG_TO_FLOAT, 8u, 6u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 2, 3, 1, 2, 1, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 1, 6 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[1], value_names_[5]); - EXPECT_EQ(value_names_[2], value_names_[6]); - EXPECT_EQ(value_names_[3], value_names_[7]); - - const size_t no_null_ck_indexes[] = { 3, 7 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - // Simple elimination of CONST_WIDE+LONG_TO_FLOAT allows simple eliminatiion of IGET. - false, false, false, false, false, true, true, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[2].ssa_rep->num_defs); - EXPECT_EQ(8, mirs_[2].ssa_rep->defs[0]); // 3 -> 8 - ASSERT_EQ(2, mirs_[2].ssa_rep->num_uses); - EXPECT_EQ(1, mirs_[2].ssa_rep->uses[0]); - EXPECT_EQ(2, mirs_[2].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[3].ssa_rep->num_defs); - EXPECT_EQ(9, mirs_[3].ssa_rep->defs[0]); // 4 -> 9 - ASSERT_EQ(1, mirs_[3].ssa_rep->num_uses); - EXPECT_EQ(0, mirs_[3].ssa_rep->uses[0]); - ASSERT_EQ(1, mirs_[4].ssa_rep->num_defs); - EXPECT_EQ(5, mirs_[4].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[4].ssa_rep->num_uses); - EXPECT_EQ(9, mirs_[4].ssa_rep->uses[0]); // 4 -> 9 -} - -TEST_F(GvnDeadCodeEliminationTestSimple, KillChain1) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_CONST(3, Instruction::CONST, 2u, 2000), - DEF_CONST(3, Instruction::CONST, 3u, 3000), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 12u, 11u, 3u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 4, 5, 6, 4, 5, 4, 5 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[4], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[6], value_names_[11]); - EXPECT_EQ(value_names_[7], value_names_[12]); - - const size_t no_null_ck_indexes[] = { 4, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false, false, true, true, true, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[6].ssa_rep->num_defs); - EXPECT_EQ(11, mirs_[6].ssa_rep->defs[0]); // 6 -> 11 - ASSERT_EQ(2, mirs_[6].ssa_rep->num_uses); - EXPECT_EQ(5, mirs_[6].ssa_rep->uses[0]); - EXPECT_EQ(2, mirs_[6].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[7].ssa_rep->num_defs); - EXPECT_EQ(12, mirs_[7].ssa_rep->defs[0]); // 7 -> 12 - ASSERT_EQ(2, mirs_[7].ssa_rep->num_uses); - EXPECT_EQ(11, mirs_[7].ssa_rep->uses[0]); // 6 -> 11 - EXPECT_EQ(3, mirs_[7].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_defs); - EXPECT_EQ(8, mirs_[8].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_uses); - EXPECT_EQ(12, mirs_[8].ssa_rep->uses[0]); // 7 -> 12 -} - -TEST_F(GvnDeadCodeEliminationTestSimple, KillChain2) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_CONST(3, Instruction::CONST, 2u, 2000), - DEF_CONST(3, Instruction::CONST, 3u, 3000), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 12u, 11u, 3u), - DEF_CONST(3, Instruction::CONST, 13u, 4000), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 5, 4, 6, 4, 7, 7, 4, 7 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 13 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[4], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[6], value_names_[11]); - EXPECT_EQ(value_names_[7], value_names_[12]); - - const size_t no_null_ck_indexes[] = { 4, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false, false, true, true, true, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[7].ssa_rep->num_defs); - EXPECT_EQ(12, mirs_[7].ssa_rep->defs[0]); // 7 -> 12 - ASSERT_EQ(2, mirs_[7].ssa_rep->num_uses); - EXPECT_EQ(6, mirs_[7].ssa_rep->uses[0]); - EXPECT_EQ(3, mirs_[7].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_defs); - EXPECT_EQ(8, mirs_[8].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_uses); - EXPECT_EQ(12, mirs_[8].ssa_rep->uses[0]); // 7 -> 12 -} - -TEST_F(GvnDeadCodeEliminationTestSimple, KillChain3) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_CONST(3, Instruction::CONST, 2u, 2000), - DEF_CONST(3, Instruction::CONST, 3u, 3000), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), - DEF_CONST(3, Instruction::CONST, 12u, 4000), - DEF_BINOP(3, Instruction::SUB_INT, 13u, 11u, 3u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 5, 4, 6, 4, 7, 4, 7, 4 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 12 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[4], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[6], value_names_[11]); - EXPECT_EQ(value_names_[7], value_names_[13]); - - const size_t no_null_ck_indexes[] = { 4, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false, false, true, true, true, false, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[7].ssa_rep->num_defs); - EXPECT_EQ(13, mirs_[7].ssa_rep->defs[0]); // 7 -> 13 - ASSERT_EQ(2, mirs_[7].ssa_rep->num_uses); - EXPECT_EQ(6, mirs_[7].ssa_rep->uses[0]); - EXPECT_EQ(3, mirs_[7].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_defs); - EXPECT_EQ(8, mirs_[8].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_uses); - EXPECT_EQ(13, mirs_[8].ssa_rep->uses[0]); // 7 -> 13 -} - -TEST_F(GvnDeadCodeEliminationTestSimple, KeepChain1) { - // KillChain2 without the final CONST. - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_CONST(3, Instruction::CONST, 2u, 2000), - DEF_CONST(3, Instruction::CONST, 3u, 3000), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 12u, 11u, 3u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 5, 4, 6, 4, 7, 7, 4 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[4], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[6], value_names_[11]); - EXPECT_EQ(value_names_[7], value_names_[12]); - - const size_t no_null_ck_indexes[] = { 4, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false, false, false, false, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, KeepChain2) { - // KillChain1 with MIRs in the middle of the chain. - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_CONST(3, Instruction::CONST, 2u, 2000), - DEF_CONST(3, Instruction::CONST, 3u, 3000), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), - DEF_CONST(3, Instruction::CONST, 11u, 4000), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 12u, 11u), - DEF_BINOP(3, Instruction::MUL_INT, 13u, 10u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 14u, 13u, 3u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 4, 5, 6, 4, 5, 4, 7, 4, 5 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[4], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[6], value_names_[13]); - EXPECT_EQ(value_names_[7], value_names_[14]); - - const size_t no_null_ck_indexes[] = { 4, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestDiamond, CreatePhi1) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000), - DEF_CONST(4, Instruction::CONST, 1u, 1000), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - - static const bool eliminated[] = { - false, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that we've created a single-input Phi to replace the CONST 3u. - BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); - MIR* phi = bb4->first_mir_insn; - ASSERT_TRUE(phi != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi->dalvikInsn.opcode)); - ASSERT_EQ(1, phi->ssa_rep->num_uses); - EXPECT_EQ(0, phi->ssa_rep->uses[0]); - ASSERT_EQ(1, phi->ssa_rep->num_defs); - EXPECT_EQ(1, phi->ssa_rep->defs[0]); - EXPECT_EQ(0u, phi->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestDiamond, CreatePhi2) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000), - DEF_MOVE(4, Instruction::MOVE, 1u, 0u), - DEF_CONST(4, Instruction::CONST, 2u, 1000), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - static const bool eliminated[] = { - false, false, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that we've created a single-input Phi to replace the CONST 3u. - BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); - MIR* phi = bb4->first_mir_insn; - ASSERT_TRUE(phi != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi->dalvikInsn.opcode)); - ASSERT_EQ(1, phi->ssa_rep->num_uses); - EXPECT_EQ(0, phi->ssa_rep->uses[0]); - ASSERT_EQ(1, phi->ssa_rep->num_defs); - EXPECT_EQ(2, phi->ssa_rep->defs[0]); - EXPECT_EQ(0u, phi->dalvikInsn.vA); - MIR* move = phi->next; - ASSERT_TRUE(move != nullptr); - ASSERT_EQ(Instruction::MOVE, move->dalvikInsn.opcode); - ASSERT_EQ(1, move->ssa_rep->num_uses); - EXPECT_EQ(2, move->ssa_rep->uses[0]); - ASSERT_EQ(1, move->ssa_rep->num_defs); - EXPECT_EQ(1, move->ssa_rep->defs[0]); - EXPECT_EQ(1u, move->dalvikInsn.vA); - EXPECT_EQ(0u, move->dalvikInsn.vB); -} - -TEST_F(GvnDeadCodeEliminationTestDiamond, CreatePhi3) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(4, Instruction::CONST, 1u, 1000), - DEF_IPUT(4, Instruction::IPUT, 1u, 0u, 0u), - DEF_CONST(5, Instruction::CONST, 3u, 2000), - DEF_IPUT(5, Instruction::IPUT, 3u, 0u, 0u), - DEF_IGET(6, Instruction::IGET, 5u, 0u, 0u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2 /* dummy */, 1, 2 /* dummy */, 1 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 3, 5 }; - ExpectValueNamesNE(diff_indexes); - - const size_t no_null_ck_indexes[] = { 2, 4, 5 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that we've created a two-input Phi to replace the IGET 5u. - BasicBlock* bb6 = cu_.mir_graph->GetBasicBlock(6); - MIR* phi = bb6->first_mir_insn; - ASSERT_TRUE(phi != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi->dalvikInsn.opcode)); - ASSERT_EQ(2, phi->ssa_rep->num_uses); - EXPECT_EQ(1, phi->ssa_rep->uses[0]); - EXPECT_EQ(3, phi->ssa_rep->uses[1]); - ASSERT_EQ(1, phi->ssa_rep->num_defs); - EXPECT_EQ(5, phi->ssa_rep->defs[0]); - EXPECT_EQ(1u, phi->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestDiamond, KillChainInAnotherBlock1) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, // linked list - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 0u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 2u, 1u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 3u, 2u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 4u, 3u, 0u), - DEF_IFZ(3, Instruction::IF_NEZ, 4u), - DEF_IGET(4, Instruction::IGET_OBJECT, 6u, 0u, 0u), - DEF_IGET(4, Instruction::IGET_OBJECT, 7u, 6u, 0u), - DEF_IGET(4, Instruction::IGET_OBJECT, 8u, 7u, 0u), - DEF_IGET(4, Instruction::IGET_OBJECT, 9u, 8u, 0u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 2, 3 /* dummy */, 1, 2, 1, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[1], value_names_[6]); - EXPECT_EQ(value_names_[2], value_names_[7]); - EXPECT_EQ(value_names_[3], value_names_[8]); - EXPECT_EQ(value_names_[4], value_names_[9]); - - const size_t no_null_ck_indexes[] = { 1, 6, 7, 8, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, true, true, true, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that we've created two single-input Phis to replace the IGET 8u and IGET 9u; - // the IGET 6u and IGET 7u were killed without a replacement. - BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); - MIR* phi1 = bb4->first_mir_insn; - ASSERT_TRUE(phi1 != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi1->dalvikInsn.opcode)); - MIR* phi2 = phi1->next; - ASSERT_TRUE(phi2 != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi2->dalvikInsn.opcode)); - ASSERT_TRUE(phi2->next == &mirs_[6]); - if (phi1->dalvikInsn.vA == 2u) { - std::swap(phi1, phi2); - } - ASSERT_EQ(1, phi1->ssa_rep->num_uses); - EXPECT_EQ(3, phi1->ssa_rep->uses[0]); - ASSERT_EQ(1, phi1->ssa_rep->num_defs); - EXPECT_EQ(8, phi1->ssa_rep->defs[0]); - EXPECT_EQ(1u, phi1->dalvikInsn.vA); - ASSERT_EQ(1, phi2->ssa_rep->num_uses); - EXPECT_EQ(4, phi2->ssa_rep->uses[0]); - ASSERT_EQ(1, phi2->ssa_rep->num_defs); - EXPECT_EQ(9, phi2->ssa_rep->defs[0]); - EXPECT_EQ(2u, phi2->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestDiamond, KillChainInAnotherBlock2) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, // linked list - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 0u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 2u, 1u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 3u, 2u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 4u, 3u, 0u), - DEF_IFZ(3, Instruction::IF_NEZ, 4u), - DEF_IGET(4, Instruction::IGET_OBJECT, 6u, 0u, 0u), - DEF_IGET(4, Instruction::IGET_OBJECT, 7u, 6u, 0u), - DEF_IGET(4, Instruction::IGET_OBJECT, 8u, 7u, 0u), - DEF_CONST(4, Instruction::CONST, 9u, 1000), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 2, 3 /* dummy */, 1, 2, 1, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 9 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[1], value_names_[6]); - EXPECT_EQ(value_names_[2], value_names_[7]); - EXPECT_EQ(value_names_[3], value_names_[8]); - - const size_t no_null_ck_indexes[] = { 1, 6, 7, 8 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, true, true, true, false, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that we've created a single-input Phi to replace the IGET 8u; - // the IGET 6u and IGET 7u were killed without a replacement. - BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); - MIR* phi = bb4->first_mir_insn; - ASSERT_TRUE(phi != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi->dalvikInsn.opcode)); - ASSERT_TRUE(phi->next == &mirs_[6]); - ASSERT_EQ(1, phi->ssa_rep->num_uses); - EXPECT_EQ(3, phi->ssa_rep->uses[0]); - ASSERT_EQ(1, phi->ssa_rep->num_defs); - EXPECT_EQ(8, phi->ssa_rep->defs[0]); - EXPECT_EQ(1u, phi->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestLoop, IFieldLoopVariable) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1), - DEF_CONST(3, Instruction::CONST, 2u, 0), - DEF_IPUT(3, Instruction::IPUT, 2u, 0u, 0u), - DEF_IGET(4, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(4, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_IPUT(4, Instruction::IPUT, 5u, 0u, 0u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3 /* dummy */, 2, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 4, 5 }; - ExpectValueNamesNE(diff_indexes); - - const size_t no_null_ck_indexes[] = { 3, 4, 6 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, true, false, false, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that we've created a two-input Phi to replace the IGET 3u. - BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); - MIR* phi = bb4->first_mir_insn; - ASSERT_TRUE(phi != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi->dalvikInsn.opcode)); - ASSERT_TRUE(phi->next == &mirs_[4]); - ASSERT_EQ(2, phi->ssa_rep->num_uses); - EXPECT_EQ(2, phi->ssa_rep->uses[0]); - EXPECT_EQ(5, phi->ssa_rep->uses[1]); - ASSERT_EQ(1, phi->ssa_rep->num_defs); - EXPECT_EQ(4, phi->ssa_rep->defs[0]); - EXPECT_EQ(2u, phi->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestDiamond, LongOverlaps1) { - static const MIRDef mirs[] = { - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000u), - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 2u, 1000u), - DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 4u, 0u), - DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 6u, 2u), - DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 8u, 4u), - DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 10u, 6u), - }; - - // The last insn should overlap the first and second. - static const int32_t sreg_to_vreg_map[] = { 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 0, 2, 4, 6, 8, 10 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - EXPECT_EQ(value_names_[0], value_names_[3]); - EXPECT_EQ(value_names_[0], value_names_[4]); - - static const bool eliminated[] = { - false, false, false, false, false, false, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, LongOverlaps2) { - static const MIRDef mirs[] = { - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000u), - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 2u, 0u), - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 4u, 2u), - }; - - // The last insn should overlap the first and second. - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 1, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 0, 2, 4 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - static const bool eliminated[] = { - false, true, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the CONST_WIDE registers have been correctly renamed. - MIR* const_wide = &mirs_[0]; - ASSERT_EQ(2u, const_wide->ssa_rep->num_defs); - EXPECT_EQ(4, const_wide->ssa_rep->defs[0]); - EXPECT_EQ(5, const_wide->ssa_rep->defs[1]); - EXPECT_EQ(1u, const_wide->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, LongOverlaps3) { - static const MIRDef mirs[] = { - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000u), - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 2u, 0u), - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 4u, 2u), - }; - - // The last insn should overlap the first and second. - static const int32_t sreg_to_vreg_map[] = { 2, 3, 0, 1, 1, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 0, 2, 4 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - static const bool eliminated[] = { - false, true, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the CONST_WIDE registers have been correctly renamed. - MIR* const_wide = &mirs_[0]; - ASSERT_EQ(2u, const_wide->ssa_rep->num_defs); - EXPECT_EQ(4, const_wide->ssa_rep->defs[0]); - EXPECT_EQ(5, const_wide->ssa_rep->defs[1]); - EXPECT_EQ(1u, const_wide->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, MixedOverlaps1) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_MOVE(3, Instruction::MOVE, 1u, 0u), - DEF_CONST(3, Instruction::CONST, 2u, 2000u), - { 3, Instruction::INT_TO_LONG, 0, 0u, 1, { 2u }, 2, { 3u, 4u } }, - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 5u, 3u), - DEF_CONST(3, Instruction::CONST, 7u, 3000u), - DEF_CONST(3, Instruction::CONST, 8u, 4000u), - }; - - static const int32_t sreg_to_vreg_map[] = { 1, 2, 0, 0, 1, 3, 4, 0, 1 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 3, 5 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 2, 3, 5, 6 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[3], value_names_[4]); - - static const bool eliminated[] = { - false, true, false, false, true, false, false, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check renamed registers in CONST. - MIR* cst = &mirs_[0]; - ASSERT_EQ(Instruction::CONST, cst->dalvikInsn.opcode); - ASSERT_EQ(0, cst->ssa_rep->num_uses); - ASSERT_EQ(1, cst->ssa_rep->num_defs); - EXPECT_EQ(1, cst->ssa_rep->defs[0]); - EXPECT_EQ(2u, cst->dalvikInsn.vA); - // Check renamed registers in INT_TO_LONG. - MIR* int_to_long = &mirs_[3]; - ASSERT_EQ(Instruction::INT_TO_LONG, int_to_long->dalvikInsn.opcode); - ASSERT_EQ(1, int_to_long->ssa_rep->num_uses); - EXPECT_EQ(2, int_to_long->ssa_rep->uses[0]); - ASSERT_EQ(2, int_to_long->ssa_rep->num_defs); - EXPECT_EQ(5, int_to_long->ssa_rep->defs[0]); - EXPECT_EQ(6, int_to_long->ssa_rep->defs[1]); - EXPECT_EQ(3u, int_to_long->dalvikInsn.vA); - EXPECT_EQ(0u, int_to_long->dalvikInsn.vB); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, UnusedRegs1) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_CONST(3, Instruction::CONST, 1u, 2000u), - DEF_BINOP(3, Instruction::ADD_INT, 2u, 1u, 0u), - DEF_CONST(3, Instruction::CONST, 3u, 1000u), // NOT killed (b/21702651). - DEF_BINOP(3, Instruction::ADD_INT, 4u, 1u, 3u), // Killed (RecordPass) - DEF_CONST(3, Instruction::CONST, 5u, 2000u), // Killed with 9u (BackwardPass) - DEF_BINOP(3, Instruction::ADD_INT, 6u, 5u, 0u), // Killed (RecordPass) - DEF_CONST(3, Instruction::CONST, 7u, 4000u), - DEF_MOVE(3, Instruction::MOVE, 8u, 0u), // Killed with 6u (BackwardPass) - }; - - static const int32_t sreg_to_vreg_map[] = { 1, 2, 3, 0, 3, 0, 3, 4, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 7 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[3]); - EXPECT_EQ(value_names_[2], value_names_[4]); - EXPECT_EQ(value_names_[1], value_names_[5]); - EXPECT_EQ(value_names_[2], value_names_[6]); - EXPECT_EQ(value_names_[0], value_names_[8]); - - static const bool eliminated[] = { - false, false, false, false, true, true, true, false, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, UnusedRegs2) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_CONST(3, Instruction::CONST, 1u, 2000u), - DEF_BINOP(3, Instruction::ADD_INT, 2u, 1u, 0u), - DEF_CONST(3, Instruction::CONST, 3u, 1000u), // Killed (BackwardPass; b/21702651) - DEF_BINOP(3, Instruction::ADD_INT, 4u, 1u, 3u), // Killed (RecordPass) - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 5u, 4000u), - { 3, Instruction::LONG_TO_INT, 0, 0u, 2, { 5u, 6u }, 1, { 7u } }, - DEF_BINOP(3, Instruction::ADD_INT, 8u, 7u, 0u), - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 9u, 4000u), // Killed with 12u (BackwardPass) - DEF_CONST(3, Instruction::CONST, 11u, 6000u), - { 3, Instruction::LONG_TO_INT, 0, 0u, 2, { 9u, 10u }, 1, { 12u } }, // Killed with 9u (BP) - }; - - static const int32_t sreg_to_vreg_map[] = { - 2, 3, 4, 1, 4, 5, 6 /* high word */, 0, 7, 0, 1 /* high word */, 8, 0 - }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 5, 9 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 5, 6, 7, 9 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[3]); - EXPECT_EQ(value_names_[2], value_names_[4]); - EXPECT_EQ(value_names_[5], value_names_[8]); - EXPECT_EQ(value_names_[6], value_names_[10]); - - static const bool eliminated[] = { - false, false, false, true, true, false, false, false, true, false, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, ArrayLengthThrows) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 0), // null - DEF_UNOP(3, Instruction::ARRAY_LENGTH, 1u, 0u), // null.length - DEF_CONST(3, Instruction::CONST, 2u, 1000u), // Overwrite the array-length dest. - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 1 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2 }; - ExpectValueNamesNE(diff_indexes); - - static const bool eliminated[] = { - false, false, false, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Dependancy) { - static const MIRDef mirs[] = { - DEF_MOVE(3, Instruction::MOVE, 5u, 1u), // move v5,v1 - DEF_MOVE(3, Instruction::MOVE, 6u, 1u), // move v12,v1 - DEF_MOVE(3, Instruction::MOVE, 7u, 0u), // move v13,v0 - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 8u, 2u), // move v0_1,v2_3 - DEF_MOVE(3, Instruction::MOVE, 10u, 6u), // move v3,v12 - DEF_MOVE(3, Instruction::MOVE, 11u, 4u), // move v2,v4 - DEF_MOVE(3, Instruction::MOVE, 12u, 7u), // move v4,v13 - DEF_MOVE(3, Instruction::MOVE, 13, 11u), // move v12,v2 - DEF_MOVE(3, Instruction::MOVE, 14u, 10u), // move v2,v3 - DEF_MOVE(3, Instruction::MOVE, 15u, 5u), // move v3,v5 - DEF_MOVE(3, Instruction::MOVE, 16u, 12u), // move v5,v4 - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 12, 13, 0, 1, 3, 2, 4, 12, 2, 3, 5 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 2, 8 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, true, true, false, false, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -} // namespace art diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc deleted file mode 100644 index 38f7d1e712a35a8c3d7950c7189953229133f0a9..0000000000000000000000000000000000000000 --- a/compiler/dex/local_value_numbering.cc +++ /dev/null @@ -1,2038 +0,0 @@ -/* - * Copyright (C) 2012 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 "local_value_numbering.h" - -#include "base/bit_utils.h" -#include "global_value_numbering.h" -#include "mir_field_info.h" -#include "mir_graph.h" -#include "utils.h" - -namespace art { - -namespace { // anonymous namespace - -// Operations used for value map keys instead of actual opcode. -static constexpr uint16_t kInvokeMemoryVersionBumpOp = Instruction::INVOKE_VIRTUAL; -static constexpr uint16_t kUnresolvedSFieldOp = Instruction::SGET; -static constexpr uint16_t kResolvedSFieldOp = Instruction::SGET_WIDE; -static constexpr uint16_t kUnresolvedIFieldOp = Instruction::IGET; -static constexpr uint16_t kNonAliasingIFieldLocOp = Instruction::IGET_WIDE; -static constexpr uint16_t kNonAliasingIFieldInitialOp = Instruction::IGET_OBJECT; -static constexpr uint16_t kAliasingIFieldOp = Instruction::IGET_BOOLEAN; -static constexpr uint16_t kAliasingIFieldStartVersionOp = Instruction::IGET_BYTE; -static constexpr uint16_t kAliasingIFieldBumpVersionOp = Instruction::IGET_CHAR; -static constexpr uint16_t kNonAliasingArrayOp = Instruction::AGET; -static constexpr uint16_t kNonAliasingArrayStartVersionOp = Instruction::AGET_WIDE; -static constexpr uint16_t kNonAliasingArrayBumpVersionOp = Instruction::AGET_OBJECT; -static constexpr uint16_t kAliasingArrayOp = Instruction::AGET_BOOLEAN; -static constexpr uint16_t kAliasingArrayStartVersionOp = Instruction::AGET_BYTE; -static constexpr uint16_t kAliasingArrayBumpVersionOp = Instruction::AGET_CHAR; -static constexpr uint16_t kMergeBlockMemoryVersionBumpOp = Instruction::INVOKE_VIRTUAL_RANGE; -static constexpr uint16_t kMergeBlockAliasingIFieldVersionBumpOp = Instruction::IPUT; -static constexpr uint16_t kMergeBlockAliasingIFieldMergeLocationOp = Instruction::IPUT_WIDE; -static constexpr uint16_t kMergeBlockNonAliasingArrayVersionBumpOp = Instruction::APUT; -static constexpr uint16_t kMergeBlockNonAliasingArrayMergeLocationOp = Instruction::APUT_WIDE; -static constexpr uint16_t kMergeBlockAliasingArrayVersionBumpOp = Instruction::APUT_OBJECT; -static constexpr uint16_t kMergeBlockAliasingArrayMergeLocationOp = Instruction::APUT_BOOLEAN; -static constexpr uint16_t kMergeBlockNonAliasingIFieldVersionBumpOp = Instruction::APUT_BYTE; -static constexpr uint16_t kMergeBlockSFieldVersionBumpOp = Instruction::APUT_CHAR; - -} // anonymous namespace - -class LocalValueNumbering::AliasingIFieldVersions { - public: - static uint16_t StartMemoryVersion(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, - uint16_t field_id) { - uint16_t type = gvn->GetIFieldType(field_id); - return gvn->LookupValue(kAliasingIFieldStartVersionOp, field_id, - lvn->global_memory_version_, lvn->unresolved_ifield_version_[type]); - } - - static uint16_t BumpMemoryVersion(GlobalValueNumbering* gvn, uint16_t old_version, - uint16_t store_ref_set_id, uint16_t stored_value) { - return gvn->LookupValue(kAliasingIFieldBumpVersionOp, old_version, - store_ref_set_id, stored_value); - } - - static uint16_t LookupGlobalValue(GlobalValueNumbering* gvn, - uint16_t field_id, uint16_t base, uint16_t memory_version) { - return gvn->LookupValue(kAliasingIFieldOp, field_id, base, memory_version); - } - - static uint16_t LookupMergeValue(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, - uint16_t field_id, uint16_t base) { - // If the base/field_id is non-aliasing in lvn, use the non-aliasing value. - uint16_t type = gvn->GetIFieldType(field_id); - if (lvn->IsNonAliasingIField(base, field_id, type)) { - uint16_t loc = gvn->LookupValue(kNonAliasingIFieldLocOp, base, field_id, type); - auto lb = lvn->non_aliasing_ifield_value_map_.find(loc); - return (lb != lvn->non_aliasing_ifield_value_map_.end()) - ? lb->second - : gvn->LookupValue(kNonAliasingIFieldInitialOp, loc, kNoValue, kNoValue); - } - return AliasingValuesMergeGet( - gvn, lvn, &lvn->aliasing_ifield_value_map_, field_id, base); - } - - static bool HasNewBaseVersion(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, - uint16_t field_id) { - uint16_t type = gvn->GetIFieldType(field_id); - return lvn->unresolved_ifield_version_[type] == lvn->merge_new_memory_version_ || - lvn->global_memory_version_ == lvn->merge_new_memory_version_; - } - - static uint16_t LookupMergeBlockValue(GlobalValueNumbering* gvn, uint16_t lvn_id, - uint16_t field_id) { - return gvn->LookupValue(kMergeBlockAliasingIFieldVersionBumpOp, field_id, kNoValue, lvn_id); - } - - static uint16_t LookupMergeLocationValue(GlobalValueNumbering* gvn, uint16_t lvn_id, - uint16_t field_id, uint16_t base) { - return gvn->LookupValue(kMergeBlockAliasingIFieldMergeLocationOp, field_id, base, lvn_id); - } -}; - -class LocalValueNumbering::NonAliasingArrayVersions { - public: - static uint16_t StartMemoryVersion(GlobalValueNumbering* gvn, - const LocalValueNumbering* lvn ATTRIBUTE_UNUSED, - uint16_t array) { - return gvn->LookupValue(kNonAliasingArrayStartVersionOp, array, kNoValue, kNoValue); - } - - static uint16_t BumpMemoryVersion(GlobalValueNumbering* gvn, uint16_t old_version, - uint16_t store_ref_set_id, uint16_t stored_value) { - return gvn->LookupValue(kNonAliasingArrayBumpVersionOp, old_version, - store_ref_set_id, stored_value); - } - - static uint16_t LookupGlobalValue(GlobalValueNumbering* gvn, - uint16_t array, uint16_t index, uint16_t memory_version) { - return gvn->LookupValue(kNonAliasingArrayOp, array, index, memory_version); - } - - static uint16_t LookupMergeValue(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, - uint16_t array, uint16_t index) { - return AliasingValuesMergeGet( - gvn, lvn, &lvn->non_aliasing_array_value_map_, array, index); - } - - static bool HasNewBaseVersion(GlobalValueNumbering* gvn ATTRIBUTE_UNUSED, - const LocalValueNumbering* lvn ATTRIBUTE_UNUSED, - uint16_t array ATTRIBUTE_UNUSED) { - return false; // Not affected by global_memory_version_. - } - - static uint16_t LookupMergeBlockValue(GlobalValueNumbering* gvn, uint16_t lvn_id, - uint16_t array) { - return gvn->LookupValue(kMergeBlockNonAliasingArrayVersionBumpOp, array, kNoValue, lvn_id); - } - - static uint16_t LookupMergeLocationValue(GlobalValueNumbering* gvn, uint16_t lvn_id, - uint16_t array, uint16_t index) { - return gvn->LookupValue(kMergeBlockNonAliasingArrayMergeLocationOp, array, index, lvn_id); - } -}; - -class LocalValueNumbering::AliasingArrayVersions { - public: - static uint16_t StartMemoryVersion(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, - uint16_t type) { - return gvn->LookupValue(kAliasingArrayStartVersionOp, type, lvn->global_memory_version_, - kNoValue); - } - - static uint16_t BumpMemoryVersion(GlobalValueNumbering* gvn, uint16_t old_version, - uint16_t store_ref_set_id, uint16_t stored_value) { - return gvn->LookupValue(kAliasingArrayBumpVersionOp, old_version, - store_ref_set_id, stored_value); - } - - static uint16_t LookupGlobalValue(GlobalValueNumbering* gvn, - uint16_t type, uint16_t location, uint16_t memory_version) { - return gvn->LookupValue(kAliasingArrayOp, type, location, memory_version); - } - - static uint16_t LookupMergeValue(GlobalValueNumbering* gvn, - const LocalValueNumbering* lvn, - uint16_t type, uint16_t location) { - // If the location is non-aliasing in lvn, use the non-aliasing value. - uint16_t array = gvn->GetArrayLocationBase(location); - if (lvn->IsNonAliasingArray(array, type)) { - uint16_t index = gvn->GetArrayLocationIndex(location); - return NonAliasingArrayVersions::LookupMergeValue(gvn, lvn, array, index); - } - return AliasingValuesMergeGet( - gvn, lvn, &lvn->aliasing_array_value_map_, type, location); - } - - static bool HasNewBaseVersion(GlobalValueNumbering* gvn ATTRIBUTE_UNUSED, - const LocalValueNumbering* lvn, - uint16_t type ATTRIBUTE_UNUSED) { - return lvn->global_memory_version_ == lvn->merge_new_memory_version_; - } - - static uint16_t LookupMergeBlockValue(GlobalValueNumbering* gvn, uint16_t lvn_id, - uint16_t type) { - return gvn->LookupValue(kMergeBlockAliasingArrayVersionBumpOp, type, kNoValue, lvn_id); - } - - static uint16_t LookupMergeLocationValue(GlobalValueNumbering* gvn, uint16_t lvn_id, - uint16_t type, uint16_t location) { - return gvn->LookupValue(kMergeBlockAliasingArrayMergeLocationOp, type, location, lvn_id); - } -}; - -template -LocalValueNumbering::AliasingValues* LocalValueNumbering::GetAliasingValues( - Map* map, const typename Map::key_type& key) { - auto lb = map->lower_bound(key); - if (lb == map->end() || map->key_comp()(key, lb->first)) { - lb = map->PutBefore(lb, key, AliasingValues(this)); - } - return &lb->second; -} - -template -void LocalValueNumbering::UpdateAliasingValuesLoadVersion(const KeyType& key, - AliasingValues* values) { - if (values->last_load_memory_version == kNoValue) { - // Get the start version that accounts for aliasing with unresolved fields of the same - // type and make it unique for the field by including the field_id. - uint16_t memory_version = values->memory_version_before_stores; - if (memory_version == kNoValue) { - memory_version = Versions::StartMemoryVersion(gvn_, this, key); - } - if (!values->store_loc_set.empty()) { - uint16_t ref_set_id = gvn_->GetRefSetId(values->store_loc_set); - memory_version = Versions::BumpMemoryVersion(gvn_, memory_version, ref_set_id, - values->last_stored_value); - } - values->last_load_memory_version = memory_version; - } -} - -template -uint16_t LocalValueNumbering::AliasingValuesMergeGet(GlobalValueNumbering* gvn, - const LocalValueNumbering* lvn, - Map* map, const typename Map::key_type& key, - uint16_t location) { - // Retrieve the value name that we would get from - // const_cast(lvn)->HandleAliasingValueGet(map. key, location) - // but don't modify the map. - uint16_t value_name; - auto it = map->find(key); - if (it == map->end()) { - uint16_t start_version = Versions::StartMemoryVersion(gvn, lvn, key); - value_name = Versions::LookupGlobalValue(gvn, key, location, start_version); - } else if (it->second.store_loc_set.count(location) != 0u) { - value_name = it->second.last_stored_value; - } else { - auto load_it = it->second.load_value_map.find(location); - if (load_it != it->second.load_value_map.end()) { - value_name = load_it->second; - } else { - value_name = Versions::LookupGlobalValue(gvn, key, location, it->second.last_load_memory_version); - } - } - return value_name; -} - -template -uint16_t LocalValueNumbering::HandleAliasingValuesGet(Map* map, const typename Map::key_type& key, - uint16_t location) { - // Retrieve the value name for IGET/SGET/AGET, update the map with new value if any. - uint16_t res; - AliasingValues* values = GetAliasingValues(map, key); - if (values->store_loc_set.count(location) != 0u) { - res = values->last_stored_value; - } else { - UpdateAliasingValuesLoadVersion(key, values); - auto lb = values->load_value_map.lower_bound(location); - if (lb != values->load_value_map.end() && lb->first == location) { - res = lb->second; - } else { - res = Versions::LookupGlobalValue(gvn_, key, location, values->last_load_memory_version); - values->load_value_map.PutBefore(lb, location, res); - } - } - return res; -} - -template -bool LocalValueNumbering::HandleAliasingValuesPut(Map* map, const typename Map::key_type& key, - uint16_t location, uint16_t value) { - AliasingValues* values = GetAliasingValues(map, key); - auto load_values_it = values->load_value_map.find(location); - if (load_values_it != values->load_value_map.end() && load_values_it->second == value) { - // This insn can be eliminated, it stores the same value that's already in the field. - return false; - } - if (value == values->last_stored_value) { - auto store_loc_lb = values->store_loc_set.lower_bound(location); - if (store_loc_lb != values->store_loc_set.end() && *store_loc_lb == location) { - // This insn can be eliminated, it stores the same value that's already in the field. - return false; - } - values->store_loc_set.emplace_hint(store_loc_lb, location); - } else { - UpdateAliasingValuesLoadVersion(key, values); - values->memory_version_before_stores = values->last_load_memory_version; - values->last_stored_value = value; - values->store_loc_set.clear(); - values->store_loc_set.insert(location); - } - // Clear the last load memory version and remove all potentially overwritten values. - values->last_load_memory_version = kNoValue; - auto it = values->load_value_map.begin(), end = values->load_value_map.end(); - while (it != end) { - if (it->second == value) { - ++it; - } else { - it = values->load_value_map.erase(it); - } - } - return true; -} - -template -void LocalValueNumbering::CopyAliasingValuesMap(ScopedArenaSafeMap* dest, - const ScopedArenaSafeMap& src) { - // We need each new AliasingValues (or rather its map members) to be constructed - // with our allocator, rather than the allocator of the source. - for (const auto& entry : src) { - auto it = dest->PutBefore(dest->end(), entry.first, AliasingValues(this)); - it->second = entry.second; // Map assignments preserve current allocator. - } -} - -LocalValueNumbering::LocalValueNumbering(GlobalValueNumbering* gvn, uint16_t id, - ScopedArenaAllocator* allocator) - : gvn_(gvn), - id_(id), - sreg_value_map_(std::less(), allocator->Adapter()), - sreg_wide_value_map_(std::less(), allocator->Adapter()), - sfield_value_map_(std::less(), allocator->Adapter()), - non_aliasing_ifield_value_map_(std::less(), allocator->Adapter()), - aliasing_ifield_value_map_(std::less(), allocator->Adapter()), - non_aliasing_array_value_map_(std::less(), allocator->Adapter()), - aliasing_array_value_map_(std::less(), allocator->Adapter()), - global_memory_version_(0u), - non_aliasing_refs_(std::less(), allocator->Adapter()), - escaped_refs_(std::less(), allocator->Adapter()), - escaped_ifield_clobber_set_(EscapedIFieldClobberKeyComparator(), allocator->Adapter()), - escaped_array_clobber_set_(EscapedArrayClobberKeyComparator(), allocator->Adapter()), - range_checked_(RangeCheckKeyComparator() , allocator->Adapter()), - null_checked_(std::less(), allocator->Adapter()), - div_zero_checked_(std::less(), allocator->Adapter()), - merge_names_(allocator->Adapter()), - merge_map_(std::less>(), allocator->Adapter()), - merge_new_memory_version_(kNoValue) { - std::fill_n(unresolved_sfield_version_, arraysize(unresolved_sfield_version_), 0u); - std::fill_n(unresolved_ifield_version_, arraysize(unresolved_ifield_version_), 0u); -} - -bool LocalValueNumbering::Equals(const LocalValueNumbering& other) const { - DCHECK(gvn_ == other.gvn_); - // Compare the maps/sets and memory versions. - return sreg_value_map_ == other.sreg_value_map_ && - sreg_wide_value_map_ == other.sreg_wide_value_map_ && - sfield_value_map_ == other.sfield_value_map_ && - non_aliasing_ifield_value_map_ == other.non_aliasing_ifield_value_map_ && - aliasing_ifield_value_map_ == other.aliasing_ifield_value_map_ && - non_aliasing_array_value_map_ == other.non_aliasing_array_value_map_ && - aliasing_array_value_map_ == other.aliasing_array_value_map_ && - SameMemoryVersion(other) && - non_aliasing_refs_ == other.non_aliasing_refs_ && - escaped_refs_ == other.escaped_refs_ && - escaped_ifield_clobber_set_ == other.escaped_ifield_clobber_set_ && - escaped_array_clobber_set_ == other.escaped_array_clobber_set_ && - range_checked_ == other.range_checked_ && - null_checked_ == other.null_checked_ && - div_zero_checked_ == other.div_zero_checked_; -} - -void LocalValueNumbering::MergeOne(const LocalValueNumbering& other, MergeType merge_type) { - CopyLiveSregValues(&sreg_value_map_, other.sreg_value_map_); - CopyLiveSregValues(&sreg_wide_value_map_, other.sreg_wide_value_map_); - - if (merge_type == kReturnMerge) { - // RETURN or PHI+RETURN. We need only sreg value maps. - return; - } - - non_aliasing_ifield_value_map_ = other.non_aliasing_ifield_value_map_; - CopyAliasingValuesMap(&non_aliasing_array_value_map_, other.non_aliasing_array_value_map_); - non_aliasing_refs_ = other.non_aliasing_refs_; - range_checked_ = other.range_checked_; - null_checked_ = other.null_checked_; - div_zero_checked_ = other.div_zero_checked_; - - const BasicBlock* pred_bb = gvn_->GetBasicBlock(other.Id()); - if (GlobalValueNumbering::HasNullCheckLastInsn(pred_bb, Id())) { - int s_reg = pred_bb->last_mir_insn->ssa_rep->uses[0]; - null_checked_.insert(other.GetOperandValue(s_reg)); - } - - if (merge_type == kCatchMerge) { - // Memory is clobbered. Use new memory version and don't merge aliasing locations. - global_memory_version_ = NewMemoryVersion(&merge_new_memory_version_); - std::fill_n(unresolved_sfield_version_, arraysize(unresolved_sfield_version_), - global_memory_version_); - std::fill_n(unresolved_ifield_version_, arraysize(unresolved_ifield_version_), - global_memory_version_); - PruneNonAliasingRefsForCatch(); - return; - } - - DCHECK(merge_type == kNormalMerge); - global_memory_version_ = other.global_memory_version_; - std::copy_n(other.unresolved_ifield_version_, arraysize(unresolved_sfield_version_), - unresolved_ifield_version_); - std::copy_n(other.unresolved_sfield_version_, arraysize(unresolved_ifield_version_), - unresolved_sfield_version_); - sfield_value_map_ = other.sfield_value_map_; - CopyAliasingValuesMap(&aliasing_ifield_value_map_, other.aliasing_ifield_value_map_); - CopyAliasingValuesMap(&aliasing_array_value_map_, other.aliasing_array_value_map_); - escaped_refs_ = other.escaped_refs_; - escaped_ifield_clobber_set_ = other.escaped_ifield_clobber_set_; - escaped_array_clobber_set_ = other.escaped_array_clobber_set_; -} - -bool LocalValueNumbering::SameMemoryVersion(const LocalValueNumbering& other) const { - return - global_memory_version_ == other.global_memory_version_ && - std::equal(unresolved_ifield_version_, - unresolved_ifield_version_ + arraysize(unresolved_ifield_version_), - other.unresolved_ifield_version_) && - std::equal(unresolved_sfield_version_, - unresolved_sfield_version_ + arraysize(unresolved_sfield_version_), - other.unresolved_sfield_version_); -} - -uint16_t LocalValueNumbering::NewMemoryVersion(uint16_t* new_version) { - if (*new_version == kNoValue) { - *new_version = gvn_->LookupValue(kMergeBlockMemoryVersionBumpOp, 0u, 0u, id_); - } - return *new_version; -} - -void LocalValueNumbering::MergeMemoryVersions(bool clobbered_catch) { - DCHECK_GE(gvn_->merge_lvns_.size(), 2u); - const LocalValueNumbering* cmp = gvn_->merge_lvns_[0]; - // Check if the global version has changed. - bool new_global_version = clobbered_catch; - if (!new_global_version) { - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn->global_memory_version_ != cmp->global_memory_version_) { - // Use a new version for everything. - new_global_version = true; - break; - } - } - } - if (new_global_version) { - global_memory_version_ = NewMemoryVersion(&merge_new_memory_version_); - std::fill_n(unresolved_sfield_version_, arraysize(unresolved_sfield_version_), - merge_new_memory_version_); - std::fill_n(unresolved_ifield_version_, arraysize(unresolved_ifield_version_), - merge_new_memory_version_); - } else { - // Initialize with a copy of memory versions from the comparison LVN. - global_memory_version_ = cmp->global_memory_version_; - std::copy_n(cmp->unresolved_ifield_version_, arraysize(unresolved_sfield_version_), - unresolved_ifield_version_); - std::copy_n(cmp->unresolved_sfield_version_, arraysize(unresolved_ifield_version_), - unresolved_sfield_version_); - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn == cmp) { - continue; - } - for (size_t i = 0; i != kDexMemAccessTypeCount; ++i) { - if (lvn->unresolved_ifield_version_[i] != cmp->unresolved_ifield_version_[i]) { - unresolved_ifield_version_[i] = NewMemoryVersion(&merge_new_memory_version_); - } - if (lvn->unresolved_sfield_version_[i] != cmp->unresolved_sfield_version_[i]) { - unresolved_sfield_version_[i] = NewMemoryVersion(&merge_new_memory_version_); - } - } - } - } -} - -void LocalValueNumbering::PruneNonAliasingRefsForCatch() { - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - const BasicBlock* bb = gvn_->GetBasicBlock(lvn->Id()); - if (UNLIKELY(bb->taken == id_) || UNLIKELY(bb->fall_through == id_)) { - // Non-exceptional path to a catch handler means that the catch block was actually - // empty and all exceptional paths lead to the shared path after that empty block. - continue; - } - DCHECK_EQ(bb->taken, kNullBlock); - DCHECK_NE(bb->fall_through, kNullBlock); - const BasicBlock* fall_through_bb = gvn_->GetBasicBlock(bb->fall_through); - const MIR* mir = fall_through_bb->first_mir_insn; - DCHECK(mir != nullptr); - // Only INVOKEs can leak and clobber non-aliasing references if they throw. - if ((mir->dalvikInsn.FlagsOf() & Instruction::kInvoke) != 0) { - HandleInvokeArgs(mir, lvn); - } - } -} - - -template -void LocalValueNumbering::IntersectSets() { - DCHECK_GE(gvn_->merge_lvns_.size(), 2u); - - // Find the LVN with the least entries in the set. - const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0]; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if ((lvn->*set_ptr).size() < (least_entries_lvn->*set_ptr).size()) { - least_entries_lvn = lvn; - } - } - - // For each key check if it's in all the LVNs. - for (const auto& key : least_entries_lvn->*set_ptr) { - bool checked = true; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn != least_entries_lvn && (lvn->*set_ptr).count(key) == 0u) { - checked = false; - break; - } - } - if (checked) { - (this->*set_ptr).emplace_hint((this->*set_ptr).end(), key); - } - } -} - -void LocalValueNumbering::CopyLiveSregValues(SregValueMap* dest, const SregValueMap& src) { - auto dest_end = dest->end(); - ArenaBitVector* live_in_v = gvn_->GetMirGraph()->GetBasicBlock(id_)->data_flow_info->live_in_v; - DCHECK(live_in_v != nullptr); - for (const auto& entry : src) { - bool live = live_in_v->IsBitSet(gvn_->GetMirGraph()->SRegToVReg(entry.first)); - if (live) { - dest->PutBefore(dest_end, entry.first, entry.second); - } - } -} - -template -void LocalValueNumbering::IntersectSregValueMaps() { - DCHECK_GE(gvn_->merge_lvns_.size(), 2u); - - // Find the LVN with the least entries in the set. - const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0]; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if ((lvn->*map_ptr).size() < (least_entries_lvn->*map_ptr).size()) { - least_entries_lvn = lvn; - } - } - - // For each key check if it's in all the LVNs. - ArenaBitVector* live_in_v = gvn_->GetMirGraph()->GetBasicBlock(id_)->data_flow_info->live_in_v; - DCHECK(live_in_v != nullptr); - for (const auto& entry : least_entries_lvn->*map_ptr) { - bool live_and_same = live_in_v->IsBitSet(gvn_->GetMirGraph()->SRegToVReg(entry.first)); - if (live_and_same) { - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn != least_entries_lvn) { - auto it = (lvn->*map_ptr).find(entry.first); - if (it == (lvn->*map_ptr).end() || !(it->second == entry.second)) { - live_and_same = false; - break; - } - } - } - } - if (live_and_same) { - (this->*map_ptr).PutBefore((this->*map_ptr).end(), entry.first, entry.second); - } - } -} - -// Intersect maps as sets. The value type must be equality-comparable. -template -void LocalValueNumbering::InPlaceIntersectMaps(Map* work_map, const Map& other_map) { - auto work_it = work_map->begin(), work_end = work_map->end(); - auto cmp = work_map->value_comp(); - for (const auto& entry : other_map) { - while (work_it != work_end && - (cmp(*work_it, entry) || - (!cmp(entry, *work_it) && !(work_it->second == entry.second)))) { - work_it = work_map->erase(work_it); - } - if (work_it == work_end) { - return; - } - ++work_it; - } -} - -template -void LocalValueNumbering::MergeSets() { - auto cmp = (this->*set_ptr).value_comp(); - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - auto my_it = (this->*set_ptr).begin(), my_end = (this->*set_ptr).end(); - for (const auto& entry : lvn->*set_ptr) { - while (my_it != my_end && cmp(*my_it, entry)) { - ++my_it; - } - if (my_it != my_end && !cmp(entry, *my_it)) { - // Already handled. - ++my_it; - } else { - // Merge values for this field_id. - (this->*MergeFn)(entry, my_it); // my_it remains valid across inserts to std::set/SafeMap. - } - } - } -} - -void LocalValueNumbering::IntersectAliasingValueLocations(AliasingValues* work_values, - const AliasingValues* values) { - auto cmp = work_values->load_value_map.key_comp(); - auto work_it = work_values->load_value_map.begin(), work_end = work_values->load_value_map.end(); - auto store_it = values->store_loc_set.begin(), store_end = values->store_loc_set.end(); - auto load_it = values->load_value_map.begin(), load_end = values->load_value_map.end(); - while (store_it != store_end || load_it != load_end) { - uint16_t loc; - if (store_it != store_end && (load_it == load_end || *store_it < load_it->first)) { - loc = *store_it; - ++store_it; - } else { - loc = load_it->first; - ++load_it; - DCHECK(store_it == store_end || cmp(loc, *store_it)); - } - while (work_it != work_end && cmp(work_it->first, loc)) { - work_it = work_values->load_value_map.erase(work_it); - } - if (work_it != work_end && !cmp(loc, work_it->first)) { - // The location matches, keep it. - ++work_it; - } - } - while (work_it != work_end) { - work_it = work_values->load_value_map.erase(work_it); - } -} - -void LocalValueNumbering::MergeEscapedRefs(const ValueNameSet::value_type& entry, - ValueNameSet::iterator hint) { - // See if the ref is either escaped or non-aliasing in each predecessor. - bool is_escaped = true; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn->non_aliasing_refs_.count(entry) == 0u && - lvn->escaped_refs_.count(entry) == 0u) { - is_escaped = false; - break; - } - } - if (is_escaped) { - escaped_refs_.emplace_hint(hint, entry); - } -} - -void LocalValueNumbering::MergeEscapedIFieldTypeClobberSets( - const EscapedIFieldClobberSet::value_type& entry, EscapedIFieldClobberSet::iterator hint) { - // Insert only type-clobber entries (field_id == kNoValue) of escaped refs. - if (entry.field_id == kNoValue && escaped_refs_.count(entry.base) != 0u) { - escaped_ifield_clobber_set_.emplace_hint(hint, entry); - } -} - -void LocalValueNumbering::MergeEscapedIFieldClobberSets( - const EscapedIFieldClobberSet::value_type& entry, EscapedIFieldClobberSet::iterator hint) { - // Insert only those entries of escaped refs that are not overridden by a type clobber. - if (!(hint == escaped_ifield_clobber_set_.end() && - hint->base == entry.base && hint->type == entry.type) && - escaped_refs_.count(entry.base) != 0u) { - escaped_ifield_clobber_set_.emplace_hint(hint, entry); - } -} - -void LocalValueNumbering::MergeEscapedArrayClobberSets( - const EscapedArrayClobberSet::value_type& entry, EscapedArrayClobberSet::iterator hint) { - if (escaped_refs_.count(entry.base) != 0u) { - escaped_array_clobber_set_.emplace_hint(hint, entry); - } -} - -void LocalValueNumbering::MergeNullChecked() { - DCHECK_GE(gvn_->merge_lvns_.size(), 2u); - - // Find the LVN with the least entries in the set. - const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0]; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn->null_checked_.size() < least_entries_lvn->null_checked_.size()) { - least_entries_lvn = lvn; - } - } - - // For each null-checked value name check if it's null-checked in all the LVNs. - for (const auto& value_name : least_entries_lvn->null_checked_) { - // Merge null_checked_ for this ref. - merge_names_.clear(); - merge_names_.resize(gvn_->merge_lvns_.size(), value_name); - if (gvn_->NullCheckedInAllPredecessors(merge_names_)) { - null_checked_.insert(null_checked_.end(), value_name); - } - } - - // Now check if the least_entries_lvn has a null-check as the last insn. - const BasicBlock* least_entries_bb = gvn_->GetBasicBlock(least_entries_lvn->Id()); - if (gvn_->HasNullCheckLastInsn(least_entries_bb, id_)) { - int s_reg = least_entries_bb->last_mir_insn->ssa_rep->uses[0]; - uint32_t value_name = least_entries_lvn->GetOperandValue(s_reg); - merge_names_.clear(); - merge_names_.resize(gvn_->merge_lvns_.size(), value_name); - if (gvn_->NullCheckedInAllPredecessors(merge_names_)) { - null_checked_.insert(value_name); - } - } -} - -void LocalValueNumbering::MergeDivZeroChecked() { - DCHECK_GE(gvn_->merge_lvns_.size(), 2u); - - // Find the LVN with the least entries in the set. - const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0]; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn->div_zero_checked_.size() < least_entries_lvn->div_zero_checked_.size()) { - least_entries_lvn = lvn; - } - } - - // For each div-zero value name check if it's div-zero checked in all the LVNs. - for (const auto& value_name : least_entries_lvn->div_zero_checked_) { - // Merge null_checked_ for this ref. - merge_names_.clear(); - merge_names_.resize(gvn_->merge_lvns_.size(), value_name); - if (gvn_->DivZeroCheckedInAllPredecessors(merge_names_)) { - div_zero_checked_.insert(div_zero_checked_.end(), value_name); - } - } -} - -void LocalValueNumbering::MergeSFieldValues(const SFieldToValueMap::value_type& entry, - SFieldToValueMap::iterator hint) { - uint16_t field_id = entry.first; - merge_names_.clear(); - uint16_t value_name = kNoValue; - bool same_values = true; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - // Get the value name as in HandleSGet() but don't modify *lvn. - auto it = lvn->sfield_value_map_.find(field_id); - if (it != lvn->sfield_value_map_.end()) { - value_name = it->second; - } else { - uint16_t type = gvn_->GetSFieldType(field_id); - value_name = gvn_->LookupValue(kResolvedSFieldOp, field_id, - lvn->unresolved_sfield_version_[type], - lvn->global_memory_version_); - } - - same_values = same_values && (merge_names_.empty() || value_name == merge_names_.back()); - merge_names_.push_back(value_name); - } - if (same_values) { - // value_name already contains the result. - } else { - auto lb = merge_map_.lower_bound(merge_names_); - if (lb != merge_map_.end() && !merge_map_.key_comp()(merge_names_, lb->first)) { - value_name = lb->second; - } else { - value_name = gvn_->LookupValue(kMergeBlockSFieldVersionBumpOp, field_id, id_, kNoValue); - merge_map_.PutBefore(lb, merge_names_, value_name); - if (gvn_->NullCheckedInAllPredecessors(merge_names_)) { - null_checked_.insert(value_name); - } - } - } - sfield_value_map_.PutBefore(hint, field_id, value_name); -} - -void LocalValueNumbering::MergeNonAliasingIFieldValues(const IFieldLocToValueMap::value_type& entry, - IFieldLocToValueMap::iterator hint) { - uint16_t field_loc = entry.first; - merge_names_.clear(); - uint16_t value_name = kNoValue; - bool same_values = true; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - // Get the value name as in HandleIGet() but don't modify *lvn. - auto it = lvn->non_aliasing_ifield_value_map_.find(field_loc); - if (it != lvn->non_aliasing_ifield_value_map_.end()) { - value_name = it->second; - } else { - value_name = gvn_->LookupValue(kNonAliasingIFieldInitialOp, field_loc, kNoValue, kNoValue); - } - - same_values = same_values && (merge_names_.empty() || value_name == merge_names_.back()); - merge_names_.push_back(value_name); - } - if (same_values) { - // value_name already contains the result. - } else { - auto lb = merge_map_.lower_bound(merge_names_); - if (lb != merge_map_.end() && !merge_map_.key_comp()(merge_names_, lb->first)) { - value_name = lb->second; - } else { - value_name = gvn_->LookupValue(kMergeBlockNonAliasingIFieldVersionBumpOp, field_loc, - id_, kNoValue); - merge_map_.PutBefore(lb, merge_names_, value_name); - if (gvn_->NullCheckedInAllPredecessors(merge_names_)) { - null_checked_.insert(value_name); - } - } - } - non_aliasing_ifield_value_map_.PutBefore(hint, field_loc, value_name); -} - -template -void LocalValueNumbering::MergeAliasingValues(const typename Map::value_type& entry, - typename Map::iterator hint) { - const typename Map::key_type& key = entry.first; - - auto it = (this->*map_ptr).PutBefore(hint, key, AliasingValues(this)); - AliasingValues* my_values = &it->second; - - const AliasingValues* cmp_values = nullptr; - bool same_version = !Versions::HasNewBaseVersion(gvn_, this, key); - uint16_t load_memory_version_for_same_version = kNoValue; - if (same_version) { - // Find the first non-null values. - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - auto value = (lvn->*map_ptr).find(key); - if (value != (lvn->*map_ptr).end()) { - cmp_values = &value->second; - break; - } - } - DCHECK(cmp_values != nullptr); // There must be at least one non-null values. - - // Check if we have identical memory versions, i.e. the global memory version, unresolved - // field version and the values' memory_version_before_stores, last_stored_value - // and store_loc_set are identical. - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - auto value = (lvn->*map_ptr).find(key); - if (value == (lvn->*map_ptr).end()) { - if (cmp_values->memory_version_before_stores != kNoValue) { - same_version = false; - break; - } - } else if (cmp_values->last_stored_value != value->second.last_stored_value || - cmp_values->memory_version_before_stores != value->second.memory_version_before_stores || - cmp_values->store_loc_set != value->second.store_loc_set) { - same_version = false; - break; - } else if (value->second.last_load_memory_version != kNoValue) { - DCHECK(load_memory_version_for_same_version == kNoValue || - load_memory_version_for_same_version == value->second.last_load_memory_version); - load_memory_version_for_same_version = value->second.last_load_memory_version; - } - } - } - - if (same_version) { - // Copy the identical values. - my_values->memory_version_before_stores = cmp_values->memory_version_before_stores; - my_values->last_stored_value = cmp_values->last_stored_value; - my_values->store_loc_set = cmp_values->store_loc_set; - my_values->last_load_memory_version = load_memory_version_for_same_version; - // Merge load values seen in all incoming arcs (i.e. an intersection). - if (!cmp_values->load_value_map.empty()) { - my_values->load_value_map = cmp_values->load_value_map; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - auto value = (lvn->*map_ptr).find(key); - if (value == (lvn->*map_ptr).end() || value->second.load_value_map.empty()) { - my_values->load_value_map.clear(); - break; - } - InPlaceIntersectMaps(&my_values->load_value_map, value->second.load_value_map); - if (my_values->load_value_map.empty()) { - break; - } - } - } - } else { - // Bump version number for the merge. - my_values->memory_version_before_stores = my_values->last_load_memory_version = - Versions::LookupMergeBlockValue(gvn_, id_, key); - - // Calculate the locations that have been either read from or written to in each incoming LVN. - bool first_lvn = true; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - auto value = (lvn->*map_ptr).find(key); - if (value == (lvn->*map_ptr).end()) { - my_values->load_value_map.clear(); - break; - } - if (first_lvn) { - first_lvn = false; - // Copy the first LVN's locations. Values will be overwritten later. - my_values->load_value_map = value->second.load_value_map; - for (uint16_t location : value->second.store_loc_set) { - my_values->load_value_map.Put(location, 0u); - } - } else { - IntersectAliasingValueLocations(my_values, &value->second); - } - } - // Calculate merged values for the intersection. - for (auto& load_value_entry : my_values->load_value_map) { - uint16_t location = load_value_entry.first; - merge_names_.clear(); - uint16_t value_name = kNoValue; - bool same_values = true; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - value_name = Versions::LookupMergeValue(gvn_, lvn, key, location); - same_values = same_values && (merge_names_.empty() || value_name == merge_names_.back()); - merge_names_.push_back(value_name); - } - if (same_values) { - // value_name already contains the result. - } else { - auto lb = merge_map_.lower_bound(merge_names_); - if (lb != merge_map_.end() && !merge_map_.key_comp()(merge_names_, lb->first)) { - value_name = lb->second; - } else { - // NOTE: In addition to the key and id_ which don't change on an LVN recalculation - // during GVN, we also add location which can actually change on recalculation, so the - // value_name below may change. This could lead to an infinite loop if the location - // value name always changed when the refereced value name changes. However, given that - // we assign unique value names for other merges, such as Phis, such a dependency is - // not possible in a well-formed SSA graph. - value_name = Versions::LookupMergeLocationValue(gvn_, id_, key, location); - merge_map_.PutBefore(lb, merge_names_, value_name); - if (gvn_->NullCheckedInAllPredecessors(merge_names_)) { - null_checked_.insert(value_name); - } - } - } - load_value_entry.second = value_name; - } - } -} - -void LocalValueNumbering::Merge(MergeType merge_type) { - DCHECK_GE(gvn_->merge_lvns_.size(), 2u); - - // Always reserve space in merge_names_. Even if we don't use it in Merge() we may need it - // in GetStartingVregValueNumberImpl() when the merge_names_'s allocator is not the top. - merge_names_.reserve(gvn_->merge_lvns_.size()); - - IntersectSregValueMaps<&LocalValueNumbering::sreg_value_map_>(); - IntersectSregValueMaps<&LocalValueNumbering::sreg_wide_value_map_>(); - if (merge_type == kReturnMerge) { - // RETURN or PHI+RETURN. We need only sreg value maps. - return; - } - - MergeMemoryVersions(merge_type == kCatchMerge); - - // Merge non-aliasing maps/sets. - IntersectSets(); - if (!non_aliasing_refs_.empty() && merge_type == kCatchMerge) { - PruneNonAliasingRefsForCatch(); - } - if (!non_aliasing_refs_.empty()) { - MergeSets(); - MergeSets>(); - } - - // We won't do anything complicated for range checks, just calculate the intersection. - IntersectSets(); - - // Merge null_checked_. We may later insert more, such as merged object field values. - MergeNullChecked(); - - // Now merge the div_zero_checked_. - MergeDivZeroChecked(); - - if (merge_type == kCatchMerge) { - // Memory is clobbered. New memory version already created, don't merge aliasing locations. - return; - } - - DCHECK(merge_type == kNormalMerge); - - // Merge escaped refs and clobber sets. - MergeSets(); - if (!escaped_refs_.empty()) { - MergeSets(); - MergeSets(); - MergeSets(); - } - - MergeSets(); - MergeSets>(); - MergeSets>(); -} - -void LocalValueNumbering::PrepareEntryBlock() { - uint32_t vreg = gvn_->GetMirGraph()->GetFirstInVR(); - CompilationUnit* cu = gvn_->GetCompilationUnit(); - const char* shorty = cu->shorty; - ++shorty; // Skip return value. - if ((cu->access_flags & kAccStatic) == 0) { - // If non-static method, mark "this" as non-null - uint16_t value_name = GetOperandValue(vreg); - ++vreg; - null_checked_.insert(value_name); - } - for ( ; *shorty != 0; ++shorty, ++vreg) { - if (*shorty == 'J' || *shorty == 'D') { - uint16_t value_name = GetOperandValueWide(vreg); - SetOperandValueWide(vreg, value_name); - ++vreg; - } - } -} - -uint16_t LocalValueNumbering::MarkNonAliasingNonNull(MIR* mir) { - uint16_t res = GetOperandValue(mir->ssa_rep->defs[0]); - DCHECK(null_checked_.find(res) == null_checked_.end()); - null_checked_.insert(res); - non_aliasing_refs_.insert(res); - return res; -} - -bool LocalValueNumbering::IsNonAliasing(uint16_t reg) const { - return non_aliasing_refs_.find(reg) != non_aliasing_refs_.end(); -} - -bool LocalValueNumbering::IsNonAliasingIField(uint16_t reg, uint16_t field_id, - uint16_t type) const { - if (IsNonAliasing(reg)) { - return true; - } - if (escaped_refs_.find(reg) == escaped_refs_.end()) { - return false; - } - // Check for IPUTs to unresolved fields. - EscapedIFieldClobberKey key1 = { reg, type, kNoValue }; - if (escaped_ifield_clobber_set_.find(key1) != escaped_ifield_clobber_set_.end()) { - return false; - } - // Check for aliased IPUTs to the same field. - EscapedIFieldClobberKey key2 = { reg, type, field_id }; - return escaped_ifield_clobber_set_.find(key2) == escaped_ifield_clobber_set_.end(); -} - -bool LocalValueNumbering::IsNonAliasingArray(uint16_t reg, uint16_t type) const { - if (IsNonAliasing(reg)) { - return true; - } - if (escaped_refs_.count(reg) == 0u) { - return false; - } - // Check for aliased APUTs. - EscapedArrayClobberKey key = { reg, type }; - return escaped_array_clobber_set_.find(key) == escaped_array_clobber_set_.end(); -} - -void LocalValueNumbering::HandleNullCheck(MIR* mir, uint16_t reg) { - auto lb = null_checked_.lower_bound(reg); - if (lb != null_checked_.end() && *lb == reg) { - if (LIKELY(gvn_->CanModify())) { - if (gvn_->GetCompilationUnit()->verbose) { - LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; - } - } else { - null_checked_.insert(lb, reg); - } -} - -void LocalValueNumbering::HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index) { - RangeCheckKey key = { array, index }; - auto lb = range_checked_.lower_bound(key); - if (lb != range_checked_.end() && !RangeCheckKeyComparator()(key, *lb)) { - if (LIKELY(gvn_->CanModify())) { - if (gvn_->GetCompilationUnit()->verbose) { - LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK; - } - } else { - // Mark range check completed. - range_checked_.insert(lb, key); - } -} - -void LocalValueNumbering::HandleDivZeroCheck(MIR* mir, uint16_t reg) { - auto lb = div_zero_checked_.lower_bound(reg); - if (lb != div_zero_checked_.end() && *lb == reg) { - if (LIKELY(gvn_->CanModify())) { - if (gvn_->GetCompilationUnit()->verbose) { - LOG(INFO) << "Removing div zero check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_IGNORE_DIV_ZERO_CHECK; - } - } else { - div_zero_checked_.insert(lb, reg); - } -} - -void LocalValueNumbering::HandlePutObject(MIR* mir) { - // If we're storing a non-aliasing reference, stop tracking it as non-aliasing now. - uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]); - HandleEscapingRef(base); - if (gvn_->CanModify() && null_checked_.count(base) != 0u) { - if (gvn_->GetCompilationUnit()->verbose) { - LOG(INFO) << "Removing GC card mark value null check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_STORE_NON_NULL_VALUE; - } -} - -void LocalValueNumbering::HandleEscapingRef(uint16_t base) { - auto it = non_aliasing_refs_.find(base); - if (it != non_aliasing_refs_.end()) { - non_aliasing_refs_.erase(it); - escaped_refs_.insert(base); - } -} - -void LocalValueNumbering::HandleInvokeArgs(const MIR* mir, const LocalValueNumbering* mir_lvn) { - const int32_t* uses = mir->ssa_rep->uses; - const int32_t* uses_end = uses + mir->ssa_rep->num_uses; - while (uses != uses_end) { - uint16_t sreg = *uses; - ++uses; - // Avoid LookupValue() so that we don't store new values in the global value map. - auto local_it = mir_lvn->sreg_value_map_.find(sreg); - if (local_it != mir_lvn->sreg_value_map_.end()) { - non_aliasing_refs_.erase(local_it->second); - } else { - uint16_t value_name = gvn_->FindValue(kNoValue, sreg, kNoValue, kNoValue); - if (value_name != kNoValue) { - non_aliasing_refs_.erase(value_name); - } - } - } -} - -uint16_t LocalValueNumbering::HandlePhi(MIR* mir) { - if (gvn_->merge_lvns_.empty()) { - // Running LVN without a full GVN? - return kNoValue; - } - // Determine if this Phi is merging wide regs. - RegLocation raw_dest = gvn_->GetMirGraph()->GetRawDest(mir); - if (raw_dest.high_word) { - // This is the high part of a wide reg. Ignore the Phi. - return kNoValue; - } - bool wide = raw_dest.wide; - // Iterate over *merge_lvns_ and skip incoming sregs for BBs without associated LVN. - merge_names_.clear(); - uint16_t value_name = kNoValue; - bool same_values = true; - BasicBlockId* incoming = mir->meta.phi_incoming; - int32_t* uses = mir->ssa_rep->uses; - int16_t pos = 0; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - DCHECK_LT(pos, mir->ssa_rep->num_uses); - while (incoming[pos] != lvn->Id()) { - ++pos; - DCHECK_LT(pos, mir->ssa_rep->num_uses); - } - int s_reg = uses[pos]; - ++pos; - value_name = wide ? lvn->GetOperandValueWide(s_reg) : lvn->GetOperandValue(s_reg); - - same_values = same_values && (merge_names_.empty() || value_name == merge_names_.back()); - merge_names_.push_back(value_name); - } - if (same_values) { - // value_name already contains the result. - } else { - auto lb = merge_map_.lower_bound(merge_names_); - if (lb != merge_map_.end() && !merge_map_.key_comp()(merge_names_, lb->first)) { - value_name = lb->second; - } else { - value_name = gvn_->LookupValue(kNoValue, mir->ssa_rep->defs[0], kNoValue, kNoValue); - merge_map_.PutBefore(lb, merge_names_, value_name); - if (!wide && gvn_->NullCheckedInAllPredecessors(merge_names_)) { - null_checked_.insert(value_name); - } - if (gvn_->DivZeroCheckedInAllPredecessors(merge_names_)) { - div_zero_checked_.insert(value_name); - } - } - } - if (wide) { - SetOperandValueWide(mir->ssa_rep->defs[0], value_name); - } else { - SetOperandValue(mir->ssa_rep->defs[0], value_name); - } - return value_name; -} - -uint16_t LocalValueNumbering::HandleConst(MIR* mir, uint32_t value) { - RegLocation raw_dest = gvn_->GetMirGraph()->GetRawDest(mir); - uint16_t res; - if (value == 0u && raw_dest.ref) { - res = GlobalValueNumbering::kNullValue; - } else { - Instruction::Code op = raw_dest.fp ? Instruction::CONST_HIGH16 : Instruction::CONST; - res = gvn_->LookupValue(op, Low16Bits(value), High16Bits(value), 0); - } - SetOperandValue(mir->ssa_rep->defs[0], res); - return res; -} - -uint16_t LocalValueNumbering::HandleConstWide(MIR* mir, uint64_t value) { - RegLocation raw_dest = gvn_->GetMirGraph()->GetRawDest(mir); - Instruction::Code op = raw_dest.fp ? Instruction::CONST_HIGH16 : Instruction::CONST; - uint32_t low_word = Low32Bits(value); - uint32_t high_word = High32Bits(value); - uint16_t low_res = gvn_->LookupValue(op, Low16Bits(low_word), High16Bits(low_word), 1); - uint16_t high_res = gvn_->LookupValue(op, Low16Bits(high_word), High16Bits(high_word), 2); - uint16_t res = gvn_->LookupValue(op, low_res, high_res, 3); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - return res; -} - -uint16_t LocalValueNumbering::HandleAGet(MIR* mir, uint16_t opcode) { - uint16_t array = GetOperandValue(mir->ssa_rep->uses[0]); - HandleNullCheck(mir, array); - uint16_t index = GetOperandValue(mir->ssa_rep->uses[1]); - HandleRangeCheck(mir, array, index); - uint16_t type = AGetMemAccessType(static_cast(opcode)); - // Establish value number for loaded register. - uint16_t res; - if (IsNonAliasingArray(array, type)) { - res = HandleAliasingValuesGet(&non_aliasing_array_value_map_, - array, index); - } else { - uint16_t location = gvn_->GetArrayLocation(array, index); - res = HandleAliasingValuesGet(&aliasing_array_value_map_, - type, location); - } - if (opcode == Instruction::AGET_WIDE) { - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } else { - SetOperandValue(mir->ssa_rep->defs[0], res); - } - return res; -} - -void LocalValueNumbering::HandleAPut(MIR* mir, uint16_t opcode) { - int array_idx = (opcode == Instruction::APUT_WIDE) ? 2 : 1; - int index_idx = array_idx + 1; - uint16_t array = GetOperandValue(mir->ssa_rep->uses[array_idx]); - HandleNullCheck(mir, array); - uint16_t index = GetOperandValue(mir->ssa_rep->uses[index_idx]); - HandleRangeCheck(mir, array, index); - - uint16_t type = APutMemAccessType(static_cast(opcode)); - uint16_t value = (opcode == Instruction::APUT_WIDE) - ? GetOperandValueWide(mir->ssa_rep->uses[0]) - : GetOperandValue(mir->ssa_rep->uses[0]); - if (IsNonAliasing(array)) { - bool put_is_live = HandleAliasingValuesPut( - &non_aliasing_array_value_map_, array, index, value); - if (!put_is_live) { - // This APUT can be eliminated, it stores the same value that's already in the field. - // TODO: Eliminate the APUT. - return; - } - } else { - uint16_t location = gvn_->GetArrayLocation(array, index); - bool put_is_live = HandleAliasingValuesPut( - &aliasing_array_value_map_, type, location, value); - if (!put_is_live) { - // This APUT can be eliminated, it stores the same value that's already in the field. - // TODO: Eliminate the APUT. - return; - } - - // Clobber all escaped array refs for this type. - for (uint16_t escaped_array : escaped_refs_) { - EscapedArrayClobberKey clobber_key = { escaped_array, type }; - escaped_array_clobber_set_.insert(clobber_key); - } - } -} - -uint16_t LocalValueNumbering::HandleIGet(MIR* mir, uint16_t opcode) { - uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]); - HandleNullCheck(mir, base); - const MirFieldInfo& field_info = gvn_->GetMirGraph()->GetIFieldLoweringInfo(mir); - uint16_t res; - if (!field_info.IsResolved() || field_info.IsVolatile()) { - // Unresolved fields may be volatile, so handle them as such to be safe. - HandleInvokeOrClInitOrAcquireOp(mir); // Volatile GETs have acquire semantics. - // Volatile fields always get a new memory version; field id is irrelevant. - // Use result s_reg - will be unique. - res = gvn_->LookupValue(kNoValue, mir->ssa_rep->defs[0], kNoValue, kNoValue); - } else { - uint16_t type = IGetMemAccessType(static_cast(opcode)); - uint16_t field_id = gvn_->GetIFieldId(mir); - if (IsNonAliasingIField(base, field_id, type)) { - uint16_t loc = gvn_->LookupValue(kNonAliasingIFieldLocOp, base, field_id, type); - auto lb = non_aliasing_ifield_value_map_.lower_bound(loc); - if (lb != non_aliasing_ifield_value_map_.end() && lb->first == loc) { - res = lb->second; - } else { - res = gvn_->LookupValue(kNonAliasingIFieldInitialOp, loc, kNoValue, kNoValue); - non_aliasing_ifield_value_map_.PutBefore(lb, loc, res); - } - } else { - res = HandleAliasingValuesGet(&aliasing_ifield_value_map_, - field_id, base); - } - } - if (opcode == Instruction::IGET_WIDE) { - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } else { - SetOperandValue(mir->ssa_rep->defs[0], res); - } - return res; -} - -void LocalValueNumbering::HandleIPut(MIR* mir, uint16_t opcode) { - int base_reg = (opcode == Instruction::IPUT_WIDE) ? 2 : 1; - uint16_t base = GetOperandValue(mir->ssa_rep->uses[base_reg]); - HandleNullCheck(mir, base); - uint16_t type = IPutMemAccessType(static_cast(opcode)); - const MirFieldInfo& field_info = gvn_->GetMirGraph()->GetIFieldLoweringInfo(mir); - if (!field_info.IsResolved()) { - // Unresolved fields always alias with everything of the same type. - // Use mir->offset as modifier; without elaborate inlining, it will be unique. - unresolved_ifield_version_[type] = - gvn_->LookupValue(kUnresolvedIFieldOp, kNoValue, kNoValue, mir->offset); - - // For simplicity, treat base as escaped now. - HandleEscapingRef(base); - - // Clobber all fields of escaped references of the same type. - for (uint16_t escaped_ref : escaped_refs_) { - EscapedIFieldClobberKey clobber_key = { escaped_ref, type, kNoValue }; - escaped_ifield_clobber_set_.insert(clobber_key); - } - - // Aliasing fields of the same type may have been overwritten. - auto it = aliasing_ifield_value_map_.begin(), end = aliasing_ifield_value_map_.end(); - while (it != end) { - if (gvn_->GetIFieldType(it->first) != type) { - ++it; - } else { - it = aliasing_ifield_value_map_.erase(it); - } - } - } else if (field_info.IsVolatile()) { - // Nothing to do, resolved volatile fields always get a new memory version anyway and - // can't alias with resolved non-volatile fields. - } else { - uint16_t field_id = gvn_->GetIFieldId(mir); - uint16_t value = (opcode == Instruction::IPUT_WIDE) - ? GetOperandValueWide(mir->ssa_rep->uses[0]) - : GetOperandValue(mir->ssa_rep->uses[0]); - if (IsNonAliasing(base)) { - uint16_t loc = gvn_->LookupValue(kNonAliasingIFieldLocOp, base, field_id, type); - auto lb = non_aliasing_ifield_value_map_.lower_bound(loc); - if (lb != non_aliasing_ifield_value_map_.end() && lb->first == loc) { - if (lb->second == value) { - // This IPUT can be eliminated, it stores the same value that's already in the field. - // TODO: Eliminate the IPUT. - return; - } - lb->second = value; // Overwrite. - } else { - non_aliasing_ifield_value_map_.PutBefore(lb, loc, value); - } - } else { - bool put_is_live = HandleAliasingValuesPut( - &aliasing_ifield_value_map_, field_id, base, value); - if (!put_is_live) { - // This IPUT can be eliminated, it stores the same value that's already in the field. - // TODO: Eliminate the IPUT. - return; - } - - // Clobber all fields of escaped references for this field. - for (uint16_t escaped_ref : escaped_refs_) { - EscapedIFieldClobberKey clobber_key = { escaped_ref, type, field_id }; - escaped_ifield_clobber_set_.insert(clobber_key); - } - } - } -} - -uint16_t LocalValueNumbering::HandleSGet(MIR* mir, uint16_t opcode) { - const MirSFieldLoweringInfo& field_info = gvn_->GetMirGraph()->GetSFieldLoweringInfo(mir); - if (!field_info.IsResolved() || field_info.IsVolatile() || - (!field_info.IsClassInitialized() && - (mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0)) { - // Volatile SGETs (and unresolved fields are potentially volatile) have acquire semantics - // and class initialization can call arbitrary functions, we need to wipe aliasing values. - HandleInvokeOrClInitOrAcquireOp(mir); - } - uint16_t res; - if (!field_info.IsResolved() || field_info.IsVolatile()) { - // Unresolved fields may be volatile, so handle them as such to be safe. - // Volatile fields always get a new memory version; field id is irrelevant. - // Use result s_reg - will be unique. - res = gvn_->LookupValue(kNoValue, mir->ssa_rep->defs[0], kNoValue, kNoValue); - } else { - uint16_t type = SGetMemAccessType(static_cast(opcode)); - uint16_t field_id = gvn_->GetSFieldId(mir); - auto lb = sfield_value_map_.lower_bound(field_id); - if (lb != sfield_value_map_.end() && lb->first == field_id) { - res = lb->second; - } else { - // Resolved non-volatile static fields can alias with non-resolved fields of the same type, - // so we need to use unresolved_sfield_version_[type] in addition to global_memory_version_ - // to determine the version of the field. - res = gvn_->LookupValue(kResolvedSFieldOp, field_id, - unresolved_sfield_version_[type], global_memory_version_); - sfield_value_map_.PutBefore(lb, field_id, res); - } - } - if (opcode == Instruction::SGET_WIDE) { - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } else { - SetOperandValue(mir->ssa_rep->defs[0], res); - } - return res; -} - -void LocalValueNumbering::HandleSPut(MIR* mir, uint16_t opcode) { - const MirSFieldLoweringInfo& field_info = gvn_->GetMirGraph()->GetSFieldLoweringInfo(mir); - if (!field_info.IsClassInitialized() && - (mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0) { - // Class initialization can call arbitrary functions, we need to wipe aliasing values. - HandleInvokeOrClInitOrAcquireOp(mir); - } - uint16_t type = SPutMemAccessType(static_cast(opcode)); - if (!field_info.IsResolved()) { - // Unresolved fields always alias with everything of the same type. - // Use mir->offset as modifier; without elaborate inlining, it will be unique. - unresolved_sfield_version_[type] = - gvn_->LookupValue(kUnresolvedSFieldOp, kNoValue, kNoValue, mir->offset); - RemoveSFieldsForType(type); - } else if (field_info.IsVolatile()) { - // Nothing to do, resolved volatile fields always get a new memory version anyway and - // can't alias with resolved non-volatile fields. - } else { - uint16_t field_id = gvn_->GetSFieldId(mir); - uint16_t value = (opcode == Instruction::SPUT_WIDE) - ? GetOperandValueWide(mir->ssa_rep->uses[0]) - : GetOperandValue(mir->ssa_rep->uses[0]); - // Resolved non-volatile static fields can alias with non-resolved fields of the same type, - // so we need to use unresolved_sfield_version_[type] in addition to global_memory_version_ - // to determine the version of the field. - auto lb = sfield_value_map_.lower_bound(field_id); - if (lb != sfield_value_map_.end() && lb->first == field_id) { - if (lb->second == value) { - // This SPUT can be eliminated, it stores the same value that's already in the field. - // TODO: Eliminate the SPUT. - return; - } - lb->second = value; // Overwrite. - } else { - sfield_value_map_.PutBefore(lb, field_id, value); - } - } -} - -void LocalValueNumbering::RemoveSFieldsForType(uint16_t type) { - // Erase all static fields of this type from the sfield_value_map_. - for (auto it = sfield_value_map_.begin(), end = sfield_value_map_.end(); it != end; ) { - if (gvn_->GetSFieldType(it->first) == type) { - it = sfield_value_map_.erase(it); - } else { - ++it; - } - } -} - -void LocalValueNumbering::HandleInvokeOrClInitOrAcquireOp(MIR* mir) { - // Use mir->offset as modifier; without elaborate inlining, it will be unique. - global_memory_version_ = - gvn_->LookupValue(kInvokeMemoryVersionBumpOp, 0u, 0u, mir->offset); - // All static fields and instance fields and array elements of aliasing references, - // including escaped references, may have been modified. - sfield_value_map_.clear(); - aliasing_ifield_value_map_.clear(); - aliasing_array_value_map_.clear(); - escaped_refs_.clear(); - escaped_ifield_clobber_set_.clear(); - escaped_array_clobber_set_.clear(); -} - -uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { - uint16_t res = kNoValue; - uint16_t opcode = mir->dalvikInsn.opcode; - switch (opcode) { - case Instruction::NOP: - case Instruction::RETURN_VOID: - case Instruction::RETURN: - case Instruction::RETURN_OBJECT: - case Instruction::RETURN_WIDE: - case Instruction::GOTO: - case Instruction::GOTO_16: - case Instruction::GOTO_32: - case Instruction::THROW: - case Instruction::FILL_ARRAY_DATA: - case Instruction::PACKED_SWITCH: - case Instruction::SPARSE_SWITCH: - case Instruction::IF_EQ: - case Instruction::IF_NE: - case Instruction::IF_LT: - case Instruction::IF_GE: - case Instruction::IF_GT: - case Instruction::IF_LE: - case Instruction::IF_EQZ: - case Instruction::IF_NEZ: - case Instruction::IF_LTZ: - case Instruction::IF_GEZ: - case Instruction::IF_GTZ: - case Instruction::IF_LEZ: - case kMirOpFusedCmplFloat: - case kMirOpFusedCmpgFloat: - case kMirOpFusedCmplDouble: - case kMirOpFusedCmpgDouble: - case kMirOpFusedCmpLong: - // Nothing defined - take no action. - break; - - case Instruction::MONITOR_ENTER: - HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0])); - HandleInvokeOrClInitOrAcquireOp(mir); // Acquire operation. - break; - - case Instruction::MONITOR_EXIT: - HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0])); - // If we're running GVN and CanModify(), uneliminated null check indicates bytecode error. - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0 && - gvn_->work_lvn_ != nullptr && gvn_->CanModify()) { - LOG(WARNING) << "Bytecode error: MONITOR_EXIT is still null checked at 0x" << std::hex - << mir->offset << " in " << PrettyMethod(gvn_->cu_->method_idx, *gvn_->cu_->dex_file); - } - break; - - case Instruction::FILLED_NEW_ARRAY: - case Instruction::FILLED_NEW_ARRAY_RANGE: - // Nothing defined but the result will be unique and non-null. - if (mir->next != nullptr && mir->next->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { - uint16_t array = MarkNonAliasingNonNull(mir->next); - // Do not SetOperandValue(), we'll do that when we process the MOVE_RESULT_OBJECT. - if (kLocalValueNumberingEnableFilledNewArrayTracking && mir->ssa_rep->num_uses != 0u) { - AliasingValues* values = GetAliasingValues(&non_aliasing_array_value_map_, array); - // Clear the value if we got a merged version in a loop. - *values = AliasingValues(this); - for (size_t i = 0u, count = mir->ssa_rep->num_uses; i != count; ++i) { - DCHECK_EQ(High16Bits(i), 0u); - uint16_t index = gvn_->LookupValue(Instruction::CONST, i, 0u, 0); - uint16_t value = GetOperandValue(mir->ssa_rep->uses[i]); - values->load_value_map.Put(index, value); - RangeCheckKey key = { array, index }; - range_checked_.insert(key); - } - } - // The MOVE_RESULT_OBJECT will be processed next and we'll return the value name then. - } - // All args escaped (if references). - for (size_t i = 0u, count = mir->ssa_rep->num_uses; i != count; ++i) { - uint16_t reg = GetOperandValue(mir->ssa_rep->uses[i]); - HandleEscapingRef(reg); - } - break; - - case kMirOpNullCheck: - HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0])); - break; - - case Instruction::INVOKE_DIRECT: - case Instruction::INVOKE_DIRECT_RANGE: - case Instruction::INVOKE_VIRTUAL: - case Instruction::INVOKE_VIRTUAL_RANGE: - case Instruction::INVOKE_SUPER: - case Instruction::INVOKE_SUPER_RANGE: - case Instruction::INVOKE_INTERFACE: - case Instruction::INVOKE_INTERFACE_RANGE: { - // Nothing defined but handle the null check. - uint16_t reg = GetOperandValue(mir->ssa_rep->uses[0]); - HandleNullCheck(mir, reg); - } - FALLTHROUGH_INTENDED; - case Instruction::INVOKE_STATIC: - case Instruction::INVOKE_STATIC_RANGE: - // Make ref args aliasing. - HandleInvokeArgs(mir, this); - HandleInvokeOrClInitOrAcquireOp(mir); - break; - - case Instruction::INSTANCE_OF: { - uint16_t operand = GetOperandValue(mir->ssa_rep->uses[0]); - uint16_t type = mir->dalvikInsn.vC; - res = gvn_->LookupValue(Instruction::INSTANCE_OF, operand, type, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - case Instruction::CHECK_CAST: - if (gvn_->CanModify()) { - // Check if there was an instance-of operation on the same value and if we are - // in a block where its result is true. If so, we can eliminate the check-cast. - uint16_t operand = GetOperandValue(mir->ssa_rep->uses[0]); - uint16_t type = mir->dalvikInsn.vB; - uint16_t cond = gvn_->FindValue(Instruction::INSTANCE_OF, operand, type, kNoValue); - if (cond != kNoValue && gvn_->IsTrueInBlock(cond, Id())) { - if (gvn_->GetCompilationUnit()->verbose) { - LOG(INFO) << "Removing check-cast at 0x" << std::hex << mir->offset; - } - // Don't use kMirOpNop. Keep the check-cast as it defines the type of the register. - mir->optimization_flags |= MIR_IGNORE_CHECK_CAST; - } - } - break; - - case Instruction::MOVE_RESULT: - case Instruction::MOVE_RESULT_OBJECT: - // 1 result, treat as unique each time, use result s_reg - will be unique. - res = GetOperandValue(mir->ssa_rep->defs[0]); - SetOperandValue(mir->ssa_rep->defs[0], res); - break; - case Instruction::MOVE_EXCEPTION: - case Instruction::NEW_INSTANCE: - case Instruction::NEW_ARRAY: - // 1 result, treat as unique each time, use result s_reg - will be unique. - res = MarkNonAliasingNonNull(mir); - SetOperandValue(mir->ssa_rep->defs[0], res); - break; - case Instruction::CONST_CLASS: - DCHECK_EQ(Low16Bits(mir->dalvikInsn.vB), mir->dalvikInsn.vB); - res = gvn_->LookupValue(Instruction::CONST_CLASS, mir->dalvikInsn.vB, 0, 0); - SetOperandValue(mir->ssa_rep->defs[0], res); - null_checked_.insert(res); - non_aliasing_refs_.insert(res); - break; - case Instruction::CONST_STRING: - case Instruction::CONST_STRING_JUMBO: - // These strings are internalized, so assign value based on the string pool index. - res = gvn_->LookupValue(Instruction::CONST_STRING, Low16Bits(mir->dalvikInsn.vB), - High16Bits(mir->dalvikInsn.vB), 0); - SetOperandValue(mir->ssa_rep->defs[0], res); - null_checked_.insert(res); // May already be there. - // NOTE: Hacking the contents of an internalized string via reflection is possible - // but the behavior is undefined. Therefore, we consider the string constant and - // the reference non-aliasing. - // TUNING: We could keep this property even if the reference "escapes". - non_aliasing_refs_.insert(res); // May already be there. - break; - case Instruction::MOVE_RESULT_WIDE: - // 1 wide result, treat as unique each time, use result s_reg - will be unique. - res = GetOperandValueWide(mir->ssa_rep->defs[0]); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - break; - - case kMirOpPhi: - res = HandlePhi(mir); - break; - - case Instruction::MOVE: - case Instruction::MOVE_OBJECT: - case Instruction::MOVE_16: - case Instruction::MOVE_OBJECT_16: - case Instruction::MOVE_FROM16: - case Instruction::MOVE_OBJECT_FROM16: - case kMirOpCopy: - // Just copy value number of source to value number of result. - res = GetOperandValue(mir->ssa_rep->uses[0]); - SetOperandValue(mir->ssa_rep->defs[0], res); - break; - - case Instruction::MOVE_WIDE: - case Instruction::MOVE_WIDE_16: - case Instruction::MOVE_WIDE_FROM16: - // Just copy value number of source to value number of result. - res = GetOperandValueWide(mir->ssa_rep->uses[0]); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - break; - - case Instruction::CONST_HIGH16: - res = HandleConst(mir, mir->dalvikInsn.vB << 16); - break; - case Instruction::CONST: - case Instruction::CONST_4: - case Instruction::CONST_16: - res = HandleConst(mir, mir->dalvikInsn.vB); - break; - - case Instruction::CONST_WIDE_16: - case Instruction::CONST_WIDE_32: - res = HandleConstWide( - mir, - mir->dalvikInsn.vB + - ((mir->dalvikInsn.vB & 0x80000000) != 0 ? UINT64_C(0xffffffff00000000) : 0u)); - break; - - case Instruction::CONST_WIDE: - res = HandleConstWide(mir, mir->dalvikInsn.vB_wide); - break; - - case Instruction::CONST_WIDE_HIGH16: - res = HandleConstWide(mir, static_cast(mir->dalvikInsn.vB) << 48); - break; - - case Instruction::ARRAY_LENGTH: { - // Handle the null check. - uint16_t reg = GetOperandValue(mir->ssa_rep->uses[0]); - HandleNullCheck(mir, reg); - } - FALLTHROUGH_INTENDED; - case Instruction::NEG_INT: - case Instruction::NOT_INT: - case Instruction::NEG_FLOAT: - case Instruction::INT_TO_BYTE: - case Instruction::INT_TO_SHORT: - case Instruction::INT_TO_CHAR: - case Instruction::INT_TO_FLOAT: - case Instruction::FLOAT_TO_INT: { - // res = op + 1 operand - uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); - res = gvn_->LookupValue(opcode, operand1, kNoValue, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::LONG_TO_FLOAT: - case Instruction::LONG_TO_INT: - case Instruction::DOUBLE_TO_FLOAT: - case Instruction::DOUBLE_TO_INT: { - // res = op + 1 wide operand - uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); - res = gvn_->LookupValue(opcode, operand1, kNoValue, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::DOUBLE_TO_LONG: - case Instruction::LONG_TO_DOUBLE: - case Instruction::NEG_LONG: - case Instruction::NOT_LONG: - case Instruction::NEG_DOUBLE: { - // wide res = op + 1 wide operand - uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); - res = gvn_->LookupValue(opcode, operand1, kNoValue, kNoValue); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::FLOAT_TO_DOUBLE: - case Instruction::FLOAT_TO_LONG: - case Instruction::INT_TO_DOUBLE: - case Instruction::INT_TO_LONG: { - // wide res = op + 1 operand - uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); - res = gvn_->LookupValue(opcode, operand1, kNoValue, kNoValue); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::CMPL_DOUBLE: - case Instruction::CMPG_DOUBLE: - case Instruction::CMP_LONG: { - // res = op + 2 wide operands - uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); - uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]); - res = gvn_->LookupValue(opcode, operand1, operand2, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::DIV_INT: - case Instruction::DIV_INT_2ADDR: - case Instruction::REM_INT: - case Instruction::REM_INT_2ADDR: - HandleDivZeroCheck(mir, GetOperandValue(mir->ssa_rep->uses[1])); - FALLTHROUGH_INTENDED; - - case Instruction::CMPG_FLOAT: - case Instruction::CMPL_FLOAT: - case Instruction::ADD_INT: - case Instruction::ADD_INT_2ADDR: - case Instruction::MUL_INT: - case Instruction::MUL_INT_2ADDR: - case Instruction::AND_INT: - case Instruction::AND_INT_2ADDR: - case Instruction::OR_INT: - case Instruction::OR_INT_2ADDR: - case Instruction::XOR_INT: - case Instruction::XOR_INT_2ADDR: - case Instruction::SUB_INT: - case Instruction::SUB_INT_2ADDR: - case Instruction::SHL_INT: - case Instruction::SHL_INT_2ADDR: - case Instruction::SHR_INT: - case Instruction::SHR_INT_2ADDR: - case Instruction::USHR_INT: - case Instruction::USHR_INT_2ADDR: { - // res = op + 2 operands - uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); - uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]); - res = gvn_->LookupValue(opcode, operand1, operand2, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::DIV_LONG: - case Instruction::REM_LONG: - case Instruction::DIV_LONG_2ADDR: - case Instruction::REM_LONG_2ADDR: - HandleDivZeroCheck(mir, GetOperandValueWide(mir->ssa_rep->uses[2])); - FALLTHROUGH_INTENDED; - - case Instruction::ADD_LONG: - case Instruction::SUB_LONG: - case Instruction::MUL_LONG: - case Instruction::AND_LONG: - case Instruction::OR_LONG: - case Instruction::XOR_LONG: - case Instruction::ADD_LONG_2ADDR: - case Instruction::SUB_LONG_2ADDR: - case Instruction::MUL_LONG_2ADDR: - case Instruction::AND_LONG_2ADDR: - case Instruction::OR_LONG_2ADDR: - case Instruction::XOR_LONG_2ADDR: - case Instruction::ADD_DOUBLE: - case Instruction::SUB_DOUBLE: - case Instruction::MUL_DOUBLE: - case Instruction::DIV_DOUBLE: - case Instruction::REM_DOUBLE: - case Instruction::ADD_DOUBLE_2ADDR: - case Instruction::SUB_DOUBLE_2ADDR: - case Instruction::MUL_DOUBLE_2ADDR: - case Instruction::DIV_DOUBLE_2ADDR: - case Instruction::REM_DOUBLE_2ADDR: { - // wide res = op + 2 wide operands - uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); - uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]); - res = gvn_->LookupValue(opcode, operand1, operand2, kNoValue); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::SHL_LONG: - case Instruction::SHR_LONG: - case Instruction::USHR_LONG: - case Instruction::SHL_LONG_2ADDR: - case Instruction::SHR_LONG_2ADDR: - case Instruction::USHR_LONG_2ADDR: { - // wide res = op + 1 wide operand + 1 operand - uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); - uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[2]); - res = gvn_->LookupValue(opcode, operand1, operand2, kNoValue); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::ADD_FLOAT: - case Instruction::SUB_FLOAT: - case Instruction::MUL_FLOAT: - case Instruction::DIV_FLOAT: - case Instruction::REM_FLOAT: - case Instruction::ADD_FLOAT_2ADDR: - case Instruction::SUB_FLOAT_2ADDR: - case Instruction::MUL_FLOAT_2ADDR: - case Instruction::DIV_FLOAT_2ADDR: - case Instruction::REM_FLOAT_2ADDR: { - // res = op + 2 operands - uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); - uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]); - res = gvn_->LookupValue(opcode, operand1, operand2, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::RSUB_INT: - case Instruction::ADD_INT_LIT16: - case Instruction::MUL_INT_LIT16: - case Instruction::DIV_INT_LIT16: - case Instruction::REM_INT_LIT16: - case Instruction::AND_INT_LIT16: - case Instruction::OR_INT_LIT16: - case Instruction::XOR_INT_LIT16: - case Instruction::ADD_INT_LIT8: - case Instruction::RSUB_INT_LIT8: - case Instruction::MUL_INT_LIT8: - case Instruction::DIV_INT_LIT8: - case Instruction::REM_INT_LIT8: - case Instruction::AND_INT_LIT8: - case Instruction::OR_INT_LIT8: - case Instruction::XOR_INT_LIT8: - case Instruction::SHL_INT_LIT8: - case Instruction::SHR_INT_LIT8: - case Instruction::USHR_INT_LIT8: { - // Same as res = op + 2 operands, except use vC as operand 2 - uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); - uint16_t operand2 = gvn_->LookupValue(Instruction::CONST, mir->dalvikInsn.vC, 0, 0); - res = gvn_->LookupValue(opcode, operand1, operand2, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::AGET_OBJECT: - case Instruction::AGET: - case Instruction::AGET_WIDE: - case Instruction::AGET_BOOLEAN: - case Instruction::AGET_BYTE: - case Instruction::AGET_CHAR: - case Instruction::AGET_SHORT: - res = HandleAGet(mir, opcode); - break; - - case Instruction::APUT_OBJECT: - HandlePutObject(mir); - FALLTHROUGH_INTENDED; - case Instruction::APUT: - case Instruction::APUT_WIDE: - case Instruction::APUT_BYTE: - case Instruction::APUT_BOOLEAN: - case Instruction::APUT_SHORT: - case Instruction::APUT_CHAR: - HandleAPut(mir, opcode); - break; - - case Instruction::IGET_OBJECT: - case Instruction::IGET: - case Instruction::IGET_WIDE: - case Instruction::IGET_BOOLEAN: - case Instruction::IGET_BYTE: - case Instruction::IGET_CHAR: - case Instruction::IGET_SHORT: - res = HandleIGet(mir, opcode); - break; - - case Instruction::IPUT_OBJECT: - HandlePutObject(mir); - FALLTHROUGH_INTENDED; - case Instruction::IPUT: - case Instruction::IPUT_WIDE: - case Instruction::IPUT_BOOLEAN: - case Instruction::IPUT_BYTE: - case Instruction::IPUT_CHAR: - case Instruction::IPUT_SHORT: - HandleIPut(mir, opcode); - break; - - case Instruction::SGET_OBJECT: - case Instruction::SGET: - case Instruction::SGET_WIDE: - case Instruction::SGET_BOOLEAN: - case Instruction::SGET_BYTE: - case Instruction::SGET_CHAR: - case Instruction::SGET_SHORT: - res = HandleSGet(mir, opcode); - break; - - case Instruction::SPUT_OBJECT: - HandlePutObject(mir); - FALLTHROUGH_INTENDED; - case Instruction::SPUT: - case Instruction::SPUT_WIDE: - case Instruction::SPUT_BOOLEAN: - case Instruction::SPUT_BYTE: - case Instruction::SPUT_CHAR: - case Instruction::SPUT_SHORT: - HandleSPut(mir, opcode); - break; - } - return res; -} - -uint16_t LocalValueNumbering::GetEndingVregValueNumberImpl(int v_reg, bool wide) const { - const BasicBlock* bb = gvn_->GetBasicBlock(Id()); - DCHECK(bb != nullptr); - int s_reg = bb->data_flow_info->vreg_to_ssa_map_exit[v_reg]; - if (s_reg == INVALID_SREG) { - return kNoValue; - } - if (gvn_->GetMirGraph()->GetRegLocation(s_reg).wide != wide) { - return kNoValue; - } - if (wide) { - int high_s_reg = bb->data_flow_info->vreg_to_ssa_map_exit[v_reg + 1]; - if (high_s_reg != s_reg + 1) { - return kNoValue; // High word has been overwritten. - } - return GetSregValueWide(s_reg); - } else { - return GetSregValue(s_reg); - } -} - -uint16_t LocalValueNumbering::GetStartingVregValueNumberImpl(int v_reg, bool wide) const { - DCHECK_EQ(gvn_->mode_, GlobalValueNumbering::kModeGvnPostProcessing); - DCHECK(gvn_->CanModify()); - const BasicBlock* bb = gvn_->GetBasicBlock(Id()); - DCHECK(bb != nullptr); - DCHECK_NE(bb->predecessors.size(), 0u); - if (bb->predecessors.size() == 1u) { - return gvn_->GetLvn(bb->predecessors[0])->GetEndingVregValueNumberImpl(v_reg, wide); - } - merge_names_.clear(); - uint16_t value_name = kNoValue; - bool same_values = true; - for (BasicBlockId pred_id : bb->predecessors) { - value_name = gvn_->GetLvn(pred_id)->GetEndingVregValueNumberImpl(v_reg, wide); - if (value_name == kNoValue) { - return kNoValue; - } - same_values = same_values && (merge_names_.empty() || value_name == merge_names_.back()); - merge_names_.push_back(value_name); - } - if (same_values) { - // value_name already contains the result. - } else { - auto lb = merge_map_.lower_bound(merge_names_); - if (lb != merge_map_.end() && !merge_map_.key_comp()(merge_names_, lb->first)) { - value_name = lb->second; - } else { - value_name = kNoValue; // We never assigned a value name to this set of merged names. - } - } - return value_name; -} - -} // namespace art diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h deleted file mode 100644 index dff5e27521a036c9a26ede1ff5e39e367c2161e3..0000000000000000000000000000000000000000 --- a/compiler/dex/local_value_numbering.h +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (C) 2012 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_LOCAL_VALUE_NUMBERING_H_ -#define ART_COMPILER_DEX_LOCAL_VALUE_NUMBERING_H_ - -#include - -#include "base/arena_object.h" -#include "base/logging.h" -#include "dex_instruction_utils.h" -#include "global_value_numbering.h" - -namespace art { - -class DexFile; - -// Enable/disable tracking values stored in the FILLED_NEW_ARRAY result. -static constexpr bool kLocalValueNumberingEnableFilledNewArrayTracking = true; - -class LocalValueNumbering : public DeletableArenaObject { - private: - static constexpr uint16_t kNoValue = GlobalValueNumbering::kNoValue; - - public: - LocalValueNumbering(GlobalValueNumbering* gvn, BasicBlockId id, ScopedArenaAllocator* allocator); - - BasicBlockId Id() const { - return id_; - } - - bool Equals(const LocalValueNumbering& other) const; - - bool IsValueNullChecked(uint16_t value_name) const { - return null_checked_.find(value_name) != null_checked_.end(); - } - - bool IsValueDivZeroChecked(uint16_t value_name) const { - return div_zero_checked_.find(value_name) != div_zero_checked_.end(); - } - - uint16_t GetSregValue(uint16_t s_reg) const { - DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); - return GetSregValueImpl(s_reg, &sreg_value_map_); - } - - uint16_t GetSregValueWide(uint16_t s_reg) const { - DCHECK(gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); - return GetSregValueImpl(s_reg, &sreg_wide_value_map_); - } - - // Get the starting value number for a given dalvik register. - uint16_t GetStartingVregValueNumber(int v_reg) const { - return GetStartingVregValueNumberImpl(v_reg, false); - } - - // Get the starting value number for a given wide dalvik register. - uint16_t GetStartingVregValueNumberWide(int v_reg) const { - return GetStartingVregValueNumberImpl(v_reg, true); - } - - enum MergeType { - kNormalMerge, - kCatchMerge, - kReturnMerge, // RETURN or PHI+RETURN. Merge only sreg maps. - }; - - void MergeOne(const LocalValueNumbering& other, MergeType merge_type); - void Merge(MergeType merge_type); // Merge gvn_->merge_lvns_. - void PrepareEntryBlock(); - - uint16_t GetValueNumber(MIR* mir); - - private: - // A set of value names. - typedef GlobalValueNumbering::ValueNameSet ValueNameSet; - - // Key is s_reg, value is value name. - typedef ScopedArenaSafeMap SregValueMap; - - uint16_t GetEndingVregValueNumberImpl(int v_reg, bool wide) const; - uint16_t GetStartingVregValueNumberImpl(int v_reg, bool wide) const; - - uint16_t GetSregValueImpl(int s_reg, const SregValueMap* map) const { - uint16_t res = kNoValue; - auto lb = map->find(s_reg); - if (lb != map->end()) { - res = lb->second; - } else { - res = gvn_->FindValue(kNoValue, s_reg, kNoValue, kNoValue); - } - return res; - } - - void SetOperandValueImpl(uint16_t s_reg, uint16_t value, SregValueMap* map) { - DCHECK_EQ(map->count(s_reg), 0u); - map->Put(s_reg, value); - } - - uint16_t GetOperandValueImpl(int s_reg, const SregValueMap* map) const { - uint16_t res = kNoValue; - auto lb = map->find(s_reg); - if (lb != map->end()) { - res = lb->second; - } else { - // Using the original value; s_reg refers to an input reg. - res = gvn_->LookupValue(kNoValue, s_reg, kNoValue, kNoValue); - } - return res; - } - - void SetOperandValue(uint16_t s_reg, uint16_t value) { - DCHECK_EQ(sreg_wide_value_map_.count(s_reg), 0u); - DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); - SetOperandValueImpl(s_reg, value, &sreg_value_map_); - } - - uint16_t GetOperandValue(int s_reg) const { - DCHECK_EQ(sreg_wide_value_map_.count(s_reg), 0u); - DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); - return GetOperandValueImpl(s_reg, &sreg_value_map_); - } - - void SetOperandValueWide(uint16_t s_reg, uint16_t value) { - DCHECK_EQ(sreg_value_map_.count(s_reg), 0u); - DCHECK(gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); - DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).high_word); - SetOperandValueImpl(s_reg, value, &sreg_wide_value_map_); - } - - uint16_t GetOperandValueWide(int s_reg) const { - DCHECK_EQ(sreg_value_map_.count(s_reg), 0u); - DCHECK(gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); - DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).high_word); - return GetOperandValueImpl(s_reg, &sreg_wide_value_map_); - } - - struct RangeCheckKey { - uint16_t array; - uint16_t index; - - // NOTE: Can't define this at namespace scope for a private struct. - bool operator==(const RangeCheckKey& other) const { - return array == other.array && index == other.index; - } - }; - - struct RangeCheckKeyComparator { - bool operator()(const RangeCheckKey& lhs, const RangeCheckKey& rhs) const { - if (lhs.array != rhs.array) { - return lhs.array < rhs.array; - } - return lhs.index < rhs.index; - } - }; - - typedef ScopedArenaSet RangeCheckSet; - - // Maps instance field "location" (derived from base, field_id and type) to value name. - typedef ScopedArenaSafeMap IFieldLocToValueMap; - - // Maps static field id to value name - typedef ScopedArenaSafeMap SFieldToValueMap; - - struct EscapedIFieldClobberKey { - uint16_t base; // Or array. - uint16_t type; - uint16_t field_id; // None (kNoValue) for arrays and unresolved instance field stores. - - // NOTE: Can't define this at namespace scope for a private struct. - bool operator==(const EscapedIFieldClobberKey& other) const { - return base == other.base && type == other.type && field_id == other.field_id; - } - }; - - struct EscapedIFieldClobberKeyComparator { - bool operator()(const EscapedIFieldClobberKey& lhs, const EscapedIFieldClobberKey& rhs) const { - // Compare base first. This makes sequential iteration respect the order of base. - if (lhs.base != rhs.base) { - return lhs.base < rhs.base; - } - // Compare type second. This makes the type-clobber entries (field_id == kNoValue) last - // for given base and type and makes it easy to prune unnecessary entries when merging - // escaped_ifield_clobber_set_ from multiple LVNs. - if (lhs.type != rhs.type) { - return lhs.type < rhs.type; - } - return lhs.field_id < rhs.field_id; - } - }; - - typedef ScopedArenaSet - EscapedIFieldClobberSet; - - struct EscapedArrayClobberKey { - uint16_t base; - uint16_t type; - - // NOTE: Can't define this at namespace scope for a private struct. - bool operator==(const EscapedArrayClobberKey& other) const { - return base == other.base && type == other.type; - } - }; - - struct EscapedArrayClobberKeyComparator { - bool operator()(const EscapedArrayClobberKey& lhs, const EscapedArrayClobberKey& rhs) const { - // Compare base first. This makes sequential iteration respect the order of base. - if (lhs.base != rhs.base) { - return lhs.base < rhs.base; - } - return lhs.type < rhs.type; - } - }; - - // Clobber set for previously non-aliasing array refs that escaped. - typedef ScopedArenaSet - EscapedArrayClobberSet; - - // Known location values for an aliasing set. The set can be tied to one of: - // 1. Instance field. The locations are aliasing references used to access the field. - // 2. Non-aliasing array reference. The locations are indexes to the array. - // 3. Aliasing array type. The locations are (reference, index) pair ids assigned by GVN. - // In each case we keep track of the last stored value, if any, and the set of locations - // where it was stored. We also keep track of all values known for the current write state - // (load_value_map), which can be known either because they have been loaded since the last - // store or because they contained the last_stored_value before the store and thus could not - // have changed as a result. - struct AliasingValues { - explicit AliasingValues(LocalValueNumbering* lvn) - : memory_version_before_stores(kNoValue), - last_stored_value(kNoValue), - store_loc_set(std::less(), lvn->null_checked_.get_allocator()), - last_load_memory_version(kNoValue), - load_value_map(std::less(), lvn->null_checked_.get_allocator()) { - } - - uint16_t memory_version_before_stores; // kNoValue if start version for the field. - uint16_t last_stored_value; // Last stored value name, kNoValue if none. - ValueNameSet store_loc_set; // Where was last_stored_value stored. - - // Maps refs (other than stored_to) to currently known values for this field other. On write, - // anything that differs from the written value is removed as it may be overwritten. - uint16_t last_load_memory_version; // kNoValue if not known. - ScopedArenaSafeMap load_value_map; - - // NOTE: Can't define this at namespace scope for a private struct. - bool operator==(const AliasingValues& other) const { - return memory_version_before_stores == other.memory_version_before_stores && - last_load_memory_version == other.last_load_memory_version && - last_stored_value == other.last_stored_value && - store_loc_set == other.store_loc_set && - load_value_map == other.load_value_map; - } - }; - - // Maps instance field id to AliasingValues, locations are object refs. - typedef ScopedArenaSafeMap AliasingIFieldValuesMap; - - // Maps non-aliasing array reference to AliasingValues, locations are array indexes. - typedef ScopedArenaSafeMap NonAliasingArrayValuesMap; - - // Maps aliasing array type to AliasingValues, locations are (array, index) pair ids. - typedef ScopedArenaSafeMap AliasingArrayValuesMap; - - // Helper classes defining versions for updating and merging the AliasingValues maps above. - class AliasingIFieldVersions; - class NonAliasingArrayVersions; - class AliasingArrayVersions; - - template - AliasingValues* GetAliasingValues(Map* map, const typename Map::key_type& key); - - template - void UpdateAliasingValuesLoadVersion(const KeyType& key, AliasingValues* values); - - template - static uint16_t AliasingValuesMergeGet(GlobalValueNumbering* gvn, - const LocalValueNumbering* lvn, - Map* map, const typename Map::key_type& key, - uint16_t location); - - template - uint16_t HandleAliasingValuesGet(Map* map, const typename Map::key_type& key, - uint16_t location); - - template - bool HandleAliasingValuesPut(Map* map, const typename Map::key_type& key, - uint16_t location, uint16_t value); - - template - void CopyAliasingValuesMap(ScopedArenaSafeMap* dest, - const ScopedArenaSafeMap& src); - - uint16_t MarkNonAliasingNonNull(MIR* mir); - bool IsNonAliasing(uint16_t reg) const; - bool IsNonAliasingIField(uint16_t reg, uint16_t field_id, uint16_t type) const; - bool IsNonAliasingArray(uint16_t reg, uint16_t type) const; - void HandleNullCheck(MIR* mir, uint16_t reg); - void HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index); - void HandleDivZeroCheck(MIR* mir, uint16_t reg); - void HandlePutObject(MIR* mir); - void HandleEscapingRef(uint16_t base); - void HandleInvokeArgs(const MIR* mir, const LocalValueNumbering* mir_lvn); - uint16_t HandlePhi(MIR* mir); - uint16_t HandleConst(MIR* mir, uint32_t value); - uint16_t HandleConstWide(MIR* mir, uint64_t value); - uint16_t HandleAGet(MIR* mir, uint16_t opcode); - void HandleAPut(MIR* mir, uint16_t opcode); - uint16_t HandleIGet(MIR* mir, uint16_t opcode); - void HandleIPut(MIR* mir, uint16_t opcode); - uint16_t HandleSGet(MIR* mir, uint16_t opcode); - void HandleSPut(MIR* mir, uint16_t opcode); - void RemoveSFieldsForType(uint16_t type); - void HandleInvokeOrClInitOrAcquireOp(MIR* mir); - - bool SameMemoryVersion(const LocalValueNumbering& other) const; - - uint16_t NewMemoryVersion(uint16_t* new_version); - void MergeMemoryVersions(bool clobbered_catch); - - void PruneNonAliasingRefsForCatch(); - - template - void IntersectSets(); - - void CopyLiveSregValues(SregValueMap* dest, const SregValueMap& src); - - // Intersect SSA reg value maps as sets, ignore dead regs. - template - void IntersectSregValueMaps(); - - // Intersect maps as sets. The value type must be equality-comparable. - template - static void InPlaceIntersectMaps(Map* work_map, const Map& other_map); - - template - void MergeSets(); - - void IntersectAliasingValueLocations(AliasingValues* work_values, const AliasingValues* values); - - void MergeEscapedRefs(const ValueNameSet::value_type& entry, ValueNameSet::iterator hint); - void MergeEscapedIFieldTypeClobberSets(const EscapedIFieldClobberSet::value_type& entry, - EscapedIFieldClobberSet::iterator hint); - void MergeEscapedIFieldClobberSets(const EscapedIFieldClobberSet::value_type& entry, - EscapedIFieldClobberSet::iterator hint); - void MergeEscapedArrayClobberSets(const EscapedArrayClobberSet::value_type& entry, - EscapedArrayClobberSet::iterator hint); - void MergeSFieldValues(const SFieldToValueMap::value_type& entry, - SFieldToValueMap::iterator hint); - void MergeNonAliasingIFieldValues(const IFieldLocToValueMap::value_type& entry, - IFieldLocToValueMap::iterator hint); - void MergeNullChecked(); - void MergeDivZeroChecked(); - - template - void MergeAliasingValues(const typename Map::value_type& entry, typename Map::iterator hint); - - GlobalValueNumbering* gvn_; - - // We're using the block id as a 16-bit operand value for some lookups. - static_assert(sizeof(BasicBlockId) == sizeof(uint16_t), "BasicBlockId must be 16 bit"); - BasicBlockId id_; - - SregValueMap sreg_value_map_; - SregValueMap sreg_wide_value_map_; - - SFieldToValueMap sfield_value_map_; - IFieldLocToValueMap non_aliasing_ifield_value_map_; - AliasingIFieldValuesMap aliasing_ifield_value_map_; - NonAliasingArrayValuesMap non_aliasing_array_value_map_; - AliasingArrayValuesMap aliasing_array_value_map_; - - // Data for dealing with memory clobbering and store/load aliasing. - uint16_t global_memory_version_; - uint16_t unresolved_sfield_version_[kDexMemAccessTypeCount]; - uint16_t unresolved_ifield_version_[kDexMemAccessTypeCount]; - // Value names of references to objects that cannot be reached through a different value name. - ValueNameSet non_aliasing_refs_; - // Previously non-aliasing refs that escaped but can still be used for non-aliasing AGET/IGET. - ValueNameSet escaped_refs_; - // Blacklists for cases where escaped_refs_ can't be used. - EscapedIFieldClobberSet escaped_ifield_clobber_set_; - EscapedArrayClobberSet escaped_array_clobber_set_; - - // Range check and null check elimination. - RangeCheckSet range_checked_; - ValueNameSet null_checked_; - ValueNameSet div_zero_checked_; - - // Reuse one vector for all merges to avoid leaking too much memory on the ArenaStack. - mutable ScopedArenaVector merge_names_; - // Map to identify when different locations merge the same values. - ScopedArenaSafeMap, uint16_t> merge_map_; - // New memory version for merge, kNoValue if all memory versions matched. - uint16_t merge_new_memory_version_; - - DISALLOW_COPY_AND_ASSIGN(LocalValueNumbering); -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_LOCAL_VALUE_NUMBERING_H_ diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc deleted file mode 100644 index f98969effd0866b517aa753230591c132fee5fe4..0000000000000000000000000000000000000000 --- a/compiler/dex/local_value_numbering_test.cc +++ /dev/null @@ -1,920 +0,0 @@ -/* - * 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 "dex/mir_field_info.h" -#include "global_value_numbering.h" -#include "local_value_numbering.h" -#include "gtest/gtest.h" - -namespace art { - -class LocalValueNumberingTest : public testing::Test { - protected: - struct IFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_field_idx; - bool is_volatile; - DexMemAccessType type; - }; - - struct SFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_field_idx; - bool is_volatile; - DexMemAccessType type; - }; - - struct MIRDef { - static constexpr size_t kMaxSsaDefs = 2; - static constexpr size_t kMaxSsaUses = 4; - - Instruction::Code opcode; - int64_t value; - uint32_t field_info; - size_t num_uses; - int32_t uses[kMaxSsaUses]; - size_t num_defs; - int32_t defs[kMaxSsaDefs]; - }; - -#define DEF_CONST(opcode, reg, value) \ - { opcode, value, 0u, 0, { }, 1, { reg } } -#define DEF_CONST_WIDE(opcode, reg, value) \ - { opcode, value, 0u, 0, { }, 2, { reg, reg + 1 } } -#define DEF_CONST_STRING(opcode, reg, index) \ - { opcode, index, 0u, 0, { }, 1, { reg } } -#define DEF_IGET(opcode, reg, obj, field_info) \ - { opcode, 0u, field_info, 1, { obj }, 1, { reg } } -#define DEF_IGET_WIDE(opcode, reg, obj, field_info) \ - { opcode, 0u, field_info, 1, { obj }, 2, { reg, reg + 1 } } -#define DEF_IPUT(opcode, reg, obj, field_info) \ - { opcode, 0u, field_info, 2, { reg, obj }, 0, { } } -#define DEF_IPUT_WIDE(opcode, reg, obj, field_info) \ - { opcode, 0u, field_info, 3, { reg, reg + 1, obj }, 0, { } } -#define DEF_SGET(opcode, reg, field_info) \ - { opcode, 0u, field_info, 0, { }, 1, { reg } } -#define DEF_SGET_WIDE(opcode, reg, field_info) \ - { opcode, 0u, field_info, 0, { }, 2, { reg, reg + 1 } } -#define DEF_SPUT(opcode, reg, field_info) \ - { opcode, 0u, field_info, 1, { reg }, 0, { } } -#define DEF_SPUT_WIDE(opcode, reg, field_info) \ - { opcode, 0u, field_info, 2, { reg, reg + 1 }, 0, { } } -#define DEF_AGET(opcode, reg, obj, idx) \ - { opcode, 0u, 0u, 2, { obj, idx }, 1, { reg } } -#define DEF_AGET_WIDE(opcode, reg, obj, idx) \ - { opcode, 0u, 0u, 2, { obj, idx }, 2, { reg, reg + 1 } } -#define DEF_APUT(opcode, reg, obj, idx) \ - { opcode, 0u, 0u, 3, { reg, obj, idx }, 0, { } } -#define DEF_APUT_WIDE(opcode, reg, obj, idx) \ - { opcode, 0u, 0u, 4, { reg, reg + 1, obj, idx }, 0, { } } -#define DEF_INVOKE1(opcode, reg) \ - { opcode, 0u, 0u, 1, { reg }, 0, { } } -#define DEF_UNIQUE_REF(opcode, reg) \ - { opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ... -#define DEF_DIV_REM(opcode, result, dividend, divisor) \ - { opcode, 0u, 0u, 2, { dividend, divisor }, 1, { result } } -#define DEF_DIV_REM_WIDE(opcode, result, dividend, divisor) \ - { opcode, 0u, 0u, 4, { dividend, dividend + 1, divisor, divisor + 1 }, 2, { result, result + 1 } } - - void DoPrepareIFields(const IFieldDef* defs, size_t count) { - cu_.mir_graph->ifield_lowering_infos_.clear(); - cu_.mir_graph->ifield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const IFieldDef* def = &defs[i]; - MirIFieldLoweringInfo field_info(def->field_idx, def->type, false); - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ &= ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile); - } - cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareIFields(const IFieldDef (&defs)[count]) { - DoPrepareIFields(defs, count); - } - - void DoPrepareSFields(const SFieldDef* defs, size_t count) { - cu_.mir_graph->sfield_lowering_infos_.clear(); - cu_.mir_graph->sfield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const SFieldDef* def = &defs[i]; - MirSFieldLoweringInfo field_info(def->field_idx, def->type); - // Mark even unresolved fields as initialized. - field_info.flags_ |= MirSFieldLoweringInfo::kFlagClassIsInitialized; - // NOTE: MirSFieldLoweringInfo::kFlagClassIsInDexCache isn't used by LVN. - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ &= ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile); - } - cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareSFields(const SFieldDef (&defs)[count]) { - DoPrepareSFields(defs, count); - } - - void DoPrepareMIRs(const MIRDef* defs, size_t count) { - mir_count_ = count; - mirs_ = cu_.arena.AllocArray(count, kArenaAllocMIR); - ssa_reps_.resize(count); - for (size_t i = 0u; i != count; ++i) { - const MIRDef* def = &defs[i]; - MIR* mir = &mirs_[i]; - mir->dalvikInsn.opcode = def->opcode; - mir->dalvikInsn.vB = static_cast(def->value); - mir->dalvikInsn.vB_wide = def->value; - if (IsInstructionIGetOrIPut(def->opcode)) { - ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size()); - mir->meta.ifield_lowering_info = def->field_info; - ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_info].MemAccessType(), - IGetOrIPutMemAccessType(def->opcode)); - } else if (IsInstructionSGetOrSPut(def->opcode)) { - ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size()); - mir->meta.sfield_lowering_info = def->field_info; - ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_info].MemAccessType(), - SGetOrSPutMemAccessType(def->opcode)); - } - mir->ssa_rep = &ssa_reps_[i]; - mir->ssa_rep->num_uses = def->num_uses; - mir->ssa_rep->uses = const_cast(def->uses); // Not modified by LVN. - mir->ssa_rep->num_defs = def->num_defs; - mir->ssa_rep->defs = const_cast(def->defs); // Not modified by LVN. - mir->dalvikInsn.opcode = def->opcode; - mir->offset = i; // LVN uses offset only for debug output - mir->optimization_flags = 0u; - - if (i != 0u) { - mirs_[i - 1u].next = mir; - } - } - mirs_[count - 1u].next = nullptr; - } - - template - void PrepareMIRs(const MIRDef (&defs)[count]) { - DoPrepareMIRs(defs, count); - } - - void MakeSFieldUninitialized(uint32_t sfield_index) { - CHECK_LT(sfield_index, cu_.mir_graph->sfield_lowering_infos_.size()); - cu_.mir_graph->sfield_lowering_infos_[sfield_index].flags_ &= - ~MirSFieldLoweringInfo::kFlagClassIsInitialized; - } - - template - void MarkAsWideSRegs(const int32_t (&sregs)[count]) { - for (int32_t sreg : sregs) { - cu_.mir_graph->reg_location_[sreg].wide = true; - cu_.mir_graph->reg_location_[sreg + 1].wide = true; - cu_.mir_graph->reg_location_[sreg + 1].high_word = true; - } - } - - void PerformLVN() { - cu_.mir_graph->temp_.gvn.ifield_ids = GlobalValueNumbering::PrepareGvnFieldIds( - allocator_.get(), cu_.mir_graph->ifield_lowering_infos_); - cu_.mir_graph->temp_.gvn.sfield_ids = GlobalValueNumbering::PrepareGvnFieldIds( - allocator_.get(), cu_.mir_graph->sfield_lowering_infos_); - gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(), - GlobalValueNumbering::kModeLvn)); - lvn_.reset(new (allocator_.get()) LocalValueNumbering(gvn_.get(), 0u, allocator_.get())); - value_names_.resize(mir_count_); - for (size_t i = 0; i != mir_count_; ++i) { - value_names_[i] = lvn_->GetValueNumber(&mirs_[i]); - } - EXPECT_TRUE(gvn_->Good()); - } - - LocalValueNumberingTest() - : pool_(), - cu_(&pool_, kRuntimeISA, nullptr, nullptr), - mir_count_(0u), - mirs_(nullptr), - ssa_reps_(), - allocator_(), - gvn_(), - lvn_(), - value_names_() { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); - allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); - // By default, the zero-initialized reg_location_[.] with ref == false tells LVN that - // 0 constants are integral, not references, and the values are all narrow. - // Nothing else is used by LVN/GVN. Tests can override the default values as needed. - cu_.mir_graph->reg_location_ = static_cast(cu_.arena.Alloc( - kMaxSsaRegs * sizeof(cu_.mir_graph->reg_location_[0]), kArenaAllocRegAlloc)); - cu_.mir_graph->num_ssa_regs_ = kMaxSsaRegs; - } - - static constexpr size_t kMaxSsaRegs = 16384u; - - ArenaPool pool_; - CompilationUnit cu_; - size_t mir_count_; - MIR* mirs_; - std::vector ssa_reps_; - std::unique_ptr allocator_; - std::unique_ptr gvn_; - std::unique_ptr lvn_; - std::vector value_names_; -}; - -TEST_F(LocalValueNumberingTest, IGetIGetInvokeIGet) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_IGET(Instruction::IGET, 0u, 10u, 0u), - DEF_IGET(Instruction::IGET, 1u, 10u, 0u), - DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 11u), - DEF_IGET(Instruction::IGET, 2u, 10u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 4u); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_NE(value_names_[0], value_names_[3]); - EXPECT_EQ(mirs_[0].optimization_flags, 0u); - EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK); - EXPECT_EQ(mirs_[2].optimization_flags, 0u); - EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK); -} - -TEST_F(LocalValueNumberingTest, IGetIPutIGetIGetIGet) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessObject }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_IGET(Instruction::IGET_OBJECT, 0u, 10u, 0u), - DEF_IPUT(Instruction::IPUT_OBJECT, 1u, 11u, 0u), // May alias. - DEF_IGET(Instruction::IGET_OBJECT, 2u, 10u, 0u), - DEF_IGET(Instruction::IGET, 3u, 0u, 1u), - DEF_IGET(Instruction::IGET, 4u, 2u, 1u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 5u); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_NE(value_names_[3], value_names_[4]); - for (size_t i = 0; i != arraysize(mirs); ++i) { - EXPECT_EQ((i == 2u) ? MIR_IGNORE_NULL_CHECK : 0, - mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UniquePreserve1) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 10u), - DEF_IGET(Instruction::IGET, 0u, 10u, 0u), - DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u), // No aliasing since 10u is unique. - DEF_IGET(Instruction::IGET, 2u, 10u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 4u); - EXPECT_EQ(value_names_[1], value_names_[3]); - for (size_t i = 0; i != arraysize(mirs); ++i) { - EXPECT_EQ((i == 1u || i == 3u) ? MIR_IGNORE_NULL_CHECK : 0, - mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UniquePreserve2) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 11u), - DEF_IGET(Instruction::IGET, 0u, 10u, 0u), - DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u), // No aliasing since 11u is unique. - DEF_IGET(Instruction::IGET, 2u, 10u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 4u); - EXPECT_EQ(value_names_[1], value_names_[3]); - for (size_t i = 0; i != arraysize(mirs); ++i) { - EXPECT_EQ((i == 2u || i == 3u) ? MIR_IGNORE_NULL_CHECK : 0, - mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UniquePreserveAndEscape) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 10u), - DEF_IGET(Instruction::IGET, 0u, 10u, 0u), - DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 11u), // 10u still unique. - DEF_IGET(Instruction::IGET, 2u, 10u, 0u), - DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 10u), // 10u not unique anymore. - DEF_IGET(Instruction::IGET, 3u, 10u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 6u); - EXPECT_EQ(value_names_[1], value_names_[3]); - EXPECT_NE(value_names_[1], value_names_[5]); - for (size_t i = 0; i != arraysize(mirs); ++i) { - EXPECT_EQ((i == 1u || i == 3u || i == 4u || i == 5u) ? MIR_IGNORE_NULL_CHECK : 0, - mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, Volatile) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, true, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_IGET(Instruction::IGET, 0u, 10u, 1u), // Volatile. - DEF_IGET(Instruction::IGET, 1u, 0u, 0u), // Non-volatile. - DEF_IGET(Instruction::IGET, 2u, 10u, 1u), // Volatile. - DEF_IGET(Instruction::IGET, 3u, 2u, 1u), // Non-volatile. - DEF_IGET(Instruction::IGET, 4u, 0u, 0u), // Non-volatile. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 5u); - EXPECT_NE(value_names_[0], value_names_[2]); // Volatile has always different value name. - EXPECT_NE(value_names_[1], value_names_[3]); // Used different base because of volatile. - EXPECT_NE(value_names_[1], value_names_[4]); // Not guaranteed to be the same after "acquire". - - for (size_t i = 0; i != arraysize(mirs); ++i) { - EXPECT_EQ((i == 2u || i == 4u) ? MIR_IGNORE_NULL_CHECK : 0, - mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UnresolvedIField) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, // Resolved field #1. - { 2u, 1u, 2u, false, kDexMemAccessWide }, // Resolved field #2. - { 3u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved field. - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 30u), - DEF_IGET(Instruction::IGET, 1u, 30u, 0u), // Resolved field #1, unique object. - DEF_IGET(Instruction::IGET, 2u, 31u, 0u), // Resolved field #1. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 3u, 31u, 1u), // Resolved field #2. - DEF_IGET(Instruction::IGET, 5u, 32u, 2u), // Unresolved IGET can be "acquire". - DEF_IGET(Instruction::IGET, 6u, 30u, 0u), // Resolved field #1, unique object. - DEF_IGET(Instruction::IGET, 7u, 31u, 0u), // Resolved field #1. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 8u, 31u, 1u), // Resolved field #2. - DEF_IPUT(Instruction::IPUT, 10u, 32u, 2u), // IPUT clobbers field #1 (#2 is wide). - DEF_IGET(Instruction::IGET, 11u, 30u, 0u), // Resolved field #1, unique object. - DEF_IGET(Instruction::IGET, 12u, 31u, 0u), // Resolved field #1, new value name. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 13u, 31u, 1u), // Resolved field #2. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 15u, 30u, 1u), // Resolved field #2, unique object. - DEF_IPUT(Instruction::IPUT, 17u, 30u, 2u), // IPUT clobbers field #1 (#2 is wide). - DEF_IGET(Instruction::IGET, 18u, 30u, 0u), // Resolved field #1, unique object. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 19u, 30u, 1u), // Resolved field #2, unique object. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 3, 8, 13, 15, 19 }; - MarkAsWideSRegs(wide_sregs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 16u); - // Unresolved field is potentially volatile, so we need to adhere to the volatile semantics. - EXPECT_EQ(value_names_[1], value_names_[5]); // Unique object. - EXPECT_NE(value_names_[2], value_names_[6]); // Not guaranteed to be the same after "acquire". - EXPECT_NE(value_names_[3], value_names_[7]); // Not guaranteed to be the same after "acquire". - EXPECT_EQ(value_names_[1], value_names_[9]); // Unique object. - EXPECT_NE(value_names_[6], value_names_[10]); // This aliased with unresolved IPUT. - EXPECT_EQ(value_names_[7], value_names_[11]); // Still the same after "release". - EXPECT_EQ(value_names_[12], value_names_[15]); // Still the same after "release". - EXPECT_NE(value_names_[1], value_names_[14]); // This aliased with unresolved IPUT. - EXPECT_EQ(mirs_[0].optimization_flags, 0u); - EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK); - EXPECT_EQ(mirs_[2].optimization_flags, 0u); - EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK); - EXPECT_EQ(mirs_[4].optimization_flags, 0u); - for (size_t i = 5u; i != mir_count_; ++i) { - EXPECT_EQ((i == 1u || i == 3u || i >=5u) ? MIR_IGNORE_NULL_CHECK : 0, - mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UnresolvedSField) { - static const SFieldDef sfields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, // Resolved field #1. - { 2u, 1u, 2u, false, kDexMemAccessWide }, // Resolved field #2. - { 3u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved field. - }; - static const MIRDef mirs[] = { - DEF_SGET(Instruction::SGET, 0u, 0u), // Resolved field #1. - DEF_SGET_WIDE(Instruction::SGET_WIDE, 1u, 1u), // Resolved field #2. - DEF_SGET(Instruction::SGET, 3u, 2u), // Unresolved SGET can be "acquire". - DEF_SGET(Instruction::SGET, 4u, 0u), // Resolved field #1. - DEF_SGET_WIDE(Instruction::SGET_WIDE, 5u, 1u), // Resolved field #2. - DEF_SPUT(Instruction::SPUT, 7u, 2u), // SPUT clobbers field #1 (#2 is wide). - DEF_SGET(Instruction::SGET, 8u, 0u), // Resolved field #1. - DEF_SGET_WIDE(Instruction::SGET_WIDE, 9u, 1u), // Resolved field #2. - }; - - PrepareSFields(sfields); - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 1, 5, 9 }; - MarkAsWideSRegs(wide_sregs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 8u); - // Unresolved field is potentially volatile, so we need to adhere to the volatile semantics. - EXPECT_NE(value_names_[0], value_names_[3]); // Not guaranteed to be the same after "acquire". - EXPECT_NE(value_names_[1], value_names_[4]); // Not guaranteed to be the same after "acquire". - EXPECT_NE(value_names_[3], value_names_[6]); // This aliased with unresolved IPUT. - EXPECT_EQ(value_names_[4], value_names_[7]); // Still the same after "release". - for (size_t i = 0u; i != mir_count_; ++i) { - EXPECT_EQ(0, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UninitializedSField) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, // Resolved field #1. - }; - static const SFieldDef sfields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, // Resolved field #1. - { 2u, 1u, 2u, false, kDexMemAccessWord }, // Resolved field #2; uninitialized. - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 200u), - DEF_IGET(Instruction::IGET, 1u, 100u, 0u), - DEF_IGET(Instruction::IGET, 2u, 200u, 0u), - DEF_SGET(Instruction::SGET, 3u, 0u), - DEF_SGET(Instruction::SGET, 4u, 1u), // Can call (). - DEF_IGET(Instruction::IGET, 5u, 100u, 0u), // Differs from 1u. - DEF_IGET(Instruction::IGET, 6u, 200u, 0u), // Same as 2u. - DEF_SGET(Instruction::SGET, 7u, 0u), // Differs from 3u. - }; - - PrepareIFields(ifields); - PrepareSFields(sfields); - MakeSFieldUninitialized(1u); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 8u); - EXPECT_NE(value_names_[1], value_names_[5]); - EXPECT_EQ(value_names_[2], value_names_[6]); - EXPECT_NE(value_names_[3], value_names_[7]); -} - -TEST_F(LocalValueNumberingTest, ConstString) { - static const MIRDef mirs[] = { - DEF_CONST_STRING(Instruction::CONST_STRING, 0u, 0u), - DEF_CONST_STRING(Instruction::CONST_STRING, 1u, 0u), - DEF_CONST_STRING(Instruction::CONST_STRING, 2u, 2u), - DEF_CONST_STRING(Instruction::CONST_STRING, 3u, 0u), - DEF_INVOKE1(Instruction::INVOKE_DIRECT, 2u), - DEF_CONST_STRING(Instruction::CONST_STRING, 4u, 2u), - }; - - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 6u); - EXPECT_EQ(value_names_[1], value_names_[0]); - EXPECT_NE(value_names_[2], value_names_[0]); - EXPECT_EQ(value_names_[3], value_names_[0]); - EXPECT_EQ(value_names_[5], value_names_[2]); -} - -TEST_F(LocalValueNumberingTest, SameValueInDifferentMemoryLocations) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const SFieldDef sfields[] = { - { 3u, 1u, 3u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_ARRAY, 201u), - DEF_IGET(Instruction::IGET, 0u, 100u, 0u), - DEF_IPUT(Instruction::IPUT, 0u, 100u, 1u), - DEF_IPUT(Instruction::IPUT, 0u, 101u, 1u), - DEF_APUT(Instruction::APUT, 0u, 200u, 300u), - DEF_APUT(Instruction::APUT, 0u, 200u, 301u), - DEF_APUT(Instruction::APUT, 0u, 201u, 300u), - DEF_APUT(Instruction::APUT, 0u, 201u, 301u), - DEF_SPUT(Instruction::SPUT, 0u, 0u), - DEF_IGET(Instruction::IGET, 9u, 100u, 0u), - DEF_IGET(Instruction::IGET, 10u, 100u, 1u), - DEF_IGET(Instruction::IGET, 11u, 101u, 1u), - DEF_AGET(Instruction::AGET, 12u, 200u, 300u), - DEF_AGET(Instruction::AGET, 13u, 200u, 301u), - DEF_AGET(Instruction::AGET, 14u, 201u, 300u), - DEF_AGET(Instruction::AGET, 15u, 201u, 301u), - DEF_SGET(Instruction::SGET, 16u, 0u), - }; - - PrepareIFields(ifields); - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 17u); - for (size_t i = 9; i != arraysize(mirs); ++i) { - EXPECT_EQ(value_names_[1], value_names_[i]) << i; - } - for (size_t i = 0; i != arraysize(mirs); ++i) { - int expected_flags = - ((i == 2u || (i >= 5u && i <= 7u) || (i >= 9u && i <= 15u)) ? MIR_IGNORE_NULL_CHECK : 0) | - ((i >= 12u && i <= 15u) ? MIR_IGNORE_RANGE_CHECK : 0); - EXPECT_EQ(expected_flags, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UniqueArrayAliasing) { - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_ARRAY, 20u), - DEF_AGET(Instruction::AGET, 1u, 20u, 40u), - DEF_APUT(Instruction::APUT, 2u, 20u, 41u), // May alias with index for sreg 40u. - DEF_AGET(Instruction::AGET, 3u, 20u, 40u), - }; - - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 4u); - EXPECT_NE(value_names_[1], value_names_[3]); - for (size_t i = 0; i != arraysize(mirs); ++i) { - int expected_flags = - ((i >= 1u) ? MIR_IGNORE_NULL_CHECK : 0) | - ((i == 3u) ? MIR_IGNORE_RANGE_CHECK : 0); - EXPECT_EQ(expected_flags, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, EscapingRefs) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, // Field #1. - { 2u, 1u, 2u, false, kDexMemAccessWord }, // Field #2. - { 3u, 1u, 3u, false, kDexMemAccessObject }, // For storing escaping refs. - { 4u, 1u, 4u, false, kDexMemAccessWide }, // Wide. - { 5u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved field, int. - { 6u, 0u, 0u, false, kDexMemAccessWide }, // Unresolved field, wide. - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 20u), - DEF_IGET(Instruction::IGET, 1u, 20u, 0u), - DEF_IGET(Instruction::IGET, 2u, 20u, 1u), - DEF_IPUT(Instruction::IPUT_OBJECT, 20u, 30u, 2u), // Ref escapes. - DEF_IGET(Instruction::IGET, 4u, 20u, 0u), - DEF_IGET(Instruction::IGET, 5u, 20u, 1u), - DEF_IPUT(Instruction::IPUT, 6u, 31u, 0u), // May alias with field #1. - DEF_IGET(Instruction::IGET, 7u, 20u, 0u), // New value. - DEF_IGET(Instruction::IGET, 8u, 20u, 1u), // Still the same. - DEF_IPUT_WIDE(Instruction::IPUT_WIDE, 9u, 31u, 3u), // No aliasing, different type. - DEF_IGET(Instruction::IGET, 11u, 20u, 0u), - DEF_IGET(Instruction::IGET, 12u, 20u, 1u), - DEF_IPUT_WIDE(Instruction::IPUT_WIDE, 13u, 31u, 5u), // No aliasing, different type. - DEF_IGET(Instruction::IGET, 15u, 20u, 0u), - DEF_IGET(Instruction::IGET, 16u, 20u, 1u), - DEF_IPUT(Instruction::IPUT, 17u, 31u, 4u), // Aliasing, same type. - DEF_IGET(Instruction::IGET, 18u, 20u, 0u), - DEF_IGET(Instruction::IGET, 19u, 20u, 1u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 9, 13 }; - MarkAsWideSRegs(wide_sregs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 18u); - EXPECT_EQ(value_names_[1], value_names_[4]); - EXPECT_EQ(value_names_[2], value_names_[5]); - EXPECT_NE(value_names_[4], value_names_[7]); // New value. - EXPECT_EQ(value_names_[5], value_names_[8]); - EXPECT_EQ(value_names_[7], value_names_[10]); - EXPECT_EQ(value_names_[8], value_names_[11]); - EXPECT_EQ(value_names_[10], value_names_[13]); - EXPECT_EQ(value_names_[11], value_names_[14]); - EXPECT_NE(value_names_[13], value_names_[16]); // New value. - EXPECT_NE(value_names_[14], value_names_[17]); // New value. - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = - ((i != 0u && i != 3u && i != 6u) ? MIR_IGNORE_NULL_CHECK : 0) | - ((i == 3u) ? MIR_STORE_NON_NULL_VALUE: 0); - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, EscapingArrayRefs) { - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_ARRAY, 20u), - DEF_AGET(Instruction::AGET, 1u, 20u, 40u), - DEF_AGET(Instruction::AGET, 2u, 20u, 41u), - DEF_APUT(Instruction::APUT_OBJECT, 20u, 30u, 42u), // Array ref escapes. - DEF_AGET(Instruction::AGET, 4u, 20u, 40u), - DEF_AGET(Instruction::AGET, 5u, 20u, 41u), - DEF_APUT_WIDE(Instruction::APUT_WIDE, 6u, 31u, 43u), // No aliasing, different type. - DEF_AGET(Instruction::AGET, 8u, 20u, 40u), - DEF_AGET(Instruction::AGET, 9u, 20u, 41u), - DEF_APUT(Instruction::APUT, 10u, 32u, 40u), // May alias with all elements. - DEF_AGET(Instruction::AGET, 11u, 20u, 40u), // New value (same index name). - DEF_AGET(Instruction::AGET, 12u, 20u, 41u), // New value (different index name). - }; - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 6 }; - MarkAsWideSRegs(wide_sregs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 12u); - EXPECT_EQ(value_names_[1], value_names_[4]); - EXPECT_EQ(value_names_[2], value_names_[5]); - EXPECT_EQ(value_names_[4], value_names_[7]); - EXPECT_EQ(value_names_[5], value_names_[8]); - EXPECT_NE(value_names_[7], value_names_[10]); // New value. - EXPECT_NE(value_names_[8], value_names_[11]); // New value. - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = - ((i != 0u && i != 3u && i != 6u && i != 9u) ? MIR_IGNORE_NULL_CHECK : 0u) | - ((i >= 4 && i != 6u && i != 9u) ? MIR_IGNORE_RANGE_CHECK : 0u) | - ((i == 3u) ? MIR_STORE_NON_NULL_VALUE: 0); - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, StoringSameValueKeepsMemoryVersion) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const SFieldDef sfields[] = { - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_IGET(Instruction::IGET, 0u, 30u, 0u), - DEF_IGET(Instruction::IGET, 1u, 31u, 0u), - DEF_IPUT(Instruction::IPUT, 1u, 31u, 0u), // Store the same value. - DEF_IGET(Instruction::IGET, 3u, 30u, 0u), - DEF_AGET(Instruction::AGET, 4u, 32u, 40u), - DEF_AGET(Instruction::AGET, 5u, 33u, 40u), - DEF_APUT(Instruction::APUT, 5u, 33u, 40u), // Store the same value. - DEF_AGET(Instruction::AGET, 7u, 32u, 40u), - DEF_SGET(Instruction::SGET, 8u, 0u), - DEF_SPUT(Instruction::SPUT, 8u, 0u), // Store the same value. - DEF_SGET(Instruction::SGET, 10u, 0u), - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 50u), // Test with unique references. - { Instruction::FILLED_NEW_ARRAY, 0, 0u, 2, { 12u, 13u }, 0, { } }, - DEF_UNIQUE_REF(Instruction::MOVE_RESULT_OBJECT, 51u), - DEF_IGET(Instruction::IGET, 14u, 50u, 0u), - DEF_IGET(Instruction::IGET, 15u, 50u, 1u), - DEF_IPUT(Instruction::IPUT, 15u, 50u, 1u), // Store the same value. - DEF_IGET(Instruction::IGET, 17u, 50u, 0u), - DEF_AGET(Instruction::AGET, 18u, 51u, 40u), - DEF_AGET(Instruction::AGET, 19u, 51u, 41u), - DEF_APUT(Instruction::APUT, 19u, 51u, 41u), // Store the same value. - DEF_AGET(Instruction::AGET, 21u, 51u, 40u), - }; - - PrepareIFields(ifields); - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 22u); - EXPECT_NE(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[3]); - EXPECT_NE(value_names_[4], value_names_[5]); - EXPECT_EQ(value_names_[4], value_names_[7]); - EXPECT_EQ(value_names_[8], value_names_[10]); - EXPECT_NE(value_names_[14], value_names_[15]); - EXPECT_EQ(value_names_[14], value_names_[17]); - EXPECT_NE(value_names_[18], value_names_[19]); - EXPECT_EQ(value_names_[18], value_names_[21]); - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = - ((i == 2u || i == 3u || i == 6u || i == 7u || (i >= 14u)) ? MIR_IGNORE_NULL_CHECK : 0u) | - ((i == 6u || i == 7u || i >= 20u) ? MIR_IGNORE_RANGE_CHECK : 0u); - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, FilledNewArrayTracking) { - if (!kLocalValueNumberingEnableFilledNewArrayTracking) { - // Feature disabled. - return; - } - static const MIRDef mirs[] = { - DEF_CONST(Instruction::CONST, 0u, 100), - DEF_CONST(Instruction::CONST, 1u, 200), - { Instruction::FILLED_NEW_ARRAY, 0, 0u, 2, { 0u, 1u }, 0, { } }, - DEF_UNIQUE_REF(Instruction::MOVE_RESULT_OBJECT, 10u), - DEF_CONST(Instruction::CONST, 20u, 0), - DEF_CONST(Instruction::CONST, 21u, 1), - DEF_AGET(Instruction::AGET, 6u, 10u, 20u), - DEF_AGET(Instruction::AGET, 7u, 10u, 21u), - }; - - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 8u); - EXPECT_EQ(value_names_[0], value_names_[6]); - EXPECT_EQ(value_names_[1], value_names_[7]); - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = (i == 6u || i == 7u) ? (MIR_IGNORE_NULL_CHECK | MIR_IGNORE_RANGE_CHECK) : 0u; - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, ClInitOnSget) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - { 1u, 2u, 1u, false, kDexMemAccessObject }, - }; - static const MIRDef mirs[] = { - DEF_SGET(Instruction::SGET_OBJECT, 0u, 0u), - DEF_AGET(Instruction::AGET, 1u, 0u, 100u), - DEF_SGET(Instruction::SGET_OBJECT, 2u, 1u), - DEF_SGET(Instruction::SGET_OBJECT, 3u, 0u), - DEF_AGET(Instruction::AGET, 4u, 3u, 100u), - }; - - PrepareSFields(sfields); - MakeSFieldUninitialized(1u); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 5u); - EXPECT_NE(value_names_[0], value_names_[3]); -} - -TEST_F(LocalValueNumberingTest, DivZeroCheck) { - static const MIRDef mirs[] = { - DEF_DIV_REM(Instruction::DIV_INT, 1u, 10u, 20u), - DEF_DIV_REM(Instruction::DIV_INT, 2u, 20u, 20u), - DEF_DIV_REM(Instruction::DIV_INT_2ADDR, 3u, 10u, 1u), - DEF_DIV_REM(Instruction::REM_INT, 4u, 30u, 20u), - DEF_DIV_REM_WIDE(Instruction::REM_LONG, 5u, 12u, 14u), - DEF_DIV_REM_WIDE(Instruction::DIV_LONG_2ADDR, 7u, 16u, 14u), - }; - - static const bool expected_ignore_div_zero_check[] = { - false, true, false, true, false, true, - }; - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 5, 7, 12, 14, 16 }; - MarkAsWideSRegs(wide_sregs); - PerformLVN(); - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = expected_ignore_div_zero_check[i] ? MIR_IGNORE_DIV_ZERO_CHECK : 0u; - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -static constexpr int64_t shift_minus_1(size_t by) { - return static_cast(static_cast(INT64_C(-1)) << by); -} - -TEST_F(LocalValueNumberingTest, ConstWide) { - static const MIRDef mirs[] = { - // Core reg constants. - DEF_CONST(Instruction::CONST_WIDE_16, 0u, 0), - DEF_CONST(Instruction::CONST_WIDE_16, 2u, 1), - DEF_CONST(Instruction::CONST_WIDE_16, 4u, -1), - DEF_CONST(Instruction::CONST_WIDE_32, 6u, 1 << 16), - DEF_CONST(Instruction::CONST_WIDE_32, 8u, shift_minus_1(16)), - DEF_CONST(Instruction::CONST_WIDE_32, 10u, (1 << 16) + 1), - DEF_CONST(Instruction::CONST_WIDE_32, 12u, (1 << 16) - 1), - DEF_CONST(Instruction::CONST_WIDE_32, 14u, -(1 << 16) + 1), - DEF_CONST(Instruction::CONST_WIDE_32, 16u, -(1 << 16) - 1), - DEF_CONST(Instruction::CONST_WIDE, 18u, INT64_C(1) << 32), - DEF_CONST(Instruction::CONST_WIDE, 20u, shift_minus_1(32)), - DEF_CONST(Instruction::CONST_WIDE, 22u, (INT64_C(1) << 32) + 1), - DEF_CONST(Instruction::CONST_WIDE, 24u, (INT64_C(1) << 32) - 1), - DEF_CONST(Instruction::CONST_WIDE, 26u, shift_minus_1(32) + 1), - DEF_CONST(Instruction::CONST_WIDE, 28u, shift_minus_1(32) - 1), - DEF_CONST(Instruction::CONST_WIDE_HIGH16, 30u, 1), // Effectively 1 << 48. - DEF_CONST(Instruction::CONST_WIDE_HIGH16, 32u, 0xffff), // Effectively -1 << 48. - DEF_CONST(Instruction::CONST_WIDE, 34u, (INT64_C(1) << 48) + 1), - DEF_CONST(Instruction::CONST_WIDE, 36u, (INT64_C(1) << 48) - 1), - DEF_CONST(Instruction::CONST_WIDE, 38u, shift_minus_1(48) + 1), - DEF_CONST(Instruction::CONST_WIDE, 40u, shift_minus_1(48) - 1), - // FP reg constants. - DEF_CONST(Instruction::CONST_WIDE_16, 42u, 0), - DEF_CONST(Instruction::CONST_WIDE_16, 44u, 1), - DEF_CONST(Instruction::CONST_WIDE_16, 46u, -1), - DEF_CONST(Instruction::CONST_WIDE_32, 48u, 1 << 16), - DEF_CONST(Instruction::CONST_WIDE_32, 50u, shift_minus_1(16)), - DEF_CONST(Instruction::CONST_WIDE_32, 52u, (1 << 16) + 1), - DEF_CONST(Instruction::CONST_WIDE_32, 54u, (1 << 16) - 1), - DEF_CONST(Instruction::CONST_WIDE_32, 56u, -(1 << 16) + 1), - DEF_CONST(Instruction::CONST_WIDE_32, 58u, -(1 << 16) - 1), - DEF_CONST(Instruction::CONST_WIDE, 60u, INT64_C(1) << 32), - DEF_CONST(Instruction::CONST_WIDE, 62u, shift_minus_1(32)), - DEF_CONST(Instruction::CONST_WIDE, 64u, (INT64_C(1) << 32) + 1), - DEF_CONST(Instruction::CONST_WIDE, 66u, (INT64_C(1) << 32) - 1), - DEF_CONST(Instruction::CONST_WIDE, 68u, shift_minus_1(32) + 1), - DEF_CONST(Instruction::CONST_WIDE, 70u, shift_minus_1(32) - 1), - DEF_CONST(Instruction::CONST_WIDE_HIGH16, 72u, 1), // Effectively 1 << 48. - DEF_CONST(Instruction::CONST_WIDE_HIGH16, 74u, 0xffff), // Effectively -1 << 48. - DEF_CONST(Instruction::CONST_WIDE, 76u, (INT64_C(1) << 48) + 1), - DEF_CONST(Instruction::CONST_WIDE, 78u, (INT64_C(1) << 48) - 1), - DEF_CONST(Instruction::CONST_WIDE, 80u, shift_minus_1(48) + 1), - DEF_CONST(Instruction::CONST_WIDE, 82u, shift_minus_1(48) - 1), - }; - - PrepareMIRs(mirs); - for (size_t i = 0; i != arraysize(mirs); ++i) { - const int32_t wide_sregs[] = { mirs_[i].ssa_rep->defs[0] }; - MarkAsWideSRegs(wide_sregs); - } - for (size_t i = arraysize(mirs) / 2u; i != arraysize(mirs); ++i) { - cu_.mir_graph->reg_location_[mirs_[i].ssa_rep->defs[0]].fp = true; - } - PerformLVN(); - for (size_t i = 0u; i != mir_count_; ++i) { - for (size_t j = i + 1u; j != mir_count_; ++j) { - EXPECT_NE(value_names_[i], value_names_[j]) << i << " " << j; - } - } -} - -TEST_F(LocalValueNumberingTest, Const) { - static const MIRDef mirs[] = { - // Core reg constants. - DEF_CONST(Instruction::CONST_4, 0u, 0), - DEF_CONST(Instruction::CONST_4, 1u, 1), - DEF_CONST(Instruction::CONST_4, 2u, -1), - DEF_CONST(Instruction::CONST_16, 3u, 1 << 4), - DEF_CONST(Instruction::CONST_16, 4u, shift_minus_1(4)), - DEF_CONST(Instruction::CONST_16, 5u, (1 << 4) + 1), - DEF_CONST(Instruction::CONST_16, 6u, (1 << 4) - 1), - DEF_CONST(Instruction::CONST_16, 7u, -(1 << 4) + 1), - DEF_CONST(Instruction::CONST_16, 8u, -(1 << 4) - 1), - DEF_CONST(Instruction::CONST_HIGH16, 9u, 1), // Effectively 1 << 16. - DEF_CONST(Instruction::CONST_HIGH16, 10u, 0xffff), // Effectively -1 << 16. - DEF_CONST(Instruction::CONST, 11u, (1 << 16) + 1), - DEF_CONST(Instruction::CONST, 12u, (1 << 16) - 1), - DEF_CONST(Instruction::CONST, 13u, shift_minus_1(16) + 1), - DEF_CONST(Instruction::CONST, 14u, shift_minus_1(16) - 1), - // FP reg constants. - DEF_CONST(Instruction::CONST_4, 15u, 0), - DEF_CONST(Instruction::CONST_4, 16u, 1), - DEF_CONST(Instruction::CONST_4, 17u, -1), - DEF_CONST(Instruction::CONST_16, 18u, 1 << 4), - DEF_CONST(Instruction::CONST_16, 19u, shift_minus_1(4)), - DEF_CONST(Instruction::CONST_16, 20u, (1 << 4) + 1), - DEF_CONST(Instruction::CONST_16, 21u, (1 << 4) - 1), - DEF_CONST(Instruction::CONST_16, 22u, -(1 << 4) + 1), - DEF_CONST(Instruction::CONST_16, 23u, -(1 << 4) - 1), - DEF_CONST(Instruction::CONST_HIGH16, 24u, 1), // Effectively 1 << 16. - DEF_CONST(Instruction::CONST_HIGH16, 25u, 0xffff), // Effectively -1 << 16. - DEF_CONST(Instruction::CONST, 26u, (1 << 16) + 1), - DEF_CONST(Instruction::CONST, 27u, (1 << 16) - 1), - DEF_CONST(Instruction::CONST, 28u, shift_minus_1(16) + 1), - DEF_CONST(Instruction::CONST, 29u, shift_minus_1(16) - 1), - // null reference constant. - DEF_CONST(Instruction::CONST_4, 30u, 0), - }; - - PrepareMIRs(mirs); - static_assert((arraysize(mirs) & 1) != 0, "missing null or unmatched fp/core"); - cu_.mir_graph->reg_location_[arraysize(mirs) - 1].ref = true; - for (size_t i = arraysize(mirs) / 2u; i != arraysize(mirs) - 1; ++i) { - cu_.mir_graph->reg_location_[mirs_[i].ssa_rep->defs[0]].fp = true; - } - PerformLVN(); - for (size_t i = 0u; i != mir_count_; ++i) { - for (size_t j = i + 1u; j != mir_count_; ++j) { - EXPECT_NE(value_names_[i], value_names_[j]) << i << " " << j; - } - } -} - -} // namespace art diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc deleted file mode 100644 index 18ce563fc2b0f6aac5bdc65af07e9c7fc52645b6..0000000000000000000000000000000000000000 --- a/compiler/dex/mir_analysis.cc +++ /dev/null @@ -1,1433 +0,0 @@ -/* - * Copyright (C) 2013 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 -#include - -#include "base/logging.h" -#include "base/scoped_arena_containers.h" -#include "dataflow_iterator-inl.h" -#include "compiler_ir.h" -#include "dex_flags.h" -#include "dex_instruction-inl.h" -#include "dex/mir_field_info.h" -#include "dex/verified_method.h" -#include "dex/quick/dex_file_method_inliner.h" -#include "dex/quick/dex_file_to_method_inliner_map.h" -#include "driver/compiler_driver.h" -#include "driver/compiler_options.h" -#include "driver/dex_compilation_unit.h" -#include "scoped_thread_state_change.h" -#include "utils.h" - -namespace art { - -enum InstructionAnalysisAttributeOps : uint8_t { - kUninterestingOp = 0, - kArithmeticOp, - kFpOp, - kSingleOp, - kDoubleOp, - kIntOp, - kLongOp, - kBranchOp, - kInvokeOp, - kArrayOp, - kHeavyweightOp, - kSimpleConstOp, - kMoveOp, - kSwitch -}; - -enum InstructionAnalysisAttributeMasks : uint16_t { - kAnNone = 1 << kUninterestingOp, - kAnMath = 1 << kArithmeticOp, - kAnFp = 1 << kFpOp, - kAnLong = 1 << kLongOp, - kAnInt = 1 << kIntOp, - kAnSingle = 1 << kSingleOp, - kAnDouble = 1 << kDoubleOp, - kAnFloatMath = 1 << kFpOp, - kAnBranch = 1 << kBranchOp, - kAnInvoke = 1 << kInvokeOp, - kAnArrayOp = 1 << kArrayOp, - kAnHeavyWeight = 1 << kHeavyweightOp, - kAnSimpleConst = 1 << kSimpleConstOp, - kAnMove = 1 << kMoveOp, - kAnSwitch = 1 << kSwitch, - kAnComputational = kAnMath | kAnArrayOp | kAnMove | kAnSimpleConst, -}; - -// Instruction characteristics used to statically identify computation-intensive methods. -static const uint16_t kAnalysisAttributes[kMirOpLast] = { - // 00 NOP - kAnNone, - - // 01 MOVE vA, vB - kAnMove, - - // 02 MOVE_FROM16 vAA, vBBBB - kAnMove, - - // 03 MOVE_16 vAAAA, vBBBB - kAnMove, - - // 04 MOVE_WIDE vA, vB - kAnMove, - - // 05 MOVE_WIDE_FROM16 vAA, vBBBB - kAnMove, - - // 06 MOVE_WIDE_16 vAAAA, vBBBB - kAnMove, - - // 07 MOVE_OBJECT vA, vB - kAnMove, - - // 08 MOVE_OBJECT_FROM16 vAA, vBBBB - kAnMove, - - // 09 MOVE_OBJECT_16 vAAAA, vBBBB - kAnMove, - - // 0A MOVE_RESULT vAA - kAnMove, - - // 0B MOVE_RESULT_WIDE vAA - kAnMove, - - // 0C MOVE_RESULT_OBJECT vAA - kAnMove, - - // 0D MOVE_EXCEPTION vAA - kAnMove, - - // 0E RETURN_VOID - kAnBranch, - - // 0F RETURN vAA - kAnBranch, - - // 10 RETURN_WIDE vAA - kAnBranch, - - // 11 RETURN_OBJECT vAA - kAnBranch, - - // 12 CONST_4 vA, #+B - kAnSimpleConst, - - // 13 CONST_16 vAA, #+BBBB - kAnSimpleConst, - - // 14 CONST vAA, #+BBBBBBBB - kAnSimpleConst, - - // 15 CONST_HIGH16 VAA, #+BBBB0000 - kAnSimpleConst, - - // 16 CONST_WIDE_16 vAA, #+BBBB - kAnSimpleConst, - - // 17 CONST_WIDE_32 vAA, #+BBBBBBBB - kAnSimpleConst, - - // 18 CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB - kAnSimpleConst, - - // 19 CONST_WIDE_HIGH16 vAA, #+BBBB000000000000 - kAnSimpleConst, - - // 1A CONST_STRING vAA, string@BBBB - kAnNone, - - // 1B CONST_STRING_JUMBO vAA, string@BBBBBBBB - kAnNone, - - // 1C CONST_CLASS vAA, type@BBBB - kAnNone, - - // 1D MONITOR_ENTER vAA - kAnNone, - - // 1E MONITOR_EXIT vAA - kAnNone, - - // 1F CHK_CAST vAA, type@BBBB - kAnNone, - - // 20 INSTANCE_OF vA, vB, type@CCCC - kAnNone, - - // 21 ARRAY_LENGTH vA, vB - kAnArrayOp, - - // 22 NEW_INSTANCE vAA, type@BBBB - kAnHeavyWeight, - - // 23 NEW_ARRAY vA, vB, type@CCCC - kAnHeavyWeight, - - // 24 FILLED_NEW_ARRAY {vD, vE, vF, vG, vA} - kAnHeavyWeight, - - // 25 FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB - kAnHeavyWeight, - - // 26 FILL_ARRAY_DATA vAA, +BBBBBBBB - kAnNone, - - // 27 THROW vAA - kAnHeavyWeight | kAnBranch, - - // 28 GOTO - kAnBranch, - - // 29 GOTO_16 - kAnBranch, - - // 2A GOTO_32 - kAnBranch, - - // 2B PACKED_SWITCH vAA, +BBBBBBBB - kAnSwitch, - - // 2C SPARSE_SWITCH vAA, +BBBBBBBB - kAnSwitch, - - // 2D CMPL_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // 2E CMPG_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // 2F CMPL_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // 30 CMPG_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // 31 CMP_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // 32 IF_EQ vA, vB, +CCCC - kAnMath | kAnBranch | kAnInt, - - // 33 IF_NE vA, vB, +CCCC - kAnMath | kAnBranch | kAnInt, - - // 34 IF_LT vA, vB, +CCCC - kAnMath | kAnBranch | kAnInt, - - // 35 IF_GE vA, vB, +CCCC - kAnMath | kAnBranch | kAnInt, - - // 36 IF_GT vA, vB, +CCCC - kAnMath | kAnBranch | kAnInt, - - // 37 IF_LE vA, vB, +CCCC - kAnMath | kAnBranch | kAnInt, - - // 38 IF_EQZ vAA, +BBBB - kAnMath | kAnBranch | kAnInt, - - // 39 IF_NEZ vAA, +BBBB - kAnMath | kAnBranch | kAnInt, - - // 3A IF_LTZ vAA, +BBBB - kAnMath | kAnBranch | kAnInt, - - // 3B IF_GEZ vAA, +BBBB - kAnMath | kAnBranch | kAnInt, - - // 3C IF_GTZ vAA, +BBBB - kAnMath | kAnBranch | kAnInt, - - // 3D IF_LEZ vAA, +BBBB - kAnMath | kAnBranch | kAnInt, - - // 3E UNUSED_3E - kAnNone, - - // 3F UNUSED_3F - kAnNone, - - // 40 UNUSED_40 - kAnNone, - - // 41 UNUSED_41 - kAnNone, - - // 42 UNUSED_42 - kAnNone, - - // 43 UNUSED_43 - kAnNone, - - // 44 AGET vAA, vBB, vCC - kAnArrayOp, - - // 45 AGET_WIDE vAA, vBB, vCC - kAnArrayOp, - - // 46 AGET_OBJECT vAA, vBB, vCC - kAnArrayOp, - - // 47 AGET_BOOLEAN vAA, vBB, vCC - kAnArrayOp, - - // 48 AGET_BYTE vAA, vBB, vCC - kAnArrayOp, - - // 49 AGET_CHAR vAA, vBB, vCC - kAnArrayOp, - - // 4A AGET_SHORT vAA, vBB, vCC - kAnArrayOp, - - // 4B APUT vAA, vBB, vCC - kAnArrayOp, - - // 4C APUT_WIDE vAA, vBB, vCC - kAnArrayOp, - - // 4D APUT_OBJECT vAA, vBB, vCC - kAnArrayOp, - - // 4E APUT_BOOLEAN vAA, vBB, vCC - kAnArrayOp, - - // 4F APUT_BYTE vAA, vBB, vCC - kAnArrayOp, - - // 50 APUT_CHAR vAA, vBB, vCC - kAnArrayOp, - - // 51 APUT_SHORT vAA, vBB, vCC - kAnArrayOp, - - // 52 IGET vA, vB, field@CCCC - kAnNone, - - // 53 IGET_WIDE vA, vB, field@CCCC - kAnNone, - - // 54 IGET_OBJECT vA, vB, field@CCCC - kAnNone, - - // 55 IGET_BOOLEAN vA, vB, field@CCCC - kAnNone, - - // 56 IGET_BYTE vA, vB, field@CCCC - kAnNone, - - // 57 IGET_CHAR vA, vB, field@CCCC - kAnNone, - - // 58 IGET_SHORT vA, vB, field@CCCC - kAnNone, - - // 59 IPUT vA, vB, field@CCCC - kAnNone, - - // 5A IPUT_WIDE vA, vB, field@CCCC - kAnNone, - - // 5B IPUT_OBJECT vA, vB, field@CCCC - kAnNone, - - // 5C IPUT_BOOLEAN vA, vB, field@CCCC - kAnNone, - - // 5D IPUT_BYTE vA, vB, field@CCCC - kAnNone, - - // 5E IPUT_CHAR vA, vB, field@CCCC - kAnNone, - - // 5F IPUT_SHORT vA, vB, field@CCCC - kAnNone, - - // 60 SGET vAA, field@BBBB - kAnNone, - - // 61 SGET_WIDE vAA, field@BBBB - kAnNone, - - // 62 SGET_OBJECT vAA, field@BBBB - kAnNone, - - // 63 SGET_BOOLEAN vAA, field@BBBB - kAnNone, - - // 64 SGET_BYTE vAA, field@BBBB - kAnNone, - - // 65 SGET_CHAR vAA, field@BBBB - kAnNone, - - // 66 SGET_SHORT vAA, field@BBBB - kAnNone, - - // 67 SPUT vAA, field@BBBB - kAnNone, - - // 68 SPUT_WIDE vAA, field@BBBB - kAnNone, - - // 69 SPUT_OBJECT vAA, field@BBBB - kAnNone, - - // 6A SPUT_BOOLEAN vAA, field@BBBB - kAnNone, - - // 6B SPUT_BYTE vAA, field@BBBB - kAnNone, - - // 6C SPUT_CHAR vAA, field@BBBB - kAnNone, - - // 6D SPUT_SHORT vAA, field@BBBB - kAnNone, - - // 6E INVOKE_VIRTUAL {vD, vE, vF, vG, vA} - kAnInvoke | kAnHeavyWeight, - - // 6F INVOKE_SUPER {vD, vE, vF, vG, vA} - kAnInvoke | kAnHeavyWeight, - - // 70 INVOKE_DIRECT {vD, vE, vF, vG, vA} - kAnInvoke | kAnHeavyWeight, - - // 71 INVOKE_STATIC {vD, vE, vF, vG, vA} - kAnInvoke | kAnHeavyWeight, - - // 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA} - kAnInvoke | kAnHeavyWeight, - - // 73 RETURN_VOID_NO_BARRIER - kAnBranch, - - // 74 INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN} - kAnInvoke | kAnHeavyWeight, - - // 75 INVOKE_SUPER_RANGE {vCCCC .. vNNNN} - kAnInvoke | kAnHeavyWeight, - - // 76 INVOKE_DIRECT_RANGE {vCCCC .. vNNNN} - kAnInvoke | kAnHeavyWeight, - - // 77 INVOKE_STATIC_RANGE {vCCCC .. vNNNN} - kAnInvoke | kAnHeavyWeight, - - // 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN} - kAnInvoke | kAnHeavyWeight, - - // 79 UNUSED_79 - kAnNone, - - // 7A UNUSED_7A - kAnNone, - - // 7B NEG_INT vA, vB - kAnMath | kAnInt, - - // 7C NOT_INT vA, vB - kAnMath | kAnInt, - - // 7D NEG_LONG vA, vB - kAnMath | kAnLong, - - // 7E NOT_LONG vA, vB - kAnMath | kAnLong, - - // 7F NEG_FLOAT vA, vB - kAnMath | kAnFp | kAnSingle, - - // 80 NEG_DOUBLE vA, vB - kAnMath | kAnFp | kAnDouble, - - // 81 INT_TO_LONG vA, vB - kAnMath | kAnInt | kAnLong, - - // 82 INT_TO_FLOAT vA, vB - kAnMath | kAnFp | kAnInt | kAnSingle, - - // 83 INT_TO_DOUBLE vA, vB - kAnMath | kAnFp | kAnInt | kAnDouble, - - // 84 LONG_TO_INT vA, vB - kAnMath | kAnInt | kAnLong, - - // 85 LONG_TO_FLOAT vA, vB - kAnMath | kAnFp | kAnLong | kAnSingle, - - // 86 LONG_TO_DOUBLE vA, vB - kAnMath | kAnFp | kAnLong | kAnDouble, - - // 87 FLOAT_TO_INT vA, vB - kAnMath | kAnFp | kAnInt | kAnSingle, - - // 88 FLOAT_TO_LONG vA, vB - kAnMath | kAnFp | kAnLong | kAnSingle, - - // 89 FLOAT_TO_DOUBLE vA, vB - kAnMath | kAnFp | kAnSingle | kAnDouble, - - // 8A DOUBLE_TO_INT vA, vB - kAnMath | kAnFp | kAnInt | kAnDouble, - - // 8B DOUBLE_TO_LONG vA, vB - kAnMath | kAnFp | kAnLong | kAnDouble, - - // 8C DOUBLE_TO_FLOAT vA, vB - kAnMath | kAnFp | kAnSingle | kAnDouble, - - // 8D INT_TO_BYTE vA, vB - kAnMath | kAnInt, - - // 8E INT_TO_CHAR vA, vB - kAnMath | kAnInt, - - // 8F INT_TO_SHORT vA, vB - kAnMath | kAnInt, - - // 90 ADD_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 91 SUB_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 92 MUL_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 93 DIV_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 94 REM_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 95 AND_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 96 OR_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 97 XOR_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 98 SHL_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 99 SHR_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 9A USHR_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 9B ADD_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // 9C SUB_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // 9D MUL_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // 9E DIV_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // 9F REM_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A0 AND_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A1 OR_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A2 XOR_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A3 SHL_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A4 SHR_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A5 USHR_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A6 ADD_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // A7 SUB_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // A8 MUL_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // A9 DIV_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // AA REM_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // AB ADD_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // AC SUB_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // AD MUL_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // AE DIV_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // AF REM_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // B0 ADD_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B1 SUB_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B2 MUL_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B3 DIV_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B4 REM_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B5 AND_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B6 OR_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B7 XOR_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B8 SHL_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B9 SHR_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // BA USHR_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // BB ADD_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // BC SUB_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // BD MUL_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // BE DIV_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // BF REM_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C0 AND_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C1 OR_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C2 XOR_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C3 SHL_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C4 SHR_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C5 USHR_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C6 ADD_FLOAT_2ADDR vA, vB - kAnMath | kAnFp | kAnSingle, - - // C7 SUB_FLOAT_2ADDR vA, vB - kAnMath | kAnFp | kAnSingle, - - // C8 MUL_FLOAT_2ADDR vA, vB - kAnMath | kAnFp | kAnSingle, - - // C9 DIV_FLOAT_2ADDR vA, vB - kAnMath | kAnFp | kAnSingle, - - // CA REM_FLOAT_2ADDR vA, vB - kAnMath | kAnFp | kAnSingle, - - // CB ADD_DOUBLE_2ADDR vA, vB - kAnMath | kAnFp | kAnDouble, - - // CC SUB_DOUBLE_2ADDR vA, vB - kAnMath | kAnFp | kAnDouble, - - // CD MUL_DOUBLE_2ADDR vA, vB - kAnMath | kAnFp | kAnDouble, - - // CE DIV_DOUBLE_2ADDR vA, vB - kAnMath | kAnFp | kAnDouble, - - // CF REM_DOUBLE_2ADDR vA, vB - kAnMath | kAnFp | kAnDouble, - - // D0 ADD_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D1 RSUB_INT vA, vB, #+CCCC - kAnMath | kAnInt, - - // D2 MUL_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D3 DIV_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D4 REM_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D5 AND_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D6 OR_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D7 XOR_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D8 ADD_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // D9 RSUB_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // DA MUL_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // DB DIV_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // DC REM_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // DD AND_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // DE OR_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // DF XOR_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // E0 SHL_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // E1 SHR_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // E2 USHR_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // E3 IGET_QUICK - kAnNone, - - // E4 IGET_WIDE_QUICK - kAnNone, - - // E5 IGET_OBJECT_QUICK - kAnNone, - - // E6 IPUT_QUICK - kAnNone, - - // E7 IPUT_WIDE_QUICK - kAnNone, - - // E8 IPUT_OBJECT_QUICK - kAnNone, - - // E9 INVOKE_VIRTUAL_QUICK - kAnInvoke | kAnHeavyWeight, - - // EA INVOKE_VIRTUAL_RANGE_QUICK - kAnInvoke | kAnHeavyWeight, - - // EB IPUT_BOOLEAN_QUICK - kAnNone, - - // EC IPUT_BYTE_QUICK - kAnNone, - - // ED IPUT_CHAR_QUICK - kAnNone, - - // EE IPUT_SHORT_QUICK - kAnNone, - - // EF IGET_BOOLEAN_QUICK - kAnNone, - - // F0 IGET_BYTE_QUICK - kAnNone, - - // F1 IGET_CHAR_QUICK - kAnNone, - - // F2 IGET_SHORT_QUICK - kAnNone, - - // F3 UNUSED_F3 - kAnNone, - - // F4 UNUSED_F4 - kAnNone, - - // F5 UNUSED_F5 - kAnNone, - - // F6 UNUSED_F6 - kAnNone, - - // F7 UNUSED_F7 - kAnNone, - - // F8 UNUSED_F8 - kAnNone, - - // F9 UNUSED_F9 - kAnNone, - - // FA UNUSED_FA - kAnNone, - - // FB UNUSED_FB - kAnNone, - - // FC UNUSED_FC - kAnNone, - - // FD UNUSED_FD - kAnNone, - - // FE UNUSED_FE - kAnNone, - - // FF UNUSED_FF - kAnNone, - - // Beginning of extended MIR opcodes - // 100 MIR_PHI - kAnNone, - - // 101 MIR_COPY - kAnNone, - - // 102 MIR_FUSED_CMPL_FLOAT - kAnNone, - - // 103 MIR_FUSED_CMPG_FLOAT - kAnNone, - - // 104 MIR_FUSED_CMPL_DOUBLE - kAnNone, - - // 105 MIR_FUSED_CMPG_DOUBLE - kAnNone, - - // 106 MIR_FUSED_CMP_LONG - kAnNone, - - // 107 MIR_NOP - kAnNone, - - // 108 MIR_NULL_CHECK - kAnNone, - - // 109 MIR_RANGE_CHECK - kAnNone, - - // 10A MIR_DIV_ZERO_CHECK - kAnNone, - - // 10B MIR_CHECK - kAnNone, - - // 10C MIR_CHECKPART2 - kAnNone, - - // 10D MIR_SELECT - kAnNone, - - // 10E MirOpConstVector - kAnNone, - - // 10F MirOpMoveVector - kAnNone, - - // 110 MirOpPackedMultiply - kAnNone, - - // 111 MirOpPackedAddition - kAnNone, - - // 112 MirOpPackedSubtract - kAnNone, - - // 113 MirOpPackedShiftLeft - kAnNone, - - // 114 MirOpPackedSignedShiftRight - kAnNone, - - // 115 MirOpPackedUnsignedShiftRight - kAnNone, - - // 116 MirOpPackedAnd - kAnNone, - - // 117 MirOpPackedOr - kAnNone, - - // 118 MirOpPackedXor - kAnNone, - - // 119 MirOpPackedAddReduce - kAnNone, - - // 11A MirOpPackedReduce - kAnNone, - - // 11B MirOpPackedSet - kAnNone, - - // 11C MirOpReserveVectorRegisters - kAnNone, - - // 11D MirOpReturnVectorRegisters - kAnNone, - - // 11E MirOpMemBarrier - kAnNone, - - // 11F MirOpPackedArrayGet - kAnArrayOp, - - // 120 MirOpPackedArrayPut - kAnArrayOp, -}; - -struct MethodStats { - int dex_instructions; - int math_ops; - int fp_ops; - int array_ops; - int branch_ops; - int heavyweight_ops; - bool has_computational_loop; - bool has_switch; - float math_ratio; - float fp_ratio; - float array_ratio; - float branch_ratio; - float heavyweight_ratio; -}; - -void MIRGraph::AnalyzeBlock(BasicBlock* bb, MethodStats* stats) { - if (bb->visited || (bb->block_type != kDalvikByteCode)) { - return; - } - bool computational_block = true; - bool has_math = false; - /* - * For the purposes of this scan, we want to treat the set of basic blocks broken - * by an exception edge as a single basic block. We'll scan forward along the fallthrough - * edges until we reach an explicit branch or return. - */ - BasicBlock* ending_bb = bb; - if (ending_bb->last_mir_insn != nullptr) { - uint32_t ending_flags = kAnalysisAttributes[ending_bb->last_mir_insn->dalvikInsn.opcode]; - while ((ending_flags & kAnBranch) == 0) { - ending_bb = GetBasicBlock(ending_bb->fall_through); - ending_flags = kAnalysisAttributes[ending_bb->last_mir_insn->dalvikInsn.opcode]; - } - } - /* - * Ideally, we'd weight the operations by loop nesting level, but to do so we'd - * first need to do some expensive loop detection - and the point of this is to make - * an informed guess before investing in computation. However, we can cheaply detect - * many simple loop forms without having to do full dataflow analysis. - */ - int loop_scale_factor = 1; - // Simple for and while loops - if ((ending_bb->taken != NullBasicBlockId) && (ending_bb->fall_through == NullBasicBlockId)) { - if ((GetBasicBlock(ending_bb->taken)->taken == bb->id) || - (GetBasicBlock(ending_bb->taken)->fall_through == bb->id)) { - loop_scale_factor = 25; - } - } - // Simple do-while loop - if ((ending_bb->taken != NullBasicBlockId) && (ending_bb->taken == bb->id)) { - loop_scale_factor = 25; - } - - BasicBlock* tbb = bb; - bool done = false; - while (!done) { - tbb->visited = true; - for (MIR* mir = tbb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) { - // Skip any MIR pseudo-op. - continue; - } - uint16_t flags = kAnalysisAttributes[mir->dalvikInsn.opcode]; - stats->dex_instructions += loop_scale_factor; - if ((flags & kAnBranch) == 0) { - computational_block &= ((flags & kAnComputational) != 0); - } else { - stats->branch_ops += loop_scale_factor; - } - if ((flags & kAnMath) != 0) { - stats->math_ops += loop_scale_factor; - has_math = true; - } - if ((flags & kAnFp) != 0) { - stats->fp_ops += loop_scale_factor; - } - if ((flags & kAnArrayOp) != 0) { - stats->array_ops += loop_scale_factor; - } - if ((flags & kAnHeavyWeight) != 0) { - stats->heavyweight_ops += loop_scale_factor; - } - if ((flags & kAnSwitch) != 0) { - stats->has_switch = true; - } - } - if (tbb == ending_bb) { - done = true; - } else { - tbb = GetBasicBlock(tbb->fall_through); - } - } - if (has_math && computational_block && (loop_scale_factor > 1)) { - stats->has_computational_loop = true; - } -} - -bool MIRGraph::ComputeSkipCompilation(MethodStats* stats, bool skip_default, - std::string* skip_message) { - float count = stats->dex_instructions; - stats->math_ratio = stats->math_ops / count; - stats->fp_ratio = stats->fp_ops / count; - stats->branch_ratio = stats->branch_ops / count; - stats->array_ratio = stats->array_ops / count; - stats->heavyweight_ratio = stats->heavyweight_ops / count; - - if (cu_->enable_debug & (1 << kDebugShowFilterStats)) { - LOG(INFO) << "STATS " << stats->dex_instructions << ", math:" - << stats->math_ratio << ", fp:" - << stats->fp_ratio << ", br:" - << stats->branch_ratio << ", hw:" - << stats->heavyweight_ratio << ", arr:" - << stats->array_ratio << ", hot:" - << stats->has_computational_loop << ", " - << PrettyMethod(cu_->method_idx, *cu_->dex_file); - } - - // Computation intensive? - if (stats->has_computational_loop && (stats->heavyweight_ratio < 0.04)) { - return false; - } - - // Complex, logic-intensive? - if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) && - stats->branch_ratio > 0.3) { - return false; - } - - // Significant floating point? - if (stats->fp_ratio > 0.05) { - return false; - } - - // Significant generic math? - if (stats->math_ratio > 0.3) { - return false; - } - - // If array-intensive, compiling is probably worthwhile. - if (stats->array_ratio > 0.1) { - return false; - } - - // Switch operations benefit greatly from compilation, so go ahead and spend the cycles. - if (stats->has_switch) { - return false; - } - - // If significant in size and high proportion of expensive operations, skip. - if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) && - (stats->heavyweight_ratio > 0.3)) { - *skip_message = "Is a small method with heavyweight ratio " + - std::to_string(stats->heavyweight_ratio); - return true; - } - - return skip_default; -} - - /* - * Will eventually want this to be a bit more sophisticated and happen at verification time. - */ -bool MIRGraph::SkipCompilation(std::string* skip_message) { - const CompilerOptions& compiler_options = cu_->compiler_driver->GetCompilerOptions(); - CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter(); - if (compiler_filter == CompilerOptions::kEverything) { - return false; - } - - // Contains a pattern we don't want to compile? - if (PuntToInterpreter()) { - *skip_message = "Punt to interpreter set"; - return true; - } - - DCHECK(compiler_options.IsCompilationEnabled()); - - // Set up compilation cutoffs based on current filter mode. - size_t small_cutoff; - size_t default_cutoff; - switch (compiler_filter) { - case CompilerOptions::kBalanced: - small_cutoff = compiler_options.GetSmallMethodThreshold(); - default_cutoff = compiler_options.GetLargeMethodThreshold(); - break; - case CompilerOptions::kSpace: - small_cutoff = compiler_options.GetTinyMethodThreshold(); - default_cutoff = compiler_options.GetSmallMethodThreshold(); - break; - case CompilerOptions::kSpeed: - case CompilerOptions::kTime: - small_cutoff = compiler_options.GetHugeMethodThreshold(); - default_cutoff = compiler_options.GetHugeMethodThreshold(); - break; - default: - LOG(FATAL) << "Unexpected compiler_filter_: " << compiler_filter; - UNREACHABLE(); - } - - // If size < cutoff, assume we'll compile - but allow removal. - bool skip_compilation = (GetNumDalvikInsns() >= default_cutoff); - if (skip_compilation) { - *skip_message = "#Insns >= default_cutoff: " + std::to_string(GetNumDalvikInsns()); - } - - /* - * Filter 1: Huge methods are likely to be machine generated, but some aren't. - * If huge, assume we won't compile, but allow futher analysis to turn it back on. - */ - if (compiler_options.IsHugeMethod(GetNumDalvikInsns())) { - skip_compilation = true; - *skip_message = "Huge method: " + std::to_string(GetNumDalvikInsns()); - // If we're got a huge number of basic blocks, don't bother with further analysis. - if (static_cast(GetNumBlocks()) > (compiler_options.GetHugeMethodThreshold() / 2)) { - return true; - } - } else if (compiler_options.IsLargeMethod(GetNumDalvikInsns()) && - /* If it's large and contains no branches, it's likely to be machine generated initialization */ - (GetBranchCount() == 0)) { - *skip_message = "Large method with no branches"; - return true; - } else if (compiler_filter == CompilerOptions::kSpeed) { - // If not huge, compile. - return false; - } - - // Filter 2: Skip class initializers. - if (((cu_->access_flags & kAccConstructor) != 0) && ((cu_->access_flags & kAccStatic) != 0)) { - *skip_message = "Class initializer"; - return true; - } - - // Filter 3: if this method is a special pattern, go ahead and emit the canned pattern. - if (cu_->compiler_driver->GetMethodInlinerMap() != nullptr && - cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file) - ->IsSpecial(cu_->method_idx)) { - return false; - } - - // Filter 4: if small, just compile. - if (GetNumDalvikInsns() < small_cutoff) { - return false; - } - - // Analyze graph for: - // o floating point computation - // o basic blocks contained in loop with heavy arithmetic. - // o proportion of conditional branches. - - MethodStats stats; - memset(&stats, 0, sizeof(stats)); - - ClearAllVisitedFlags(); - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - AnalyzeBlock(bb, &stats); - } - - return ComputeSkipCompilation(&stats, skip_compilation, skip_message); -} - -void MIRGraph::DoCacheFieldLoweringInfo() { - static constexpr uint32_t kFieldIndexFlagQuickened = 0x80000000; - // All IGET/IPUT/SGET/SPUT instructions take 2 code units and there must also be a RETURN. - const uint32_t max_refs = (GetNumDalvikInsns() - 1u) / 2u; - ScopedArenaAllocator allocator(&cu_->arena_stack); - auto* field_idxs = allocator.AllocArray(max_refs, kArenaAllocMisc); - DexMemAccessType* field_types = allocator.AllocArray( - max_refs, kArenaAllocMisc); - // Find IGET/IPUT/SGET/SPUT insns, store IGET/IPUT fields at the beginning, SGET/SPUT at the end. - size_t ifield_pos = 0u; - size_t sfield_pos = max_refs; - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - if (bb->block_type != kDalvikByteCode) { - continue; - } - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - // Get field index and try to find it among existing indexes. If found, it's usually among - // the last few added, so we'll start the search from ifield_pos/sfield_pos. Though this - // is a linear search, it actually performs much better than map based approach. - const bool is_iget_or_iput = IsInstructionIGetOrIPut(mir->dalvikInsn.opcode); - const bool is_iget_or_iput_quick = IsInstructionIGetQuickOrIPutQuick(mir->dalvikInsn.opcode); - if (is_iget_or_iput || is_iget_or_iput_quick) { - uint32_t field_idx; - DexMemAccessType access_type; - if (is_iget_or_iput) { - field_idx = mir->dalvikInsn.vC; - access_type = IGetOrIPutMemAccessType(mir->dalvikInsn.opcode); - } else { - DCHECK(is_iget_or_iput_quick); - // Set kFieldIndexFlagQuickened so that we don't deduplicate against non quickened field - // indexes. - field_idx = mir->offset | kFieldIndexFlagQuickened; - access_type = IGetQuickOrIPutQuickMemAccessType(mir->dalvikInsn.opcode); - } - size_t i = ifield_pos; - while (i != 0u && field_idxs[i - 1] != field_idx) { - --i; - } - if (i != 0u) { - mir->meta.ifield_lowering_info = i - 1; - DCHECK_EQ(field_types[i - 1], access_type); - } else { - mir->meta.ifield_lowering_info = ifield_pos; - field_idxs[ifield_pos] = field_idx; - field_types[ifield_pos] = access_type; - ++ifield_pos; - } - } else if (IsInstructionSGetOrSPut(mir->dalvikInsn.opcode)) { - auto field_idx = mir->dalvikInsn.vB; - size_t i = sfield_pos; - while (i != max_refs && field_idxs[i] != field_idx) { - ++i; - } - if (i != max_refs) { - mir->meta.sfield_lowering_info = max_refs - i - 1u; - DCHECK_EQ(field_types[i], SGetOrSPutMemAccessType(mir->dalvikInsn.opcode)); - } else { - mir->meta.sfield_lowering_info = max_refs - sfield_pos; - --sfield_pos; - field_idxs[sfield_pos] = field_idx; - field_types[sfield_pos] = SGetOrSPutMemAccessType(mir->dalvikInsn.opcode); - } - } - DCHECK_LE(ifield_pos, sfield_pos); - } - } - - if (ifield_pos != 0u) { - // Resolve instance field infos. - DCHECK_EQ(ifield_lowering_infos_.size(), 0u); - ifield_lowering_infos_.reserve(ifield_pos); - for (size_t pos = 0u; pos != ifield_pos; ++pos) { - const uint32_t field_idx = field_idxs[pos]; - const bool is_quickened = (field_idx & kFieldIndexFlagQuickened) != 0; - const uint32_t masked_field_idx = field_idx & ~kFieldIndexFlagQuickened; - CHECK_LT(masked_field_idx, 1u << 16); - ifield_lowering_infos_.push_back( - MirIFieldLoweringInfo(masked_field_idx, field_types[pos], is_quickened)); - } - ScopedObjectAccess soa(Thread::Current()); - MirIFieldLoweringInfo::Resolve(soa, - cu_->compiler_driver, - GetCurrentDexCompilationUnit(), - ifield_lowering_infos_.data(), - ifield_pos); - } - - if (sfield_pos != max_refs) { - // Resolve static field infos. - DCHECK_EQ(sfield_lowering_infos_.size(), 0u); - sfield_lowering_infos_.reserve(max_refs - sfield_pos); - for (size_t pos = max_refs; pos != sfield_pos;) { - --pos; - sfield_lowering_infos_.push_back(MirSFieldLoweringInfo(field_idxs[pos], field_types[pos])); - } - MirSFieldLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(), - sfield_lowering_infos_.data(), max_refs - sfield_pos); - } -} - -void MIRGraph::DoCacheMethodLoweringInfo() { - static constexpr uint16_t invoke_types[] = { kVirtual, kSuper, kDirect, kStatic, kInterface }; - static constexpr uint32_t kMethodIdxFlagQuickened = 0x80000000; - - // Embed the map value in the entry to avoid extra padding in 64-bit builds. - struct MapEntry { - // Map key: target_method_idx, invoke_type, devirt_target. Ordered to avoid padding. - const MethodReference* devirt_target; - uint32_t target_method_idx; - uint32_t vtable_idx; - uint16_t invoke_type; - // Map value. - uint32_t lowering_info_index; - }; - - struct MapEntryComparator { - bool operator()(const MapEntry& lhs, const MapEntry& rhs) const { - if (lhs.target_method_idx != rhs.target_method_idx) { - return lhs.target_method_idx < rhs.target_method_idx; - } - if (lhs.invoke_type != rhs.invoke_type) { - return lhs.invoke_type < rhs.invoke_type; - } - if (lhs.vtable_idx != rhs.vtable_idx) { - return lhs.vtable_idx < rhs.vtable_idx; - } - if (lhs.devirt_target != rhs.devirt_target) { - if (lhs.devirt_target == nullptr) { - return true; - } - if (rhs.devirt_target == nullptr) { - return false; - } - return devirt_cmp(*lhs.devirt_target, *rhs.devirt_target); - } - return false; - } - MethodReferenceComparator devirt_cmp; - }; - - ScopedArenaAllocator allocator(&cu_->arena_stack); - - // All INVOKE instructions take 3 code units and there must also be a RETURN. - const uint32_t max_refs = (GetNumDalvikInsns() - 1u) / 3u; - - // Map invoke key (see MapEntry) to lowering info index and vice versa. - // The invoke_map and sequential entries are essentially equivalent to Boost.MultiIndex's - // multi_index_container with one ordered index and one sequential index. - ScopedArenaSet invoke_map(MapEntryComparator(), - allocator.Adapter()); - const MapEntry** sequential_entries = - allocator.AllocArray(max_refs, kArenaAllocMisc); - - // Find INVOKE insns and their devirtualization targets. - const VerifiedMethod* verified_method = GetCurrentDexCompilationUnit()->GetVerifiedMethod(); - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - if (bb->block_type != kDalvikByteCode) { - continue; - } - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - const bool is_quick_invoke = IsInstructionQuickInvoke(mir->dalvikInsn.opcode); - const bool is_invoke = IsInstructionInvoke(mir->dalvikInsn.opcode); - if (is_quick_invoke || is_invoke) { - uint32_t vtable_index = 0; - uint32_t target_method_idx = 0; - uint32_t invoke_type_idx = 0; // Default to virtual (in case of quickened). - DCHECK_EQ(invoke_types[invoke_type_idx], kVirtual); - if (is_quick_invoke) { - // We need to store the vtable index since we can't necessarily recreate it at resolve - // phase if the dequickening resolved to an interface method. - vtable_index = mir->dalvikInsn.vB; - // Fake up the method index by storing the mir offset so that we can read the dequicken - // info in resolve. - target_method_idx = mir->offset | kMethodIdxFlagQuickened; - } else { - DCHECK(is_invoke); - // Decode target method index and invoke type. - invoke_type_idx = InvokeInstructionType(mir->dalvikInsn.opcode); - target_method_idx = mir->dalvikInsn.vB; - } - // Find devirtualization target. - // TODO: The devirt map is ordered by the dex pc here. Is there a way to get INVOKEs - // ordered by dex pc as well? That would allow us to keep an iterator to devirt targets - // and increment it as needed instead of making O(log n) lookups. - const MethodReference* devirt_target = verified_method->GetDevirtTarget(mir->offset); - // Try to insert a new entry. If the insertion fails, we will have found an old one. - MapEntry entry = { - devirt_target, - target_method_idx, - vtable_index, - invoke_types[invoke_type_idx], - static_cast(invoke_map.size()) - }; - auto it = invoke_map.insert(entry).first; // Iterator to either the old or the new entry. - mir->meta.method_lowering_info = it->lowering_info_index; - // If we didn't actually insert, this will just overwrite an existing value with the same. - sequential_entries[it->lowering_info_index] = &*it; - } - } - } - if (invoke_map.empty()) { - return; - } - // Prepare unique method infos, set method info indexes for their MIRs. - const size_t count = invoke_map.size(); - method_lowering_infos_.reserve(count); - for (size_t pos = 0u; pos != count; ++pos) { - const MapEntry* entry = sequential_entries[pos]; - const bool is_quick = (entry->target_method_idx & kMethodIdxFlagQuickened) != 0; - const uint32_t masked_method_idx = entry->target_method_idx & ~kMethodIdxFlagQuickened; - MirMethodLoweringInfo method_info(masked_method_idx, - static_cast(entry->invoke_type), is_quick); - if (entry->devirt_target != nullptr) { - method_info.SetDevirtualizationTarget(*entry->devirt_target); - } - if (is_quick) { - method_info.SetVTableIndex(entry->vtable_idx); - } - method_lowering_infos_.push_back(method_info); - } - MirMethodLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(), - method_lowering_infos_.data(), count); -} - -} // namespace art diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc deleted file mode 100644 index f1cc5fc4d2e2a5d75ebf0af4f3a5b415e4daf969..0000000000000000000000000000000000000000 --- a/compiler/dex/mir_dataflow.cc +++ /dev/null @@ -1,1453 +0,0 @@ -/* - * Copyright (C) 2011 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 "local_value_numbering.h" -#include "dataflow_iterator-inl.h" - -namespace art { - -/* - * Main table containing data flow attributes for each bytecode. The - * first kNumPackedOpcodes entries are for Dalvik bytecode - * instructions, where extended opcode at the MIR level are appended - * afterwards. - * - * TODO - many optimization flags are incomplete - they will only limit the - * scope of optimizations but will not cause mis-optimizations. - */ -const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { - // 00 NOP - DF_NOP, - - // 01 MOVE vA, vB - DF_DA | DF_UB | DF_IS_MOVE, - - // 02 MOVE_FROM16 vAA, vBBBB - DF_DA | DF_UB | DF_IS_MOVE, - - // 03 MOVE_16 vAAAA, vBBBB - DF_DA | DF_UB | DF_IS_MOVE, - - // 04 MOVE_WIDE vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE, - - // 05 MOVE_WIDE_FROM16 vAA, vBBBB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE, - - // 06 MOVE_WIDE_16 vAAAA, vBBBB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE, - - // 07 MOVE_OBJECT vA, vB - DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B, - - // 08 MOVE_OBJECT_FROM16 vAA, vBBBB - DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B, - - // 09 MOVE_OBJECT_16 vAAAA, vBBBB - DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B, - - // 0A MOVE_RESULT vAA - DF_DA, - - // 0B MOVE_RESULT_WIDE vAA - DF_DA | DF_A_WIDE, - - // 0C MOVE_RESULT_OBJECT vAA - DF_DA | DF_REF_A, - - // 0D MOVE_EXCEPTION vAA - DF_DA | DF_REF_A | DF_NON_NULL_DST, - - // 0E RETURN_VOID - DF_NOP, - - // 0F RETURN vAA - DF_UA, - - // 10 RETURN_WIDE vAA - DF_UA | DF_A_WIDE, - - // 11 RETURN_OBJECT vAA - DF_UA | DF_REF_A, - - // 12 CONST_4 vA, #+B - DF_DA | DF_SETS_CONST, - - // 13 CONST_16 vAA, #+BBBB - DF_DA | DF_SETS_CONST, - - // 14 CONST vAA, #+BBBBBBBB - DF_DA | DF_SETS_CONST, - - // 15 CONST_HIGH16 VAA, #+BBBB0000 - DF_DA | DF_SETS_CONST, - - // 16 CONST_WIDE_16 vAA, #+BBBB - DF_DA | DF_A_WIDE | DF_SETS_CONST, - - // 17 CONST_WIDE_32 vAA, #+BBBBBBBB - DF_DA | DF_A_WIDE | DF_SETS_CONST, - - // 18 CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB - DF_DA | DF_A_WIDE | DF_SETS_CONST, - - // 19 CONST_WIDE_HIGH16 vAA, #+BBBB000000000000 - DF_DA | DF_A_WIDE | DF_SETS_CONST, - - // 1A CONST_STRING vAA, string@BBBB - DF_DA | DF_REF_A | DF_NON_NULL_DST, - - // 1B CONST_STRING_JUMBO vAA, string@BBBBBBBB - DF_DA | DF_REF_A | DF_NON_NULL_DST, - - // 1C CONST_CLASS vAA, type@BBBB - DF_DA | DF_REF_A | DF_NON_NULL_DST, - - // 1D MONITOR_ENTER vAA - DF_UA | DF_NULL_CHK_A | DF_REF_A, - - // 1E MONITOR_EXIT vAA - DF_UA | DF_NULL_CHK_A | DF_REF_A, - - // 1F CHK_CAST vAA, type@BBBB - DF_UA | DF_REF_A | DF_CHK_CAST | DF_UMS, - - // 20 INSTANCE_OF vA, vB, type@CCCC - DF_DA | DF_UB | DF_CORE_A | DF_REF_B | DF_UMS, - - // 21 ARRAY_LENGTH vA, vB - DF_DA | DF_UB | DF_NULL_CHK_B | DF_CORE_A | DF_REF_B, - - // 22 NEW_INSTANCE vAA, type@BBBB - DF_DA | DF_NON_NULL_DST | DF_REF_A | DF_UMS, - - // 23 NEW_ARRAY vA, vB, type@CCCC - DF_DA | DF_UB | DF_NON_NULL_DST | DF_REF_A | DF_CORE_B | DF_UMS, - - // 24 FILLED_NEW_ARRAY {vD, vE, vF, vG, vA} - DF_FORMAT_35C | DF_NON_NULL_RET | DF_UMS, - - // 25 FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB - DF_FORMAT_3RC | DF_NON_NULL_RET | DF_UMS, - - // 26 FILL_ARRAY_DATA vAA, +BBBBBBBB - DF_UA | DF_REF_A | DF_UMS, - - // 27 THROW vAA - DF_UA | DF_REF_A | DF_UMS, - - // 28 GOTO - DF_NOP, - - // 29 GOTO_16 - DF_NOP, - - // 2A GOTO_32 - DF_NOP, - - // 2B PACKED_SWITCH vAA, +BBBBBBBB - DF_UA | DF_CORE_A, - - // 2C SPARSE_SWITCH vAA, +BBBBBBBB - DF_UA | DF_CORE_A, - - // 2D CMPL_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C | DF_CORE_A, - - // 2E CMPG_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C | DF_CORE_A, - - // 2F CMPL_DOUBLE vAA, vBB, vCC - DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_B | DF_FP_C | DF_CORE_A, - - // 30 CMPG_DOUBLE vAA, vBB, vCC - DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_B | DF_FP_C | DF_CORE_A, - - // 31 CMP_LONG vAA, vBB, vCC - DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 32 IF_EQ vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, - - // 33 IF_NE vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, - - // 34 IF_LT vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, - - // 35 IF_GE vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, - - // 36 IF_GT vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, - - // 37 IF_LE vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, - - // 38 IF_EQZ vAA, +BBBB - DF_UA, - - // 39 IF_NEZ vAA, +BBBB - DF_UA, - - // 3A IF_LTZ vAA, +BBBB - DF_UA, - - // 3B IF_GEZ vAA, +BBBB - DF_UA, - - // 3C IF_GTZ vAA, +BBBB - DF_UA, - - // 3D IF_LEZ vAA, +BBBB - DF_UA, - - // 3E UNUSED_3E - DF_NOP, - - // 3F UNUSED_3F - DF_NOP, - - // 40 UNUSED_40 - DF_NOP, - - // 41 UNUSED_41 - DF_NOP, - - // 42 UNUSED_42 - DF_NOP, - - // 43 UNUSED_43 - DF_NOP, - - // 44 AGET vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 45 AGET_WIDE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 46 AGET_OBJECT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN, - - // 47 AGET_BOOLEAN vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 48 AGET_BYTE vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 49 AGET_CHAR vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 4A AGET_SHORT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 4B APUT vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 4C APUT_WIDE vAA, vBB, vCC - DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 4D APUT_OBJECT vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN, - - // 4E APUT_BOOLEAN vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 4F APUT_BYTE vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 50 APUT_CHAR vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 51 APUT_SHORT vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 52 IGET vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 53 IGET_WIDE vA, vB, field@CCCC - DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 54 IGET_OBJECT vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, - - // 55 IGET_BOOLEAN vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 56 IGET_BYTE vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 57 IGET_CHAR vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 58 IGET_SHORT vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 59 IPUT vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 5A IPUT_WIDE vA, vB, field@CCCC - DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 5B IPUT_OBJECT vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, - - // 5C IPUT_BOOLEAN vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 5D IPUT_BYTE vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 5E IPUT_CHAR vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 5F IPUT_SHORT vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 60 SGET vAA, field@BBBB - DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 61 SGET_WIDE vAA, field@BBBB - DF_DA | DF_A_WIDE | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 62 SGET_OBJECT vAA, field@BBBB - DF_DA | DF_REF_A | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 63 SGET_BOOLEAN vAA, field@BBBB - DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 64 SGET_BYTE vAA, field@BBBB - DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 65 SGET_CHAR vAA, field@BBBB - DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 66 SGET_SHORT vAA, field@BBBB - DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 67 SPUT vAA, field@BBBB - DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 68 SPUT_WIDE vAA, field@BBBB - DF_UA | DF_A_WIDE | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 69 SPUT_OBJECT vAA, field@BBBB - DF_UA | DF_REF_A | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 6A SPUT_BOOLEAN vAA, field@BBBB - DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 6B SPUT_BYTE vAA, field@BBBB - DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 6C SPUT_CHAR vAA, field@BBBB - DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 6D SPUT_SHORT vAA, field@BBBB - DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 6E INVOKE_VIRTUAL {vD, vE, vF, vG, vA} - DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS, - - // 6F INVOKE_SUPER {vD, vE, vF, vG, vA} - DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS, - - // 70 INVOKE_DIRECT {vD, vE, vF, vG, vA} - DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS, - - // 71 INVOKE_STATIC {vD, vE, vF, vG, vA} - DF_FORMAT_35C | DF_CLINIT | DF_UMS, - - // 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA} - DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS, - - // 73 RETURN_VOID_NO_BARRIER - DF_NOP, - - // 74 INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN} - DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS, - - // 75 INVOKE_SUPER_RANGE {vCCCC .. vNNNN} - DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS, - - // 76 INVOKE_DIRECT_RANGE {vCCCC .. vNNNN} - DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS, - - // 77 INVOKE_STATIC_RANGE {vCCCC .. vNNNN} - DF_FORMAT_3RC | DF_CLINIT | DF_UMS, - - // 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN} - DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS, - - // 79 UNUSED_79 - DF_NOP, - - // 7A UNUSED_7A - DF_NOP, - - // 7B NEG_INT vA, vB - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // 7C NOT_INT vA, vB - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // 7D NEG_LONG vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // 7E NOT_LONG vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // 7F NEG_FLOAT vA, vB - DF_DA | DF_UB | DF_FP_A | DF_FP_B, - - // 80 NEG_DOUBLE vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // 81 INT_TO_LONG vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_CORE_A | DF_CORE_B, - - // 82 INT_TO_FLOAT vA, vB - DF_DA | DF_UB | DF_FP_A | DF_CORE_B, - - // 83 INT_TO_DOUBLE vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_FP_A | DF_CORE_B, - - // 84 LONG_TO_INT vA, vB - DF_DA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // 85 LONG_TO_FLOAT vA, vB - DF_DA | DF_UB | DF_B_WIDE | DF_FP_A | DF_CORE_B, - - // 86 LONG_TO_DOUBLE vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_CORE_B, - - // 87 FLOAT_TO_INT vA, vB - DF_DA | DF_UB | DF_FP_B | DF_CORE_A, - - // 88 FLOAT_TO_LONG vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_FP_B | DF_CORE_A, - - // 89 FLOAT_TO_DOUBLE vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_FP_A | DF_FP_B, - - // 8A DOUBLE_TO_INT vA, vB - DF_DA | DF_UB | DF_B_WIDE | DF_FP_B | DF_CORE_A, - - // 8B DOUBLE_TO_LONG vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_B | DF_CORE_A, - - // 8C DOUBLE_TO_FLOAT vA, vB - DF_DA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // 8D INT_TO_BYTE vA, vB - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // 8E INT_TO_CHAR vA, vB - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // 8F INT_TO_SHORT vA, vB - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // 90 ADD_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 91 SUB_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 92 MUL_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 93 DIV_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 94 REM_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 95 AND_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 96 OR_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 97 XOR_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 98 SHL_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 99 SHR_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 9A USHR_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 9B ADD_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 9C SUB_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 9D MUL_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 9E DIV_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 9F REM_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A0 AND_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A1 OR_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A2 XOR_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A3 SHL_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A4 SHR_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A5 USHR_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A6 ADD_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, - - // A7 SUB_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, - - // A8 MUL_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, - - // A9 DIV_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, - - // AA REM_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, - - // AB ADD_DOUBLE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, - - // AC SUB_DOUBLE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, - - // AD MUL_DOUBLE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, - - // AE DIV_DOUBLE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, - - // AF REM_DOUBLE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, - - // B0 ADD_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B1 SUB_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B2 MUL_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B3 DIV_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B4 REM_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B5 AND_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B6 OR_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B7 XOR_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B8 SHL_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B9 SHR_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // BA USHR_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // BB ADD_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // BC SUB_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // BD MUL_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // BE DIV_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // BF REM_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // C0 AND_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // C1 OR_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // C2 XOR_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // C3 SHL_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // C4 SHR_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // C5 USHR_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // C6 ADD_FLOAT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // C7 SUB_FLOAT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // C8 MUL_FLOAT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // C9 DIV_FLOAT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // CA REM_FLOAT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // CB ADD_DOUBLE_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // CC SUB_DOUBLE_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // CD MUL_DOUBLE_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // CE DIV_DOUBLE_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // CF REM_DOUBLE_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // D0 ADD_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D1 RSUB_INT vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D2 MUL_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D3 DIV_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D4 REM_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D5 AND_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D6 OR_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D7 XOR_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D8 ADD_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D9 RSUB_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // DA MUL_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // DB DIV_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // DC REM_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // DD AND_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // DE OR_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // DF XOR_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // E0 SHL_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // E1 SHR_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // E2 USHR_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // E3 IGET_QUICK - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // E4 IGET_WIDE_QUICK - DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // E5 IGET_OBJECT_QUICK - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, - - // E6 IPUT_QUICK - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // E7 IPUT_WIDE_QUICK - DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // E8 IPUT_OBJECT_QUICK - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, - - // E9 INVOKE_VIRTUAL_QUICK - DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS, - - // EA INVOKE_VIRTUAL_RANGE_QUICK - DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS, - - // EB IPUT_BOOLEAN_QUICK vA, vB, index - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // EC IPUT_BYTE_QUICK vA, vB, index - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // ED IPUT_CHAR_QUICK vA, vB, index - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // EE IPUT_SHORT_QUICK vA, vB, index - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // EF IGET_BOOLEAN_QUICK vA, vB, index - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // F0 IGET_BYTE_QUICK vA, vB, index - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // F1 IGET_CHAR_QUICK vA, vB, index - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // F2 IGET_SHORT_QUICK vA, vB, index - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // F3 UNUSED_F3 - DF_NOP, - - // F4 UNUSED_F4 - DF_NOP, - - // F5 UNUSED_F5 - DF_NOP, - - // F6 UNUSED_F6 - DF_NOP, - - // F7 UNUSED_F7 - DF_NOP, - - // F8 UNUSED_F8 - DF_NOP, - - // F9 UNUSED_F9 - DF_NOP, - - // FA UNUSED_FA - DF_NOP, - - // FB UNUSED_FB - DF_NOP, - - // FC UNUSED_FC - DF_NOP, - - // FD UNUSED_FD - DF_NOP, - - // FE UNUSED_FE - DF_NOP, - - // FF UNUSED_FF - DF_NOP, - - // Beginning of extended MIR opcodes - // 100 MIR_PHI - DF_DA | DF_NULL_TRANSFER_N, - - // 101 MIR_COPY - DF_DA | DF_UB | DF_IS_MOVE, - - // 102 MIR_FUSED_CMPL_FLOAT - DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // 103 MIR_FUSED_CMPG_FLOAT - DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // 104 MIR_FUSED_CMPL_DOUBLE - DF_UA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // 105 MIR_FUSED_CMPG_DOUBLE - DF_UA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // 106 MIR_FUSED_CMP_LONG - DF_UA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // 107 MIR_NOP - DF_NOP, - - // 108 MIR_NULL_CHECK - DF_UA | DF_REF_A | DF_NULL_CHK_A | DF_LVN, - - // 109 MIR_RANGE_CHECK - 0, - - // 10A MIR_DIV_ZERO_CHECK - 0, - - // 10B MIR_CHECK - 0, - - // 10D MIR_SELECT - DF_DA | DF_UB, - - // 10E MirOpConstVector - 0, - - // 10F MirOpMoveVector - 0, - - // 110 MirOpPackedMultiply - 0, - - // 111 MirOpPackedAddition - 0, - - // 112 MirOpPackedSubtract - 0, - - // 113 MirOpPackedShiftLeft - 0, - - // 114 MirOpPackedSignedShiftRight - 0, - - // 115 MirOpPackedUnsignedShiftRight - 0, - - // 116 MirOpPackedAnd - 0, - - // 117 MirOpPackedOr - 0, - - // 118 MirOpPackedXor - 0, - - // 119 MirOpPackedAddReduce - DF_FORMAT_EXTENDED, - - // 11A MirOpPackedReduce - DF_FORMAT_EXTENDED, - - // 11B MirOpPackedSet - DF_FORMAT_EXTENDED, - - // 11C MirOpReserveVectorRegisters - 0, - - // 11D MirOpReturnVectorRegisters - 0, - - // 11E MirOpMemBarrier - 0, - - // 11F MirOpPackedArrayGet - DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 120 MirOpPackedArrayPut - DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 121 MirOpMaddInt - DF_FORMAT_EXTENDED, - - // 122 MirOpMsubInt - DF_FORMAT_EXTENDED, - - // 123 MirOpMaddLong - DF_FORMAT_EXTENDED, - - // 124 MirOpMsubLong - DF_FORMAT_EXTENDED, -}; - -/* Any register that is used before being defined is considered live-in */ -void MIRGraph::HandleLiveInUse(ArenaBitVector* use_v, ArenaBitVector* def_v, - ArenaBitVector* live_in_v, int dalvik_reg_id) { - use_v->SetBit(dalvik_reg_id); - if (!def_v->IsBitSet(dalvik_reg_id)) { - live_in_v->SetBit(dalvik_reg_id); - } -} - -/* Mark a reg as being defined */ -void MIRGraph::HandleDef(ArenaBitVector* def_v, int dalvik_reg_id) { - def_v->SetBit(dalvik_reg_id); -} - -void MIRGraph::HandleExtended(ArenaBitVector* use_v, ArenaBitVector* def_v, - ArenaBitVector* live_in_v, - const MIR::DecodedInstruction& d_insn) { - // For vector MIRs, vC contains type information - bool is_vector_type_wide = false; - int type_size = d_insn.vC >> 16; - if (type_size == k64 || type_size == kDouble) { - is_vector_type_wide = true; - } - - switch (static_cast(d_insn.opcode)) { - case kMirOpPackedAddReduce: - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vA); - if (is_vector_type_wide == true) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vA + 1); - } - HandleDef(def_v, d_insn.vA); - if (is_vector_type_wide == true) { - HandleDef(def_v, d_insn.vA + 1); - } - break; - case kMirOpPackedReduce: - HandleDef(def_v, d_insn.vA); - if (is_vector_type_wide == true) { - HandleDef(def_v, d_insn.vA + 1); - } - break; - case kMirOpPackedSet: - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB); - if (is_vector_type_wide == true) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB + 1); - } - break; - case kMirOpMaddInt: - case kMirOpMsubInt: - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vC); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.arg[0]); - HandleDef(def_v, d_insn.vA); - break; - case kMirOpMaddLong: - case kMirOpMsubLong: - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB + 1); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vC); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vC + 1); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.arg[0]); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.arg[0] + 1); - HandleDef(def_v, d_insn.vA); - HandleDef(def_v, d_insn.vA + 1); - break; - default: - LOG(ERROR) << "Unexpected Extended Opcode " << d_insn.opcode; - break; - } -} - -/* - * Find out live-in variables for natural loops. Variables that are live-in in - * the main loop body are considered to be defined in the entry block. - */ -bool MIRGraph::FindLocalLiveIn(BasicBlock* bb) { - MIR* mir; - ArenaBitVector *use_v, *def_v, *live_in_v; - - if (bb->data_flow_info == nullptr) return false; - - use_v = bb->data_flow_info->use_v = - new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false); - def_v = bb->data_flow_info->def_v = - new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false); - live_in_v = bb->data_flow_info->live_in_v = - new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false); - - for (mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - uint64_t df_attributes = GetDataFlowAttributes(mir); - MIR::DecodedInstruction* d_insn = &mir->dalvikInsn; - - if (df_attributes & DF_HAS_USES) { - if (df_attributes & DF_UA) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vA); - if (df_attributes & DF_A_WIDE) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vA+1); - } - } - if (df_attributes & DF_UB) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vB); - if (df_attributes & DF_B_WIDE) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vB+1); - } - } - if (df_attributes & DF_UC) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vC); - if (df_attributes & DF_C_WIDE) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vC+1); - } - } - } - if (df_attributes & DF_FORMAT_35C) { - for (unsigned int i = 0; i < d_insn->vA; i++) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->arg[i]); - } - } - if (df_attributes & DF_FORMAT_3RC) { - for (unsigned int i = 0; i < d_insn->vA; i++) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vC+i); - } - } - if (df_attributes & DF_HAS_DEFS) { - HandleDef(def_v, d_insn->vA); - if (df_attributes & DF_A_WIDE) { - HandleDef(def_v, d_insn->vA+1); - } - } - if (df_attributes & DF_FORMAT_EXTENDED) { - HandleExtended(use_v, def_v, live_in_v, mir->dalvikInsn); - } - } - return true; -} - -int MIRGraph::AddNewSReg(int v_reg) { - int subscript = ++ssa_last_defs_[v_reg]; - uint32_t ssa_reg = GetNumSSARegs(); - SetNumSSARegs(ssa_reg + 1); - ssa_base_vregs_.push_back(v_reg); - ssa_subscripts_.push_back(subscript); - DCHECK_EQ(ssa_base_vregs_.size(), ssa_subscripts_.size()); - // If we are expanding very late, update use counts too. - if (ssa_reg > 0 && use_counts_.size() == ssa_reg) { - // Need to expand the counts. - use_counts_.push_back(0); - raw_use_counts_.push_back(0); - } - return ssa_reg; -} - -/* Find out the latest SSA register for a given Dalvik register */ -void MIRGraph::HandleSSAUse(int* uses, int dalvik_reg, int reg_index) { - DCHECK((dalvik_reg >= 0) && (dalvik_reg < static_cast(GetNumOfCodeAndTempVRs()))); - uses[reg_index] = vreg_to_ssa_map_[dalvik_reg]; -} - -/* Setup a new SSA register for a given Dalvik register */ -void MIRGraph::HandleSSADef(int* defs, int dalvik_reg, int reg_index) { - DCHECK((dalvik_reg >= 0) && (dalvik_reg < static_cast(GetNumOfCodeAndTempVRs()))); - int ssa_reg = AddNewSReg(dalvik_reg); - vreg_to_ssa_map_[dalvik_reg] = ssa_reg; - defs[reg_index] = ssa_reg; -} - -void MIRGraph::AllocateSSAUseData(MIR *mir, int num_uses) { - mir->ssa_rep->num_uses = num_uses; - - if (mir->ssa_rep->num_uses_allocated < num_uses) { - mir->ssa_rep->uses = arena_->AllocArray(num_uses, kArenaAllocDFInfo); - } -} - -void MIRGraph::AllocateSSADefData(MIR *mir, int num_defs) { - mir->ssa_rep->num_defs = num_defs; - - if (mir->ssa_rep->num_defs_allocated < num_defs) { - mir->ssa_rep->defs = arena_->AllocArray(num_defs, kArenaAllocDFInfo); - } -} - -/* Look up new SSA names for format_35c instructions */ -void MIRGraph::DataFlowSSAFormat35C(MIR* mir) { - MIR::DecodedInstruction* d_insn = &mir->dalvikInsn; - int num_uses = d_insn->vA; - int i; - - AllocateSSAUseData(mir, num_uses); - - for (i = 0; i < num_uses; i++) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->arg[i], i); - } -} - -/* Look up new SSA names for format_3rc instructions */ -void MIRGraph::DataFlowSSAFormat3RC(MIR* mir) { - MIR::DecodedInstruction* d_insn = &mir->dalvikInsn; - int num_uses = d_insn->vA; - int i; - - AllocateSSAUseData(mir, num_uses); - - for (i = 0; i < num_uses; i++) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vC+i, i); - } -} - -void MIRGraph::DataFlowSSAFormatExtended(MIR* mir) { - const MIR::DecodedInstruction& d_insn = mir->dalvikInsn; - // For vector MIRs, vC contains type information - bool is_vector_type_wide = false; - int type_size = d_insn.vC >> 16; - if (type_size == k64 || type_size == kDouble) { - is_vector_type_wide = true; - } - - switch (static_cast(mir->dalvikInsn.opcode)) { - case kMirOpPackedAddReduce: - // We have one use, plus one more for wide - AllocateSSAUseData(mir, is_vector_type_wide ? 2 : 1); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vA, 0); - if (is_vector_type_wide == true) { - HandleSSAUse(mir->ssa_rep->uses, d_insn.vA + 1, 1); - } - - // We have a def, plus one more for wide - AllocateSSADefData(mir, is_vector_type_wide ? 2 : 1); - HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0); - if (is_vector_type_wide == true) { - HandleSSADef(mir->ssa_rep->defs, d_insn.vA + 1, 1); - } - break; - case kMirOpPackedReduce: - // We have a def, plus one more for wide - AllocateSSADefData(mir, is_vector_type_wide ? 2 : 1); - HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0); - if (is_vector_type_wide == true) { - HandleSSADef(mir->ssa_rep->defs, d_insn.vA + 1, 1); - } - break; - case kMirOpPackedSet: - // We have one use, plus one more for wide - AllocateSSAUseData(mir, is_vector_type_wide ? 2 : 1); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vB, 0); - if (is_vector_type_wide == true) { - HandleSSAUse(mir->ssa_rep->uses, d_insn.vB + 1, 1); - } - break; - case kMirOpMaddInt: - case kMirOpMsubInt: - AllocateSSAUseData(mir, 3); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vB, 0); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vC, 1); - HandleSSAUse(mir->ssa_rep->uses, d_insn.arg[0], 2); - AllocateSSADefData(mir, 1); - HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0); - break; - case kMirOpMaddLong: - case kMirOpMsubLong: - AllocateSSAUseData(mir, 6); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vB, 0); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vB + 1, 1); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vC, 2); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vC + 1, 3); - HandleSSAUse(mir->ssa_rep->uses, d_insn.arg[0], 4); - HandleSSAUse(mir->ssa_rep->uses, d_insn.arg[0] + 1, 5); - AllocateSSADefData(mir, 2); - HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0); - HandleSSADef(mir->ssa_rep->defs, d_insn.vA + 1, 1); - break; - default: - LOG(ERROR) << "Missing case for extended MIR: " << mir->dalvikInsn.opcode; - break; - } -} - -/* Entry function to convert a block into SSA representation */ -bool MIRGraph::DoSSAConversion(BasicBlock* bb) { - if (bb->data_flow_info == nullptr) return false; - - /* - * Pruned SSA form: Insert phi nodes for each dalvik register marked in phi_node_blocks - * only if the dalvik register is in the live-in set. - */ - BasicBlockId bb_id = bb->id; - for (int dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) { - if (temp_.ssa.phi_node_blocks[dalvik_reg]->IsBitSet(bb_id)) { - if (!bb->data_flow_info->live_in_v->IsBitSet(dalvik_reg)) { - /* Variable will be clobbered before being used - no need for phi */ - vreg_to_ssa_map_[dalvik_reg] = INVALID_SREG; - continue; - } - MIR *phi = NewMIR(); - phi->dalvikInsn.opcode = static_cast(kMirOpPhi); - phi->dalvikInsn.vA = dalvik_reg; - phi->offset = bb->start_offset; - phi->m_unit_index = 0; // Arbitrarily assign all Phi nodes to outermost method. - bb->PrependMIR(phi); - } - } - - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - mir->ssa_rep = - static_cast(arena_->Alloc(sizeof(SSARepresentation), - kArenaAllocDFInfo)); - memset(mir->ssa_rep, 0, sizeof(*mir->ssa_rep)); - - uint64_t df_attributes = GetDataFlowAttributes(mir); - - // If not a pseudo-op, note non-leaf or can throw - if (!MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) { - int flags = mir->dalvikInsn.FlagsOf(); - - if ((flags & Instruction::kInvoke) != 0) { - attributes_ &= ~METHOD_IS_LEAF; - } - } - - int num_uses = 0; - - if (df_attributes & DF_FORMAT_35C) { - DataFlowSSAFormat35C(mir); - continue; - } - - if (df_attributes & DF_FORMAT_3RC) { - DataFlowSSAFormat3RC(mir); - continue; - } - - if (df_attributes & DF_FORMAT_EXTENDED) { - DataFlowSSAFormatExtended(mir); - continue; - } - - if (df_attributes & DF_HAS_USES) { - if (df_attributes & DF_UA) { - num_uses++; - if (df_attributes & DF_A_WIDE) { - num_uses++; - } - } - if (df_attributes & DF_UB) { - num_uses++; - if (df_attributes & DF_B_WIDE) { - num_uses++; - } - } - if (df_attributes & DF_UC) { - num_uses++; - if (df_attributes & DF_C_WIDE) { - num_uses++; - } - } - } - - AllocateSSAUseData(mir, num_uses); - - int num_defs = 0; - - if (df_attributes & DF_HAS_DEFS) { - num_defs++; - if (df_attributes & DF_A_WIDE) { - num_defs++; - } - } - - AllocateSSADefData(mir, num_defs); - - MIR::DecodedInstruction* d_insn = &mir->dalvikInsn; - - if (df_attributes & DF_HAS_USES) { - num_uses = 0; - if (df_attributes & DF_UA) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vA, num_uses++); - if (df_attributes & DF_A_WIDE) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vA+1, num_uses++); - } - } - if (df_attributes & DF_UB) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vB, num_uses++); - if (df_attributes & DF_B_WIDE) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vB+1, num_uses++); - } - } - if (df_attributes & DF_UC) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vC, num_uses++); - if (df_attributes & DF_C_WIDE) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vC+1, num_uses++); - } - } - } - if (df_attributes & DF_HAS_DEFS) { - HandleSSADef(mir->ssa_rep->defs, d_insn->vA, 0); - if (df_attributes & DF_A_WIDE) { - HandleSSADef(mir->ssa_rep->defs, d_insn->vA+1, 1); - } - } - } - - /* - * Take a snapshot of Dalvik->SSA mapping at the end of each block. The - * input to PHI nodes can be derived from the snapshot of all - * predecessor blocks. - */ - bb->data_flow_info->vreg_to_ssa_map_exit = - arena_->AllocArray(GetNumOfCodeAndTempVRs(), kArenaAllocDFInfo); - - memcpy(bb->data_flow_info->vreg_to_ssa_map_exit, vreg_to_ssa_map_, - sizeof(int) * GetNumOfCodeAndTempVRs()); - return true; -} - -void MIRGraph::InitializeBasicBlockDataFlow() { - /* - * Allocate the BasicBlockDataFlow structure for the entry and code blocks. - */ - for (BasicBlock* bb : block_list_) { - if (bb->hidden == true) continue; - if (bb->block_type == kDalvikByteCode || - bb->block_type == kEntryBlock || - bb->block_type == kExitBlock) { - bb->data_flow_info = - static_cast(arena_->Alloc(sizeof(BasicBlockDataFlow), - kArenaAllocDFInfo)); - } - } -} - -/* Setup the basic data structures for SSA conversion */ -void MIRGraph::CompilerInitializeSSAConversion() { - size_t num_reg = GetNumOfCodeAndTempVRs(); - - ssa_base_vregs_.clear(); - ssa_base_vregs_.reserve(num_reg + GetDefCount() + 128); - ssa_subscripts_.clear(); - ssa_subscripts_.reserve(num_reg + GetDefCount() + 128); - - /* - * Initial number of SSA registers is equal to the number of Dalvik - * registers. - */ - SetNumSSARegs(num_reg); - - /* - * Initialize the SSA2Dalvik map list. For the first num_reg elements, - * the subscript is 0 so we use the ENCODE_REG_SUB macro to encode the value - * into "(0 << 16) | i" - */ - for (unsigned int i = 0; i < num_reg; i++) { - ssa_base_vregs_.push_back(i); - ssa_subscripts_.push_back(0); - } - - /* - * Initialize the DalvikToSSAMap map. There is one entry for each - * Dalvik register, and the SSA names for those are the same. - */ - vreg_to_ssa_map_ = arena_->AllocArray(num_reg, kArenaAllocDFInfo); - /* Keep track of the higest def for each dalvik reg */ - ssa_last_defs_ = arena_->AllocArray(num_reg, kArenaAllocDFInfo); - - for (unsigned int i = 0; i < num_reg; i++) { - vreg_to_ssa_map_[i] = i; - ssa_last_defs_[i] = 0; - } - - // Create a compiler temporary for Method*. This is done after SSA initialization. - CompilerTemp* method_temp = GetNewCompilerTemp(kCompilerTempSpecialMethodPtr, false); - // The MIR graph keeps track of the sreg for method pointer specially, so record that now. - method_sreg_ = method_temp->s_reg_low; - - InitializeBasicBlockDataFlow(); -} - -uint32_t MIRGraph::GetUseCountWeight(BasicBlock* bb) const { - // Each level of nesting adds *100 to count, up to 3 levels deep. - uint32_t depth = std::min(3U, static_cast(bb->nesting_depth)); - uint32_t weight = std::max(1U, depth * 100); - return weight; -} - -/* - * Count uses, weighting by loop nesting depth. This code only - * counts explicitly used s_regs. A later phase will add implicit - * counts for things such as Method*, null-checked references, etc. - */ -void MIRGraph::CountUses(BasicBlock* bb) { - if (bb->block_type != kDalvikByteCode) { - return; - } - uint32_t weight = GetUseCountWeight(bb); - for (MIR* mir = bb->first_mir_insn; (mir != nullptr); mir = mir->next) { - if (mir->ssa_rep == nullptr) { - continue; - } - for (int i = 0; i < mir->ssa_rep->num_uses; i++) { - int s_reg = mir->ssa_rep->uses[i]; - raw_use_counts_[s_reg] += 1u; - use_counts_[s_reg] += weight; - } - } -} - -/* Verify if all the successor is connected with all the claimed predecessors */ -bool MIRGraph::VerifyPredInfo(BasicBlock* bb) { - for (BasicBlockId pred_id : bb->predecessors) { - BasicBlock* pred_bb = GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - bool found = false; - if (pred_bb->taken == bb->id) { - found = true; - } else if (pred_bb->fall_through == bb->id) { - found = true; - } else if (pred_bb->successor_block_list_type != kNotUsed) { - for (SuccessorBlockInfo* successor_block_info : pred_bb->successor_blocks) { - BasicBlockId succ_bb = successor_block_info->block; - if (succ_bb == bb->id) { - found = true; - break; - } - } - } - if (found == false) { - char block_name1[BLOCK_NAME_LEN], block_name2[BLOCK_NAME_LEN]; - GetBlockName(bb, block_name1); - GetBlockName(pred_bb, block_name2); - DumpCFG("/sdcard/cfg/", false); - LOG(FATAL) << "Successor " << block_name1 << " not found from " - << block_name2; - } - } - return true; -} - -void MIRGraph::VerifyDataflow() { - /* Verify if all blocks are connected as claimed */ - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - VerifyPredInfo(bb); - } -} - -} // namespace art diff --git a/compiler/dex/mir_field_info.cc b/compiler/dex/mir_field_info.cc deleted file mode 100644 index 13bbc3e98312ac0702f31819fc0330c95aca7010..0000000000000000000000000000000000000000 --- a/compiler/dex/mir_field_info.cc +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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 "mir_field_info.h" - -#include - -#include "base/logging.h" -#include "dex/verified_method.h" -#include "driver/compiler_driver.h" -#include "driver/compiler_driver-inl.h" -#include "mirror/class_loader.h" // Only to allow casts in Handle. -#include "mirror/dex_cache.h" // Only to allow casts in Handle. -#include "scoped_thread_state_change.h" -#include "handle_scope-inl.h" - -namespace art { - -void MirIFieldLoweringInfo::Resolve(const ScopedObjectAccess& soa, - CompilerDriver* compiler_driver, - const DexCompilationUnit* mUnit, - MirIFieldLoweringInfo* field_infos, size_t count) { - if (kIsDebugBuild) { - DCHECK(field_infos != nullptr); - DCHECK_NE(count, 0u); - for (auto it = field_infos, end = field_infos + count; it != end; ++it) { - MirIFieldLoweringInfo unresolved(it->field_idx_, it->MemAccessType(), it->IsQuickened()); - unresolved.field_offset_ = it->field_offset_; - unresolved.CheckEquals(*it); - } - } - - // We're going to resolve fields and check access in a tight loop. It's better to hold - // the lock and needed references once than re-acquiring them again and again. - StackHandleScope<3> hs(soa.Self()); - Handle dex_cache(hs.NewHandle(compiler_driver->GetDexCache(mUnit))); - Handle class_loader( - hs.NewHandle(compiler_driver->GetClassLoader(soa, mUnit))); - Handle referrer_class(hs.NewHandle( - compiler_driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit))); - const VerifiedMethod* const verified_method = mUnit->GetVerifiedMethod(); - // Even if the referrer class is unresolved (i.e. we're compiling a method without class - // definition) we still want to resolve fields and record all available info. - for (auto it = field_infos, end = field_infos + count; it != end; ++it) { - uint32_t field_idx; - ArtField* resolved_field; - if (!it->IsQuickened()) { - field_idx = it->field_idx_; - resolved_field = compiler_driver->ResolveField(soa, dex_cache, class_loader, mUnit, - field_idx, false); - } else { - const auto mir_offset = it->field_idx_; - // For quickened instructions, it->field_offset_ actually contains the mir offset. - // We need to use the de-quickening info to get dex file / field idx - auto* field_idx_ptr = verified_method->GetDequickenIndex(mir_offset); - CHECK(field_idx_ptr != nullptr); - field_idx = field_idx_ptr->index; - StackHandleScope<1> hs2(soa.Self()); - auto h_dex_cache = hs2.NewHandle(compiler_driver->FindDexCache(field_idx_ptr->dex_file)); - resolved_field = compiler_driver->ResolveFieldWithDexFile( - soa, h_dex_cache, class_loader, field_idx_ptr->dex_file, field_idx, false); - // Since we don't have a valid field index we can't go slow path later. - CHECK(resolved_field != nullptr); - } - if (UNLIKELY(resolved_field == nullptr)) { - continue; - } - compiler_driver->GetResolvedFieldDexFileLocation(resolved_field, - &it->declaring_dex_file_, &it->declaring_class_idx_, &it->declaring_field_idx_); - bool is_volatile = compiler_driver->IsFieldVolatile(resolved_field); - it->field_offset_ = compiler_driver->GetFieldOffset(resolved_field); - std::pair fast_path = compiler_driver->IsFastInstanceField( - dex_cache.Get(), referrer_class.Get(), resolved_field, field_idx); - it->flags_ = 0u | // Without kFlagIsStatic. - (it->flags_ & (kMemAccessTypeMask << kBitMemAccessTypeBegin)) | - (is_volatile ? kFlagIsVolatile : 0u) | - (fast_path.first ? kFlagFastGet : 0u) | - (fast_path.second ? kFlagFastPut : 0u); - } -} - -void MirSFieldLoweringInfo::Resolve(CompilerDriver* compiler_driver, - const DexCompilationUnit* mUnit, - MirSFieldLoweringInfo* field_infos, size_t count) { - if (kIsDebugBuild) { - DCHECK(field_infos != nullptr); - DCHECK_NE(count, 0u); - for (auto it = field_infos, end = field_infos + count; it != end; ++it) { - MirSFieldLoweringInfo unresolved(it->field_idx_, it->MemAccessType()); - // In 64-bit builds, there's padding after storage_index_, don't include it in memcmp. - size_t size = OFFSETOF_MEMBER(MirSFieldLoweringInfo, storage_index_) + - sizeof(it->storage_index_); - DCHECK_EQ(memcmp(&unresolved, &*it, size), 0); - } - } - - // We're going to resolve fields and check access in a tight loop. It's better to hold - // the lock and needed references once than re-acquiring them again and again. - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<3> hs(soa.Self()); - Handle dex_cache(hs.NewHandle(compiler_driver->GetDexCache(mUnit))); - Handle class_loader( - hs.NewHandle(compiler_driver->GetClassLoader(soa, mUnit))); - Handle referrer_class_handle(hs.NewHandle( - compiler_driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit))); - // Even if the referrer class is unresolved (i.e. we're compiling a method without class - // definition) we still want to resolve fields and record all available info. - - for (auto it = field_infos, end = field_infos + count; it != end; ++it) { - uint32_t field_idx = it->field_idx_; - ArtField* resolved_field = - compiler_driver->ResolveField(soa, dex_cache, class_loader, mUnit, field_idx, true); - if (UNLIKELY(resolved_field == nullptr)) { - continue; - } - compiler_driver->GetResolvedFieldDexFileLocation(resolved_field, - &it->declaring_dex_file_, &it->declaring_class_idx_, &it->declaring_field_idx_); - bool is_volatile = compiler_driver->IsFieldVolatile(resolved_field) ? 1u : 0u; - - mirror::Class* referrer_class = referrer_class_handle.Get(); - std::pair fast_path = compiler_driver->IsFastStaticField( - dex_cache.Get(), referrer_class, resolved_field, field_idx, &it->storage_index_); - uint16_t flags = kFlagIsStatic | - (it->flags_ & (kMemAccessTypeMask << kBitMemAccessTypeBegin)) | - (is_volatile ? kFlagIsVolatile : 0u) | - (fast_path.first ? kFlagFastGet : 0u) | - (fast_path.second ? kFlagFastPut : 0u); - if (fast_path.first) { - it->field_offset_ = compiler_driver->GetFieldOffset(resolved_field); - bool is_referrers_class = - compiler_driver->IsStaticFieldInReferrerClass(referrer_class, resolved_field); - bool is_class_initialized = - compiler_driver->IsStaticFieldsClassInitialized(referrer_class, resolved_field); - bool is_class_in_dex_cache = !is_referrers_class && // If referrer's class, we don't care. - compiler_driver->CanAssumeTypeIsPresentInDexCache(*dex_cache->GetDexFile(), - it->storage_index_); - flags |= (is_referrers_class ? kFlagIsReferrersClass : 0u) | - (is_class_initialized ? kFlagClassIsInitialized : 0u) | - (is_class_in_dex_cache ? kFlagClassIsInDexCache : 0u); - } - it->flags_ = flags; - } -} - -} // namespace art diff --git a/compiler/dex/mir_field_info.h b/compiler/dex/mir_field_info.h deleted file mode 100644 index b6dc27d26eece6a6efeae153f41111a9a2c8865b..0000000000000000000000000000000000000000 --- a/compiler/dex/mir_field_info.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * 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_MIR_FIELD_INFO_H_ -#define ART_COMPILER_DEX_MIR_FIELD_INFO_H_ - -#include "base/macros.h" -#include "dex_file.h" -#include "dex_instruction_utils.h" -#include "offsets.h" - -namespace art { - -class CompilerDriver; -class DexCompilationUnit; -class ScopedObjectAccess; - -/* - * Field info is calculated from the perspective of the compilation unit that accesses - * the field and stored in that unit's MIRGraph. Therefore it does not need to reference the - * dex file or method for which it has been calculated. However, we do store the declaring - * field index, class index and dex file of the resolved field to help distinguish between fields. - */ - -class MirFieldInfo { - public: - uint16_t FieldIndex() const { - return field_idx_; - } - void SetFieldIndex(uint16_t field_idx) { - field_idx_ = field_idx; - } - - bool IsStatic() const { - return (flags_ & kFlagIsStatic) != 0u; - } - - bool IsResolved() const { - return declaring_dex_file_ != nullptr; - } - - const DexFile* DeclaringDexFile() const { - return declaring_dex_file_; - } - void SetDeclaringDexFile(const DexFile* dex_file) { - declaring_dex_file_ = dex_file; - } - - uint16_t DeclaringClassIndex() const { - return declaring_class_idx_; - } - - uint16_t DeclaringFieldIndex() const { - return declaring_field_idx_; - } - - bool IsVolatile() const { - return (flags_ & kFlagIsVolatile) != 0u; - } - - // IGET_QUICK, IGET_BYTE_QUICK, ... - bool IsQuickened() const { - return (flags_ & kFlagIsQuickened) != 0u; - } - - DexMemAccessType MemAccessType() const { - return static_cast((flags_ >> kBitMemAccessTypeBegin) & kMemAccessTypeMask); - } - - void CheckEquals(const MirFieldInfo& other) const { - CHECK_EQ(field_idx_, other.field_idx_); - CHECK_EQ(flags_, other.flags_); - CHECK_EQ(declaring_field_idx_, other.declaring_field_idx_); - CHECK_EQ(declaring_class_idx_, other.declaring_class_idx_); - CHECK_EQ(declaring_dex_file_, other.declaring_dex_file_); - } - - protected: - enum { - kBitIsStatic = 0, - kBitIsVolatile, - kBitIsQuickened, - kBitMemAccessTypeBegin, - kBitMemAccessTypeEnd = kBitMemAccessTypeBegin + 3, // 3 bits for raw type. - kFieldInfoBitEnd = kBitMemAccessTypeEnd - }; - static constexpr uint16_t kFlagIsVolatile = 1u << kBitIsVolatile; - static constexpr uint16_t kFlagIsStatic = 1u << kBitIsStatic; - static constexpr uint16_t kFlagIsQuickened = 1u << kBitIsQuickened; - static constexpr uint16_t kMemAccessTypeMask = 7u; - static_assert((1u << (kBitMemAccessTypeEnd - kBitMemAccessTypeBegin)) - 1u == kMemAccessTypeMask, - "Invalid raw type mask"); - - MirFieldInfo(uint16_t field_idx, uint16_t flags, DexMemAccessType type) - : field_idx_(field_idx), - flags_(flags | static_cast(type) << kBitMemAccessTypeBegin), - declaring_field_idx_(0u), - declaring_class_idx_(0u), - declaring_dex_file_(nullptr) { - } - - // Make copy-ctor/assign/dtor protected to avoid slicing. - MirFieldInfo(const MirFieldInfo& other) = default; - MirFieldInfo& operator=(const MirFieldInfo& other) = default; - ~MirFieldInfo() = default; - - // The field index in the compiling method's dex file. - uint16_t field_idx_; - // Flags, for volatility and derived class data. - uint16_t flags_; - // The field index in the dex file that defines field, 0 if unresolved. - uint16_t declaring_field_idx_; - // The type index of the class declaring the field, 0 if unresolved. - uint16_t declaring_class_idx_; - // The dex file that defines the class containing the field and the field, null if unresolved. - const DexFile* declaring_dex_file_; -}; - -class MirIFieldLoweringInfo : public MirFieldInfo { - public: - // For each requested instance field retrieve the field's declaring location (dex file, class - // index and field index) and volatility and compute whether we can fast path the access - // with IGET/IPUT. For fast path fields, retrieve the field offset. - static void Resolve(const ScopedObjectAccess& soa, - CompilerDriver* compiler_driver, - const DexCompilationUnit* mUnit, - MirIFieldLoweringInfo* field_infos, - size_t count) - SHARED_REQUIRES(Locks::mutator_lock_); - - // Construct an unresolved instance field lowering info. - MirIFieldLoweringInfo(uint16_t field_idx, DexMemAccessType type, bool is_quickened) - : MirFieldInfo(field_idx, - kFlagIsVolatile | (is_quickened ? kFlagIsQuickened : 0u), - type), // Without kFlagIsStatic. - field_offset_(0u) { - } - - bool FastGet() const { - return (flags_ & kFlagFastGet) != 0u; - } - - bool FastPut() const { - return (flags_ & kFlagFastPut) != 0u; - } - - MemberOffset FieldOffset() const { - return field_offset_; - } - - void CheckEquals(const MirIFieldLoweringInfo& other) const { - MirFieldInfo::CheckEquals(other); - CHECK_EQ(field_offset_.Uint32Value(), other.field_offset_.Uint32Value()); - } - - private: - enum { - kBitFastGet = kFieldInfoBitEnd, - kBitFastPut, - kIFieldLoweringInfoBitEnd - }; - static_assert(kIFieldLoweringInfoBitEnd <= 16, "Too many flags"); - static constexpr uint16_t kFlagFastGet = 1u << kBitFastGet; - static constexpr uint16_t kFlagFastPut = 1u << kBitFastPut; - - // The member offset of the field, 0u if unresolved. - MemberOffset field_offset_; - - friend class NullCheckEliminationTest; - friend class GlobalValueNumberingTest; - friend class GvnDeadCodeEliminationTest; - friend class LocalValueNumberingTest; - friend class TypeInferenceTest; -}; - -class MirSFieldLoweringInfo : public MirFieldInfo { - public: - // For each requested static field retrieve the field's declaring location (dex file, class - // index and field index) and volatility and compute whether we can fast path the access with - // IGET/IPUT. For fast path fields (at least for IGET), retrieve the information needed for - // the field access, i.e. the field offset, whether the field is in the same class as the - // method being compiled, whether the declaring class can be safely assumed to be initialized - // and the type index of the declaring class in the compiled method's dex file. - static void Resolve(CompilerDriver* compiler_driver, const DexCompilationUnit* mUnit, - MirSFieldLoweringInfo* field_infos, size_t count) - REQUIRES(!Locks::mutator_lock_); - - // Construct an unresolved static field lowering info. - MirSFieldLoweringInfo(uint16_t field_idx, DexMemAccessType type) - : MirFieldInfo(field_idx, kFlagIsVolatile | kFlagIsStatic, type), - field_offset_(0u), - storage_index_(DexFile::kDexNoIndex) { - } - - bool FastGet() const { - return (flags_ & kFlagFastGet) != 0u; - } - - bool FastPut() const { - return (flags_ & kFlagFastPut) != 0u; - } - - bool IsReferrersClass() const { - return (flags_ & kFlagIsReferrersClass) != 0u; - } - - bool IsClassInitialized() const { - return (flags_ & kFlagClassIsInitialized) != 0u; - } - - bool IsClassInDexCache() const { - return (flags_ & kFlagClassIsInDexCache) != 0u; - } - - MemberOffset FieldOffset() const { - return field_offset_; - } - - uint32_t StorageIndex() const { - return storage_index_; - } - - private: - enum { - kBitFastGet = kFieldInfoBitEnd, - kBitFastPut, - kBitIsReferrersClass, - kBitClassIsInitialized, - kBitClassIsInDexCache, - kSFieldLoweringInfoBitEnd - }; - static_assert(kSFieldLoweringInfoBitEnd <= 16, "Too many flags"); - static constexpr uint16_t kFlagFastGet = 1u << kBitFastGet; - static constexpr uint16_t kFlagFastPut = 1u << kBitFastPut; - static constexpr uint16_t kFlagIsReferrersClass = 1u << kBitIsReferrersClass; - static constexpr uint16_t kFlagClassIsInitialized = 1u << kBitClassIsInitialized; - static constexpr uint16_t kFlagClassIsInDexCache = 1u << kBitClassIsInDexCache; - - // The member offset of the field, 0u if unresolved. - MemberOffset field_offset_; - // The type index of the declaring class in the compiling method's dex file, - // -1 if the field is unresolved or there's no appropriate TypeId in that dex file. - uint32_t storage_index_; - - friend class ClassInitCheckEliminationTest; - friend class GlobalValueNumberingTest; - friend class GvnDeadCodeEliminationTest; - friend class LocalValueNumberingTest; - friend class TypeInferenceTest; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_MIR_FIELD_INFO_H_ diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc deleted file mode 100644 index 6dc148dfdb56ed85bd7b3f4a783c45999c49b170..0000000000000000000000000000000000000000 --- a/compiler/dex/mir_graph.cc +++ /dev/null @@ -1,2589 +0,0 @@ -/* - * Copyright (C) 2013 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 "mir_graph.h" - -#include -#include -#include - -#include "base/bit_vector-inl.h" -#include "base/logging.h" -#include "base/stl_util.h" -#include "base/stringprintf.h" -#include "base/scoped_arena_containers.h" -#include "compiler_ir.h" -#include "dex_file-inl.h" -#include "dex_flags.h" -#include "dex_instruction-inl.h" -#include "driver/compiler_driver.h" -#include "driver/dex_compilation_unit.h" -#include "dex/quick/quick_compiler.h" -#include "leb128.h" -#include "pass_driver_me_post_opt.h" -#include "stack.h" -#include "utils.h" - -namespace art { - -#define MAX_PATTERN_LEN 5 - -const char* MIRGraph::extended_mir_op_names_[kMirOpLast - kMirOpFirst] = { - "Phi", - "Copy", - "FusedCmplFloat", - "FusedCmpgFloat", - "FusedCmplDouble", - "FusedCmpgDouble", - "FusedCmpLong", - "Nop", - "OpNullCheck", - "OpRangeCheck", - "OpDivZeroCheck", - "Check", - "Select", - "ConstVector", - "MoveVector", - "PackedMultiply", - "PackedAddition", - "PackedSubtract", - "PackedShiftLeft", - "PackedSignedShiftRight", - "PackedUnsignedShiftRight", - "PackedAnd", - "PackedOr", - "PackedXor", - "PackedAddReduce", - "PackedReduce", - "PackedSet", - "ReserveVectorRegisters", - "ReturnVectorRegisters", - "MemBarrier", - "PackedArrayGet", - "PackedArrayPut", - "MaddInt", - "MsubInt", - "MaddLong", - "MsubLong", -}; - -MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) - : reg_location_(nullptr), - block_id_map_(std::less(), arena->Adapter()), - cu_(cu), - ssa_base_vregs_(arena->Adapter(kArenaAllocSSAToDalvikMap)), - ssa_subscripts_(arena->Adapter(kArenaAllocSSAToDalvikMap)), - vreg_to_ssa_map_(nullptr), - ssa_last_defs_(nullptr), - is_constant_v_(nullptr), - constant_values_(nullptr), - use_counts_(arena->Adapter()), - raw_use_counts_(arena->Adapter()), - num_reachable_blocks_(0), - max_num_reachable_blocks_(0), - dfs_orders_up_to_date_(false), - domination_up_to_date_(false), - mir_ssa_rep_up_to_date_(false), - topological_order_up_to_date_(false), - dfs_order_(arena->Adapter(kArenaAllocDfsPreOrder)), - dfs_post_order_(arena->Adapter(kArenaAllocDfsPostOrder)), - dom_post_order_traversal_(arena->Adapter(kArenaAllocDomPostOrder)), - topological_order_(arena->Adapter(kArenaAllocTopologicalSortOrder)), - topological_order_loop_ends_(arena->Adapter(kArenaAllocTopologicalSortOrder)), - topological_order_indexes_(arena->Adapter(kArenaAllocTopologicalSortOrder)), - topological_order_loop_head_stack_(arena->Adapter(kArenaAllocTopologicalSortOrder)), - max_nested_loops_(0u), - i_dom_list_(nullptr), - temp_scoped_alloc_(), - block_list_(arena->Adapter(kArenaAllocBBList)), - try_block_addr_(nullptr), - entry_block_(nullptr), - exit_block_(nullptr), - current_code_item_(nullptr), - m_units_(arena->Adapter()), - method_stack_(arena->Adapter()), - current_method_(kInvalidEntry), - current_offset_(kInvalidEntry), - def_count_(0), - opcode_count_(nullptr), - num_ssa_regs_(0), - extended_basic_blocks_(arena->Adapter()), - method_sreg_(0), - attributes_(METHOD_IS_LEAF), // Start with leaf assumption, change on encountering invoke. - checkstats_(nullptr), - arena_(arena), - backward_branches_(0), - forward_branches_(0), - num_non_special_compiler_temps_(0), - max_available_special_compiler_temps_(1), // We only need the method ptr as a special temp for now. - requested_backend_temp_(false), - compiler_temps_committed_(false), - punt_to_interpreter_(false), - merged_df_flags_(0u), - ifield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)), - sfield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)), - method_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)), - suspend_checks_in_loops_(nullptr) { - memset(&temp_, 0, sizeof(temp_)); - use_counts_.reserve(256); - raw_use_counts_.reserve(256); - block_list_.reserve(100); - try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */); - - - if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) { - // X86 requires a temp to keep track of the method address. - // TODO For x86_64, addressing can be done with RIP. When that is implemented, - // this needs to be updated to reserve 0 temps for BE. - max_available_non_special_compiler_temps_ = cu_->target64 ? 2 : 1; - reserved_temps_for_backend_ = max_available_non_special_compiler_temps_; - } else { - // Other architectures do not have a known lower bound for non-special temps. - // We allow the update of the max to happen at BE initialization stage and simply set 0 for now. - max_available_non_special_compiler_temps_ = 0; - reserved_temps_for_backend_ = 0; - } -} - -MIRGraph::~MIRGraph() { - STLDeleteElements(&block_list_); - STLDeleteElements(&m_units_); -} - -/* - * Parse an instruction, return the length of the instruction - */ -int MIRGraph::ParseInsn(const uint16_t* code_ptr, MIR::DecodedInstruction* decoded_instruction) { - const Instruction* inst = Instruction::At(code_ptr); - decoded_instruction->opcode = inst->Opcode(); - decoded_instruction->vA = inst->HasVRegA() ? inst->VRegA() : 0; - decoded_instruction->vB = inst->HasVRegB() ? inst->VRegB() : 0; - decoded_instruction->vB_wide = inst->HasWideVRegB() ? inst->WideVRegB() : 0; - decoded_instruction->vC = inst->HasVRegC() ? inst->VRegC() : 0; - if (inst->HasVarArgs35c()) { - inst->GetVarArgs(decoded_instruction->arg); - } - return inst->SizeInCodeUnits(); -} - - -/* Split an existing block from the specified code offset into two */ -BasicBlock* MIRGraph::SplitBlock(DexOffset code_offset, - BasicBlock* orig_block, BasicBlock** immed_pred_block_p) { - DCHECK_GT(code_offset, orig_block->start_offset); - MIR* insn = orig_block->first_mir_insn; - MIR* prev = nullptr; // Will be set to instruction before split. - while (insn) { - if (insn->offset == code_offset) break; - prev = insn; - insn = insn->next; - } - if (insn == nullptr) { - LOG(FATAL) << "Break split failed"; - } - // Now insn is at the instruction where we want to split, namely - // insn will be the first instruction of the "bottom" block. - // Similarly, prev will be the last instruction of the "top" block - - BasicBlock* bottom_block = CreateNewBB(kDalvikByteCode); - - bottom_block->start_offset = code_offset; - bottom_block->first_mir_insn = insn; - bottom_block->last_mir_insn = orig_block->last_mir_insn; - - /* If this block was terminated by a return, conditional branch or throw, - * the flag needs to go with the bottom block - */ - bottom_block->terminated_by_return = orig_block->terminated_by_return; - orig_block->terminated_by_return = false; - - bottom_block->conditional_branch = orig_block->conditional_branch; - orig_block->conditional_branch = false; - - bottom_block->explicit_throw = orig_block->explicit_throw; - orig_block->explicit_throw = false; - - /* Handle the taken path */ - bottom_block->taken = orig_block->taken; - if (bottom_block->taken != NullBasicBlockId) { - orig_block->taken = NullBasicBlockId; - BasicBlock* bb_taken = GetBasicBlock(bottom_block->taken); - bb_taken->ErasePredecessor(orig_block->id); - bb_taken->predecessors.push_back(bottom_block->id); - } - - /* Handle the fallthrough path */ - bottom_block->fall_through = orig_block->fall_through; - orig_block->fall_through = bottom_block->id; - bottom_block->predecessors.push_back(orig_block->id); - if (bottom_block->fall_through != NullBasicBlockId) { - BasicBlock* bb_fall_through = GetBasicBlock(bottom_block->fall_through); - bb_fall_through->ErasePredecessor(orig_block->id); - bb_fall_through->predecessors.push_back(bottom_block->id); - } - - /* Handle the successor list */ - if (orig_block->successor_block_list_type != kNotUsed) { - bottom_block->successor_block_list_type = orig_block->successor_block_list_type; - bottom_block->successor_blocks.swap(orig_block->successor_blocks); - orig_block->successor_block_list_type = kNotUsed; - DCHECK(orig_block->successor_blocks.empty()); // Empty after the swap() above. - for (SuccessorBlockInfo* successor_block_info : bottom_block->successor_blocks) { - BasicBlock* bb = GetBasicBlock(successor_block_info->block); - if (bb != nullptr) { - bb->ErasePredecessor(orig_block->id); - bb->predecessors.push_back(bottom_block->id); - } - } - } - - orig_block->last_mir_insn = prev; - prev->next = nullptr; - - /* - * Update the immediate predecessor block pointer so that outgoing edges - * can be applied to the proper block. - */ - if (immed_pred_block_p) { - DCHECK_EQ(*immed_pred_block_p, orig_block); - *immed_pred_block_p = bottom_block; - } - - // Associate dex instructions in the bottom block with the new container. - DCHECK(insn != nullptr); - DCHECK(insn != orig_block->first_mir_insn); - DCHECK(insn == bottom_block->first_mir_insn); - DCHECK_EQ(insn->offset, bottom_block->start_offset); - // Scan the "bottom" instructions, remapping them to the - // newly created "bottom" block. - MIR* p = insn; - p->bb = bottom_block->id; - while (p != bottom_block->last_mir_insn) { - p = p->next; - DCHECK(p != nullptr); - p->bb = bottom_block->id; - } - - return bottom_block; -} - -/* - * Given a code offset, find out the block that starts with it. If the offset - * is in the middle of an existing block, split it into two. If immed_pred_block_p - * is not non-null and is the block being split, update *immed_pred_block_p to - * point to the bottom block so that outgoing edges can be set up properly - * (by the caller) - * Utilizes a map for fast lookup of the typical cases. - */ -BasicBlock* MIRGraph::FindBlock(DexOffset code_offset, bool create, - BasicBlock** immed_pred_block_p, - ScopedArenaVector* dex_pc_to_block_map) { - if (UNLIKELY(code_offset >= current_code_item_->insns_size_in_code_units_)) { - // There can be a fall-through out of the method code. We shall record such a block - // here (assuming create==true) and check that it's dead at the end of InlineMethod(). - // Though we're only aware of the cases where code_offset is exactly the same as - // insns_size_in_code_units_, treat greater code_offset the same just in case. - code_offset = current_code_item_->insns_size_in_code_units_; - } - - int block_id = (*dex_pc_to_block_map)[code_offset]; - BasicBlock* bb = GetBasicBlock(block_id); - - if ((bb != nullptr) && (bb->start_offset == code_offset)) { - // Does this containing block start with the desired instruction? - return bb; - } - - // No direct hit. - if (!create) { - return nullptr; - } - - if (bb != nullptr) { - // The target exists somewhere in an existing block. - BasicBlock* bottom_block = SplitBlock(code_offset, bb, bb == *immed_pred_block_p ? immed_pred_block_p : nullptr); - DCHECK(bottom_block != nullptr); - MIR* p = bottom_block->first_mir_insn; - BasicBlock* orig_block = bb; - DCHECK_EQ((*dex_pc_to_block_map)[p->offset], orig_block->id); - // Scan the "bottom" instructions, remapping them to the - // newly created "bottom" block. - (*dex_pc_to_block_map)[p->offset] = bottom_block->id; - while (p != bottom_block->last_mir_insn) { - p = p->next; - DCHECK(p != nullptr); - int opcode = p->dalvikInsn.opcode; - /* - * Some messiness here to ensure that we only enter real opcodes and only the - * first half of a potentially throwing instruction that has been split into - * CHECK and work portions. Since the 2nd half of a split operation is always - * the first in a BasicBlock, we can't hit it here. - */ - if ((opcode == kMirOpCheck) || !MIR::DecodedInstruction::IsPseudoMirOp(opcode)) { - BasicBlockId mapped_id = (*dex_pc_to_block_map)[p->offset]; - // At first glance the instructions should all be mapped to orig_block. - // However, multiple instructions may correspond to the same dex, hence an earlier - // instruction may have already moved the mapping for dex to bottom_block. - DCHECK((mapped_id == orig_block->id) || (mapped_id == bottom_block->id)); - (*dex_pc_to_block_map)[p->offset] = bottom_block->id; - } - } - return bottom_block; - } - - // Create a new block. - bb = CreateNewBB(kDalvikByteCode); - bb->start_offset = code_offset; - (*dex_pc_to_block_map)[bb->start_offset] = bb->id; - return bb; -} - - -/* Identify code range in try blocks and set up the empty catch blocks */ -void MIRGraph::ProcessTryCatchBlocks(ScopedArenaVector* dex_pc_to_block_map) { - int tries_size = current_code_item_->tries_size_; - DexOffset offset; - - if (tries_size == 0) { - return; - } - - for (int i = 0; i < tries_size; i++) { - const DexFile::TryItem* pTry = - DexFile::GetTryItems(*current_code_item_, i); - DexOffset start_offset = pTry->start_addr_; - DexOffset end_offset = start_offset + pTry->insn_count_; - for (offset = start_offset; offset < end_offset; offset++) { - try_block_addr_->SetBit(offset); - } - } - - // Iterate over each of the handlers to enqueue the empty Catch blocks. - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*current_code_item_, 0); - uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); - for (uint32_t idx = 0; idx < handlers_size; idx++) { - CatchHandlerIterator iterator(handlers_ptr); - for (; iterator.HasNext(); iterator.Next()) { - uint32_t address = iterator.GetHandlerAddress(); - FindBlock(address, true /*create*/, /* immed_pred_block_p */ nullptr, dex_pc_to_block_map); - } - handlers_ptr = iterator.EndDataPointer(); - } -} - -bool MIRGraph::IsBadMonitorExitCatch(NarrowDexOffset monitor_exit_offset, - NarrowDexOffset catch_offset) { - // Catches for monitor-exit during stack unwinding have the pattern - // move-exception (move)* (goto)? monitor-exit throw - // In the currently generated dex bytecode we see these catching a bytecode range including - // either its own or an identical monitor-exit, http://b/15745363 . This function checks if - // it's the case for a given monitor-exit and catch block so that we can ignore it. - // (We don't want to ignore all monitor-exit catches since one could enclose a synchronized - // block in a try-block and catch the NPE, Error or Throwable and we should let it through; - // even though a throwing monitor-exit certainly indicates a bytecode error.) - const Instruction* monitor_exit = Instruction::At(current_code_item_->insns_ + monitor_exit_offset); - DCHECK(monitor_exit->Opcode() == Instruction::MONITOR_EXIT); - int monitor_reg = monitor_exit->VRegA_11x(); - const Instruction* check_insn = Instruction::At(current_code_item_->insns_ + catch_offset); - if (check_insn->Opcode() == Instruction::MOVE_EXCEPTION) { - if (check_insn->VRegA_11x() == monitor_reg) { - // Unexpected move-exception to the same register. Probably not the pattern we're looking for. - return false; - } - check_insn = check_insn->Next(); - } - while (true) { - int dest = -1; - bool wide = false; - switch (check_insn->Opcode()) { - case Instruction::MOVE_WIDE: - wide = true; - FALLTHROUGH_INTENDED; - case Instruction::MOVE_OBJECT: - case Instruction::MOVE: - dest = check_insn->VRegA_12x(); - break; - - case Instruction::MOVE_WIDE_FROM16: - wide = true; - FALLTHROUGH_INTENDED; - case Instruction::MOVE_OBJECT_FROM16: - case Instruction::MOVE_FROM16: - dest = check_insn->VRegA_22x(); - break; - - case Instruction::MOVE_WIDE_16: - wide = true; - FALLTHROUGH_INTENDED; - case Instruction::MOVE_OBJECT_16: - case Instruction::MOVE_16: - dest = check_insn->VRegA_32x(); - break; - - case Instruction::GOTO: - case Instruction::GOTO_16: - case Instruction::GOTO_32: - check_insn = check_insn->RelativeAt(check_insn->GetTargetOffset()); - FALLTHROUGH_INTENDED; - default: - return check_insn->Opcode() == Instruction::MONITOR_EXIT && - check_insn->VRegA_11x() == monitor_reg; - } - - if (dest == monitor_reg || (wide && dest + 1 == monitor_reg)) { - return false; - } - - check_insn = check_insn->Next(); - } -} - -/* Process instructions with the kBranch flag */ -BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, - int width, int flags, const uint16_t* code_ptr, - const uint16_t* code_end, - ScopedArenaVector* dex_pc_to_block_map) { - DexOffset target = cur_offset; - switch (insn->dalvikInsn.opcode) { - case Instruction::GOTO: - case Instruction::GOTO_16: - case Instruction::GOTO_32: - target += insn->dalvikInsn.vA; - break; - case Instruction::IF_EQ: - case Instruction::IF_NE: - case Instruction::IF_LT: - case Instruction::IF_GE: - case Instruction::IF_GT: - case Instruction::IF_LE: - cur_block->conditional_branch = true; - target += insn->dalvikInsn.vC; - break; - case Instruction::IF_EQZ: - case Instruction::IF_NEZ: - case Instruction::IF_LTZ: - case Instruction::IF_GEZ: - case Instruction::IF_GTZ: - case Instruction::IF_LEZ: - cur_block->conditional_branch = true; - target += insn->dalvikInsn.vB; - break; - default: - LOG(FATAL) << "Unexpected opcode(" << insn->dalvikInsn.opcode << ") with kBranch set"; - } - CountBranch(target); - BasicBlock* taken_block = FindBlock(target, /* create */ true, - /* immed_pred_block_p */ &cur_block, - dex_pc_to_block_map); - DCHECK(taken_block != nullptr); - cur_block->taken = taken_block->id; - taken_block->predecessors.push_back(cur_block->id); - - /* Always terminate the current block for conditional branches */ - if (flags & Instruction::kContinue) { - BasicBlock* fallthrough_block = FindBlock(cur_offset + width, - /* create */ - true, - /* immed_pred_block_p */ - &cur_block, - dex_pc_to_block_map); - DCHECK(fallthrough_block != nullptr); - cur_block->fall_through = fallthrough_block->id; - fallthrough_block->predecessors.push_back(cur_block->id); - } else if (code_ptr < code_end) { - FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr, dex_pc_to_block_map); - } - return cur_block; -} - -/* Process instructions with the kSwitch flag */ -BasicBlock* MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, - int width, int flags ATTRIBUTE_UNUSED, - ScopedArenaVector* dex_pc_to_block_map) { - const uint16_t* switch_data = - reinterpret_cast(GetCurrentInsns() + cur_offset + - static_cast(insn->dalvikInsn.vB)); - int size; - const int* keyTable; - const int* target_table; - int i; - int first_key; - - /* - * Packed switch data format: - * ushort ident = 0x0100 magic value - * ushort size number of entries in the table - * int first_key first (and lowest) switch case value - * int targets[size] branch targets, relative to switch opcode - * - * Total size is (4+size*2) 16-bit code units. - */ - if (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) { - DCHECK_EQ(static_cast(switch_data[0]), - static_cast(Instruction::kPackedSwitchSignature)); - size = switch_data[1]; - first_key = switch_data[2] | (switch_data[3] << 16); - target_table = reinterpret_cast(&switch_data[4]); - keyTable = nullptr; // Make the compiler happy. - /* - * Sparse switch data format: - * ushort ident = 0x0200 magic value - * ushort size number of entries in the table; > 0 - * int keys[size] keys, sorted low-to-high; 32-bit aligned - * int targets[size] branch targets, relative to switch opcode - * - * Total size is (2+size*4) 16-bit code units. - */ - } else { - DCHECK_EQ(static_cast(switch_data[0]), - static_cast(Instruction::kSparseSwitchSignature)); - size = switch_data[1]; - keyTable = reinterpret_cast(&switch_data[2]); - target_table = reinterpret_cast(&switch_data[2 + size*2]); - first_key = 0; // To make the compiler happy. - } - - if (cur_block->successor_block_list_type != kNotUsed) { - LOG(FATAL) << "Successor block list already in use: " - << static_cast(cur_block->successor_block_list_type); - } - cur_block->successor_block_list_type = - (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ? kPackedSwitch : kSparseSwitch; - cur_block->successor_blocks.reserve(size); - - for (i = 0; i < size; i++) { - BasicBlock* case_block = FindBlock(cur_offset + target_table[i], /* create */ true, - /* immed_pred_block_p */ &cur_block, - dex_pc_to_block_map); - DCHECK(case_block != nullptr); - SuccessorBlockInfo* successor_block_info = - static_cast(arena_->Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessors)); - successor_block_info->block = case_block->id; - successor_block_info->key = - (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ? - first_key + i : keyTable[i]; - cur_block->successor_blocks.push_back(successor_block_info); - case_block->predecessors.push_back(cur_block->id); - } - - /* Fall-through case */ - BasicBlock* fallthrough_block = FindBlock(cur_offset + width, /* create */ true, - /* immed_pred_block_p */ nullptr, - dex_pc_to_block_map); - DCHECK(fallthrough_block != nullptr); - cur_block->fall_through = fallthrough_block->id; - fallthrough_block->predecessors.push_back(cur_block->id); - return cur_block; -} - -/* Process instructions with the kThrow flag */ -BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, - MIR* insn, - DexOffset cur_offset, - int width, - int flags ATTRIBUTE_UNUSED, - ArenaBitVector* try_block_addr, - const uint16_t* code_ptr, - const uint16_t* code_end, - ScopedArenaVector* dex_pc_to_block_map) { - bool in_try_block = try_block_addr->IsBitSet(cur_offset); - bool is_throw = (insn->dalvikInsn.opcode == Instruction::THROW); - - /* In try block */ - if (in_try_block) { - CatchHandlerIterator iterator(*current_code_item_, cur_offset); - - if (cur_block->successor_block_list_type != kNotUsed) { - LOG(INFO) << PrettyMethod(cu_->method_idx, *cu_->dex_file); - LOG(FATAL) << "Successor block list already in use: " - << static_cast(cur_block->successor_block_list_type); - } - - for (; iterator.HasNext(); iterator.Next()) { - BasicBlock* catch_block = FindBlock(iterator.GetHandlerAddress(), false /* create */, - nullptr /* immed_pred_block_p */, - dex_pc_to_block_map); - if (insn->dalvikInsn.opcode == Instruction::MONITOR_EXIT && - IsBadMonitorExitCatch(insn->offset, catch_block->start_offset)) { - // Don't allow monitor-exit to catch its own exception, http://b/15745363 . - continue; - } - if (cur_block->successor_block_list_type == kNotUsed) { - cur_block->successor_block_list_type = kCatch; - } - catch_block->catch_entry = true; - if (kIsDebugBuild) { - catches_.insert(catch_block->start_offset); - } - SuccessorBlockInfo* successor_block_info = reinterpret_cast - (arena_->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); - successor_block_info->block = catch_block->id; - successor_block_info->key = iterator.GetHandlerTypeIndex(); - cur_block->successor_blocks.push_back(successor_block_info); - catch_block->predecessors.push_back(cur_block->id); - } - in_try_block = (cur_block->successor_block_list_type != kNotUsed); - } - bool build_all_edges = - (cu_->disable_opt & (1 << kSuppressExceptionEdges)) || is_throw || in_try_block; - if (!in_try_block && build_all_edges) { - BasicBlock* eh_block = CreateNewBB(kExceptionHandling); - cur_block->taken = eh_block->id; - eh_block->start_offset = cur_offset; - eh_block->predecessors.push_back(cur_block->id); - } - - if (is_throw) { - cur_block->explicit_throw = true; - if (code_ptr < code_end) { - // Force creation of new block following THROW via side-effect. - FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr, dex_pc_to_block_map); - } - if (!in_try_block) { - // Don't split a THROW that can't rethrow - we're done. - return cur_block; - } - } - - if (!build_all_edges) { - /* - * Even though there is an exception edge here, control cannot return to this - * method. Thus, for the purposes of dataflow analysis and optimization, we can - * ignore the edge. Doing this reduces compile time, and increases the scope - * of the basic-block level optimization pass. - */ - return cur_block; - } - - /* - * Split the potentially-throwing instruction into two parts. - * The first half will be a pseudo-op that captures the exception - * edges and terminates the basic block. It always falls through. - * Then, create a new basic block that begins with the throwing instruction - * (minus exceptions). Note: this new basic block must NOT be entered into - * the block_map. If the potentially-throwing instruction is the target of a - * future branch, we need to find the check psuedo half. The new - * basic block containing the work portion of the instruction should - * only be entered via fallthrough from the block containing the - * pseudo exception edge MIR. Note also that this new block is - * not automatically terminated after the work portion, and may - * contain following instructions. - * - * Note also that the dex_pc_to_block_map entry for the potentially - * throwing instruction will refer to the original basic block. - */ - BasicBlock* new_block = CreateNewBB(kDalvikByteCode); - new_block->start_offset = insn->offset; - cur_block->fall_through = new_block->id; - new_block->predecessors.push_back(cur_block->id); - MIR* new_insn = NewMIR(); - *new_insn = *insn; - insn->dalvikInsn.opcode = static_cast(kMirOpCheck); - // Associate the two halves. - insn->meta.throw_insn = new_insn; - new_block->AppendMIR(new_insn); - return new_block; -} - -/* Parse a Dex method and insert it into the MIRGraph at the current insert point. */ -void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, - InvokeType invoke_type ATTRIBUTE_UNUSED, uint16_t class_def_idx, - uint32_t method_idx, jobject class_loader, const DexFile& dex_file, - Handle dex_cache) { - current_code_item_ = code_item; - method_stack_.push_back(std::make_pair(current_method_, current_offset_)); - current_method_ = m_units_.size(); - current_offset_ = 0; - // TODO: will need to snapshot stack image and use that as the mir context identification. - m_units_.push_back(new (arena_) DexCompilationUnit( - cu_, class_loader, Runtime::Current()->GetClassLinker(), dex_file, current_code_item_, - class_def_idx, method_idx, access_flags, - cu_->compiler_driver->GetVerifiedMethod(&dex_file, method_idx), dex_cache)); - const uint16_t* code_ptr = current_code_item_->insns_; - const uint16_t* code_end = - current_code_item_->insns_ + current_code_item_->insns_size_in_code_units_; - - // TODO: need to rework expansion of block list & try_block_addr when inlining activated. - // TUNING: use better estimate of basic blocks for following resize. - block_list_.reserve(block_list_.size() + current_code_item_->insns_size_in_code_units_); - // FindBlock lookup cache. - ScopedArenaAllocator allocator(&cu_->arena_stack); - ScopedArenaVector dex_pc_to_block_map(allocator.Adapter()); - dex_pc_to_block_map.resize(current_code_item_->insns_size_in_code_units_ + - 1 /* Fall-through on last insn; dead or punt to interpreter. */); - - // TODO: replace with explicit resize routine. Using automatic extension side effect for now. - try_block_addr_->SetBit(current_code_item_->insns_size_in_code_units_); - try_block_addr_->ClearBit(current_code_item_->insns_size_in_code_units_); - - // If this is the first method, set up default entry and exit blocks. - if (current_method_ == 0) { - DCHECK(entry_block_ == nullptr); - DCHECK(exit_block_ == nullptr); - DCHECK_EQ(GetNumBlocks(), 0U); - // Use id 0 to represent a null block. - BasicBlock* null_block = CreateNewBB(kNullBlock); - DCHECK_EQ(null_block->id, NullBasicBlockId); - null_block->hidden = true; - entry_block_ = CreateNewBB(kEntryBlock); - exit_block_ = CreateNewBB(kExitBlock); - } else { - UNIMPLEMENTED(FATAL) << "Nested inlining not implemented."; - /* - * Will need to manage storage for ins & outs, push prevous state and update - * insert point. - */ - } - - /* Current block to record parsed instructions */ - BasicBlock* cur_block = CreateNewBB(kDalvikByteCode); - DCHECK_EQ(current_offset_, 0U); - cur_block->start_offset = current_offset_; - // TODO: for inlining support, insert at the insert point rather than entry block. - entry_block_->fall_through = cur_block->id; - cur_block->predecessors.push_back(entry_block_->id); - - /* Identify code range in try blocks and set up the empty catch blocks */ - ProcessTryCatchBlocks(&dex_pc_to_block_map); - - uint64_t merged_df_flags = 0u; - - /* Parse all instructions and put them into containing basic blocks */ - while (code_ptr < code_end) { - MIR *insn = NewMIR(); - insn->offset = current_offset_; - insn->m_unit_index = current_method_; - int width = ParseInsn(code_ptr, &insn->dalvikInsn); - Instruction::Code opcode = insn->dalvikInsn.opcode; - if (opcode_count_ != nullptr) { - opcode_count_[static_cast(opcode)]++; - } - - int flags = insn->dalvikInsn.FlagsOf(); - int verify_flags = Instruction::VerifyFlagsOf(insn->dalvikInsn.opcode); - - uint64_t df_flags = GetDataFlowAttributes(insn); - merged_df_flags |= df_flags; - - if (df_flags & DF_HAS_DEFS) { - def_count_ += (df_flags & DF_A_WIDE) ? 2 : 1; - } - - if (df_flags & DF_LVN) { - cur_block->use_lvn = true; // Run local value numbering on this basic block. - } - - // Check for inline data block signatures. - if (opcode == Instruction::NOP) { - // A simple NOP will have a width of 1 at this point, embedded data NOP > 1. - if ((width == 1) && ((current_offset_ & 0x1) == 0x1) && ((code_end - code_ptr) > 1)) { - // Could be an aligning nop. If an embedded data NOP follows, treat pair as single unit. - uint16_t following_raw_instruction = code_ptr[1]; - if ((following_raw_instruction == Instruction::kSparseSwitchSignature) || - (following_raw_instruction == Instruction::kPackedSwitchSignature) || - (following_raw_instruction == Instruction::kArrayDataSignature)) { - width += Instruction::At(code_ptr + 1)->SizeInCodeUnits(); - } - } - if (width == 1) { - // It is a simple nop - treat normally. - cur_block->AppendMIR(insn); - } else { - DCHECK(cur_block->fall_through == NullBasicBlockId); - DCHECK(cur_block->taken == NullBasicBlockId); - // Unreachable instruction, mark for no continuation and end basic block. - flags &= ~Instruction::kContinue; - FindBlock(current_offset_ + width, /* create */ true, - /* immed_pred_block_p */ nullptr, &dex_pc_to_block_map); - } - } else { - cur_block->AppendMIR(insn); - } - - // Associate the starting dex_pc for this opcode with its containing basic block. - dex_pc_to_block_map[insn->offset] = cur_block->id; - - code_ptr += width; - - if (flags & Instruction::kBranch) { - cur_block = ProcessCanBranch(cur_block, insn, current_offset_, - width, flags, code_ptr, code_end, &dex_pc_to_block_map); - } else if (flags & Instruction::kReturn) { - cur_block->terminated_by_return = true; - cur_block->fall_through = exit_block_->id; - exit_block_->predecessors.push_back(cur_block->id); - /* - * Terminate the current block if there are instructions - * afterwards. - */ - if (code_ptr < code_end) { - /* - * Create a fallthrough block for real instructions - * (incl. NOP). - */ - FindBlock(current_offset_ + width, /* create */ true, - /* immed_pred_block_p */ nullptr, &dex_pc_to_block_map); - } - } else if (flags & Instruction::kThrow) { - cur_block = ProcessCanThrow(cur_block, insn, current_offset_, width, flags, try_block_addr_, - code_ptr, code_end, &dex_pc_to_block_map); - } else if (flags & Instruction::kSwitch) { - cur_block = ProcessCanSwitch(cur_block, insn, current_offset_, width, - flags, &dex_pc_to_block_map); - } - if (verify_flags & Instruction::kVerifyVarArgRange || - verify_flags & Instruction::kVerifyVarArgRangeNonZero) { - /* - * The Quick backend's runtime model includes a gap between a method's - * argument ("in") vregs and the rest of its vregs. Handling a range instruction - * which spans the gap is somewhat complicated, and should not happen - * in normal usage of dx. Punt to the interpreter. - */ - int first_reg_in_range = insn->dalvikInsn.vC; - int last_reg_in_range = first_reg_in_range + insn->dalvikInsn.vA - 1; - if (IsInVReg(first_reg_in_range) != IsInVReg(last_reg_in_range)) { - punt_to_interpreter_ = true; - } - } - current_offset_ += width; - BasicBlock* next_block = FindBlock(current_offset_, /* create */ false, - /* immed_pred_block_p */ nullptr, - &dex_pc_to_block_map); - if (next_block) { - /* - * The next instruction could be the target of a previously parsed - * forward branch so a block is already created. If the current - * instruction is not an unconditional branch, connect them through - * the fall-through link. - */ - DCHECK(cur_block->fall_through == NullBasicBlockId || - GetBasicBlock(cur_block->fall_through) == next_block || - GetBasicBlock(cur_block->fall_through) == exit_block_); - - if ((cur_block->fall_through == NullBasicBlockId) && (flags & Instruction::kContinue)) { - cur_block->fall_through = next_block->id; - next_block->predecessors.push_back(cur_block->id); - } - cur_block = next_block; - } - } - merged_df_flags_ = merged_df_flags; - - if (cu_->enable_debug & (1 << kDebugDumpCFG)) { - DumpCFG("/sdcard/1_post_parse_cfg/", true); - } - - if (cu_->verbose) { - DumpMIRGraph(); - } - - // Check if there's been a fall-through out of the method code. - BasicBlockId out_bb_id = dex_pc_to_block_map[current_code_item_->insns_size_in_code_units_]; - if (UNLIKELY(out_bb_id != NullBasicBlockId)) { - // Eagerly calculate DFS order to determine if the block is dead. - DCHECK(!DfsOrdersUpToDate()); - ComputeDFSOrders(); - BasicBlock* out_bb = GetBasicBlock(out_bb_id); - DCHECK(out_bb != nullptr); - if (out_bb->block_type != kDead) { - LOG(WARNING) << "Live fall-through out of method in " << PrettyMethod(method_idx, dex_file); - SetPuntToInterpreter(true); - } - } -} - -void MIRGraph::ShowOpcodeStats() { - DCHECK(opcode_count_ != nullptr); - LOG(INFO) << "Opcode Count"; - for (int i = 0; i < kNumPackedOpcodes; i++) { - if (opcode_count_[i] != 0) { - LOG(INFO) << "-C- " << Instruction::Name(static_cast(i)) - << " " << opcode_count_[i]; - } - } -} - -uint64_t MIRGraph::GetDataFlowAttributes(Instruction::Code opcode) { - DCHECK_LT((size_t) opcode, (sizeof(oat_data_flow_attributes_) / sizeof(oat_data_flow_attributes_[0]))); - return oat_data_flow_attributes_[opcode]; -} - -uint64_t MIRGraph::GetDataFlowAttributes(MIR* mir) { - DCHECK(mir != nullptr); - Instruction::Code opcode = mir->dalvikInsn.opcode; - return GetDataFlowAttributes(opcode); -} - -// The path can easily surpass FS limits because of parameters etc. Use pathconf to get FS -// restrictions here. Note that a successful invocation will return an actual value. If the path -// is too long for some reason, the return will be ENAMETOOLONG. Then cut off part of the name. -// -// It's possible the path is not valid, or some other errors appear. In that case return false. -static bool CreateDumpFile(std::string& fname, const char* dir_prefix, NarrowDexOffset start_offset, - const char *suffix, int nr, std::string* output) { - std::string dir = StringPrintf("./%s", dir_prefix); - int64_t max_name_length = pathconf(dir.c_str(), _PC_NAME_MAX); - if (max_name_length <= 0) { - PLOG(ERROR) << "Could not get file name restrictions for " << dir; - return false; - } - - std::string name = StringPrintf("%s%x%s_%d.dot", fname.c_str(), start_offset, - suffix == nullptr ? "" : suffix, nr); - std::string fpath; - if (static_cast(name.size()) > max_name_length) { - std::string suffix_str = StringPrintf("_%d.dot", nr); - name = name.substr(0, static_cast(max_name_length) - suffix_str.size()) + suffix_str; - } - // Sanity check. - DCHECK_LE(name.size(), static_cast(max_name_length)); - - *output = StringPrintf("%s%s", dir_prefix, name.c_str()); - return true; -} - -// TODO: use a configurable base prefix, and adjust callers to supply pass name. -/* Dump the CFG into a DOT graph */ -void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suffix) { - FILE* file; - static AtomicInteger cnt(0); - - // Increment counter to get a unique file number. - cnt++; - int nr = cnt.LoadRelaxed(); - - std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file)); - ReplaceSpecialChars(fname); - std::string fpath; - if (!CreateDumpFile(fname, dir_prefix, GetBasicBlock(GetEntryBlock()->fall_through)->start_offset, - suffix, nr, &fpath)) { - LOG(ERROR) << "Could not create dump file name for " << fname; - return; - } - file = fopen(fpath.c_str(), "w"); - if (file == nullptr) { - PLOG(ERROR) << "Could not open " << fpath << " for DumpCFG."; - return; - } - fprintf(file, "digraph G {\n"); - - fprintf(file, " rankdir=TB\n"); - - int num_blocks = all_blocks ? GetNumBlocks() : num_reachable_blocks_; - int idx; - - for (idx = 0; idx < num_blocks; idx++) { - int block_idx = all_blocks ? idx : dfs_order_[idx]; - BasicBlock* bb = GetBasicBlock(block_idx); - if (bb == nullptr) continue; - if (bb->block_type == kDead) continue; - if (bb->hidden) continue; - if (bb->block_type == kEntryBlock) { - fprintf(file, " entry_%d [shape=Mdiamond];\n", bb->id); - } else if (bb->block_type == kExitBlock) { - fprintf(file, " exit_%d [shape=Mdiamond];\n", bb->id); - } else if (bb->block_type == kDalvikByteCode) { - fprintf(file, " block%04x_%d [shape=record,label = \"{ \\\n", - bb->start_offset, bb->id); - const MIR* mir; - fprintf(file, " {block id %d\\l}%s\\\n", bb->id, - bb->first_mir_insn ? " | " : " "); - for (mir = bb->first_mir_insn; mir; mir = mir->next) { - int opcode = mir->dalvikInsn.opcode; - fprintf(file, " {%04x %s %s %s %s %s %s %s %s %s\\l}%s\\\n", mir->offset, - mir->ssa_rep ? GetDalvikDisassembly(mir) : - !MIR::DecodedInstruction::IsPseudoMirOp(opcode) ? - Instruction::Name(mir->dalvikInsn.opcode) : - extended_mir_op_names_[opcode - kMirOpFirst], - (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0 ? " no_rangecheck" : " ", - (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0 ? " no_nullcheck" : " ", - (mir->optimization_flags & MIR_IGNORE_SUSPEND_CHECK) != 0 ? " no_suspendcheck" : " ", - (mir->optimization_flags & MIR_STORE_NON_TEMPORAL) != 0 ? " non_temporal" : " ", - (mir->optimization_flags & MIR_CALLEE) != 0 ? " inlined" : " ", - (mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0 ? " cl_inited" : " ", - (mir->optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0 ? " cl_in_cache" : " ", - (mir->optimization_flags & MIR_IGNORE_DIV_ZERO_CHECK) != 0 ? " no_div_check" : " ", - mir->next ? " | " : " "); - } - fprintf(file, " }\"];\n\n"); - } else if (bb->block_type == kExceptionHandling) { - char block_name[BLOCK_NAME_LEN]; - - GetBlockName(bb, block_name); - fprintf(file, " %s [shape=invhouse];\n", block_name); - } - - char block_name1[BLOCK_NAME_LEN], block_name2[BLOCK_NAME_LEN]; - - if (bb->taken != NullBasicBlockId) { - GetBlockName(bb, block_name1); - GetBlockName(GetBasicBlock(bb->taken), block_name2); - fprintf(file, " %s:s -> %s:n [style=dotted]\n", - block_name1, block_name2); - } - if (bb->fall_through != NullBasicBlockId) { - GetBlockName(bb, block_name1); - GetBlockName(GetBasicBlock(bb->fall_through), block_name2); - fprintf(file, " %s:s -> %s:n\n", block_name1, block_name2); - } - - if (bb->successor_block_list_type != kNotUsed) { - fprintf(file, " succ%04x_%d [shape=%s,label = \"{ \\\n", - bb->start_offset, bb->id, - (bb->successor_block_list_type == kCatch) ? "Mrecord" : "record"); - - int last_succ_id = static_cast(bb->successor_blocks.size() - 1u); - int succ_id = 0; - for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) { - BasicBlock* dest_block = GetBasicBlock(successor_block_info->block); - fprintf(file, " { %04x: %04x\\l}%s\\\n", - succ_id, - successor_block_info->key, - dest_block->start_offset, - (succ_id != last_succ_id) ? " | " : " "); - ++succ_id; - } - fprintf(file, " }\"];\n\n"); - - GetBlockName(bb, block_name1); - fprintf(file, " %s:s -> succ%04x_%d:n [style=dashed]\n", - block_name1, bb->start_offset, bb->id); - - // Link the successor pseudo-block with all of its potential targets. - succ_id = 0; - for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) { - BasicBlock* dest_block = GetBasicBlock(successor_block_info->block); - - GetBlockName(dest_block, block_name2); - fprintf(file, " succ%04x_%d:f%d:e -> %s:n\n", bb->start_offset, - bb->id, succ_id++, block_name2); - } - } - fprintf(file, "\n"); - - if (cu_->verbose) { - /* Display the dominator tree */ - GetBlockName(bb, block_name1); - fprintf(file, " cfg%s [label=\"%s\", shape=none];\n", - block_name1, block_name1); - if (bb->i_dom) { - GetBlockName(GetBasicBlock(bb->i_dom), block_name2); - fprintf(file, " cfg%s:s -> cfg%s:n\n\n", block_name2, block_name1); - } - } - } - fprintf(file, "}\n"); - fclose(file); -} - -/* Insert an MIR instruction to the end of a basic block. */ -void BasicBlock::AppendMIR(MIR* mir) { - // Insert it after the last MIR. - InsertMIRListAfter(last_mir_insn, mir, mir); -} - -void BasicBlock::AppendMIRList(MIR* first_list_mir, MIR* last_list_mir) { - // Insert it after the last MIR. - InsertMIRListAfter(last_mir_insn, first_list_mir, last_list_mir); -} - -void BasicBlock::AppendMIRList(const std::vector& insns) { - for (std::vector::const_iterator it = insns.begin(); it != insns.end(); it++) { - MIR* new_mir = *it; - - // Add a copy of each MIR. - InsertMIRListAfter(last_mir_insn, new_mir, new_mir); - } -} - -/* Insert a MIR instruction after the specified MIR. */ -void BasicBlock::InsertMIRAfter(MIR* current_mir, MIR* new_mir) { - InsertMIRListAfter(current_mir, new_mir, new_mir); -} - -void BasicBlock::InsertMIRListAfter(MIR* insert_after, MIR* first_list_mir, MIR* last_list_mir) { - // If no MIR, we are done. - if (first_list_mir == nullptr || last_list_mir == nullptr) { - return; - } - - // If insert_after is null, assume BB is empty. - if (insert_after == nullptr) { - first_mir_insn = first_list_mir; - last_mir_insn = last_list_mir; - last_list_mir->next = nullptr; - } else { - MIR* after_list = insert_after->next; - insert_after->next = first_list_mir; - last_list_mir->next = after_list; - if (after_list == nullptr) { - last_mir_insn = last_list_mir; - } - } - - // Set this BB to be the basic block of the MIRs. - MIR* last = last_list_mir->next; - for (MIR* mir = first_list_mir; mir != last; mir = mir->next) { - mir->bb = id; - } -} - -/* Insert an MIR instruction to the head of a basic block. */ -void BasicBlock::PrependMIR(MIR* mir) { - InsertMIRListBefore(first_mir_insn, mir, mir); -} - -void BasicBlock::PrependMIRList(MIR* first_list_mir, MIR* last_list_mir) { - // Insert it before the first MIR. - InsertMIRListBefore(first_mir_insn, first_list_mir, last_list_mir); -} - -void BasicBlock::PrependMIRList(const std::vector& to_add) { - for (std::vector::const_iterator it = to_add.begin(); it != to_add.end(); it++) { - MIR* mir = *it; - - InsertMIRListBefore(first_mir_insn, mir, mir); - } -} - -/* Insert a MIR instruction before the specified MIR. */ -void BasicBlock::InsertMIRBefore(MIR* current_mir, MIR* new_mir) { - // Insert as a single element list. - return InsertMIRListBefore(current_mir, new_mir, new_mir); -} - -MIR* BasicBlock::FindPreviousMIR(MIR* mir) { - MIR* current = first_mir_insn; - - while (current != nullptr) { - MIR* next = current->next; - - if (next == mir) { - return current; - } - - current = next; - } - - return nullptr; -} - -void BasicBlock::InsertMIRListBefore(MIR* insert_before, MIR* first_list_mir, MIR* last_list_mir) { - // If no MIR, we are done. - if (first_list_mir == nullptr || last_list_mir == nullptr) { - return; - } - - // If insert_before is null, assume BB is empty. - if (insert_before == nullptr) { - first_mir_insn = first_list_mir; - last_mir_insn = last_list_mir; - last_list_mir->next = nullptr; - } else { - if (first_mir_insn == insert_before) { - last_list_mir->next = first_mir_insn; - first_mir_insn = first_list_mir; - } else { - // Find the preceding MIR. - MIR* before_list = FindPreviousMIR(insert_before); - DCHECK(before_list != nullptr); - before_list->next = first_list_mir; - last_list_mir->next = insert_before; - } - } - - // Set this BB to be the basic block of the MIRs. - for (MIR* mir = first_list_mir; mir != last_list_mir->next; mir = mir->next) { - mir->bb = id; - } -} - -bool BasicBlock::RemoveMIR(MIR* mir) { - // Remove as a single element list. - return RemoveMIRList(mir, mir); -} - -bool BasicBlock::RemoveMIRList(MIR* first_list_mir, MIR* last_list_mir) { - if (first_list_mir == nullptr) { - return false; - } - - // Try to find the MIR. - MIR* before_list = nullptr; - MIR* after_list = nullptr; - - // If we are removing from the beginning of the MIR list. - if (first_mir_insn == first_list_mir) { - before_list = nullptr; - } else { - before_list = FindPreviousMIR(first_list_mir); - if (before_list == nullptr) { - // We did not find the mir. - return false; - } - } - - // Remove the BB information and also find the after_list. - for (MIR* mir = first_list_mir; mir != last_list_mir->next; mir = mir->next) { - mir->bb = NullBasicBlockId; - } - - after_list = last_list_mir->next; - - // If there is nothing before the list, after_list is the first_mir. - if (before_list == nullptr) { - first_mir_insn = after_list; - } else { - before_list->next = after_list; - } - - // If there is nothing after the list, before_list is last_mir. - if (after_list == nullptr) { - last_mir_insn = before_list; - } - - return true; -} - -MIR* BasicBlock::GetFirstNonPhiInsn() { - MIR* mir = first_mir_insn; - while (mir != nullptr && static_cast(mir->dalvikInsn.opcode) == kMirOpPhi) { - mir = mir->next; - } - return mir; -} - -MIR* BasicBlock::GetNextUnconditionalMir(MIRGraph* mir_graph, MIR* current) { - MIR* next_mir = nullptr; - - if (current != nullptr) { - next_mir = current->next; - } - - if (next_mir == nullptr) { - // Only look for next MIR that follows unconditionally. - if ((taken == NullBasicBlockId) && (fall_through != NullBasicBlockId)) { - next_mir = mir_graph->GetBasicBlock(fall_through)->first_mir_insn; - } - } - - return next_mir; -} - -static void FillTypeSizeString(uint32_t type_size, std::string* decoded_mir) { - DCHECK(decoded_mir != nullptr); - OpSize type = static_cast(type_size >> 16); - uint16_t vect_size = (type_size & 0xFFFF); - - // Now print the type and vector size. - std::stringstream ss; - ss << " (type:"; - ss << type; - ss << " vectsize:"; - ss << vect_size; - ss << ")"; - - decoded_mir->append(ss.str()); -} - -void MIRGraph::DisassembleExtendedInstr(const MIR* mir, std::string* decoded_mir) { - DCHECK(decoded_mir != nullptr); - int opcode = mir->dalvikInsn.opcode; - SSARepresentation* ssa_rep = mir->ssa_rep; - int defs = (ssa_rep != nullptr) ? ssa_rep->num_defs : 0; - int uses = (ssa_rep != nullptr) ? ssa_rep->num_uses : 0; - - if (opcode < kMirOpFirst) { - return; // It is not an extended instruction. - } - - decoded_mir->append(extended_mir_op_names_[opcode - kMirOpFirst]); - - switch (opcode) { - case kMirOpPhi: { - if (defs > 0 && uses > 0) { - BasicBlockId* incoming = mir->meta.phi_incoming; - decoded_mir->append(StringPrintf(" %s = (%s", - GetSSANameWithConst(ssa_rep->defs[0], true).c_str(), - GetSSANameWithConst(ssa_rep->uses[0], true).c_str())); - decoded_mir->append(StringPrintf(":%d", incoming[0])); - for (int i = 1; i < uses; i++) { - decoded_mir->append(StringPrintf(", %s:%d", GetSSANameWithConst(ssa_rep->uses[i], true).c_str(), incoming[i])); - } - decoded_mir->append(")"); - } - break; - } - case kMirOpCopy: - if (ssa_rep != nullptr) { - decoded_mir->append(" "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false)); - if (defs > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false)); - } - decoded_mir->append(" = "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[0], false)); - if (uses > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[1], false)); - } - } else { - decoded_mir->append(StringPrintf(" v%d = v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - } - break; - case kMirOpFusedCmplFloat: - case kMirOpFusedCmpgFloat: - case kMirOpFusedCmplDouble: - case kMirOpFusedCmpgDouble: - case kMirOpFusedCmpLong: - if (ssa_rep != nullptr) { - decoded_mir->append(" "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[0], false)); - for (int i = 1; i < uses; i++) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[i], false)); - } - } else { - decoded_mir->append(StringPrintf(" v%d, v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - } - break; - case kMirOpMoveVector: - decoded_mir->append(StringPrintf(" vect%d = vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedAddition: - decoded_mir->append(StringPrintf(" vect%d = vect%d + vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedMultiply: - decoded_mir->append(StringPrintf(" vect%d = vect%d * vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedSubtract: - decoded_mir->append(StringPrintf(" vect%d = vect%d - vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedAnd: - decoded_mir->append(StringPrintf(" vect%d = vect%d & vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedOr: - decoded_mir->append(StringPrintf(" vect%d = vect%d \\| vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedXor: - decoded_mir->append(StringPrintf(" vect%d = vect%d ^ vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedShiftLeft: - decoded_mir->append(StringPrintf(" vect%d = vect%d \\<\\< %d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedUnsignedShiftRight: - decoded_mir->append(StringPrintf(" vect%d = vect%d \\>\\>\\> %d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedSignedShiftRight: - decoded_mir->append(StringPrintf(" vect%d = vect%d \\>\\> %d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpConstVector: - decoded_mir->append(StringPrintf(" vect%d = %x, %x, %x, %x", mir->dalvikInsn.vA, mir->dalvikInsn.arg[0], - mir->dalvikInsn.arg[1], mir->dalvikInsn.arg[2], mir->dalvikInsn.arg[3])); - break; - case kMirOpPackedSet: - if (ssa_rep != nullptr) { - decoded_mir->append(StringPrintf(" vect%d = %s", mir->dalvikInsn.vA, - GetSSANameWithConst(ssa_rep->uses[0], false).c_str())); - if (uses > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[1], false)); - } - } else { - decoded_mir->append(StringPrintf(" vect%d = v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - } - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedAddReduce: - if (ssa_rep != nullptr) { - decoded_mir->append(" "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false)); - if (defs > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false)); - } - decoded_mir->append(StringPrintf(" = vect%d + %s", mir->dalvikInsn.vB, - GetSSANameWithConst(ssa_rep->uses[0], false).c_str())); - if (uses > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[1], false)); - } - } else { - decoded_mir->append(StringPrintf("v%d = vect%d + v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB, mir->dalvikInsn.vA)); - } - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedReduce: - if (ssa_rep != nullptr) { - decoded_mir->append(" "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false)); - if (defs > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false)); - } - decoded_mir->append(StringPrintf(" = vect%d (extr_idx:%d)", mir->dalvikInsn.vB, mir->dalvikInsn.arg[0])); - } else { - decoded_mir->append(StringPrintf(" v%d = vect%d (extr_idx:%d)", mir->dalvikInsn.vA, - mir->dalvikInsn.vB, mir->dalvikInsn.arg[0])); - } - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpReserveVectorRegisters: - case kMirOpReturnVectorRegisters: - decoded_mir->append(StringPrintf(" vect%d - vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - break; - case kMirOpMemBarrier: { - decoded_mir->append(" type:"); - std::stringstream ss; - ss << static_cast(mir->dalvikInsn.vA); - decoded_mir->append(ss.str()); - break; - } - case kMirOpPackedArrayGet: - case kMirOpPackedArrayPut: - decoded_mir->append(StringPrintf(" vect%d", mir->dalvikInsn.vA)); - if (ssa_rep != nullptr) { - decoded_mir->append(StringPrintf(", %s[%s]", - GetSSANameWithConst(ssa_rep->uses[0], false).c_str(), - GetSSANameWithConst(ssa_rep->uses[1], false).c_str())); - } else { - decoded_mir->append(StringPrintf(", v%d[v%d]", mir->dalvikInsn.vB, mir->dalvikInsn.vC)); - } - FillTypeSizeString(mir->dalvikInsn.arg[0], decoded_mir); - break; - case kMirOpMaddInt: - case kMirOpMsubInt: - case kMirOpMaddLong: - case kMirOpMsubLong: - if (ssa_rep != nullptr) { - decoded_mir->append(" "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false)); - if (defs > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false)); - } - for (int i = 0; i < uses; i++) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[i], false)); - } - } else { - decoded_mir->append(StringPrintf(" v%d, v%d, v%d, v%d", - mir->dalvikInsn.vA, mir->dalvikInsn.vB, - mir->dalvikInsn.vC, mir->dalvikInsn.arg[0])); - } - break; - default: - break; - } -} - -char* MIRGraph::GetDalvikDisassembly(const MIR* mir) { - MIR::DecodedInstruction insn = mir->dalvikInsn; - std::string str; - int flags = 0; - int opcode = insn.opcode; - char* ret; - bool nop = false; - SSARepresentation* ssa_rep = mir->ssa_rep; - Instruction::Format dalvik_format = Instruction::k10x; // Default to no-operand format. - - // Handle special cases that recover the original dalvik instruction. - if (opcode == kMirOpCheck) { - str.append(extended_mir_op_names_[opcode - kMirOpFirst]); - str.append(": "); - // Recover the original Dex instruction. - insn = mir->meta.throw_insn->dalvikInsn; - ssa_rep = mir->meta.throw_insn->ssa_rep; - opcode = insn.opcode; - } else if (opcode == kMirOpNop) { - str.append("["); - if (mir->offset < current_code_item_->insns_size_in_code_units_) { - // Recover original opcode. - insn.opcode = Instruction::At(current_code_item_->insns_ + mir->offset)->Opcode(); - opcode = insn.opcode; - } - nop = true; - } - int defs = (ssa_rep != nullptr) ? ssa_rep->num_defs : 0; - int uses = (ssa_rep != nullptr) ? ssa_rep->num_uses : 0; - - if (MIR::DecodedInstruction::IsPseudoMirOp(opcode)) { - // Note that this does not check the MIR's opcode in all cases. In cases where it - // recovered dalvik instruction, it uses opcode of that instead of the extended one. - DisassembleExtendedInstr(mir, &str); - } else { - dalvik_format = Instruction::FormatOf(insn.opcode); - flags = insn.FlagsOf(); - str.append(Instruction::Name(insn.opcode)); - - // For invokes-style formats, treat wide regs as a pair of singles. - bool show_singles = ((dalvik_format == Instruction::k35c) || - (dalvik_format == Instruction::k3rc)); - if (defs != 0) { - str.append(" "); - str.append(GetSSANameWithConst(ssa_rep->defs[0], false)); - if (defs > 1) { - str.append(", "); - str.append(GetSSANameWithConst(ssa_rep->defs[1], false)); - } - if (uses != 0) { - str.append(", "); - } - } - for (int i = 0; i < uses; i++) { - str.append(" "); - str.append(GetSSANameWithConst(ssa_rep->uses[i], show_singles)); - if (!show_singles && (reg_location_ != nullptr) && reg_location_[i].wide) { - // For the listing, skip the high sreg. - i++; - } - if (i != (uses - 1)) { - str.append(","); - } - } - - switch (dalvik_format) { - case Instruction::k11n: // Add one immediate from vB. - case Instruction::k21s: - case Instruction::k31i: - case Instruction::k21h: - str.append(StringPrintf(", #0x%x", insn.vB)); - break; - case Instruction::k51l: // Add one wide immediate. - str.append(StringPrintf(", #%" PRId64, insn.vB_wide)); - break; - case Instruction::k21c: // One register, one string/type/method index. - case Instruction::k31c: - str.append(StringPrintf(", index #0x%x", insn.vB)); - break; - case Instruction::k22c: // Two registers, one string/type/method index. - str.append(StringPrintf(", index #0x%x", insn.vC)); - break; - case Instruction::k22s: // Add one immediate from vC. - case Instruction::k22b: - str.append(StringPrintf(", #0x%x", insn.vC)); - break; - default: - // Nothing left to print. - break; - } - - if ((flags & Instruction::kBranch) != 0) { - // For branches, decode the instructions to print out the branch targets. - int offset = 0; - switch (dalvik_format) { - case Instruction::k21t: - offset = insn.vB; - break; - case Instruction::k22t: - offset = insn.vC; - break; - case Instruction::k10t: - case Instruction::k20t: - case Instruction::k30t: - offset = insn.vA; - break; - default: - LOG(FATAL) << "Unexpected branch format " << dalvik_format << " from " << insn.opcode; - break; - } - str.append(StringPrintf(", 0x%x (%c%x)", mir->offset + offset, - offset > 0 ? '+' : '-', offset > 0 ? offset : -offset)); - } - - if (nop) { - str.append("]--optimized away"); - } - } - int length = str.length() + 1; - ret = arena_->AllocArray(length, kArenaAllocDFInfo); - strncpy(ret, str.c_str(), length); - return ret; -} - -/* Turn method name into a legal Linux file name */ -void MIRGraph::ReplaceSpecialChars(std::string& str) { - static const struct { const char before; const char after; } match[] = { - {'/', '-'}, {';', '#'}, {' ', '#'}, {'$', '+'}, - {'(', '@'}, {')', '@'}, {'<', '='}, {'>', '='} - }; - for (unsigned int i = 0; i < sizeof(match)/sizeof(match[0]); i++) { - std::replace(str.begin(), str.end(), match[i].before, match[i].after); - } -} - -std::string MIRGraph::GetSSAName(int ssa_reg) { - // TODO: This value is needed for debugging. Currently, we compute this and then copy to the - // arena. We should be smarter and just place straight into the arena, or compute the - // value more lazily. - int vreg = SRegToVReg(ssa_reg); - if (vreg >= static_cast(GetFirstTempVR())) { - return StringPrintf("t%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg)); - } else { - return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg)); - } -} - -// Similar to GetSSAName, but if ssa name represents an immediate show that as well. -std::string MIRGraph::GetSSANameWithConst(int ssa_reg, bool singles_only) { - if (reg_location_ == nullptr) { - // Pre-SSA - just use the standard name. - return GetSSAName(ssa_reg); - } - if (IsConst(reg_location_[ssa_reg])) { - if (!singles_only && reg_location_[ssa_reg].wide && - !reg_location_[ssa_reg].high_word) { - return StringPrintf("v%d_%d#0x%" PRIx64, SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg), - ConstantValueWide(reg_location_[ssa_reg])); - } else { - return StringPrintf("v%d_%d#0x%x", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg), - ConstantValue(reg_location_[ssa_reg])); - } - } else { - int vreg = SRegToVReg(ssa_reg); - if (vreg >= static_cast(GetFirstTempVR())) { - return StringPrintf("t%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg)); - } else { - return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg)); - } - } -} - -void MIRGraph::GetBlockName(BasicBlock* bb, char* name) { - switch (bb->block_type) { - case kEntryBlock: - snprintf(name, BLOCK_NAME_LEN, "entry_%d", bb->id); - break; - case kExitBlock: - snprintf(name, BLOCK_NAME_LEN, "exit_%d", bb->id); - break; - case kDalvikByteCode: - snprintf(name, BLOCK_NAME_LEN, "block%04x_%d", bb->start_offset, bb->id); - break; - case kExceptionHandling: - snprintf(name, BLOCK_NAME_LEN, "exception%04x_%d", bb->start_offset, - bb->id); - break; - default: - snprintf(name, BLOCK_NAME_LEN, "_%d", bb->id); - break; - } -} - -const char* MIRGraph::GetShortyFromMethodReference(const MethodReference& target_method) { - const DexFile::MethodId& method_id = - target_method.dex_file->GetMethodId(target_method.dex_method_index); - return target_method.dex_file->GetShorty(method_id.proto_idx_); -} - -/* Debug Utility - dump a compilation unit */ -void MIRGraph::DumpMIRGraph() { - const char* block_type_names[] = { - "Null Block", - "Entry Block", - "Code Block", - "Exit Block", - "Exception Handling", - "Catch Block" - }; - - LOG(INFO) << "Compiling " << PrettyMethod(cu_->method_idx, *cu_->dex_file); - LOG(INFO) << GetInsns(0) << " insns"; - LOG(INFO) << GetNumBlocks() << " blocks in total"; - - for (BasicBlock* bb : block_list_) { - LOG(INFO) << StringPrintf("Block %d (%s) (insn %04x - %04x%s)", - bb->id, - block_type_names[bb->block_type], - bb->start_offset, - bb->last_mir_insn ? bb->last_mir_insn->offset : bb->start_offset, - bb->last_mir_insn ? "" : " empty"); - if (bb->taken != NullBasicBlockId) { - LOG(INFO) << " Taken branch: block " << bb->taken - << "(0x" << std::hex << GetBasicBlock(bb->taken)->start_offset << ")"; - } - if (bb->fall_through != NullBasicBlockId) { - LOG(INFO) << " Fallthrough : block " << bb->fall_through - << " (0x" << std::hex << GetBasicBlock(bb->fall_through)->start_offset << ")"; - } - } -} - -/* - * Build an array of location records for the incoming arguments. - * Note: one location record per word of arguments, with dummy - * high-word loc for wide arguments. Also pull up any following - * MOVE_RESULT and incorporate it into the invoke. - */ -CallInfo* MIRGraph::NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, bool is_range) { - CallInfo* info = static_cast(arena_->Alloc(sizeof(CallInfo), - kArenaAllocMisc)); - MIR* move_result_mir = FindMoveResult(bb, mir); - if (move_result_mir == nullptr) { - info->result.location = kLocInvalid; - } else { - info->result = GetRawDest(move_result_mir); - move_result_mir->dalvikInsn.opcode = static_cast(kMirOpNop); - } - info->num_arg_words = mir->ssa_rep->num_uses; - info->args = (info->num_arg_words == 0) ? nullptr : - arena_->AllocArray(info->num_arg_words, kArenaAllocMisc); - for (size_t i = 0; i < info->num_arg_words; i++) { - info->args[i] = GetRawSrc(mir, i); - } - info->opt_flags = mir->optimization_flags; - info->type = type; - info->is_range = is_range; - if (IsInstructionQuickInvoke(mir->dalvikInsn.opcode)) { - const auto& method_info = GetMethodLoweringInfo(mir); - info->method_ref = method_info.GetTargetMethod(); - } else { - info->method_ref = MethodReference(GetCurrentDexCompilationUnit()->GetDexFile(), - mir->dalvikInsn.vB); - } - info->index = mir->dalvikInsn.vB; - info->offset = mir->offset; - info->mir = mir; - return info; -} - -// Allocate a new MIR. -MIR* MIRGraph::NewMIR() { - MIR* mir = new (arena_) MIR(); - return mir; -} - -// Allocate a new basic block. -BasicBlock* MIRGraph::NewMemBB(BBType block_type, int block_id) { - BasicBlock* bb = new (arena_) BasicBlock(block_id, block_type, arena_); - - // TUNING: better estimate of the exit block predecessors? - bb->predecessors.reserve((block_type == kExitBlock) ? 2048 : 2); - block_id_map_.Put(block_id, block_id); - return bb; -} - -void MIRGraph::InitializeConstantPropagation() { - is_constant_v_ = new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false); - constant_values_ = arena_->AllocArray(GetNumSSARegs(), kArenaAllocDFInfo); -} - -void MIRGraph::InitializeMethodUses() { - // The gate starts by initializing the use counts. - int num_ssa_regs = GetNumSSARegs(); - use_counts_.clear(); - use_counts_.reserve(num_ssa_regs + 32); - use_counts_.resize(num_ssa_regs, 0u); - raw_use_counts_.clear(); - raw_use_counts_.reserve(num_ssa_regs + 32); - raw_use_counts_.resize(num_ssa_regs, 0u); -} - -void MIRGraph::SSATransformationStart() { - DCHECK(temp_scoped_alloc_.get() == nullptr); - temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_.ssa.num_vregs = GetNumOfCodeAndTempVRs(); - temp_.ssa.work_live_vregs = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.ssa.num_vregs, false); -} - -void MIRGraph::SSATransformationEnd() { - // Verify the dataflow information after the pass. - if (cu_->enable_debug & (1 << kDebugVerifyDataflow)) { - VerifyDataflow(); - } - - temp_.ssa.num_vregs = 0u; - temp_.ssa.work_live_vregs = nullptr; - DCHECK(temp_.ssa.def_block_matrix == nullptr); - temp_.ssa.phi_node_blocks = nullptr; - DCHECK(temp_scoped_alloc_.get() != nullptr); - temp_scoped_alloc_.reset(); - - // Update the maximum number of reachable blocks. - max_num_reachable_blocks_ = num_reachable_blocks_; - - // Mark MIR SSA representations as up to date. - mir_ssa_rep_up_to_date_ = true; -} - -size_t MIRGraph::GetNumDalvikInsns() const { - size_t cumulative_size = 0u; - bool counted_current_item = false; - const uint8_t size_for_null_code_item = 2u; - - for (auto it : m_units_) { - const DexFile::CodeItem* code_item = it->GetCodeItem(); - // Even if the code item is null, we still count non-zero value so that - // each m_unit is counted as having impact. - cumulative_size += (code_item == nullptr ? - size_for_null_code_item : code_item->insns_size_in_code_units_); - if (code_item == current_code_item_) { - counted_current_item = true; - } - } - - // If the current code item was not counted yet, count it now. - // This can happen for example in unit tests where some fields like m_units_ - // are not initialized. - if (counted_current_item == false) { - cumulative_size += (current_code_item_ == nullptr ? - size_for_null_code_item : current_code_item_->insns_size_in_code_units_); - } - - return cumulative_size; -} - -static BasicBlock* SelectTopologicalSortOrderFallBack( - MIRGraph* mir_graph, const ArenaBitVector* current_loop, - const ScopedArenaVector* visited_cnt_values, ScopedArenaAllocator* allocator, - ScopedArenaVector* tmp_stack) { - // No true loop head has been found but there may be true loop heads after the mess we need - // to resolve. To avoid taking one of those, pick the candidate with the highest number of - // reachable unvisited nodes. That candidate will surely be a part of a loop. - BasicBlock* fall_back = nullptr; - size_t fall_back_num_reachable = 0u; - // Reuse the same bit vector for each candidate to mark reachable unvisited blocks. - ArenaBitVector candidate_reachable(allocator, mir_graph->GetNumBlocks(), false); - AllNodesIterator iter(mir_graph); - for (BasicBlock* candidate = iter.Next(); candidate != nullptr; candidate = iter.Next()) { - if (candidate->hidden || // Hidden, or - candidate->visited || // already processed, or - (*visited_cnt_values)[candidate->id] == 0u || // no processed predecessors, or - (current_loop != nullptr && // outside current loop. - !current_loop->IsBitSet(candidate->id))) { - continue; - } - DCHECK(tmp_stack->empty()); - tmp_stack->push_back(candidate->id); - candidate_reachable.ClearAllBits(); - size_t num_reachable = 0u; - while (!tmp_stack->empty()) { - BasicBlockId current_id = tmp_stack->back(); - tmp_stack->pop_back(); - BasicBlock* current_bb = mir_graph->GetBasicBlock(current_id); - DCHECK(current_bb != nullptr); - ChildBlockIterator child_iter(current_bb, mir_graph); - BasicBlock* child_bb = child_iter.Next(); - for ( ; child_bb != nullptr; child_bb = child_iter.Next()) { - DCHECK(!child_bb->hidden); - if (child_bb->visited || // Already processed, or - (current_loop != nullptr && // outside current loop. - !current_loop->IsBitSet(child_bb->id))) { - continue; - } - if (!candidate_reachable.IsBitSet(child_bb->id)) { - candidate_reachable.SetBit(child_bb->id); - tmp_stack->push_back(child_bb->id); - num_reachable += 1u; - } - } - } - if (fall_back_num_reachable < num_reachable) { - fall_back_num_reachable = num_reachable; - fall_back = candidate; - } - } - return fall_back; -} - -// Compute from which unvisited blocks is bb_id reachable through unvisited blocks. -static void ComputeUnvisitedReachableFrom(MIRGraph* mir_graph, BasicBlockId bb_id, - ArenaBitVector* reachable, - ScopedArenaVector* tmp_stack) { - // NOTE: Loop heads indicated by the "visited" flag. - DCHECK(tmp_stack->empty()); - reachable->ClearAllBits(); - tmp_stack->push_back(bb_id); - while (!tmp_stack->empty()) { - BasicBlockId current_id = tmp_stack->back(); - tmp_stack->pop_back(); - BasicBlock* current_bb = mir_graph->GetBasicBlock(current_id); - DCHECK(current_bb != nullptr); - for (BasicBlockId pred_id : current_bb->predecessors) { - BasicBlock* pred_bb = mir_graph->GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - if (!pred_bb->visited && !reachable->IsBitSet(pred_bb->id)) { - reachable->SetBit(pred_bb->id); - tmp_stack->push_back(pred_bb->id); - } - } - } -} - -void MIRGraph::ComputeTopologicalSortOrder() { - ScopedArenaAllocator allocator(&cu_->arena_stack); - unsigned int num_blocks = GetNumBlocks(); - - ScopedArenaQueue q(allocator.Adapter()); - ScopedArenaVector visited_cnt_values(num_blocks, 0u, allocator.Adapter()); - ScopedArenaVector loop_head_stack(allocator.Adapter()); - size_t max_nested_loops = 0u; - ArenaBitVector loop_exit_blocks(&allocator, num_blocks, false); - loop_exit_blocks.ClearAllBits(); - - // Count the number of blocks to process and add the entry block(s). - unsigned int num_blocks_to_process = 0u; - for (BasicBlock* bb : block_list_) { - if (bb->hidden == true) { - continue; - } - - num_blocks_to_process += 1u; - - if (bb->predecessors.size() == 0u) { - // Add entry block to the queue. - q.push(bb); - } - } - - // Clear the topological order arrays. - topological_order_.clear(); - topological_order_.reserve(num_blocks); - topological_order_loop_ends_.clear(); - topological_order_loop_ends_.resize(num_blocks, 0u); - topological_order_indexes_.clear(); - topological_order_indexes_.resize(num_blocks, static_cast(-1)); - - // Mark all blocks as unvisited. - ClearAllVisitedFlags(); - - // For loop heads, keep track from which blocks they are reachable not going through other - // loop heads. Other loop heads are excluded to detect the heads of nested loops. The children - // in this set go into the loop body, the other children are jumping over the loop. - ScopedArenaVector loop_head_reachable_from(allocator.Adapter()); - loop_head_reachable_from.resize(num_blocks, nullptr); - // Reuse the same temp stack whenever calculating a loop_head_reachable_from[loop_head_id]. - ScopedArenaVector tmp_stack(allocator.Adapter()); - - while (num_blocks_to_process != 0u) { - BasicBlock* bb = nullptr; - if (!q.empty()) { - num_blocks_to_process -= 1u; - // Get top. - bb = q.front(); - q.pop(); - if (bb->visited) { - // Loop head: it was already processed, mark end and copy exit blocks to the queue. - DCHECK(q.empty()) << PrettyMethod(cu_->method_idx, *cu_->dex_file); - uint16_t idx = static_cast(topological_order_.size()); - topological_order_loop_ends_[topological_order_indexes_[bb->id]] = idx; - DCHECK_EQ(loop_head_stack.back(), bb->id); - loop_head_stack.pop_back(); - ArenaBitVector* reachable = - loop_head_stack.empty() ? nullptr : loop_head_reachable_from[loop_head_stack.back()]; - for (BasicBlockId candidate_id : loop_exit_blocks.Indexes()) { - if (reachable == nullptr || reachable->IsBitSet(candidate_id)) { - q.push(GetBasicBlock(candidate_id)); - // NOTE: The BitVectorSet::IndexIterator will not check the pointed-to bit again, - // so clearing the bit has no effect on the iterator. - loop_exit_blocks.ClearBit(candidate_id); - } - } - continue; - } - } else { - // Find the new loop head. - AllNodesIterator iter(this); - while (true) { - BasicBlock* candidate = iter.Next(); - if (candidate == nullptr) { - // We did not find a true loop head, fall back to a reachable block in any loop. - ArenaBitVector* current_loop = - loop_head_stack.empty() ? nullptr : loop_head_reachable_from[loop_head_stack.back()]; - bb = SelectTopologicalSortOrderFallBack(this, current_loop, &visited_cnt_values, - &allocator, &tmp_stack); - DCHECK(bb != nullptr) << PrettyMethod(cu_->method_idx, *cu_->dex_file); - if (kIsDebugBuild && cu_->dex_file != nullptr) { - LOG(INFO) << "Topological sort order: Using fall-back in " - << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " BB #" << bb->id - << " @0x" << std::hex << bb->start_offset - << ", num_blocks = " << std::dec << num_blocks; - } - break; - } - if (candidate->hidden || // Hidden, or - candidate->visited || // already processed, or - visited_cnt_values[candidate->id] == 0u || // no processed predecessors, or - (!loop_head_stack.empty() && // outside current loop. - !loop_head_reachable_from[loop_head_stack.back()]->IsBitSet(candidate->id))) { - continue; - } - - for (BasicBlockId pred_id : candidate->predecessors) { - BasicBlock* pred_bb = GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - if (pred_bb != candidate && !pred_bb->visited && - !pred_bb->dominators->IsBitSet(candidate->id)) { - candidate = nullptr; // Set candidate to null to indicate failure. - break; - } - } - if (candidate != nullptr) { - bb = candidate; - break; - } - } - // Compute blocks from which the loop head is reachable and process those blocks first. - ArenaBitVector* reachable = - new (&allocator) ArenaBitVector(&allocator, num_blocks, false); - loop_head_reachable_from[bb->id] = reachable; - ComputeUnvisitedReachableFrom(this, bb->id, reachable, &tmp_stack); - // Now mark as loop head. (Even if it's only a fall back when we don't find a true loop.) - loop_head_stack.push_back(bb->id); - max_nested_loops = std::max(max_nested_loops, loop_head_stack.size()); - } - - DCHECK_EQ(bb->hidden, false); - DCHECK_EQ(bb->visited, false); - bb->visited = true; - bb->nesting_depth = loop_head_stack.size(); - - // Now add the basic block. - uint16_t idx = static_cast(topological_order_.size()); - topological_order_indexes_[bb->id] = idx; - topological_order_.push_back(bb->id); - - // Update visited_cnt_values for children. - ChildBlockIterator succIter(bb, this); - BasicBlock* successor = succIter.Next(); - for ( ; successor != nullptr; successor = succIter.Next()) { - if (successor->hidden) { - continue; - } - - // One more predecessor was visited. - visited_cnt_values[successor->id] += 1u; - if (visited_cnt_values[successor->id] == successor->predecessors.size()) { - if (loop_head_stack.empty() || - loop_head_reachable_from[loop_head_stack.back()]->IsBitSet(successor->id)) { - q.push(successor); - } else { - DCHECK(!loop_exit_blocks.IsBitSet(successor->id)); - loop_exit_blocks.SetBit(successor->id); - } - } - } - } - - // Prepare the loop head stack for iteration. - topological_order_loop_head_stack_.clear(); - topological_order_loop_head_stack_.reserve(max_nested_loops); - max_nested_loops_ = max_nested_loops; - topological_order_up_to_date_ = true; -} - -bool BasicBlock::IsExceptionBlock() const { - if (block_type == kExceptionHandling) { - return true; - } - return false; -} - -ChildBlockIterator::ChildBlockIterator(BasicBlock* bb, MIRGraph* mir_graph) - : basic_block_(bb), mir_graph_(mir_graph), visited_fallthrough_(false), - visited_taken_(false), have_successors_(false) { - // Check if we actually do have successors. - if (basic_block_ != 0 && basic_block_->successor_block_list_type != kNotUsed) { - have_successors_ = true; - successor_iter_ = basic_block_->successor_blocks.cbegin(); - } -} - -BasicBlock* ChildBlockIterator::Next() { - // We check if we have a basic block. If we don't we cannot get next child. - if (basic_block_ == nullptr) { - return nullptr; - } - - // If we haven't visited fallthrough, return that. - if (visited_fallthrough_ == false) { - visited_fallthrough_ = true; - - BasicBlock* result = mir_graph_->GetBasicBlock(basic_block_->fall_through); - if (result != nullptr) { - return result; - } - } - - // If we haven't visited taken, return that. - if (visited_taken_ == false) { - visited_taken_ = true; - - BasicBlock* result = mir_graph_->GetBasicBlock(basic_block_->taken); - if (result != nullptr) { - return result; - } - } - - // We visited both taken and fallthrough. Now check if we have successors we need to visit. - if (have_successors_ == true) { - // Get information about next successor block. - auto end = basic_block_->successor_blocks.cend(); - while (successor_iter_ != end) { - SuccessorBlockInfo* successor_block_info = *successor_iter_; - ++successor_iter_; - // If block was replaced by zero block, take next one. - if (successor_block_info->block != NullBasicBlockId) { - return mir_graph_->GetBasicBlock(successor_block_info->block); - } - } - } - - // We do not have anything. - return nullptr; -} - -BasicBlock* BasicBlock::Copy(CompilationUnit* c_unit) { - MIRGraph* mir_graph = c_unit->mir_graph.get(); - return Copy(mir_graph); -} - -BasicBlock* BasicBlock::Copy(MIRGraph* mir_graph) { - BasicBlock* result_bb = mir_graph->CreateNewBB(block_type); - - // We don't do a memcpy style copy here because it would lead to a lot of things - // to clean up. Let us do it by hand instead. - // Copy in taken and fallthrough. - result_bb->fall_through = fall_through; - result_bb->taken = taken; - - // Copy successor links if needed. - ArenaAllocator* arena = mir_graph->GetArena(); - - result_bb->successor_block_list_type = successor_block_list_type; - if (result_bb->successor_block_list_type != kNotUsed) { - result_bb->successor_blocks.reserve(successor_blocks.size()); - for (SuccessorBlockInfo* sbi_old : successor_blocks) { - SuccessorBlockInfo* sbi_new = static_cast( - arena->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); - memcpy(sbi_new, sbi_old, sizeof(SuccessorBlockInfo)); - result_bb->successor_blocks.push_back(sbi_new); - } - } - - // Copy offset, method. - result_bb->start_offset = start_offset; - - // Now copy instructions. - for (MIR* mir = first_mir_insn; mir != 0; mir = mir->next) { - // Get a copy first. - MIR* copy = mir->Copy(mir_graph); - - // Append it. - result_bb->AppendMIR(copy); - } - - return result_bb; -} - -MIR* MIR::Copy(MIRGraph* mir_graph) { - MIR* res = mir_graph->NewMIR(); - *res = *this; - - // Remove links - res->next = nullptr; - res->bb = NullBasicBlockId; - res->ssa_rep = nullptr; - - return res; -} - -MIR* MIR::Copy(CompilationUnit* c_unit) { - return Copy(c_unit->mir_graph.get()); -} - -uint32_t SSARepresentation::GetStartUseIndex(Instruction::Code opcode) { - // Default result. - int res = 0; - - // We are basically setting the iputs to their igets counterparts. - switch (opcode) { - case Instruction::IPUT: - case Instruction::IPUT_OBJECT: - case Instruction::IPUT_BOOLEAN: - case Instruction::IPUT_BYTE: - case Instruction::IPUT_CHAR: - case Instruction::IPUT_SHORT: - case Instruction::IPUT_QUICK: - case Instruction::IPUT_OBJECT_QUICK: - case Instruction::IPUT_BOOLEAN_QUICK: - case Instruction::IPUT_BYTE_QUICK: - case Instruction::IPUT_CHAR_QUICK: - case Instruction::IPUT_SHORT_QUICK: - case Instruction::APUT: - case Instruction::APUT_OBJECT: - case Instruction::APUT_BOOLEAN: - case Instruction::APUT_BYTE: - case Instruction::APUT_CHAR: - case Instruction::APUT_SHORT: - case Instruction::SPUT: - case Instruction::SPUT_OBJECT: - case Instruction::SPUT_BOOLEAN: - case Instruction::SPUT_BYTE: - case Instruction::SPUT_CHAR: - case Instruction::SPUT_SHORT: - // Skip the VR containing what to store. - res = 1; - break; - case Instruction::IPUT_WIDE: - case Instruction::IPUT_WIDE_QUICK: - case Instruction::APUT_WIDE: - case Instruction::SPUT_WIDE: - // Skip the two VRs containing what to store. - res = 2; - break; - default: - // Do nothing in the general case. - break; - } - - return res; -} - -/** - * @brief Given a decoded instruction, it checks whether the instruction - * sets a constant and if it does, more information is provided about the - * constant being set. - * @param ptr_value pointer to a 64-bit holder for the constant. - * @param wide Updated by function whether a wide constant is being set by bytecode. - * @return Returns false if the decoded instruction does not represent a constant bytecode. - */ -bool MIR::DecodedInstruction::GetConstant(int64_t* ptr_value, bool* wide) const { - bool sets_const = true; - int64_t value = vB; - - DCHECK(ptr_value != nullptr); - DCHECK(wide != nullptr); - - switch (opcode) { - case Instruction::CONST_4: - case Instruction::CONST_16: - case Instruction::CONST: - *wide = false; - value <<= 32; // In order to get the sign extend. - value >>= 32; - break; - case Instruction::CONST_HIGH16: - *wide = false; - value <<= 48; // In order to get the sign extend. - value >>= 32; - break; - case Instruction::CONST_WIDE_16: - case Instruction::CONST_WIDE_32: - *wide = true; - value <<= 32; // In order to get the sign extend. - value >>= 32; - break; - case Instruction::CONST_WIDE: - *wide = true; - value = vB_wide; - break; - case Instruction::CONST_WIDE_HIGH16: - *wide = true; - value <<= 48; // In order to get the sign extend. - break; - default: - sets_const = false; - break; - } - - if (sets_const) { - *ptr_value = value; - } - - return sets_const; -} - -void BasicBlock::ResetOptimizationFlags(uint16_t reset_flags) { - // Reset flags for all MIRs in bb. - for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { - mir->optimization_flags &= (~reset_flags); - } -} - -void BasicBlock::Kill(MIRGraph* mir_graph) { - for (BasicBlockId pred_id : predecessors) { - BasicBlock* pred_bb = mir_graph->GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - - // Sadly we have to go through the children by hand here. - pred_bb->ReplaceChild(id, NullBasicBlockId); - } - predecessors.clear(); - - // Mark as dead and hidden. - block_type = kDead; - hidden = true; - - // Detach it from its MIRs so we don't generate code for them. Also detached MIRs - // are updated to know that they no longer have a parent. - for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { - mir->bb = NullBasicBlockId; - } - first_mir_insn = nullptr; - last_mir_insn = nullptr; - - data_flow_info = nullptr; - - // Erase this bb from all children's predecessors and kill unreachable children. - ChildBlockIterator iter(this, mir_graph); - for (BasicBlock* succ_bb = iter.Next(); succ_bb != nullptr; succ_bb = iter.Next()) { - succ_bb->ErasePredecessor(id); - } - - // Remove links to children. - fall_through = NullBasicBlockId; - taken = NullBasicBlockId; - successor_block_list_type = kNotUsed; - - if (kIsDebugBuild) { - if (catch_entry) { - DCHECK_EQ(mir_graph->catches_.count(start_offset), 1u); - mir_graph->catches_.erase(start_offset); - } - } -} - -bool BasicBlock::IsSSALiveOut(const CompilationUnit* c_unit, int ssa_reg) { - // In order to determine if the ssa reg is live out, we scan all the MIRs. We remember - // the last SSA number of the same dalvik register. At the end, if it is different than ssa_reg, - // then it is not live out of this BB. - int dalvik_reg = c_unit->mir_graph->SRegToVReg(ssa_reg); - - int last_ssa_reg = -1; - - // Walk through the MIRs backwards. - for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { - // Get ssa rep. - SSARepresentation *ssa_rep = mir->ssa_rep; - - // Go through the defines for this MIR. - for (int i = 0; i < ssa_rep->num_defs; i++) { - DCHECK(ssa_rep->defs != nullptr); - - // Get the ssa reg. - int def_ssa_reg = ssa_rep->defs[i]; - - // Get dalvik reg. - int def_dalvik_reg = c_unit->mir_graph->SRegToVReg(def_ssa_reg); - - // Compare dalvik regs. - if (dalvik_reg == def_dalvik_reg) { - // We found a def of the register that we are being asked about. - // Remember it. - last_ssa_reg = def_ssa_reg; - } - } - } - - if (last_ssa_reg == -1) { - // If we get to this point we couldn't find a define of register user asked about. - // Let's assume the user knows what he's doing so we can be safe and say that if we - // couldn't find a def, it is live out. - return true; - } - - // If it is not -1, we found a match, is it ssa_reg? - return (ssa_reg == last_ssa_reg); -} - -bool BasicBlock::ReplaceChild(BasicBlockId old_bb, BasicBlockId new_bb) { - // We need to check taken, fall_through, and successor_blocks to replace. - bool found = false; - if (taken == old_bb) { - taken = new_bb; - found = true; - } - - if (fall_through == old_bb) { - fall_through = new_bb; - found = true; - } - - if (successor_block_list_type != kNotUsed) { - for (SuccessorBlockInfo* successor_block_info : successor_blocks) { - if (successor_block_info->block == old_bb) { - successor_block_info->block = new_bb; - found = true; - } - } - } - - return found; -} - -void BasicBlock::ErasePredecessor(BasicBlockId old_pred) { - auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred); - DCHECK(pos != predecessors.end()); - // It's faster to move the back() to *pos than erase(pos). - *pos = predecessors.back(); - predecessors.pop_back(); - size_t idx = std::distance(predecessors.begin(), pos); - for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { - if (static_cast(mir->dalvikInsn.opcode) != kMirOpPhi) { - break; - } - DCHECK_EQ(mir->ssa_rep->num_uses - 1u, predecessors.size()); - DCHECK_EQ(mir->meta.phi_incoming[idx], old_pred); - mir->meta.phi_incoming[idx] = mir->meta.phi_incoming[predecessors.size()]; - mir->ssa_rep->uses[idx] = mir->ssa_rep->uses[predecessors.size()]; - mir->ssa_rep->num_uses = predecessors.size(); - } -} - -void BasicBlock::UpdatePredecessor(BasicBlockId old_pred, BasicBlockId new_pred) { - DCHECK_NE(new_pred, NullBasicBlockId); - auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred); - DCHECK(pos != predecessors.end()); - *pos = new_pred; - size_t idx = std::distance(predecessors.begin(), pos); - for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { - if (static_cast(mir->dalvikInsn.opcode) != kMirOpPhi) { - break; - } - DCHECK_EQ(mir->meta.phi_incoming[idx], old_pred); - mir->meta.phi_incoming[idx] = new_pred; - } -} - -// Create a new basic block with block_id as num_blocks_ that is -// post-incremented. -BasicBlock* MIRGraph::CreateNewBB(BBType block_type) { - BasicBlockId id = static_cast(block_list_.size()); - BasicBlock* res = NewMemBB(block_type, id); - block_list_.push_back(res); - return res; -} - -void MIRGraph::CalculateBasicBlockInformation(const PassManager* const post_opt_pass_manager) { - /* Create the pass driver and launch it */ - PassDriverMEPostOpt driver(post_opt_pass_manager, cu_); - driver.Launch(); -} - -int MIR::DecodedInstruction::FlagsOf() const { - // Calculate new index. - int idx = static_cast(opcode) - kNumPackedOpcodes; - - // Check if it is an extended or not. - if (idx < 0) { - return Instruction::FlagsOf(opcode); - } - - // For extended, we use a switch. - switch (static_cast(opcode)) { - case kMirOpPhi: - return Instruction::kContinue; - case kMirOpCopy: - return Instruction::kContinue; - case kMirOpFusedCmplFloat: - return Instruction::kContinue | Instruction::kBranch; - case kMirOpFusedCmpgFloat: - return Instruction::kContinue | Instruction::kBranch; - case kMirOpFusedCmplDouble: - return Instruction::kContinue | Instruction::kBranch; - case kMirOpFusedCmpgDouble: - return Instruction::kContinue | Instruction::kBranch; - case kMirOpFusedCmpLong: - return Instruction::kContinue | Instruction::kBranch; - case kMirOpNop: - return Instruction::kContinue; - case kMirOpNullCheck: - return Instruction::kContinue | Instruction::kThrow; - case kMirOpRangeCheck: - return Instruction::kContinue | Instruction::kThrow; - case kMirOpDivZeroCheck: - return Instruction::kContinue | Instruction::kThrow; - case kMirOpCheck: - return Instruction::kContinue | Instruction::kThrow; - case kMirOpSelect: - return Instruction::kContinue; - case kMirOpConstVector: - return Instruction::kContinue; - case kMirOpMoveVector: - return Instruction::kContinue; - case kMirOpPackedMultiply: - return Instruction::kContinue; - case kMirOpPackedAddition: - return Instruction::kContinue; - case kMirOpPackedSubtract: - return Instruction::kContinue; - case kMirOpPackedShiftLeft: - return Instruction::kContinue; - case kMirOpPackedSignedShiftRight: - return Instruction::kContinue; - case kMirOpPackedUnsignedShiftRight: - return Instruction::kContinue; - case kMirOpPackedAnd: - return Instruction::kContinue; - case kMirOpPackedOr: - return Instruction::kContinue; - case kMirOpPackedXor: - return Instruction::kContinue; - case kMirOpPackedAddReduce: - return Instruction::kContinue; - case kMirOpPackedReduce: - return Instruction::kContinue; - case kMirOpPackedSet: - return Instruction::kContinue; - case kMirOpReserveVectorRegisters: - return Instruction::kContinue; - case kMirOpReturnVectorRegisters: - return Instruction::kContinue; - case kMirOpMemBarrier: - return Instruction::kContinue; - case kMirOpPackedArrayGet: - return Instruction::kContinue | Instruction::kThrow; - case kMirOpPackedArrayPut: - return Instruction::kContinue | Instruction::kThrow; - case kMirOpMaddInt: - case kMirOpMsubInt: - case kMirOpMaddLong: - case kMirOpMsubLong: - return Instruction::kContinue; - default: - LOG(WARNING) << "ExtendedFlagsOf: Unhandled case: " << static_cast (opcode); - return 0; - } -} - -const uint16_t* MIRGraph::GetInsns(int m_unit_index) const { - return m_units_[m_unit_index]->GetCodeItem()->insns_; -} - -void MIRGraph::SetPuntToInterpreter(bool val) { - punt_to_interpreter_ = val; - if (val) { - // Disable all subsequent optimizations. They may not be safe to run. (For example, - // LVN/GVN assumes there are no conflicts found by the type inference pass.) - cu_->disable_opt = ~static_castdisable_opt)>(0); - } -} - -} // namespace art diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h deleted file mode 100644 index 3191fe9d57c651a9cca5c756baad3327d66c00a4..0000000000000000000000000000000000000000 --- a/compiler/dex/mir_graph.h +++ /dev/null @@ -1,1488 +0,0 @@ -/* - * Copyright (C) 2013 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_MIR_GRAPH_H_ -#define ART_COMPILER_DEX_MIR_GRAPH_H_ - -#include - -#include "base/arena_bit_vector.h" -#include "base/arena_containers.h" -#include "base/bit_utils.h" -#include "base/scoped_arena_containers.h" -#include "dex_file.h" -#include "dex_instruction.h" -#include "dex_types.h" -#include "invoke_type.h" -#include "mir_field_info.h" -#include "mir_method_info.h" -#include "reg_location.h" -#include "reg_storage.h" - -namespace art { - -struct CompilationUnit; -class DexCompilationUnit; -class DexFileMethodInliner; -class GlobalValueNumbering; -class GvnDeadCodeElimination; -class PassManager; -class TypeInference; - -// Forward declaration. -class MIRGraph; - -enum DataFlowAttributePos { - kUA = 0, - kUB, - kUC, - kAWide, - kBWide, - kCWide, - kDA, - kIsMove, - kSetsConst, - kFormat35c, - kFormat3rc, - kFormatExtended, // Extended format for extended MIRs. - kNullCheckA, // Null check of A. - kNullCheckB, // Null check of B. - kNullCheckOut0, // Null check out outgoing arg0. - kDstNonNull, // May assume dst is non-null. - kRetNonNull, // May assume retval is non-null. - kNullTransferSrc0, // Object copy src[0] -> dst. - kNullTransferSrcN, // Phi null check state transfer. - kRangeCheckC, // Range check of C. - kCheckCastA, // Check cast of A. - kFPA, - kFPB, - kFPC, - kCoreA, - kCoreB, - kCoreC, - kRefA, - kRefB, - kRefC, - kSameTypeAB, // A and B have the same type but it can be core/ref/fp (IF_cc). - kUsesMethodStar, // Implicit use of Method*. - kUsesIField, // Accesses an instance field (IGET/IPUT). - kUsesSField, // Accesses a static field (SGET/SPUT). - kCanInitializeClass, // Can trigger class initialization (SGET/SPUT/INVOKE_STATIC). - kDoLVN, // Worth computing local value numbers. -}; - -#define DF_NOP UINT64_C(0) -#define DF_UA (UINT64_C(1) << kUA) -#define DF_UB (UINT64_C(1) << kUB) -#define DF_UC (UINT64_C(1) << kUC) -#define DF_A_WIDE (UINT64_C(1) << kAWide) -#define DF_B_WIDE (UINT64_C(1) << kBWide) -#define DF_C_WIDE (UINT64_C(1) << kCWide) -#define DF_DA (UINT64_C(1) << kDA) -#define DF_IS_MOVE (UINT64_C(1) << kIsMove) -#define DF_SETS_CONST (UINT64_C(1) << kSetsConst) -#define DF_FORMAT_35C (UINT64_C(1) << kFormat35c) -#define DF_FORMAT_3RC (UINT64_C(1) << kFormat3rc) -#define DF_FORMAT_EXTENDED (UINT64_C(1) << kFormatExtended) -#define DF_NULL_CHK_A (UINT64_C(1) << kNullCheckA) -#define DF_NULL_CHK_B (UINT64_C(1) << kNullCheckB) -#define DF_NULL_CHK_OUT0 (UINT64_C(1) << kNullCheckOut0) -#define DF_NON_NULL_DST (UINT64_C(1) << kDstNonNull) -#define DF_NON_NULL_RET (UINT64_C(1) << kRetNonNull) -#define DF_NULL_TRANSFER_0 (UINT64_C(1) << kNullTransferSrc0) -#define DF_NULL_TRANSFER_N (UINT64_C(1) << kNullTransferSrcN) -#define DF_RANGE_CHK_C (UINT64_C(1) << kRangeCheckC) -#define DF_CHK_CAST (UINT64_C(1) << kCheckCastA) -#define DF_FP_A (UINT64_C(1) << kFPA) -#define DF_FP_B (UINT64_C(1) << kFPB) -#define DF_FP_C (UINT64_C(1) << kFPC) -#define DF_CORE_A (UINT64_C(1) << kCoreA) -#define DF_CORE_B (UINT64_C(1) << kCoreB) -#define DF_CORE_C (UINT64_C(1) << kCoreC) -#define DF_REF_A (UINT64_C(1) << kRefA) -#define DF_REF_B (UINT64_C(1) << kRefB) -#define DF_REF_C (UINT64_C(1) << kRefC) -#define DF_SAME_TYPE_AB (UINT64_C(1) << kSameTypeAB) -#define DF_UMS (UINT64_C(1) << kUsesMethodStar) -#define DF_IFIELD (UINT64_C(1) << kUsesIField) -#define DF_SFIELD (UINT64_C(1) << kUsesSField) -#define DF_CLINIT (UINT64_C(1) << kCanInitializeClass) -#define DF_LVN (UINT64_C(1) << kDoLVN) - -#define DF_HAS_USES (DF_UA | DF_UB | DF_UC) - -#define DF_HAS_DEFS (DF_DA) - -#define DF_HAS_NULL_CHKS (DF_NULL_CHK_A | \ - DF_NULL_CHK_B | \ - DF_NULL_CHK_OUT0) - -#define DF_HAS_RANGE_CHKS (DF_RANGE_CHK_C) - -#define DF_HAS_NR_CHKS (DF_HAS_NULL_CHKS | \ - DF_HAS_RANGE_CHKS) - -#define DF_A_IS_REG (DF_UA | DF_DA) -#define DF_B_IS_REG (DF_UB) -#define DF_C_IS_REG (DF_UC) -#define DF_USES_FP (DF_FP_A | DF_FP_B | DF_FP_C) -#define DF_NULL_TRANSFER (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N) -#define DF_IS_INVOKE (DF_FORMAT_35C | DF_FORMAT_3RC) - -enum OatMethodAttributes { - kIsLeaf, // Method is leaf. -}; - -#define METHOD_IS_LEAF (1 << kIsLeaf) - -// Minimum field size to contain Dalvik v_reg number. -#define VREG_NUM_WIDTH 16 - -#define INVALID_VREG (0xFFFFU) -#define INVALID_OFFSET (0xDEADF00FU) - -#define MIR_IGNORE_NULL_CHECK (1 << kMIRIgnoreNullCheck) -#define MIR_IGNORE_RANGE_CHECK (1 << kMIRIgnoreRangeCheck) -#define MIR_IGNORE_CHECK_CAST (1 << kMIRIgnoreCheckCast) -#define MIR_STORE_NON_NULL_VALUE (1 << kMIRStoreNonNullValue) -#define MIR_CLASS_IS_INITIALIZED (1 << kMIRClassIsInitialized) -#define MIR_CLASS_IS_IN_DEX_CACHE (1 << kMIRClassIsInDexCache) -#define MIR_IGNORE_DIV_ZERO_CHECK (1 << kMirIgnoreDivZeroCheck) -#define MIR_INLINED (1 << kMIRInlined) -#define MIR_INLINED_PRED (1 << kMIRInlinedPred) -#define MIR_CALLEE (1 << kMIRCallee) -#define MIR_IGNORE_SUSPEND_CHECK (1 << kMIRIgnoreSuspendCheck) -#define MIR_DUP (1 << kMIRDup) -#define MIR_MARK (1 << kMIRMark) -#define MIR_STORE_NON_TEMPORAL (1 << kMIRStoreNonTemporal) - -#define BLOCK_NAME_LEN 80 - -typedef uint16_t BasicBlockId; -static const BasicBlockId NullBasicBlockId = 0; - -// Leaf optimization is basically the removal of suspend checks from leaf methods. -// This is incompatible with SuspendCheckElimination (SCE) which eliminates suspend -// checks from loops that call any non-intrinsic method, since a loop that calls -// only a leaf method would end up without any suspend checks at all. So turning -// this on automatically disables the SCE in MIRGraph::EliminateSuspendChecksGate(). -// -// Since the Optimizing compiler is actually applying the same optimization, Quick -// must not run SCE anyway, so we enable this optimization as a way to disable SCE -// while keeping a consistent behavior across the backends, b/22657404. -static constexpr bool kLeafOptimization = true; - -/* - * In general, vreg/sreg describe Dalvik registers that originated with dx. However, - * it is useful to have compiler-generated temporary registers and have them treated - * in the same manner as dx-generated virtual registers. This struct records the SSA - * name of compiler-introduced temporaries. - */ -struct CompilerTemp { - int32_t v_reg; // Virtual register number for temporary. - int32_t s_reg_low; // SSA name for low Dalvik word. -}; - -enum CompilerTempType { - kCompilerTempVR, // A virtual register temporary. - kCompilerTempSpecialMethodPtr, // Temporary that keeps track of current method pointer. - kCompilerTempBackend, // Temporary that is used by backend. -}; - -// When debug option enabled, records effectiveness of null and range check elimination. -struct Checkstats { - int32_t null_checks; - int32_t null_checks_eliminated; - int32_t range_checks; - int32_t range_checks_eliminated; -}; - -// Dataflow attributes of a basic block. -struct BasicBlockDataFlow { - ArenaBitVector* use_v; - ArenaBitVector* def_v; - ArenaBitVector* live_in_v; - int32_t* vreg_to_ssa_map_exit; -}; - -/* - * Normalized use/def for a MIR operation using SSA names rather than vregs. Note that - * uses/defs retain the Dalvik convention that long operations operate on a pair of 32-bit - * vregs. For example, "ADD_LONG v0, v2, v3" would have 2 defs (v0/v1) and 4 uses (v2/v3, v4/v5). - * Following SSA renaming, this is the primary struct used by code generators to locate - * operand and result registers. This is a somewhat confusing and unhelpful convention that - * we may want to revisit in the future. - * - * TODO: - * 1. Add accessors for uses/defs and make data private - * 2. Change fp_use/fp_def to a bit array (could help memory usage) - * 3. Combine array storage into internal array and handled via accessors from 1. - */ -struct SSARepresentation { - int32_t* uses; - int32_t* defs; - uint16_t num_uses_allocated; - uint16_t num_defs_allocated; - uint16_t num_uses; - uint16_t num_defs; - - static uint32_t GetStartUseIndex(Instruction::Code opcode); -}; - -/* - * The Midlevel Intermediate Representation node, which may be largely considered a - * wrapper around a Dalvik byte code. - */ -class MIR : public ArenaObject { - public: - /* - * TODO: remove embedded DecodedInstruction to save space, keeping only opcode. Recover - * additional fields on as-needed basis. Question: how to support MIR Pseudo-ops; probably - * need to carry aux data pointer. - */ - struct DecodedInstruction { - uint32_t vA; - uint32_t vB; - uint64_t vB_wide; /* for k51l */ - uint32_t vC; - uint32_t arg[5]; /* vC/D/E/F/G in invoke or filled-new-array */ - Instruction::Code opcode; - - DecodedInstruction() : vA(0), vB(0), vB_wide(0), vC(0), opcode(Instruction::NOP) { - } - - /* - * Given a decoded instruction representing a const bytecode, it updates - * the out arguments with proper values as dictated by the constant bytecode. - */ - bool GetConstant(int64_t* ptr_value, bool* wide) const; - - static bool IsPseudoMirOp(Instruction::Code opcode) { - return static_cast(opcode) >= static_cast(kMirOpFirst); - } - - static bool IsPseudoMirOp(int opcode) { - return opcode >= static_cast(kMirOpFirst); - } - - bool IsInvoke() const { - return ((FlagsOf() & Instruction::kInvoke) == Instruction::kInvoke); - } - - bool IsStore() const { - return ((FlagsOf() & Instruction::kStore) == Instruction::kStore); - } - - bool IsLoad() const { - return ((FlagsOf() & Instruction::kLoad) == Instruction::kLoad); - } - - bool IsConditionalBranch() const { - return (FlagsOf() == (Instruction::kContinue | Instruction::kBranch)); - } - - /** - * @brief Is the register C component of the decoded instruction a constant? - */ - bool IsCFieldOrConstant() const { - return ((FlagsOf() & Instruction::kRegCFieldOrConstant) == Instruction::kRegCFieldOrConstant); - } - - /** - * @brief Is the register C component of the decoded instruction a constant? - */ - bool IsBFieldOrConstant() const { - return ((FlagsOf() & Instruction::kRegBFieldOrConstant) == Instruction::kRegBFieldOrConstant); - } - - bool IsCast() const { - return ((FlagsOf() & Instruction::kCast) == Instruction::kCast); - } - - /** - * @brief Does the instruction clobber memory? - * @details Clobber means that the instruction changes the memory not in a punctual way. - * Therefore any supposition on memory aliasing or memory contents should be disregarded - * when crossing such an instruction. - */ - bool Clobbers() const { - return ((FlagsOf() & Instruction::kClobber) == Instruction::kClobber); - } - - bool IsLinear() const { - return (FlagsOf() & (Instruction::kAdd | Instruction::kSubtract)) != 0; - } - - int FlagsOf() const; - } dalvikInsn; - - NarrowDexOffset offset; // Offset of the instruction in code units. - uint16_t optimization_flags; - int16_t m_unit_index; // From which method was this MIR included - BasicBlockId bb; - MIR* next; - SSARepresentation* ssa_rep; - union { - // Incoming edges for phi node. - BasicBlockId* phi_incoming; - // Establish link from check instruction (kMirOpCheck) to the actual throwing instruction. - MIR* throw_insn; - // Branch condition for fused cmp or select. - ConditionCode ccode; - // IGET/IPUT lowering info index, points to MIRGraph::ifield_lowering_infos_. Due to limit on - // the number of code points (64K) and size of IGET/IPUT insn (2), this will never exceed 32K. - uint32_t ifield_lowering_info; - // SGET/SPUT lowering info index, points to MIRGraph::sfield_lowering_infos_. Due to limit on - // the number of code points (64K) and size of SGET/SPUT insn (2), this will never exceed 32K. - uint32_t sfield_lowering_info; - // INVOKE data index, points to MIRGraph::method_lowering_infos_. Also used for inlined - // CONST and MOVE insn (with MIR_CALLEE) to remember the invoke for type inference. - uint32_t method_lowering_info; - } meta; - - MIR() : offset(0), optimization_flags(0), m_unit_index(0), bb(NullBasicBlockId), - next(nullptr), ssa_rep(nullptr) { - memset(&meta, 0, sizeof(meta)); - } - - uint32_t GetStartUseIndex() const { - return SSARepresentation::GetStartUseIndex(dalvikInsn.opcode); - } - - MIR* Copy(CompilationUnit *c_unit); - MIR* Copy(MIRGraph* mir_Graph); -}; - -struct SuccessorBlockInfo; - -class BasicBlock : public DeletableArenaObject { - public: - BasicBlock(BasicBlockId block_id, BBType type, ArenaAllocator* allocator) - : id(block_id), - dfs_id(), start_offset(), fall_through(), taken(), i_dom(), nesting_depth(), - block_type(type), - successor_block_list_type(kNotUsed), - visited(), hidden(), catch_entry(), explicit_throw(), conditional_branch(), - terminated_by_return(), dominates_return(), use_lvn(), first_mir_insn(), - last_mir_insn(), data_flow_info(), dominators(), i_dominated(), dom_frontier(), - predecessors(allocator->Adapter(kArenaAllocBBPredecessors)), - successor_blocks(allocator->Adapter(kArenaAllocSuccessors)) { - } - BasicBlockId id; - BasicBlockId dfs_id; - NarrowDexOffset start_offset; // Offset in code units. - BasicBlockId fall_through; - BasicBlockId taken; - BasicBlockId i_dom; // Immediate dominator. - uint16_t nesting_depth; - BBType block_type:4; - BlockListType successor_block_list_type:4; - bool visited:1; - bool hidden:1; - bool catch_entry:1; - bool explicit_throw:1; - bool conditional_branch:1; - bool terminated_by_return:1; // Block ends with a Dalvik return opcode. - bool dominates_return:1; // Is a member of return extended basic block. - bool use_lvn:1; // Run local value numbering on this block. - MIR* first_mir_insn; - MIR* last_mir_insn; - BasicBlockDataFlow* data_flow_info; - ArenaBitVector* dominators; - ArenaBitVector* i_dominated; // Set nodes being immediately dominated. - ArenaBitVector* dom_frontier; // Dominance frontier. - ArenaVector predecessors; - ArenaVector successor_blocks; - - void AppendMIR(MIR* mir); - void AppendMIRList(MIR* first_list_mir, MIR* last_list_mir); - void AppendMIRList(const std::vector& insns); - void PrependMIR(MIR* mir); - void PrependMIRList(MIR* first_list_mir, MIR* last_list_mir); - void PrependMIRList(const std::vector& to_add); - void InsertMIRAfter(MIR* current_mir, MIR* new_mir); - void InsertMIRListAfter(MIR* insert_after, MIR* first_list_mir, MIR* last_list_mir); - MIR* FindPreviousMIR(MIR* mir); - void InsertMIRBefore(MIR* insert_before, MIR* list); - void InsertMIRListBefore(MIR* insert_before, MIR* first_list_mir, MIR* last_list_mir); - bool RemoveMIR(MIR* mir); - bool RemoveMIRList(MIR* first_list_mir, MIR* last_list_mir); - - BasicBlock* Copy(CompilationUnit* c_unit); - BasicBlock* Copy(MIRGraph* mir_graph); - - /** - * @brief Reset the optimization_flags field of each MIR. - */ - void ResetOptimizationFlags(uint16_t reset_flags); - - /** - * @brief Kill the BasicBlock. - * @details Unlink predecessors and successors, remove all MIRs, set the block type to kDead - * and set hidden to true. - */ - void Kill(MIRGraph* mir_graph); - - /** - * @brief Is ssa_reg the last SSA definition of that VR in the block? - */ - bool IsSSALiveOut(const CompilationUnit* c_unit, int ssa_reg); - - /** - * @brief Replace the edge going to old_bb to now go towards new_bb. - */ - bool ReplaceChild(BasicBlockId old_bb, BasicBlockId new_bb); - - /** - * @brief Erase the predecessor old_pred. - */ - void ErasePredecessor(BasicBlockId old_pred); - - /** - * @brief Update the predecessor array from old_pred to new_pred. - */ - void UpdatePredecessor(BasicBlockId old_pred, BasicBlockId new_pred); - - /** - * @brief Return first non-Phi insn. - */ - MIR* GetFirstNonPhiInsn(); - - /** - * @brief Checks whether the block ends with if-nez or if-eqz that branches to - * the given successor only if the register in not zero. - */ - bool BranchesToSuccessorOnlyIfNotZero(BasicBlockId succ_id) const { - if (last_mir_insn == nullptr) { - return false; - } - Instruction::Code last_opcode = last_mir_insn->dalvikInsn.opcode; - return ((last_opcode == Instruction::IF_EQZ && fall_through == succ_id) || - (last_opcode == Instruction::IF_NEZ && taken == succ_id)) && - // Make sure the other successor isn't the same (empty if), b/21614284. - (fall_through != taken); - } - - /** - * @brief Used to obtain the next MIR that follows unconditionally. - * @details The implementation does not guarantee that a MIR does not - * follow even if this method returns nullptr. - * @param mir_graph the MIRGraph. - * @param current The MIR for which to find an unconditional follower. - * @return Returns the following MIR if one can be found. - */ - MIR* GetNextUnconditionalMir(MIRGraph* mir_graph, MIR* current); - bool IsExceptionBlock() const; - - private: - DISALLOW_COPY_AND_ASSIGN(BasicBlock); -}; - -/* - * The "blocks" field in "successor_block_list" points to an array of elements with the type - * "SuccessorBlockInfo". For catch blocks, key is type index for the exception. For switch - * blocks, key is the case value. - */ -struct SuccessorBlockInfo { - BasicBlockId block; - int key; -}; - -/** - * @class ChildBlockIterator - * @brief Enable an easy iteration of the children. - */ -class ChildBlockIterator { - public: - /** - * @brief Constructs a child iterator. - * @param bb The basic whose children we need to iterate through. - * @param mir_graph The MIRGraph used to get the basic block during iteration. - */ - ChildBlockIterator(BasicBlock* bb, MIRGraph* mir_graph); - BasicBlock* Next(); - - private: - BasicBlock* basic_block_; - MIRGraph* mir_graph_; - bool visited_fallthrough_; - bool visited_taken_; - bool have_successors_; - ArenaVector::const_iterator successor_iter_; -}; - -/* - * Collection of information describing an invoke, and the destination of - * the subsequent MOVE_RESULT (if applicable). Collected as a unit to enable - * more efficient invoke code generation. - */ -struct CallInfo { - size_t num_arg_words; // Note: word count, not arg count. - RegLocation* args; // One for each word of arguments. - RegLocation result; // Eventual target of MOVE_RESULT. - int opt_flags; - InvokeType type; - uint32_t dex_idx; - MethodReference method_ref; - uint32_t index; // Method idx for invokes, type idx for FilledNewArray. - uintptr_t direct_code; - uintptr_t direct_method; - RegLocation target; // Target of following move_result. - bool skip_this; - bool is_range; - DexOffset offset; // Offset in code units. - MIR* mir; - int32_t string_init_offset; -}; - - -const RegLocation bad_loc = {kLocDalvikFrame, 0, 0, 0, 0, 0, 0, 0, 0, RegStorage(), INVALID_SREG, - INVALID_SREG}; - -class MIRGraph { - public: - MIRGraph(CompilationUnit* cu, ArenaAllocator* arena); - virtual ~MIRGraph(); - - /* - * Examine the graph to determine whether it's worthwile to spend the time compiling - * this method. - */ - bool SkipCompilation(std::string* skip_message); - - /* - * Parse dex method and add MIR at current insert point. Returns id (which is - * actually the index of the method in the m_units_ array). - */ - void InlineMethod(const DexFile::CodeItem* code_item, - uint32_t access_flags, - InvokeType invoke_type, - uint16_t class_def_idx, - uint32_t method_idx, - jobject class_loader, - const DexFile& dex_file, - Handle dex_cache); - - /* Find existing block */ - BasicBlock* FindBlock(DexOffset code_offset, - ScopedArenaVector* dex_pc_to_block_map) { - return FindBlock(code_offset, false, nullptr, dex_pc_to_block_map); - } - - const uint16_t* GetCurrentInsns() const { - return current_code_item_->insns_; - } - - /** - * @brief Used to obtain the raw dex bytecode instruction pointer. - * @param m_unit_index The method index in MIRGraph (caused by having multiple methods). - * This is guaranteed to contain index 0 which is the base method being compiled. - * @return Returns the raw instruction pointer. - */ - const uint16_t* GetInsns(int m_unit_index) const; - - /** - * @brief Used to obtain the raw data table. - * @param mir sparse switch, packed switch, of fill-array-data - * @param table_offset The table offset from start of method. - * @return Returns the raw table pointer. - */ - const uint16_t* GetTable(MIR* mir, uint32_t table_offset) const { - return GetInsns(mir->m_unit_index) + mir->offset + static_cast(table_offset); - } - - unsigned int GetNumBlocks() const { - return block_list_.size(); - } - - /** - * @brief Provides the total size in code units of all instructions in MIRGraph. - * @details Includes the sizes of all methods in compilation unit. - * @return Returns the cumulative sum of all insn sizes (in code units). - */ - size_t GetNumDalvikInsns() const; - - ArenaBitVector* GetTryBlockAddr() const { - return try_block_addr_; - } - - BasicBlock* GetEntryBlock() const { - return entry_block_; - } - - BasicBlock* GetExitBlock() const { - return exit_block_; - } - - BasicBlock* GetBasicBlock(unsigned int block_id) const { - DCHECK_LT(block_id, block_list_.size()); // NOTE: NullBasicBlockId is 0. - return (block_id == NullBasicBlockId) ? nullptr : block_list_[block_id]; - } - - size_t GetBasicBlockListCount() const { - return block_list_.size(); - } - - const ArenaVector& GetBlockList() { - return block_list_; - } - - const ArenaVector& GetDfsOrder() { - return dfs_order_; - } - - const ArenaVector& GetDfsPostOrder() { - return dfs_post_order_; - } - - const ArenaVector& GetDomPostOrder() { - return dom_post_order_traversal_; - } - - int GetDefCount() const { - return def_count_; - } - - ArenaAllocator* GetArena() const { - return arena_; - } - - void EnableOpcodeCounting() { - opcode_count_ = arena_->AllocArray(kNumPackedOpcodes, kArenaAllocMisc); - } - - void ShowOpcodeStats(); - - DexCompilationUnit* GetCurrentDexCompilationUnit() const { - return m_units_[current_method_]; - } - - /** - * @brief Dump a CFG into a dot file format. - * @param dir_prefix the directory the file will be created in. - * @param all_blocks does the dumper use all the basic blocks or use the reachable blocks. - * @param suffix does the filename require a suffix or not (default = nullptr). - */ - void DumpCFG(const char* dir_prefix, bool all_blocks, const char* suffix = nullptr); - - bool HasCheckCast() const { - return (merged_df_flags_ & DF_CHK_CAST) != 0u; - } - - bool HasFieldAccess() const { - return (merged_df_flags_ & (DF_IFIELD | DF_SFIELD)) != 0u; - } - - bool HasStaticFieldAccess() const { - return (merged_df_flags_ & DF_SFIELD) != 0u; - } - - bool HasInvokes() const { - // NOTE: These formats include the rare filled-new-array/range. - return (merged_df_flags_ & (DF_FORMAT_35C | DF_FORMAT_3RC)) != 0u; - } - - void DoCacheFieldLoweringInfo(); - - const MirIFieldLoweringInfo& GetIFieldLoweringInfo(MIR* mir) const { - return GetIFieldLoweringInfo(mir->meta.ifield_lowering_info); - } - - const MirIFieldLoweringInfo& GetIFieldLoweringInfo(uint32_t lowering_info) const { - DCHECK_LT(lowering_info, ifield_lowering_infos_.size()); - return ifield_lowering_infos_[lowering_info]; - } - - size_t GetIFieldLoweringInfoCount() const { - return ifield_lowering_infos_.size(); - } - - const MirSFieldLoweringInfo& GetSFieldLoweringInfo(MIR* mir) const { - return GetSFieldLoweringInfo(mir->meta.sfield_lowering_info); - } - - const MirSFieldLoweringInfo& GetSFieldLoweringInfo(uint32_t lowering_info) const { - DCHECK_LT(lowering_info, sfield_lowering_infos_.size()); - return sfield_lowering_infos_[lowering_info]; - } - - size_t GetSFieldLoweringInfoCount() const { - return sfield_lowering_infos_.size(); - } - - void DoCacheMethodLoweringInfo(); - - const MirMethodLoweringInfo& GetMethodLoweringInfo(MIR* mir) const { - return GetMethodLoweringInfo(mir->meta.method_lowering_info); - } - - const MirMethodLoweringInfo& GetMethodLoweringInfo(uint32_t lowering_info) const { - DCHECK_LT(lowering_info, method_lowering_infos_.size()); - return method_lowering_infos_[lowering_info]; - } - - size_t GetMethodLoweringInfoCount() const { - return method_lowering_infos_.size(); - } - - void ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke, MIR* iget_or_iput); - - void InitRegLocations(); - - void RemapRegLocations(); - - void DumpRegLocTable(RegLocation* table, int count); - - void BasicBlockOptimizationStart(); - void BasicBlockOptimization(); - void BasicBlockOptimizationEnd(); - - void StringChange(); - - const ArenaVector& GetTopologicalSortOrder() { - DCHECK(!topological_order_.empty()); - return topological_order_; - } - - const ArenaVector& GetTopologicalSortOrderLoopEnds() { - DCHECK(!topological_order_loop_ends_.empty()); - return topological_order_loop_ends_; - } - - const ArenaVector& GetTopologicalSortOrderIndexes() { - DCHECK(!topological_order_indexes_.empty()); - return topological_order_indexes_; - } - - ArenaVector>* GetTopologicalSortOrderLoopHeadStack() { - DCHECK(!topological_order_.empty()); // Checking the main array, not the stack. - return &topological_order_loop_head_stack_; - } - - size_t GetMaxNestedLoops() const { - return max_nested_loops_; - } - - bool IsLoopHead(BasicBlockId bb_id) { - return topological_order_loop_ends_[topological_order_indexes_[bb_id]] != 0u; - } - - bool IsConst(int32_t s_reg) const { - return is_constant_v_->IsBitSet(s_reg); - } - - bool IsConst(RegLocation loc) const { - return loc.orig_sreg < 0 ? false : IsConst(loc.orig_sreg); - } - - int32_t ConstantValue(RegLocation loc) const { - DCHECK(IsConst(loc)); - return constant_values_[loc.orig_sreg]; - } - - int32_t ConstantValue(int32_t s_reg) const { - DCHECK(IsConst(s_reg)); - return constant_values_[s_reg]; - } - - /** - * @brief Used to obtain 64-bit value of a pair of ssa registers. - * @param s_reg_low The ssa register representing the low bits. - * @param s_reg_high The ssa register representing the high bits. - * @return Retusn the 64-bit constant value. - */ - int64_t ConstantValueWide(int32_t s_reg_low, int32_t s_reg_high) const { - DCHECK(IsConst(s_reg_low)); - DCHECK(IsConst(s_reg_high)); - return (static_cast(constant_values_[s_reg_high]) << 32) | - Low32Bits(static_cast(constant_values_[s_reg_low])); - } - - int64_t ConstantValueWide(RegLocation loc) const { - DCHECK(IsConst(loc)); - DCHECK(!loc.high_word); // Do not allow asking for the high partner. - DCHECK_LT(loc.orig_sreg + 1, GetNumSSARegs()); - return (static_cast(constant_values_[loc.orig_sreg + 1]) << 32) | - Low32Bits(static_cast(constant_values_[loc.orig_sreg])); - } - - /** - * @brief Used to mark ssa register as being constant. - * @param ssa_reg The ssa register. - * @param value The constant value of ssa register. - */ - void SetConstant(int32_t ssa_reg, int32_t value); - - /** - * @brief Used to mark ssa register and its wide counter-part as being constant. - * @param ssa_reg The ssa register. - * @param value The 64-bit constant value of ssa register and its pair. - */ - void SetConstantWide(int32_t ssa_reg, int64_t value); - - bool IsConstantNullRef(RegLocation loc) const { - return loc.ref && loc.is_const && (ConstantValue(loc) == 0); - } - - int GetNumSSARegs() const { - return num_ssa_regs_; - } - - void SetNumSSARegs(int new_num) { - /* - * TODO: It's theoretically possible to exceed 32767, though any cases which did - * would be filtered out with current settings. When orig_sreg field is removed - * from RegLocation, expand s_reg_low to handle all possible cases and remove DCHECK(). - */ - CHECK_EQ(new_num, static_cast(new_num)); - num_ssa_regs_ = new_num; - } - - unsigned int GetNumReachableBlocks() const { - return num_reachable_blocks_; - } - - uint32_t GetUseCount(int sreg) const { - DCHECK_LT(static_cast(sreg), use_counts_.size()); - return use_counts_[sreg]; - } - - uint32_t GetRawUseCount(int sreg) const { - DCHECK_LT(static_cast(sreg), raw_use_counts_.size()); - return raw_use_counts_[sreg]; - } - - int GetSSASubscript(int ssa_reg) const { - DCHECK_LT(static_cast(ssa_reg), ssa_subscripts_.size()); - return ssa_subscripts_[ssa_reg]; - } - - RegLocation GetRawSrc(MIR* mir, int num) { - DCHECK(num < mir->ssa_rep->num_uses); - RegLocation res = reg_location_[mir->ssa_rep->uses[num]]; - return res; - } - - RegLocation GetRawDest(MIR* mir) { - DCHECK_GT(mir->ssa_rep->num_defs, 0); - RegLocation res = reg_location_[mir->ssa_rep->defs[0]]; - return res; - } - - RegLocation GetDest(MIR* mir) { - RegLocation res = GetRawDest(mir); - DCHECK(!res.wide); - return res; - } - - RegLocation GetSrc(MIR* mir, int num) { - RegLocation res = GetRawSrc(mir, num); - DCHECK(!res.wide); - return res; - } - - RegLocation GetDestWide(MIR* mir) { - RegLocation res = GetRawDest(mir); - DCHECK(res.wide); - return res; - } - - RegLocation GetSrcWide(MIR* mir, int low) { - RegLocation res = GetRawSrc(mir, low); - DCHECK(res.wide); - return res; - } - - RegLocation GetBadLoc() { - return bad_loc; - } - - int GetMethodSReg() const { - return method_sreg_; - } - - /** - * @brief Used to obtain the number of compiler temporaries being used. - * @return Returns the number of compiler temporaries. - */ - size_t GetNumUsedCompilerTemps() const { - // Assume that the special temps will always be used. - return GetNumNonSpecialCompilerTemps() + max_available_special_compiler_temps_; - } - - /** - * @brief Used to obtain number of bytes needed for special temps. - * @details This space is always needed because temps have special location on stack. - * @return Returns number of bytes for the special temps. - */ - size_t GetNumBytesForSpecialTemps() const; - - /** - * @brief Used by backend as a hint for maximum number of bytes for non-special temps. - * @details Returns 4 bytes for each temp because that is the maximum amount needed - * for storing each temp. The BE could be smarter though and allocate a smaller - * spill region. - * @return Returns the maximum number of bytes needed for non-special temps. - */ - size_t GetMaximumBytesForNonSpecialTemps() const { - return GetNumNonSpecialCompilerTemps() * sizeof(uint32_t); - } - - /** - * @brief Used to obtain the number of non-special compiler temporaries being used. - * @return Returns the number of non-special compiler temporaries. - */ - size_t GetNumNonSpecialCompilerTemps() const { - return num_non_special_compiler_temps_; - } - - /** - * @brief Used to set the total number of available non-special compiler temporaries. - * @details Can fail setting the new max if there are more temps being used than the new_max. - * @param new_max The new maximum number of non-special compiler temporaries. - * @return Returns true if the max was set and false if failed to set. - */ - bool SetMaxAvailableNonSpecialCompilerTemps(size_t new_max) { - // Make sure that enough temps still exist for backend and also that the - // new max can still keep around all of the already requested temps. - if (new_max < (GetNumNonSpecialCompilerTemps() + reserved_temps_for_backend_)) { - return false; - } else { - max_available_non_special_compiler_temps_ = new_max; - return true; - } - } - - /** - * @brief Provides the number of non-special compiler temps available for use by ME. - * @details Even if this returns zero, special compiler temps are guaranteed to be available. - * Additionally, this makes sure to not use any temps reserved for BE only. - * @return Returns the number of available temps. - */ - size_t GetNumAvailableVRTemps(); - - /** - * @brief Used to obtain the maximum number of compiler temporaries that can be requested. - * @return Returns the maximum number of compiler temporaries, whether used or not. - */ - size_t GetMaxPossibleCompilerTemps() const { - return max_available_special_compiler_temps_ + max_available_non_special_compiler_temps_; - } - - /** - * @brief Used to signal that the compiler temps have been committed. - * @details This should be used once the number of temps can no longer change, - * such as after frame size is committed and cannot be changed. - */ - void CommitCompilerTemps() { - compiler_temps_committed_ = true; - } - - /** - * @brief Used to obtain a new unique compiler temporary. - * @details Two things are done for convenience when allocating a new compiler - * temporary. The ssa register is automatically requested and the information - * about reg location is filled. This helps when the temp is requested post - * ssa initialization, such as when temps are requested by the backend. - * @warning If the temp requested will be used for ME and have multiple versions, - * the sreg provided by the temp will be invalidated on next ssa recalculation. - * @param ct_type Type of compiler temporary requested. - * @param wide Whether we should allocate a wide temporary. - * @return Returns the newly created compiler temporary. - */ - CompilerTemp* GetNewCompilerTemp(CompilerTempType ct_type, bool wide); - - /** - * @brief Used to remove last created compiler temporary when it's not needed. - * @param temp the temporary to remove. - */ - void RemoveLastCompilerTemp(CompilerTempType ct_type, bool wide, CompilerTemp* temp); - - bool MethodIsLeaf() { - return attributes_ & METHOD_IS_LEAF; - } - - RegLocation GetRegLocation(int index) { - DCHECK((index >= 0) && (index < num_ssa_regs_)); - return reg_location_[index]; - } - - RegLocation GetMethodLoc() { - return reg_location_[method_sreg_]; - } - - bool IsBackEdge(BasicBlock* branch_bb, BasicBlockId target_bb_id) { - DCHECK_NE(target_bb_id, NullBasicBlockId); - DCHECK_LT(target_bb_id, topological_order_indexes_.size()); - DCHECK_LT(branch_bb->id, topological_order_indexes_.size()); - return topological_order_indexes_[target_bb_id] <= topological_order_indexes_[branch_bb->id]; - } - - bool IsSuspendCheckEdge(BasicBlock* branch_bb, BasicBlockId target_bb_id) { - if (!IsBackEdge(branch_bb, target_bb_id)) { - return false; - } - if (suspend_checks_in_loops_ == nullptr) { - // We didn't run suspend check elimination. - return true; - } - uint16_t target_depth = GetBasicBlock(target_bb_id)->nesting_depth; - return (suspend_checks_in_loops_[branch_bb->id] & (1u << (target_depth - 1u))) == 0; - } - - void CountBranch(DexOffset target_offset) { - if (target_offset <= current_offset_) { - backward_branches_++; - } else { - forward_branches_++; - } - } - - int GetBranchCount() { - return backward_branches_ + forward_branches_; - } - - // Is this vreg in the in set? - bool IsInVReg(uint32_t vreg) { - return (vreg >= GetFirstInVR()) && (vreg < GetFirstTempVR()); - } - - uint32_t GetNumOfCodeVRs() const { - return current_code_item_->registers_size_; - } - - uint32_t GetNumOfCodeAndTempVRs() const { - // Include all of the possible temps so that no structures overflow when initialized. - return GetNumOfCodeVRs() + GetMaxPossibleCompilerTemps(); - } - - uint32_t GetNumOfLocalCodeVRs() const { - // This also refers to the first "in" VR. - return GetNumOfCodeVRs() - current_code_item_->ins_size_; - } - - uint32_t GetNumOfInVRs() const { - return current_code_item_->ins_size_; - } - - uint32_t GetNumOfOutVRs() const { - return current_code_item_->outs_size_; - } - - uint32_t GetFirstInVR() const { - return GetNumOfLocalCodeVRs(); - } - - uint32_t GetFirstTempVR() const { - // Temp VRs immediately follow code VRs. - return GetNumOfCodeVRs(); - } - - uint32_t GetFirstSpecialTempVR() const { - // Special temps appear first in the ordering before non special temps. - return GetFirstTempVR(); - } - - uint32_t GetFirstNonSpecialTempVR() const { - // We always leave space for all the special temps before the non-special ones. - return GetFirstSpecialTempVR() + max_available_special_compiler_temps_; - } - - bool HasTryCatchBlocks() const { - return current_code_item_->tries_size_ != 0; - } - - void DumpCheckStats(); - MIR* FindMoveResult(BasicBlock* bb, MIR* mir); - - /* Return the base virtual register for a SSA name */ - int SRegToVReg(int ssa_reg) const { - return ssa_base_vregs_[ssa_reg]; - } - - void VerifyDataflow(); - void CheckForDominanceFrontier(BasicBlock* dom_bb, const BasicBlock* succ_bb); - bool EliminateNullChecksGate(); - bool EliminateNullChecks(BasicBlock* bb); - void EliminateNullChecksEnd(); - void InferTypesStart(); - bool InferTypes(BasicBlock* bb); - void InferTypesEnd(); - bool EliminateClassInitChecksGate(); - bool EliminateClassInitChecks(BasicBlock* bb); - void EliminateClassInitChecksEnd(); - bool ApplyGlobalValueNumberingGate(); - bool ApplyGlobalValueNumbering(BasicBlock* bb); - void ApplyGlobalValueNumberingEnd(); - bool EliminateDeadCodeGate(); - bool EliminateDeadCode(BasicBlock* bb); - void EliminateDeadCodeEnd(); - void GlobalValueNumberingCleanup(); - bool EliminateSuspendChecksGate(); - bool EliminateSuspendChecks(BasicBlock* bb); - - uint16_t GetGvnIFieldId(MIR* mir) const { - DCHECK(IsInstructionIGetOrIPut(mir->dalvikInsn.opcode)); - DCHECK_LT(mir->meta.ifield_lowering_info, ifield_lowering_infos_.size()); - DCHECK(temp_.gvn.ifield_ids != nullptr); - return temp_.gvn.ifield_ids[mir->meta.ifield_lowering_info]; - } - - uint16_t GetGvnSFieldId(MIR* mir) const { - DCHECK(IsInstructionSGetOrSPut(mir->dalvikInsn.opcode)); - DCHECK_LT(mir->meta.sfield_lowering_info, sfield_lowering_infos_.size()); - DCHECK(temp_.gvn.sfield_ids != nullptr); - return temp_.gvn.sfield_ids[mir->meta.sfield_lowering_info]; - } - - bool PuntToInterpreter() { - return punt_to_interpreter_; - } - - void SetPuntToInterpreter(bool val); - - void DisassembleExtendedInstr(const MIR* mir, std::string* decoded_mir); - char* GetDalvikDisassembly(const MIR* mir); - void ReplaceSpecialChars(std::string& str); - std::string GetSSAName(int ssa_reg); - std::string GetSSANameWithConst(int ssa_reg, bool singles_only); - void GetBlockName(BasicBlock* bb, char* name); - const char* GetShortyFromMethodReference(const MethodReference& target_method); - void DumpMIRGraph(); - CallInfo* NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, bool is_range); - BasicBlock* NewMemBB(BBType block_type, int block_id); - MIR* NewMIR(); - MIR* AdvanceMIR(BasicBlock** p_bb, MIR* mir); - BasicBlock* NextDominatedBlock(BasicBlock* bb); - bool LayoutBlocks(BasicBlock* bb); - void ComputeTopologicalSortOrder(); - BasicBlock* CreateNewBB(BBType block_type); - - bool InlineSpecialMethodsGate(); - void InlineSpecialMethodsStart(); - void InlineSpecialMethods(BasicBlock* bb); - void InlineSpecialMethodsEnd(); - - /** - * @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 SSATransformationStart(); - - /** - * @brief Insert a the operands for the Phi nodes. - * @param bb the considered BasicBlock. - * @return true - */ - bool InsertPhiNodeOperands(BasicBlock* bb); - - /** - * @brief Perform the cleanup after the SSA Transformation. - */ - void SSATransformationEnd(); - - /** - * @brief Perform constant propagation on a BasicBlock. - * @param bb the considered BasicBlock. - */ - void DoConstantPropagation(BasicBlock* bb); - - /** - * @brief Get use count weight for a given block. - * @param bb the BasicBlock. - */ - uint32_t GetUseCountWeight(BasicBlock* bb) const; - - /** - * @brief Count the uses in the BasicBlock - * @param bb the BasicBlock - */ - void CountUses(BasicBlock* bb); - - static uint64_t GetDataFlowAttributes(Instruction::Code opcode); - static uint64_t GetDataFlowAttributes(MIR* mir); - - /** - * @brief Combine BasicBlocks - * @param the BasicBlock we are considering - */ - void CombineBlocks(BasicBlock* bb); - - void ClearAllVisitedFlags(); - - void AllocateSSAUseData(MIR *mir, int num_uses); - void AllocateSSADefData(MIR *mir, int num_defs); - void CalculateBasicBlockInformation(const PassManager* const post_opt); - void ComputeDFSOrders(); - void ComputeDefBlockMatrix(); - void ComputeDominators(); - void CompilerInitializeSSAConversion(); - virtual void InitializeBasicBlockDataFlow(); - void FindPhiNodeBlocks(); - void DoDFSPreOrderSSARename(BasicBlock* block); - - bool DfsOrdersUpToDate() const { - return dfs_orders_up_to_date_; - } - - bool DominationUpToDate() const { - return domination_up_to_date_; - } - - bool MirSsaRepUpToDate() const { - return mir_ssa_rep_up_to_date_; - } - - bool TopologicalOrderUpToDate() const { - return topological_order_up_to_date_; - } - - /* - * 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. - */ - std::set catches_; - - // TODO: make these private. - RegLocation* reg_location_; // Map SSA names to location. - ArenaSafeMap block_id_map_; // Block collapse lookup cache. - - static const char* extended_mir_op_names_[kMirOpLast - kMirOpFirst]; - - void HandleSSADef(int* defs, int dalvik_reg, int reg_index); - - protected: - int FindCommonParent(int block1, int block2); - void ComputeSuccLineIn(ArenaBitVector* dest, const ArenaBitVector* src1, - const ArenaBitVector* src2); - void HandleLiveInUse(ArenaBitVector* use_v, ArenaBitVector* def_v, - ArenaBitVector* live_in_v, int dalvik_reg_id); - void HandleDef(ArenaBitVector* def_v, int dalvik_reg_id); - void HandleExtended(ArenaBitVector* use_v, ArenaBitVector* def_v, - ArenaBitVector* live_in_v, - const MIR::DecodedInstruction& d_insn); - bool DoSSAConversion(BasicBlock* bb); - int ParseInsn(const uint16_t* code_ptr, MIR::DecodedInstruction* decoded_instruction); - bool ContentIsInsn(const uint16_t* code_ptr); - BasicBlock* SplitBlock(DexOffset code_offset, BasicBlock* orig_block, - BasicBlock** immed_pred_block_p); - BasicBlock* FindBlock(DexOffset code_offset, bool create, BasicBlock** immed_pred_block_p, - ScopedArenaVector* dex_pc_to_block_map); - void ProcessTryCatchBlocks(ScopedArenaVector* dex_pc_to_block_map); - bool IsBadMonitorExitCatch(NarrowDexOffset monitor_exit_offset, NarrowDexOffset catch_offset); - BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, - int flags, const uint16_t* code_ptr, const uint16_t* code_end, - ScopedArenaVector* dex_pc_to_block_map); - BasicBlock* ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, - int flags, - ScopedArenaVector* dex_pc_to_block_map); - BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, - int flags, ArenaBitVector* try_block_addr, const uint16_t* code_ptr, - const uint16_t* code_end, - ScopedArenaVector* dex_pc_to_block_map); - int AddNewSReg(int v_reg); - void HandleSSAUse(int* uses, int dalvik_reg, int reg_index); - void DataFlowSSAFormat35C(MIR* mir); - void DataFlowSSAFormat3RC(MIR* mir); - void DataFlowSSAFormatExtended(MIR* mir); - bool FindLocalLiveIn(BasicBlock* bb); - bool VerifyPredInfo(BasicBlock* bb); - BasicBlock* NeedsVisit(BasicBlock* bb); - BasicBlock* NextUnvisitedSuccessor(BasicBlock* bb); - void MarkPreOrder(BasicBlock* bb); - void RecordDFSOrders(BasicBlock* bb); - void ComputeDomPostOrderTraversal(BasicBlock* bb); - int GetSSAUseCount(int s_reg); - bool BasicBlockOpt(BasicBlock* bb); - void MultiplyAddOpt(BasicBlock* bb); - - /** - * @brief Check whether the given MIR is possible to throw an exception. - * @param mir The mir to check. - * @return Returns 'true' if the given MIR might throw an exception. - */ - bool CanThrow(MIR* mir) const; - - /** - * @brief Combine multiply and add/sub MIRs into corresponding extended MAC MIR. - * @param mul_mir The multiply MIR to be combined. - * @param add_mir The add/sub MIR to be combined. - * @param mul_is_first_addend 'true' if multiply product is the first addend of add operation. - * @param is_wide 'true' if the operations are long type. - * @param is_sub 'true' if it is a multiply-subtract operation. - */ - void CombineMultiplyAdd(MIR* mul_mir, MIR* add_mir, bool mul_is_first_addend, - bool is_wide, bool is_sub); - /* - * @brief Check whether the first MIR anti-depends on the second MIR. - * @details To check whether one of first MIR's uses of vregs is redefined by the second MIR, - * i.e. there is a write-after-read dependency. - * @param first The first MIR. - * @param second The second MIR. - * @param Returns true if there is a write-after-read dependency. - */ - bool HasAntiDependency(MIR* first, MIR* second); - - bool BuildExtendedBBList(class BasicBlock* bb); - bool FillDefBlockMatrix(BasicBlock* bb); - void InitializeDominationInfo(BasicBlock* bb); - bool ComputeblockIDom(BasicBlock* bb); - bool ComputeBlockDominators(BasicBlock* bb); - bool SetDominators(BasicBlock* bb); - bool ComputeBlockLiveIns(BasicBlock* bb); - bool ComputeDominanceFrontier(BasicBlock* bb); - - void CountChecks(BasicBlock* bb); - void AnalyzeBlock(BasicBlock* bb, struct MethodStats* stats); - bool ComputeSkipCompilation(struct MethodStats* stats, bool skip_default, - std::string* skip_message); - - CompilationUnit* const cu_; - ArenaVector ssa_base_vregs_; - ArenaVector ssa_subscripts_; - // Map original Dalvik virtual reg i to the current SSA name. - int32_t* vreg_to_ssa_map_; // length == method->registers_size - int* ssa_last_defs_; // length == method->registers_size - ArenaBitVector* is_constant_v_; // length == num_ssa_reg - int* constant_values_; // length == num_ssa_reg - // Use counts of ssa names. - ArenaVector use_counts_; // Weighted by nesting depth - ArenaVector raw_use_counts_; // Not weighted - unsigned int num_reachable_blocks_; - unsigned int max_num_reachable_blocks_; - bool dfs_orders_up_to_date_; - bool domination_up_to_date_; - bool mir_ssa_rep_up_to_date_; - bool topological_order_up_to_date_; - ArenaVector dfs_order_; - ArenaVector dfs_post_order_; - ArenaVector dom_post_order_traversal_; - ArenaVector topological_order_; - // Indexes in topological_order_ need to be only as big as the BasicBlockId. - static_assert(sizeof(BasicBlockId) == sizeof(uint16_t), "Assuming 16 bit BasicBlockId"); - // For each loop head, remember the past-the-end index of the end of the loop. 0 if not loop head. - ArenaVector topological_order_loop_ends_; - // Map BB ids to topological_order_ indexes. 0xffff if not included (hidden or null block). - ArenaVector topological_order_indexes_; - // Stack of the loop head indexes and recalculation flags for RepeatingTopologicalSortIterator. - ArenaVector> topological_order_loop_head_stack_; - size_t max_nested_loops_; - int* i_dom_list_; - std::unique_ptr temp_scoped_alloc_; - // Union of temporaries used by different passes. - union { - // Class init check elimination. - struct { - size_t num_class_bits; // 2 bits per class: class initialized and class in dex cache. - ArenaBitVector* work_classes_to_check; - ArenaBitVector** ending_classes_to_check_matrix; // num_blocks_ x num_class_bits. - uint16_t* indexes; - } cice; - // Null check elimination. - struct { - size_t num_vregs; - ArenaBitVector* work_vregs_to_check; - ArenaBitVector** ending_vregs_to_check_matrix; // num_blocks_ x num_vregs. - } nce; - // Special method inlining. - struct { - size_t num_indexes; - ArenaBitVector* processed_indexes; - uint16_t* lowering_infos; - } smi; - // SSA transformation. - struct { - size_t num_vregs; - ArenaBitVector* work_live_vregs; - ArenaBitVector** def_block_matrix; // num_vregs x num_blocks_. - ArenaBitVector** phi_node_blocks; // num_vregs x num_blocks_. - TypeInference* ti; - } ssa; - // Global value numbering. - struct { - GlobalValueNumbering* gvn; - uint16_t* ifield_ids; // Part of GVN/LVN but cached here for LVN to avoid recalculation. - uint16_t* sfield_ids; // Ditto. - GvnDeadCodeElimination* dce; - } gvn; - } temp_; - static const int kInvalidEntry = -1; - ArenaVector block_list_; - ArenaBitVector* try_block_addr_; - BasicBlock* entry_block_; - BasicBlock* exit_block_; - const DexFile::CodeItem* current_code_item_; - ArenaVector m_units_; // List of methods included in this graph - typedef std::pair MIRLocation; // Insert point, (m_unit_ index, offset) - ArenaVector method_stack_; // Include stack - int current_method_; - DexOffset current_offset_; // Offset in code units - int def_count_; // Used to estimate size of ssa name storage. - int* opcode_count_; // Dex opcode coverage stats. - int num_ssa_regs_; // Number of names following SSA transformation. - ArenaVector extended_basic_blocks_; // Heads of block "traces". - int method_sreg_; - unsigned int attributes_; - Checkstats* checkstats_; - ArenaAllocator* const arena_; - int backward_branches_; - int forward_branches_; - size_t num_non_special_compiler_temps_; // Keeps track of allocated non-special compiler temps. These are VRs that are in compiler temp region on stack. - size_t max_available_non_special_compiler_temps_; // Keeps track of maximum available non-special temps. - size_t max_available_special_compiler_temps_; // Keeps track of maximum available special temps. - bool requested_backend_temp_; // Keeps track whether BE temps have been requested. - size_t reserved_temps_for_backend_; // Keeps track of the remaining temps that are reserved for BE. - bool compiler_temps_committed_; // Keeps track whether number of temps has been frozen (for example post frame size calculation). - bool punt_to_interpreter_; // Difficult or not worthwhile - just interpret. - uint64_t merged_df_flags_; - ArenaVector ifield_lowering_infos_; - ArenaVector sfield_lowering_infos_; - ArenaVector method_lowering_infos_; - - // In the suspend check elimination pass we determine for each basic block and enclosing - // loop whether there's guaranteed to be a suspend check on the path from the loop head - // to this block. If so, we can eliminate the back-edge suspend check. - // The bb->id is index into suspend_checks_in_loops_ and the loop head's depth is bit index - // in a suspend_checks_in_loops_[bb->id]. - uint32_t* suspend_checks_in_loops_; - - static const uint64_t oat_data_flow_attributes_[kMirOpLast]; - - friend class MirOptimizationTest; - friend class ClassInitCheckEliminationTest; - friend class SuspendCheckEliminationTest; - friend class NullCheckEliminationTest; - friend class GlobalValueNumberingTest; - friend class GvnDeadCodeEliminationTest; - friend class LocalValueNumberingTest; - friend class TopologicalSortOrderTest; - friend class TypeInferenceTest; - friend class QuickCFITest; - friend class QuickAssembleX86TestBase; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_MIR_GRAPH_H_ diff --git a/compiler/dex/mir_graph_test.cc b/compiler/dex/mir_graph_test.cc deleted file mode 100644 index 7858681e003c7c56f6949aa6064016fea2254f62..0000000000000000000000000000000000000000 --- a/compiler/dex/mir_graph_test.cc +++ /dev/null @@ -1,446 +0,0 @@ -/* - * 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 "compiler_ir.h" -#include "dataflow_iterator-inl.h" -#include "mir_graph.h" -#include "gtest/gtest.h" - -namespace art { - -class TopologicalSortOrderTest : public testing::Test { - protected: - struct BBDef { - static constexpr size_t kMaxSuccessors = 4; - static constexpr size_t kMaxPredecessors = 4; - - BBType type; - size_t num_successors; - BasicBlockId successors[kMaxPredecessors]; - size_t num_predecessors; - BasicBlockId predecessors[kMaxPredecessors]; - }; - -#define DEF_SUCC0() \ - 0u, { } -#define DEF_SUCC1(s1) \ - 1u, { s1 } -#define DEF_SUCC2(s1, s2) \ - 2u, { s1, s2 } -#define DEF_SUCC3(s1, s2, s3) \ - 3u, { s1, s2, s3 } -#define DEF_SUCC4(s1, s2, s3, s4) \ - 4u, { s1, s2, s3, s4 } -#define DEF_PRED0() \ - 0u, { } -#define DEF_PRED1(p1) \ - 1u, { p1 } -#define DEF_PRED2(p1, p2) \ - 2u, { p1, p2 } -#define DEF_PRED3(p1, p2, p3) \ - 3u, { p1, p2, p3 } -#define DEF_PRED4(p1, p2, p3, p4) \ - 4u, { p1, p2, p3, p4 } -#define DEF_BB(type, succ, pred) \ - { type, succ, pred } - - void DoPrepareBasicBlocks(const BBDef* defs, size_t count) { - cu_.mir_graph->block_id_map_.clear(); - cu_.mir_graph->block_list_.clear(); - ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block. - ASSERT_EQ(kNullBlock, defs[0].type); - ASSERT_EQ(kEntryBlock, defs[1].type); - ASSERT_EQ(kExitBlock, defs[2].type); - for (size_t i = 0u; i != count; ++i) { - const BBDef* def = &defs[i]; - BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type); - if (def->num_successors <= 2) { - bb->successor_block_list_type = kNotUsed; - bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u; - bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u; - } else { - bb->successor_block_list_type = kPackedSwitch; - bb->fall_through = 0u; - bb->taken = 0u; - bb->successor_blocks.reserve(def->num_successors); - for (size_t j = 0u; j != def->num_successors; ++j) { - SuccessorBlockInfo* successor_block_info = - static_cast(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessors)); - successor_block_info->block = j; - successor_block_info->key = 0u; // Not used by class init check elimination. - bb->successor_blocks.push_back(successor_block_info); - } - } - bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors); - if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) { - bb->data_flow_info = static_cast( - cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo)); - } - } - ASSERT_EQ(count, cu_.mir_graph->block_list_.size()); - cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1]; - ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type); - cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2]; - ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type); - - DexFile::CodeItem* code_item = static_cast(cu_.arena.Alloc(sizeof(DexFile::CodeItem), - kArenaAllocMisc)); - cu_.mir_graph->current_code_item_ = code_item; - } - - template - void PrepareBasicBlocks(const BBDef (&defs)[count]) { - DoPrepareBasicBlocks(defs, count); - } - - void ComputeTopologicalSortOrder() { - cu_.mir_graph->SSATransformationStart(); - cu_.mir_graph->ComputeDFSOrders(); - cu_.mir_graph->ComputeDominators(); - cu_.mir_graph->ComputeTopologicalSortOrder(); - cu_.mir_graph->SSATransformationEnd(); - ASSERT_FALSE(cu_.mir_graph->topological_order_.empty()); - ASSERT_FALSE(cu_.mir_graph->topological_order_loop_ends_.empty()); - ASSERT_FALSE(cu_.mir_graph->topological_order_indexes_.empty()); - ASSERT_EQ(cu_.mir_graph->GetNumBlocks(), cu_.mir_graph->topological_order_indexes_.size()); - for (size_t i = 0, size = cu_.mir_graph->GetTopologicalSortOrder().size(); i != size; ++i) { - ASSERT_LT(cu_.mir_graph->topological_order_[i], cu_.mir_graph->GetNumBlocks()); - BasicBlockId id = cu_.mir_graph->topological_order_[i]; - EXPECT_EQ(i, cu_.mir_graph->topological_order_indexes_[id]); - } - } - - void DoCheckOrder(const BasicBlockId* ids, size_t count) { - ASSERT_EQ(count, cu_.mir_graph->GetTopologicalSortOrder().size()); - for (size_t i = 0; i != count; ++i) { - EXPECT_EQ(ids[i], cu_.mir_graph->GetTopologicalSortOrder()[i]) << i; - } - } - - template - void CheckOrder(const BasicBlockId (&ids)[count]) { - DoCheckOrder(ids, count); - } - - void DoCheckLoopEnds(const uint16_t* ends, size_t count) { - for (size_t i = 0; i != count; ++i) { - ASSERT_LT(i, cu_.mir_graph->GetTopologicalSortOrderLoopEnds().size()); - EXPECT_EQ(ends[i], cu_.mir_graph->GetTopologicalSortOrderLoopEnds()[i]) << i; - } - } - - template - void CheckLoopEnds(const uint16_t (&ends)[count]) { - DoCheckLoopEnds(ends, count); - } - - TopologicalSortOrderTest() - : pool_(), - cu_(&pool_, kRuntimeISA, nullptr, nullptr) { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); - } - - ArenaPool pool_; - CompilationUnit cu_; -}; - -TEST_F(TopologicalSortOrderTest, DoWhile) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 2 - }; - const uint16_t loop_ends[] = { - 0, 0, 3, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, While) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED2(1, 4)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(3), DEF_PRED1(3)), // Loops to 3. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 2 - }; - const uint16_t loop_ends[] = { - 0, 3, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, WhileWithTwoBackEdges) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 6), DEF_PRED3(1, 4, 5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 3), DEF_PRED1(3)), // Loops to 3. - DEF_BB(kDalvikByteCode, DEF_SUCC1(3), DEF_PRED1(4)), // Loops to 3. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 6, 2 - }; - const uint16_t loop_ends[] = { - 0, 4, 0, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, NestedLoop) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 7), DEF_PRED2(1, 6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 6), DEF_PRED2(3, 5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)), // Loops to 4. - DEF_BB(kDalvikByteCode, DEF_SUCC1(3), DEF_PRED1(4)), // Loops to 3. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 6, 7, 2 - }; - const uint16_t loop_ends[] = { - 0, 5, 4, 0, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, NestedLoopHeadLoops) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 6), DEF_PRED2(1, 4)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 3), DEF_PRED2(3, 5)), // Nested head, loops to 3. - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)), // Loops to 4. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 6, 2 - }; - const uint16_t loop_ends[] = { - 0, 4, 4, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, NestedLoopSameBackBranchBlock) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 6), DEF_PRED2(1, 5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED2(3, 5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 3), DEF_PRED1(4)), // Loops to 4 and 3. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 6, 2 - }; - const uint16_t loop_ends[] = { - 0, 4, 4, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, TwoReorderedInnerLoops) { - // This is a simplified version of real code graph where the branch from 8 to 5 must prevent - // the block 5 from being considered a loop head before processing the loop 7-8. - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(9)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 9), DEF_PRED2(1, 5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 7), DEF_PRED1(3)), // Branch over loop in 5. - DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 3), DEF_PRED3(4, 6, 8)), // Loops to 4; inner loop. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)), // Loops to 5. - DEF_BB(kDalvikByteCode, DEF_SUCC1(8), DEF_PRED2(4, 8)), // Loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC2(7, 5), DEF_PRED1(7)), // Loops to 7; branches to 5. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 7, 8, 5, 6, 9, 2 - }; - const uint16_t loop_ends[] = { - 0, 7, 0, 5, 0, 7, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, NestedLoopWithBackEdgeAfterOuterLoopBackEdge) { - // This is a simplified version of real code graph. The back-edge from 7 to the inner - // loop head 4 comes after the back-edge from 6 to the outer loop head 3. To make this - // appear a bit more complex, there's also a back-edge from 5 to 4. - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED2(1, 6)), // Outer loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 6), DEF_PRED3(3, 5, 7)), // Inner loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)), // Loops to inner loop head 4. - DEF_BB(kDalvikByteCode, DEF_SUCC2(7, 3), DEF_PRED1(4)), // Loops to outer loop head 3. - DEF_BB(kDalvikByteCode, DEF_SUCC2(2, 4), DEF_PRED1(6)), // Loops to inner loop head 4. - }; - const BasicBlockId expected_order[] = { - // NOTE: The 5 goes before 6 only because 5 is a "fall-through" from 4 while 6 is "taken". - 1, 3, 4, 5, 6, 7, 2 - }; - const uint16_t loop_ends[] = { - 0, 6, 6, 0, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, LoopWithTwoEntryPoints) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED2(3, 6)), // Fall-back block is chosen as - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED2(3, 4)), // the earlier from these two. - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 7), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(6)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 6, 7, 2 - }; - const uint16_t loop_ends[] = { - 0, 0, 5, 0, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, UnnaturalLoops) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(10)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED2(11, 3)), // Unnatural loop head (top-level). - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED2(3, 4)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(9, 7), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(8), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(9), DEF_PRED2(10, 7)), // Unnatural loop head (nested). - DEF_BB(kDalvikByteCode, DEF_SUCC1(10), DEF_PRED2(6, 8)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 11), DEF_PRED1(9)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 2), DEF_PRED1(10)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 2 - }; - const uint16_t loop_ends[] = { - 0, 0, 10, 0, 0, 0, 9, 0, 0, 0, 0, - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); - - const std::pair expected_and_change[] = { - { 1, false }, - { 3, false }, - { 4, true }, // Initial run of the outer loop. - { 5, true }, - { 6, true }, - { 7, true }, - { 8, true }, // Initial run of the inner loop. - { 9, true }, - { 10, true }, - { 8, true }, // Recalculation of the inner loop - changed. - { 9, true }, - { 10, true }, - { 8, false }, // Recalculation of the inner loop - unchanged. - { 11, true }, - { 4, true }, // Recalculation of the outer loop - changed. - { 5, true }, - { 6, true }, - { 7, false }, // No change: skip inner loop head because inputs are unchanged. - { 9, true }, - { 10, true }, - { 8, true }, // Recalculation of the inner loop - changed. - { 9, true }, - { 10, true }, - { 8, false }, // Recalculation of the inner loop - unchanged. - { 11, true }, - { 4, false }, // Recalculation of the outer loop - unchanged. - { 2, false }, - }; - size_t pos = 0; - LoopRepeatingTopologicalSortIterator iter(cu_.mir_graph.get()); - bool change = false; - for (BasicBlock* bb = iter.Next(change); bb != nullptr; bb = iter.Next(change)) { - ASSERT_NE(arraysize(expected_and_change), pos); - ASSERT_EQ(expected_and_change[pos].first, bb->id) << pos; - change = expected_and_change[pos].second; - ++pos; - } - ASSERT_EQ(arraysize(expected_and_change), pos); -} - -} // namespace art diff --git a/compiler/dex/mir_method_info.cc b/compiler/dex/mir_method_info.cc deleted file mode 100644 index c250bd9fd2157942f431943bc83f7281bd3ff034..0000000000000000000000000000000000000000 --- a/compiler/dex/mir_method_info.cc +++ /dev/null @@ -1,194 +0,0 @@ -/* - * 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 "mir_method_info.h" - -#include "dex/compiler_ir.h" -#include "dex/quick/dex_file_method_inliner.h" -#include "dex/quick/dex_file_to_method_inliner_map.h" -#include "dex/verified_method.h" -#include "driver/compiler_driver.h" -#include "driver/dex_compilation_unit.h" -#include "driver/compiler_driver-inl.h" -#include "driver/compiler_options.h" -#include "mirror/class_loader.h" // Only to allow casts in Handle. -#include "mirror/dex_cache.h" // Only to allow casts in Handle. -#include "scoped_thread_state_change.h" -#include "handle_scope-inl.h" - -namespace art { - -void MirMethodLoweringInfo::Resolve(CompilerDriver* compiler_driver, - const DexCompilationUnit* mUnit, - MirMethodLoweringInfo* method_infos, size_t count) { - if (kIsDebugBuild) { - DCHECK(method_infos != nullptr); - DCHECK_NE(count, 0u); - for (auto it = method_infos, end = method_infos + count; it != end; ++it) { - MirMethodLoweringInfo unresolved(it->MethodIndex(), it->GetInvokeType(), it->IsQuickened()); - unresolved.declaring_dex_file_ = it->declaring_dex_file_; - unresolved.vtable_idx_ = it->vtable_idx_; - if (it->target_dex_file_ != nullptr) { - unresolved.target_dex_file_ = it->target_dex_file_; - unresolved.target_method_idx_ = it->target_method_idx_; - } - if (kIsDebugBuild) { - unresolved.CheckEquals(*it); - } - } - } - - // We're going to resolve methods and check access in a tight loop. It's better to hold - // the lock and needed references once than re-acquiring them again and again. - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<4> hs(soa.Self()); - Handle dex_cache(hs.NewHandle(compiler_driver->GetDexCache(mUnit))); - Handle class_loader( - hs.NewHandle(compiler_driver->GetClassLoader(soa, mUnit))); - Handle referrer_class(hs.NewHandle( - compiler_driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit))); - auto current_dex_cache(hs.NewHandle(nullptr)); - // Even if the referrer class is unresolved (i.e. we're compiling a method without class - // definition) we still want to resolve methods and record all available info. - Runtime* const runtime = Runtime::Current(); - const DexFile* const dex_file = mUnit->GetDexFile(); - const bool use_jit = runtime->UseJit(); - const VerifiedMethod* const verified_method = mUnit->GetVerifiedMethod(); - DexFileToMethodInlinerMap* inliner_map = compiler_driver->GetMethodInlinerMap(); - DexFileMethodInliner* default_inliner = - (inliner_map != nullptr) ? inliner_map->GetMethodInliner(dex_file) : nullptr; - - for (auto it = method_infos, end = method_infos + count; it != end; ++it) { - // For quickened invokes, the dex method idx is actually the mir offset. - if (it->IsQuickened()) { - const auto* dequicken_ref = verified_method->GetDequickenIndex(it->method_idx_); - CHECK(dequicken_ref != nullptr); - it->target_dex_file_ = dequicken_ref->dex_file; - it->target_method_idx_ = dequicken_ref->index; - } - // Remember devirtualized invoke target and set the called method to the default. - MethodReference devirt_ref(it->target_dex_file_, it->target_method_idx_); - MethodReference* devirt_target = (it->target_dex_file_ != nullptr) ? &devirt_ref : nullptr; - InvokeType invoke_type = it->GetInvokeType(); - ArtMethod* resolved_method = nullptr; - - bool string_init = false; - if (default_inliner->IsStringInitMethodIndex(it->MethodIndex())) { - string_init = true; - invoke_type = kDirect; - } - - if (!it->IsQuickened()) { - it->target_dex_file_ = dex_file; - it->target_method_idx_ = it->MethodIndex(); - current_dex_cache.Assign(dex_cache.Get()); - resolved_method = compiler_driver->ResolveMethod(soa, dex_cache, class_loader, mUnit, - it->target_method_idx_, invoke_type, true); - } else { - // The method index is actually the dex PC in this case. - // Calculate the proper dex file and target method idx. - - // We must be in JIT mode if we get here. - CHECK(use_jit); - - // The invoke type better be virtual, except for the string init special case above. - CHECK_EQ(invoke_type, string_init ? kDirect : kVirtual); - // Don't devirt if we are in a different dex file since we can't have direct invokes in - // another dex file unless we always put a direct / patch pointer. - devirt_target = nullptr; - current_dex_cache.Assign(runtime->GetClassLinker()->FindDexCache( - soa.Self(), *it->target_dex_file_)); - CHECK(current_dex_cache.Get() != nullptr); - DexCompilationUnit cu( - mUnit->GetCompilationUnit(), mUnit->GetClassLoader(), mUnit->GetClassLinker(), - *it->target_dex_file_, nullptr /* code_item not used */, 0u /* class_def_idx not used */, - it->target_method_idx_, 0u /* access_flags not used */, - nullptr /* verified_method not used */, - current_dex_cache); - resolved_method = compiler_driver->ResolveMethod(soa, current_dex_cache, class_loader, &cu, - it->target_method_idx_, invoke_type, false); - if (resolved_method == nullptr) { - // If the method is null then it should be a miranda method, in this case try - // re-loading it, this time as an interface method. The actual miranda method is in the - // vtable, but it will resolve to an interface method. - resolved_method = compiler_driver->ResolveMethod( - soa, current_dex_cache, class_loader, &cu, it->target_method_idx_, kInterface, false); - CHECK(resolved_method != nullptr); - } - if (resolved_method != nullptr) { - // Since this was a dequickened virtual, it is guaranteed to be resolved. However, it may be - // resolved to an interface method. If this is the case then change the invoke type to - // interface with the assumption that sharp_type will be kVirtual. - if (resolved_method->GetInvokeType() == kInterface) { - it->flags_ = (it->flags_ & ~(kInvokeTypeMask << kBitInvokeTypeBegin)) | - (static_cast(kInterface) << kBitInvokeTypeBegin); - } - } - } - if (UNLIKELY(resolved_method == nullptr)) { - continue; - } - - compiler_driver->GetResolvedMethodDexFileLocation(resolved_method, - &it->declaring_dex_file_, &it->declaring_class_idx_, &it->declaring_method_idx_); - if (!it->IsQuickened()) { - // For quickened invoke virtuals we may have desharpened to an interface method which - // wont give us the right method index, in this case blindly dispatch or else we can't - // compile the method. Converting the invoke to interface dispatch doesn't work since we - // have no way to get the dex method index for quickened invoke virtuals in the interface - // trampolines. - it->vtable_idx_ = - compiler_driver->GetResolvedMethodVTableIndex(resolved_method, invoke_type); - } - - MethodReference target_method(it->target_dex_file_, it->target_method_idx_); - int fast_path_flags = compiler_driver->IsFastInvoke( - soa, current_dex_cache, class_loader, mUnit, referrer_class.Get(), resolved_method, - &invoke_type, &target_method, devirt_target, &it->direct_code_, &it->direct_method_); - const bool is_referrers_class = referrer_class.Get() == resolved_method->GetDeclaringClass(); - const bool is_class_initialized = - compiler_driver->IsMethodsClassInitialized(referrer_class.Get(), resolved_method); - - // Check if the target method is intrinsic or special. - InlineMethodFlags is_intrinsic_or_special = kNoInlineMethodFlags; - if (inliner_map != nullptr) { - auto* inliner = (target_method.dex_file == dex_file) - ? default_inliner - : inliner_map->GetMethodInliner(target_method.dex_file); - is_intrinsic_or_special = inliner->IsIntrinsicOrSpecial(target_method.dex_method_index); - } - - uint16_t other_flags = it->flags_ & - ~(kFlagFastPath | kFlagIsIntrinsic | kFlagIsSpecial | kFlagClassIsInitialized | - (kInvokeTypeMask << kBitSharpTypeBegin)); - it->flags_ = other_flags | - // String init path is a special always-fast path. - (fast_path_flags != 0 || string_init ? kFlagFastPath : 0u) | - ((is_intrinsic_or_special & kInlineIntrinsic) != 0 ? kFlagIsIntrinsic : 0u) | - ((is_intrinsic_or_special & kInlineSpecial) != 0 ? kFlagIsSpecial : 0u) | - (static_cast(invoke_type) << kBitSharpTypeBegin) | - (is_referrers_class ? kFlagIsReferrersClass : 0u) | - (is_class_initialized ? kFlagClassIsInitialized : 0u); - it->target_dex_file_ = target_method.dex_file; - it->target_method_idx_ = target_method.dex_method_index; - it->stats_flags_ = fast_path_flags; - if (string_init) { - it->direct_code_ = 0; - } - } -} - -} // namespace art diff --git a/compiler/dex/mir_method_info.h b/compiler/dex/mir_method_info.h deleted file mode 100644 index 4512f35a99c0b6bc01b3685ee763ecfaf8d597f0..0000000000000000000000000000000000000000 --- a/compiler/dex/mir_method_info.h +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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_MIR_METHOD_INFO_H_ -#define ART_COMPILER_DEX_MIR_METHOD_INFO_H_ - -#include "base/logging.h" -#include "base/macros.h" -#include "base/mutex.h" -#include "invoke_type.h" -#include "method_reference.h" - -namespace art { - -class CompilerDriver; -class DexCompilationUnit; -class DexFile; - -class MirMethodInfo { - public: - uint16_t MethodIndex() const { - return method_idx_; - } - - bool IsStatic() const { - return (flags_ & kFlagIsStatic) != 0u; - } - - bool IsResolved() const { - return declaring_dex_file_ != nullptr; - } - - const DexFile* DeclaringDexFile() const { - return declaring_dex_file_; - } - void SetDeclaringDexFile(const DexFile* dex_file) { - declaring_dex_file_ = dex_file; - } - - uint16_t DeclaringClassIndex() const { - return declaring_class_idx_; - } - - uint16_t DeclaringMethodIndex() const { - return declaring_method_idx_; - } - - protected: - enum { - kBitIsStatic = 0, - kMethodInfoBitEnd - }; - static_assert(kMethodInfoBitEnd <= 16, "Too many flags"); - static constexpr uint16_t kFlagIsStatic = 1u << kBitIsStatic; - - MirMethodInfo(uint16_t method_idx, uint16_t flags) - : method_idx_(method_idx), - flags_(flags), - declaring_method_idx_(0u), - declaring_class_idx_(0u), - declaring_dex_file_(nullptr) { - } - - // Make copy-ctor/assign/dtor protected to avoid slicing. - MirMethodInfo(const MirMethodInfo& other) = default; - MirMethodInfo& operator=(const MirMethodInfo& other) = default; - ~MirMethodInfo() = default; - - // The method index in the compiling method's dex file. - uint16_t method_idx_; - // Flags, for volatility and derived class data. - uint16_t flags_; - // The method index in the dex file that defines the method, 0 if unresolved. - uint16_t declaring_method_idx_; - // The type index of the class declaring the method, 0 if unresolved. - uint16_t declaring_class_idx_; - // The dex file that defines the class containing the method and the method, - // null if unresolved. - const DexFile* declaring_dex_file_; -}; - -class MirMethodLoweringInfo : public MirMethodInfo { - public: - // For each requested method retrieve the method's declaring location (dex file, class - // index and method index) and compute whether we can fast path the method call. For fast - // path methods, retrieve the method's vtable index and direct code and method when applicable. - static void Resolve(CompilerDriver* compiler_driver, const DexCompilationUnit* mUnit, - MirMethodLoweringInfo* method_infos, size_t count) - REQUIRES(!Locks::mutator_lock_); - - MirMethodLoweringInfo(uint16_t method_idx, InvokeType type, bool is_quickened) - : MirMethodInfo(method_idx, - ((type == kStatic) ? kFlagIsStatic : 0u) | - (static_cast(type) << kBitInvokeTypeBegin) | - (static_cast(type) << kBitSharpTypeBegin) | - (is_quickened ? kFlagQuickened : 0u)), - direct_code_(0u), - direct_method_(0u), - target_dex_file_(nullptr), - target_method_idx_(0u), - vtable_idx_(0u), - stats_flags_(0) { - } - - void SetDevirtualizationTarget(const MethodReference& ref) { - DCHECK(target_dex_file_ == nullptr); - DCHECK_EQ(target_method_idx_, 0u); - DCHECK_LE(ref.dex_method_index, 0xffffu); - target_dex_file_ = ref.dex_file; - target_method_idx_ = ref.dex_method_index; - } - - bool FastPath() const { - return (flags_ & kFlagFastPath) != 0u; - } - - bool IsIntrinsic() const { - return (flags_ & kFlagIsIntrinsic) != 0u; - } - - bool IsSpecial() const { - return (flags_ & kFlagIsSpecial) != 0u; - } - - bool IsReferrersClass() const { - return (flags_ & kFlagIsReferrersClass) != 0; - } - - bool IsClassInitialized() const { - return (flags_ & kFlagClassIsInitialized) != 0u; - } - - // Returns true iff the method invoke is INVOKE_VIRTUAL_QUICK or INVOKE_VIRTUAL_RANGE_QUICK. - bool IsQuickened() const { - return (flags_ & kFlagQuickened) != 0u; - } - - InvokeType GetInvokeType() const { - return static_cast((flags_ >> kBitInvokeTypeBegin) & kInvokeTypeMask); - } - - art::InvokeType GetSharpType() const { - return static_cast((flags_ >> kBitSharpTypeBegin) & kInvokeTypeMask); - } - - MethodReference GetTargetMethod() const { - return MethodReference(target_dex_file_, target_method_idx_); - } - - uint16_t VTableIndex() const { - return vtable_idx_; - } - void SetVTableIndex(uint16_t index) { - vtable_idx_ = index; - } - - uintptr_t DirectCode() const { - return direct_code_; - } - - uintptr_t DirectMethod() const { - return direct_method_; - } - - int StatsFlags() const { - return stats_flags_; - } - - void CheckEquals(const MirMethodLoweringInfo& info) const { - CHECK_EQ(method_idx_, info.method_idx_); - CHECK_EQ(flags_, info.flags_); - CHECK_EQ(declaring_method_idx_, info.declaring_method_idx_); - CHECK_EQ(declaring_class_idx_, info.declaring_class_idx_); - CHECK_EQ(declaring_dex_file_, info.declaring_dex_file_); - CHECK_EQ(direct_code_, info.direct_code_); - CHECK_EQ(direct_method_, info.direct_method_); - CHECK_EQ(target_dex_file_, info.target_dex_file_); - CHECK_EQ(target_method_idx_, info.target_method_idx_); - CHECK_EQ(vtable_idx_, info.vtable_idx_); - CHECK_EQ(stats_flags_, info.stats_flags_); - } - - private: - enum { - kBitFastPath = kMethodInfoBitEnd, - kBitIsIntrinsic, - kBitIsSpecial, - kBitInvokeTypeBegin, - kBitInvokeTypeEnd = kBitInvokeTypeBegin + 3, // 3 bits for invoke type. - kBitSharpTypeBegin = kBitInvokeTypeEnd, - kBitSharpTypeEnd = kBitSharpTypeBegin + 3, // 3 bits for sharp type. - kBitIsReferrersClass = kBitSharpTypeEnd, - kBitClassIsInitialized, - kBitQuickened, - kMethodLoweringInfoBitEnd - }; - static_assert(kMethodLoweringInfoBitEnd <= 16, "Too many flags"); - static constexpr uint16_t kFlagFastPath = 1u << kBitFastPath; - static constexpr uint16_t kFlagIsIntrinsic = 1u << kBitIsIntrinsic; - static constexpr uint16_t kFlagIsSpecial = 1u << kBitIsSpecial; - static constexpr uint16_t kFlagIsReferrersClass = 1u << kBitIsReferrersClass; - static constexpr uint16_t kFlagClassIsInitialized = 1u << kBitClassIsInitialized; - static constexpr uint16_t kFlagQuickened = 1u << kBitQuickened; - static constexpr uint16_t kInvokeTypeMask = 7u; - static_assert((1u << (kBitInvokeTypeEnd - kBitInvokeTypeBegin)) - 1u == kInvokeTypeMask, - "assert invoke type bits failed"); - static_assert((1u << (kBitSharpTypeEnd - kBitSharpTypeBegin)) - 1u == kInvokeTypeMask, - "assert sharp type bits failed"); - - uintptr_t direct_code_; - uintptr_t direct_method_; - // Before Resolve(), target_dex_file_ and target_method_idx_ hold the verification-based - // devirtualized invoke target if available, null and 0u otherwise. - // After Resolve() they hold the actual target method that will be called; it will be either - // a devirtualized target method or the compilation's unit's dex file and MethodIndex(). - const DexFile* target_dex_file_; - uint16_t target_method_idx_; - uint16_t vtable_idx_; - int stats_flags_; - - friend class MirOptimizationTest; - friend class TypeInferenceTest; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_MIR_METHOD_INFO_H_ diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc deleted file mode 100644 index 0e74a48aa1a01c11a3e74e9a8423e89acc3bc7d2..0000000000000000000000000000000000000000 --- a/compiler/dex/mir_optimization.cc +++ /dev/null @@ -1,1997 +0,0 @@ -/* - * Copyright (C) 2011 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 "base/bit_vector-inl.h" -#include "base/logging.h" -#include "base/scoped_arena_containers.h" -#include "class_linker-inl.h" -#include "dataflow_iterator-inl.h" -#include "dex/verified_method.h" -#include "dex_flags.h" -#include "driver/compiler_driver.h" -#include "driver/dex_compilation_unit.h" -#include "global_value_numbering.h" -#include "gvn_dead_code_elimination.h" -#include "local_value_numbering.h" -#include "mir_field_info.h" -#include "mirror/string.h" -#include "quick/dex_file_method_inliner.h" -#include "quick/dex_file_to_method_inliner_map.h" -#include "stack.h" -#include "thread-inl.h" -#include "type_inference.h" -#include "utils.h" - -namespace art { - -static unsigned int Predecessors(BasicBlock* bb) { - return bb->predecessors.size(); -} - -/* Setup a constant value for opcodes thare have the DF_SETS_CONST attribute */ -void MIRGraph::SetConstant(int32_t ssa_reg, int32_t value) { - is_constant_v_->SetBit(ssa_reg); - constant_values_[ssa_reg] = value; - reg_location_[ssa_reg].is_const = true; -} - -void MIRGraph::SetConstantWide(int32_t ssa_reg, int64_t value) { - is_constant_v_->SetBit(ssa_reg); - is_constant_v_->SetBit(ssa_reg + 1); - constant_values_[ssa_reg] = Low32Bits(value); - constant_values_[ssa_reg + 1] = High32Bits(value); - reg_location_[ssa_reg].is_const = true; - reg_location_[ssa_reg + 1].is_const = true; -} - -void MIRGraph::DoConstantPropagation(BasicBlock* bb) { - MIR* mir; - - for (mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - // Skip pass if BB has MIR without SSA representation. - if (mir->ssa_rep == nullptr) { - return; - } - - uint64_t df_attributes = GetDataFlowAttributes(mir); - - MIR::DecodedInstruction* d_insn = &mir->dalvikInsn; - - if (!(df_attributes & DF_HAS_DEFS)) continue; - - /* Handle instructions that set up constants directly */ - if (df_attributes & DF_SETS_CONST) { - if (df_attributes & DF_DA) { - int32_t vB = static_cast(d_insn->vB); - switch (d_insn->opcode) { - case Instruction::CONST_4: - case Instruction::CONST_16: - case Instruction::CONST: - SetConstant(mir->ssa_rep->defs[0], vB); - break; - case Instruction::CONST_HIGH16: - SetConstant(mir->ssa_rep->defs[0], vB << 16); - break; - case Instruction::CONST_WIDE_16: - case Instruction::CONST_WIDE_32: - SetConstantWide(mir->ssa_rep->defs[0], static_cast(vB)); - break; - case Instruction::CONST_WIDE: - SetConstantWide(mir->ssa_rep->defs[0], d_insn->vB_wide); - break; - case Instruction::CONST_WIDE_HIGH16: - SetConstantWide(mir->ssa_rep->defs[0], static_cast(vB) << 48); - break; - default: - break; - } - } - /* Handle instructions that set up constants directly */ - } else if (df_attributes & DF_IS_MOVE) { - int i; - - for (i = 0; i < mir->ssa_rep->num_uses; i++) { - if (!is_constant_v_->IsBitSet(mir->ssa_rep->uses[i])) break; - } - /* Move a register holding a constant to another register */ - if (i == mir->ssa_rep->num_uses) { - SetConstant(mir->ssa_rep->defs[0], constant_values_[mir->ssa_rep->uses[0]]); - if (df_attributes & DF_A_WIDE) { - SetConstant(mir->ssa_rep->defs[1], constant_values_[mir->ssa_rep->uses[1]]); - } - } - } - } - /* TODO: implement code to handle arithmetic operations */ -} - -/* Advance to next strictly dominated MIR node in an extended basic block */ -MIR* MIRGraph::AdvanceMIR(BasicBlock** p_bb, MIR* mir) { - BasicBlock* bb = *p_bb; - if (mir != nullptr) { - mir = mir->next; - while (mir == nullptr) { - bb = GetBasicBlock(bb->fall_through); - if ((bb == nullptr) || Predecessors(bb) != 1) { - // mir is null and we cannot proceed further. - break; - } else { - *p_bb = bb; - mir = bb->first_mir_insn; - } - } - } - return mir; -} - -/* - * To be used at an invoke mir. If the logically next mir node represents - * a move-result, return it. Else, return nullptr. If a move-result exists, - * it is required to immediately follow the invoke with no intervening - * opcodes or incoming arcs. However, if the result of the invoke is not - * used, a move-result may not be present. - */ -MIR* MIRGraph::FindMoveResult(BasicBlock* bb, MIR* mir) { - BasicBlock* tbb = bb; - mir = AdvanceMIR(&tbb, mir); - while (mir != nullptr) { - if ((mir->dalvikInsn.opcode == Instruction::MOVE_RESULT) || - (mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) || - (mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE)) { - break; - } - // Keep going if pseudo op, otherwise terminate - if (MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) { - mir = AdvanceMIR(&tbb, mir); - } else { - mir = nullptr; - } - } - return mir; -} - -BasicBlock* MIRGraph::NextDominatedBlock(BasicBlock* bb) { - if (bb->block_type == kDead) { - return nullptr; - } - DCHECK((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode) - || (bb->block_type == kExitBlock)); - BasicBlock* bb_taken = GetBasicBlock(bb->taken); - BasicBlock* bb_fall_through = GetBasicBlock(bb->fall_through); - if (((bb_fall_through == nullptr) && (bb_taken != nullptr)) && - ((bb_taken->block_type == kDalvikByteCode) || (bb_taken->block_type == kExitBlock))) { - // Follow simple unconditional branches. - bb = bb_taken; - } else { - // Follow simple fallthrough - bb = (bb_taken != nullptr) ? nullptr : bb_fall_through; - } - if (bb == nullptr || (Predecessors(bb) != 1)) { - return nullptr; - } - DCHECK((bb->block_type == kDalvikByteCode) || (bb->block_type == kExitBlock)); - return bb; -} - -static MIR* FindPhi(BasicBlock* bb, int ssa_name) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (static_cast(mir->dalvikInsn.opcode) == kMirOpPhi) { - for (int i = 0; i < mir->ssa_rep->num_uses; i++) { - if (mir->ssa_rep->uses[i] == ssa_name) { - return mir; - } - } - } - } - return nullptr; -} - -static SelectInstructionKind SelectKind(MIR* mir) { - // Work with the case when mir is null. - if (mir == nullptr) { - return kSelectNone; - } - switch (mir->dalvikInsn.opcode) { - case Instruction::MOVE: - case Instruction::MOVE_OBJECT: - case Instruction::MOVE_16: - case Instruction::MOVE_OBJECT_16: - case Instruction::MOVE_FROM16: - case Instruction::MOVE_OBJECT_FROM16: - return kSelectMove; - case Instruction::CONST: - case Instruction::CONST_4: - case Instruction::CONST_16: - return kSelectConst; - case Instruction::GOTO: - case Instruction::GOTO_16: - case Instruction::GOTO_32: - return kSelectGoto; - default: - return kSelectNone; - } -} - -static constexpr ConditionCode kIfCcZConditionCodes[] = { - kCondEq, kCondNe, kCondLt, kCondGe, kCondGt, kCondLe -}; - -static_assert(arraysize(kIfCcZConditionCodes) == Instruction::IF_LEZ - Instruction::IF_EQZ + 1, - "if_ccz_ccodes_size1"); - -static constexpr ConditionCode ConditionCodeForIfCcZ(Instruction::Code opcode) { - return kIfCcZConditionCodes[opcode - Instruction::IF_EQZ]; -} - -static_assert(ConditionCodeForIfCcZ(Instruction::IF_EQZ) == kCondEq, "if_eqz ccode"); -static_assert(ConditionCodeForIfCcZ(Instruction::IF_NEZ) == kCondNe, "if_nez ccode"); -static_assert(ConditionCodeForIfCcZ(Instruction::IF_LTZ) == kCondLt, "if_ltz ccode"); -static_assert(ConditionCodeForIfCcZ(Instruction::IF_GEZ) == kCondGe, "if_gez ccode"); -static_assert(ConditionCodeForIfCcZ(Instruction::IF_GTZ) == kCondGt, "if_gtz ccode"); -static_assert(ConditionCodeForIfCcZ(Instruction::IF_LEZ) == kCondLe, "if_lez ccode"); - -int MIRGraph::GetSSAUseCount(int s_reg) { - DCHECK_LT(static_cast(s_reg), ssa_subscripts_.size()); - return raw_use_counts_[s_reg]; -} - -size_t MIRGraph::GetNumBytesForSpecialTemps() const { - // This logic is written with assumption that Method* is only special temp. - DCHECK_EQ(max_available_special_compiler_temps_, 1u); - return InstructionSetPointerSize(cu_->instruction_set); -} - -size_t MIRGraph::GetNumAvailableVRTemps() { - // First take into account all temps reserved for backend. - if (max_available_non_special_compiler_temps_ < reserved_temps_for_backend_) { - return 0; - } - - // Calculate remaining ME temps available. - size_t remaining_me_temps = max_available_non_special_compiler_temps_ - - reserved_temps_for_backend_; - - if (num_non_special_compiler_temps_ >= remaining_me_temps) { - return 0; - } else { - return remaining_me_temps - num_non_special_compiler_temps_; - } -} - -// FIXME - will probably need to revisit all uses of this, as type not defined. -static const RegLocation temp_loc = {kLocCompilerTemp, - 0, 1 /*defined*/, 0, 0, 0, 0, 0, 1 /*home*/, - RegStorage(), INVALID_SREG, INVALID_SREG}; - -CompilerTemp* MIRGraph::GetNewCompilerTemp(CompilerTempType ct_type, bool wide) { - // Once the compiler temps have been committed, new ones cannot be requested anymore. - DCHECK_EQ(compiler_temps_committed_, false); - // Make sure that reserved for BE set is sane. - DCHECK_LE(reserved_temps_for_backend_, max_available_non_special_compiler_temps_); - - bool verbose = cu_->verbose; - const char* ct_type_str = nullptr; - - if (verbose) { - switch (ct_type) { - case kCompilerTempBackend: - ct_type_str = "backend"; - break; - case kCompilerTempSpecialMethodPtr: - ct_type_str = "method*"; - break; - case kCompilerTempVR: - ct_type_str = "VR"; - break; - default: - ct_type_str = "unknown"; - break; - } - LOG(INFO) << "CompilerTemps: A compiler temp of type " << ct_type_str << " that is " - << (wide ? "wide is being requested." : "not wide is being requested."); - } - - CompilerTemp *compiler_temp = static_cast(arena_->Alloc(sizeof(CompilerTemp), - kArenaAllocRegAlloc)); - - // Create the type of temp requested. Special temps need special handling because - // they have a specific virtual register assignment. - if (ct_type == kCompilerTempSpecialMethodPtr) { - // This has a special location on stack which is 32-bit or 64-bit depending - // on mode. However, we don't want to overlap with non-special section - // and thus even for 64-bit, we allow only a non-wide temp to be requested. - DCHECK_EQ(wide, false); - - // The vreg is always the first special temp for method ptr. - compiler_temp->v_reg = GetFirstSpecialTempVR(); - - CHECK(reg_location_ == nullptr); - } else if (ct_type == kCompilerTempBackend) { - requested_backend_temp_ = true; - - // Make sure that we are not exceeding temps reserved for BE. - // Since VR temps cannot be requested once the BE temps are requested, we - // allow reservation of VR temps as well for BE. We - size_t available_temps = reserved_temps_for_backend_ + GetNumAvailableVRTemps(); - size_t needed_temps = wide ? 2u : 1u; - if (available_temps < needed_temps) { - if (verbose) { - LOG(INFO) << "CompilerTemps: Not enough temp(s) of type " << ct_type_str - << " are available."; - } - return nullptr; - } - - // Update the remaining reserved temps since we have now used them. - // Note that the code below is actually subtracting to remove them from reserve - // once they have been claimed. It is careful to not go below zero. - reserved_temps_for_backend_ = - std::max(reserved_temps_for_backend_, needed_temps) - needed_temps; - - // The new non-special compiler temp must receive a unique v_reg. - compiler_temp->v_reg = GetFirstNonSpecialTempVR() + num_non_special_compiler_temps_; - num_non_special_compiler_temps_++; - } else if (ct_type == kCompilerTempVR) { - // Once we start giving out BE temps, we don't allow anymore ME temps to be requested. - // This is done in order to prevent problems with ssa since these structures are allocated - // and managed by the ME. - DCHECK_EQ(requested_backend_temp_, false); - - // There is a limit to the number of non-special temps so check to make sure it wasn't exceeded. - size_t available_temps = GetNumAvailableVRTemps(); - if (available_temps <= 0 || (available_temps <= 1 && wide)) { - if (verbose) { - LOG(INFO) << "CompilerTemps: Not enough temp(s) of type " << ct_type_str - << " are available."; - } - return nullptr; - } - - // The new non-special compiler temp must receive a unique v_reg. - compiler_temp->v_reg = GetFirstNonSpecialTempVR() + num_non_special_compiler_temps_; - num_non_special_compiler_temps_++; - } else { - UNIMPLEMENTED(FATAL) << "No handling for compiler temp type " << ct_type_str << "."; - } - - // We allocate an sreg as well to make developer life easier. - // However, if this is requested from an ME pass that will recalculate ssa afterwards, - // this sreg is no longer valid. The caller should be aware of this. - compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg); - - if (verbose) { - LOG(INFO) << "CompilerTemps: New temp of type " << ct_type_str << " with v" - << compiler_temp->v_reg << " and s" << compiler_temp->s_reg_low << " has been created."; - } - - if (wide) { - // Only non-special temps are handled as wide for now. - // Note that the number of non special temps is incremented below. - DCHECK(ct_type == kCompilerTempBackend || ct_type == kCompilerTempVR); - - // Ensure that the two registers are consecutive. - int ssa_reg_low = compiler_temp->s_reg_low; - int ssa_reg_high = AddNewSReg(compiler_temp->v_reg + 1); - num_non_special_compiler_temps_++; - - if (verbose) { - LOG(INFO) << "CompilerTemps: The wide part of temp of type " << ct_type_str << " is v" - << compiler_temp->v_reg + 1 << " and s" << ssa_reg_high << "."; - } - - if (reg_location_ != nullptr) { - reg_location_[ssa_reg_high] = temp_loc; - reg_location_[ssa_reg_high].high_word = true; - reg_location_[ssa_reg_high].s_reg_low = ssa_reg_low; - reg_location_[ssa_reg_high].wide = true; - } - } - - // If the register locations have already been allocated, add the information - // about the temp. We will not overflow because they have been initialized - // to support the maximum number of temps. For ME temps that have multiple - // ssa versions, the structures below will be expanded on the post pass cleanup. - if (reg_location_ != nullptr) { - int ssa_reg_low = compiler_temp->s_reg_low; - reg_location_[ssa_reg_low] = temp_loc; - reg_location_[ssa_reg_low].s_reg_low = ssa_reg_low; - reg_location_[ssa_reg_low].wide = wide; - } - - return compiler_temp; -} - -void MIRGraph::RemoveLastCompilerTemp(CompilerTempType ct_type, bool wide, CompilerTemp* temp) { - // Once the compiler temps have been committed, it's too late for any modifications. - DCHECK_EQ(compiler_temps_committed_, false); - - size_t used_temps = wide ? 2u : 1u; - - if (ct_type == kCompilerTempBackend) { - DCHECK(requested_backend_temp_); - - // Make the temps available to backend again. - reserved_temps_for_backend_ += used_temps; - } else if (ct_type == kCompilerTempVR) { - DCHECK(!requested_backend_temp_); - } else { - UNIMPLEMENTED(FATAL) << "No handling for compiler temp type " << static_cast(ct_type); - } - - // Reduce the number of non-special compiler temps. - DCHECK_LE(used_temps, num_non_special_compiler_temps_); - num_non_special_compiler_temps_ -= used_temps; - - // Check that this was really the last temp. - DCHECK_EQ(static_cast(temp->v_reg), - GetFirstNonSpecialTempVR() + num_non_special_compiler_temps_); - - if (cu_->verbose) { - LOG(INFO) << "Last temporary has been removed."; - } -} - -static bool EvaluateBranch(Instruction::Code opcode, int32_t src1, int32_t src2) { - bool is_taken; - switch (opcode) { - case Instruction::IF_EQ: is_taken = (src1 == src2); break; - case Instruction::IF_NE: is_taken = (src1 != src2); break; - case Instruction::IF_LT: is_taken = (src1 < src2); break; - case Instruction::IF_GE: is_taken = (src1 >= src2); break; - case Instruction::IF_GT: is_taken = (src1 > src2); break; - case Instruction::IF_LE: is_taken = (src1 <= src2); break; - case Instruction::IF_EQZ: is_taken = (src1 == 0); break; - case Instruction::IF_NEZ: is_taken = (src1 != 0); break; - case Instruction::IF_LTZ: is_taken = (src1 < 0); break; - case Instruction::IF_GEZ: is_taken = (src1 >= 0); break; - case Instruction::IF_GTZ: is_taken = (src1 > 0); break; - case Instruction::IF_LEZ: is_taken = (src1 <= 0); break; - default: - LOG(FATAL) << "Unexpected opcode " << opcode; - UNREACHABLE(); - } - return is_taken; -} - -/* Do some MIR-level extended basic block optimizations */ -bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { - if (bb->block_type == kDead) { - return true; - } - // Currently multiply-accumulate backend supports are only available on arm32 and arm64. - if (cu_->instruction_set == kArm64 || cu_->instruction_set == kThumb2) { - MultiplyAddOpt(bb); - } - bool use_lvn = bb->use_lvn && (cu_->disable_opt & (1u << kLocalValueNumbering)) == 0u; - std::unique_ptr allocator; - std::unique_ptr global_valnum; - std::unique_ptr local_valnum; - if (use_lvn) { - allocator.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - global_valnum.reset(new (allocator.get()) GlobalValueNumbering(cu_, allocator.get(), - GlobalValueNumbering::kModeLvn)); - local_valnum.reset(new (allocator.get()) LocalValueNumbering(global_valnum.get(), bb->id, - allocator.get())); - } - while (bb != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - // TUNING: use the returned value number for CSE. - if (use_lvn) { - local_valnum->GetValueNumber(mir); - } - // Look for interesting opcodes, skip otherwise - Instruction::Code opcode = mir->dalvikInsn.opcode; - switch (opcode) { - case Instruction::IF_EQ: - case Instruction::IF_NE: - case Instruction::IF_LT: - case Instruction::IF_GE: - case Instruction::IF_GT: - case Instruction::IF_LE: - if (!IsConst(mir->ssa_rep->uses[1])) { - break; - } - FALLTHROUGH_INTENDED; - case Instruction::IF_EQZ: - case Instruction::IF_NEZ: - case Instruction::IF_LTZ: - case Instruction::IF_GEZ: - case Instruction::IF_GTZ: - case Instruction::IF_LEZ: - // Result known at compile time? - if (IsConst(mir->ssa_rep->uses[0])) { - int32_t rhs = (mir->ssa_rep->num_uses == 2) ? ConstantValue(mir->ssa_rep->uses[1]) : 0; - bool is_taken = EvaluateBranch(opcode, ConstantValue(mir->ssa_rep->uses[0]), rhs); - BasicBlockId edge_to_kill = is_taken ? bb->fall_through : bb->taken; - if (is_taken) { - // Replace with GOTO. - bb->fall_through = NullBasicBlockId; - mir->dalvikInsn.opcode = Instruction::GOTO; - mir->dalvikInsn.vA = - IsInstructionIfCc(opcode) ? mir->dalvikInsn.vC : mir->dalvikInsn.vB; - } else { - // Make NOP. - bb->taken = NullBasicBlockId; - mir->dalvikInsn.opcode = static_cast(kMirOpNop); - } - mir->ssa_rep->num_uses = 0; - BasicBlock* successor_to_unlink = GetBasicBlock(edge_to_kill); - successor_to_unlink->ErasePredecessor(bb->id); - // We have changed the graph structure. - dfs_orders_up_to_date_ = false; - domination_up_to_date_ = false; - topological_order_up_to_date_ = false; - // Keep MIR SSA rep, the worst that can happen is a Phi with just 1 input. - } - break; - case Instruction::CMPL_FLOAT: - case Instruction::CMPL_DOUBLE: - case Instruction::CMPG_FLOAT: - case Instruction::CMPG_DOUBLE: - case Instruction::CMP_LONG: - if ((cu_->disable_opt & (1 << kBranchFusing)) != 0) { - // Bitcode doesn't allow this optimization. - break; - } - if (mir->next != nullptr) { - MIR* mir_next = mir->next; - // Make sure result of cmp is used by next insn and nowhere else - if (IsInstructionIfCcZ(mir_next->dalvikInsn.opcode) && - (mir->ssa_rep->defs[0] == mir_next->ssa_rep->uses[0]) && - (GetSSAUseCount(mir->ssa_rep->defs[0]) == 1)) { - mir_next->meta.ccode = ConditionCodeForIfCcZ(mir_next->dalvikInsn.opcode); - switch (opcode) { - case Instruction::CMPL_FLOAT: - mir_next->dalvikInsn.opcode = - static_cast(kMirOpFusedCmplFloat); - break; - case Instruction::CMPL_DOUBLE: - mir_next->dalvikInsn.opcode = - static_cast(kMirOpFusedCmplDouble); - break; - case Instruction::CMPG_FLOAT: - mir_next->dalvikInsn.opcode = - static_cast(kMirOpFusedCmpgFloat); - break; - case Instruction::CMPG_DOUBLE: - mir_next->dalvikInsn.opcode = - static_cast(kMirOpFusedCmpgDouble); - break; - case Instruction::CMP_LONG: - mir_next->dalvikInsn.opcode = - static_cast(kMirOpFusedCmpLong); - break; - default: LOG(ERROR) << "Unexpected opcode: " << opcode; - } - mir->dalvikInsn.opcode = static_cast(kMirOpNop); - // Clear use count of temp VR. - use_counts_[mir->ssa_rep->defs[0]] = 0; - raw_use_counts_[mir->ssa_rep->defs[0]] = 0; - // Copy the SSA information that is relevant. - mir_next->ssa_rep->num_uses = mir->ssa_rep->num_uses; - mir_next->ssa_rep->uses = mir->ssa_rep->uses; - mir_next->ssa_rep->num_defs = 0; - mir->ssa_rep->num_uses = 0; - mir->ssa_rep->num_defs = 0; - // Copy in the decoded instruction information for potential SSA re-creation. - mir_next->dalvikInsn.vA = mir->dalvikInsn.vB; - mir_next->dalvikInsn.vB = mir->dalvikInsn.vC; - } - } - break; - default: - break; - } - // Is this the select pattern? - // TODO: flesh out support for Mips. NOTE: llvm's select op doesn't quite work here. - // TUNING: expand to support IF_xx compare & branches - if ((cu_->instruction_set == kArm64 || cu_->instruction_set == kThumb2 || - cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) && - IsInstructionIfCcZ(mir->dalvikInsn.opcode)) { - BasicBlock* ft = GetBasicBlock(bb->fall_through); - DCHECK(ft != nullptr); - BasicBlock* ft_ft = GetBasicBlock(ft->fall_through); - BasicBlock* ft_tk = GetBasicBlock(ft->taken); - - BasicBlock* tk = GetBasicBlock(bb->taken); - DCHECK(tk != nullptr); - BasicBlock* tk_ft = GetBasicBlock(tk->fall_through); - BasicBlock* tk_tk = GetBasicBlock(tk->taken); - - /* - * In the select pattern, the taken edge goes to a block that unconditionally - * transfers to the rejoin block and the fall_though edge goes to a block that - * unconditionally falls through to the rejoin block. - */ - if ((tk_ft == nullptr) && (ft_tk == nullptr) && (tk_tk == ft_ft) && - (Predecessors(tk) == 1) && (Predecessors(ft) == 1)) { - /* - * Okay - we have the basic diamond shape. - */ - - // TODO: Add logic for LONG. - // Are the block bodies something we can handle? - if ((ft->first_mir_insn == ft->last_mir_insn) && - (tk->first_mir_insn != tk->last_mir_insn) && - (tk->first_mir_insn->next == tk->last_mir_insn) && - ((SelectKind(ft->first_mir_insn) == kSelectMove) || - (SelectKind(ft->first_mir_insn) == kSelectConst)) && - (SelectKind(ft->first_mir_insn) == SelectKind(tk->first_mir_insn)) && - (SelectKind(tk->last_mir_insn) == kSelectGoto)) { - // Almost there. Are the instructions targeting the same vreg? - MIR* if_true = tk->first_mir_insn; - MIR* if_false = ft->first_mir_insn; - // It's possible that the target of the select isn't used - skip those (rare) cases. - MIR* phi = FindPhi(tk_tk, if_true->ssa_rep->defs[0]); - if ((phi != nullptr) && (if_true->dalvikInsn.vA == if_false->dalvikInsn.vA)) { - /* - * We'll convert the IF_EQZ/IF_NEZ to a SELECT. We need to find the - * Phi node in the merge block and delete it (while using the SSA name - * of the merge as the target of the SELECT. Delete both taken and - * fallthrough blocks, and set fallthrough to merge block. - * NOTE: not updating other dataflow info (no longer used at this point). - * If this changes, need to update i_dom, etc. here (and in CombineBlocks). - */ - mir->meta.ccode = ConditionCodeForIfCcZ(mir->dalvikInsn.opcode); - mir->dalvikInsn.opcode = static_cast(kMirOpSelect); - bool const_form = (SelectKind(if_true) == kSelectConst); - if ((SelectKind(if_true) == kSelectMove)) { - if (IsConst(if_true->ssa_rep->uses[0]) && - IsConst(if_false->ssa_rep->uses[0])) { - const_form = true; - if_true->dalvikInsn.vB = ConstantValue(if_true->ssa_rep->uses[0]); - if_false->dalvikInsn.vB = ConstantValue(if_false->ssa_rep->uses[0]); - } - } - if (const_form) { - /* - * TODO: If both constants are the same value, then instead of generating - * a select, we should simply generate a const bytecode. This should be - * considered after inlining which can lead to CFG of this form. - */ - // "true" set val in vB - mir->dalvikInsn.vB = if_true->dalvikInsn.vB; - // "false" set val in vC - mir->dalvikInsn.vC = if_false->dalvikInsn.vB; - } else { - DCHECK_EQ(SelectKind(if_true), kSelectMove); - DCHECK_EQ(SelectKind(if_false), kSelectMove); - int32_t* src_ssa = arena_->AllocArray(3, kArenaAllocDFInfo); - src_ssa[0] = mir->ssa_rep->uses[0]; - src_ssa[1] = if_true->ssa_rep->uses[0]; - src_ssa[2] = if_false->ssa_rep->uses[0]; - mir->ssa_rep->uses = src_ssa; - mir->ssa_rep->num_uses = 3; - } - AllocateSSADefData(mir, 1); - /* - * There is usually a Phi node in the join block for our two cases. If the - * Phi node only contains our two cases as input, we will use the result - * SSA name of the Phi node as our select result and delete the Phi. If - * the Phi node has more than two operands, we will arbitrarily use the SSA - * name of the "false" path, delete the SSA name of the "true" path from the - * Phi node (and fix up the incoming arc list). - */ - if (phi->ssa_rep->num_uses == 2) { - mir->ssa_rep->defs[0] = phi->ssa_rep->defs[0]; - // Rather than changing the Phi to kMirOpNop, remove it completely. - // This avoids leaving other Phis after kMirOpNop (i.e. a non-Phi) insn. - tk_tk->RemoveMIR(phi); - int dead_false_def = if_false->ssa_rep->defs[0]; - raw_use_counts_[dead_false_def] = use_counts_[dead_false_def] = 0; - } else { - int live_def = if_false->ssa_rep->defs[0]; - mir->ssa_rep->defs[0] = live_def; - } - int dead_true_def = if_true->ssa_rep->defs[0]; - raw_use_counts_[dead_true_def] = use_counts_[dead_true_def] = 0; - // Update ending vreg->sreg map for GC maps generation. - int def_vreg = SRegToVReg(mir->ssa_rep->defs[0]); - bb->data_flow_info->vreg_to_ssa_map_exit[def_vreg] = mir->ssa_rep->defs[0]; - // We want to remove ft and tk and link bb directly to ft_ft. First, we need - // to update all Phi inputs correctly with UpdatePredecessor(ft->id, bb->id) - // since the live_def above comes from ft->first_mir_insn (if_false). - DCHECK(if_false == ft->first_mir_insn); - ft_ft->UpdatePredecessor(ft->id, bb->id); - // Correct the rest of the links between bb, ft and ft_ft. - ft->ErasePredecessor(bb->id); - ft->fall_through = NullBasicBlockId; - bb->fall_through = ft_ft->id; - // Now we can kill tk and ft. - tk->Kill(this); - ft->Kill(this); - // NOTE: DFS order, domination info and topological order are still usable - // despite the newly dead blocks. - } - } - } - } - } - bb = ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) ? NextDominatedBlock(bb) : - nullptr; - } - if (use_lvn && UNLIKELY(!global_valnum->Good())) { - LOG(WARNING) << "LVN overflow in " << PrettyMethod(cu_->method_idx, *cu_->dex_file); - } - - return true; -} - -/* Collect stats on number of checks removed */ -void MIRGraph::CountChecks(class BasicBlock* bb) { - if (bb->data_flow_info != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (mir->ssa_rep == nullptr) { - continue; - } - uint64_t df_attributes = GetDataFlowAttributes(mir); - if (df_attributes & DF_HAS_NULL_CHKS) { - checkstats_->null_checks++; - if (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) { - checkstats_->null_checks_eliminated++; - } - } - if (df_attributes & DF_HAS_RANGE_CHKS) { - checkstats_->range_checks++; - if (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) { - checkstats_->range_checks_eliminated++; - } - } - } - } -} - -/* Try to make common case the fallthrough path. */ -bool MIRGraph::LayoutBlocks(BasicBlock* bb) { - // TODO: For now, just looking for direct throws. Consider generalizing for profile feedback. - if (!bb->explicit_throw) { - return false; - } - - // If we visited it, we are done. - if (bb->visited) { - return false; - } - bb->visited = true; - - BasicBlock* walker = bb; - while (true) { - // Check termination conditions. - if ((walker->block_type == kEntryBlock) || (Predecessors(walker) != 1)) { - break; - } - DCHECK(!walker->predecessors.empty()); - BasicBlock* prev = GetBasicBlock(walker->predecessors[0]); - - // If we visited the predecessor, we are done. - if (prev->visited) { - return false; - } - prev->visited = true; - - if (prev->conditional_branch) { - if (GetBasicBlock(prev->fall_through) == walker) { - // Already done - return. - break; - } - DCHECK_EQ(walker, GetBasicBlock(prev->taken)); - // Got one. Flip it and exit. - Instruction::Code opcode = prev->last_mir_insn->dalvikInsn.opcode; - switch (opcode) { - case Instruction::IF_EQ: opcode = Instruction::IF_NE; break; - case Instruction::IF_NE: opcode = Instruction::IF_EQ; break; - case Instruction::IF_LT: opcode = Instruction::IF_GE; break; - case Instruction::IF_GE: opcode = Instruction::IF_LT; break; - case Instruction::IF_GT: opcode = Instruction::IF_LE; break; - case Instruction::IF_LE: opcode = Instruction::IF_GT; break; - case Instruction::IF_EQZ: opcode = Instruction::IF_NEZ; break; - case Instruction::IF_NEZ: opcode = Instruction::IF_EQZ; break; - case Instruction::IF_LTZ: opcode = Instruction::IF_GEZ; break; - case Instruction::IF_GEZ: opcode = Instruction::IF_LTZ; break; - case Instruction::IF_GTZ: opcode = Instruction::IF_LEZ; break; - case Instruction::IF_LEZ: opcode = Instruction::IF_GTZ; break; - default: LOG(FATAL) << "Unexpected opcode " << opcode; - } - prev->last_mir_insn->dalvikInsn.opcode = opcode; - BasicBlockId t_bb = prev->taken; - prev->taken = prev->fall_through; - prev->fall_through = t_bb; - break; - } - walker = prev; - } - return false; -} - -/* Combine any basic blocks terminated by instructions that we now know can't throw */ -void MIRGraph::CombineBlocks(class BasicBlock* bb) { - // Loop here to allow combining a sequence of blocks - while ((bb->block_type == kDalvikByteCode) && - (bb->last_mir_insn != nullptr) && - (static_cast(bb->last_mir_insn->dalvikInsn.opcode) == kMirOpCheck)) { - MIR* mir = bb->last_mir_insn; - DCHECK(bb->first_mir_insn != nullptr); - - // Get the paired insn and check if it can still throw. - MIR* throw_insn = mir->meta.throw_insn; - if (CanThrow(throw_insn)) { - break; - } - - // OK - got one. Combine - BasicBlock* bb_next = GetBasicBlock(bb->fall_through); - DCHECK(!bb_next->catch_entry); - DCHECK_EQ(bb_next->predecessors.size(), 1u); - - // Now move instructions from bb_next to bb. Start off with doing a sanity check - // that kMirOpCheck's throw instruction is first one in the bb_next. - DCHECK_EQ(bb_next->first_mir_insn, throw_insn); - // Now move all instructions (throw instruction to last one) from bb_next to bb. - MIR* last_to_move = bb_next->last_mir_insn; - bb_next->RemoveMIRList(throw_insn, last_to_move); - bb->InsertMIRListAfter(bb->last_mir_insn, throw_insn, last_to_move); - // The kMirOpCheck instruction is not needed anymore. - mir->dalvikInsn.opcode = static_cast(kMirOpNop); - bb->RemoveMIR(mir); - - // Before we overwrite successors, remove their predecessor links to bb. - bb_next->ErasePredecessor(bb->id); - if (bb->taken != NullBasicBlockId) { - DCHECK_EQ(bb->successor_block_list_type, kNotUsed); - BasicBlock* bb_taken = GetBasicBlock(bb->taken); - // bb->taken will be overwritten below. - DCHECK_EQ(bb_taken->block_type, kExceptionHandling); - DCHECK_EQ(bb_taken->predecessors.size(), 1u); - DCHECK_EQ(bb_taken->predecessors[0], bb->id); - bb_taken->predecessors.clear(); - bb_taken->block_type = kDead; - DCHECK(bb_taken->data_flow_info == nullptr); - } else { - DCHECK_EQ(bb->successor_block_list_type, kCatch); - for (SuccessorBlockInfo* succ_info : bb->successor_blocks) { - if (succ_info->block != NullBasicBlockId) { - BasicBlock* succ_bb = GetBasicBlock(succ_info->block); - DCHECK(succ_bb->catch_entry); - succ_bb->ErasePredecessor(bb->id); - } - } - } - // Use the successor info from the next block - bb->successor_block_list_type = bb_next->successor_block_list_type; - bb->successor_blocks.swap(bb_next->successor_blocks); // Swap instead of copying. - bb_next->successor_block_list_type = kNotUsed; - // Use the ending block linkage from the next block - bb->fall_through = bb_next->fall_through; - bb_next->fall_through = NullBasicBlockId; - bb->taken = bb_next->taken; - bb_next->taken = NullBasicBlockId; - /* - * If lower-half of pair of blocks to combine contained - * a return or a conditional branch or an explicit throw, - * move the flag to the newly combined block. - */ - bb->terminated_by_return = bb_next->terminated_by_return; - bb->conditional_branch = bb_next->conditional_branch; - bb->explicit_throw = bb_next->explicit_throw; - // Merge the use_lvn flag. - bb->use_lvn |= bb_next->use_lvn; - - // Kill the unused block. - bb_next->data_flow_info = nullptr; - - /* - * NOTE: we aren't updating all dataflow info here. Should either make sure this pass - * happens after uses of i_dominated, dom_frontier or update the dataflow info here. - * NOTE: GVN uses bb->data_flow_info->live_in_v which is unaffected by the block merge. - */ - - // Kill bb_next and remap now-dead id to parent. - bb_next->block_type = kDead; - bb_next->data_flow_info = nullptr; // Must be null for dead blocks. (Relied on by the GVN.) - block_id_map_.Overwrite(bb_next->id, bb->id); - // Update predecessors in children. - ChildBlockIterator iter(bb, this); - for (BasicBlock* child = iter.Next(); child != nullptr; child = iter.Next()) { - child->UpdatePredecessor(bb_next->id, bb->id); - } - - // DFS orders, domination and topological order are not up to date anymore. - dfs_orders_up_to_date_ = false; - domination_up_to_date_ = false; - topological_order_up_to_date_ = false; - - // Now, loop back and see if we can keep going - } -} - -bool MIRGraph::EliminateNullChecksGate() { - if ((cu_->disable_opt & (1 << kNullCheckElimination)) != 0 || - (merged_df_flags_ & DF_HAS_NULL_CHKS) == 0) { - return false; - } - - DCHECK(temp_scoped_alloc_.get() == nullptr); - temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_.nce.num_vregs = GetNumOfCodeAndTempVRs(); - temp_.nce.work_vregs_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.nce.num_vregs, false); - temp_.nce.ending_vregs_to_check_matrix = - temp_scoped_alloc_->AllocArray(GetNumBlocks(), kArenaAllocMisc); - std::fill_n(temp_.nce.ending_vregs_to_check_matrix, GetNumBlocks(), nullptr); - - // reset MIR_MARK - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - mir->optimization_flags &= ~MIR_MARK; - } - } - - return true; -} - -/* - * Eliminate unnecessary null checks for a basic block. - */ -bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { - if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) { - // Ignore the kExitBlock as well. - DCHECK(bb->first_mir_insn == nullptr); - return false; - } - - ArenaBitVector* vregs_to_check = temp_.nce.work_vregs_to_check; - /* - * Set initial state. Catch blocks don't need any special treatment. - */ - if (bb->block_type == kEntryBlock) { - vregs_to_check->ClearAllBits(); - // Assume all ins are objects. - for (uint16_t in_reg = GetFirstInVR(); - in_reg < GetNumOfCodeVRs(); in_reg++) { - vregs_to_check->SetBit(in_reg); - } - if ((cu_->access_flags & kAccStatic) == 0) { - // If non-static method, mark "this" as non-null. - int this_reg = GetFirstInVR(); - vregs_to_check->ClearBit(this_reg); - } - } else { - DCHECK_EQ(bb->block_type, kDalvikByteCode); - // Starting state is union of all incoming arcs. - bool copied_first = false; - for (BasicBlockId pred_id : bb->predecessors) { - if (temp_.nce.ending_vregs_to_check_matrix[pred_id] == nullptr) { - continue; - } - BasicBlock* pred_bb = GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - MIR* null_check_insn = nullptr; - // Check to see if predecessor had an explicit null-check. - if (pred_bb->BranchesToSuccessorOnlyIfNotZero(bb->id)) { - // Remember the null check insn if there's no other predecessor requiring null check. - if (!copied_first || !vregs_to_check->IsBitSet(pred_bb->last_mir_insn->dalvikInsn.vA)) { - null_check_insn = pred_bb->last_mir_insn; - DCHECK(null_check_insn != nullptr); - } - } - if (!copied_first) { - copied_first = true; - vregs_to_check->Copy(temp_.nce.ending_vregs_to_check_matrix[pred_id]); - } else { - vregs_to_check->Union(temp_.nce.ending_vregs_to_check_matrix[pred_id]); - } - if (null_check_insn != nullptr) { - vregs_to_check->ClearBit(null_check_insn->dalvikInsn.vA); - } - } - DCHECK(copied_first); // At least one predecessor must have been processed before this bb. - } - // At this point, vregs_to_check shows which sregs have an object definition with - // no intervening uses. - - // Walk through the instruction in the block, updating as necessary - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - uint64_t df_attributes = GetDataFlowAttributes(mir); - - if ((df_attributes & DF_NULL_TRANSFER_N) != 0u) { - // The algorithm was written in a phi agnostic way. - continue; - } - - // Might need a null check? - if (df_attributes & DF_HAS_NULL_CHKS) { - int src_vreg; - if (df_attributes & DF_NULL_CHK_OUT0) { - DCHECK_NE(df_attributes & DF_IS_INVOKE, 0u); - src_vreg = mir->dalvikInsn.vC; - } else if (df_attributes & DF_NULL_CHK_B) { - DCHECK_NE(df_attributes & DF_REF_B, 0u); - src_vreg = mir->dalvikInsn.vB; - } else { - DCHECK_NE(df_attributes & DF_NULL_CHK_A, 0u); - DCHECK_NE(df_attributes & DF_REF_A, 0u); - src_vreg = mir->dalvikInsn.vA; - } - if (!vregs_to_check->IsBitSet(src_vreg)) { - // Eliminate the null check. - mir->optimization_flags |= MIR_MARK; - } else { - // Do the null check. - mir->optimization_flags &= ~MIR_MARK; - // Mark src_vreg as null-checked. - vregs_to_check->ClearBit(src_vreg); - } - } - - if ((df_attributes & DF_A_WIDE) || - (df_attributes & (DF_REF_A | DF_SETS_CONST | DF_NULL_TRANSFER)) == 0) { - continue; - } - - /* - * First, mark all object definitions as requiring null check. - * Note: we can't tell if a CONST definition might be used as an object, so treat - * them all as object definitions. - */ - if ((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A) || - (df_attributes & DF_SETS_CONST)) { - vregs_to_check->SetBit(mir->dalvikInsn.vA); - } - - // Then, remove mark from all object definitions we know are non-null. - if (df_attributes & DF_NON_NULL_DST) { - // Mark target of NEW* as non-null - DCHECK_NE(df_attributes & DF_REF_A, 0u); - vregs_to_check->ClearBit(mir->dalvikInsn.vA); - } - - // Mark non-null returns from invoke-style NEW* - if (df_attributes & DF_NON_NULL_RET) { - MIR* next_mir = mir->next; - // Next should be an MOVE_RESULT_OBJECT - if (UNLIKELY(next_mir == nullptr)) { - // The MethodVerifier makes sure there's no MOVE_RESULT at the catch entry or branch - // target, so the MOVE_RESULT cannot be broken away into another block. - LOG(WARNING) << "Unexpected end of block following new"; - } else if (UNLIKELY(next_mir->dalvikInsn.opcode != Instruction::MOVE_RESULT_OBJECT)) { - LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode; - } else { - // Mark as null checked. - vregs_to_check->ClearBit(next_mir->dalvikInsn.vA); - } - } - - // Propagate null check state on register copies. - if (df_attributes & DF_NULL_TRANSFER_0) { - DCHECK_EQ(df_attributes | ~(DF_DA | DF_REF_A | DF_UB | DF_REF_B), static_cast(-1)); - if (vregs_to_check->IsBitSet(mir->dalvikInsn.vB)) { - vregs_to_check->SetBit(mir->dalvikInsn.vA); - } else { - vregs_to_check->ClearBit(mir->dalvikInsn.vA); - } - } - } - - // Did anything change? - bool nce_changed = false; - ArenaBitVector* old_ending_ssa_regs_to_check = temp_.nce.ending_vregs_to_check_matrix[bb->id]; - if (old_ending_ssa_regs_to_check == nullptr) { - DCHECK(temp_scoped_alloc_.get() != nullptr); - nce_changed = vregs_to_check->GetHighestBitSet() != -1; - temp_.nce.ending_vregs_to_check_matrix[bb->id] = vregs_to_check; - // Create a new vregs_to_check for next BB. - temp_.nce.work_vregs_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.nce.num_vregs, false); - } else if (!vregs_to_check->SameBitsSet(old_ending_ssa_regs_to_check)) { - nce_changed = true; - temp_.nce.ending_vregs_to_check_matrix[bb->id] = vregs_to_check; - temp_.nce.work_vregs_to_check = old_ending_ssa_regs_to_check; // Reuse for next BB. - } - return nce_changed; -} - -void MIRGraph::EliminateNullChecksEnd() { - // Clean up temporaries. - temp_.nce.num_vregs = 0u; - temp_.nce.work_vregs_to_check = nullptr; - temp_.nce.ending_vregs_to_check_matrix = nullptr; - DCHECK(temp_scoped_alloc_.get() != nullptr); - temp_scoped_alloc_.reset(); - - // converge MIR_MARK with MIR_IGNORE_NULL_CHECK - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - constexpr int kMarkToIgnoreNullCheckShift = kMIRMark - kMIRIgnoreNullCheck; - static_assert(kMarkToIgnoreNullCheckShift > 0, "Not a valid right-shift"); - uint16_t mirMarkAdjustedToIgnoreNullCheck = - (mir->optimization_flags & MIR_MARK) >> kMarkToIgnoreNullCheckShift; - mir->optimization_flags |= mirMarkAdjustedToIgnoreNullCheck; - } - } -} - -void MIRGraph::InferTypesStart() { - DCHECK(temp_scoped_alloc_ != nullptr); - temp_.ssa.ti = new (temp_scoped_alloc_.get()) TypeInference(this, temp_scoped_alloc_.get()); -} - -/* - * Perform type and size inference for a basic block. - */ -bool MIRGraph::InferTypes(BasicBlock* bb) { - if (bb->data_flow_info == nullptr) return false; - - DCHECK(temp_.ssa.ti != nullptr); - return temp_.ssa.ti->Apply(bb); -} - -void MIRGraph::InferTypesEnd() { - DCHECK(temp_.ssa.ti != nullptr); - temp_.ssa.ti->Finish(); - delete temp_.ssa.ti; - temp_.ssa.ti = nullptr; -} - -bool MIRGraph::EliminateClassInitChecksGate() { - if ((cu_->disable_opt & (1 << kClassInitCheckElimination)) != 0 || - (merged_df_flags_ & DF_CLINIT) == 0) { - return false; - } - - DCHECK(temp_scoped_alloc_.get() == nullptr); - temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - - // Each insn we use here has at least 2 code units, offset/2 will be a unique index. - const size_t end = (GetNumDalvikInsns() + 1u) / 2u; - temp_.cice.indexes = temp_scoped_alloc_->AllocArray(end, kArenaAllocGrowableArray); - std::fill_n(temp_.cice.indexes, end, 0xffffu); - - uint32_t unique_class_count = 0u; - { - // Get unique_class_count and store indexes in temp_insn_data_ using a map on a nested - // ScopedArenaAllocator. - - // Embed the map value in the entry to save space. - struct MapEntry { - // Map key: the class identified by the declaring dex file and type index. - const DexFile* declaring_dex_file; - uint16_t declaring_class_idx; - // Map value: index into bit vectors of classes requiring initialization checks. - uint16_t index; - }; - struct MapEntryComparator { - bool operator()(const MapEntry& lhs, const MapEntry& rhs) const { - if (lhs.declaring_class_idx != rhs.declaring_class_idx) { - return lhs.declaring_class_idx < rhs.declaring_class_idx; - } - return lhs.declaring_dex_file < rhs.declaring_dex_file; - } - }; - - ScopedArenaAllocator allocator(&cu_->arena_stack); - ScopedArenaSet class_to_index_map(MapEntryComparator(), - allocator.Adapter()); - - // First, find all SGET/SPUTs that may need class initialization checks, record INVOKE_STATICs. - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - if (bb->block_type == kDalvikByteCode) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (IsInstructionSGetOrSPut(mir->dalvikInsn.opcode)) { - const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir); - if (!field_info.IsReferrersClass()) { - DCHECK_LT(class_to_index_map.size(), 0xffffu); - MapEntry entry = { - // Treat unresolved fields as if each had its own class. - field_info.IsResolved() ? field_info.DeclaringDexFile() - : nullptr, - field_info.IsResolved() ? field_info.DeclaringClassIndex() - : field_info.FieldIndex(), - static_cast(class_to_index_map.size()) - }; - uint16_t index = class_to_index_map.insert(entry).first->index; - // Using offset/2 for index into temp_.cice.indexes. - temp_.cice.indexes[mir->offset / 2u] = index; - } - } else if (IsInstructionInvokeStatic(mir->dalvikInsn.opcode)) { - const MirMethodLoweringInfo& method_info = GetMethodLoweringInfo(mir); - DCHECK(method_info.IsStatic()); - if (method_info.FastPath() && !method_info.IsReferrersClass()) { - MapEntry entry = { - method_info.DeclaringDexFile(), - method_info.DeclaringClassIndex(), - static_cast(class_to_index_map.size()) - }; - uint16_t index = class_to_index_map.insert(entry).first->index; - // Using offset/2 for index into temp_.cice.indexes. - temp_.cice.indexes[mir->offset / 2u] = index; - } - } - } - } - } - unique_class_count = static_cast(class_to_index_map.size()); - } - - if (unique_class_count == 0u) { - // All SGET/SPUTs refer to initialized classes. Nothing to do. - temp_.cice.indexes = nullptr; - temp_scoped_alloc_.reset(); - return false; - } - - // 2 bits for each class: is class initialized, is class in dex cache. - temp_.cice.num_class_bits = 2u * unique_class_count; - temp_.cice.work_classes_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.cice.num_class_bits, false); - temp_.cice.ending_classes_to_check_matrix = - temp_scoped_alloc_->AllocArray(GetNumBlocks(), kArenaAllocMisc); - std::fill_n(temp_.cice.ending_classes_to_check_matrix, GetNumBlocks(), nullptr); - DCHECK_GT(temp_.cice.num_class_bits, 0u); - return true; -} - -/* - * Eliminate unnecessary class initialization checks for a basic block. - */ -bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) { - DCHECK_EQ((cu_->disable_opt & (1 << kClassInitCheckElimination)), 0u); - if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) { - // Ignore the kExitBlock as well. - DCHECK(bb->first_mir_insn == nullptr); - return false; - } - - /* - * Set initial state. Catch blocks don't need any special treatment. - */ - ArenaBitVector* classes_to_check = temp_.cice.work_classes_to_check; - DCHECK(classes_to_check != nullptr); - if (bb->block_type == kEntryBlock) { - classes_to_check->SetInitialBits(temp_.cice.num_class_bits); - } else { - // Starting state is union of all incoming arcs. - bool copied_first = false; - for (BasicBlockId pred_id : bb->predecessors) { - if (temp_.cice.ending_classes_to_check_matrix[pred_id] == nullptr) { - continue; - } - if (!copied_first) { - copied_first = true; - classes_to_check->Copy(temp_.cice.ending_classes_to_check_matrix[pred_id]); - } else { - classes_to_check->Union(temp_.cice.ending_classes_to_check_matrix[pred_id]); - } - } - DCHECK(copied_first); // At least one predecessor must have been processed before this bb. - } - // At this point, classes_to_check shows which classes need clinit checks. - - // Walk through the instruction in the block, updating as necessary - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - uint16_t index = temp_.cice.indexes[mir->offset / 2u]; - if (index != 0xffffu) { - bool check_initialization = false; - bool check_dex_cache = false; - - // NOTE: index != 0xffff does not guarantee that this is an SGET/SPUT/INVOKE_STATIC. - // Dex instructions with width 1 can have the same offset/2. - - if (IsInstructionSGetOrSPut(mir->dalvikInsn.opcode)) { - check_initialization = true; - check_dex_cache = true; - } else if (IsInstructionInvokeStatic(mir->dalvikInsn.opcode)) { - check_initialization = true; - // NOTE: INVOKE_STATIC doesn't guarantee that the type will be in the dex cache. - } - - if (check_dex_cache) { - uint32_t check_dex_cache_index = 2u * index + 1u; - if (!classes_to_check->IsBitSet(check_dex_cache_index)) { - // Eliminate the class init check. - mir->optimization_flags |= MIR_CLASS_IS_IN_DEX_CACHE; - } else { - // Do the class init check. - mir->optimization_flags &= ~MIR_CLASS_IS_IN_DEX_CACHE; - } - classes_to_check->ClearBit(check_dex_cache_index); - } - if (check_initialization) { - uint32_t check_clinit_index = 2u * index; - if (!classes_to_check->IsBitSet(check_clinit_index)) { - // Eliminate the class init check. - mir->optimization_flags |= MIR_CLASS_IS_INITIALIZED; - } else { - // Do the class init check. - mir->optimization_flags &= ~MIR_CLASS_IS_INITIALIZED; - } - // Mark the class as initialized. - classes_to_check->ClearBit(check_clinit_index); - } - } - } - - // Did anything change? - bool changed = false; - ArenaBitVector* old_ending_classes_to_check = temp_.cice.ending_classes_to_check_matrix[bb->id]; - if (old_ending_classes_to_check == nullptr) { - DCHECK(temp_scoped_alloc_.get() != nullptr); - changed = classes_to_check->GetHighestBitSet() != -1; - temp_.cice.ending_classes_to_check_matrix[bb->id] = classes_to_check; - // Create a new classes_to_check for next BB. - temp_.cice.work_classes_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.cice.num_class_bits, false); - } else if (!classes_to_check->Equal(old_ending_classes_to_check)) { - changed = true; - temp_.cice.ending_classes_to_check_matrix[bb->id] = classes_to_check; - temp_.cice.work_classes_to_check = old_ending_classes_to_check; // Reuse for next BB. - } - return changed; -} - -void MIRGraph::EliminateClassInitChecksEnd() { - // Clean up temporaries. - temp_.cice.num_class_bits = 0u; - temp_.cice.work_classes_to_check = nullptr; - temp_.cice.ending_classes_to_check_matrix = nullptr; - DCHECK(temp_.cice.indexes != nullptr); - temp_.cice.indexes = nullptr; - DCHECK(temp_scoped_alloc_.get() != nullptr); - temp_scoped_alloc_.reset(); -} - -static void DisableGVNDependentOptimizations(CompilationUnit* cu) { - cu->disable_opt |= (1u << kGvnDeadCodeElimination); -} - -bool MIRGraph::ApplyGlobalValueNumberingGate() { - if (GlobalValueNumbering::Skip(cu_)) { - DisableGVNDependentOptimizations(cu_); - return false; - } - - DCHECK(temp_scoped_alloc_ == nullptr); - temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_.gvn.ifield_ids = - GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), ifield_lowering_infos_); - temp_.gvn.sfield_ids = - GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), sfield_lowering_infos_); - DCHECK(temp_.gvn.gvn == nullptr); - temp_.gvn.gvn = new (temp_scoped_alloc_.get()) GlobalValueNumbering( - cu_, temp_scoped_alloc_.get(), GlobalValueNumbering::kModeGvn); - return true; -} - -bool MIRGraph::ApplyGlobalValueNumbering(BasicBlock* bb) { - DCHECK(temp_.gvn.gvn != nullptr); - LocalValueNumbering* lvn = temp_.gvn.gvn->PrepareBasicBlock(bb); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - lvn->GetValueNumber(mir); - } - } - bool change = (lvn != nullptr) && temp_.gvn.gvn->FinishBasicBlock(bb); - return change; -} - -void MIRGraph::ApplyGlobalValueNumberingEnd() { - // Perform modifications. - DCHECK(temp_.gvn.gvn != nullptr); - if (temp_.gvn.gvn->Good()) { - temp_.gvn.gvn->StartPostProcessing(); - if (max_nested_loops_ != 0u) { - TopologicalSortIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - ScopedArenaAllocator allocator(&cu_->arena_stack); // Reclaim memory after each LVN. - LocalValueNumbering* lvn = temp_.gvn.gvn->PrepareBasicBlock(bb, &allocator); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - lvn->GetValueNumber(mir); - } - bool change = temp_.gvn.gvn->FinishBasicBlock(bb); - DCHECK(!change) << PrettyMethod(cu_->method_idx, *cu_->dex_file); - } - } - } - // GVN was successful, running the LVN would be useless. - cu_->disable_opt |= (1u << kLocalValueNumbering); - } else { - LOG(WARNING) << "GVN failed for " << PrettyMethod(cu_->method_idx, *cu_->dex_file); - DisableGVNDependentOptimizations(cu_); - } -} - -bool MIRGraph::EliminateDeadCodeGate() { - if ((cu_->disable_opt & (1 << kGvnDeadCodeElimination)) != 0 || temp_.gvn.gvn == nullptr) { - return false; - } - DCHECK(temp_scoped_alloc_ != nullptr); - temp_.gvn.dce = new (temp_scoped_alloc_.get()) GvnDeadCodeElimination(temp_.gvn.gvn, - temp_scoped_alloc_.get()); - return true; -} - -bool MIRGraph::EliminateDeadCode(BasicBlock* bb) { - DCHECK(temp_scoped_alloc_ != nullptr); - DCHECK(temp_.gvn.gvn != nullptr); - if (bb->block_type != kDalvikByteCode) { - return false; - } - DCHECK(temp_.gvn.dce != nullptr); - temp_.gvn.dce->Apply(bb); - return false; // No need to repeat. -} - -void MIRGraph::EliminateDeadCodeEnd() { - if (kIsDebugBuild) { - // DCE can make some previously dead vregs alive again. Make sure the obsolete - // live-in information is not used anymore. - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - if (bb->data_flow_info != nullptr) { - bb->data_flow_info->live_in_v = nullptr; - } - } - } -} - -void MIRGraph::GlobalValueNumberingCleanup() { - // If the GVN didn't run, these pointers should be null and everything is effectively no-op. - delete temp_.gvn.dce; - temp_.gvn.dce = nullptr; - delete temp_.gvn.gvn; - temp_.gvn.gvn = nullptr; - temp_.gvn.ifield_ids = nullptr; - temp_.gvn.sfield_ids = nullptr; - temp_scoped_alloc_.reset(); -} - -void MIRGraph::ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke, MIR* iget_or_iput) { - uint32_t method_index = invoke->meta.method_lowering_info; - if (temp_.smi.processed_indexes->IsBitSet(method_index)) { - iget_or_iput->meta.ifield_lowering_info = temp_.smi.lowering_infos[method_index]; - DCHECK_EQ(field_idx, GetIFieldLoweringInfo(iget_or_iput).FieldIndex()); - return; - } - - const MirMethodLoweringInfo& method_info = GetMethodLoweringInfo(invoke); - MethodReference target = method_info.GetTargetMethod(); - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - Handle dex_cache( - hs.NewHandle(cu_->class_linker->FindDexCache(hs.Self(), *target.dex_file))); - DexCompilationUnit inlined_unit(cu_, - cu_->class_loader, - cu_->class_linker, - *target.dex_file, - nullptr /* code_item not used */, - 0u /* class_def_idx not used */, - target.dex_method_index, - 0u /* access_flags not used */, - nullptr /* verified_method not used */, - dex_cache); - DexMemAccessType type = IGetOrIPutMemAccessType(iget_or_iput->dalvikInsn.opcode); - MirIFieldLoweringInfo inlined_field_info(field_idx, type, false); - MirIFieldLoweringInfo::Resolve(soa, cu_->compiler_driver, &inlined_unit, &inlined_field_info, 1u); - DCHECK(inlined_field_info.IsResolved()); - - uint32_t field_info_index = ifield_lowering_infos_.size(); - ifield_lowering_infos_.push_back(inlined_field_info); - temp_.smi.processed_indexes->SetBit(method_index); - temp_.smi.lowering_infos[method_index] = field_info_index; - iget_or_iput->meta.ifield_lowering_info = field_info_index; -} - -bool MIRGraph::InlineSpecialMethodsGate() { - if ((cu_->disable_opt & (1 << kSuppressMethodInlining)) != 0 || - method_lowering_infos_.size() == 0u) { - return false; - } - if (cu_->compiler_driver->GetMethodInlinerMap() == nullptr) { - // This isn't the Quick compiler. - return false; - } - return true; -} - -void MIRGraph::InlineSpecialMethodsStart() { - // Prepare for inlining getters/setters. Since we're inlining at most 1 IGET/IPUT from - // each INVOKE, we can index the data by the MIR::meta::method_lowering_info index. - - DCHECK(temp_scoped_alloc_.get() == nullptr); - temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_.smi.num_indexes = method_lowering_infos_.size(); - temp_.smi.processed_indexes = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.smi.num_indexes, false); - temp_.smi.processed_indexes->ClearAllBits(); - temp_.smi.lowering_infos = - temp_scoped_alloc_->AllocArray(temp_.smi.num_indexes, kArenaAllocGrowableArray); -} - -void MIRGraph::InlineSpecialMethods(BasicBlock* bb) { - if (bb->block_type != kDalvikByteCode) { - return; - } - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) { - continue; - } - if (!(mir->dalvikInsn.FlagsOf() & Instruction::kInvoke)) { - continue; - } - const MirMethodLoweringInfo& method_info = GetMethodLoweringInfo(mir); - if (!method_info.FastPath() || !method_info.IsSpecial()) { - continue; - } - - InvokeType sharp_type = method_info.GetSharpType(); - if ((sharp_type != kDirect) && (sharp_type != kStatic)) { - continue; - } - - if (sharp_type == kStatic) { - bool needs_clinit = !method_info.IsClassInitialized() && - ((mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0); - if (needs_clinit) { - continue; - } - } - - DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr); - MethodReference target = method_info.GetTargetMethod(); - if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(target.dex_file) - ->GenInline(this, bb, mir, target.dex_method_index)) { - if (cu_->verbose || cu_->print_pass) { - LOG(INFO) << "SpecialMethodInliner: Inlined " << method_info.GetInvokeType() << " (" - << sharp_type << ") call to \"" << PrettyMethod(target.dex_method_index, - *target.dex_file) - << "\" from \"" << PrettyMethod(cu_->method_idx, *cu_->dex_file) - << "\" @0x" << std::hex << mir->offset; - } - } - } -} - -void MIRGraph::InlineSpecialMethodsEnd() { - // Clean up temporaries. - DCHECK(temp_.smi.lowering_infos != nullptr); - temp_.smi.lowering_infos = nullptr; - temp_.smi.num_indexes = 0u; - DCHECK(temp_.smi.processed_indexes != nullptr); - temp_.smi.processed_indexes = nullptr; - DCHECK(temp_scoped_alloc_.get() != nullptr); - temp_scoped_alloc_.reset(); -} - -void MIRGraph::DumpCheckStats() { - Checkstats* stats = - static_cast(arena_->Alloc(sizeof(Checkstats), kArenaAllocDFInfo)); - checkstats_ = stats; - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - CountChecks(bb); - } - if (stats->null_checks > 0) { - float eliminated = static_cast(stats->null_checks_eliminated); - float checks = static_cast(stats->null_checks); - LOG(INFO) << "Null Checks: " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " - << stats->null_checks_eliminated << " of " << stats->null_checks << " -> " - << (eliminated/checks) * 100.0 << "%"; - } - if (stats->range_checks > 0) { - float eliminated = static_cast(stats->range_checks_eliminated); - float checks = static_cast(stats->range_checks); - LOG(INFO) << "Range Checks: " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " - << stats->range_checks_eliminated << " of " << stats->range_checks << " -> " - << (eliminated/checks) * 100.0 << "%"; - } -} - -bool MIRGraph::BuildExtendedBBList(class BasicBlock* bb) { - if (bb->visited) return false; - if (!((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode) - || (bb->block_type == kExitBlock))) { - // Ignore special blocks - bb->visited = true; - return false; - } - // Must be head of extended basic block. - BasicBlock* start_bb = bb; - extended_basic_blocks_.push_back(bb->id); - bool terminated_by_return = false; - bool do_local_value_numbering = false; - // Visit blocks strictly dominated by this head. - while (bb != nullptr) { - bb->visited = true; - terminated_by_return |= bb->terminated_by_return; - do_local_value_numbering |= bb->use_lvn; - bb = NextDominatedBlock(bb); - } - if (terminated_by_return || do_local_value_numbering) { - // Do lvn for all blocks in this extended set. - bb = start_bb; - while (bb != nullptr) { - bb->use_lvn = do_local_value_numbering; - bb->dominates_return = terminated_by_return; - bb = NextDominatedBlock(bb); - } - } - return false; // Not iterative - return value will be ignored -} - -void MIRGraph::BasicBlockOptimizationStart() { - if ((cu_->disable_opt & (1 << kLocalValueNumbering)) == 0) { - temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_.gvn.ifield_ids = - GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), ifield_lowering_infos_); - temp_.gvn.sfield_ids = - GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), sfield_lowering_infos_); - } -} - -void MIRGraph::BasicBlockOptimization() { - if ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) { - ClearAllVisitedFlags(); - PreOrderDfsIterator iter2(this); - for (BasicBlock* bb = iter2.Next(); bb != nullptr; 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 != nullptr; bb = iter.Next()) { - BasicBlockOpt(bb); - } - } -} - -void MIRGraph::BasicBlockOptimizationEnd() { - // Clean up after LVN. - temp_.gvn.ifield_ids = nullptr; - temp_.gvn.sfield_ids = nullptr; - temp_scoped_alloc_.reset(); -} - -void MIRGraph::StringChange() { - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - // Look for new instance opcodes, skip otherwise - Instruction::Code opcode = mir->dalvikInsn.opcode; - if (opcode == Instruction::NEW_INSTANCE) { - uint32_t type_idx = mir->dalvikInsn.vB; - if (cu_->compiler_driver->IsStringTypeIndex(type_idx, cu_->dex_file)) { - LOG(FATAL) << "Quick cannot compile String allocations"; - } - } else if ((opcode == Instruction::INVOKE_DIRECT) || - (opcode == Instruction::INVOKE_DIRECT_RANGE)) { - uint32_t method_idx = mir->dalvikInsn.vB; - DexFileMethodInliner* inliner = - cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file); - if (inliner->IsStringInitMethodIndex(method_idx)) { - LOG(FATAL) << "Quick cannot compile String allocations"; - } - } - } - } -} - -bool MIRGraph::EliminateSuspendChecksGate() { - if (kLeafOptimization || // Incompatible (could create loops without suspend checks). - (cu_->disable_opt & (1 << kSuspendCheckElimination)) != 0 || // Disabled. - GetMaxNestedLoops() == 0u || // Nothing to do. - GetMaxNestedLoops() >= 32u || // Only 32 bits in suspend_checks_in_loops_[.]. - // Exclude 32 as well to keep bit shifts well-defined. - !HasInvokes()) { // No invokes to actually eliminate any suspend checks. - return false; - } - suspend_checks_in_loops_ = arena_->AllocArray(GetNumBlocks(), kArenaAllocMisc); - return true; -} - -bool MIRGraph::EliminateSuspendChecks(BasicBlock* bb) { - if (bb->block_type != kDalvikByteCode) { - return false; - } - DCHECK_EQ(GetTopologicalSortOrderLoopHeadStack()->size(), bb->nesting_depth); - if (bb->nesting_depth == 0u) { - // Out of loops. - DCHECK_EQ(suspend_checks_in_loops_[bb->id], 0u); // The array was zero-initialized. - return false; - } - uint32_t suspend_checks_in_loops = (1u << bb->nesting_depth) - 1u; // Start with all loop heads. - bool found_invoke = false; - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if ((IsInstructionInvoke(mir->dalvikInsn.opcode) || - IsInstructionQuickInvoke(mir->dalvikInsn.opcode)) && - !GetMethodLoweringInfo(mir).IsIntrinsic()) { - // Non-intrinsic invoke, rely on a suspend point in the invoked method. - found_invoke = true; - break; - } - } - if (!found_invoke) { - // Intersect suspend checks from predecessors. - uint16_t bb_topo_idx = topological_order_indexes_[bb->id]; - uint32_t pred_mask_union = 0u; - for (BasicBlockId pred_id : bb->predecessors) { - uint16_t pred_topo_idx = topological_order_indexes_[pred_id]; - if (pred_topo_idx < bb_topo_idx) { - // Determine the loop depth of the predecessors relative to this block. - size_t pred_loop_depth = topological_order_loop_head_stack_.size(); - while (pred_loop_depth != 0u && - pred_topo_idx < topological_order_loop_head_stack_[pred_loop_depth - 1].first) { - --pred_loop_depth; - } - DCHECK_LE(pred_loop_depth, GetBasicBlock(pred_id)->nesting_depth); - uint32_t pred_mask = (1u << pred_loop_depth) - 1u; - // Intersect pred_mask bits in suspend_checks_in_loops with - // suspend_checks_in_loops_[pred_id]. - uint32_t pred_loops_without_checks = pred_mask & ~suspend_checks_in_loops_[pred_id]; - suspend_checks_in_loops = suspend_checks_in_loops & ~pred_loops_without_checks; - pred_mask_union |= pred_mask; - } - } - // DCHECK_EQ() may not hold for unnatural loop heads, so use DCHECK_GE(). - DCHECK_GE(((1u << (IsLoopHead(bb->id) ? bb->nesting_depth - 1u: bb->nesting_depth)) - 1u), - pred_mask_union); - suspend_checks_in_loops &= pred_mask_union; - } - suspend_checks_in_loops_[bb->id] = suspend_checks_in_loops; - if (suspend_checks_in_loops == 0u) { - return false; - } - // Apply MIR_IGNORE_SUSPEND_CHECK if appropriate. - if (bb->taken != NullBasicBlockId) { - DCHECK(bb->last_mir_insn != nullptr); - DCHECK(IsInstructionIfCc(bb->last_mir_insn->dalvikInsn.opcode) || - IsInstructionIfCcZ(bb->last_mir_insn->dalvikInsn.opcode) || - IsInstructionGoto(bb->last_mir_insn->dalvikInsn.opcode) || - (static_cast(bb->last_mir_insn->dalvikInsn.opcode) >= kMirOpFusedCmplFloat && - static_cast(bb->last_mir_insn->dalvikInsn.opcode) <= kMirOpFusedCmpLong)); - if (!IsSuspendCheckEdge(bb, bb->taken) && - (bb->fall_through == NullBasicBlockId || !IsSuspendCheckEdge(bb, bb->fall_through))) { - bb->last_mir_insn->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK; - } - } else if (bb->fall_through != NullBasicBlockId && IsSuspendCheckEdge(bb, bb->fall_through)) { - // We've got a fall-through suspend edge. Add an artificial GOTO to force suspend check. - MIR* mir = NewMIR(); - mir->dalvikInsn.opcode = Instruction::GOTO; - mir->dalvikInsn.vA = 0; // Branch offset. - mir->offset = GetBasicBlock(bb->fall_through)->start_offset; - mir->m_unit_index = current_method_; - mir->ssa_rep = reinterpret_cast( - arena_->Alloc(sizeof(SSARepresentation), kArenaAllocDFInfo)); // Zero-initialized. - bb->AppendMIR(mir); - std::swap(bb->fall_through, bb->taken); // The fall-through has become taken. - } - return true; -} - -bool MIRGraph::CanThrow(MIR* mir) const { - if ((mir->dalvikInsn.FlagsOf() & Instruction::kThrow) == 0) { - return false; - } - const int opt_flags = mir->optimization_flags; - uint64_t df_attributes = GetDataFlowAttributes(mir); - - // First, check if the insn can still throw NPE. - if (((df_attributes & DF_HAS_NULL_CHKS) != 0) && ((opt_flags & MIR_IGNORE_NULL_CHECK) == 0)) { - return true; - } - - // Now process specific instructions. - if ((df_attributes & DF_IFIELD) != 0) { - // The IGET/IPUT family. We have processed the IGET/IPUT null check above. - DCHECK_NE(opt_flags & MIR_IGNORE_NULL_CHECK, 0); - // If not fast, weird things can happen and the insn can throw. - const MirIFieldLoweringInfo& field_info = GetIFieldLoweringInfo(mir); - bool fast = (df_attributes & DF_DA) != 0 ? field_info.FastGet() : field_info.FastPut(); - return !fast; - } else if ((df_attributes & DF_SFIELD) != 0) { - // The SGET/SPUT family. Check for potentially throwing class initialization. - // Also, if not fast, weird things can happen and the insn can throw. - const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir); - bool fast = (df_attributes & DF_DA) != 0 ? field_info.FastGet() : field_info.FastPut(); - bool is_class_initialized = field_info.IsClassInitialized() || - ((mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0); - return !(fast && is_class_initialized); - } else if ((df_attributes & DF_HAS_RANGE_CHKS) != 0) { - // Only AGET/APUT have range checks. We have processed the AGET/APUT null check above. - DCHECK_NE(opt_flags & MIR_IGNORE_NULL_CHECK, 0); - // Non-throwing only if range check has been eliminated. - return ((opt_flags & MIR_IGNORE_RANGE_CHECK) == 0); - } else if (mir->dalvikInsn.opcode == Instruction::CHECK_CAST && - (opt_flags & MIR_IGNORE_CHECK_CAST) != 0) { - return false; - } else if (mir->dalvikInsn.opcode == Instruction::ARRAY_LENGTH || - static_cast(mir->dalvikInsn.opcode) == kMirOpNullCheck) { - // No more checks for these (null check was processed above). - return false; - } - return true; -} - -bool MIRGraph::HasAntiDependency(MIR* first, MIR* second) { - DCHECK(first->ssa_rep != nullptr); - DCHECK(second->ssa_rep != nullptr); - if ((second->ssa_rep->num_defs > 0) && (first->ssa_rep->num_uses > 0)) { - int vreg0 = SRegToVReg(second->ssa_rep->defs[0]); - int vreg1 = (second->ssa_rep->num_defs == 2) ? - SRegToVReg(second->ssa_rep->defs[1]) : INVALID_VREG; - for (int i = 0; i < first->ssa_rep->num_uses; i++) { - int32_t use = SRegToVReg(first->ssa_rep->uses[i]); - if (use == vreg0 || use == vreg1) { - return true; - } - } - } - return false; -} - -void MIRGraph::CombineMultiplyAdd(MIR* mul_mir, MIR* add_mir, bool mul_is_first_addend, - bool is_wide, bool is_sub) { - if (is_wide) { - if (is_sub) { - add_mir->dalvikInsn.opcode = static_cast(kMirOpMsubLong); - } else { - add_mir->dalvikInsn.opcode = static_cast(kMirOpMaddLong); - } - } else { - if (is_sub) { - add_mir->dalvikInsn.opcode = static_cast(kMirOpMsubInt); - } else { - add_mir->dalvikInsn.opcode = static_cast(kMirOpMaddInt); - } - } - add_mir->ssa_rep->num_uses = is_wide ? 6 : 3; - int32_t addend0 = INVALID_SREG; - int32_t addend1 = INVALID_SREG; - if (is_wide) { - addend0 = mul_is_first_addend ? add_mir->ssa_rep->uses[2] : add_mir->ssa_rep->uses[0]; - addend1 = mul_is_first_addend ? add_mir->ssa_rep->uses[3] : add_mir->ssa_rep->uses[1]; - } else { - addend0 = mul_is_first_addend ? add_mir->ssa_rep->uses[1] : add_mir->ssa_rep->uses[0]; - } - - AllocateSSAUseData(add_mir, add_mir->ssa_rep->num_uses); - add_mir->ssa_rep->uses[0] = mul_mir->ssa_rep->uses[0]; - add_mir->ssa_rep->uses[1] = mul_mir->ssa_rep->uses[1]; - // Clear the original multiply product ssa use count, as it is not used anymore. - raw_use_counts_[mul_mir->ssa_rep->defs[0]] = 0; - use_counts_[mul_mir->ssa_rep->defs[0]] = 0; - if (is_wide) { - DCHECK_EQ(add_mir->ssa_rep->num_uses, 6); - add_mir->ssa_rep->uses[2] = mul_mir->ssa_rep->uses[2]; - add_mir->ssa_rep->uses[3] = mul_mir->ssa_rep->uses[3]; - add_mir->ssa_rep->uses[4] = addend0; - add_mir->ssa_rep->uses[5] = addend1; - raw_use_counts_[mul_mir->ssa_rep->defs[1]] = 0; - use_counts_[mul_mir->ssa_rep->defs[1]] = 0; - } else { - DCHECK_EQ(add_mir->ssa_rep->num_uses, 3); - add_mir->ssa_rep->uses[2] = addend0; - } - // Copy in the decoded instruction information. - add_mir->dalvikInsn.vB = SRegToVReg(add_mir->ssa_rep->uses[0]); - if (is_wide) { - add_mir->dalvikInsn.vC = SRegToVReg(add_mir->ssa_rep->uses[2]); - add_mir->dalvikInsn.arg[0] = SRegToVReg(add_mir->ssa_rep->uses[4]); - } else { - add_mir->dalvikInsn.vC = SRegToVReg(add_mir->ssa_rep->uses[1]); - add_mir->dalvikInsn.arg[0] = SRegToVReg(add_mir->ssa_rep->uses[2]); - } - // Original multiply MIR is set to Nop. - mul_mir->dalvikInsn.opcode = static_cast(kMirOpNop); -} - -void MIRGraph::MultiplyAddOpt(BasicBlock* bb) { - if (bb->block_type == kDead) { - return; - } - ScopedArenaAllocator allocator(&cu_->arena_stack); - ScopedArenaSafeMap ssa_mul_map(std::less(), allocator.Adapter()); - ScopedArenaSafeMap::iterator map_it; - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - Instruction::Code opcode = mir->dalvikInsn.opcode; - bool is_sub = true; - bool is_candidate_multiply = false; - switch (opcode) { - case Instruction::MUL_INT: - case Instruction::MUL_INT_2ADDR: - is_candidate_multiply = true; - break; - case Instruction::MUL_LONG: - case Instruction::MUL_LONG_2ADDR: - if (cu_->target64) { - is_candidate_multiply = true; - } - break; - case Instruction::ADD_INT: - case Instruction::ADD_INT_2ADDR: - is_sub = false; - FALLTHROUGH_INTENDED; - case Instruction::SUB_INT: - case Instruction::SUB_INT_2ADDR: - if (((map_it = ssa_mul_map.find(mir->ssa_rep->uses[0])) != ssa_mul_map.end()) && !is_sub) { - // a*b+c - CombineMultiplyAdd(map_it->second, mir, true /* product is the first addend */, - false /* is_wide */, false /* is_sub */); - ssa_mul_map.erase(mir->ssa_rep->uses[0]); - } else if ((map_it = ssa_mul_map.find(mir->ssa_rep->uses[1])) != ssa_mul_map.end()) { - // c+a*b or c-a*b - CombineMultiplyAdd(map_it->second, mir, false /* product is the second addend */, - false /* is_wide */, is_sub); - ssa_mul_map.erase(map_it); - } - break; - case Instruction::ADD_LONG: - case Instruction::ADD_LONG_2ADDR: - is_sub = false; - FALLTHROUGH_INTENDED; - case Instruction::SUB_LONG: - case Instruction::SUB_LONG_2ADDR: - if (!cu_->target64) { - break; - } - if ((map_it = ssa_mul_map.find(mir->ssa_rep->uses[0])) != ssa_mul_map.end() && !is_sub) { - // a*b+c - CombineMultiplyAdd(map_it->second, mir, true /* product is the first addend */, - true /* is_wide */, false /* is_sub */); - ssa_mul_map.erase(map_it); - } else if ((map_it = ssa_mul_map.find(mir->ssa_rep->uses[2])) != ssa_mul_map.end()) { - // c+a*b or c-a*b - CombineMultiplyAdd(map_it->second, mir, false /* product is the second addend */, - true /* is_wide */, is_sub); - ssa_mul_map.erase(map_it); - } - break; - default: - if (!ssa_mul_map.empty() && CanThrow(mir)) { - // Should not combine multiply and add MIRs across potential exception. - ssa_mul_map.clear(); - } - break; - } - - // Exclude the case when an MIR writes a vreg which is previous candidate multiply MIR's uses. - // It is because that current RA may allocate the same physical register to them. For this - // kind of cases, the multiplier has been updated, we should not use updated value to the - // multiply-add insn. - if (ssa_mul_map.size() > 0) { - for (auto it = ssa_mul_map.begin(); it != ssa_mul_map.end();) { - MIR* mul = it->second; - if (HasAntiDependency(mul, mir)) { - it = ssa_mul_map.erase(it); - } else { - ++it; - } - } - } - - if (is_candidate_multiply && - (GetRawUseCount(mir->ssa_rep->defs[0]) == 1) && (mir->next != nullptr)) { - ssa_mul_map.Put(mir->ssa_rep->defs[0], mir); - } - } -} - -} // namespace art diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc deleted file mode 100644 index a0cedff9b8aa8b71ce7cb4c4b4c8eacbd90e876d..0000000000000000000000000000000000000000 --- a/compiler/dex/mir_optimization_test.cc +++ /dev/null @@ -1,1186 +0,0 @@ -/* - * 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 - -#include "base/logging.h" -#include "dataflow_iterator.h" -#include "dataflow_iterator-inl.h" -#include "dex/compiler_ir.h" -#include "dex/mir_field_info.h" -#include "gtest/gtest.h" - -namespace art { - -class MirOptimizationTest : public testing::Test { - protected: - struct BBDef { - static constexpr size_t kMaxSuccessors = 4; - static constexpr size_t kMaxPredecessors = 4; - - BBType type; - size_t num_successors; - BasicBlockId successors[kMaxPredecessors]; - size_t num_predecessors; - BasicBlockId predecessors[kMaxPredecessors]; - }; - - struct MethodDef { - uint16_t method_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_class_idx; - uint16_t declaring_method_idx; - InvokeType invoke_type; - InvokeType sharp_type; - bool is_referrers_class; - bool is_initialized; - }; - - struct MIRDef { - BasicBlockId bbid; - Instruction::Code opcode; - uint32_t field_or_method_info; - uint32_t vA; - uint32_t vB; - uint32_t vC; - }; - -#define DEF_SUCC0() \ - 0u, { } -#define DEF_SUCC1(s1) \ - 1u, { s1 } -#define DEF_SUCC2(s1, s2) \ - 2u, { s1, s2 } -#define DEF_SUCC3(s1, s2, s3) \ - 3u, { s1, s2, s3 } -#define DEF_SUCC4(s1, s2, s3, s4) \ - 4u, { s1, s2, s3, s4 } -#define DEF_PRED0() \ - 0u, { } -#define DEF_PRED1(p1) \ - 1u, { p1 } -#define DEF_PRED2(p1, p2) \ - 2u, { p1, p2 } -#define DEF_PRED3(p1, p2, p3) \ - 3u, { p1, p2, p3 } -#define DEF_PRED4(p1, p2, p3, p4) \ - 4u, { p1, p2, p3, p4 } -#define DEF_BB(type, succ, pred) \ - { type, succ, pred } - -#define DEF_SGET_SPUT(bb, opcode, vA, field_info) \ - { bb, opcode, field_info, vA, 0u, 0u } -#define DEF_IGET_IPUT(bb, opcode, vA, vB, field_info) \ - { bb, opcode, field_info, vA, vB, 0u } -#define DEF_AGET_APUT(bb, opcode, vA, vB, vC) \ - { bb, opcode, 0u, vA, vB, vC } -#define DEF_INVOKE(bb, opcode, vC, method_info) \ - { bb, opcode, method_info, 0u, 0u, vC } -#define DEF_OTHER0(bb, opcode) \ - { bb, opcode, 0u, 0u, 0u, 0u } -#define DEF_OTHER1(bb, opcode, vA) \ - { bb, opcode, 0u, vA, 0u, 0u } -#define DEF_OTHER2(bb, opcode, vA, vB) \ - { bb, opcode, 0u, vA, vB, 0u } - - void DoPrepareBasicBlocks(const BBDef* defs, size_t count) { - cu_.mir_graph->block_id_map_.clear(); - cu_.mir_graph->block_list_.clear(); - ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block. - ASSERT_EQ(kNullBlock, defs[0].type); - ASSERT_EQ(kEntryBlock, defs[1].type); - ASSERT_EQ(kExitBlock, defs[2].type); - for (size_t i = 0u; i != count; ++i) { - const BBDef* def = &defs[i]; - BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type); - if (def->num_successors <= 2) { - bb->successor_block_list_type = kNotUsed; - bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u; - bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u; - } else { - bb->successor_block_list_type = kPackedSwitch; - bb->fall_through = 0u; - bb->taken = 0u; - bb->successor_blocks.reserve(def->num_successors); - for (size_t j = 0u; j != def->num_successors; ++j) { - SuccessorBlockInfo* successor_block_info = - static_cast(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessors)); - successor_block_info->block = j; - successor_block_info->key = 0u; // Not used by class init check elimination. - bb->successor_blocks.push_back(successor_block_info); - } - } - bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors); - if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) { - bb->data_flow_info = static_cast( - cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo)); - } - } - ASSERT_EQ(count, cu_.mir_graph->block_list_.size()); - cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1]; - ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type); - cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2]; - ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type); - } - - template - void PrepareBasicBlocks(const BBDef (&defs)[count]) { - DoPrepareBasicBlocks(defs, count); - } - - void PrepareSingleBlock() { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)), - }; - PrepareBasicBlocks(bbs); - } - - void PrepareDiamond() { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), - }; - PrepareBasicBlocks(bbs); - } - - void PrepareLoop() { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), - }; - PrepareBasicBlocks(bbs); - } - - void PrepareNestedLoopsWhile_While() { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(8)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 8), DEF_PRED2(3, 7)), // Outer while loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)), // Inner while loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)), // "taken" loops to inner head. - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(5)), // "taken" loops to outer head. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), - }; - PrepareBasicBlocks(bbs); - } - - void PrepareNestedLoopsWhile_WhileWhile() { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(10)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 10), DEF_PRED2(3, 9)), // Outer while loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)), // Inner while loop head 1. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)), // Loops to inner head 1. - DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED2(5, 8)), // Inner while loop head 2. - DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(7)), // loops to inner head 2. - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(7)), // loops to outer head. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), - }; - PrepareBasicBlocks(bbs); - } - - void PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge() { - // Extra edge from the first inner loop body to second inner loop body (6u->8u). - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(10)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 10), DEF_PRED2(3, 9)), // Outer while loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)), // Inner while loop head 1. - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 8), DEF_PRED1(5)), // Loops to inner head 1. - DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED2(5, 8)), // Inner while loop head 2. - DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED2(7, 6)), // loops to inner head 2. - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(7)), // loops to outer head. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), - }; - PrepareBasicBlocks(bbs); - } - - void PrepareCatch() { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), // The top. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // The throwing insn. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Catch handler. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // The merged block. - }; - PrepareBasicBlocks(bbs); - BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u); - catch_handler->catch_entry = true; - // Add successor block info to the check block. - BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u); - check_bb->successor_block_list_type = kCatch; - SuccessorBlockInfo* successor_block_info = reinterpret_cast - (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); - successor_block_info->block = catch_handler->id; - check_bb->successor_blocks.push_back(successor_block_info); - } - - void DoPrepareMethods(const MethodDef* defs, size_t count) { - cu_.mir_graph->method_lowering_infos_.clear(); - cu_.mir_graph->method_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const MethodDef* def = &defs[i]; - MirMethodLoweringInfo method_info(def->method_idx, def->invoke_type, false); - if (def->declaring_dex_file != 0u) { - method_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - method_info.declaring_class_idx_ = def->declaring_class_idx; - method_info.declaring_method_idx_ = def->declaring_method_idx; - } - ASSERT_EQ(def->invoke_type != kStatic, def->sharp_type != kStatic); - method_info.flags_ = - ((def->invoke_type == kStatic) ? MirMethodLoweringInfo::kFlagIsStatic : 0u) | - MirMethodLoweringInfo::kFlagFastPath | - (static_cast(def->invoke_type) << MirMethodLoweringInfo::kBitInvokeTypeBegin) | - (static_cast(def->sharp_type) << MirMethodLoweringInfo::kBitSharpTypeBegin) | - ((def->is_referrers_class) ? MirMethodLoweringInfo::kFlagIsReferrersClass : 0u) | - ((def->is_initialized == kStatic) ? MirMethodLoweringInfo::kFlagClassIsInitialized : 0u); - ASSERT_EQ(def->declaring_dex_file != 0u, method_info.IsResolved()); - cu_.mir_graph->method_lowering_infos_.push_back(method_info); - } - } - - template - void PrepareMethods(const MethodDef (&defs)[count]) { - DoPrepareMethods(defs, count); - } - - void DoPrepareMIRs(const MIRDef* defs, size_t count) { - mir_count_ = count; - mirs_ = cu_.arena.AllocArray(count, kArenaAllocMIR); - uint64_t merged_df_flags = 0u; - for (size_t i = 0u; i != count; ++i) { - const MIRDef* def = &defs[i]; - MIR* mir = &mirs_[i]; - mir->dalvikInsn.opcode = def->opcode; - ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size()); - BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid]; - bb->AppendMIR(mir); - if (IsInstructionIGetOrIPut(def->opcode)) { - ASSERT_LT(def->field_or_method_info, cu_.mir_graph->ifield_lowering_infos_.size()); - mir->meta.ifield_lowering_info = def->field_or_method_info; - ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_or_method_info].MemAccessType(), - IGetOrIPutMemAccessType(def->opcode)); - } else if (IsInstructionSGetOrSPut(def->opcode)) { - ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.size()); - mir->meta.sfield_lowering_info = def->field_or_method_info; - ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_or_method_info].MemAccessType(), - SGetOrSPutMemAccessType(def->opcode)); - } else if (IsInstructionInvoke(def->opcode)) { - ASSERT_LT(def->field_or_method_info, cu_.mir_graph->method_lowering_infos_.size()); - mir->meta.method_lowering_info = def->field_or_method_info; - } - mir->dalvikInsn.vA = def->vA; - mir->dalvikInsn.vB = def->vB; - mir->dalvikInsn.vC = def->vC; - mir->ssa_rep = nullptr; - mir->offset = 2 * i; // All insns need to be at least 2 code units long. - mir->optimization_flags = 0u; - merged_df_flags |= MIRGraph::GetDataFlowAttributes(def->opcode); - } - cu_.mir_graph->merged_df_flags_ = merged_df_flags; - - code_item_ = static_cast( - cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc)); - memset(code_item_, 0, sizeof(DexFile::CodeItem)); - code_item_->insns_size_in_code_units_ = 2u * count; - cu_.mir_graph->current_code_item_ = code_item_; - } - - template - void PrepareMIRs(const MIRDef (&defs)[count]) { - DoPrepareMIRs(defs, count); - } - - MirOptimizationTest() - : pool_(), - cu_(&pool_, kRuntimeISA, nullptr, nullptr), - mir_count_(0u), - mirs_(nullptr), - code_item_(nullptr) { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); - cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test. - } - - ArenaPool pool_; - CompilationUnit cu_; - size_t mir_count_; - MIR* mirs_; - DexFile::CodeItem* code_item_; -}; - -class ClassInitCheckEliminationTest : public MirOptimizationTest { - protected: - struct SFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_class_idx; - uint16_t declaring_field_idx; - DexMemAccessType type; - }; - - void DoPrepareSFields(const SFieldDef* defs, size_t count) { - cu_.mir_graph->sfield_lowering_infos_.clear(); - cu_.mir_graph->sfield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const SFieldDef* def = &defs[i]; - MirSFieldLoweringInfo field_info(def->field_idx, def->type); - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_class_idx_ = def->declaring_class_idx; - field_info.declaring_field_idx_ = def->declaring_field_idx; - // We don't care about the volatile flag in these tests. - } - ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved()); - ASSERT_FALSE(field_info.IsClassInitialized()); - cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareSFields(const SFieldDef (&defs)[count]) { - DoPrepareSFields(defs, count); - } - - void PerformClassInitCheckElimination() { - cu_.mir_graph->ComputeDFSOrders(); - bool gate_result = cu_.mir_graph->EliminateClassInitChecksGate(); - ASSERT_TRUE(gate_result); - RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get()); - bool change = false; - for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { - change = cu_.mir_graph->EliminateClassInitChecks(bb); - } - cu_.mir_graph->EliminateClassInitChecksEnd(); - } - - ClassInitCheckEliminationTest() - : MirOptimizationTest() { - } -}; - -class NullCheckEliminationTest : public MirOptimizationTest { - protected: - struct IFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_class_idx; - uint16_t declaring_field_idx; - DexMemAccessType type; - }; - - void DoPrepareIFields(const IFieldDef* defs, size_t count) { - cu_.mir_graph->ifield_lowering_infos_.clear(); - cu_.mir_graph->ifield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const IFieldDef* def = &defs[i]; - MirIFieldLoweringInfo field_info(def->field_idx, def->type, false); - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_class_idx_ = def->declaring_class_idx; - field_info.declaring_field_idx_ = def->declaring_field_idx; - // We don't care about the volatile flag in these tests. - } - ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved()); - cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareIFields(const IFieldDef (&defs)[count]) { - DoPrepareIFields(defs, count); - } - - void PerformNullCheckElimination() { - // Make vregs in range [100, 1000) input registers, i.e. requiring a null check. - code_item_->registers_size_ = 1000; - code_item_->ins_size_ = 900; - - cu_.mir_graph->ComputeDFSOrders(); - bool gate_result = cu_.mir_graph->EliminateNullChecksGate(); - ASSERT_TRUE(gate_result); - RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get()); - bool change = false; - for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { - change = cu_.mir_graph->EliminateNullChecks(bb); - } - cu_.mir_graph->EliminateNullChecksEnd(); - } - - NullCheckEliminationTest() - : MirOptimizationTest() { - static const MethodDef methods[] = { - { 0u, 1u, 0u, 0u, kDirect, kDirect, false, false }, // Dummy. - }; - PrepareMethods(methods); - } -}; - -class SuspendCheckEliminationTest : public MirOptimizationTest { - protected: - bool IsBackEdge(BasicBlockId branch_bb, BasicBlockId target_bb) { - BasicBlock* branch = cu_.mir_graph->GetBasicBlock(branch_bb); - return target_bb != NullBasicBlockId && cu_.mir_graph->IsBackEdge(branch, target_bb); - } - - bool IsSuspendCheckEdge(BasicBlockId branch_bb, BasicBlockId target_bb) { - BasicBlock* branch = cu_.mir_graph->GetBasicBlock(branch_bb); - return cu_.mir_graph->IsSuspendCheckEdge(branch, target_bb); - } - - void PerformSuspendCheckElimination() { - cu_.mir_graph->SSATransformationStart(); - cu_.mir_graph->ComputeDFSOrders(); - cu_.mir_graph->ComputeDominators(); - cu_.mir_graph->ComputeTopologicalSortOrder(); - cu_.mir_graph->SSATransformationEnd(); - - bool gate_result = cu_.mir_graph->EliminateSuspendChecksGate(); - ASSERT_NE(gate_result, kLeafOptimization); - if (kLeafOptimization) { - // Even with kLeafOptimization on and Gate() refusing to allow SCE, we want - // to run the SCE test to avoid bitrot, so we need to initialize explicitly. - cu_.mir_graph->suspend_checks_in_loops_ = - cu_.mir_graph->arena_->AllocArray(cu_.mir_graph->GetNumBlocks(), - kArenaAllocMisc); - } - - TopologicalSortIterator iterator(cu_.mir_graph.get()); - bool change = false; - for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { - change = cu_.mir_graph->EliminateSuspendChecks(bb); - } - } - - SuspendCheckEliminationTest() - : MirOptimizationTest() { - static const MethodDef methods[] = { - { 0u, 1u, 0u, 0u, kDirect, kDirect, false, false }, // Dummy. - }; - PrepareMethods(methods); - } -}; - -TEST_F(ClassInitCheckEliminationTest, SingleBlock) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - { 2u, 1u, 2u, 2u, kDexMemAccessWord }, - { 3u, 1u, 3u, 3u, kDexMemAccessWord }, // Same declaring class as sfield[4]. - { 4u, 1u, 3u, 4u, kDexMemAccessWord }, // Same declaring class as sfield[3]. - { 5u, 0u, 0u, 0u, kDexMemAccessWord }, // Unresolved. - }; - static const MIRDef mirs[] = { - DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 5u), // Unresolved. - DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 5u), // Unresolved. - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 5u), // Unresolved. - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 3u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 4u), - }; - static const bool expected_ignore_clinit_check[] = { - false, false, false, false, true, true, true, true, true, false, true - }; - - PrepareSFields(sfields); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(ClassInitCheckEliminationTest, SingleBlockWithInvokes) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - { 2u, 1u, 2u, 2u, kDexMemAccessWord }, - }; - static const MethodDef methods[] = { - { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false }, - { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false }, - { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false }, - }; - static const MIRDef mirs[] = { - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u), - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u), - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u), - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u), - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u), - }; - static const bool expected_class_initialized[] = { - false, true, false, true, false, true - }; - static const bool expected_class_in_dex_cache[] = { - false, false, false, false, false, false - }; - - PrepareSFields(sfields); - PrepareMethods(methods); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_class_initialized), mir_count_); - ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_class_initialized[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_class_in_dex_cache[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(ClassInitCheckEliminationTest, Diamond) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - { 2u, 1u, 2u, 2u, kDexMemAccessWord }, - { 3u, 1u, 3u, 3u, kDexMemAccessWord }, - { 4u, 1u, 4u, 4u, kDexMemAccessWord }, - { 5u, 1u, 5u, 5u, kDexMemAccessWord }, - { 6u, 1u, 6u, 6u, kDexMemAccessWord }, - { 7u, 1u, 7u, 7u, kDexMemAccessWord }, - { 8u, 1u, 8u, 8u, kDexMemAccessWord }, // Same declaring class as sfield[9]. - { 9u, 1u, 8u, 9u, kDexMemAccessWord }, // Same declaring class as sfield[8]. - { 10u, 0u, 0u, 0u, kDexMemAccessWord }, // Unresolved. - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 10u), // Unresolved. - DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 10u), // Unresolved. - DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 0u), // Eliminated (BB #3 dominates #6). - DEF_SGET_SPUT(4u, Instruction::SPUT, 0u, 1u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 1u), // Not eliminated (BB #4 doesn't dominate #6). - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u), - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u), // Eliminated (BB #3 dominates #4). - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 3u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 3u), // Eliminated (BB #3 dominates #5). - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 4u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 4u), // Eliminated (BB #3 dominates #6). - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 5u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 5u), // Not eliminated (BB #4 doesn't dominate #6). - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 6u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 6u), // Not eliminated (BB #5 doesn't dominate #6). - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 7u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 7u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 7u), // Eliminated (initialized in both #3 and #4). - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 8u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 9u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 8u), // Eliminated (with sfield[9] in BB #5). - DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 9u), // Eliminated (with sfield[8] in BB #4). - }; - static const bool expected_ignore_clinit_check[] = { - false, true, // Unresolved: sfield[10] - false, true, // sfield[0] - false, false, // sfield[1] - false, true, // sfield[2] - false, true, // sfield[3] - false, true, // sfield[4] - false, false, // sfield[5] - false, false, // sfield[6] - false, false, true, // sfield[7] - false, false, true, true, // sfield[8], sfield[9] - }; - - PrepareSFields(sfields); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(ClassInitCheckEliminationTest, DiamondWithInvokes) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - { 2u, 1u, 2u, 2u, kDexMemAccessWord }, - { 3u, 1u, 3u, 3u, kDexMemAccessWord }, - { 4u, 1u, 4u, 4u, kDexMemAccessWord }, - }; - static const MethodDef methods[] = { - { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false }, - { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false }, - { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false }, - { 3u, 1u, 3u, 3u, kStatic, kStatic, false, false }, - { 4u, 1u, 4u, 4u, kStatic, kStatic, false, false }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u), - DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u), - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u), - DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 1u), - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u), - DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u), - DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 2u), - DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 3u), - DEF_SGET_SPUT(5u, Instruction::SPUT, 0u, 3u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 3u), - DEF_SGET_SPUT(4u, Instruction::SPUT, 0u, 4u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 4u), - DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u /* dummy */, 4u), - }; - static const bool expected_class_initialized[] = { - false, true, // BB #3 SPUT, BB#6 INVOKE_STATIC - false, true, // BB #3 INVOKE_STATIC, BB#6 SPUT - false, false, true, // BB #4 SGET, BB #5 INVOKE_STATIC, BB #6 SPUT - false, false, true, // BB #4 INVOKE_STATIC, BB #5 SPUT, BB #6 SGET - false, false, true, // BB #4 SPUT, BB #5 SGET, BB #6 INVOKE_STATIC - }; - static const bool expected_class_in_dex_cache[] = { - false, false, // BB #3 SPUT, BB#6 INVOKE_STATIC - false, false, // BB #3 INVOKE_STATIC, BB#6 SPUT - false, false, false, // BB #4 SGET, BB #5 INVOKE_STATIC, BB #6 SPUT - false, false, false, // BB #4 INVOKE_STATIC, BB #5 SPUT, BB #6 SGET - false, false, false, // BB #4 SPUT, BB #5 SGET, BB #6 INVOKE_STATIC - }; - - PrepareSFields(sfields); - PrepareMethods(methods); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_class_initialized), mir_count_); - ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_class_initialized[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_class_in_dex_cache[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(ClassInitCheckEliminationTest, Loop) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - { 2u, 1u, 2u, 2u, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u), - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 0u), // Eliminated. - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 1u), // Eliminated. - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 2u), // Eliminated. - }; - static const bool expected_ignore_clinit_check[] = { - false, true, false, true, false, true, - }; - - PrepareSFields(sfields); - PrepareLoop(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(ClassInitCheckEliminationTest, LoopWithInvokes) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - }; - static const MethodDef methods[] = { - { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false }, - { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false }, - { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false }, - }; - static const MIRDef mirs[] = { - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u), - DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u), - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u), - DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u), - DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u), - DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 0u), - }; - static const bool expected_class_initialized[] = { - false, true, false, true, false, true, true, - }; - static const bool expected_class_in_dex_cache[] = { - false, false, false, false, false, false, false, - }; - - PrepareSFields(sfields); - PrepareMethods(methods); - PrepareLoop(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_class_initialized), mir_count_); - ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_class_initialized[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_class_in_dex_cache[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(ClassInitCheckEliminationTest, Catch) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - { 2u, 1u, 2u, 2u, kDexMemAccessWord }, - { 3u, 1u, 3u, 3u, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u), // Before the exception edge. - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u), // Before the exception edge. - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u), // After the exception edge. - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 3u), // After the exception edge. - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 0u), // In catch handler; eliminated. - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 2u), // In catch handler; not eliminated. - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 0u), // Class init check eliminated. - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 1u), // Class init check eliminated. - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 2u), // Class init check eliminated. - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 3u), // Class init check not eliminated. - }; - static const bool expected_ignore_clinit_check[] = { - false, false, false, false, true, false, true, true, true, false - }; - - PrepareSFields(sfields); - PrepareCatch(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(NullCheckEliminationTest, SingleBlock) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 0u, 1u, kDexMemAccessWord }, - { 2u, 1u, 0u, 2u, kDexMemAccessObject }, - }; - static const MIRDef mirs[] = { - DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 0u, 100u, 2u), - DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 0u, 1u), - DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 2u, 100u, 2u), // Differs from 0u (no LVN here). - DEF_IGET_IPUT(3u, Instruction::IGET, 3u, 2u, 1u), - DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 101u, 0u), - DEF_IGET_IPUT(3u, Instruction::IGET, 5u, 102u, 0u), - DEF_IGET_IPUT(3u, Instruction::IGET, 6u, 103u, 0u), - DEF_IGET_IPUT(3u, Instruction::IGET, 7u, 103u, 1u), - DEF_IGET_IPUT(3u, Instruction::IPUT, 8u, 104u, 0u), - DEF_IGET_IPUT(3u, Instruction::IPUT, 9u, 104u, 1u), - DEF_IGET_IPUT(3u, Instruction::IGET, 10u, 105u, 0u), - DEF_IGET_IPUT(3u, Instruction::IPUT, 11u, 105u, 1u), - DEF_IGET_IPUT(3u, Instruction::IPUT, 12u, 106u, 0u), - DEF_IGET_IPUT(3u, Instruction::IGET, 13u, 106u, 1u), - DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 107, 0u /* dummy */), - DEF_IGET_IPUT(3u, Instruction::IGET, 15u, 107u, 1u), - DEF_IGET_IPUT(3u, Instruction::IGET, 16u, 108u, 0u), - DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 108, 0u /* dummy */), - DEF_AGET_APUT(3u, Instruction::AGET, 18u, 109u, 110u), - DEF_AGET_APUT(3u, Instruction::APUT, 19u, 109u, 111u), - DEF_OTHER2(3u, Instruction::ARRAY_LENGTH, 20u, 112u), - DEF_AGET_APUT(3u, Instruction::AGET, 21u, 112u, 113u), - DEF_OTHER1(3u, Instruction::MONITOR_ENTER, 114u), - DEF_OTHER1(3u, Instruction::MONITOR_EXIT, 114u), - }; - static const bool expected_ignore_null_check[] = { - false, false, true, false /* Not doing LVN. */, - false, true /* Set before running NCE. */, - false, true, // IGET, IGET - false, true, // IPUT, IPUT - false, true, // IGET, IPUT - false, true, // IPUT, IGET - false, true, // INVOKE, IGET - false, true, // IGET, INVOKE - false, true, // AGET, APUT - false, true, // ARRAY_LENGTH, AGET - false, true, // MONITOR_ENTER, MONITOR_EXIT - }; - - PrepareIFields(ifields); - PrepareSingleBlock(); - PrepareMIRs(mirs); - - // Mark IGET 5u as null-checked to test that NCE doesn't clear this flag. - mirs_[5u].optimization_flags |= MIR_IGNORE_NULL_CHECK; - - PerformNullCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(NullCheckEliminationTest, Diamond) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 0u, 1u, kDexMemAccessWord }, - { 2u, 1u, 0u, 2u, kDexMemAccessObject }, // int[]. - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_IGET_IPUT(3u, Instruction::IPUT, 0u, 100u, 0u), - DEF_IGET_IPUT(6u, Instruction::IGET, 1u, 100u, 1u), // Eliminated (BB #3 dominates #6). - DEF_IGET_IPUT(3u, Instruction::IGET, 2u, 101u, 0u), - DEF_IGET_IPUT(4u, Instruction::IPUT, 3u, 101u, 0u), // Eliminated (BB #3 dominates #4). - DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u), - DEF_IGET_IPUT(5u, Instruction::IPUT, 5u, 102u, 1u), // Eliminated (BB #3 dominates #5). - DEF_IGET_IPUT(4u, Instruction::IPUT, 6u, 103u, 0u), - DEF_IGET_IPUT(6u, Instruction::IPUT, 7u, 103u, 1u), // Not eliminated (going through BB #5). - DEF_IGET_IPUT(5u, Instruction::IGET, 8u, 104u, 1u), - DEF_IGET_IPUT(6u, Instruction::IGET, 9u, 104u, 0u), // Not eliminated (going through BB #4). - DEF_INVOKE(4u, Instruction::INVOKE_DIRECT, 105u, 0u /* dummy */), - DEF_IGET_IPUT(5u, Instruction::IGET, 11u, 105u, 1u), - DEF_IGET_IPUT(6u, Instruction::IPUT, 12u, 105u, 0u), // Eliminated. - DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 13u, 106u, 2u), - DEF_OTHER1(3u, Instruction::IF_EQZ, 13u), // Last insn in the BB #3. - DEF_OTHER2(5u, Instruction::NEW_ARRAY, 13u, 107u), - DEF_AGET_APUT(6u, Instruction::AGET, 16u, 13u, 108u), // Eliminated. - }; - static const bool expected_ignore_null_check[] = { - false, true, // BB #3 IPUT, BB #6 IGET - false, true, // BB #3 IGET, BB #4 IPUT - false, true, // BB #3 IGET, BB #5 IPUT - false, false, // BB #4 IPUT, BB #6 IPUT - false, false, // BB #5 IGET, BB #6 IGET - false, false, true, // BB #4 INVOKE, BB #5 IGET, BB #6 IPUT - false, false, // BB #3 IGET_OBJECT & IF_EQZ - false, true, // BB #5 NEW_ARRAY, BB #6 AGET - }; - - PrepareIFields(ifields); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformNullCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(NullCheckEliminationTest, Loop) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u), - DEF_IGET_IPUT(4u, Instruction::IGET, 1u, 101u, 0u), - DEF_IGET_IPUT(5u, Instruction::IGET, 2u, 100u, 1u), // Eliminated. - DEF_IGET_IPUT(5u, Instruction::IGET, 3u, 101u, 1u), // Eliminated. - DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u), - DEF_IGET_IPUT(4u, Instruction::IGET, 5u, 102u, 1u), // Not eliminated (MOVE_OBJECT_16). - DEF_OTHER2(4u, Instruction::MOVE_OBJECT_16, 102u, 103u), - }; - static const bool expected_ignore_null_check[] = { - false, false, true, true, - false, false, false, - }; - - PrepareIFields(ifields); - PrepareLoop(); - PrepareMIRs(mirs); - PerformNullCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(NullCheckEliminationTest, Catch) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u), // Before the exception edge. - DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 101u, 0u), // Before the exception edge. - DEF_IGET_IPUT(4u, Instruction::IGET, 2u, 102u, 0u), // After the exception edge. - DEF_IGET_IPUT(4u, Instruction::IGET, 3u, 103u, 0u), // After the exception edge. - DEF_IGET_IPUT(5u, Instruction::IGET, 4u, 100u, 1u), // In catch handler; eliminated. - DEF_IGET_IPUT(5u, Instruction::IGET, 5u, 102u, 1u), // In catch handler; not eliminated. - DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 100u, 0u), // Null check eliminated. - DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 101u, 1u), // Null check eliminated. - DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 102u, 0u), // Null check eliminated. - DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 103u, 1u), // Null check not eliminated. - }; - static const bool expected_ignore_null_check[] = { - false, false, false, false, true, false, true, true, true, false - }; - - PrepareIFields(ifields); - PrepareCatch(); - PrepareMIRs(mirs); - PerformNullCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(SuspendCheckEliminationTest, LoopNoElimination) { - static const MIRDef mirs[] = { - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u, 0u), // Force the pass to run. - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge back. - }; - - PrepareLoop(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(4u, 4u)); - EXPECT_TRUE(IsSuspendCheckEdge(4u, 4u)); // Suspend point on loop to self. -} - -TEST_F(SuspendCheckEliminationTest, LoopElimination) { - static const MIRDef mirs[] = { - DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in the loop. - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge back. - }; - - PrepareLoop(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(4u, 4u)); - EXPECT_FALSE(IsSuspendCheckEdge(4u, 4u)); // No suspend point on loop to self. -} - -TEST_F(SuspendCheckEliminationTest, While_While_NoElimination) { - static const MIRDef mirs[] = { - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u, 0u), // Force the pass to run. - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_While(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(7u, 4u)); - EXPECT_TRUE(IsSuspendCheckEdge(7u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_While_InvokeInOuterLoopHead) { - static const MIRDef mirs[] = { - DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in outer loop head. - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_While(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(7u, 4u)); - EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_While_InvokeInOuterLoopBody) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_INVOKE(7u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in outer loop body. - DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_While(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(7u, 4u)); - EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_While_InvokeInInnerLoopHead) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in inner loop head. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_While(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(7u, 4u)); - EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_While_InvokeInInnerLoopBody) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop. - DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in inner loop body. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_While(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(7u, 4u)); - EXPECT_TRUE(IsSuspendCheckEdge(7u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_WhileWhile_InvokeInFirstInnerLoopHead) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in first inner loop head. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 1. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER1(7u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 2. - DEF_OTHER0(8u, Instruction::GOTO), // Edge back to inner loop 2 head. - DEF_OTHER0(9u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_WhileWhile(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(8u, 7u)); - EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u)); - ASSERT_TRUE(IsBackEdge(9u, 4u)); - EXPECT_FALSE(IsSuspendCheckEdge(9u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_WhileWhile_InvokeInFirstInnerLoopBody) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 1. - DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in first inner loop body. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER1(7u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 2. - DEF_OTHER0(8u, Instruction::GOTO), // Edge back to inner loop 2 head. - DEF_OTHER0(9u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_WhileWhile(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(8u, 7u)); - EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u)); - ASSERT_TRUE(IsBackEdge(9u, 4u)); - EXPECT_TRUE(IsSuspendCheckEdge(9u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_WhileWhile_WithExtraEdge_InvokeInFirstInnerLoopBody) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 1. - DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in first inner loop body. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER1(7u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 2. - DEF_OTHER0(8u, Instruction::GOTO), // Edge back to inner loop 2 head. - DEF_OTHER0(9u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(8u, 7u)); - EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u)); // Unaffected by the extra edge. - ASSERT_TRUE(IsBackEdge(9u, 4u)); - EXPECT_TRUE(IsSuspendCheckEdge(9u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_WhileWhile_WithExtraEdge_InvokeInSecondInnerLoopHead) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 1. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_INVOKE(7u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in second inner loop head. - DEF_OTHER1(7u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 2. - DEF_OTHER0(8u, Instruction::GOTO), // Edge back to inner loop 2 head. - DEF_OTHER0(9u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(8u, 7u)); - EXPECT_FALSE(IsSuspendCheckEdge(8u, 7u)); // Unaffected by the extra edge. - ASSERT_TRUE(IsBackEdge(9u, 4u)); - EXPECT_FALSE(IsSuspendCheckEdge(9u, 4u)); -} - -} // namespace art diff --git a/compiler/dex/pass.h b/compiler/dex/pass.h deleted file mode 100644 index 16414efada9921e1fb95d1854f420d2163ba3474..0000000000000000000000000000000000000000 --- a/compiler/dex/pass.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 - -#include "base/logging.h" - -namespace art { - -// Forward declarations. -class BasicBlock; -class Pass; - -// Empty Pass Data Class, can be extended by any pass extending the base Pass class. -class PassDataHolder { -}; - -/** - * @class Pass - * @brief Base Pass class, can be extended to perform a more defined way of doing the work call. - */ -class Pass { - public: - explicit Pass(const char* name) - : pass_name_(name) { - } - - virtual ~Pass() { - } - - virtual const char* GetName() const { - return pass_name_; - } - - /** - * @brief Gate for the pass: determines whether to execute the pass or not considering a CompilationUnit - * @param data the PassDataHolder. - * @return whether or not to execute the pass. - */ - virtual bool Gate(const PassDataHolder* data ATTRIBUTE_UNUSED) const { - // Base class says yes. - return true; - } - - /** - * @brief Start of the pass: called before the Worker function. - */ - virtual void Start(PassDataHolder* data ATTRIBUTE_UNUSED) const { - } - - /** - * @brief End of the pass: called after the WalkBasicBlocks function. - */ - virtual void End(PassDataHolder* data ATTRIBUTE_UNUSED) const { - } - - /** - * @param data the object containing data necessary for the pass. - * @return whether or not there is a change when walking the BasicBlock - */ - virtual bool Worker(PassDataHolder* data ATTRIBUTE_UNUSED) const { - // Passes that do all their work in Start() or End() should not allow useless node iteration. - LOG(FATAL) << "Unsupported default Worker() used for " << GetName(); - UNREACHABLE(); - } - - protected: - /** @brief The pass name: used for searching for a pass when running a particular pass or debugging. */ - const char* const pass_name_; - - 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.h b/compiler/dex/pass_driver.h deleted file mode 100644 index 34a6f630f11063c8528ace75e0380490be4bc467..0000000000000000000000000000000000000000 --- a/compiler/dex/pass_driver.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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 - -#include "base/logging.h" -#include "pass.h" -#include "pass_manager.h" - -namespace art { - -class Pass; -class PassDataHolder; -class PassDriver; -class PassManager; - -// Empty holder for the constructor. -class PassDriverDataHolder { -}; - -/** - * @class PassDriver - * @brief PassDriver is the wrapper around all Pass instances in order to execute them - */ -class PassDriver { - public: - explicit PassDriver(const PassManager* const pass_manager) : pass_manager_(pass_manager) { - pass_list_ = *pass_manager_->GetDefaultPassList(); - DCHECK(!pass_list_.empty()); - } - - virtual ~PassDriver() { - } - - /** - * @brief Insert a Pass: can warn if multiple passes have the same name. - */ - void InsertPass(const Pass* new_pass) { - DCHECK(new_pass != nullptr); - DCHECK(new_pass->GetName() != nullptr); - DCHECK_NE(new_pass->GetName()[0], 0); - - // It is an error to override an existing pass. - DCHECK(GetPass(new_pass->GetName()) == nullptr) - << "Pass name " << new_pass->GetName() << " already used."; - // Now add to the list. - pass_list_.push_back(new_pass); - } - - /** - * @brief Run a pass using the name as key. - * @return whether the pass was applied. - */ - virtual bool RunPass(const char* pass_name) { - // Paranoid: c_unit cannot be null and we need a pass name. - DCHECK(pass_name != nullptr); - DCHECK_NE(pass_name[0], 0); - - const Pass* cur_pass = GetPass(pass_name); - - if (cur_pass != nullptr) { - return RunPass(cur_pass); - } - - // Return false, we did not find the pass. - return false; - } - - /** - * @brief Runs all the passes with the pass_list_. - */ - void Launch() { - for (const Pass* cur_pass : pass_list_) { - RunPass(cur_pass); - } - } - - /** - * @brief Searches for a particular pass. - * @param the name of the pass to be searched for. - */ - const Pass* GetPass(const char* name) const { - for (const Pass* cur_pass : pass_list_) { - if (strcmp(name, cur_pass->GetName()) == 0) { - return cur_pass; - } - } - return nullptr; - } - - /** - * @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. - */ - virtual bool RunPass(const Pass* pass, bool time_split = false) = 0; - - protected: - /** - * @brief Apply a patch: perform start/work/end functions. - */ - virtual void ApplyPass(PassDataHolder* data, const Pass* pass) { - pass->Start(data); - DispatchPass(pass); - pass->End(data); - } - - /** - * @brief Dispatch a patch. - * Gives the ability to add logic when running the patch. - */ - virtual void DispatchPass(const Pass* pass ATTRIBUTE_UNUSED) { - } - - /** @brief List of passes: provides the order to execute the passes. - * Passes are owned by pass_manager_. */ - std::vector pass_list_; - - const PassManager* const pass_manager_; -}; - -} // namespace art -#endif // ART_COMPILER_DEX_PASS_DRIVER_H_ diff --git a/compiler/dex/pass_driver_me.h b/compiler/dex/pass_driver_me.h deleted file mode 100644 index d0af71c061dc1200b174a669fa023c7b59ce56cc..0000000000000000000000000000000000000000 --- a/compiler/dex/pass_driver_me.h +++ /dev/null @@ -1,316 +0,0 @@ -/* - * 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_ME_H_ -#define ART_COMPILER_DEX_PASS_DRIVER_ME_H_ - -#include -#include - -#include "bb_optimizations.h" -#include "dataflow_iterator.h" -#include "dataflow_iterator-inl.h" -#include "dex_flags.h" -#include "pass_driver.h" -#include "pass_manager.h" -#include "pass_me.h" -#include "safe_map.h" - -namespace art { - -class PassManager; -class PassManagerOptions; - -class PassDriverME: public PassDriver { - public: - PassDriverME(const PassManager* const pass_manager, CompilationUnit* cu) - : PassDriver(pass_manager), pass_me_data_holder_(), dump_cfg_folder_("/sdcard/") { - pass_me_data_holder_.bb = nullptr; - pass_me_data_holder_.c_unit = cu; - } - - ~PassDriverME() { - } - - void DispatchPass(const Pass* pass) { - VLOG(compiler) << "Dispatching " << pass->GetName(); - const PassME* me_pass = down_cast(pass); - - DataFlowAnalysisMode mode = me_pass->GetTraversal(); - - switch (mode) { - case kPreOrderDFSTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kRepeatingPreOrderDFSTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kRepeatingPostOrderDFSTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kReversePostOrderDFSTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kRepeatingReversePostOrderDFSTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kPostOrderDOMTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kTopologicalSortTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kLoopRepeatingTopologicalSortTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kAllNodes: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kNoNodes: - break; - default: - LOG(FATAL) << "Iterator mode not handled in dispatcher: " << mode; - break; - } - } - - bool RunPass(const Pass* pass, bool time_split) OVERRIDE { - // Paranoid: c_unit and pass cannot be null, and the pass should have a name. - DCHECK(pass != nullptr); - DCHECK(pass->GetName() != nullptr && pass->GetName()[0] != 0); - CompilationUnit* c_unit = pass_me_data_holder_.c_unit; - DCHECK(c_unit != nullptr); - - // Do we perform a time split - if (time_split) { - c_unit->NewTimingSplit(pass->GetName()); - } - - // First, work on determining pass verbosity. - bool old_print_pass = c_unit->print_pass; - c_unit->print_pass = pass_manager_->GetOptions().GetPrintAllPasses(); - auto* const options = &pass_manager_->GetOptions(); - const std::string& print_pass_list = options->GetPrintPassList(); - if (!print_pass_list.empty() && strstr(print_pass_list.c_str(), pass->GetName()) != nullptr) { - c_unit->print_pass = true; - } - - // Next, check if there are any overridden settings for the pass that change default - // configuration. - c_unit->overridden_pass_options.clear(); - FillOverriddenPassSettings(options, pass->GetName(), c_unit->overridden_pass_options); - if (c_unit->print_pass) { - for (auto setting_it : c_unit->overridden_pass_options) { - LOG(INFO) << "Overridden option \"" << setting_it.first << ":" - << setting_it.second << "\" for pass \"" << pass->GetName() << "\""; - } - } - - // Check the pass gate first. - bool should_apply_pass = pass->Gate(&pass_me_data_holder_); - if (should_apply_pass) { - // Applying the pass: first start, doWork, and end calls. - this->ApplyPass(&pass_me_data_holder_, pass); - - bool should_dump = (c_unit->enable_debug & (1 << kDebugDumpCFG)) != 0; - - const std::string& dump_pass_list = pass_manager_->GetOptions().GetDumpPassList(); - if (!dump_pass_list.empty()) { - const bool found = strstr(dump_pass_list.c_str(), pass->GetName()); - should_dump = should_dump || found; - } - - if (should_dump) { - // Do we want to log it? - if ((c_unit->enable_debug& (1 << kDebugDumpCFG)) != 0) { - // Do we have a pass folder? - const PassME* me_pass = (down_cast(pass)); - const char* passFolder = me_pass->GetDumpCFGFolder(); - DCHECK(passFolder != nullptr); - - if (passFolder[0] != 0) { - // Create directory prefix. - std::string prefix = GetDumpCFGFolder(); - prefix += passFolder; - prefix += "/"; - - c_unit->mir_graph->DumpCFG(prefix.c_str(), false); - } - } - } - } - - // Before wrapping up with this pass, restore old pass verbosity flag. - c_unit->print_pass = old_print_pass; - - // If the pass gate passed, we can declare success. - return should_apply_pass; - } - - static void PrintPassOptions(PassManager* manager) { - for (const auto* pass : *manager->GetDefaultPassList()) { - const PassME* me_pass = down_cast(pass); - if (me_pass->HasOptions()) { - LOG(INFO) << "Pass options for \"" << me_pass->GetName() << "\" are:"; - SafeMap overridden_settings; - FillOverriddenPassSettings(&manager->GetOptions(), me_pass->GetName(), - overridden_settings); - me_pass->PrintPassOptions(overridden_settings); - } - } - } - - const char* GetDumpCFGFolder() const { - return dump_cfg_folder_; - } - - protected: - /** @brief The data holder that contains data needed for the PassDriverME. */ - PassMEDataHolder pass_me_data_holder_; - - /** @brief Dump CFG base folder: where is the base folder for dumping CFGs. */ - const char* dump_cfg_folder_; - - static void DoWalkBasicBlocks(PassMEDataHolder* data, const PassME* pass, - DataflowIterator* iterator) { - // Paranoid: Check the iterator before walking the BasicBlocks. - DCHECK(iterator != nullptr); - bool change = false; - for (BasicBlock* bb = iterator->Next(change); bb != nullptr; bb = iterator->Next(change)) { - data->bb = bb; - change = pass->Worker(data); - } - } - - template - inline static void DoWalkBasicBlocks(PassMEDataHolder* data, const PassME* pass) { - DCHECK(data != nullptr); - CompilationUnit* c_unit = data->c_unit; - DCHECK(c_unit != nullptr); - Iterator iterator(c_unit->mir_graph.get()); - DoWalkBasicBlocks(data, pass, &iterator); - } - - /** - * @brief Fills the settings_to_fill by finding all of the applicable options in the - * overridden_pass_options_list_. - * @param pass_name The pass name for which to fill settings. - * @param settings_to_fill Fills the options to contain the mapping of name of option to the new - * configuration. - */ - static void FillOverriddenPassSettings( - const PassManagerOptions* options, const char* pass_name, - SafeMap& settings_to_fill) { - const std::string& settings = options->GetOverriddenPassOptions(); - const size_t settings_len = settings.size(); - - // Before anything, check if we care about anything right now. - if (settings_len == 0) { - return; - } - - const size_t pass_name_len = strlen(pass_name); - const size_t min_setting_size = 4; // 2 delimiters, 1 setting name, 1 setting - size_t search_pos = 0; - - // If there is no room for pass options, exit early. - if (settings_len < pass_name_len + min_setting_size) { - return; - } - - do { - search_pos = settings.find(pass_name, search_pos); - - // Check if we found this pass name in rest of string. - if (search_pos == std::string::npos) { - // No more settings for this pass. - break; - } - - // The string contains the pass name. Now check that there is - // room for the settings: at least one char for setting name, - // two chars for two delimiter, and at least one char for setting. - if (search_pos + pass_name_len + min_setting_size >= settings_len) { - // No more settings for this pass. - break; - } - - // Update the current search position to not include the pass name. - search_pos += pass_name_len; - - // The format must be "PassName:SettingName:#" where # is the setting. - // Thus look for the first ":" which must exist. - if (settings[search_pos] != ':') { - // Missing delimiter right after pass name. - continue; - } else { - search_pos += 1; - } - - // Now look for the actual setting by finding the next ":" delimiter. - const size_t setting_name_pos = search_pos; - size_t setting_pos = settings.find(':', setting_name_pos); - - if (setting_pos == std::string::npos) { - // Missing a delimiter that would capture where setting starts. - continue; - } else if (setting_pos == setting_name_pos) { - // Missing setting thus did not move from setting name - continue; - } else { - // Skip the delimiter. - setting_pos += 1; - } - - // Look for the terminating delimiter which must be a comma. - size_t next_configuration_separator = settings.find(',', setting_pos); - if (next_configuration_separator == std::string::npos) { - next_configuration_separator = settings_len; - } - - // Prevent end of string errors. - if (next_configuration_separator == setting_pos) { - continue; - } - - // Get the actual setting itself. - std::string setting_string = - settings.substr(setting_pos, next_configuration_separator - setting_pos); - - std::string setting_name = - settings.substr(setting_name_pos, setting_pos - setting_name_pos - 1); - - // We attempt to convert the option value to integer. Strtoll is being used to - // convert because it is exception safe. - char* end_ptr = nullptr; - const char* setting_ptr = setting_string.c_str(); - DCHECK(setting_ptr != nullptr); // Paranoid: setting_ptr must be a valid pointer. - int64_t int_value = strtoll(setting_ptr, &end_ptr, 0); - DCHECK(end_ptr != nullptr); // Paranoid: end_ptr must be set by the strtoll call. - - // If strtoll call succeeded, the option is now considered as integer. - if (*setting_ptr != '\0' && end_ptr != setting_ptr && *end_ptr == '\0') { - settings_to_fill.Put(setting_name, OptionContent(int_value)); - } else { - // Otherwise, it is considered as a string. - settings_to_fill.Put(setting_name, OptionContent(setting_string.c_str())); - } - search_pos = next_configuration_separator; - } while (true); - } -}; -} // namespace art -#endif // ART_COMPILER_DEX_PASS_DRIVER_ME_H_ diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc deleted file mode 100644 index 375003bf1fde575464015451d0359a9d2b8d9967..0000000000000000000000000000000000000000 --- a/compiler/dex/pass_driver_me_opts.cc +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 "pass_driver_me_opts.h" - -#include "base/logging.h" -#include "base/macros.h" -#include "bb_optimizations.h" -#include "dataflow_iterator.h" -#include "dataflow_iterator-inl.h" -#include "pass_driver_me_opts.h" -#include "pass_manager.h" -#include "post_opt_passes.h" - -namespace art { - -void PassDriverMEOpts::SetupPasses(PassManager* pass_manager) { - /* - * Create the pass list. These passes are immutable and are shared across the threads. - * - * 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. - */ - pass_manager->AddPass(new StringChange); - pass_manager->AddPass(new CacheFieldLoweringInfo); - pass_manager->AddPass(new CacheMethodLoweringInfo); - pass_manager->AddPass(new CalculatePredecessors); - pass_manager->AddPass(new DFSOrders); - pass_manager->AddPass(new ClassInitCheckElimination); - pass_manager->AddPass(new SpecialMethodInliner); - pass_manager->AddPass(new NullCheckElimination); - pass_manager->AddPass(new BBCombine); - pass_manager->AddPass(new CodeLayout); - pass_manager->AddPass(new GlobalValueNumberingPass); - pass_manager->AddPass(new DeadCodeEliminationPass); - pass_manager->AddPass(new GlobalValueNumberingCleanupPass); - pass_manager->AddPass(new ConstantPropagation); - pass_manager->AddPass(new MethodUseCount); - pass_manager->AddPass(new BBOptimizations); - pass_manager->AddPass(new SuspendCheckElimination); -} - -void PassDriverMEOpts::ApplyPass(PassDataHolder* data, const Pass* pass) { - const PassME* const pass_me = down_cast(pass); - DCHECK(pass_me != nullptr); - PassMEDataHolder* const pass_me_data_holder = down_cast(data); - // Set to dirty. - pass_me_data_holder->dirty = true; - // First call the base class' version. - PassDriver::ApplyPass(data, pass); - // Now we care about flags. - if ((pass_me->GetFlag(kOptimizationBasicBlockChange) == true) || - (pass_me->GetFlag(kOptimizationDefUsesChange) == true)) { - // Is it dirty at least? - if (pass_me_data_holder->dirty == true) { - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - c_unit->mir_graph.get()->CalculateBasicBlockInformation(post_opt_pass_manager_); - } - } -} - -} // namespace art diff --git a/compiler/dex/pass_driver_me_opts.h b/compiler/dex/pass_driver_me_opts.h deleted file mode 100644 index c8093d0a02d2b23c48f60931ea0269c17a971e65..0000000000000000000000000000000000000000 --- a/compiler/dex/pass_driver_me_opts.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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_ME_OPTS_H_ -#define ART_COMPILER_DEX_PASS_DRIVER_ME_OPTS_H_ - -#include "pass_driver_me.h" - -namespace art { - -// Forward Declarations. -struct CompilationUnit; -class Pass; -class PassDataHolder; -class PassManager; - -class PassDriverMEOpts : public PassDriverME { - public: - PassDriverMEOpts(const PassManager* const manager, - const PassManager* const post_opt_pass_manager, - CompilationUnit* cu) - : PassDriverME(manager, cu), post_opt_pass_manager_(post_opt_pass_manager) { - } - - ~PassDriverMEOpts() { - } - - /** - * @brief Write and allocate corresponding passes into the pass manager. - */ - static void SetupPasses(PassManager* pass_manasger); - - /** - * @brief Apply a patch: perform start/work/end functions. - */ - virtual void ApplyPass(PassDataHolder* data, const Pass* pass) OVERRIDE; - - const PassManager* const post_opt_pass_manager_; -}; - -} // namespace art -#endif // ART_COMPILER_DEX_PASS_DRIVER_ME_OPTS_H_ diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc deleted file mode 100644 index b35bc3d7d3eeb868625b0aa1901de0d4264b3066..0000000000000000000000000000000000000000 --- a/compiler/dex/pass_driver_me_post_opt.cc +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 "pass_driver_me_post_opt.h" - -#include "base/macros.h" -#include "post_opt_passes.h" -#include "pass_manager.h" - -namespace art { - -void PassDriverMEPostOpt::SetupPasses(PassManager* pass_manager) { - /* - * Create the pass list. These passes are immutable and are shared across the threads. - * - * 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. - */ - // The initial list of passes to be used by the PassDriveMEPostOpt. - pass_manager->AddPass(new DFSOrders); - pass_manager->AddPass(new BuildDomination); - pass_manager->AddPass(new TopologicalSortOrders); - pass_manager->AddPass(new InitializeSSATransformation); - pass_manager->AddPass(new ClearPhiInstructions); - pass_manager->AddPass(new DefBlockMatrix); - pass_manager->AddPass(new FindPhiNodeBlocksPass); - pass_manager->AddPass(new SSAConversion); - pass_manager->AddPass(new PhiNodeOperands); - pass_manager->AddPass(new PerformInitRegLocations); - pass_manager->AddPass(new TypeInferencePass); - pass_manager->AddPass(new FinishSSATransformation); -} - -} // namespace art diff --git a/compiler/dex/pass_driver_me_post_opt.h b/compiler/dex/pass_driver_me_post_opt.h deleted file mode 100644 index 94176dbf0fca0aff5375ef92b8ad0838d9303399..0000000000000000000000000000000000000000 --- a/compiler/dex/pass_driver_me_post_opt.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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_ME_POST_OPT_H_ -#define ART_COMPILER_DEX_PASS_DRIVER_ME_POST_OPT_H_ - -#include "pass_driver_me.h" - -namespace art { - -// Forward Declarations. -struct CompilationUnit; -class Pass; -class PassDataHolder; - -class PassDriverMEPostOpt : public PassDriverME { - public: - PassDriverMEPostOpt(const PassManager* const manager, CompilationUnit* cu) - : PassDriverME(manager, cu) { - } - - ~PassDriverMEPostOpt() { - } - - /** - * @brief Write and allocate corresponding passes into the pass manager. - */ - static void SetupPasses(PassManager* pass_manager); -}; - -} // namespace art -#endif // ART_COMPILER_DEX_PASS_DRIVER_ME_POST_OPT_H_ diff --git a/compiler/dex/pass_manager.cc b/compiler/dex/pass_manager.cc deleted file mode 100644 index 6377a6c07a8680e6eeec1507d6e10575c227df16..0000000000000000000000000000000000000000 --- a/compiler/dex/pass_manager.cc +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "pass_manager.h" - -#include "base/stl_util.h" -#include "pass_me.h" - -namespace art { - -PassManager::PassManager(const PassManagerOptions& options) : options_(options) { -} - -PassManager::~PassManager() { - STLDeleteElements(&passes_); -} - -void PassManager::CreateDefaultPassList() { - default_pass_list_.clear(); - // Add each pass which isn't disabled into default_pass_list_. - for (const auto* pass : passes_) { - if (options_.GetDisablePassList().find(pass->GetName()) != std::string::npos) { - VLOG(compiler) << "Skipping disabled pass " << pass->GetName(); - } else { - default_pass_list_.push_back(pass); - } - } -} - -void PassManager::PrintPassNames() const { - LOG(INFO) << "Loop Passes are:"; - for (const Pass* cur_pass : default_pass_list_) { - LOG(INFO) << "\t-" << cur_pass->GetName(); - } -} - -} // namespace art diff --git a/compiler/dex/pass_manager.h b/compiler/dex/pass_manager.h deleted file mode 100644 index 68e488d128747ad5581b459caed263d16240551b..0000000000000000000000000000000000000000 --- a/compiler/dex/pass_manager.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_PASS_MANAGER_H_ -#define ART_COMPILER_DEX_PASS_MANAGER_H_ - -#include -#include - -#include "base/logging.h" - -namespace art { - -class Pass; - -class PassManagerOptions { - public: - PassManagerOptions() - : default_print_passes_(false), - print_pass_names_(false), - print_pass_options_(false) { - } - explicit PassManagerOptions(const PassManagerOptions&) = default; - - void SetPrintPassNames(bool b) { - print_pass_names_ = b; - } - - void SetPrintAllPasses() { - default_print_passes_ = true; - } - bool GetPrintAllPasses() const { - return default_print_passes_; - } - - void SetDisablePassList(const std::string& list) { - disable_pass_list_ = list; - } - const std::string& GetDisablePassList() const { - return disable_pass_list_; - } - - void SetPrintPassList(const std::string& list) { - print_pass_list_ = list; - } - const std::string& GetPrintPassList() const { - return print_pass_list_; - } - - void SetDumpPassList(const std::string& list) { - dump_pass_list_ = list; - } - const std::string& GetDumpPassList() const { - return dump_pass_list_; - } - - /** - * @brief Used to set a string that contains the overridden pass options. - * @details An overridden pass option means that the pass uses this option - * instead of using its default option. - * @param s The string passed by user with overridden options. The string is in format - * Pass1Name:Pass1Option:Pass1Setting,Pass2Name:Pass2Option::Pass2Setting - */ - void SetOverriddenPassOptions(const std::string& list) { - overridden_pass_options_list_ = list; - } - const std::string& GetOverriddenPassOptions() const { - return overridden_pass_options_list_; - } - - void SetPrintPassOptions(bool b) { - print_pass_options_ = b; - } - bool GetPrintPassOptions() const { - return print_pass_options_; - } - - private: - /** @brief Do we, by default, want to be printing the log messages? */ - bool default_print_passes_; - - /** @brief What are the passes we want to be printing the log messages? */ - std::string print_pass_list_; - - /** @brief What are the passes we want to be dumping the CFG? */ - std::string dump_pass_list_; - - /** @brief String of all options that should be overridden for selected passes */ - std::string overridden_pass_options_list_; - - /** @brief String of all options that should be overridden for selected passes */ - std::string disable_pass_list_; - - /** @brief Whether or not we print all the passes when we create the pass manager */ - bool print_pass_names_; - - /** @brief Whether or not we print all the pass options when we create the pass manager */ - bool print_pass_options_; -}; - -/** - * @class PassManager - * @brief Owns passes - */ -class PassManager { - public: - explicit PassManager(const PassManagerOptions& options); - virtual ~PassManager(); - void CreateDefaultPassList(); - void AddPass(const Pass* pass) { - passes_.push_back(pass); - } - /** - * @brief Print the pass names of all the passes available. - */ - void PrintPassNames() const; - const std::vector* GetDefaultPassList() const { - return &default_pass_list_; - } - const PassManagerOptions& GetOptions() const { - return options_; - } - - private: - /** @brief The set of possible passes. */ - std::vector passes_; - - /** @brief The default pass list is used to initialize pass_list_. */ - std::vector default_pass_list_; - - /** @brief Pass manager options. */ - PassManagerOptions options_; - - DISALLOW_COPY_AND_ASSIGN(PassManager); -}; -} // namespace art -#endif // ART_COMPILER_DEX_PASS_MANAGER_H_ diff --git a/compiler/dex/pass_me.h b/compiler/dex/pass_me.h deleted file mode 100644 index d3cf3933686d26505de3476dc66f4930aa7c671c..0000000000000000000000000000000000000000 --- a/compiler/dex/pass_me.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - * 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_ME_H_ -#define ART_COMPILER_DEX_PASS_ME_H_ - -#include - -#include "base/logging.h" -#include "pass.h" -#include "compiler_ir.h" -#include "safe_map.h" - -namespace art { - -// Forward declarations. -class BasicBlock; -struct CompilationUnit; - -/** - * @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 { - kOptimizationBasicBlockChange = 1, /// @brief Has there been a change to a BasicBlock? - kOptimizationDefUsesChange = 2, /// @brief Has there been a change to a def-use? - kLoopStructureChange = 4, /// @brief Has there been a loop structural change? -}; -std::ostream& operator<<(std::ostream& os, const OptimizationFlag& rhs); - -// Data holder class. -class PassMEDataHolder: public PassDataHolder { - public: - CompilationUnit* c_unit; - BasicBlock* bb; - void* data; /**< @brief Any data the pass wants to use */ - bool dirty; /**< @brief Has the pass rendered the CFG dirty, requiring post-opt? */ -}; - -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. - kTopologicalSortTraversal, /// @brief Topological Order traversal. - kLoopRepeatingTopologicalSortTraversal, /// @brief Loop-repeating Topological Order traversal. - kNoNodes, /// @brief Skip BasicBlock traversal. -}; -std::ostream& operator<<(std::ostream& os, const DataFlowAnalysisMode& rhs); - -/** - * @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 PassME : public Pass { - public: - explicit PassME(const char* name, DataFlowAnalysisMode type = kAllNodes, - unsigned int flags = 0u, const char* dump = "") - : Pass(name), traversal_type_(type), flags_(flags), dump_cfg_folder_(dump) { - } - - PassME(const char* name, DataFlowAnalysisMode type, const char* dump) - : Pass(name), traversal_type_(type), flags_(0), dump_cfg_folder_(dump) { - } - - PassME(const char* name, const char* dump) - : Pass(name), traversal_type_(kAllNodes), flags_(0), dump_cfg_folder_(dump) { - } - - ~PassME() { - default_options_.clear(); - } - - virtual DataFlowAnalysisMode GetTraversal() const { - return traversal_type_; - } - - /** - * @return Returns whether the pass has any configurable options. - */ - bool HasOptions() const { - return default_options_.size() != 0; - } - - /** - * @brief Prints the pass options along with default settings if there are any. - * @details The printing is done using LOG(INFO). - */ - void PrintPassDefaultOptions() const { - for (const auto& option : default_options_) { - LOG(INFO) << "\t" << option.first << ":" << option.second; - } - } - - /** - * @brief Prints the pass options along with either default or overridden setting. - * @param overridden_options The overridden settings for this pass. - */ - void PrintPassOptions(SafeMap& overridden_options) const { - // We walk through the default options only to get the pass names. We use GetPassOption to - // also consider the overridden ones. - for (const auto& option : default_options_) { - LOG(INFO) << "\t" << option.first << ":" - << GetPassOption(option.first, overridden_options); - } - } - - /** - * @brief Used to obtain the option structure for a pass. - * @details Will return the overridden option if it exists or default one otherwise. - * @param option_name The name of option whose setting to look for. - * @param c_unit The compilation unit currently being handled. - * @return Returns the option structure containing the option value. - */ - const OptionContent& GetPassOption(const char* option_name, CompilationUnit* c_unit) const { - return GetPassOption(option_name, c_unit->overridden_pass_options); - } - - /** - * @brief Used to obtain the option for a pass as a string. - * @details Will return the overridden option if it exists or default one otherwise. - * It will return nullptr if the required option value is not a string. - * @param option_name The name of option whose setting to look for. - * @param c_unit The compilation unit currently being handled. - * @return Returns the overridden option if it exists or the default one otherwise. - */ - const char* GetStringPassOption(const char* option_name, CompilationUnit* c_unit) const { - return GetStringPassOption(option_name, c_unit->overridden_pass_options); - } - - /** - * @brief Used to obtain the pass option value as an integer. - * @details Will return the overridden option if it exists or default one otherwise. - * It will return 0 if the required option value is not an integer. - * @param c_unit The compilation unit currently being handled. - * @return Returns the overriden option if it exists or the default one otherwise. - */ - int64_t GetIntegerPassOption(const char* option_name, CompilationUnit* c_unit) const { - return GetIntegerPassOption(option_name, c_unit->overridden_pass_options); - } - - const char* GetDumpCFGFolder() const { - return dump_cfg_folder_; - } - - bool GetFlag(OptimizationFlag flag) const { - return (flags_ & flag); - } - - protected: - const OptionContent& GetPassOption(const char* option_name, - const SafeMap& overridden_options) const { - DCHECK(option_name != nullptr); - - // First check if there are any overridden settings. - auto overridden_it = overridden_options.find(std::string(option_name)); - if (overridden_it != overridden_options.end()) { - return overridden_it->second; - } else { - // Otherwise, there must be a default value for this option name. - auto default_it = default_options_.find(option_name); - // An invalid option is being requested. - if (default_it == default_options_.end()) { - LOG(FATAL) << "Fatal: Cannot find an option named \"" << option_name << "\""; - } - - return default_it->second; - } - } - - const char* GetStringPassOption(const char* option_name, - const SafeMap& overridden_options) const { - const OptionContent& option_content = GetPassOption(option_name, overridden_options); - if (option_content.type != OptionContent::kString) { - return nullptr; - } - - return option_content.GetString(); - } - - int64_t GetIntegerPassOption(const char* option_name, - const SafeMap& overridden_options) const { - const OptionContent& option_content = GetPassOption(option_name, overridden_options); - if (option_content.type != OptionContent::kInteger) { - return 0; - } - - return option_content.GetInteger(); - } - - /** @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 - * post-optimization pass is necessary. */ - 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_; - - /** - * @brief Contains a map of options with the default settings. - * @details The constructor of the specific pass instance should fill this - * with default options. - * */ - SafeMap default_options_; -}; -} // namespace art -#endif // ART_COMPILER_DEX_PASS_ME_H_ diff --git a/compiler/dex/post_opt_passes.cc b/compiler/dex/post_opt_passes.cc deleted file mode 100644 index 9262440ae2e3efd9a8d1e74bac3cdc261254a2f2..0000000000000000000000000000000000000000 --- a/compiler/dex/post_opt_passes.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 "post_opt_passes.h" - -#include "dataflow_iterator-inl.h" - -namespace art { - -bool ClearPhiInstructions::Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - MIR* mir = bb->first_mir_insn; - - while (mir != nullptr) { - MIR* next = mir->next; - - Instruction::Code opcode = mir->dalvikInsn.opcode; - - if (opcode == static_cast (kMirOpPhi)) { - bb->RemoveMIR(mir); - } - - mir = next; - } - - // We do not care in reporting a change or not in the MIR. - return false; -} - -void CalculatePredecessors::Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - // First get the MIRGraph here to factorize a bit the code. - MIRGraph *mir_graph = c_unit->mir_graph.get(); - - // First clear all predecessors. - AllNodesIterator first(mir_graph); - for (BasicBlock* bb = first.Next(); bb != nullptr; bb = first.Next()) { - bb->predecessors.clear(); - } - - // Now calculate all predecessors. - AllNodesIterator second(mir_graph); - for (BasicBlock* bb = second.Next(); bb != nullptr; bb = second.Next()) { - // We only care about non hidden blocks. - if (bb->hidden == true) { - continue; - } - - // Create iterator for visiting children. - ChildBlockIterator child_iter(bb, mir_graph); - - // Now iterate through the children to set the predecessor bits. - for (BasicBlock* child = child_iter.Next(); child != nullptr; child = child_iter.Next()) { - child->predecessors.push_back(bb->id); - } - } -} - -} // namespace art diff --git a/compiler/dex/post_opt_passes.h b/compiler/dex/post_opt_passes.h deleted file mode 100644 index e9fa0eb578f57d5b87bc7ab758a76ad1f43aaf94..0000000000000000000000000000000000000000 --- a/compiler/dex/post_opt_passes.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * 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_POST_OPT_PASSES_H_ -#define ART_COMPILER_DEX_POST_OPT_PASSES_H_ - -#include "base/casts.h" -#include "base/logging.h" -#include "compiler_ir.h" -#include "dex_flags.h" -#include "mir_graph.h" -#include "pass_me.h" - -namespace art { - -/** - * @class PassMEMirSsaRep - * @brief Convenience class for passes that check MIRGraph::MirSsaRepUpToDate(). - */ -class PassMEMirSsaRep : public PassME { - public: - PassMEMirSsaRep(const char* name, DataFlowAnalysisMode type = kAllNodes) - : PassME(name, type) { - } - - bool Gate(const PassDataHolder* data) const OVERRIDE { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return !c_unit->mir_graph->MirSsaRepUpToDate(); - } -}; - -/** - * @class InitializeSSATransformation - * @brief There is some data that needs to be initialized before performing - * the post optimization passes. - */ -class InitializeSSATransformation : public PassMEMirSsaRep { - public: - InitializeSSATransformation() : PassMEMirSsaRep("InitializeSSATransformation", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - // New blocks may have been inserted so the first thing we do is ensure that - // the c_unit's number of blocks matches the actual count of basic blocks. - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->SSATransformationStart(); - c_unit->mir_graph->CompilerInitializeSSAConversion(); - } -}; - -/** - * @class ClearPhiInformation - * @brief Clear the PHI nodes from the CFG. - */ -class ClearPhiInstructions : public PassMEMirSsaRep { - public: - ClearPhiInstructions() : PassMEMirSsaRep("ClearPhiInstructions") { - } - - bool Worker(PassDataHolder* data) const; -}; - -/** - * @class CalculatePredecessors - * @brief Calculate the predecessor BitVector of each Basicblock. - */ -class CalculatePredecessors : public PassME { - public: - CalculatePredecessors() : PassME("CalculatePredecessors", kNoNodes) { - } - - void Start(PassDataHolder* data) const; -}; - -/** - * @class DFSOrders - * @brief Compute the DFS order of the MIR graph - */ -class DFSOrders : public PassME { - public: - DFSOrders() : PassME("DFSOrders", kNoNodes) { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return !c_unit->mir_graph->DfsOrdersUpToDate(); - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->ComputeDFSOrders(); - } -}; - -/** - * @class BuildDomination - * @brief Build the domination information of the MIR Graph - */ -class BuildDomination : public PassME { - public: - BuildDomination() : PassME("BuildDomination", kNoNodes) { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return !c_unit->mir_graph->DominationUpToDate(); - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->ComputeDominators(); - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - // Verify the dataflow information after the pass. - if (c_unit->enable_debug & (1 << kDebugVerifyDataflow)) { - c_unit->mir_graph->VerifyDataflow(); - } - } -}; - -/** - * @class TopologicalSortOrders - * @brief Compute the topological sort order of the MIR graph - */ -class TopologicalSortOrders : public PassME { - public: - TopologicalSortOrders() : PassME("TopologicalSortOrders", kNoNodes) { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return !c_unit->mir_graph->TopologicalOrderUpToDate(); - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->ComputeTopologicalSortOrder(); - } -}; - -/** - * @class DefBlockMatrix - * @brief Calculate the matrix of definition per basic block - */ -class DefBlockMatrix : public PassMEMirSsaRep { - public: - DefBlockMatrix() : PassMEMirSsaRep("DefBlockMatrix", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->ComputeDefBlockMatrix(); - } -}; - -/** - * @class FindPhiNodeBlocksPass - * @brief Pass to find out where we need to insert the phi nodes for the SSA conversion. - */ -class FindPhiNodeBlocksPass : public PassMEMirSsaRep { - public: - FindPhiNodeBlocksPass() : PassMEMirSsaRep("FindPhiNodeBlocks", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->FindPhiNodeBlocks(); - } -}; - -/** - * @class SSAConversion - * @brief Pass for SSA conversion of MIRs - */ -class SSAConversion : public PassMEMirSsaRep { - public: - SSAConversion() : PassMEMirSsaRep("SSAConversion", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - MIRGraph *mir_graph = c_unit->mir_graph.get(); - mir_graph->ClearAllVisitedFlags(); - mir_graph->DoDFSPreOrderSSARename(mir_graph->GetEntryBlock()); - } -}; - -/** - * @class PhiNodeOperands - * @brief Pass to insert the Phi node operands to basic blocks - */ -class PhiNodeOperands : public PassMEMirSsaRep { - public: - PhiNodeOperands() : PassMEMirSsaRep("PhiNodeOperands", kPreOrderDFSTraversal) { - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = down_cast(data)->bb; - DCHECK(bb != nullptr); - c_unit->mir_graph->InsertPhiNodeOperands(bb); - // No need of repeating, so just return false. - return false; - } -}; - -/** - * @class InitRegLocations - * @brief Initialize Register Locations. - */ -class PerformInitRegLocations : public PassMEMirSsaRep { - public: - PerformInitRegLocations() : PassMEMirSsaRep("PerformInitRegLocation", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->InitRegLocations(); - } -}; - -/** - * @class TypeInferencePass - * @brief Type inference pass. - */ -class TypeInferencePass : public PassMEMirSsaRep { - public: - TypeInferencePass() : PassMEMirSsaRep("TypeInference", kRepeatingPreOrderDFSTraversal) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->InferTypesStart(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - return c_unit->mir_graph->InferTypes(bb); - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->InferTypesEnd(); - } -}; - -/** - * @class FinishSSATransformation - * @brief There is some data that needs to be freed after performing the post optimization passes. - */ -class FinishSSATransformation : public PassMEMirSsaRep { - public: - FinishSSATransformation() : PassMEMirSsaRep("FinishSSATransformation", kNoNodes) { - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->SSATransformationEnd(); - } -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_POST_OPT_PASSES_H_ diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h deleted file mode 100644 index 971745930ec4e4fb4a5b2c9a6fbeab23b5932060..0000000000000000000000000000000000000000 --- a/compiler/dex/quick/arm/arm_lir.h +++ /dev/null @@ -1,605 +0,0 @@ -/* - * Copyright (C) 2011 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_QUICK_ARM_ARM_LIR_H_ -#define ART_COMPILER_DEX_QUICK_ARM_ARM_LIR_H_ - -#include "dex/compiler_enums.h" -#include "dex/reg_location.h" -#include "dex/reg_storage.h" - -namespace art { - -/* - * Runtime register usage conventions. - * - * r0-r3: Argument registers in both Dalvik and C/C++ conventions. - * However, for Dalvik->Dalvik calls we'll pass the target's Method* - * pointer in r0 as a hidden arg0. Otherwise used as codegen scratch - * registers. - * r0-r1: As in C/C++ r0 is 32-bit return register and r0/r1 is 64-bit - * r4 : If ARM_R4_SUSPEND_FLAG is set then reserved as a suspend check/debugger - * assist flag, otherwise a callee save promotion target. - * r5 : Callee save (promotion target) - * r6 : Callee save (promotion target) - * r7 : Callee save (promotion target) - * r8 : Callee save (promotion target) - * r9 : (rARM_SELF) is reserved (pointer to thread-local storage) - * r10 : Callee save (promotion target) - * r11 : Callee save (promotion target) - * r12 : Scratch, may be trashed by linkage stubs - * r13 : (sp) is reserved - * r14 : (lr) is reserved - * r15 : (pc) is reserved - * - * 5 core temps that codegen can use (r0, r1, r2, r3, r12) - * 7 core registers that can be used for promotion - * - * Floating pointer registers - * s0-s31 - * d0-d15, where d0={s0,s1}, d1={s2,s3}, ... , d15={s30,s31} - * - * s16-s31 (d8-d15) preserved across C calls - * s0-s15 (d0-d7) trashed across C calls - * - * s0-s15/d0-d7 used as codegen temp/scratch - * s16-s31/d8-d31 can be used for promotion. - * - * Calling convention - * o On a call to a Dalvik method, pass target's Method* in r0 - * o r1-r3 will be used for up to the first 3 words of arguments - * o Arguments past the first 3 words will be placed in appropriate - * out slots by the caller. - * o If a 64-bit argument would span the register/memory argument - * boundary, it will instead be fully passed in the frame. - * o Maintain a 16-byte stack alignment - * - * Stack frame diagram (stack grows down, higher addresses at top): - * - * +------------------------+ - * | IN[ins-1] | {Note: resides in caller's frame} - * | . | - * | IN[0] | - * | caller's Method* | - * +========================+ {Note: start of callee's frame} - * | spill region | {variable sized - will include lr if non-leaf.} - * +------------------------+ - * | ...filler word... | {Note: used as 2nd word of V[locals-1] if long] - * +------------------------+ - * | V[locals-1] | - * | V[locals-2] | - * | . | - * | . | - * | V[1] | - * | V[0] | - * +------------------------+ - * | 0 to 3 words padding | - * +------------------------+ - * | OUT[outs-1] | - * | OUT[outs-2] | - * | . | - * | OUT[0] | - * | cur_method* | <<== sp w/ 16-byte alignment - * +========================+ - */ - -// First FP callee save. -#define ARM_FP_CALLEE_SAVE_BASE 16 -// Flag for using R4 to do suspend check -// #define ARM_R4_SUSPEND_FLAG - -enum ArmResourceEncodingPos { - kArmGPReg0 = 0, - kArmRegSP = 13, - kArmRegLR = 14, - kArmRegPC = 15, - kArmFPReg0 = 16, - kArmFPReg16 = 32, - kArmRegEnd = 48, -}; - -enum ArmNativeRegisterPool { // private marker to avoid generate-operator-out.py from processing. - r0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 0, - r1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 1, - r2 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 2, - r3 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 3, -#ifdef ARM_R4_SUSPEND_FLAG - rARM_SUSPEND = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 4, -#else - r4 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 4, -#endif - r5 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 5, - r6 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 6, - r7 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 7, - r8 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 8, - rARM_SELF = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 9, - r10 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 10, - r11 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 11, - r12 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 12, - r13sp = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 13, - rARM_SP = r13sp, - r14lr = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 14, - rARM_LR = r14lr, - r15pc = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 15, - rARM_PC = r15pc, - - fr0 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 0, - fr1 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 1, - fr2 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 2, - fr3 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 3, - fr4 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 4, - fr5 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 5, - fr6 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 6, - fr7 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 7, - fr8 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 8, - fr9 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 9, - fr10 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 10, - fr11 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 11, - fr12 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 12, - fr13 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 13, - fr14 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 14, - fr15 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 15, - fr16 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 16, - fr17 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 17, - fr18 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 18, - fr19 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 19, - fr20 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 20, - fr21 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 21, - fr22 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 22, - fr23 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 23, - fr24 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 24, - fr25 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 25, - fr26 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 26, - fr27 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 27, - fr28 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 28, - fr29 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 29, - fr30 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 30, - fr31 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 31, - - dr0 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 0, - dr1 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 1, - dr2 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 2, - dr3 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 3, - dr4 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 4, - dr5 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 5, - dr6 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 6, - dr7 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 7, - dr8 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 8, - dr9 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 9, - dr10 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 10, - dr11 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 11, - dr12 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 12, - dr13 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 13, - dr14 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 14, - dr15 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 15, -#if 0 - // Enable when def/use and runtime able to handle these. - dr16 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 16, - dr17 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 17, - dr18 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 18, - dr19 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 19, - dr20 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 20, - dr21 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 21, - dr22 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 22, - dr23 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 23, - dr24 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 24, - dr25 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 25, - dr26 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 26, - dr27 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 27, - dr28 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 28, - dr29 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 29, - dr30 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 30, - dr31 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 31, -#endif -}; - -constexpr RegStorage rs_r0(RegStorage::kValid | r0); -constexpr RegStorage rs_r1(RegStorage::kValid | r1); -constexpr RegStorage rs_r2(RegStorage::kValid | r2); -constexpr RegStorage rs_r3(RegStorage::kValid | r3); -#ifdef ARM_R4_SUSPEND_FLAG -constexpr RegStorage rs_rARM_SUSPEND(RegStorage::kValid | rARM_SUSPEND); -#else -constexpr RegStorage rs_r4(RegStorage::kValid | r4); -#endif -constexpr RegStorage rs_r5(RegStorage::kValid | r5); -constexpr RegStorage rs_r6(RegStorage::kValid | r6); -constexpr RegStorage rs_r7(RegStorage::kValid | r7); -constexpr RegStorage rs_r8(RegStorage::kValid | r8); -constexpr RegStorage rs_rARM_SELF(RegStorage::kValid | rARM_SELF); -constexpr RegStorage rs_r10(RegStorage::kValid | r10); -constexpr RegStorage rs_r11(RegStorage::kValid | r11); -constexpr RegStorage rs_r12(RegStorage::kValid | r12); -constexpr RegStorage rs_r13sp(RegStorage::kValid | r13sp); -constexpr RegStorage rs_rARM_SP(RegStorage::kValid | rARM_SP); -constexpr RegStorage rs_r14lr(RegStorage::kValid | r14lr); -constexpr RegStorage rs_rARM_LR(RegStorage::kValid | rARM_LR); -constexpr RegStorage rs_r15pc(RegStorage::kValid | r15pc); -constexpr RegStorage rs_rARM_PC(RegStorage::kValid | rARM_PC); -constexpr RegStorage rs_invalid(RegStorage::kInvalid); - -constexpr RegStorage rs_fr0(RegStorage::kValid | fr0); -constexpr RegStorage rs_fr1(RegStorage::kValid | fr1); -constexpr RegStorage rs_fr2(RegStorage::kValid | fr2); -constexpr RegStorage rs_fr3(RegStorage::kValid | fr3); -constexpr RegStorage rs_fr4(RegStorage::kValid | fr4); -constexpr RegStorage rs_fr5(RegStorage::kValid | fr5); -constexpr RegStorage rs_fr6(RegStorage::kValid | fr6); -constexpr RegStorage rs_fr7(RegStorage::kValid | fr7); -constexpr RegStorage rs_fr8(RegStorage::kValid | fr8); -constexpr RegStorage rs_fr9(RegStorage::kValid | fr9); -constexpr RegStorage rs_fr10(RegStorage::kValid | fr10); -constexpr RegStorage rs_fr11(RegStorage::kValid | fr11); -constexpr RegStorage rs_fr12(RegStorage::kValid | fr12); -constexpr RegStorage rs_fr13(RegStorage::kValid | fr13); -constexpr RegStorage rs_fr14(RegStorage::kValid | fr14); -constexpr RegStorage rs_fr15(RegStorage::kValid | fr15); -constexpr RegStorage rs_fr16(RegStorage::kValid | fr16); -constexpr RegStorage rs_fr17(RegStorage::kValid | fr17); -constexpr RegStorage rs_fr18(RegStorage::kValid | fr18); -constexpr RegStorage rs_fr19(RegStorage::kValid | fr19); -constexpr RegStorage rs_fr20(RegStorage::kValid | fr20); -constexpr RegStorage rs_fr21(RegStorage::kValid | fr21); -constexpr RegStorage rs_fr22(RegStorage::kValid | fr22); -constexpr RegStorage rs_fr23(RegStorage::kValid | fr23); -constexpr RegStorage rs_fr24(RegStorage::kValid | fr24); -constexpr RegStorage rs_fr25(RegStorage::kValid | fr25); -constexpr RegStorage rs_fr26(RegStorage::kValid | fr26); -constexpr RegStorage rs_fr27(RegStorage::kValid | fr27); -constexpr RegStorage rs_fr28(RegStorage::kValid | fr28); -constexpr RegStorage rs_fr29(RegStorage::kValid | fr29); -constexpr RegStorage rs_fr30(RegStorage::kValid | fr30); -constexpr RegStorage rs_fr31(RegStorage::kValid | fr31); - -constexpr RegStorage rs_dr0(RegStorage::kValid | dr0); -constexpr RegStorage rs_dr1(RegStorage::kValid | dr1); -constexpr RegStorage rs_dr2(RegStorage::kValid | dr2); -constexpr RegStorage rs_dr3(RegStorage::kValid | dr3); -constexpr RegStorage rs_dr4(RegStorage::kValid | dr4); -constexpr RegStorage rs_dr5(RegStorage::kValid | dr5); -constexpr RegStorage rs_dr6(RegStorage::kValid | dr6); -constexpr RegStorage rs_dr7(RegStorage::kValid | dr7); -constexpr RegStorage rs_dr8(RegStorage::kValid | dr8); -constexpr RegStorage rs_dr9(RegStorage::kValid | dr9); -constexpr RegStorage rs_dr10(RegStorage::kValid | dr10); -constexpr RegStorage rs_dr11(RegStorage::kValid | dr11); -constexpr RegStorage rs_dr12(RegStorage::kValid | dr12); -constexpr RegStorage rs_dr13(RegStorage::kValid | dr13); -constexpr RegStorage rs_dr14(RegStorage::kValid | dr14); -constexpr RegStorage rs_dr15(RegStorage::kValid | dr15); -#if 0 -constexpr RegStorage rs_dr16(RegStorage::kValid | dr16); -constexpr RegStorage rs_dr17(RegStorage::kValid | dr17); -constexpr RegStorage rs_dr18(RegStorage::kValid | dr18); -constexpr RegStorage rs_dr19(RegStorage::kValid | dr19); -constexpr RegStorage rs_dr20(RegStorage::kValid | dr20); -constexpr RegStorage rs_dr21(RegStorage::kValid | dr21); -constexpr RegStorage rs_dr22(RegStorage::kValid | dr22); -constexpr RegStorage rs_dr23(RegStorage::kValid | dr23); -constexpr RegStorage rs_dr24(RegStorage::kValid | dr24); -constexpr RegStorage rs_dr25(RegStorage::kValid | dr25); -constexpr RegStorage rs_dr26(RegStorage::kValid | dr26); -constexpr RegStorage rs_dr27(RegStorage::kValid | dr27); -constexpr RegStorage rs_dr28(RegStorage::kValid | dr28); -constexpr RegStorage rs_dr29(RegStorage::kValid | dr29); -constexpr RegStorage rs_dr30(RegStorage::kValid | dr30); -constexpr RegStorage rs_dr31(RegStorage::kValid | dr31); -#endif - -// RegisterLocation templates return values (r0, r0/r1, s0, or d0). -// Note: The return locations are shared between quick code and quick helper. This follows quick -// ABI. Quick helper assembly routine needs to handle the ABI differences. -const RegLocation arm_loc_c_return = - {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, rs_r0, INVALID_SREG, INVALID_SREG}; -const RegLocation arm_loc_c_return_wide = - {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, - RegStorage::MakeRegPair(rs_r0, rs_r1), INVALID_SREG, INVALID_SREG}; -const RegLocation arm_loc_c_return_float = kArm32QuickCodeUseSoftFloat - ? arm_loc_c_return - : RegLocation({kLocPhysReg, 0, 0, 0, 1, 0, 0, 0, 1, rs_fr0, INVALID_SREG, INVALID_SREG}); -const RegLocation arm_loc_c_return_double = kArm32QuickCodeUseSoftFloat - ? arm_loc_c_return_wide - : RegLocation({kLocPhysReg, 1, 0, 0, 1, 0, 0, 0, 1, rs_dr0, INVALID_SREG, INVALID_SREG}); - -enum ArmShiftEncodings { - kArmLsl = 0x0, - kArmLsr = 0x1, - kArmAsr = 0x2, - kArmRor = 0x3 -}; - -/* - * The following enum defines the list of supported Thumb instructions by the - * assembler. Their corresponding EncodingMap positions will be defined in - * Assemble.cc. - */ -enum ArmOpcode { - kArmFirst = 0, - kArm16BitData = kArmFirst, // DATA [0] rd[15..0]. - kThumbAdcRR, // adc [0100000101] rm[5..3] rd[2..0]. - kThumbAddRRI3, // add(1) [0001110] imm_3[8..6] rn[5..3] rd[2..0]. - kThumbAddRI8, // add(2) [00110] rd[10..8] imm_8[7..0]. - kThumbAddRRR, // add(3) [0001100] rm[8..6] rn[5..3] rd[2..0]. - kThumbAddRRLH, // add(4) [01000100] H12[01] rm[5..3] rd[2..0]. - kThumbAddRRHL, // add(4) [01001000] H12[10] rm[5..3] rd[2..0]. - kThumbAddRRHH, // add(4) [01001100] H12[11] rm[5..3] rd[2..0]. - kThumbAddPcRel, // add(5) [10100] rd[10..8] imm_8[7..0]. - kThumbAddSpRel, // add(6) [10101] rd[10..8] imm_8[7..0]. - kThumbAddSpI7, // add(7) [101100000] imm_7[6..0]. - kThumbAndRR, // and [0100000000] rm[5..3] rd[2..0]. - kThumbAsrRRI5, // asr(1) [00010] imm_5[10..6] rm[5..3] rd[2..0]. - kThumbAsrRR, // asr(2) [0100000100] rs[5..3] rd[2..0]. - kThumbBCond, // b(1) [1101] cond[11..8] offset_8[7..0]. - kThumbBUncond, // b(2) [11100] offset_11[10..0]. - kThumbBicRR, // bic [0100001110] rm[5..3] rd[2..0]. - kThumbBkpt, // bkpt [10111110] imm_8[7..0]. - kThumbBlx1, // blx(1) [111] H[10] offset_11[10..0]. - kThumbBlx2, // blx(1) [111] H[01] offset_11[10..0]. - kThumbBl1, // blx(1) [111] H[10] offset_11[10..0]. - kThumbBl2, // blx(1) [111] H[11] offset_11[10..0]. - kThumbBlxR, // blx(2) [010001111] rm[6..3] [000]. - kThumbBx, // bx [010001110] H2[6..6] rm[5..3] SBZ[000]. - kThumbCmnRR, // cmn [0100001011] rm[5..3] rd[2..0]. - kThumbCmpRI8, // cmp(1) [00101] rn[10..8] imm_8[7..0]. - kThumbCmpRR, // cmp(2) [0100001010] rm[5..3] rd[2..0]. - kThumbCmpLH, // cmp(3) [01000101] H12[01] rm[5..3] rd[2..0]. - kThumbCmpHL, // cmp(3) [01000110] H12[10] rm[5..3] rd[2..0]. - kThumbCmpHH, // cmp(3) [01000111] H12[11] rm[5..3] rd[2..0]. - kThumbEorRR, // eor [0100000001] rm[5..3] rd[2..0]. - kThumbLdmia, // ldmia [11001] rn[10..8] reglist [7..0]. - kThumbLdrRRI5, // ldr(1) [01101] imm_5[10..6] rn[5..3] rd[2..0]. - kThumbLdrRRR, // ldr(2) [0101100] rm[8..6] rn[5..3] rd[2..0]. - kThumbLdrPcRel, // ldr(3) [01001] rd[10..8] imm_8[7..0]. - kThumbLdrSpRel, // ldr(4) [10011] rd[10..8] imm_8[7..0]. - kThumbLdrbRRI5, // ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0]. - kThumbLdrbRRR, // ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0]. - kThumbLdrhRRI5, // ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0]. - kThumbLdrhRRR, // ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0]. - kThumbLdrsbRRR, // ldrsb [0101011] rm[8..6] rn[5..3] rd[2..0]. - kThumbLdrshRRR, // ldrsh [0101111] rm[8..6] rn[5..3] rd[2..0]. - kThumbLslRRI5, // lsl(1) [00000] imm_5[10..6] rm[5..3] rd[2..0]. - kThumbLslRR, // lsl(2) [0100000010] rs[5..3] rd[2..0]. - kThumbLsrRRI5, // lsr(1) [00001] imm_5[10..6] rm[5..3] rd[2..0]. - kThumbLsrRR, // lsr(2) [0100000011] rs[5..3] rd[2..0]. - kThumbMovImm, // mov(1) [00100] rd[10..8] imm_8[7..0]. - kThumbMovRR, // mov(2) [0001110000] rn[5..3] rd[2..0]. - kThumbMovRR_H2H, // mov(3) [01000111] H12[11] rm[5..3] rd[2..0]. - kThumbMovRR_H2L, // mov(3) [01000110] H12[01] rm[5..3] rd[2..0]. - kThumbMovRR_L2H, // mov(3) [01000101] H12[10] rm[5..3] rd[2..0]. - kThumbMul, // mul [0100001101] rm[5..3] rd[2..0]. - kThumbMvn, // mvn [0100001111] rm[5..3] rd[2..0]. - kThumbNeg, // neg [0100001001] rm[5..3] rd[2..0]. - kThumbOrr, // orr [0100001100] rm[5..3] rd[2..0]. - kThumbPop, // pop [1011110] r[8..8] rl[7..0]. - kThumbPush, // push [1011010] r[8..8] rl[7..0]. - kThumbRev, // rev [1011101000] rm[5..3] rd[2..0] - kThumbRevsh, // revsh [1011101011] rm[5..3] rd[2..0] - kThumbRorRR, // ror [0100000111] rs[5..3] rd[2..0]. - kThumbSbc, // sbc [0100000110] rm[5..3] rd[2..0]. - kThumbStmia, // stmia [11000] rn[10..8] reglist [7.. 0]. - kThumbStrRRI5, // str(1) [01100] imm_5[10..6] rn[5..3] rd[2..0]. - kThumbStrRRR, // str(2) [0101000] rm[8..6] rn[5..3] rd[2..0]. - kThumbStrSpRel, // str(3) [10010] rd[10..8] imm_8[7..0]. - kThumbStrbRRI5, // strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0]. - kThumbStrbRRR, // strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0]. - kThumbStrhRRI5, // strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0]. - kThumbStrhRRR, // strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0]. - kThumbSubRRI3, // sub(1) [0001111] imm_3[8..6] rn[5..3] rd[2..0]*/ - kThumbSubRI8, // sub(2) [00111] rd[10..8] imm_8[7..0]. - kThumbSubRRR, // sub(3) [0001101] rm[8..6] rn[5..3] rd[2..0]. - kThumbSubSpI7, // sub(4) [101100001] imm_7[6..0]. - kThumbSwi, // swi [11011111] imm_8[7..0]. - kThumbTst, // tst [0100001000] rm[5..3] rn[2..0]. - kThumb2Vldrs, // vldr low sx [111011011001] rn[19..16] rd[15-12] [1010] imm_8[7..0]. - kThumb2Vldrd, // vldr low dx [111011011001] rn[19..16] rd[15-12] [1011] imm_8[7..0]. - kThumb2Vmuls, // vmul vd, vn, vm [111011100010] rn[19..16] rd[15-12] [10100000] rm[3..0]. - kThumb2Vmuld, // vmul vd, vn, vm [111011100010] rn[19..16] rd[15-12] [10110000] rm[3..0]. - kThumb2Vstrs, // vstr low sx [111011011000] rn[19..16] rd[15-12] [1010] imm_8[7..0]. - kThumb2Vstrd, // vstr low dx [111011011000] rn[19..16] rd[15-12] [1011] imm_8[7..0]. - kThumb2Vsubs, // vsub vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10100040] rm[3..0]. - kThumb2Vsubd, // vsub vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10110040] rm[3..0]. - kThumb2Vadds, // vadd vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10100000] rm[3..0]. - kThumb2Vaddd, // vadd vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10110000] rm[3..0]. - kThumb2Vdivs, // vdiv vd, vn, vm [111011101000] rn[19..16] rd[15-12] [10100000] rm[3..0]. - kThumb2Vdivd, // vdiv vd, vn, vm [111011101000] rn[19..16] rd[15-12] [10110000] rm[3..0]. - kThumb2VmlaF64, // vmla.F64 vd, vn, vm [111011100000] vn[19..16] vd[15..12] [10110000] vm[3..0]. - kThumb2VcvtIF, // vcvt.F32.S32 vd, vm [1110111010111000] vd[15..12] [10101100] vm[3..0]. - kThumb2VcvtFI, // vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] [10101100] vm[3..0]. - kThumb2VcvtDI, // vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] [10111100] vm[3..0]. - kThumb2VcvtFd, // vcvt.F64.F32 vd, vm [1110111010110111] vd[15..12] [10101100] vm[3..0]. - kThumb2VcvtDF, // vcvt.F32.F64 vd, vm [1110111010110111] vd[15..12] [10111100] vm[3..0]. - kThumb2VcvtF64S32, // vcvt.F64.S32 vd, vm [1110111010111000] vd[15..12] [10111100] vm[3..0]. - kThumb2VcvtF64U32, // vcvt.F64.U32 vd, vm [1110111010111000] vd[15..12] [10110100] vm[3..0]. - kThumb2Vsqrts, // vsqrt.f32 vd, vm [1110111010110001] vd[15..12] [10101100] vm[3..0]. - kThumb2Vsqrtd, // vsqrt.f64 vd, vm [1110111010110001] vd[15..12] [10111100] vm[3..0]. - kThumb2MovI8M, // mov(T2) rd, # [11110] i [00001001111] imm3 rd[11..8] imm8. - kThumb2MovImm16, // mov(T3) rd, # [11110] i [0010100] imm4 [0] imm3 rd[11..8] imm8. - kThumb2StrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0]. - kThumb2LdrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0]. - kThumb2StrRRI8Predec, // str(Imm,T4) rd,[rn,#-imm8] [111110000100] rn[19..16] rt[15..12] [1100] imm[7..0]. - kThumb2LdrRRI8Predec, // ldr(Imm,T4) rd,[rn,#-imm8] [111110000101] rn[19..16] rt[15..12] [1100] imm[7..0]. - kThumb2Cbnz, // cbnz rd,