diff options
author | Andreas Gampe <agampe@google.com> | 2015-04-22 18:45:06 -0700 |
---|---|---|
committer | Andreas Gampe <agampe@google.com> | 2015-04-22 18:45:06 -0700 |
commit | 1b717f63847de8762e7f7bdd6708fdfae9d24a67 (patch) | |
tree | e87208bc72796fb310750677650b2e8344f9f81c | |
parent | 8e58d76eb30a50e38c46bd6277186116937ba396 (diff) | |
download | art-1b717f63847de8762e7f7bdd6708fdfae9d24a67.zip art-1b717f63847de8762e7f7bdd6708fdfae9d24a67.tar.gz art-1b717f63847de8762e7f7bdd6708fdfae9d24a67.tar.bz2 |
Revert "Quick: Rewrite type inference pass."
Breaks arm64, as the method register is not correctly flagged
as ref and thus 32bit.
Bug: 19419671
This reverts commit e490b01c12d33f3bd5c247b55b47e507cc9c8fab.
22 files changed, 544 insertions, 4095 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 7d76795..e0f0ae5 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -191,7 +191,6 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/dex/mir_graph_test.cc \ compiler/dex/mir_optimization_test.cc \ compiler/dex/quick/quick_cfi_test.cc \ - compiler/dex/type_inference_test.cc \ compiler/dwarf/dwarf_test.cc \ compiler/driver/compiler_driver_test.cc \ compiler/elf_writer_test.cc \ @@ -228,7 +227,6 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/utils/arena_allocator_test.cc \ compiler/utils/dedupe_set_test.cc \ compiler/utils/swap_space_test.cc \ - compiler/utils/test_dex_file_builder_test.cc \ compiler/utils/arm/managed_register_arm_test.cc \ compiler/utils/arm64/managed_register_arm64_test.cc \ compiler/utils/x86/managed_register_x86_test.cc \ diff --git a/compiler/Android.mk b/compiler/Android.mk index 0ad77b4..ac95abd 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -23,7 +23,6 @@ LIBART_COMPILER_SRC_FILES := \ dex/global_value_numbering.cc \ dex/gvn_dead_code_elimination.cc \ dex/local_value_numbering.cc \ - dex/type_inference.cc \ dex/quick/arm/assemble_arm.cc \ dex/quick/arm/call_arm.cc \ dex/quick/arm/fp_arm.cc \ diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc index c538d0b..b4559ef 100644 --- a/compiler/dex/global_value_numbering_test.cc +++ b/compiler/dex/global_value_numbering_test.cc @@ -15,6 +15,7 @@ */ #include "base/logging.h" +#include "dataflow_iterator.h" #include "dataflow_iterator-inl.h" #include "dex/mir_field_info.h" #include "global_value_numbering.h" @@ -259,8 +260,10 @@ class GlobalValueNumberingTest : public testing::Test { mir->ssa_rep = &ssa_reps_[i]; mir->ssa_rep->num_uses = def->num_uses; mir->ssa_rep->uses = const_cast<int32_t*>(def->uses); // Not modified by LVN. + mir->ssa_rep->fp_use = nullptr; // Not used by LVN. mir->ssa_rep->num_defs = def->num_defs; mir->ssa_rep->defs = const_cast<int32_t*>(def->defs); // Not modified by LVN. + mir->ssa_rep->fp_def = nullptr; // Not used by LVN. mir->dalvikInsn.opcode = def->opcode; mir->offset = i; // LVN uses offset only for debug output mir->optimization_flags = 0u; diff --git a/compiler/dex/gvn_dead_code_elimination.cc b/compiler/dex/gvn_dead_code_elimination.cc index d7f36f7..ec12221 100644 --- a/compiler/dex/gvn_dead_code_elimination.cc +++ b/compiler/dex/gvn_dead_code_elimination.cc @@ -478,7 +478,7 @@ void GvnDeadCodeElimination::ChangeBinOp2AddrToPlainBinOp(MIR* mir) { mir->dalvikInsn.opcode - Instruction::ADD_INT_2ADDR + Instruction::ADD_INT); } -MIR* GvnDeadCodeElimination::CreatePhi(int s_reg) { +MIR* GvnDeadCodeElimination::CreatePhi(int s_reg, bool fp) { int v_reg = mir_graph_->SRegToVReg(s_reg); MIR* phi = mir_graph_->NewMIR(); phi->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpPhi); @@ -491,9 +491,11 @@ MIR* GvnDeadCodeElimination::CreatePhi(int s_reg) { mir_graph_->AllocateSSADefData(phi, 1); phi->ssa_rep->defs[0] = s_reg; + phi->ssa_rep->fp_def[0] = fp; size_t num_uses = bb_->predecessors.size(); mir_graph_->AllocateSSAUseData(phi, num_uses); + std::fill_n(phi->ssa_rep->fp_use, num_uses, fp); size_t idx = 0u; for (BasicBlockId pred_id : bb_->predecessors) { BasicBlock* pred_bb = mir_graph_->GetBasicBlock(pred_id); @@ -521,12 +523,14 @@ MIR* GvnDeadCodeElimination::RenameSRegDefOrCreatePhi(uint16_t def_change, uint1 // defining MIR for that dalvik reg, the preserved valus 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) { + bool fp = mir_to_kill->ssa_rep->fp_def[0]; if (wide) { DCHECK_EQ(new_s_reg + 1, mir_to_kill->ssa_rep->defs[1]); + DCHECK_EQ(fp, mir_to_kill->ssa_rep->fp_def[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. + CreatePhi(new_s_reg + 1, fp); // High word Phi. } - return CreatePhi(new_s_reg); + return CreatePhi(new_s_reg, fp); } else { DCHECK_LT(def_change, last_change); DCHECK_LE(last_change, vreg_chains_.NumMIRs()); diff --git a/compiler/dex/gvn_dead_code_elimination.h b/compiler/dex/gvn_dead_code_elimination.h index f2378f2..9a19f29 100644 --- a/compiler/dex/gvn_dead_code_elimination.h +++ b/compiler/dex/gvn_dead_code_elimination.h @@ -128,7 +128,7 @@ class GvnDeadCodeElimination : public DeletableArenaObject<kArenaAllocMisc> { void KillMIR(MIRData* data); static void KillMIR(MIR* mir); static void ChangeBinOp2AddrToPlainBinOp(MIR* mir); - MIR* CreatePhi(int s_reg); + MIR* CreatePhi(int s_reg, bool fp); MIR* RenameSRegDefOrCreatePhi(uint16_t def_change, uint16_t last_change, MIR* mir_to_kill); // Update state variables going backwards through a MIR. diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc index 0393410..566527a 100644 --- a/compiler/dex/local_value_numbering_test.cc +++ b/compiler/dex/local_value_numbering_test.cc @@ -158,8 +158,10 @@ class LocalValueNumberingTest : public testing::Test { mir->ssa_rep = &ssa_reps_[i]; mir->ssa_rep->num_uses = def->num_uses; mir->ssa_rep->uses = const_cast<int32_t*>(def->uses); // Not modified by LVN. + mir->ssa_rep->fp_use = nullptr; // Not used by LVN. mir->ssa_rep->num_defs = def->num_defs; mir->ssa_rep->defs = const_cast<int32_t*>(def->defs); // Not modified by LVN. + mir->ssa_rep->fp_def = nullptr; // Not used by LVN. mir->dalvikInsn.opcode = def->opcode; mir->offset = i; // LVN uses offset only for debug output mir->optimization_flags = 0u; diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index b4aec98..12e67cd 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -123,7 +123,7 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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, + DF_UA | DF_REF_A | DF_UMS, // 20 INSTANCE_OF vA, vB, type@CCCC DF_DA | DF_UB | DF_CORE_A | DF_REF_B | DF_UMS, @@ -159,10 +159,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_NOP, // 2B PACKED_SWITCH vAA, +BBBBBBBB - DF_UA | DF_CORE_A, + DF_UA, // 2C SPARSE_SWITCH vAA, +BBBBBBBB - DF_UA | DF_CORE_A, + DF_UA, // 2D CMPL_FLOAT vAA, vBB, vCC DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C | DF_CORE_A, @@ -180,22 +180,22 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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, + DF_UA | DF_UB, // 33 IF_NE vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, + DF_UA | DF_UB, // 34 IF_LT vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, + DF_UA | DF_UB, // 35 IF_GE vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, + DF_UA | DF_UB, // 36 IF_GT vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, + DF_UA | DF_UB, // 37 IF_LE vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, + DF_UA | DF_UB, // 38 IF_EQZ vAA, +BBBB DF_UA, @@ -1080,6 +1080,8 @@ void MIRGraph::AllocateSSAUseData(MIR *mir, int num_uses) { if (mir->ssa_rep->num_uses_allocated < num_uses) { mir->ssa_rep->uses = arena_->AllocArray<int32_t>(num_uses, kArenaAllocDFInfo); + // NOTE: will be filled in during type & size inference pass + mir->ssa_rep->fp_use = arena_->AllocArray<bool>(num_uses, kArenaAllocDFInfo); } } @@ -1088,6 +1090,7 @@ void MIRGraph::AllocateSSADefData(MIR *mir, int num_defs) { if (mir->ssa_rep->num_defs_allocated < num_defs) { mir->ssa_rep->defs = arena_->AllocArray<int32_t>(num_defs, kArenaAllocDFInfo); + mir->ssa_rep->fp_def = arena_->AllocArray<bool>(num_defs, kArenaAllocDFInfo); } } @@ -1284,27 +1287,35 @@ bool MIRGraph::DoSSAConversion(BasicBlock* bb) { if (df_attributes & DF_HAS_USES) { num_uses = 0; if (df_attributes & DF_UA) { + mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_A; HandleSSAUse(mir->ssa_rep->uses, d_insn->vA, num_uses++); if (df_attributes & DF_A_WIDE) { + mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_A; HandleSSAUse(mir->ssa_rep->uses, d_insn->vA+1, num_uses++); } } if (df_attributes & DF_UB) { + mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_B; HandleSSAUse(mir->ssa_rep->uses, d_insn->vB, num_uses++); if (df_attributes & DF_B_WIDE) { + mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_B; HandleSSAUse(mir->ssa_rep->uses, d_insn->vB+1, num_uses++); } } if (df_attributes & DF_UC) { + mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_C; HandleSSAUse(mir->ssa_rep->uses, d_insn->vC, num_uses++); if (df_attributes & DF_C_WIDE) { + mir->ssa_rep->fp_use[num_uses] = df_attributes & DF_FP_C; HandleSSAUse(mir->ssa_rep->uses, d_insn->vC+1, num_uses++); } } } if (df_attributes & DF_HAS_DEFS) { + mir->ssa_rep->fp_def[0] = df_attributes & DF_FP_A; HandleSSADef(mir->ssa_rep->defs, d_insn->vA, 0); if (df_attributes & DF_A_WIDE) { + mir->ssa_rep->fp_def[1] = df_attributes & DF_FP_A; HandleSSADef(mir->ssa_rep->defs, d_insn->vA+1, 1); } } diff --git a/compiler/dex/mir_field_info.h b/compiler/dex/mir_field_info.h index e4570fd..0d131fb 100644 --- a/compiler/dex/mir_field_info.h +++ b/compiler/dex/mir_field_info.h @@ -179,7 +179,6 @@ class MirIFieldLoweringInfo : public MirFieldInfo { friend class GlobalValueNumberingTest; friend class GvnDeadCodeEliminationTest; friend class LocalValueNumberingTest; - friend class TypeInferenceTest; }; class MirSFieldLoweringInfo : public MirFieldInfo { @@ -255,7 +254,6 @@ class MirSFieldLoweringInfo : public MirFieldInfo { friend class GlobalValueNumberingTest; friend class GvnDeadCodeEliminationTest; friend class LocalValueNumberingTest; - friend class TypeInferenceTest; }; } // namespace art diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index b5c42f1..0c1cdde 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -695,10 +695,9 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ 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))); + m_units_.push_back(new 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))); 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_; diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 0db54bf..d6dc566 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -39,7 +39,6 @@ class DexFileMethodInliner; class GlobalValueNumbering; class GvnDeadCodeElimination; class PassManager; -class TypeInference; // Forward declaration. class MIRGraph; @@ -65,7 +64,6 @@ enum DataFlowAttributePos { 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, @@ -75,7 +73,6 @@ enum DataFlowAttributePos { 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). @@ -104,7 +101,6 @@ enum DataFlowAttributePos { #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) @@ -114,7 +110,6 @@ enum DataFlowAttributePos { #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) @@ -222,11 +217,13 @@ struct BasicBlockDataFlow { */ struct SSARepresentation { int32_t* uses; + bool* fp_use; int32_t* defs; - uint16_t num_uses_allocated; - uint16_t num_defs_allocated; - uint16_t num_uses; - uint16_t num_defs; + bool* fp_def; + int16_t num_uses_allocated; + int16_t num_defs_allocated; + int16_t num_uses; + int16_t num_defs; static uint32_t GetStartUseIndex(Instruction::Code opcode); }; @@ -337,8 +334,7 @@ class MIR : public ArenaObject<kArenaAllocMIR> { // 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. + // INVOKE data index, points to MIRGraph::method_lowering_infos_. uint32_t method_lowering_info; } meta; @@ -651,10 +647,6 @@ class MIRGraph { */ 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; } @@ -699,16 +691,8 @@ class MIRGraph { 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(); + DCHECK_LT(mir->meta.method_lowering_info, method_lowering_infos_.size()); + return method_lowering_infos_[mir->meta.method_lowering_info]; } void ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke, MIR* iget_or_iput); @@ -1089,9 +1073,7 @@ class MIRGraph { bool EliminateNullChecksGate(); bool EliminateNullChecks(BasicBlock* bb); void EliminateNullChecksEnd(); - void InferTypesStart(); bool InferTypes(BasicBlock* bb); - void InferTypesEnd(); bool EliminateClassInitChecksGate(); bool EliminateClassInitChecks(BasicBlock* bb); void EliminateClassInitChecksEnd(); @@ -1118,6 +1100,34 @@ class MIRGraph { return temp_.gvn.sfield_ids[mir->meta.sfield_lowering_info]; } + /* + * Type inference handling helpers. Because Dalvik's bytecode is not fully typed, + * we have to do some work to figure out the sreg type. For some operations it is + * clear based on the opcode (i.e. ADD_FLOAT v0, v1, v2), but for others (MOVE), we + * may never know the "real" type. + * + * We perform the type inference operation by using an iterative walk over + * the graph, propagating types "defined" by typed opcodes to uses and defs in + * non-typed opcodes (such as MOVE). The Setxx(index) helpers are used to set defined + * types on typed opcodes (such as ADD_INT). The Setxx(index, is_xx) form is used to + * propagate types through non-typed opcodes such as PHI and MOVE. The is_xx flag + * tells whether our guess of the type is based on a previously typed definition. + * If so, the defined type takes precedence. Note that it's possible to have the same sreg + * show multiple defined types because dx treats constants as untyped bit patterns. + * The return value of the Setxx() helpers says whether or not the Setxx() action changed + * the current guess, and is used to know when to terminate the iterative walk. + */ + bool SetFp(int index, bool is_fp); + bool SetFp(int index); + bool SetCore(int index, bool is_core); + bool SetCore(int index); + bool SetRef(int index, bool is_ref); + bool SetRef(int index); + bool SetWide(int index, bool is_wide); + bool SetWide(int index); + bool SetHigh(int index, bool is_high); + bool SetHigh(int index); + bool PuntToInterpreter() { return punt_to_interpreter_; } @@ -1242,6 +1252,7 @@ class MIRGraph { static const char* extended_mir_op_names_[kMirOpLast - kMirOpFirst]; void HandleSSADef(int* defs, int dalvik_reg, int reg_index); + bool InferTypeAndSize(BasicBlock* bb, MIR* mir, bool changed); protected: int FindCommonParent(int block1, int block2); @@ -1388,7 +1399,6 @@ class MIRGraph { 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 { @@ -1448,7 +1458,6 @@ class MIRGraph { friend class GvnDeadCodeEliminationTest; friend class LocalValueNumberingTest; friend class TopologicalSortOrderTest; - friend class TypeInferenceTest; friend class QuickCFITest; }; diff --git a/compiler/dex/mir_method_info.h b/compiler/dex/mir_method_info.h index 946c74b..000144f 100644 --- a/compiler/dex/mir_method_info.h +++ b/compiler/dex/mir_method_info.h @@ -232,7 +232,6 @@ class MirMethodLoweringInfo : public MirMethodInfo { int stats_flags_; friend class MirOptimizationTest; - friend class TypeInferenceTest; }; } // namespace art diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index 467c14e..ac7963d 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -25,7 +25,6 @@ #include "gvn_dead_code_elimination.h" #include "local_value_numbering.h" #include "mir_field_info.h" -#include "type_inference.h" #include "quick/dex_file_method_inliner.h" #include "quick/dex_file_to_method_inliner_map.h" #include "stack.h" @@ -577,6 +576,7 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { // 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->fp_use = mir->ssa_rep->fp_use; mir_next->ssa_rep->num_defs = 0; mir->ssa_rep->num_uses = 0; mir->ssa_rep->num_defs = 0; @@ -670,7 +670,16 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { mir->ssa_rep->uses = src_ssa; mir->ssa_rep->num_uses = 3; } - AllocateSSADefData(mir, 1); + mir->ssa_rep->num_defs = 1; + mir->ssa_rep->defs = arena_->AllocArray<int32_t>(1, kArenaAllocDFInfo); + mir->ssa_rep->fp_def = arena_->AllocArray<bool>(1, kArenaAllocDFInfo); + mir->ssa_rep->fp_def[0] = if_true->ssa_rep->fp_def[0]; + // Match type of uses to def. + mir->ssa_rep->fp_use = arena_->AllocArray<bool>(mir->ssa_rep->num_uses, + kArenaAllocDFInfo); + for (int i = 0; i < mir->ssa_rep->num_uses; i++) { + mir->ssa_rep->fp_use[i] = mir->ssa_rep->fp_def[0]; + } /* * 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 @@ -1125,26 +1134,23 @@ void MIRGraph::EliminateNullChecksEnd() { } } -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); -} + bool infer_changed = false; + for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { + if (mir->ssa_rep == NULL) { + continue; + } + + // Propagate type info. + infer_changed = InferTypeAndSize(bb, mir, infer_changed); + } -void MIRGraph::InferTypesEnd() { - DCHECK(temp_.ssa.ti != nullptr); - temp_.ssa.ti->Finish(); - delete temp_.ssa.ti; - temp_.ssa.ti = nullptr; + return infer_changed; } bool MIRGraph::EliminateClassInitChecksGate() { diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc index b35bc3d..a8b8a54 100644 --- a/compiler/dex/pass_driver_me_post_opt.cc +++ b/compiler/dex/pass_driver_me_post_opt.cc @@ -41,7 +41,7 @@ void PassDriverMEPostOpt::SetupPasses(PassManager* pass_manager) { pass_manager->AddPass(new SSAConversion); pass_manager->AddPass(new PhiNodeOperands); pass_manager->AddPass(new PerformInitRegLocations); - pass_manager->AddPass(new TypeInferencePass); + pass_manager->AddPass(new TypeInference); pass_manager->AddPass(new FinishSSATransformation); } diff --git a/compiler/dex/post_opt_passes.h b/compiler/dex/post_opt_passes.h index e9fa0eb..1ab8625 100644 --- a/compiler/dex/post_opt_passes.h +++ b/compiler/dex/post_opt_passes.h @@ -263,19 +263,12 @@ class PerformInitRegLocations : public PassMEMirSsaRep { }; /** - * @class TypeInferencePass + * @class TypeInference * @brief Type inference pass. */ -class TypeInferencePass : public PassMEMirSsaRep { +class TypeInference : public PassMEMirSsaRep { public: - TypeInferencePass() : PassMEMirSsaRep("TypeInference", kRepeatingPreOrderDFSTraversal) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->InferTypesStart(); + TypeInference() : PassMEMirSsaRep("TypeInference", kRepeatingPreOrderDFSTraversal) { } bool Worker(PassDataHolder* data) const { @@ -287,13 +280,6 @@ class TypeInferencePass : public PassMEMirSsaRep { DCHECK(bb != nullptr); return c_unit->mir_graph->InferTypes(bb); } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->InferTypesEnd(); - } }; /** diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index f5e6c09..8f7eb59 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -753,7 +753,6 @@ bool DexFileMethodInliner::GenInlineConst(MIRGraph* mir_graph, BasicBlock* bb, M insn->dalvikInsn.opcode = Instruction::CONST; insn->dalvikInsn.vA = move_result->dalvikInsn.vA; insn->dalvikInsn.vB = method.d.data; - insn->meta.method_lowering_info = invoke->meta.method_lowering_info; // Preserve type info. bb->InsertMIRAfter(move_result, insn); return true; } @@ -792,7 +791,6 @@ bool DexFileMethodInliner::GenInlineReturnArg(MIRGraph* mir_graph, BasicBlock* b insn->dalvikInsn.opcode = opcode; insn->dalvikInsn.vA = move_result->dalvikInsn.vA; insn->dalvikInsn.vB = arg; - insn->meta.method_lowering_info = invoke->meta.method_lowering_info; // Preserve type info. bb->InsertMIRAfter(move_result, insn); return true; } @@ -915,7 +913,6 @@ bool DexFileMethodInliner::GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MI } move->dalvikInsn.vA = move_result->dalvikInsn.vA; move->dalvikInsn.vB = return_reg; - move->meta.method_lowering_info = invoke->meta.method_lowering_info; // Preserve type info. bb->InsertMIRAfter(insn, move); } return true; diff --git a/compiler/dex/type_inference.cc b/compiler/dex/type_inference.cc deleted file mode 100644 index 84cd69a..0000000 --- a/compiler/dex/type_inference.cc +++ /dev/null @@ -1,1064 +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 "type_inference.h" - -#include "base/bit_vector-inl.h" -#include "compiler_ir.h" -#include "dataflow_iterator-inl.h" -#include "dex_flags.h" -#include "dex_file-inl.h" -#include "driver/dex_compilation_unit.h" -#include "mir_field_info.h" -#include "mir_graph.h" -#include "mir_method_info.h" - -namespace art { - -inline TypeInference::Type TypeInference::Type::ArrayType(uint32_t array_depth, Type nested_type) { - DCHECK_NE(array_depth, 0u); - return Type(kFlagNarrow | kFlagRef | kFlagLowWord | (array_depth << kBitArrayDepthStart) | - ((nested_type.raw_bits_ & kMaskWideAndType) << kArrayTypeShift)); -} - -inline TypeInference::Type TypeInference::Type::ArrayTypeFromComponent(Type component_type) { - if (component_type.ArrayDepth() == 0u) { - return ArrayType(1u, component_type); - } - if (UNLIKELY(component_type.ArrayDepth() == kMaxArrayDepth)) { - return component_type; - } - return Type(component_type.raw_bits_ + (1u << kBitArrayDepthStart)); // array_depth + 1u; -} - -TypeInference::Type TypeInference::Type::ShortyType(char shorty) { - switch (shorty) { - case 'L': - return Type(kFlagLowWord | kFlagNarrow | kFlagRef); - case 'D': - return Type(kFlagLowWord | kFlagWide | kFlagFp); - case 'J': - return Type(kFlagLowWord | kFlagWide | kFlagCore); - case 'F': - return Type(kFlagLowWord | kFlagNarrow | kFlagFp); - default: - DCHECK(shorty == 'I' || shorty == 'S' || shorty == 'C' || shorty == 'B' || shorty == 'Z'); - return Type(kFlagLowWord | kFlagNarrow | kFlagCore); - } -} - -TypeInference::Type TypeInference::Type::DexType(const DexFile* dex_file, uint32_t type_idx) { - const char* desc = dex_file->GetTypeDescriptor(dex_file->GetTypeId(type_idx)); - if (UNLIKELY(desc[0] == 'V')) { - return Unknown(); - } else if (UNLIKELY(desc[0] == '[')) { - size_t array_depth = 0u; - while (*desc == '[') { - ++array_depth; - ++desc; - } - if (UNLIKELY(array_depth > kMaxArrayDepth)) { - LOG(WARNING) << "Array depth exceeds " << kMaxArrayDepth << ": " << array_depth - << " in dex file " << dex_file->GetLocation() << " type index " << type_idx; - array_depth = kMaxArrayDepth; - } - Type shorty_result = Type::ShortyType(desc[0]); - return ArrayType(array_depth, shorty_result); - } else { - return ShortyType(desc[0]); - } -} - -bool TypeInference::Type::MergeArrayConflict(Type src_type) { - DCHECK(Ref()); - DCHECK_NE(ArrayDepth(), src_type.ArrayDepth()); - DCHECK_GE(std::min(ArrayDepth(), src_type.ArrayDepth()), 1u); - bool size_conflict = - (ArrayDepth() == 1u && (raw_bits_ & kFlagArrayWide) != 0u) || - (src_type.ArrayDepth() == 1u && (src_type.raw_bits_ & kFlagArrayWide) != 0u); - // Mark all three array type bits so that merging any other type bits will not change this type. - return Copy(Type((raw_bits_ & kMaskNonArray) | - (1u << kBitArrayDepthStart) | kFlagArrayCore | kFlagArrayRef | kFlagArrayFp | - kFlagArrayNarrow | (size_conflict ? kFlagArrayWide : 0u))); -} - -bool TypeInference::Type::MergeStrong(Type src_type) { - bool changed = MergeNonArrayFlags(src_type); - if (src_type.ArrayDepth() != 0u) { - if (ArrayDepth() == 0u) { - DCHECK_EQ(raw_bits_ & ~kMaskNonArray, 0u); - DCHECK_NE(src_type.raw_bits_ & kFlagRef, 0u); - raw_bits_ |= src_type.raw_bits_ & (~kMaskNonArray | kFlagRef); - changed = true; - } else if (ArrayDepth() == src_type.ArrayDepth()) { - changed |= MergeBits(src_type, kMaskArrayWideAndType); - } else if (src_type.ArrayDepth() == 1u && - (((src_type.raw_bits_ ^ UnknownArrayType().raw_bits_) & kMaskArrayWideAndType) == 0u || - ((src_type.raw_bits_ ^ ObjectArrayType().raw_bits_) & kMaskArrayWideAndType) == 0u)) { - // Source type is [L or [? but current type is at least [[, preserve it. - } else if (ArrayDepth() == 1u && - (((raw_bits_ ^ UnknownArrayType().raw_bits_) & kMaskArrayWideAndType) == 0u || - ((raw_bits_ ^ ObjectArrayType().raw_bits_) & kMaskArrayWideAndType) == 0u)) { - // Overwrite [? or [L with the source array type which is at least [[. - raw_bits_ = (raw_bits_ & kMaskNonArray) | (src_type.raw_bits_ & ~kMaskNonArray); - changed = true; - } else { - // Mark the array value type with conflict - both ref and fp. - changed |= MergeArrayConflict(src_type); - } - } - return changed; -} - -bool TypeInference::Type::MergeWeak(Type src_type) { - bool changed = MergeNonArrayFlags(src_type); - if (src_type.ArrayDepth() != 0u && src_type.NonNull()) { - DCHECK_NE(src_type.ArrayDepth(), 0u); - if (ArrayDepth() == 0u) { - DCHECK_EQ(raw_bits_ & ~kMaskNonArray, 0u); - // Preserve current type. - } else if (ArrayDepth() == src_type.ArrayDepth()) { - changed |= MergeBits(src_type, kMaskArrayWideAndType); - } else if (src_type.ArrayDepth() == 1u && - (((src_type.raw_bits_ ^ UnknownArrayType().raw_bits_) & kMaskArrayWideAndType) == 0u || - ((src_type.raw_bits_ ^ ObjectArrayType().raw_bits_) & kMaskArrayWideAndType) == 0u)) { - // Source type is [L or [? but current type is at least [[, preserve it. - } else if (ArrayDepth() == 1u && - (((raw_bits_ ^ UnknownArrayType().raw_bits_) & kMaskArrayWideAndType) == 0u || - ((raw_bits_ ^ ObjectArrayType().raw_bits_) & kMaskArrayWideAndType) == 0u)) { - // We have [? or [L. If it's [?, upgrade to [L as the source array type is at least [[. - changed |= MergeBits(ObjectArrayType(), kMaskArrayWideAndType); - } else { - // Mark the array value type with conflict - both ref and fp. - changed |= MergeArrayConflict(src_type); - } - } - return changed; -} - -TypeInference::CheckCastData::CheckCastData(MIRGraph* mir_graph, ScopedArenaAllocator* alloc) - : mir_graph_(mir_graph), - alloc_(alloc), - num_blocks_(mir_graph->GetNumBlocks()), - num_sregs_(mir_graph->GetNumSSARegs()), - check_cast_map_(std::less<MIR*>(), alloc->Adapter()), - split_sreg_data_(std::less<int32_t>(), alloc->Adapter()) { -} - -void TypeInference::CheckCastData::AddCheckCast(MIR* check_cast, Type type) { - DCHECK_EQ(check_cast->dalvikInsn.opcode, Instruction::CHECK_CAST); - type.CheckPureRef(); - int32_t extra_s_reg = static_cast<int32_t>(num_sregs_); - num_sregs_ += 1; - check_cast_map_.Put(check_cast, CheckCastMapValue{extra_s_reg, type}); // NOLINT - int32_t s_reg = check_cast->ssa_rep->uses[0]; - auto lb = split_sreg_data_.lower_bound(s_reg); - if (lb == split_sreg_data_.end() || split_sreg_data_.key_comp()(s_reg, lb->first)) { - SplitSRegData split_s_reg_data = { - 0, - alloc_->AllocArray<int32_t>(num_blocks_, kArenaAllocMisc), - alloc_->AllocArray<int32_t>(num_blocks_, kArenaAllocMisc), - new (alloc_) ArenaBitVector(alloc_, num_blocks_, false) - }; - std::fill_n(split_s_reg_data.starting_mod_s_reg, num_blocks_, INVALID_SREG); - std::fill_n(split_s_reg_data.ending_mod_s_reg, num_blocks_, INVALID_SREG); - split_s_reg_data.def_phi_blocks_->ClearAllBits(); - BasicBlock* def_bb = FindDefBlock(check_cast); - split_s_reg_data.ending_mod_s_reg[def_bb->id] = s_reg; - split_s_reg_data.def_phi_blocks_->SetBit(def_bb->id); - lb = split_sreg_data_.PutBefore(lb, s_reg, split_s_reg_data); - } - lb->second.ending_mod_s_reg[check_cast->bb] = extra_s_reg; - lb->second.def_phi_blocks_->SetBit(check_cast->bb); -} - -void TypeInference::CheckCastData::AddPseudoPhis() { - // Look for pseudo-phis where a split SSA reg merges with a differently typed version - // and initialize all starting_mod_s_reg. - DCHECK(!split_sreg_data_.empty()); - ArenaBitVector* phi_blocks = new (alloc_) ArenaBitVector(alloc_, num_blocks_, false); - - for (auto& entry : split_sreg_data_) { - SplitSRegData& data = entry.second; - - // Find pseudo-phi nodes. - phi_blocks->ClearAllBits(); - ArenaBitVector* input_blocks = data.def_phi_blocks_; - do { - for (uint32_t idx : input_blocks->Indexes()) { - BasicBlock* def_bb = mir_graph_->GetBasicBlock(idx); - if (def_bb->dom_frontier != nullptr) { - phi_blocks->Union(def_bb->dom_frontier); - } - } - } while (input_blocks->Union(phi_blocks)); - - // Find live pseudo-phis. Make sure they're merging the same SSA reg. - data.def_phi_blocks_->ClearAllBits(); - int32_t s_reg = entry.first; - int v_reg = mir_graph_->SRegToVReg(s_reg); - for (uint32_t phi_bb_id : phi_blocks->Indexes()) { - BasicBlock* phi_bb = mir_graph_->GetBasicBlock(phi_bb_id); - DCHECK(phi_bb != nullptr); - DCHECK(phi_bb->data_flow_info != nullptr); - DCHECK(phi_bb->data_flow_info->live_in_v != nullptr); - if (IsSRegLiveAtStart(phi_bb, v_reg, s_reg)) { - int32_t extra_s_reg = static_cast<int32_t>(num_sregs_); - num_sregs_ += 1; - data.starting_mod_s_reg[phi_bb_id] = extra_s_reg; - data.def_phi_blocks_->SetBit(phi_bb_id); - } - } - - // SSA rename for s_reg. - TopologicalSortIterator iter(mir_graph_); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - if (bb->data_flow_info == nullptr || bb->block_type == kEntryBlock) { - continue; - } - BasicBlockId bb_id = bb->id; - if (data.def_phi_blocks_->IsBitSet(bb_id)) { - DCHECK_NE(data.starting_mod_s_reg[bb_id], INVALID_SREG); - } else { - DCHECK_EQ(data.starting_mod_s_reg[bb_id], INVALID_SREG); - if (IsSRegLiveAtStart(bb, v_reg, s_reg)) { - // The earliest predecessor must have been processed already. - BasicBlock* pred_bb = FindTopologicallyEarliestPredecessor(bb); - int32_t mod_s_reg = data.ending_mod_s_reg[pred_bb->id]; - data.starting_mod_s_reg[bb_id] = (mod_s_reg != INVALID_SREG) ? mod_s_reg : s_reg; - } else if (data.ending_mod_s_reg[bb_id] != INVALID_SREG) { - // Start the original defining block with s_reg. - data.starting_mod_s_reg[bb_id] = s_reg; - } - } - if (data.ending_mod_s_reg[bb_id] == INVALID_SREG) { - // If the block doesn't define the modified SSA reg, it propagates the starting type. - data.ending_mod_s_reg[bb_id] = data.starting_mod_s_reg[bb_id]; - } - } - } -} - -void TypeInference::CheckCastData::InitializeCheckCastSRegs(Type* sregs) const { - for (const auto& entry : check_cast_map_) { - DCHECK_LT(static_cast<size_t>(entry.second.modified_s_reg), num_sregs_); - sregs[entry.second.modified_s_reg] = entry.second.type.AsNonNull(); - } -} - -void TypeInference::CheckCastData::MergeCheckCastConflicts(Type* sregs) const { - for (const auto& entry : check_cast_map_) { - DCHECK_LT(static_cast<size_t>(entry.second.modified_s_reg), num_sregs_); - sregs[entry.first->ssa_rep->uses[0]].MergeNonArrayFlags( - sregs[entry.second.modified_s_reg].AsNull()); - } -} - -void TypeInference::CheckCastData::MarkPseudoPhiBlocks(uint64_t* bb_df_attrs) const { - for (auto& entry : split_sreg_data_) { - for (uint32_t bb_id : entry.second.def_phi_blocks_->Indexes()) { - bb_df_attrs[bb_id] |= DF_NULL_TRANSFER_N; - } - } -} - -void TypeInference::CheckCastData::Start(BasicBlock* bb) { - for (auto& entry : split_sreg_data_) { - entry.second.current_mod_s_reg = entry.second.starting_mod_s_reg[bb->id]; - } -} - -bool TypeInference::CheckCastData::ProcessPseudoPhis(BasicBlock* bb, Type* sregs) { - bool changed = false; - for (auto& entry : split_sreg_data_) { - DCHECK_EQ(entry.second.current_mod_s_reg, entry.second.starting_mod_s_reg[bb->id]); - if (entry.second.def_phi_blocks_->IsBitSet(bb->id)) { - int32_t* ending_mod_s_reg = entry.second.ending_mod_s_reg; - Type merged_type = sregs[entry.second.current_mod_s_reg]; - for (BasicBlockId pred_id : bb->predecessors) { - DCHECK_LT(static_cast<size_t>(ending_mod_s_reg[pred_id]), num_sregs_); - merged_type.MergeWeak(sregs[ending_mod_s_reg[pred_id]]); - } - if (UNLIKELY(!merged_type.IsDefined())) { - // This can happen during an initial merge of a loop head if the original def is - // actually an untyped null. (All other definitions are typed using the check-cast.) - } else if (merged_type.Wide()) { - // Ignore the pseudo-phi, just remember that there's a size mismatch. - sregs[entry.second.current_mod_s_reg].MarkSizeConflict(); - } else { - DCHECK(merged_type.Narrow() && merged_type.LowWord() && !merged_type.HighWord()); - // Propagate both down (fully) and up (without the "non-null" flag). - changed |= sregs[entry.second.current_mod_s_reg].Copy(merged_type); - merged_type = merged_type.AsNull(); - for (BasicBlockId pred_id : bb->predecessors) { - DCHECK_LT(static_cast<size_t>(ending_mod_s_reg[pred_id]), num_sregs_); - sregs[ending_mod_s_reg[pred_id]].MergeStrong(merged_type); - } - } - } - } - return changed; -} - -void TypeInference::CheckCastData::ProcessCheckCast(MIR* mir) { - auto mir_it = check_cast_map_.find(mir); - DCHECK(mir_it != check_cast_map_.end()); - auto sreg_it = split_sreg_data_.find(mir->ssa_rep->uses[0]); - DCHECK(sreg_it != split_sreg_data_.end()); - sreg_it->second.current_mod_s_reg = mir_it->second.modified_s_reg; -} - -TypeInference::SplitSRegData* TypeInference::CheckCastData::GetSplitSRegData(int32_t s_reg) { - auto it = split_sreg_data_.find(s_reg); - return (it == split_sreg_data_.end()) ? nullptr : &it->second; -} - -BasicBlock* TypeInference::CheckCastData::FindDefBlock(MIR* check_cast) { - // Find the initial definition of the SSA reg used by the check-cast. - DCHECK_EQ(check_cast->dalvikInsn.opcode, Instruction::CHECK_CAST); - int32_t s_reg = check_cast->ssa_rep->uses[0]; - if (mir_graph_->IsInVReg(s_reg)) { - return mir_graph_->GetEntryBlock(); - } - int v_reg = mir_graph_->SRegToVReg(s_reg); - BasicBlock* bb = mir_graph_->GetBasicBlock(check_cast->bb); - DCHECK(bb != nullptr); - while (true) { - // Find the earliest predecessor in the topological sort order to ensure we don't - // go in a loop. - BasicBlock* pred_bb = FindTopologicallyEarliestPredecessor(bb); - DCHECK(pred_bb != nullptr); - DCHECK(pred_bb->data_flow_info != nullptr); - DCHECK(pred_bb->data_flow_info->vreg_to_ssa_map_exit != nullptr); - if (pred_bb->data_flow_info->vreg_to_ssa_map_exit[v_reg] != s_reg) { - // The s_reg was not valid at the end of pred_bb, so it must have been defined in bb. - return bb; - } - bb = pred_bb; - } -} - -BasicBlock* TypeInference::CheckCastData::FindTopologicallyEarliestPredecessor(BasicBlock* bb) { - DCHECK(!bb->predecessors.empty()); - const auto& indexes = mir_graph_->GetTopologicalSortOrderIndexes(); - DCHECK_LT(bb->id, indexes.size()); - size_t best_idx = indexes[bb->id]; - BasicBlockId best_id = NullBasicBlockId; - for (BasicBlockId pred_id : bb->predecessors) { - DCHECK_LT(pred_id, indexes.size()); - if (best_idx > indexes[pred_id]) { - best_idx = indexes[pred_id]; - best_id = pred_id; - } - } - // There must be at least one predecessor earlier than the bb. - DCHECK_LT(best_idx, indexes[bb->id]); - return mir_graph_->GetBasicBlock(best_id); -} - -bool TypeInference::CheckCastData::IsSRegLiveAtStart(BasicBlock* bb, int v_reg, int32_t s_reg) { - DCHECK_EQ(v_reg, mir_graph_->SRegToVReg(s_reg)); - DCHECK(bb != nullptr); - DCHECK(bb->data_flow_info != nullptr); - DCHECK(bb->data_flow_info->live_in_v != nullptr); - if (!bb->data_flow_info->live_in_v->IsBitSet(v_reg)) { - return false; - } - for (BasicBlockId pred_id : bb->predecessors) { - BasicBlock* pred_bb = mir_graph_->GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - DCHECK(pred_bb->data_flow_info != nullptr); - DCHECK(pred_bb->data_flow_info->vreg_to_ssa_map_exit != nullptr); - if (pred_bb->data_flow_info->vreg_to_ssa_map_exit[v_reg] != s_reg) { - return false; - } - } - return true; -} - -TypeInference::TypeInference(MIRGraph* mir_graph, ScopedArenaAllocator* alloc) - : mir_graph_(mir_graph), - cu_(mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit()), - check_cast_data_(!mir_graph->HasCheckCast() ? nullptr : - InitializeCheckCastData(mir_graph, alloc)), - num_sregs_( - check_cast_data_ != nullptr ? check_cast_data_->NumSRegs() : mir_graph->GetNumSSARegs()), - ifields_(mir_graph->GetIFieldLoweringInfoCount() == 0u ? nullptr : - PrepareIFieldTypes(cu_->dex_file, mir_graph, alloc)), - sfields_(mir_graph->GetSFieldLoweringInfoCount() == 0u ? nullptr : - PrepareSFieldTypes(cu_->dex_file, mir_graph, alloc)), - signatures_(mir_graph->GetMethodLoweringInfoCount() == 0u ? nullptr : - PrepareSignatures(cu_->dex_file, mir_graph, alloc)), - current_method_signature_( - Signature(cu_->dex_file, cu_->method_idx, (cu_->access_flags & kAccStatic) != 0, alloc)), - sregs_(alloc->AllocArray<Type>(num_sregs_, kArenaAllocMisc)), - bb_df_attrs_(alloc->AllocArray<uint64_t>(mir_graph->GetNumBlocks(), kArenaAllocDFInfo)) { - InitializeSRegs(); -} - -bool TypeInference::Apply(BasicBlock* bb) { - bool changed = false; - uint64_t bb_df_attrs = bb_df_attrs_[bb->id]; - if (bb_df_attrs != 0u) { - if (UNLIKELY(check_cast_data_ != nullptr)) { - check_cast_data_->Start(bb); - if (bb_df_attrs & DF_NULL_TRANSFER_N) { - changed |= check_cast_data_->ProcessPseudoPhis(bb, sregs_); - } - } - MIR* mir = bb->first_mir_insn; - MIR* main_mirs_end = ((bb_df_attrs & DF_SAME_TYPE_AB) != 0u) ? bb->last_mir_insn : nullptr; - for (; mir != main_mirs_end && static_cast<int>(mir->dalvikInsn.opcode) == kMirOpPhi; - mir = mir->next) { - // Special-case handling for Phi comes first because we have 2 Phis instead of a wide one. - // At least one input must have been previously processed. Look for the first - // occurrence of a high_word or low_word flag to determine the type. - size_t num_uses = mir->ssa_rep->num_uses; - const int32_t* uses = mir->ssa_rep->uses; - const int32_t* defs = mir->ssa_rep->defs; - DCHECK_EQ(bb->predecessors.size(), num_uses); - Type merged_type = sregs_[defs[0]]; - for (size_t pred_idx = 0; pred_idx != num_uses; ++pred_idx) { - int32_t input_mod_s_reg = PhiInputModifiedSReg(uses[pred_idx], bb, pred_idx); - merged_type.MergeWeak(sregs_[input_mod_s_reg]); - } - if (UNLIKELY(!merged_type.IsDefined())) { - // No change - } else if (merged_type.HighWord()) { - // Ignore the high word phi, just remember if there's a size mismatch. - if (UNLIKELY(merged_type.LowWord())) { - sregs_[defs[0]].MarkSizeConflict(); - } - } else { - // Propagate both down (fully) and up (without the "non-null" flag). - changed |= sregs_[defs[0]].Copy(merged_type); - merged_type = merged_type.AsNull(); - for (size_t pred_idx = 0; pred_idx != num_uses; ++pred_idx) { - int32_t input_mod_s_reg = PhiInputModifiedSReg(uses[pred_idx], bb, pred_idx); - changed |= UpdateSRegFromLowWordType(input_mod_s_reg, merged_type); - } - } - } - - // Propagate types with MOVEs and AGETs, process CHECK_CASTs for modified SSA reg tracking. - for (; mir != main_mirs_end; mir = mir->next) { - uint64_t attrs = MIRGraph::GetDataFlowAttributes(mir); - size_t num_uses = mir->ssa_rep->num_uses; - const int32_t* uses = mir->ssa_rep->uses; - const int32_t* defs = mir->ssa_rep->defs; - - // Special handling for moves. Propagate type both ways. - if ((attrs & DF_IS_MOVE) != 0) { - int32_t used_mod_s_reg = ModifiedSReg(uses[0]); - int32_t defd_mod_s_reg = defs[0]; - - // The "non-null" flag is propagated only downwards from actual definitions and it's - // not initially marked for moves, so used sreg must be marked before defined sreg. - // The only exception is an inlined move where we know the type from the original invoke. - DCHECK(sregs_[used_mod_s_reg].NonNull() || !sregs_[defd_mod_s_reg].NonNull() || - (mir->optimization_flags & MIR_CALLEE) != 0); - changed |= UpdateSRegFromLowWordType(used_mod_s_reg, sregs_[defd_mod_s_reg].AsNull()); - - // The value is the same, so either both registers are null or no register is. - // In any case we can safely propagate the array type down. - changed |= UpdateSRegFromLowWordType(defd_mod_s_reg, sregs_[used_mod_s_reg]); - if (UNLIKELY((attrs & DF_REF_A) == 0 && sregs_[used_mod_s_reg].Ref())) { - // Mark type conflict: move instead of move-object. - sregs_[used_mod_s_reg].MarkTypeConflict(); - } - continue; - } - - // Handle AGET/APUT. - if ((attrs & DF_HAS_RANGE_CHKS) != 0) { - int32_t base_mod_s_reg = ModifiedSReg(uses[num_uses - 2u]); - int32_t mod_s_reg = (attrs & DF_DA) != 0 ? defs[0] : ModifiedSReg(uses[0]); - DCHECK_NE(sregs_[base_mod_s_reg].ArrayDepth(), 0u); - if (!sregs_[base_mod_s_reg].NonNull()) { - // If the base is null, don't propagate anything. All that we could determine - // has already been merged in the previous stage. - } else { - changed |= UpdateSRegFromLowWordType(mod_s_reg, sregs_[base_mod_s_reg].ComponentType()); - Type array_type = Type::ArrayTypeFromComponent(sregs_[mod_s_reg]); - if ((attrs & DF_DA) != 0) { - changed |= sregs_[base_mod_s_reg].MergeStrong(array_type); - } else { - changed |= sregs_[base_mod_s_reg].MergeWeak(array_type); - } - } - if (UNLIKELY((attrs & DF_REF_A) == 0 && sregs_[mod_s_reg].Ref())) { - // Mark type conflict: aget/aput instead of aget/aput-object. - sregs_[mod_s_reg].MarkTypeConflict(); - } - continue; - } - - // Special-case handling for check-cast to advance modified SSA reg. - if (UNLIKELY((attrs & DF_CHK_CAST) != 0)) { - DCHECK(check_cast_data_ != nullptr); - check_cast_data_->ProcessCheckCast(mir); - } - } - - // Propagate types for IF_cc if present. - if (mir != nullptr) { - DCHECK(mir == bb->last_mir_insn); - DCHECK(mir->next == nullptr); - DCHECK_NE(MIRGraph::GetDataFlowAttributes(mir) & DF_SAME_TYPE_AB, 0u); - DCHECK_EQ(mir->ssa_rep->num_uses, 2u); - const int32_t* uses = mir->ssa_rep->uses; - int32_t mod_s_reg0 = ModifiedSReg(uses[0]); - int32_t mod_s_reg1 = ModifiedSReg(uses[1]); - changed |= sregs_[mod_s_reg0].MergeWeak(sregs_[mod_s_reg1].AsNull()); - changed |= sregs_[mod_s_reg1].MergeWeak(sregs_[mod_s_reg0].AsNull()); - } - } - return changed; -} - -void TypeInference::Finish() { - if (UNLIKELY(check_cast_data_ != nullptr)) { - check_cast_data_->MergeCheckCastConflicts(sregs_); - } - - size_t num_sregs = mir_graph_->GetNumSSARegs(); // Without the extra SSA regs. - for (size_t s_reg = 0; s_reg != num_sregs; ++s_reg) { - if (sregs_[s_reg].SizeConflict()) { - /* - * The dex bytecode definition does not explicitly outlaw the definition of the same - * virtual register to be used in both a 32-bit and 64-bit pair context. However, dx - * does not generate this pattern (at least recently). Further, in the next revision of - * dex, we will forbid this. To support the few cases in the wild, detect this pattern - * and punt to the interpreter. - */ - LOG(WARNING) << PrettyMethod(cu_->method_idx, *cu_->dex_file) - << " has size conflict block for sreg " << s_reg - << ", punting to interpreter."; - mir_graph_->SetPuntToInterpreter(true); - return; - } - } - - size_t conflict_s_reg = 0; - bool type_conflict = false; - for (size_t s_reg = 0; s_reg != num_sregs; ++s_reg) { - Type type = sregs_[s_reg]; - RegLocation* loc = &mir_graph_->reg_location_[s_reg]; - loc->wide = type.Wide(); - loc->defined = type.IsDefined(); - loc->fp = type.Fp(); - loc->core = type.Core(); - loc->ref = type.Ref(); - loc->high_word = type.HighWord(); - if (UNLIKELY(type.TypeConflict())) { - type_conflict = true; - conflict_s_reg = s_reg; - } - } - - if (type_conflict) { - /* - * We don't normally expect to see a Dalvik register definition used both as a - * floating point and core value, though technically it could happen with constants. - * Until we have proper typing, detect this situation and disable register promotion - * (which relies on the distinction between core a fp usages). - */ - LOG(WARNING) << PrettyMethod(cu_->method_idx, *cu_->dex_file) - << " has type conflict block for sreg " << conflict_s_reg - << ", disabling register promotion."; - cu_->disable_opt |= (1 << kPromoteRegs); - } -} - -TypeInference::Type TypeInference::FieldType(const DexFile* dex_file, uint32_t field_idx) { - uint32_t type_idx = dex_file->GetFieldId(field_idx).type_idx_; - Type result = Type::DexType(dex_file, type_idx); - return result; -} - -TypeInference::Type* TypeInference::PrepareIFieldTypes(const DexFile* dex_file, - MIRGraph* mir_graph, - ScopedArenaAllocator* alloc) { - size_t count = mir_graph->GetIFieldLoweringInfoCount(); - Type* ifields = alloc->AllocArray<Type>(count, kArenaAllocDFInfo); - for (uint32_t i = 0u; i != count; ++i) { - // NOTE: Quickened field accesses have invalid FieldIndex() but they are always resolved. - const MirFieldInfo& info = mir_graph->GetIFieldLoweringInfo(i); - const DexFile* current_dex_file = info.IsResolved() ? info.DeclaringDexFile() : dex_file; - uint32_t field_idx = info.IsResolved() ? info.DeclaringFieldIndex() : info.FieldIndex(); - ifields[i] = FieldType(current_dex_file, field_idx); - DCHECK_EQ(info.MemAccessType() == kDexMemAccessWide, ifields[i].Wide()); - DCHECK_EQ(info.MemAccessType() == kDexMemAccessObject, ifields[i].Ref()); - } - return ifields; -} - -TypeInference::Type* TypeInference::PrepareSFieldTypes(const DexFile* dex_file, - MIRGraph* mir_graph, - ScopedArenaAllocator* alloc) { - size_t count = mir_graph->GetSFieldLoweringInfoCount(); - Type* sfields = alloc->AllocArray<Type>(count, kArenaAllocDFInfo); - for (uint32_t i = 0u; i != count; ++i) { - // FieldIndex() is always valid for static fields (no quickened instructions). - sfields[i] = FieldType(dex_file, mir_graph->GetSFieldLoweringInfo(i).FieldIndex()); - } - return sfields; -} - -TypeInference::MethodSignature TypeInference::Signature(const DexFile* dex_file, - uint32_t method_idx, - bool is_static, - ScopedArenaAllocator* alloc) { - const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx); - const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); - Type return_type = Type::DexType(dex_file, proto_id.return_type_idx_); - const DexFile::TypeList* type_list = dex_file->GetProtoParameters(proto_id); - size_t this_size = (is_static ? 0u : 1u); - size_t param_size = ((type_list != nullptr) ? type_list->Size() : 0u); - size_t size = this_size + param_size; - Type* param_types = (size != 0u) ? alloc->AllocArray<Type>(size, kArenaAllocDFInfo) : nullptr; - if (!is_static) { - param_types[0] = Type::DexType(dex_file, method_id.class_idx_); - } - for (size_t i = 0; i != param_size; ++i) { - uint32_t type_idx = type_list->GetTypeItem(i).type_idx_; - param_types[this_size + i] = Type::DexType(dex_file, type_idx); - } - return MethodSignature{ return_type, size, param_types }; // NOLINT -} - -TypeInference::MethodSignature* TypeInference::PrepareSignatures(const DexFile* dex_file, - MIRGraph* mir_graph, - ScopedArenaAllocator* alloc) { - size_t count = mir_graph->GetMethodLoweringInfoCount(); - MethodSignature* signatures = alloc->AllocArray<MethodSignature>(count, kArenaAllocDFInfo); - for (uint32_t i = 0u; i != count; ++i) { - // NOTE: Quickened invokes have invalid MethodIndex() but they are always resolved. - const MirMethodInfo& info = mir_graph->GetMethodLoweringInfo(i); - uint32_t method_idx = info.IsResolved() ? info.DeclaringMethodIndex() : info.MethodIndex(); - const DexFile* current_dex_file = info.IsResolved() ? info.DeclaringDexFile() : dex_file; - signatures[i] = Signature(current_dex_file, method_idx, info.IsStatic(), alloc); - } - return signatures; -} - -TypeInference::CheckCastData* TypeInference::InitializeCheckCastData(MIRGraph* mir_graph, - ScopedArenaAllocator* alloc) { - if (!mir_graph->HasCheckCast()) { - return nullptr; - } - - CheckCastData* data = nullptr; - const DexFile* dex_file = nullptr; - PreOrderDfsIterator iter(mir_graph); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (mir->dalvikInsn.opcode == Instruction::CHECK_CAST) { - if (data == nullptr) { - data = new (alloc) CheckCastData(mir_graph, alloc); - dex_file = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit()->dex_file; - } - Type type = Type::DexType(dex_file, mir->dalvikInsn.vB); - data->AddCheckCast(mir, type); - } - } - } - if (data != nullptr) { - data->AddPseudoPhis(); - } - return data; -} - -void TypeInference::InitializeSRegs() { - std::fill_n(sregs_, num_sregs_, Type::Unknown()); - - // Initialize parameter SSA regs at method entry. - int32_t entry_param_s_reg = mir_graph_->GetFirstInVR(); - for (size_t i = 0, size = current_method_signature_.num_params; i != size; ++i) { - Type param_type = current_method_signature_.param_types[i].AsNonNull(); - sregs_[entry_param_s_reg] = param_type; - entry_param_s_reg += param_type.Wide() ? 2 : 1; - } - DCHECK_EQ(static_cast<uint32_t>(entry_param_s_reg), - mir_graph_->GetFirstInVR() + mir_graph_->GetNumOfInVRs()); - - // Initialize check-cast types. - if (UNLIKELY(check_cast_data_ != nullptr)) { - check_cast_data_->InitializeCheckCastSRegs(sregs_); - } - - // Initialize well-known SSA register definition types. Merge inferred types - // upwards where a single merge is enough (INVOKE arguments and return type, - // RETURN type, IPUT/SPUT source type). - // NOTE: Using topological sort order to make sure the definition comes before - // any upward merging. This allows simple assignment of the defined types - // instead of MergeStrong(). - TopologicalSortIterator iter(mir_graph_); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - uint64_t bb_df_attrs = 0u; - if (UNLIKELY(check_cast_data_ != nullptr)) { - check_cast_data_->Start(bb); - } - // Ignore pseudo-phis, we're not setting types for SSA regs that depend on them in this pass. - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - uint64_t attrs = MIRGraph::GetDataFlowAttributes(mir); - bb_df_attrs |= attrs; - - const uint32_t num_uses = mir->ssa_rep->num_uses; - const int32_t* uses = mir->ssa_rep->uses; - const int32_t* defs = mir->ssa_rep->defs; - - uint16_t opcode = mir->dalvikInsn.opcode; - switch (opcode) { - 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::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: - if ((mir->optimization_flags & MIR_CALLEE) != 0) { - // Inlined const/move keeps method_lowering_info for type inference. - DCHECK_LT(mir->meta.method_lowering_info, mir_graph_->GetMethodLoweringInfoCount()); - Type return_type = signatures_[mir->meta.method_lowering_info].return_type; - DCHECK(return_type.IsDefined()); // Method return type can't be void. - sregs_[defs[0]] = return_type.AsNonNull(); - if (return_type.Wide()) { - DCHECK_EQ(defs[0] + 1, defs[1]); - sregs_[defs[1]] = return_type.ToHighWord(); - } - break; - } - FALLTHROUGH_INTENDED; - case kMirOpPhi: - // These cannot be determined in this simple pass and will be processed later. - break; - - case Instruction::MOVE_RESULT: - case Instruction::MOVE_RESULT_WIDE: - case Instruction::MOVE_RESULT_OBJECT: - // Nothing to do, handled with invoke-* or filled-new-array/-range. - break; - case Instruction::MOVE_EXCEPTION: - // NOTE: We can never catch an array. - sregs_[defs[0]] = Type::NonArrayRefType().AsNonNull(); - break; - case Instruction::CONST_STRING: - case Instruction::CONST_STRING_JUMBO: - sregs_[defs[0]] = Type::NonArrayRefType().AsNonNull(); - break; - case Instruction::CONST_CLASS: - sregs_[defs[0]] = Type::NonArrayRefType().AsNonNull(); - break; - case Instruction::CHECK_CAST: - DCHECK(check_cast_data_ != nullptr); - check_cast_data_->ProcessCheckCast(mir); - break; - case Instruction::ARRAY_LENGTH: - sregs_[ModifiedSReg(uses[0])].MergeStrong(Type::UnknownArrayType()); - break; - case Instruction::NEW_INSTANCE: - sregs_[defs[0]] = Type::DexType(cu_->dex_file, mir->dalvikInsn.vB).AsNonNull(); - DCHECK(sregs_[defs[0]].Ref()); - DCHECK_EQ(sregs_[defs[0]].ArrayDepth(), 0u); - break; - case Instruction::NEW_ARRAY: - sregs_[defs[0]] = Type::DexType(cu_->dex_file, mir->dalvikInsn.vC).AsNonNull(); - DCHECK(sregs_[defs[0]].Ref()); - DCHECK_NE(sregs_[defs[0]].ArrayDepth(), 0u); - break; - case Instruction::FILLED_NEW_ARRAY: - case Instruction::FILLED_NEW_ARRAY_RANGE: { - Type array_type = Type::DexType(cu_->dex_file, mir->dalvikInsn.vB); - array_type.CheckPureRef(); // Previously checked by the method verifier. - DCHECK_NE(array_type.ArrayDepth(), 0u); - Type component_type = array_type.ComponentType(); - DCHECK(!component_type.Wide()); - MIR* move_result_mir = mir_graph_->FindMoveResult(bb, mir); - if (move_result_mir != nullptr) { - DCHECK_EQ(move_result_mir->dalvikInsn.opcode, Instruction::MOVE_RESULT_OBJECT); - sregs_[move_result_mir->ssa_rep->defs[0]] = array_type.AsNonNull(); - } - DCHECK_EQ(num_uses, mir->dalvikInsn.vA); - for (size_t next = 0u; next != num_uses; ++next) { - int32_t input_mod_s_reg = ModifiedSReg(uses[next]); - sregs_[input_mod_s_reg].MergeStrong(component_type); - } - break; - } - case Instruction::INVOKE_VIRTUAL: - case Instruction::INVOKE_SUPER: - case Instruction::INVOKE_DIRECT: - case Instruction::INVOKE_STATIC: - case Instruction::INVOKE_INTERFACE: - case Instruction::INVOKE_VIRTUAL_RANGE: - case Instruction::INVOKE_SUPER_RANGE: - case Instruction::INVOKE_DIRECT_RANGE: - case Instruction::INVOKE_STATIC_RANGE: - case Instruction::INVOKE_INTERFACE_RANGE: - case Instruction::INVOKE_VIRTUAL_QUICK: - case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { - const MethodSignature* signature = &signatures_[mir->meta.method_lowering_info]; - MIR* move_result_mir = mir_graph_->FindMoveResult(bb, mir); - if (move_result_mir != nullptr) { - Type return_type = signature->return_type; - sregs_[move_result_mir->ssa_rep->defs[0]] = return_type.AsNonNull(); - if (return_type.Wide()) { - DCHECK_EQ(move_result_mir->ssa_rep->defs[0] + 1, move_result_mir->ssa_rep->defs[1]); - sregs_[move_result_mir->ssa_rep->defs[1]] = return_type.ToHighWord(); - } - } - size_t next = 0u; - for (size_t i = 0, size = signature->num_params; i != size; ++i) { - Type param_type = signature->param_types[i]; - int32_t param_s_reg = ModifiedSReg(uses[next]); - DCHECK(!param_type.Wide() || uses[next] + 1 == uses[next + 1]); - UpdateSRegFromLowWordType(param_s_reg, param_type); - next += param_type.Wide() ? 2 : 1; - } - DCHECK_EQ(next, num_uses); - DCHECK_EQ(next, mir->dalvikInsn.vA); - break; - } - - case Instruction::RETURN_WIDE: - DCHECK(current_method_signature_.return_type.Wide()); - DCHECK_EQ(uses[0] + 1, uses[1]); - DCHECK_EQ(ModifiedSReg(uses[0]), uses[0]); - FALLTHROUGH_INTENDED; - case Instruction::RETURN: - case Instruction::RETURN_OBJECT: { - int32_t mod_s_reg = ModifiedSReg(uses[0]); - UpdateSRegFromLowWordType(mod_s_reg, current_method_signature_.return_type); - break; - } - - // NOTE: For AGET/APUT we set only the array type. The operand type is set - // below based on the data flow attributes. - case Instruction::AGET: - case Instruction::APUT: - sregs_[ModifiedSReg(uses[num_uses - 2u])].MergeStrong(Type::NarrowArrayType()); - break; - case Instruction::AGET_WIDE: - case Instruction::APUT_WIDE: - sregs_[ModifiedSReg(uses[num_uses - 2u])].MergeStrong(Type::WideArrayType()); - break; - case Instruction::AGET_OBJECT: - sregs_[defs[0]] = sregs_[defs[0]].AsNonNull(); - FALLTHROUGH_INTENDED; - case Instruction::APUT_OBJECT: - sregs_[ModifiedSReg(uses[num_uses - 2u])].MergeStrong(Type::ObjectArrayType()); - break; - case Instruction::AGET_BOOLEAN: - case Instruction::APUT_BOOLEAN: - case Instruction::AGET_BYTE: - case Instruction::APUT_BYTE: - case Instruction::AGET_CHAR: - case Instruction::APUT_CHAR: - case Instruction::AGET_SHORT: - case Instruction::APUT_SHORT: - sregs_[ModifiedSReg(uses[num_uses - 2u])].MergeStrong(Type::NarrowCoreArrayType()); - break; - - case Instruction::IGET_WIDE: - case Instruction::IGET_WIDE_QUICK: - DCHECK_EQ(defs[0] + 1, defs[1]); - DCHECK_LT(mir->meta.ifield_lowering_info, mir_graph_->GetIFieldLoweringInfoCount()); - sregs_[defs[1]] = ifields_[mir->meta.ifield_lowering_info].ToHighWord(); - FALLTHROUGH_INTENDED; - case Instruction::IGET: - case Instruction::IGET_OBJECT: - case Instruction::IGET_BOOLEAN: - case Instruction::IGET_BYTE: - case Instruction::IGET_CHAR: - case Instruction::IGET_SHORT: - case Instruction::IGET_QUICK: - case Instruction::IGET_OBJECT_QUICK: - case Instruction::IGET_BOOLEAN_QUICK: - case Instruction::IGET_BYTE_QUICK: - case Instruction::IGET_CHAR_QUICK: - case Instruction::IGET_SHORT_QUICK: - DCHECK_LT(mir->meta.ifield_lowering_info, mir_graph_->GetIFieldLoweringInfoCount()); - sregs_[defs[0]] = ifields_[mir->meta.ifield_lowering_info].AsNonNull(); - break; - case Instruction::IPUT_WIDE: - case Instruction::IPUT_WIDE_QUICK: - DCHECK_EQ(uses[0] + 1, uses[1]); - FALLTHROUGH_INTENDED; - 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: - DCHECK_LT(mir->meta.ifield_lowering_info, mir_graph_->GetIFieldLoweringInfoCount()); - UpdateSRegFromLowWordType(ModifiedSReg(uses[0]), - ifields_[mir->meta.ifield_lowering_info]); - break; - case Instruction::SGET_WIDE: - DCHECK_EQ(defs[0] + 1, defs[1]); - DCHECK_LT(mir->meta.sfield_lowering_info, mir_graph_->GetSFieldLoweringInfoCount()); - sregs_[defs[1]] = sfields_[mir->meta.sfield_lowering_info].ToHighWord(); - FALLTHROUGH_INTENDED; - case Instruction::SGET: - case Instruction::SGET_OBJECT: - case Instruction::SGET_BOOLEAN: - case Instruction::SGET_BYTE: - case Instruction::SGET_CHAR: - case Instruction::SGET_SHORT: - DCHECK_LT(mir->meta.sfield_lowering_info, mir_graph_->GetSFieldLoweringInfoCount()); - sregs_[defs[0]] = sfields_[mir->meta.sfield_lowering_info].AsNonNull(); - break; - case Instruction::SPUT_WIDE: - DCHECK_EQ(uses[0] + 1, uses[1]); - FALLTHROUGH_INTENDED; - case Instruction::SPUT: - case Instruction::SPUT_OBJECT: - case Instruction::SPUT_BOOLEAN: - case Instruction::SPUT_BYTE: - case Instruction::SPUT_CHAR: - case Instruction::SPUT_SHORT: - DCHECK_LT(mir->meta.sfield_lowering_info, mir_graph_->GetSFieldLoweringInfoCount()); - UpdateSRegFromLowWordType(ModifiedSReg(uses[0]), - sfields_[mir->meta.sfield_lowering_info]); - break; - - default: - // No invokes or reference definitions here. - DCHECK_EQ(attrs & (DF_FORMAT_35C | DF_FORMAT_3RC), 0u); - DCHECK_NE(attrs & (DF_DA | DF_REF_A), (DF_DA | DF_REF_A)); - break; - } - - if ((attrs & DF_NULL_TRANSFER_N) != 0) { - // Don't process Phis at this stage. - continue; - } - - // Handle defs - if (attrs & DF_DA) { - int32_t s_reg = defs[0]; - sregs_[s_reg].SetLowWord(); - if (attrs & DF_FP_A) { - sregs_[s_reg].SetFp(); - } - if (attrs & DF_CORE_A) { - sregs_[s_reg].SetCore(); - } - if (attrs & DF_REF_A) { - sregs_[s_reg].SetRef(); - } - if (attrs & DF_A_WIDE) { - sregs_[s_reg].SetWide(); - DCHECK_EQ(s_reg + 1, ModifiedSReg(defs[1])); - sregs_[s_reg + 1].MergeHighWord(sregs_[s_reg]); - } else { - sregs_[s_reg].SetNarrow(); - } - } - - // Handles uses - size_t next = 0; - #define PROCESS(REG) \ - if (attrs & DF_U##REG) { \ - int32_t mod_s_reg = ModifiedSReg(uses[next]); \ - sregs_[mod_s_reg].SetLowWord(); \ - if (attrs & DF_FP_##REG) { \ - sregs_[mod_s_reg].SetFp(); \ - } \ - if (attrs & DF_CORE_##REG) { \ - sregs_[mod_s_reg].SetCore(); \ - } \ - if (attrs & DF_REF_##REG) { \ - sregs_[mod_s_reg].SetRef(); \ - } \ - if (attrs & DF_##REG##_WIDE) { \ - sregs_[mod_s_reg].SetWide(); \ - DCHECK_EQ(mod_s_reg + 1, ModifiedSReg(uses[next + 1])); \ - sregs_[mod_s_reg + 1].SetWide(); \ - sregs_[mod_s_reg + 1].MergeHighWord(sregs_[mod_s_reg]); \ - next += 2; \ - } else { \ - sregs_[mod_s_reg].SetNarrow(); \ - next++; \ - } \ - } - PROCESS(A) - PROCESS(B) - PROCESS(C) - #undef PROCESS - DCHECK(next == mir->ssa_rep->num_uses || (attrs & (DF_FORMAT_35C | DF_FORMAT_3RC)) != 0); - } - // Record relevant attributes. - bb_df_attrs_[bb->id] = bb_df_attrs & - (DF_NULL_TRANSFER_N | DF_CHK_CAST | DF_IS_MOVE | DF_HAS_RANGE_CHKS | DF_SAME_TYPE_AB); - } - - if (UNLIKELY(check_cast_data_ != nullptr)) { - check_cast_data_->MarkPseudoPhiBlocks(bb_df_attrs_); - } -} - -int32_t TypeInference::ModifiedSReg(int32_t s_reg) { - if (UNLIKELY(check_cast_data_ != nullptr)) { - SplitSRegData* split_data = check_cast_data_->GetSplitSRegData(s_reg); - if (UNLIKELY(split_data != nullptr)) { - DCHECK_NE(split_data->current_mod_s_reg, INVALID_SREG); - return split_data->current_mod_s_reg; - } - } - return s_reg; -} - -int32_t TypeInference::PhiInputModifiedSReg(int32_t s_reg, BasicBlock* bb, size_t pred_idx) { - DCHECK_LT(pred_idx, bb->predecessors.size()); - if (UNLIKELY(check_cast_data_ != nullptr)) { - SplitSRegData* split_data = check_cast_data_->GetSplitSRegData(s_reg); - if (UNLIKELY(split_data != nullptr)) { - return split_data->ending_mod_s_reg[bb->predecessors[pred_idx]]; - } - } - return s_reg; -} - -bool TypeInference::UpdateSRegFromLowWordType(int32_t mod_s_reg, Type low_word_type) { - DCHECK(low_word_type.LowWord()); - bool changed = sregs_[mod_s_reg].MergeStrong(low_word_type); - if (!sregs_[mod_s_reg].Narrow()) { // Wide without conflict with narrow. - DCHECK(!low_word_type.Narrow()); - DCHECK_LT(mod_s_reg, mir_graph_->GetNumSSARegs()); // Original SSA reg. - changed |= sregs_[mod_s_reg + 1].MergeHighWord(sregs_[mod_s_reg]); - } - return changed; -} - -} // namespace art diff --git a/compiler/dex/type_inference.h b/compiler/dex/type_inference.h deleted file mode 100644 index c9b29bf..0000000 --- a/compiler/dex/type_inference.h +++ /dev/null @@ -1,443 +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_TYPE_INFERENCE_H_ -#define ART_COMPILER_DEX_TYPE_INFERENCE_H_ - -#include "base/logging.h" -#include "base/arena_object.h" -#include "base/scoped_arena_containers.h" - -namespace art { - -class ArenaBitVector; -class BasicBlock; -struct CompilationUnit; -class DexFile; -class MirFieldInfo; -class MirMethodInfo; -class MIR; -class MIRGraph; - -/** - * @brief Determine the type of SSA registers. - * - * @details - * Because Dalvik's bytecode is not fully typed, we have to do some work to figure - * out the sreg type. For some operations it is clear based on the opcode (i.e. - * ADD_FLOAT v0, v1, v2), but for others (MOVE), we may never know the "real" type. - * - * We perform the type inference operation in two phases: - * 1. First, we make one pass over all insns in the topological sort order and - * extract known type information from all insns for their defs and uses. - * 2. Then we repeatedly go through the graph to process insns that can propagate - * types from inputs to outputs and vice versa. These insns are just the MOVEs, - * AGET/APUTs, IF_ccs and Phis (including pseudo-Phis, see below). - * - * Since the main purpose is to determine the basic FP/core/reference type, we don't - * need to record the precise reference type, we only record the array type to determine - * the result types of agets and source type of aputs. - * - * One complication is the check-cast instruction that effectively defines a new - * virtual register that has a different type than the original sreg. We need to - * track these virtual sregs and insert pseudo-phis where they merge. - * - * Another problems is with null references. The same zero constant can be used - * as differently typed null and moved around with move-object which would normally - * be an ill-formed assignment. So we need to keep track of values that can be null - * and values that cannot. - * - * Note that it's possible to have the same sreg show multiple defined types because dx - * treats constants as untyped bit patterns. We disable register promotion in that case. - */ -class TypeInference : public DeletableArenaObject<kArenaAllocMisc> { - public: - TypeInference(MIRGraph* mir_graph, ScopedArenaAllocator* alloc); - - bool Apply(BasicBlock* bb); - void Finish(); - - private: - struct Type { - static Type Unknown() { - return Type(0u); - } - - static Type NonArrayRefType() { - return Type(kFlagLowWord | kFlagNarrow | kFlagRef); - } - - static Type ObjectArrayType() { - return Type(kFlagNarrow | kFlagRef | kFlagLowWord | - (1u << kBitArrayDepthStart) | kFlagArrayNarrow | kFlagArrayRef); - } - - static Type WideArrayType() { - // Core or FP unknown. - return Type(kFlagNarrow | kFlagRef | kFlagLowWord | - (1u << kBitArrayDepthStart) | kFlagArrayWide); - } - - static Type NarrowArrayType() { - // Core or FP unknown. - return Type(kFlagNarrow | kFlagRef | kFlagLowWord | - (1u << kBitArrayDepthStart) | kFlagArrayNarrow); - } - - static Type NarrowCoreArrayType() { - return Type(kFlagNarrow | kFlagRef | kFlagLowWord | - (1u << kBitArrayDepthStart) | kFlagArrayNarrow | kFlagArrayCore); - } - - static Type UnknownArrayType() { - return Type(kFlagNarrow | kFlagRef | kFlagLowWord | (1u << kBitArrayDepthStart)); - } - - static Type ArrayType(uint32_t array_depth, Type nested_type); - static Type ArrayTypeFromComponent(Type component_type); - static Type ShortyType(char shorty); - static Type DexType(const DexFile* dex_file, uint32_t type_idx); - - bool IsDefined() { - return raw_bits_ != 0u; - } - - bool SizeConflict() const { - // NOTE: Ignore array element conflicts that don't propagate to direct conflicts. - return (Wide() && Narrow()) || (HighWord() && LowWord()); - } - - bool TypeConflict() const { - // NOTE: Ignore array element conflicts that don't propagate to direct conflicts. - return (raw_bits_ & kMaskType) != 0u && !IsPowerOfTwo(raw_bits_ & kMaskType); // 2+ bits. - } - - void MarkSizeConflict() { - SetBits(kFlagLowWord | kFlagHighWord); - } - - void MarkTypeConflict() { - // Mark all three type bits so that merging any other type bits will not change this type. - SetBits(kFlagFp | kFlagCore | kFlagRef); - } - - void CheckPureRef() const { - DCHECK_EQ(raw_bits_ & (kMaskWideAndType | kMaskWord), kFlagNarrow | kFlagRef | kFlagLowWord); - } - - // If reference, don't treat as possible null and require precise type. - // - // References without this flag are allowed to have a type conflict and their - // type will not be propagated down. However, for simplicity we allow propagation - // of other flags up as it will affect only other null references; should those - // references be marked non-null later, we would have to do it anyway. - // NOTE: This is a negative "non-null" flag rather then a positive "is-null" - // to simplify merging together with other non-array flags. - bool NonNull() const { - return IsBitSet(kFlagNonNull); - } - - bool Wide() const { - return IsBitSet(kFlagWide); - } - - bool Narrow() const { - return IsBitSet(kFlagNarrow); - } - - bool Fp() const { - return IsBitSet(kFlagFp); - } - - bool Core() const { - return IsBitSet(kFlagCore); - } - - bool Ref() const { - return IsBitSet(kFlagRef); - } - - bool LowWord() const { - return IsBitSet(kFlagLowWord); - } - - bool HighWord() const { - return IsBitSet(kFlagHighWord); - } - - uint32_t ArrayDepth() const { - return raw_bits_ >> kBitArrayDepthStart; - } - - Type NestedType() const { - DCHECK_NE(ArrayDepth(), 0u); - return Type(kFlagLowWord | ((raw_bits_ & kMaskArrayWideAndType) >> kArrayTypeShift)); - } - - Type ComponentType() const { - DCHECK_NE(ArrayDepth(), 0u); - Type temp(raw_bits_ - (1u << kBitArrayDepthStart)); // array_depth - 1u; - return (temp.ArrayDepth() != 0u) ? temp.AsNull() : NestedType(); - } - - void SetWide() { - SetBits(kFlagWide); - } - - void SetNarrow() { - SetBits(kFlagNarrow); - } - - void SetFp() { - SetBits(kFlagFp); - } - - void SetCore() { - SetBits(kFlagCore); - } - - void SetRef() { - SetBits(kFlagRef); - } - - void SetLowWord() { - SetBits(kFlagLowWord); - } - - void SetHighWord() { - SetBits(kFlagHighWord); - } - - Type ToHighWord() const { - DCHECK_EQ(raw_bits_ & (kMaskWide | kMaskWord), kFlagWide | kFlagLowWord); - return Type(raw_bits_ ^ (kFlagLowWord | kFlagHighWord)); - } - - bool MergeHighWord(Type low_word_type) { - // NOTE: low_word_type may be also Narrow() or HighWord(). - DCHECK(low_word_type.Wide() && low_word_type.LowWord()); - return MergeBits(Type(low_word_type.raw_bits_ | kFlagHighWord), - kMaskWideAndType | kFlagHighWord); - } - - bool Copy(Type type) { - if (raw_bits_ != type.raw_bits_) { - raw_bits_ = type.raw_bits_; - return true; - } - return false; - } - - // Merge non-array flags. - bool MergeNonArrayFlags(Type src_type) { - return MergeBits(src_type, kMaskNonArray); - } - - // Merge array flags for conflict. - bool MergeArrayConflict(Type src_type); - - // Merge all flags. - bool MergeStrong(Type src_type); - - // Merge all flags. - bool MergeWeak(Type src_type); - - // Get the same type but mark that it should not be treated as null. - Type AsNonNull() const { - return Type(raw_bits_ | kFlagNonNull); - } - - // Get the same type but mark that it can be treated as null. - Type AsNull() const { - return Type(raw_bits_ & ~kFlagNonNull); - } - - private: - enum FlagBits { - kBitNonNull = 0, - kBitWide, - kBitNarrow, - kBitFp, - kBitCore, - kBitRef, - kBitLowWord, - kBitHighWord, - kBitArrayWide, - kBitArrayNarrow, - kBitArrayFp, - kBitArrayCore, - kBitArrayRef, - kBitArrayDepthStart, - }; - static constexpr size_t kArrayDepthBits = sizeof(uint32_t) * 8u - kBitArrayDepthStart; - - static constexpr uint32_t kFlagNonNull = 1u << kBitNonNull; - static constexpr uint32_t kFlagWide = 1u << kBitWide; - static constexpr uint32_t kFlagNarrow = 1u << kBitNarrow; - static constexpr uint32_t kFlagFp = 1u << kBitFp; - static constexpr uint32_t kFlagCore = 1u << kBitCore; - static constexpr uint32_t kFlagRef = 1u << kBitRef; - static constexpr uint32_t kFlagLowWord = 1u << kBitLowWord; - static constexpr uint32_t kFlagHighWord = 1u << kBitHighWord; - static constexpr uint32_t kFlagArrayWide = 1u << kBitArrayWide; - static constexpr uint32_t kFlagArrayNarrow = 1u << kBitArrayNarrow; - static constexpr uint32_t kFlagArrayFp = 1u << kBitArrayFp; - static constexpr uint32_t kFlagArrayCore = 1u << kBitArrayCore; - static constexpr uint32_t kFlagArrayRef = 1u << kBitArrayRef; - - static constexpr uint32_t kMaskWide = kFlagWide | kFlagNarrow; - static constexpr uint32_t kMaskType = kFlagFp | kFlagCore | kFlagRef; - static constexpr uint32_t kMaskWord = kFlagLowWord | kFlagHighWord; - static constexpr uint32_t kMaskArrayWide = kFlagArrayWide | kFlagArrayNarrow; - static constexpr uint32_t kMaskArrayType = kFlagArrayFp | kFlagArrayCore | kFlagArrayRef; - static constexpr uint32_t kMaskWideAndType = kMaskWide | kMaskType; - static constexpr uint32_t kMaskArrayWideAndType = kMaskArrayWide | kMaskArrayType; - - static constexpr size_t kArrayTypeShift = kBitArrayWide - kBitWide; - static_assert(kArrayTypeShift == kBitArrayNarrow - kBitNarrow, "shift mismatch"); - static_assert(kArrayTypeShift == kBitArrayFp - kBitFp, "shift mismatch"); - static_assert(kArrayTypeShift == kBitArrayCore - kBitCore, "shift mismatch"); - static_assert(kArrayTypeShift == kBitArrayRef - kBitRef, "shift mismatch"); - static_assert((kMaskWide << kArrayTypeShift) == kMaskArrayWide, "shift mismatch"); - static_assert((kMaskType << kArrayTypeShift) == kMaskArrayType, "shift mismatch"); - static_assert((kMaskWideAndType << kArrayTypeShift) == kMaskArrayWideAndType, "shift mismatch"); - - static constexpr uint32_t kMaskArrayDepth = static_cast<uint32_t>(-1) << kBitArrayDepthStart; - static constexpr uint32_t kMaskNonArray = ~(kMaskArrayWideAndType | kMaskArrayDepth); - - // The maximum representable array depth. If we exceed the maximum (which can happen - // only with an absurd nested array type in a dex file which would presumably cause - // OOM while being resolved), we can report false conflicts. - static constexpr uint32_t kMaxArrayDepth = static_cast<uint32_t>(-1) >> kBitArrayDepthStart; - - explicit Type(uint32_t raw_bits) : raw_bits_(raw_bits) { } - - bool IsBitSet(uint32_t flag) const { - return (raw_bits_ & flag) != 0u; - } - - void SetBits(uint32_t flags) { - raw_bits_ |= flags; - } - - bool MergeBits(Type src_type, uint32_t mask) { - uint32_t new_bits = raw_bits_ | (src_type.raw_bits_ & mask); - if (new_bits != raw_bits_) { - raw_bits_ = new_bits; - return true; - } - return false; - } - - uint32_t raw_bits_; - }; - - struct MethodSignature { - Type return_type; - size_t num_params; - Type* param_types; - }; - - struct SplitSRegData { - int32_t current_mod_s_reg; - int32_t* starting_mod_s_reg; // Indexed by BasicBlock::id. - int32_t* ending_mod_s_reg; // Indexed by BasicBlock::id. - - // NOTE: Before AddPseudoPhis(), def_phi_blocks_ marks the blocks - // with check-casts and the block with the original SSA reg. - // After AddPseudoPhis(), it marks blocks with pseudo-phis. - ArenaBitVector* def_phi_blocks_; // Indexed by BasicBlock::id. - }; - - class CheckCastData : public DeletableArenaObject<kArenaAllocMisc> { - public: - CheckCastData(MIRGraph* mir_graph, ScopedArenaAllocator* alloc); - - size_t NumSRegs() const { - return num_sregs_; - } - - void AddCheckCast(MIR* check_cast, Type type); - void AddPseudoPhis(); - void InitializeCheckCastSRegs(Type* sregs) const; - void MergeCheckCastConflicts(Type* sregs) const; - void MarkPseudoPhiBlocks(uint64_t* bb_df_attrs) const; - - void Start(BasicBlock* bb); - bool ProcessPseudoPhis(BasicBlock* bb, Type* sregs); - void ProcessCheckCast(MIR* mir); - - SplitSRegData* GetSplitSRegData(int32_t s_reg); - - private: - BasicBlock* FindDefBlock(MIR* check_cast); - BasicBlock* FindTopologicallyEarliestPredecessor(BasicBlock* bb); - bool IsSRegLiveAtStart(BasicBlock* bb, int v_reg, int32_t s_reg); - - MIRGraph* const mir_graph_; - ScopedArenaAllocator* const alloc_; - const size_t num_blocks_; - size_t num_sregs_; - - // Map check-cast mir to special sreg and type. - struct CheckCastMapValue { - int32_t modified_s_reg; - Type type; - }; - ScopedArenaSafeMap<MIR*, CheckCastMapValue> check_cast_map_; - ScopedArenaSafeMap<int32_t, SplitSRegData> split_sreg_data_; - }; - - static Type FieldType(const DexFile* dex_file, uint32_t field_idx); - static Type* PrepareIFieldTypes(const DexFile* dex_file, MIRGraph* mir_graph, - ScopedArenaAllocator* alloc); - static Type* PrepareSFieldTypes(const DexFile* dex_file, MIRGraph* mir_graph, - ScopedArenaAllocator* alloc); - static MethodSignature Signature(const DexFile* dex_file, uint32_t method_idx, bool is_static, - ScopedArenaAllocator* alloc); - static MethodSignature* PrepareSignatures(const DexFile* dex_file, MIRGraph* mir_graph, - ScopedArenaAllocator* alloc); - static CheckCastData* InitializeCheckCastData(MIRGraph* mir_graph, ScopedArenaAllocator* alloc); - - void InitializeSRegs(); - - int32_t ModifiedSReg(int32_t s_reg); - int32_t PhiInputModifiedSReg(int32_t s_reg, BasicBlock* bb, size_t pred_idx); - - bool UpdateSRegFromLowWordType(int32_t mod_s_reg, Type low_word_type); - - MIRGraph* const mir_graph_; - CompilationUnit* const cu_; - - // The type inference propagates types also backwards but this must not happen across - // check-cast. So we need to effectively split an SSA reg into two at check-cast and - // keep track of the types separately. - std::unique_ptr<CheckCastData> check_cast_data_; - - size_t num_sregs_; // Number of SSA regs or modified SSA regs, see check-cast. - const Type* const ifields_; // Indexed by MIR::meta::ifield_lowering_info. - const Type* const sfields_; // Indexed by MIR::meta::sfield_lowering_info. - const MethodSignature* const signatures_; // Indexed by MIR::meta::method_lowering_info. - const MethodSignature current_method_signature_; - Type* const sregs_; // Indexed by SSA reg or modified SSA reg, see check-cast. - uint64_t* const bb_df_attrs_; // Indexed by BasicBlock::id. - - friend class TypeInferenceTest; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_TYPE_INFERENCE_H_ diff --git a/compiler/dex/type_inference_test.cc b/compiler/dex/type_inference_test.cc deleted file mode 100644 index 54b5747..0000000 --- a/compiler/dex/type_inference_test.cc +++ /dev/null @@ -1,2042 +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 "base/logging.h" -#include "compiler_ir.h" -#include "dataflow_iterator-inl.h" -#include "dex_flags.h" -#include "dex/mir_field_info.h" -#include "dex/mir_graph.h" -#include "driver/dex_compilation_unit.h" -#include "gtest/gtest.h" -#include "type_inference.h" -#include "utils/test_dex_file_builder.h" - -namespace art { - -class TypeInferenceTest : public testing::Test { - protected: - struct TypeDef { - const char* descriptor; - }; - - struct FieldDef { - const char* class_descriptor; - const char* type; - const char* name; - }; - - struct MethodDef { - const char* class_descriptor; - const char* signature; - const char* name; - InvokeType 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 metadata; - 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_INVOKE0(bb, opcode, method_idx) \ - { bb, opcode, 0u, method_idx, 0, { }, 0, { } } -#define DEF_INVOKE1(bb, opcode, reg, method_idx) \ - { bb, opcode, 0u, method_idx, 1, { reg }, 0, { } } -#define DEF_INVOKE2(bb, opcode, reg1, reg2, method_idx) \ - { bb, opcode, 0u, method_idx, 2, { reg1, reg2 }, 0, { } } -#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<Instruction::Code>(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) -#define DEF_NULOP(bb, opcode, result) DEF_CONST(bb, opcode, result, 0) -#define DEF_NULOP_WIDE(bb, opcode, result) DEF_CONST_WIDE(bb, opcode, result, 0) -#define DEF_CHECK_CAST(bb, opcode, reg, type) \ - { bb, opcode, 0, type, 1, { reg }, 0, { } } -#define DEF_NEW_ARRAY(bb, opcode, reg, length, type) \ - { bb, opcode, 0, type, 1, { length }, 1, { reg } } - - void AddTypes(const TypeDef* defs, size_t count) { - for (size_t i = 0; i != count; ++i) { - const TypeDef* def = &defs[i]; - dex_file_builder_.AddType(def->descriptor); - } - } - - template <size_t count> - void PrepareTypes(const TypeDef (&defs)[count]) { - type_defs_ = defs; - type_count_ = count; - AddTypes(defs, count); - } - - void AddFields(const FieldDef* defs, size_t count) { - for (size_t i = 0; i != count; ++i) { - const FieldDef* def = &defs[i]; - dex_file_builder_.AddField(def->class_descriptor, def->type, def->name); - } - } - - template <size_t count> - void PrepareIFields(const FieldDef (&defs)[count]) { - ifield_defs_ = defs; - ifield_count_ = count; - AddFields(defs, count); - } - - template <size_t count> - void PrepareSFields(const FieldDef (&defs)[count]) { - sfield_defs_ = defs; - sfield_count_ = count; - AddFields(defs, count); - } - - void AddMethods(const MethodDef* defs, size_t count) { - for (size_t i = 0; i != count; ++i) { - const MethodDef* def = &defs[i]; - dex_file_builder_.AddMethod(def->class_descriptor, def->signature, def->name); - } - } - - template <size_t count> - void PrepareMethods(const MethodDef (&defs)[count]) { - method_defs_ = defs; - method_count_ = count; - AddMethods(defs, count); - } - - DexMemAccessType AccessTypeForDescriptor(const char* descriptor) { - switch (descriptor[0]) { - case 'I': - case 'F': - return kDexMemAccessWord; - case 'J': - case 'D': - return kDexMemAccessWide; - case '[': - case 'L': - return kDexMemAccessObject; - case 'Z': - return kDexMemAccessBoolean; - case 'B': - return kDexMemAccessByte; - case 'C': - return kDexMemAccessChar; - case 'S': - return kDexMemAccessShort; - default: - LOG(FATAL) << "Bad descriptor: " << descriptor; - UNREACHABLE(); - } - } - - size_t CountIns(const std::string& test_method_signature, bool is_static) { - const char* sig = test_method_signature.c_str(); - CHECK_EQ(sig[0], '('); - ++sig; - size_t result = is_static ? 0u : 1u; - while (*sig != ')') { - result += (AccessTypeForDescriptor(sig) == kDexMemAccessWide) ? 2u : 1u; - while (*sig == '[') { - ++sig; - } - if (*sig == 'L') { - do { - ++sig; - CHECK(*sig != '\0' && *sig != ')'); - } while (*sig != ';'); - } - ++sig; - } - return result; - } - - void BuildDexFile(const std::string& test_method_signature, bool is_static) { - dex_file_builder_.AddMethod(kClassName, test_method_signature, kMethodName); - dex_file_ = dex_file_builder_.Build(kDexLocation); - cu_.dex_file = dex_file_.get(); - cu_.method_idx = dex_file_builder_.GetMethodIdx(kClassName, test_method_signature, kMethodName); - cu_.access_flags = is_static ? kAccStatic : 0u; - cu_.mir_graph->m_units_.push_back(new (cu_.mir_graph->arena_) DexCompilationUnit( - &cu_, cu_.class_loader, cu_.class_linker, *cu_.dex_file, nullptr /* code_item not used */, - 0u /* class_def_idx not used */, 0u /* method_index not used */, - cu_.access_flags, nullptr /* verified_method not used */)); - cu_.mir_graph->current_method_ = 0u; - code_item_ = static_cast<DexFile::CodeItem*>( - cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc)); - - code_item_->ins_size_ = CountIns(test_method_signature, is_static); - code_item_->registers_size_ = kLocalVRs + code_item_->ins_size_; - cu_.mir_graph->current_code_item_ = code_item_; - cu_.mir_graph->num_ssa_regs_ = kMaxSsaRegs; - - cu_.mir_graph->ifield_lowering_infos_.clear(); - cu_.mir_graph->ifield_lowering_infos_.reserve(ifield_count_); - for (size_t i = 0u; i != ifield_count_; ++i) { - const FieldDef* def = &ifield_defs_[i]; - uint32_t field_idx = - dex_file_builder_.GetFieldIdx(def->class_descriptor, def->type, def->name); - MirIFieldLoweringInfo field_info(field_idx, AccessTypeForDescriptor(def->type), false); - field_info.declaring_dex_file_ = cu_.dex_file; - field_info.declaring_field_idx_ = field_idx; - cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); - } - - cu_.mir_graph->sfield_lowering_infos_.clear(); - cu_.mir_graph->sfield_lowering_infos_.reserve(sfield_count_); - for (size_t i = 0u; i != sfield_count_; ++i) { - const FieldDef* def = &sfield_defs_[i]; - uint32_t field_idx = - dex_file_builder_.GetFieldIdx(def->class_descriptor, def->type, def->name); - MirSFieldLoweringInfo field_info(field_idx, AccessTypeForDescriptor(def->type)); - field_info.declaring_dex_file_ = cu_.dex_file; - field_info.declaring_field_idx_ = field_idx; - cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); - } - - cu_.mir_graph->method_lowering_infos_.clear(); - cu_.mir_graph->method_lowering_infos_.reserve(ifield_count_); - for (size_t i = 0u; i != method_count_; ++i) { - const MethodDef* def = &method_defs_[i]; - uint32_t method_idx = - dex_file_builder_.GetMethodIdx(def->class_descriptor, def->signature, def->name); - MirMethodLoweringInfo method_info(method_idx, def->type, false); - method_info.declaring_dex_file_ = cu_.dex_file; - method_info.declaring_method_idx_ = method_idx; - cu_.mir_graph->method_lowering_infos_.push_back(method_info); - } - } - - 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<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessor)); - 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<BasicBlockDataFlow*>( - 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 <size_t count> - 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 DoPrepareMIRs(const MIRDef* defs, size_t count) { - mir_count_ = count; - mirs_ = cu_.arena.AllocArray<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<int32_t>(def->value); - mir->dalvikInsn.vB_wide = def->value; - if (IsInstructionIGetOrIPut(def->opcode)) { - ASSERT_LT(def->metadata, cu_.mir_graph->ifield_lowering_infos_.size()); - mir->meta.ifield_lowering_info = def->metadata; - ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->metadata].MemAccessType(), - IGetOrIPutMemAccessType(def->opcode)); - cu_.mir_graph->merged_df_flags_ |= DF_IFIELD; - } else if (IsInstructionSGetOrSPut(def->opcode)) { - ASSERT_LT(def->metadata, cu_.mir_graph->sfield_lowering_infos_.size()); - mir->meta.sfield_lowering_info = def->metadata; - ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->metadata].MemAccessType(), - SGetOrSPutMemAccessType(def->opcode)); - cu_.mir_graph->merged_df_flags_ |= DF_SFIELD; - } else if (IsInstructionInvoke(def->opcode)) { - ASSERT_LT(def->metadata, cu_.mir_graph->method_lowering_infos_.size()); - mir->meta.method_lowering_info = def->metadata; - mir->dalvikInsn.vA = def->num_uses; - cu_.mir_graph->merged_df_flags_ |= DF_FORMAT_35C; - } else if (def->opcode == static_cast<Instruction::Code>(kMirOpPhi)) { - mir->meta.phi_incoming = - allocator_->AllocArray<BasicBlockId>(def->num_uses, kArenaAllocDFInfo); - ASSERT_EQ(def->num_uses, bb->predecessors.size()); - std::copy(bb->predecessors.begin(), bb->predecessors.end(), mir->meta.phi_incoming); - } else if (def->opcode == Instruction::CHECK_CAST) { - ASSERT_LT(def->metadata, type_count_); - mir->dalvikInsn.vB = dex_file_builder_.GetTypeIdx(type_defs_[def->metadata].descriptor); - cu_.mir_graph->merged_df_flags_ |= DF_CHK_CAST; - } else if (def->opcode == Instruction::NEW_ARRAY) { - ASSERT_LT(def->metadata, type_count_); - mir->dalvikInsn.vC = dex_file_builder_.GetTypeIdx(type_defs_[def->metadata].descriptor); - } - mir->ssa_rep = &ssa_reps_[i]; - mir->ssa_rep->num_uses = def->num_uses; - mir->ssa_rep->uses = const_cast<int32_t*>(def->uses); // Not modified by LVN. - mir->ssa_rep->num_defs = def->num_defs; - mir->ssa_rep->defs = const_cast<int32_t*>(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; - } - code_item_->insns_size_in_code_units_ = 2u * count; - } - - template <size_t count> - void PrepareMIRs(const MIRDef (&defs)[count]) { - DoPrepareMIRs(defs, count); - } - - // BasicBlockDataFlow::vreg_to_ssa_map_exit is used only for check-casts. - void AllocEndingVRegToSRegMaps() { - AllNodesIterator iterator(cu_.mir_graph.get()); - for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { - if (bb->data_flow_info != nullptr) { - if (bb->data_flow_info->vreg_to_ssa_map_exit == nullptr) { - size_t num_vregs = code_item_->registers_size_; - bb->data_flow_info->vreg_to_ssa_map_exit = static_cast<int32_t*>( - cu_.arena.AllocArray<int32_t>(num_vregs, kArenaAllocDFInfo)); - std::fill_n(bb->data_flow_info->vreg_to_ssa_map_exit, num_vregs, INVALID_SREG); - } - } - } - } - - template <size_t count> - void MapVRegToSReg(int vreg, int32_t sreg, const BasicBlockId (&bb_ids)[count]) { - AllocEndingVRegToSRegMaps(); - for (BasicBlockId bb_id : bb_ids) { - BasicBlock* bb = cu_.mir_graph->GetBasicBlock(bb_id); - CHECK(bb != nullptr); - CHECK(bb->data_flow_info != nullptr); - CHECK(bb->data_flow_info->vreg_to_ssa_map_exit != nullptr); - bb->data_flow_info->vreg_to_ssa_map_exit[vreg] = sreg; - } - } - - void PerformTypeInference() { - cu_.mir_graph->SSATransformationStart(); - cu_.mir_graph->ComputeDFSOrders(); - cu_.mir_graph->ComputeDominators(); - cu_.mir_graph->ComputeTopologicalSortOrder(); - cu_.mir_graph->SSATransformationEnd(); - ASSERT_TRUE(type_inference_ == nullptr); - type_inference_.reset(new (allocator_.get()) TypeInference(cu_.mir_graph.get(), - allocator_.get())); - RepeatingPreOrderDfsIterator iter(cu_.mir_graph.get()); - bool changed = false; - for (BasicBlock* bb = iter.Next(changed); bb != nullptr; bb = iter.Next(changed)) { - changed = type_inference_->Apply(bb); - } - type_inference_->Finish(); - } - - TypeInferenceTest() - : pool_(), - cu_(&pool_, kRuntimeISA, nullptr, nullptr), - mir_count_(0u), - mirs_(nullptr), - code_item_(nullptr), - ssa_reps_(), - allocator_(), - live_in_v_(new (&cu_.arena) ArenaBitVector(&cu_.arena, kMaxSsaRegs, false, kBitMapMisc)), - type_defs_(nullptr), - type_count_(0u), - ifield_defs_(nullptr), - ifield_count_(0u), - sfield_defs_(nullptr), - sfield_count_(0u), - method_defs_(nullptr), - method_count_(0u), - dex_file_builder_(), - dex_file_(nullptr) { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); - allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); - // Bind all possible sregs to live vregs for test purposes. - live_in_v_->SetInitialBits(kMaxSsaRegs); - cu_.mir_graph->reg_location_ = static_cast<RegLocation*>(cu_.arena.Alloc( - kMaxSsaRegs * sizeof(cu_.mir_graph->reg_location_[0]), kArenaAllocRegAlloc)); - // 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); - } - } - - enum ExpectFlags : uint32_t { - kExpectWide = 0x0001u, - kExpectNarrow = 0x0002u, - kExpectFp = 0x0004u, - kExpectCore = 0x0008u, - kExpectRef = 0x0010u, - kExpectArrayWide = 0x0020u, - kExpectArrayNarrow = 0x0040u, - kExpectArrayFp = 0x0080u, - kExpectArrayCore = 0x0100u, - kExpectArrayRef = 0x0200u, - kExpectNull = 0x0400u, - kExpectHigh = 0x0800u, // Reserved for ExpectSRegType(). - }; - - struct SRegExpectation { - uint32_t array_depth; - uint32_t flags; - }; - - void ExpectSRegType(int s_reg, const SRegExpectation& expectation, bool check_loc = true) { - uint32_t flags = expectation.flags; - uint32_t array_depth = expectation.array_depth; - TypeInference::Type type = type_inference_->sregs_[s_reg]; - - if (check_loc) { - RegLocation loc = cu_.mir_graph->reg_location_[s_reg]; - EXPECT_EQ((flags & kExpectWide) != 0u, loc.wide) << s_reg; - EXPECT_EQ((flags & kExpectFp) != 0u, loc.fp) << s_reg; - EXPECT_EQ((flags & kExpectCore) != 0u, loc.core) << s_reg; - EXPECT_EQ((flags & kExpectRef) != 0u, loc.ref) << s_reg; - EXPECT_EQ((flags & kExpectHigh) != 0u, loc.high_word) << s_reg; - } - - EXPECT_EQ((flags & kExpectWide) != 0u, type.Wide()) << s_reg; - EXPECT_EQ((flags & kExpectNarrow) != 0u, type.Narrow()) << s_reg; - EXPECT_EQ((flags & kExpectFp) != 0u, type.Fp()) << s_reg; - EXPECT_EQ((flags & kExpectCore) != 0u, type.Core()) << s_reg; - EXPECT_EQ((flags & kExpectRef) != 0u, type.Ref()) << s_reg; - EXPECT_EQ((flags & kExpectHigh) == 0u, type.LowWord()) << s_reg; - EXPECT_EQ((flags & kExpectHigh) != 0u, type.HighWord()) << s_reg; - - if ((flags & kExpectRef) != 0u) { - EXPECT_EQ((flags & kExpectNull) != 0u, !type.NonNull()) << s_reg; - } else { - // Null should be checked only for references. - ASSERT_EQ((flags & kExpectNull), 0u); - } - - ASSERT_EQ(array_depth, type.ArrayDepth()) << s_reg; - if (array_depth != 0u) { - ASSERT_NE((flags & kExpectRef), 0u); - TypeInference::Type nested_type = type.NestedType(); - EXPECT_EQ((flags & kExpectArrayWide) != 0u, nested_type.Wide()) << s_reg; - EXPECT_EQ((flags & kExpectArrayNarrow) != 0u, nested_type.Narrow()) << s_reg; - EXPECT_EQ((flags & kExpectArrayFp) != 0u, nested_type.Fp()) << s_reg; - EXPECT_EQ((flags & kExpectArrayCore) != 0u, nested_type.Core()) << s_reg; - EXPECT_EQ((flags & kExpectArrayRef) != 0u, nested_type.Ref()) << s_reg; - } - if (!type.Narrow() && type.LowWord() && - (expectation.flags & (kExpectWide | kExpectNarrow | kExpectHigh)) == kExpectWide) { - SRegExpectation high_expectation = { array_depth, flags | kExpectHigh }; - ExpectSRegType(s_reg + 1, high_expectation); - } - } - - void ExpectCore(int s_reg, bool core) { - EXPECT_EQ(core, type_inference_->sregs_[s_reg].Core()); - } - - void ExpectRef(int s_reg, bool ref) { - EXPECT_EQ(ref, type_inference_->sregs_[s_reg].Ref()); - } - - void ExpectArrayDepth(int s_reg, uint32_t array_depth) { - EXPECT_EQ(array_depth, type_inference_->sregs_[s_reg].ArrayDepth()); - } - - static constexpr size_t kMaxSsaRegs = 16384u; - static constexpr uint16_t kLocalVRs = 1000u; - - static constexpr const char* kDexLocation = "TypeInferenceDexFile;"; - static constexpr const char* kClassName = "LTypeInferenceTest;"; - static constexpr const char* kMethodName = "test"; - - ArenaPool pool_; - CompilationUnit cu_; - size_t mir_count_; - MIR* mirs_; - DexFile::CodeItem* code_item_; - std::vector<SSARepresentation> ssa_reps_; - std::unique_ptr<ScopedArenaAllocator> allocator_; - std::unique_ptr<TypeInference> type_inference_; - ArenaBitVector* live_in_v_; - - const TypeDef* type_defs_; - size_t type_count_; - const FieldDef* ifield_defs_; - size_t ifield_count_; - const FieldDef* sfield_defs_; - size_t sfield_count_; - const MethodDef* method_defs_; - size_t method_count_; - - TestDexFileBuilder dex_file_builder_; - std::unique_ptr<const DexFile> dex_file_; -}; - -TEST_F(TypeInferenceTest, IGet) { - static const FieldDef ifields[] = { - { kClassName, "B", "byteField" }, - { kClassName, "C", "charField" }, - { kClassName, "D", "doubleField" }, - { kClassName, "F", "floatField" }, - { kClassName, "I", "intField" }, - { kClassName, "J", "longField" }, - { kClassName, "S", "shortField" }, - { kClassName, "Z", "booleanField" }, - { kClassName, "Ljava/lang/Object;", "objectField" }, - { kClassName, "[Ljava/lang/Object;", "objectArrayField" }, - }; - constexpr uint32_t thiz = kLocalVRs; - static const MIRDef mirs[] = { - DEF_IGET(3u, Instruction::IGET_BYTE, 0u, thiz, 0u), - DEF_IGET(3u, Instruction::IGET_CHAR, 1u, thiz, 1u), - DEF_IGET_WIDE(3u, Instruction::IGET_WIDE, 2u, thiz, 2u), - DEF_IGET(3u, Instruction::IGET, 4u, thiz, 3u), - DEF_IGET(3u, Instruction::IGET, 5u, thiz, 4u), - DEF_IGET_WIDE(3u, Instruction::IGET_WIDE, 6u, thiz, 5u), - DEF_IGET(3u, Instruction::IGET_SHORT, 8u, thiz, 6u), - DEF_IGET(3u, Instruction::IGET_BOOLEAN, 9u, thiz, 7u), - DEF_IGET(3u, Instruction::IGET_OBJECT, 10u, thiz, 8u), - DEF_IGET(3u, Instruction::IGET_OBJECT, 11u, thiz, 9u), - }; - - PrepareIFields(ifields); - BuildDexFile("()V", false); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectFp | kExpectWide }, - { 0u, kExpectFp | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectWide }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - }; - static_assert(arraysize(expectations) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(expectations); ++i) { - EXPECT_EQ(mirs[i].opcode, mirs_[i].dalvikInsn.opcode); - ASSERT_LE(1u, mirs_[i].ssa_rep->num_defs); - ExpectSRegType(mirs_[i].ssa_rep->defs[0], expectations[i]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, SGet) { - static const FieldDef sfields[] = { - { kClassName, "B", "staticByteField" }, - { kClassName, "C", "staticCharField" }, - { kClassName, "D", "staticDoubleField" }, - { kClassName, "F", "staticFloatField" }, - { kClassName, "I", "staticIntField" }, - { kClassName, "J", "staticLongField" }, - { kClassName, "S", "staticShortField" }, - { kClassName, "Z", "staticBooleanField" }, - { kClassName, "Ljava/lang/Object;", "staticObjectField" }, - { kClassName, "[Ljava/lang/Object;", "staticObjectArrayField" }, - }; - static const MIRDef mirs[] = { - DEF_SGET(3u, Instruction::SGET_BYTE, 0u, 0u), - DEF_SGET(3u, Instruction::SGET_CHAR, 1u, 1u), - DEF_SGET_WIDE(3u, Instruction::SGET_WIDE, 2u, 2u), - DEF_SGET(3u, Instruction::SGET, 4u, 3u), - DEF_SGET(3u, Instruction::SGET, 5u, 4u), - DEF_SGET_WIDE(3u, Instruction::SGET_WIDE, 6u, 5u), - DEF_SGET(3u, Instruction::SGET_SHORT, 8u, 6u), - DEF_SGET(3u, Instruction::SGET_BOOLEAN, 9u, 7u), - DEF_SGET(3u, Instruction::SGET_OBJECT, 10u, 8u), - DEF_SGET(3u, Instruction::SGET_OBJECT, 11u, 9u), - }; - - PrepareSFields(sfields); - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectFp | kExpectWide }, - { 0u, kExpectFp | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectWide }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - }; - static_assert(arraysize(expectations) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(expectations); ++i) { - EXPECT_EQ(mirs[i].opcode, mirs_[i].dalvikInsn.opcode); - ASSERT_LE(1u, mirs_[i].ssa_rep->num_defs); - ExpectSRegType(mirs_[i].ssa_rep->defs[0], expectations[i]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, IPut) { - static const FieldDef ifields[] = { - { kClassName, "B", "byteField" }, - { kClassName, "C", "charField" }, - { kClassName, "D", "doubleField" }, - { kClassName, "F", "floatField" }, - { kClassName, "I", "intField" }, - { kClassName, "J", "longField" }, - { kClassName, "S", "shortField" }, - { kClassName, "Z", "booleanField" }, - { kClassName, "Ljava/lang/Object;", "objectField" }, - { kClassName, "[Ljava/lang/Object;", "objectArrayField" }, - }; - constexpr uint32_t thiz = kLocalVRs; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_IPUT(3u, Instruction::IPUT_BYTE, 0u, thiz, 0u), - DEF_CONST(3u, Instruction::CONST, 1u, 0), - DEF_IPUT(3u, Instruction::IPUT_CHAR, 1u, thiz, 1u), - DEF_CONST_WIDE(3u, Instruction::CONST_WIDE, 2u, 0), - DEF_IPUT_WIDE(3u, Instruction::IPUT_WIDE, 2u, thiz, 2u), - DEF_CONST(3u, Instruction::CONST, 4u, 0), - DEF_IPUT(3u, Instruction::IPUT, 4u, thiz, 3u), - DEF_CONST(3u, Instruction::CONST, 5u, 0), - DEF_IPUT(3u, Instruction::IPUT, 5u, thiz, 4u), - DEF_CONST_WIDE(3u, Instruction::CONST_WIDE, 6u, 0), - DEF_IPUT_WIDE(3u, Instruction::IPUT_WIDE, 6u, thiz, 5u), - DEF_CONST(3u, Instruction::CONST, 8u, 0), - DEF_IPUT(3u, Instruction::IPUT_SHORT, 8u, thiz, 6u), - DEF_CONST(3u, Instruction::CONST, 9u, 0), - DEF_IPUT(3u, Instruction::IPUT_BOOLEAN, 9u, thiz, 7u), - DEF_CONST(3u, Instruction::CONST, 10u, 0), - DEF_IPUT(3u, Instruction::IPUT_OBJECT, 10u, thiz, 8u), - DEF_CONST(3u, Instruction::CONST, 11u, 0), - DEF_IPUT(3u, Instruction::IPUT_OBJECT, 11u, thiz, 9u), - }; - - PrepareIFields(ifields); - BuildDexFile("()V", false); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - // One expectation for every 2 MIRs. - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectFp | kExpectWide }, - { 0u, kExpectFp | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectWide }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow | kExpectNull }, - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - }; - static_assert(2 * arraysize(expectations) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(expectations); ++i) { - EXPECT_EQ(mirs[2 * i].opcode, mirs_[2 * i].dalvikInsn.opcode); - EXPECT_EQ(mirs[2 * i + 1].opcode, mirs_[2 * i + 1].dalvikInsn.opcode); - ASSERT_LE(1u, mirs_[2 * i].ssa_rep->num_defs); - ExpectSRegType(mirs_[2 * i].ssa_rep->defs[0], expectations[i]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, SPut) { - static const FieldDef sfields[] = { - { kClassName, "B", "staticByteField" }, - { kClassName, "C", "staticCharField" }, - { kClassName, "D", "staticDoubleField" }, - { kClassName, "F", "staticFloatField" }, - { kClassName, "I", "staticIntField" }, - { kClassName, "J", "staticLongField" }, - { kClassName, "S", "staticShortField" }, - { kClassName, "Z", "staticBooleanField" }, - { kClassName, "Ljava/lang/Object;", "staticObjectField" }, - { kClassName, "[Ljava/lang/Object;", "staticObjectArrayField" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_SPUT(3u, Instruction::SPUT_BYTE, 0u, 0u), - DEF_CONST(3u, Instruction::CONST, 1u, 0), - DEF_SPUT(3u, Instruction::SPUT_CHAR, 1u, 1u), - DEF_CONST_WIDE(3u, Instruction::CONST_WIDE, 2u, 0), - DEF_SPUT_WIDE(3u, Instruction::SPUT_WIDE, 2u, 2u), - DEF_CONST(3u, Instruction::CONST, 4u, 0), - DEF_SPUT(3u, Instruction::SPUT, 4u, 3u), - DEF_CONST(3u, Instruction::CONST, 5u, 0), - DEF_SPUT(3u, Instruction::SPUT, 5u, 4u), - DEF_CONST_WIDE(3u, Instruction::CONST_WIDE, 6u, 0), - DEF_SPUT_WIDE(3u, Instruction::SPUT_WIDE, 6u, 5u), - DEF_CONST(3u, Instruction::CONST, 8u, 0), - DEF_SPUT(3u, Instruction::SPUT_SHORT, 8u, 6u), - DEF_CONST(3u, Instruction::CONST, 9u, 0), - DEF_SPUT(3u, Instruction::SPUT_BOOLEAN, 9u, 7u), - DEF_CONST(3u, Instruction::CONST, 10u, 0), - DEF_SPUT(3u, Instruction::SPUT_OBJECT, 10u, 8u), - DEF_CONST(3u, Instruction::CONST, 11u, 0), - DEF_SPUT(3u, Instruction::SPUT_OBJECT, 11u, 9u), - }; - - PrepareSFields(sfields); - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - // One expectation for every 2 MIRs. - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectFp | kExpectWide }, - { 0u, kExpectFp | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectWide }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow | kExpectNull }, - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - }; - static_assert(2 * arraysize(expectations) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(expectations); ++i) { - EXPECT_EQ(mirs[2 * i].opcode, mirs_[2 * i].dalvikInsn.opcode); - EXPECT_EQ(mirs[2 * i + 1].opcode, mirs_[2 * i + 1].dalvikInsn.opcode); - ASSERT_LE(1u, mirs_[2 * i].ssa_rep->num_defs); - ExpectSRegType(mirs_[2 * i].ssa_rep->defs[0], expectations[i]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, MethodReturnType) { - static const MethodDef methods[] = { - { kClassName, "()B", "byteFoo", kStatic }, - { kClassName, "()C", "charFoo", kStatic }, - { kClassName, "()D", "doubleFoo", kStatic }, - { kClassName, "()F", "floatFoo", kStatic }, - { kClassName, "()I", "intFoo", kStatic }, - { kClassName, "()J", "longFoo", kStatic }, - { kClassName, "()S", "shortFoo", kStatic }, - { kClassName, "()Z", "booleanFoo", kStatic }, - { kClassName, "()Ljava/lang/Object;", "objectFoo", kStatic }, - { kClassName, "()[Ljava/lang/Object;", "objectArrayFoo", kStatic }, - }; - static const MIRDef mirs[] = { - DEF_INVOKE0(3u, Instruction::INVOKE_STATIC, 0u), - DEF_NULOP(3u, Instruction::MOVE_RESULT, 0u), - DEF_INVOKE0(3u, Instruction::INVOKE_STATIC, 1u), - DEF_NULOP(3u, Instruction::MOVE_RESULT, 1u), - DEF_INVOKE0(3u, Instruction::INVOKE_STATIC, 2u), - DEF_NULOP_WIDE(3u, Instruction::MOVE_RESULT_WIDE, 2u), - DEF_INVOKE0(3u, Instruction::INVOKE_STATIC, 3u), - DEF_NULOP(3u, Instruction::MOVE_RESULT, 4u), - DEF_INVOKE0(3u, Instruction::INVOKE_STATIC, 4u), - DEF_NULOP(3u, Instruction::MOVE_RESULT, 5u), - DEF_INVOKE0(3u, Instruction::INVOKE_STATIC, 5u), - DEF_NULOP_WIDE(3u, Instruction::MOVE_RESULT_WIDE, 6u), - DEF_INVOKE0(3u, Instruction::INVOKE_STATIC, 6u), - DEF_NULOP(3u, Instruction::MOVE_RESULT, 8u), - DEF_INVOKE0(3u, Instruction::INVOKE_STATIC, 7u), - DEF_NULOP(3u, Instruction::MOVE_RESULT, 9u), - DEF_INVOKE0(3u, Instruction::INVOKE_STATIC, 8u), - DEF_NULOP(3u, Instruction::MOVE_RESULT_OBJECT, 10u), - DEF_INVOKE0(3u, Instruction::INVOKE_STATIC, 9u), - DEF_NULOP(3u, Instruction::MOVE_RESULT_OBJECT, 11u), - }; - - PrepareMethods(methods); - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - // One expectation for every 2 MIRs. - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectFp | kExpectWide }, - { 0u, kExpectFp | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectWide }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - }; - static_assert(2 * arraysize(expectations) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(expectations); ++i) { - EXPECT_EQ(mirs[2 * i].opcode, mirs_[2 * i].dalvikInsn.opcode); - EXPECT_EQ(mirs[2 * i + 1].opcode, mirs_[2 * i + 1].dalvikInsn.opcode); - ASSERT_LE(1u, mirs_[2 * i + 1].ssa_rep->num_defs); - ExpectSRegType(mirs_[2 * i + 1].ssa_rep->defs[0], expectations[i]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, MethodArgType) { - static const MethodDef methods[] = { - { kClassName, "(B)V", "fooByte", kStatic }, - { kClassName, "(C)V", "fooChar", kStatic }, - { kClassName, "(D)V", "fooDouble", kStatic }, - { kClassName, "(F)V", "fooFloat", kStatic }, - { kClassName, "(I)V", "fooInt", kStatic }, - { kClassName, "(J)V", "fooLong", kStatic }, - { kClassName, "(S)V", "fooShort", kStatic }, - { kClassName, "(Z)V", "fooBoolean", kStatic }, - { kClassName, "(Ljava/lang/Object;)V", "fooObject", kStatic }, - { kClassName, "([Ljava/lang/Object;)V", "fooObjectArray", kStatic }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_INVOKE1(3u, Instruction::INVOKE_STATIC, 0u, 0u), - DEF_CONST(3u, Instruction::CONST, 1u, 0), - DEF_INVOKE1(3u, Instruction::INVOKE_STATIC, 1u, 1u), - DEF_CONST_WIDE(3u, Instruction::CONST_WIDE, 2u, 0), - DEF_INVOKE2(3u, Instruction::INVOKE_STATIC, 2u, 3u, 2u), - DEF_CONST(3u, Instruction::CONST, 4u, 0), - DEF_INVOKE1(3u, Instruction::INVOKE_STATIC, 4u, 3u), - DEF_CONST(3u, Instruction::CONST, 5u, 0), - DEF_INVOKE1(3u, Instruction::INVOKE_STATIC, 5u, 4u), - DEF_CONST_WIDE(3u, Instruction::CONST_WIDE, 6u, 0), - DEF_INVOKE2(3u, Instruction::INVOKE_STATIC, 6u, 7u, 5u), - DEF_CONST(3u, Instruction::CONST, 8u, 0), - DEF_INVOKE1(3u, Instruction::INVOKE_STATIC, 8u, 6u), - DEF_CONST(3u, Instruction::CONST, 9u, 0), - DEF_INVOKE1(3u, Instruction::INVOKE_STATIC, 9u, 7u), - DEF_CONST(3u, Instruction::CONST, 10u, 0), - DEF_INVOKE1(3u, Instruction::INVOKE_STATIC, 10u, 8u), - DEF_CONST(3u, Instruction::CONST, 11u, 0), - DEF_INVOKE1(3u, Instruction::INVOKE_STATIC, 11u, 9u), - }; - - PrepareMethods(methods); - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - // One expectation for every 2 MIRs. - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectFp | kExpectWide }, - { 0u, kExpectFp | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectWide }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow | kExpectNull }, - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - }; - static_assert(2 * arraysize(expectations) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(expectations); ++i) { - EXPECT_EQ(mirs[2 * i].opcode, mirs_[2 * i].dalvikInsn.opcode); - EXPECT_EQ(mirs[2 * i + 1].opcode, mirs_[2 * i + 1].dalvikInsn.opcode); - ASSERT_LE(1u, mirs_[2 * i].ssa_rep->num_defs); - ExpectSRegType(mirs_[2 * i].ssa_rep->defs[0], expectations[i]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, APut1) { - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), // Object[] array - DEF_CONST(3u, Instruction::CONST, 1u, 0), // value; can't even determine whether core or fp. - DEF_CONST(3u, Instruction::CONST, 2u, 0), // index - DEF_APUT(3u, Instruction::APUT, 1u, 0u, 2u), - }; - - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayNarrow }, - { 0u, kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, APut2) { - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), // Object[] array - DEF_CONST(3u, Instruction::CONST, 1u, 0), // Object[] value - DEF_CONST(3u, Instruction::CONST, 2u, 0), // index - DEF_APUT(3u, Instruction::APUT_OBJECT, 1u, 0u, 2u), - }; - - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectRef | kExpectNarrow | kExpectNull }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, APut3) { - static const MIRDef mirs[] = { - // Either array1 or array2 could be Object[][] but there is no way to tell from the bytecode. - DEF_CONST(3u, Instruction::CONST, 0u, 0), // Object[] array1 - DEF_CONST(3u, Instruction::CONST, 1u, 0), // Object[] array2 - DEF_CONST(3u, Instruction::CONST, 2u, 0), // index - DEF_APUT(3u, Instruction::APUT_OBJECT, 0u, 1u, 2u), - DEF_APUT(3u, Instruction::APUT_OBJECT, 1u, 0u, 2u), - }; - - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, APut4) { - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_CONST(3u, Instruction::CONST, 1u, 0), // index - DEF_AGET(3u, Instruction::AGET_OBJECT, 2u, 0u, 1u), // Object[] array - DEF_CONST(3u, Instruction::CONST, 3u, 0), // value; can't even determine whether core or fp. - DEF_APUT(3u, Instruction::APUT, 3u, 2u, 1u), - }; - - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayNarrow }, - { 0u, kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, APut5) { - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_CONST(3u, Instruction::CONST, 1u, 0), // index - DEF_AGET(3u, Instruction::AGET_OBJECT, 2u, 0u, 1u), // Object[] array - DEF_CONST(3u, Instruction::CONST, 3u, 0), // Object[] value - DEF_APUT(3u, Instruction::APUT_OBJECT, 3u, 2u, 1u), - }; - - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectRef | kExpectNarrow | kExpectNull }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, APut6) { - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_CONST(3u, Instruction::CONST, 1u, 0), // index - // Either array1 or array2 could be Object[][] but there is no way to tell from the bytecode. - DEF_AGET(3u, Instruction::AGET_OBJECT, 2u, 0u, 1u), // Object[] array1 - DEF_AGET(3u, Instruction::AGET_OBJECT, 3u, 0u, 1u), // Object[] array2 - DEF_APUT(3u, Instruction::APUT_OBJECT, 2u, 3u, 1u), - DEF_APUT(3u, Instruction::APUT_OBJECT, 3u, 2u, 1u), - }; - - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, TwoNullObjectArraysInLoop) { - static const MIRDef mirs[] = { - // void foo() { - // Object[] array1 = ((Object[])null)[0]; - // Object[] array2 = ((Object[])null)[0]; - // for (int i = 0; i != 3; ++i) { - // Object[] a1 = null; // One of these could be Object[][] but not both. - // Object[] a2 = null; // But they will be deduced as Object[]. - // try { a1[0] = a2; } catch (Throwable ignored) { } - // try { a2[0] = a1; } catch (Throwable ignored) { } - // array1 = a1; - // array2 = a2; - // } - // } - // - // Omitting the try-catch: - DEF_CONST(3u, Instruction::CONST, 0u, 0), // null - DEF_CONST(3u, Instruction::CONST, 1u, 0), // index - DEF_AGET(3u, Instruction::AGET_OBJECT, 2u, 0u, 1u), // array1 - DEF_AGET(3u, Instruction::AGET_OBJECT, 3u, 0u, 1u), // array2 - DEF_PHI2(4u, 4u, 2u, 8u), // ? + [L -> [? gives [L (see array-length below) - DEF_PHI2(4u, 5u, 3u, 9u), // ? + [L -> ? gives ? - DEF_AGET(4u, Instruction::AGET_OBJECT, 6u, 0u, 1u), // a1 - DEF_AGET(4u, Instruction::AGET_OBJECT, 7u, 0u, 1u), // a2 - DEF_APUT(4u, Instruction::APUT_OBJECT, 6u, 7u, 1u), - DEF_APUT(4u, Instruction::APUT_OBJECT, 7u, 6u, 1u), - DEF_MOVE(4u, Instruction::MOVE_OBJECT, 8u, 6u), - DEF_MOVE(4u, Instruction::MOVE_OBJECT, 9u, 7u), - DEF_UNOP(5u, Instruction::ARRAY_LENGTH, 10u, 4u), - }; - - BuildDexFile("()V", true); - PrepareLoop(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, ArrayArrayFloat) { - static const MethodDef methods[] = { - { kClassName, "(F)V", "fooFloat", kStatic }, - }; - static const MIRDef mirs[] = { - // void foo() { - // try { - // float[][][] aaaf = null; - // float[][] array = aaaf[0]; // Make sure array is treated as properly typed. - // array[0][0] = 0.0f; // const + aget-object[1] + aput - // fooFloat(array[0][0]); // aget-object[2] + aget + invoke - // // invoke: signature => input is F. - // // aget: output is F => base is [F (precise) - // // aget-object[2]: output is [F => base is [[F (precise) - // // aput: unknown input type => base is [? - // // aget-object[1]: base is [[F => result is L or [F, merge with [? => result is [F - // // aput (again): base is [F => result is F - // // const: F determined by the aput reprocessing. - // } catch (Throwable ignored) { - // } - // } - // - // Omitting the try-catch: - DEF_CONST(3u, Instruction::CONST, 0u, 0), // 0 - DEF_CONST(3u, Instruction::CONST, 1u, 0), // aaaf - DEF_AGET(3u, Instruction::AGET_OBJECT, 2u, 1u, 0u), // array = aaaf[0] - DEF_CONST(3u, Instruction::CONST, 3u, 0), // 0.0f - DEF_AGET(3u, Instruction::AGET_OBJECT, 4u, 2u, 0u), // array[0] - DEF_APUT(3u, Instruction::APUT, 3u, 4u, 0u), // array[0][0] = 0.0f - DEF_AGET(3u, Instruction::AGET_OBJECT, 5u, 2u, 0u), // array[0] - DEF_AGET(3u, Instruction::AGET, 6u, 5u, 0u), // array[0][0] - DEF_INVOKE1(3u, Instruction::INVOKE_STATIC, 6u, 0u), // fooFloat(array[0][0]) - }; - - PrepareMethods(methods); - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 2u, kExpectRef | kExpectNarrow | kExpectArrayFp | kExpectArrayNarrow }, - { 0u, kExpectFp | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayFp | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayFp | kExpectArrayNarrow }, - { 0u, kExpectFp | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, CheckCast1) { - static const TypeDef types[] = { - { "[I" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_CONST(3u, Instruction::CONST, 1u, 0), - DEF_AGET(3u, Instruction::AGET_OBJECT, 2u, 0u, 1u), - DEF_CHECK_CAST(4u, Instruction::CHECK_CAST, 2u, 0u), - DEF_CHECK_CAST(5u, Instruction::CHECK_CAST, 2u, 0u), - // Pseudo-phi from [I and [I into L infers only L but not [. - DEF_MOVE(6u, Instruction::MOVE_OBJECT, 3u, 2u), - }; - PrepareTypes(types); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - static const BasicBlockId v0_def_blocks[] = { 3u, 4u, 5u, 6u }; - MapVRegToSReg(2, 2, v0_def_blocks); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, CheckCast2) { - static const TypeDef types[] = { - { "[I" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_CONST(3u, Instruction::CONST, 1u, 0), - DEF_AGET(3u, Instruction::AGET_OBJECT, 2u, 0u, 1u), - DEF_CHECK_CAST(4u, Instruction::CHECK_CAST, 2u, 0u), - DEF_CHECK_CAST(5u, Instruction::CHECK_CAST, 2u, 0u), - // Pseudo-phi from [I and [I into [? infers [I. - DEF_MOVE(6u, Instruction::MOVE_OBJECT, 3u, 2u), - DEF_UNOP(6u, Instruction::ARRAY_LENGTH, 4u, 2u), - }; - PrepareTypes(types); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - static const BasicBlockId v0_def_blocks[] = { 3u, 4u, 5u, 6u }; - MapVRegToSReg(2, 2, v0_def_blocks); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, CheckCast3) { - static const TypeDef types[] = { - { "[I" }, - { "[F" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_CONST(3u, Instruction::CONST, 1u, 0), - DEF_AGET(3u, Instruction::AGET_OBJECT, 2u, 0u, 1u), - DEF_CHECK_CAST(4u, Instruction::CHECK_CAST, 2u, 0u), - DEF_CHECK_CAST(5u, Instruction::CHECK_CAST, 2u, 1u), - // Pseudo-phi from [I and [F into L correctly leaves it as L. - DEF_MOVE(6u, Instruction::MOVE_OBJECT, 3u, 2u), - }; - PrepareTypes(types); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - static const BasicBlockId v0_def_blocks[] = { 3u, 4u, 5u, 6u }; - MapVRegToSReg(2, 2, v0_def_blocks); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, CheckCastConflict1) { - static const TypeDef types[] = { - { "[I" }, - { "[F" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_CONST(3u, Instruction::CONST, 1u, 0), - DEF_AGET(3u, Instruction::AGET_OBJECT, 2u, 0u, 1u), - DEF_CHECK_CAST(4u, Instruction::CHECK_CAST, 2u, 0u), - DEF_CHECK_CAST(5u, Instruction::CHECK_CAST, 2u, 1u), - // Pseudo-phi from [I and [F into [? infers conflict [I/[F. - DEF_MOVE(6u, Instruction::MOVE_OBJECT, 3u, 2u), - DEF_UNOP(6u, Instruction::ARRAY_LENGTH, 4u, 2u), - }; - PrepareTypes(types); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - static const BasicBlockId v0_def_blocks[] = { 3u, 4u, 5u, 6u }; - MapVRegToSReg(2, 2, v0_def_blocks); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayFp | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg], false); - } - // The type conflict in array element wasn't propagated to an SSA reg. - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, CheckCastConflict2) { - static const TypeDef types[] = { - { "[I" }, - { "[F" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_CONST(3u, Instruction::CONST, 1u, 0), - DEF_AGET(3u, Instruction::AGET_OBJECT, 2u, 0u, 1u), - DEF_CHECK_CAST(4u, Instruction::CHECK_CAST, 2u, 0u), - DEF_CHECK_CAST(5u, Instruction::CHECK_CAST, 2u, 1u), - // Pseudo-phi from [I and [F into [? infers conflict [I/[F. - DEF_MOVE(6u, Instruction::MOVE_OBJECT, 3u, 2u), - DEF_AGET(6u, Instruction::AGET, 4u, 2u, 1u), - }; - PrepareTypes(types); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - static const BasicBlockId v0_def_blocks[] = { 3u, 4u, 5u, 6u }; - MapVRegToSReg(2, 2, v0_def_blocks); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayFp | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectFp | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg], false); - } - // Type conflict in an SSA reg, register promotion disabled. - EXPECT_NE(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, Phi1) { - static const TypeDef types[] = { - { "[I" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 100), - DEF_NEW_ARRAY(4u, Instruction::NEW_ARRAY, 1u, 0u, 0u), - DEF_NEW_ARRAY(5u, Instruction::NEW_ARRAY, 2u, 0u, 0u), - // Phi from [I and [I infers only L but not [. - DEF_PHI2(6u, 3u, 1u, 2u), - }; - PrepareTypes(types); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, Phi2) { - static const TypeDef types[] = { - { "[F" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 100), - DEF_NEW_ARRAY(4u, Instruction::NEW_ARRAY, 1u, 0u, 0u), - DEF_NEW_ARRAY(5u, Instruction::NEW_ARRAY, 2u, 0u, 0u), - // Phi from [F and [F into [? infers [F. - DEF_PHI2(6u, 3u, 1u, 2u), - DEF_UNOP(6u, Instruction::ARRAY_LENGTH, 4u, 3u), - }; - PrepareTypes(types); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayFp | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayFp | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayFp | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, Phi3) { - static const TypeDef types[] = { - { "[I" }, - { "[F" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 100), - DEF_NEW_ARRAY(4u, Instruction::NEW_ARRAY, 1u, 0u, 0u), - DEF_NEW_ARRAY(5u, Instruction::NEW_ARRAY, 2u, 0u, 1u), - // Phi from [I and [F infers L. - DEF_PHI2(6u, 3u, 1u, 2u), - }; - PrepareTypes(types); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayFp | kExpectArrayNarrow }, - { 0u, kExpectRef | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, Phi4) { - static const TypeDef types[] = { - { "[I" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 100), - DEF_NEW_ARRAY(4u, Instruction::NEW_ARRAY, 1u, 0u, 0u), - DEF_CONST(5u, Instruction::CONST, 2u, 0), - // Pseudo-phi from [I and null infers L. - DEF_PHI2(6u, 3u, 1u, 2u), - }; - PrepareTypes(types); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayNarrow }, - { 0u, kExpectRef | kExpectNarrow | kExpectNull }, - { 0u, kExpectRef | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, PhiConflict1) { - static const TypeDef types[] = { - { "[I" }, - { "[F" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 100), - DEF_NEW_ARRAY(4u, Instruction::NEW_ARRAY, 1u, 0u, 0u), - DEF_NEW_ARRAY(5u, Instruction::NEW_ARRAY, 2u, 0u, 1u), - // Pseudo-phi from [I and [F into [? infers conflict [I/[F (then propagated upwards). - DEF_PHI2(6u, 3u, 1u, 2u), - DEF_UNOP(6u, Instruction::ARRAY_LENGTH, 4u, 3u), - }; - PrepareTypes(types); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayFp | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayFp | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayFp | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg], false); - } - // The type conflict in array element wasn't propagated to an SSA reg. - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, PhiConflict2) { - static const TypeDef types[] = { - { "[I" }, - { "[F" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 100), - DEF_NEW_ARRAY(4u, Instruction::NEW_ARRAY, 1u, 0u, 0u), - DEF_NEW_ARRAY(5u, Instruction::NEW_ARRAY, 2u, 0u, 1u), - // Pseudo-phi from [I and [F into [? infers conflict [I/[F (then propagated upwards). - DEF_PHI2(6u, 3u, 1u, 2u), - DEF_AGET(6u, Instruction::AGET, 4u, 3u, 0u), - }; - PrepareTypes(types); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayFp | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayFp | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayFp | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectFp | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg], false); - } - // Type conflict in an SSA reg, register promotion disabled. - EXPECT_NE(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, Wide1) { - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_CONST(3u, Instruction::CONST, 1u, 0), // index - DEF_AGET(3u, Instruction::AGET_OBJECT, 2u, 0u, 1u), // long[] - DEF_CONST_WIDE(3u, Instruction::CONST_WIDE, 3u, 0), // long - DEF_APUT_WIDE(3u, Instruction::APUT_WIDE, 3u, 2u, 1u), - { 3u, Instruction::RETURN_OBJECT, 0, 0u, 1u, { 2u }, 0u, { } }, - }; - - BuildDexFile("()[J", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayWide }, - { 0u, kExpectCore | kExpectWide }, - // NOTE: High word checked implicitly for sreg = 3. - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg], false); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, WideSizeConflict1) { - static const MIRDef mirs[] = { - DEF_CONST_WIDE(3u, Instruction::CONST_WIDE, 0u, 0), - DEF_MOVE(3u, Instruction::MOVE, 2u, 0u), - }; - - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 0u, kExpectNarrow | kExpectWide }, - { 0u, kExpectNarrow | kExpectWide }, - }; - ExpectSRegType(0u, expectations[0], false); - ExpectSRegType(2u, expectations[1], false); - EXPECT_TRUE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, ArrayLongLength) { - static const FieldDef sfields[] = { - { kClassName, "[J", "arrayLongField" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(4u, Instruction::CONST, 0u, 0), - DEF_SGET(5u, Instruction::SGET_OBJECT, 1u, 0u), - DEF_PHI2(6u, 2u, 0u, 1u), - DEF_UNOP(6u, Instruction::ARRAY_LENGTH, 3u, 2u), - DEF_SGET(6u, Instruction::SGET_OBJECT, 4u, 0u), - DEF_UNOP(6u, Instruction::ARRAY_LENGTH, 5u, 4u), - }; - - PrepareSFields(sfields); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayCore | kExpectArrayWide }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayWide }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayWide }, - { 0u, kExpectCore | kExpectNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayWide }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, ArrayArrayObjectLength) { - static const FieldDef sfields[] = { - { kClassName, "[[Ljava/lang/Object;", "arrayLongField" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(4u, Instruction::CONST, 0u, 0), - DEF_SGET(5u, Instruction::SGET_OBJECT, 1u, 0u), - DEF_PHI2(6u, 2u, 0u, 1u), - DEF_UNOP(6u, Instruction::ARRAY_LENGTH, 3u, 2u), - DEF_SGET(6u, Instruction::SGET_OBJECT, 4u, 0u), - DEF_UNOP(6u, Instruction::ARRAY_LENGTH, 5u, 4u), - }; - - PrepareSFields(sfields); - BuildDexFile("()V", true); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | kExpectArrayRef | kExpectArrayNarrow }, - { 2u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 2u, kExpectRef | kExpectNarrow | kExpectArrayRef | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, SGetAdd0SPut) { - static const FieldDef sfields[] = { - { kClassName, "I", "staticIntField" }, - }; - static const MIRDef mirs[] = { - DEF_SGET(3u, Instruction::SGET, 0u, 0u), - DEF_UNOP(3u, Instruction::ADD_INT_LIT8, 1u, 0u), // +0 - DEF_SPUT(3u, Instruction::SPUT, 1u, 0u), - }; - - PrepareSFields(sfields); - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, MoveObjectNull) { - static const MethodDef methods[] = { - { kClassName, "([I[D)V", "foo", kStatic }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_MOVE(3u, Instruction::MOVE_OBJECT, 1u, 0u), - DEF_INVOKE2(3u, Instruction::INVOKE_STATIC, 0u, 1u, 0u), - }; - - PrepareMethods(methods); - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectation = { - 1u, - kExpectRef | kExpectNarrow | kExpectNull | - kExpectArrayCore | kExpectArrayFp | kExpectArrayNarrow | kExpectArrayWide - }; - ExpectSRegType(0u, expectation); - ExpectSRegType(1u, expectation); - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, MoveNull1) { - static const MethodDef methods[] = { - { kClassName, "([I[D)V", "foo", kStatic }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_MOVE(3u, Instruction::MOVE, 1u, 0u), - DEF_INVOKE2(3u, Instruction::INVOKE_STATIC, 0u, 1u, 0u), - }; - - PrepareMethods(methods); - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectation = { - 1u, - kExpectCore | kExpectRef | kExpectFp | kExpectNarrow | kExpectNull | - kExpectArrayCore | kExpectArrayFp | kExpectArrayNarrow | kExpectArrayWide - }; - ExpectSRegType(0u, expectation); - ExpectSRegType(1u, expectation); - // Type conflict using move instead of move-object for null, register promotion disabled. - EXPECT_NE(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, MoveNull2) { - static const FieldDef sfields[] = { - { kClassName, "[F", "staticArrayArrayFloatField" }, - { kClassName, "[I", "staticArrayIntField" }, - { kClassName, "[[I", "staticArrayArrayIntField" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(4u, Instruction::CONST, 0u, 0), - DEF_MOVE(4u, Instruction::MOVE_OBJECT, 1u, 0u), - DEF_MOVE(4u, Instruction::MOVE_OBJECT, 2u, 1u), - DEF_SGET(5u, Instruction::SGET_OBJECT, 3u, 0u), - DEF_SGET(5u, Instruction::SGET_OBJECT, 4u, 1u), - DEF_SGET(5u, Instruction::SGET_OBJECT, 5u, 2u), - DEF_PHI2(6u, 6u, 0u, 3u), - DEF_PHI2(6u, 7u, 1u, 4u), - DEF_PHI2(6u, 8u, 2u, 5u), - DEF_UNOP(6u, Instruction::ARRAY_LENGTH, 9u, 6u), - DEF_UNOP(6u, Instruction::ARRAY_LENGTH, 10u, 7u), - DEF_UNOP(6u, Instruction::ARRAY_LENGTH, 11u, 8u), - { 6u, Instruction::RETURN_OBJECT, 0, 0u, 1u, { 8u }, 0u, { } }, - }; - - PrepareSFields(sfields); - BuildDexFile("()[[I", true); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 1u, kExpectRef | kExpectNarrow | kExpectNull | - kExpectArrayCore | kExpectArrayFp | kExpectArrayRef | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectNull | - kExpectArrayCore | kExpectArrayFp | kExpectArrayRef | kExpectArrayNarrow}, - { 1u, kExpectRef | kExpectNarrow | kExpectNull | - kExpectArrayCore | kExpectArrayFp | kExpectArrayRef | kExpectArrayNarrow}, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayFp | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayNarrow }, - { 2u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayFp | kExpectArrayNarrow }, - { 1u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayNarrow }, - { 2u, kExpectRef | kExpectNarrow | kExpectArrayCore | kExpectArrayNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - // Type conflict in array type not propagated to actual register. - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, ReuseNull1) { - static const FieldDef sfields[] = { - { kClassName, "[I", "staticArrayLongField" }, - { kClassName, "[[F", "staticArrayArrayFloatField" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_SPUT(3u, Instruction::SPUT_OBJECT, 0u, 0u), - DEF_SPUT(3u, Instruction::SPUT_OBJECT, 0u, 1u), - }; - - PrepareSFields(sfields); - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectation = { - 1u, - kExpectRef | kExpectNarrow | kExpectNull | - kExpectArrayCore | kExpectArrayRef | kExpectArrayFp | kExpectArrayNarrow - }; - ExpectSRegType(0u, expectation); - // Type conflict in array type not propagated to actual register. - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, ReuseNull2) { - static const FieldDef sfields[] = { - { kClassName, "[J", "staticArrayLongField" }, - { kClassName, "[[F", "staticArrayArrayFloatField" }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3u, Instruction::CONST, 0u, 0), - DEF_SPUT(3u, Instruction::SPUT_OBJECT, 0u, 0u), - DEF_SPUT(3u, Instruction::SPUT_OBJECT, 0u, 1u), - }; - - PrepareSFields(sfields); - BuildDexFile("()V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectation = { - 1u, - kExpectRef | kExpectNarrow | kExpectNull | - kExpectArrayCore | kExpectArrayRef | kExpectArrayFp | kExpectArrayNarrow | kExpectArrayWide - }; - ExpectSRegType(0u, expectation); - // Type conflict in array type not propagated to actual register. - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, ArgIsNonNull) { - constexpr uint32_t thiz = kLocalVRs; - static const MIRDef mirs[] = { - DEF_MOVE(3u, Instruction::MOVE_OBJECT, 0u, thiz), - }; - - BuildDexFile("(Ljava/lang/Object;)V", true); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectation = { - 0u, - kExpectRef | kExpectNarrow - }; - ExpectSRegType(0u, expectation); - // Type conflict in array type not propagated to actual register. - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -TEST_F(TypeInferenceTest, IfCc) { - static const FieldDef sfields[] = { - { kClassName, "I", "intField" }, - }; - static const MIRDef mirs[] = { - DEF_SGET(3u, Instruction::SGET, 0u, 0u), - DEF_CONST(3u, Instruction::CONST, 1u, 0u), - { 3u, Instruction::IF_EQ, 0, 0u, 2, { 0u, 1u }, 0, { } }, - }; - - PrepareSFields(sfields); - BuildDexFile("()V", false); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformTypeInference(); - - ASSERT_EQ(arraysize(mirs), mir_count_); - static const SRegExpectation expectations[] = { - { 0u, kExpectCore | kExpectNarrow }, - { 0u, kExpectCore | kExpectNarrow }, - }; - for (int32_t sreg = 0; sreg != arraysize(expectations); ++sreg) { - ExpectSRegType(sreg, expectations[sreg]); - } - EXPECT_EQ(cu_.disable_opt & (1u << kPromoteRegs), 0u); - EXPECT_FALSE(cu_.mir_graph->PuntToInterpreter()); -} - -} // namespace art diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc index e681bcf..2b78e38 100644 --- a/compiler/dex/vreg_analysis.cc +++ b/compiler/dex/vreg_analysis.cc @@ -23,6 +23,400 @@ namespace art { +bool MIRGraph::SetFp(int index, bool is_fp) { + bool change = false; + if (is_fp && !reg_location_[index].fp) { + reg_location_[index].fp = true; + reg_location_[index].defined = true; + change = true; + } + return change; +} + +bool MIRGraph::SetFp(int index) { + bool change = false; + if (!reg_location_[index].fp) { + reg_location_[index].fp = true; + reg_location_[index].defined = true; + change = true; + } + return change; +} + +bool MIRGraph::SetCore(int index, bool is_core) { + bool change = false; + if (is_core && !reg_location_[index].defined) { + reg_location_[index].core = true; + reg_location_[index].defined = true; + change = true; + } + return change; +} + +bool MIRGraph::SetCore(int index) { + bool change = false; + if (!reg_location_[index].defined) { + reg_location_[index].core = true; + reg_location_[index].defined = true; + change = true; + } + return change; +} + +bool MIRGraph::SetRef(int index, bool is_ref) { + bool change = false; + if (is_ref && !reg_location_[index].defined) { + reg_location_[index].ref = true; + reg_location_[index].defined = true; + change = true; + } + return change; +} + +bool MIRGraph::SetRef(int index) { + bool change = false; + if (!reg_location_[index].defined) { + reg_location_[index].ref = true; + reg_location_[index].defined = true; + change = true; + } + return change; +} + +bool MIRGraph::SetWide(int index, bool is_wide) { + bool change = false; + if (is_wide && !reg_location_[index].wide) { + reg_location_[index].wide = true; + change = true; + } + return change; +} + +bool MIRGraph::SetWide(int index) { + bool change = false; + if (!reg_location_[index].wide) { + reg_location_[index].wide = true; + change = true; + } + return change; +} + +bool MIRGraph::SetHigh(int index, bool is_high) { + bool change = false; + if (is_high && !reg_location_[index].high_word) { + reg_location_[index].high_word = true; + change = true; + } + return change; +} + +bool MIRGraph::SetHigh(int index) { + bool change = false; + if (!reg_location_[index].high_word) { + reg_location_[index].high_word = true; + change = true; + } + return change; +} + + +/* + * Infer types and sizes. We don't need to track change on sizes, + * as it doesn't propagate. We're guaranteed at least one pass through + * the cfg. + */ +bool MIRGraph::InferTypeAndSize(BasicBlock* bb, MIR* mir, bool changed) { + SSARepresentation *ssa_rep = mir->ssa_rep; + + /* + * The dex bytecode definition does not explicitly outlaw the definition of the same + * virtual register to be used in both a 32-bit and 64-bit pair context. However, dx + * does not generate this pattern (at least recently). Further, in the next revision of + * dex, we will forbid this. To support the few cases in the wild, detect this pattern + * and punt to the interpreter. + */ + bool type_mismatch = false; + + if (ssa_rep) { + uint64_t attrs = GetDataFlowAttributes(mir); + const int* uses = ssa_rep->uses; + const int* defs = ssa_rep->defs; + + // Handle defs + if (attrs & DF_DA) { + if (attrs & DF_CORE_A) { + changed |= SetCore(defs[0]); + } + if (attrs & DF_REF_A) { + changed |= SetRef(defs[0]); + } + if (attrs & DF_A_WIDE) { + reg_location_[defs[0]].wide = true; + reg_location_[defs[1]].wide = true; + reg_location_[defs[1]].high_word = true; + DCHECK_EQ(SRegToVReg(defs[0])+1, + SRegToVReg(defs[1])); + } + } + + + // Handles uses + int next = 0; + if (attrs & DF_UA) { + if (attrs & DF_CORE_A) { + changed |= SetCore(uses[next]); + } + if (attrs & DF_REF_A) { + changed |= SetRef(uses[next]); + } + if (attrs & DF_A_WIDE) { + reg_location_[uses[next]].wide = true; + reg_location_[uses[next + 1]].wide = true; + reg_location_[uses[next + 1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[next])+1, + SRegToVReg(uses[next + 1])); + next += 2; + } else { + type_mismatch |= reg_location_[uses[next]].wide; + next++; + } + } + if (attrs & DF_UB) { + if (attrs & DF_CORE_B) { + changed |= SetCore(uses[next]); + } + if (attrs & DF_REF_B) { + changed |= SetRef(uses[next]); + } + if (attrs & DF_B_WIDE) { + reg_location_[uses[next]].wide = true; + reg_location_[uses[next + 1]].wide = true; + reg_location_[uses[next + 1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[next])+1, + SRegToVReg(uses[next + 1])); + next += 2; + } else { + type_mismatch |= reg_location_[uses[next]].wide; + next++; + } + } + if (attrs & DF_UC) { + if (attrs & DF_CORE_C) { + changed |= SetCore(uses[next]); + } + if (attrs & DF_REF_C) { + changed |= SetRef(uses[next]); + } + if (attrs & DF_C_WIDE) { + reg_location_[uses[next]].wide = true; + reg_location_[uses[next + 1]].wide = true; + reg_location_[uses[next + 1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[next])+1, + SRegToVReg(uses[next + 1])); + } else { + type_mismatch |= reg_location_[uses[next]].wide; + } + } + + // Special-case return handling + if ((mir->dalvikInsn.opcode == Instruction::RETURN) || + (mir->dalvikInsn.opcode == Instruction::RETURN_WIDE) || + (mir->dalvikInsn.opcode == Instruction::RETURN_OBJECT)) { + switch (cu_->shorty[0]) { + case 'I': + type_mismatch |= reg_location_[uses[0]].wide; + changed |= SetCore(uses[0]); + break; + case 'J': + changed |= SetCore(uses[0]); + changed |= SetCore(uses[1]); + reg_location_[uses[0]].wide = true; + reg_location_[uses[1]].wide = true; + reg_location_[uses[1]].high_word = true; + break; + case 'F': + type_mismatch |= reg_location_[uses[0]].wide; + changed |= SetFp(uses[0]); + break; + case 'D': + changed |= SetFp(uses[0]); + changed |= SetFp(uses[1]); + reg_location_[uses[0]].wide = true; + reg_location_[uses[1]].wide = true; + reg_location_[uses[1]].high_word = true; + break; + case 'L': + type_mismatch |= reg_location_[uses[0]].wide; + changed |= SetRef(uses[0]); + break; + default: break; + } + } + + // Special-case handling for format 35c/3rc invokes + Instruction::Code opcode = mir->dalvikInsn.opcode; + int flags = MIR::DecodedInstruction::IsPseudoMirOp(opcode) ? + 0 : mir->dalvikInsn.FlagsOf(); + if ((flags & Instruction::kInvoke) && + (attrs & (DF_FORMAT_35C | DF_FORMAT_3RC))) { + DCHECK_EQ(next, 0); + const auto& lowering_info = GetMethodLoweringInfo(mir); + const char* shorty = GetShortyFromMethodReference(lowering_info.GetTargetMethod()); + // Handle result type if floating point + if ((shorty[0] == 'F') || (shorty[0] == 'D')) { + MIR* move_result_mir = FindMoveResult(bb, mir); + // Result might not be used at all, so no move-result + if (move_result_mir && (move_result_mir->dalvikInsn.opcode != + Instruction::MOVE_RESULT_OBJECT)) { + SSARepresentation* tgt_rep = move_result_mir->ssa_rep; + DCHECK(tgt_rep != NULL); + tgt_rep->fp_def[0] = true; + changed |= SetFp(tgt_rep->defs[0]); + if (shorty[0] == 'D') { + tgt_rep->fp_def[1] = true; + changed |= SetFp(tgt_rep->defs[1]); + } + } + } + int num_uses = mir->dalvikInsn.vA; + // If this is a non-static invoke, mark implicit "this" + if (!IsInstructionInvokeStatic(mir->dalvikInsn.opcode)) { + reg_location_[uses[next]].defined = true; + reg_location_[uses[next]].ref = true; + type_mismatch |= reg_location_[uses[next]].wide; + next++; + } + uint32_t cpos = 1; + if (strlen(shorty) > 1) { + for (int i = next; i < num_uses;) { + DCHECK_LT(cpos, strlen(shorty)); + switch (shorty[cpos++]) { + case 'D': + ssa_rep->fp_use[i] = true; + ssa_rep->fp_use[i+1] = true; + reg_location_[uses[i]].wide = true; + reg_location_[uses[i+1]].wide = true; + reg_location_[uses[i+1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[i])+1, SRegToVReg(uses[i+1])); + i++; + break; + case 'J': + reg_location_[uses[i]].wide = true; + reg_location_[uses[i+1]].wide = true; + reg_location_[uses[i+1]].high_word = true; + DCHECK_EQ(SRegToVReg(uses[i])+1, SRegToVReg(uses[i+1])); + changed |= SetCore(uses[i]); + i++; + break; + case 'F': + type_mismatch |= reg_location_[uses[i]].wide; + ssa_rep->fp_use[i] = true; + break; + case 'L': + type_mismatch |= reg_location_[uses[i]].wide; + changed |= SetRef(uses[i]); + break; + default: + type_mismatch |= reg_location_[uses[i]].wide; + changed |= SetCore(uses[i]); + break; + } + i++; + } + } + } + + for (int i = 0; ssa_rep->fp_use && i< ssa_rep->num_uses; i++) { + if (ssa_rep->fp_use[i]) { + changed |= SetFp(uses[i]); + } + } + for (int i = 0; ssa_rep->fp_def && i< ssa_rep->num_defs; i++) { + if (ssa_rep->fp_def[i]) { + changed |= SetFp(defs[i]); + } + } + // Special-case handling for moves & Phi + if (attrs & (DF_IS_MOVE | DF_NULL_TRANSFER_N)) { + /* + * If any of our inputs or outputs is defined, set all. + * Some ugliness related to Phi nodes and wide values. + * The Phi set will include all low words or all high + * words, so we have to treat them specially. + */ + bool is_phi = (static_cast<int>(mir->dalvikInsn.opcode) == kMirOpPhi); + RegLocation rl_temp = reg_location_[defs[0]]; + bool defined_fp = rl_temp.defined && rl_temp.fp; + bool defined_core = rl_temp.defined && rl_temp.core; + bool defined_ref = rl_temp.defined && rl_temp.ref; + bool is_wide = rl_temp.wide || ((attrs & DF_A_WIDE) != 0); + bool is_high = is_phi && rl_temp.wide && rl_temp.high_word; + for (int i = 0; i < ssa_rep->num_uses; i++) { + rl_temp = reg_location_[uses[i]]; + defined_fp |= rl_temp.defined && rl_temp.fp; + defined_core |= rl_temp.defined && rl_temp.core; + defined_ref |= rl_temp.defined && rl_temp.ref; + is_wide |= rl_temp.wide; + is_high |= is_phi && rl_temp.wide && rl_temp.high_word; + } + /* + * We don't normally expect to see a Dalvik register definition used both as a + * floating point and core value, though technically it could happen with constants. + * Until we have proper typing, detect this situation and disable register promotion + * (which relies on the distinction between core a fp usages). + */ + if ((defined_fp && (defined_core | defined_ref)) && + ((cu_->disable_opt & (1 << kPromoteRegs)) == 0)) { + LOG(WARNING) << PrettyMethod(cu_->method_idx, *cu_->dex_file) + << " op at block " << bb->id + << " has both fp and core/ref uses for same def."; + cu_->disable_opt |= (1 << kPromoteRegs); + } + changed |= SetFp(defs[0], defined_fp); + changed |= SetCore(defs[0], defined_core); + changed |= SetRef(defs[0], defined_ref); + changed |= SetWide(defs[0], is_wide); + changed |= SetHigh(defs[0], is_high); + if (attrs & DF_A_WIDE) { + changed |= SetWide(defs[1]); + changed |= SetHigh(defs[1]); + } + + bool has_ins = (GetNumOfInVRs() > 0); + + for (int i = 0; i < ssa_rep->num_uses; i++) { + if (has_ins && IsInVReg(uses[i])) { + // NB: The SSA name for the first def of an in-reg will be the same as + // the reg's actual name. + if (!reg_location_[uses[i]].fp && defined_fp) { + // If we were about to infer that this first def of an in-reg is a float + // when it wasn't previously (because float/int is set during SSA initialization), + // do not allow this to happen. + continue; + } + } + changed |= SetFp(uses[i], defined_fp); + changed |= SetCore(uses[i], defined_core); + changed |= SetRef(uses[i], defined_ref); + changed |= SetWide(uses[i], is_wide); + changed |= SetHigh(uses[i], is_high); + } + if (attrs & DF_A_WIDE) { + DCHECK_EQ(ssa_rep->num_uses, 2); + changed |= SetWide(uses[1]); + changed |= SetHigh(uses[1]); + } + } + } + if (type_mismatch) { + LOG(WARNING) << "Deprecated dex type mismatch, interpreting " + << PrettyMethod(cu_->method_idx, *cu_->dex_file); + LOG(INFO) << "@ 0x" << std::hex << mir->offset; + SetPuntToInterpreter(true); + } + return changed; +} + static const char* storage_name[] = {" Frame ", "PhysReg", " CompilerTemp "}; void MIRGraph::DumpRegLocTable(RegLocation* table, int count) { @@ -62,6 +456,56 @@ void MIRGraph::InitRegLocations() { loc[method_sreg].defined = true; reg_location_ = loc; + + int num_regs = GetNumOfCodeVRs(); + + /* Add types of incoming arguments based on signature */ + int num_ins = GetNumOfInVRs(); + if (num_ins > 0) { + int s_reg = num_regs - num_ins; + if ((cu_->access_flags & kAccStatic) == 0) { + // For non-static, skip past "this" + reg_location_[s_reg].defined = true; + reg_location_[s_reg].ref = true; + s_reg++; + } + const char* shorty = cu_->shorty; + int shorty_len = strlen(shorty); + for (int i = 1; i < shorty_len; i++) { + switch (shorty[i]) { + case 'D': + reg_location_[s_reg].wide = true; + reg_location_[s_reg+1].high_word = true; + reg_location_[s_reg+1].fp = true; + DCHECK_EQ(SRegToVReg(s_reg)+1, SRegToVReg(s_reg+1)); + reg_location_[s_reg].fp = true; + reg_location_[s_reg].defined = true; + s_reg++; + break; + case 'J': + reg_location_[s_reg].wide = true; + reg_location_[s_reg+1].high_word = true; + DCHECK_EQ(SRegToVReg(s_reg)+1, SRegToVReg(s_reg+1)); + reg_location_[s_reg].core = true; + reg_location_[s_reg].defined = true; + s_reg++; + break; + case 'F': + reg_location_[s_reg].fp = true; + reg_location_[s_reg].defined = true; + break; + case 'L': + reg_location_[s_reg].ref = true; + reg_location_[s_reg].defined = true; + break; + default: + reg_location_[s_reg].core = true; + reg_location_[s_reg].defined = true; + break; + } + s_reg++; + } + } } /* diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h index 3983006..03ae489 100644 --- a/compiler/driver/dex_compilation_unit.h +++ b/compiler/driver/dex_compilation_unit.h @@ -21,7 +21,6 @@ #include "dex_file.h" #include "jni.h" -#include "base/arena_object.h" namespace art { namespace mirror { @@ -32,7 +31,7 @@ class ClassLinker; struct CompilationUnit; class VerifiedMethod; -class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> { +class DexCompilationUnit { public: explicit DexCompilationUnit(CompilationUnit* cu); diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h deleted file mode 100644 index ab039aa..0000000 --- a/compiler/utils/test_dex_file_builder.h +++ /dev/null @@ -1,372 +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_UTILS_TEST_DEX_FILE_BUILDER_H_ -#define ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ - -#include <cstring> -#include <set> -#include <map> -#include <vector> - -#include "dex_file.h" -#include "utils.h" - -namespace art { - -class TestDexFileBuilder { - public: - TestDexFileBuilder() - : strings_(), types_(), fields_(), protos_(), dex_file_data_() { - } - - void AddString(const std::string& str) { - CHECK(dex_file_data_.empty()); - auto it = strings_.emplace(str, IdxAndDataOffset()).first; - CHECK_LT(it->first.length(), 128u); // Don't allow multi-byte length in uleb128. - } - - void AddType(const std::string& descriptor) { - CHECK(dex_file_data_.empty()); - AddString(descriptor); - types_.emplace(descriptor, 0u); - } - - void AddField(const std::string& class_descriptor, const std::string& type, - const std::string& name) { - CHECK(dex_file_data_.empty()); - AddType(class_descriptor); - AddType(type); - AddString(name); - FieldKey key = { class_descriptor, type, name }; - fields_.emplace(key, 0u); - } - - void AddMethod(const std::string& class_descriptor, const std::string& signature, - const std::string& name) { - CHECK(dex_file_data_.empty()); - AddType(class_descriptor); - AddString(name); - - ProtoKey proto_key = CreateProtoKey(signature); - AddString(proto_key.shorty); - AddType(proto_key.return_type); - for (const auto& arg_type : proto_key.args) { - AddType(arg_type); - } - auto it = protos_.emplace(proto_key, IdxAndDataOffset()).first; - const ProtoKey* proto = &it->first; // Valid as long as the element remains in protos_. - - MethodKey method_key = { - class_descriptor, name, proto - }; - methods_.emplace(method_key, 0u); - } - - // NOTE: The builder holds the actual data, so it must live as long as the dex file. - std::unique_ptr<const DexFile> Build(const std::string& dex_location) { - CHECK(dex_file_data_.empty()); - union { - uint8_t data[sizeof(DexFile::Header)]; - uint64_t force_alignment; - } header_data; - std::memset(header_data.data, 0, sizeof(header_data.data)); - DexFile::Header* header = reinterpret_cast<DexFile::Header*>(&header_data.data); - std::copy_n(DexFile::kDexMagic, 4u, header->magic_); - std::copy_n(DexFile::kDexMagicVersion, 4u, header->magic_ + 4u); - header->header_size_ = sizeof(header); - header->endian_tag_ = DexFile::kDexEndianConstant; - header->link_size_ = 0u; // Unused. - header->link_off_ = 0u; // Unused. - header->map_off_ = 0u; // Unused. - - uint32_t data_section_size = 0u; - - uint32_t string_ids_offset = sizeof(DexFile::Header); - uint32_t string_idx = 0u; - for (auto& entry : strings_) { - entry.second.idx = string_idx; - string_idx += 1u; - entry.second.data_offset = data_section_size; - data_section_size += entry.first.length() + 1u /* length */ + 1u /* null-terminator */; - } - header->string_ids_size_ = strings_.size(); - header->string_ids_off_ = strings_.empty() ? 0u : string_ids_offset; - - uint32_t type_ids_offset = string_ids_offset + strings_.size() * sizeof(DexFile::StringId); - uint32_t type_idx = 0u; - for (auto& entry : types_) { - entry.second = type_idx; - type_idx += 1u; - } - header->type_ids_size_ = types_.size(); - header->type_ids_off_ = types_.empty() ? 0u : type_ids_offset; - - uint32_t proto_ids_offset = type_ids_offset + types_.size() * sizeof(DexFile::TypeId); - uint32_t proto_idx = 0u; - for (auto& entry : protos_) { - entry.second.idx = proto_idx; - proto_idx += 1u; - size_t num_args = entry.first.args.size(); - if (num_args != 0u) { - entry.second.data_offset = RoundUp(data_section_size, 4u); - data_section_size = entry.second.data_offset + 4u + num_args * sizeof(DexFile::TypeItem); - } else { - entry.second.data_offset = 0u; - } - } - header->proto_ids_size_ = protos_.size(); - header->proto_ids_off_ = protos_.empty() ? 0u : proto_ids_offset; - - uint32_t field_ids_offset = proto_ids_offset + protos_.size() * sizeof(DexFile::ProtoId); - uint32_t field_idx = 0u; - for (auto& entry : fields_) { - entry.second = field_idx; - field_idx += 1u; - } - header->field_ids_size_ = fields_.size(); - header->field_ids_off_ = fields_.empty() ? 0u : field_ids_offset; - - uint32_t method_ids_offset = field_ids_offset + fields_.size() * sizeof(DexFile::FieldId); - uint32_t method_idx = 0u; - for (auto& entry : methods_) { - entry.second = method_idx; - method_idx += 1u; - } - header->method_ids_size_ = methods_.size(); - header->method_ids_off_ = methods_.empty() ? 0u : method_ids_offset; - - // No class defs. - header->class_defs_size_ = 0u; - header->class_defs_off_ = 0u; - - uint32_t data_section_offset = method_ids_offset + methods_.size() * sizeof(DexFile::MethodId); - header->data_size_ = data_section_size; - header->data_off_ = (data_section_size != 0u) ? data_section_offset : 0u; - - uint32_t total_size = data_section_offset + data_section_size; - - dex_file_data_.resize(total_size); - std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header)); - - for (const auto& entry : strings_) { - CHECK_LT(entry.first.size(), 128u); - uint32_t raw_offset = data_section_offset + entry.second.data_offset; - dex_file_data_[raw_offset] = static_cast<uint8_t>(entry.first.size()); - std::memcpy(&dex_file_data_[raw_offset + 1], entry.first.c_str(), entry.first.size() + 1); - Write32(string_ids_offset + entry.second.idx * sizeof(DexFile::StringId), raw_offset); - } - - for (const auto& entry : types_) { - Write32(type_ids_offset + entry.second * sizeof(DexFile::TypeId), GetStringIdx(entry.first)); - ++type_idx; - } - - for (const auto& entry : protos_) { - size_t num_args = entry.first.args.size(); - uint32_t type_list_offset = - (num_args != 0u) ? data_section_offset + entry.second.data_offset : 0u; - uint32_t raw_offset = proto_ids_offset + entry.second.idx * sizeof(DexFile::ProtoId); - Write32(raw_offset + 0u, GetStringIdx(entry.first.shorty)); - Write16(raw_offset + 4u, GetTypeIdx(entry.first.return_type)); - Write32(raw_offset + 8u, type_list_offset); - if (num_args != 0u) { - CHECK_NE(entry.second.data_offset, 0u); - Write32(type_list_offset, num_args); - for (size_t i = 0; i != num_args; ++i) { - Write16(type_list_offset + 4u + i * sizeof(DexFile::TypeItem), - GetTypeIdx(entry.first.args[i])); - } - } - } - - for (const auto& entry : fields_) { - uint32_t raw_offset = field_ids_offset + entry.second * sizeof(DexFile::FieldId); - Write16(raw_offset + 0u, GetTypeIdx(entry.first.class_descriptor)); - Write16(raw_offset + 2u, GetTypeIdx(entry.first.type)); - Write32(raw_offset + 4u, GetStringIdx(entry.first.name)); - } - - for (const auto& entry : methods_) { - uint32_t raw_offset = method_ids_offset + entry.second * sizeof(DexFile::MethodId); - Write16(raw_offset + 0u, GetTypeIdx(entry.first.class_descriptor)); - auto it = protos_.find(*entry.first.proto); - CHECK(it != protos_.end()); - Write16(raw_offset + 2u, it->second.idx); - Write32(raw_offset + 4u, GetStringIdx(entry.first.name)); - } - - // Leave checksum and signature as zeros. - - std::string error_msg; - std::unique_ptr<const DexFile> dex_file(DexFile::Open( - &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, &error_msg)); - CHECK(dex_file != nullptr) << error_msg; - return std::move(dex_file); - } - - uint32_t GetStringIdx(const std::string& type) { - auto it = strings_.find(type); - CHECK(it != strings_.end()); - return it->second.idx; - } - - uint32_t GetTypeIdx(const std::string& type) { - auto it = types_.find(type); - CHECK(it != types_.end()); - return it->second; - } - - uint32_t GetFieldIdx(const std::string& class_descriptor, const std::string& type, - const std::string& name) { - FieldKey key = { class_descriptor, type, name }; - auto it = fields_.find(key); - CHECK(it != fields_.end()); - return it->second; - } - - uint32_t GetMethodIdx(const std::string& class_descriptor, const std::string& signature, - const std::string& name) { - ProtoKey proto_key = CreateProtoKey(signature); - MethodKey method_key = { class_descriptor, name, &proto_key }; - auto it = methods_.find(method_key); - CHECK(it != methods_.end()); - return it->second; - } - - private: - struct IdxAndDataOffset { - uint32_t idx; - uint32_t data_offset; - }; - - struct FieldKey { - const std::string class_descriptor; - const std::string type; - const std::string name; - }; - struct FieldKeyComparator { - bool operator()(const FieldKey& lhs, const FieldKey& rhs) const { - if (lhs.class_descriptor != rhs.class_descriptor) { - return lhs.class_descriptor < rhs.class_descriptor; - } - if (lhs.name != rhs.name) { - return lhs.name < rhs.name; - } - return lhs.type < rhs.type; - } - }; - - struct ProtoKey { - std::string shorty; - std::string return_type; - std::vector<std::string> args; - }; - struct ProtoKeyComparator { - bool operator()(const ProtoKey& lhs, const ProtoKey& rhs) const { - if (lhs.return_type != rhs.return_type) { - return lhs.return_type < rhs.return_type; - } - size_t min_args = std::min(lhs.args.size(), rhs.args.size()); - for (size_t i = 0; i != min_args; ++i) { - if (lhs.args[i] != rhs.args[i]) { - return lhs.args[i] < rhs.args[i]; - } - } - return lhs.args.size() < rhs.args.size(); - } - }; - - struct MethodKey { - std::string class_descriptor; - std::string name; - const ProtoKey* proto; - }; - struct MethodKeyComparator { - bool operator()(const MethodKey& lhs, const MethodKey& rhs) const { - if (lhs.class_descriptor != rhs.class_descriptor) { - return lhs.class_descriptor < rhs.class_descriptor; - } - if (lhs.name != rhs.name) { - return lhs.name < rhs.name; - } - return ProtoKeyComparator()(*lhs.proto, *rhs.proto); - } - }; - - ProtoKey CreateProtoKey(const std::string& signature) { - CHECK_EQ(signature[0], '('); - const char* args = signature.c_str() + 1; - const char* args_end = std::strchr(args, ')'); - CHECK(args_end != nullptr); - const char* return_type = args_end + 1; - - ProtoKey key = { - std::string() + ((*return_type == '[') ? 'L' : *return_type), - return_type, - std::vector<std::string>() - }; - while (args != args_end) { - key.shorty += (*args == '[') ? 'L' : *args; - const char* arg_start = args; - while (*args == '[') { - ++args; - } - if (*args == 'L') { - do { - ++args; - CHECK_NE(args, args_end); - } while (*args != ';'); - } - ++args; - key.args.emplace_back(arg_start, args); - } - return key; - } - - void Write32(size_t offset, uint32_t value) { - CHECK_LE(offset + 4u, dex_file_data_.size()); - CHECK_EQ(dex_file_data_[offset + 0], 0u); - CHECK_EQ(dex_file_data_[offset + 1], 0u); - CHECK_EQ(dex_file_data_[offset + 2], 0u); - CHECK_EQ(dex_file_data_[offset + 3], 0u); - dex_file_data_[offset + 0] = static_cast<uint8_t>(value >> 0); - dex_file_data_[offset + 1] = static_cast<uint8_t>(value >> 8); - dex_file_data_[offset + 2] = static_cast<uint8_t>(value >> 16); - dex_file_data_[offset + 3] = static_cast<uint8_t>(value >> 24); - } - - void Write16(size_t offset, uint32_t value) { - CHECK_LE(value, 0xffffu); - CHECK_LE(offset + 2u, dex_file_data_.size()); - CHECK_EQ(dex_file_data_[offset + 0], 0u); - CHECK_EQ(dex_file_data_[offset + 1], 0u); - dex_file_data_[offset + 0] = static_cast<uint8_t>(value >> 0); - dex_file_data_[offset + 1] = static_cast<uint8_t>(value >> 8); - } - - std::map<std::string, IdxAndDataOffset> strings_; - std::map<std::string, uint32_t> types_; - std::map<FieldKey, uint32_t, FieldKeyComparator> fields_; - std::map<ProtoKey, IdxAndDataOffset, ProtoKeyComparator> protos_; - std::map<MethodKey, uint32_t, MethodKeyComparator> methods_; - - std::vector<uint8_t> dex_file_data_; -}; - -} // namespace art - -#endif // ART_COMPILER_UTILS_TEST_DEX_FILE_BUILDER_H_ diff --git a/compiler/utils/test_dex_file_builder_test.cc b/compiler/utils/test_dex_file_builder_test.cc deleted file mode 100644 index ee6e35d..0000000 --- a/compiler/utils/test_dex_file_builder_test.cc +++ /dev/null @@ -1,84 +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 "test_dex_file_builder.h" - -#include "dex_file-inl.h" -#include "gtest/gtest.h" - -namespace art { - -TEST(TestDexFileBuilderTest, SimpleTest) { - TestDexFileBuilder builder; - builder.AddString("Arbitrary string"); - builder.AddType("Ljava/lang/Class;"); - builder.AddField("LTestClass;", "[I", "intField"); - builder.AddMethod("LTestClass;", "()I", "foo"); - builder.AddMethod("LTestClass;", "(Ljava/lang/Object;[Ljava/lang/Object;)LTestClass;", "bar"); - const char* dex_location = "TestDexFileBuilder/SimpleTest"; - std::unique_ptr<const DexFile> dex_file(builder.Build(dex_location)); - ASSERT_TRUE(dex_file != nullptr); - EXPECT_STREQ(dex_location, dex_file->GetLocation().c_str()); - - static const char* const expected_strings[] = { - "Arbitrary string", - "I", - "LLL", // shorty - "LTestClass;", - "Ljava/lang/Class;", - "Ljava/lang/Object;", - "[I", - "[Ljava/lang/Object;", - "bar", - "foo", - "intField", - }; - ASSERT_EQ(arraysize(expected_strings), dex_file->NumStringIds()); - for (size_t i = 0; i != arraysize(expected_strings); ++i) { - EXPECT_STREQ(expected_strings[i], dex_file->GetStringData(dex_file->GetStringId(i))) << i; - } - - static const char* const expected_types[] = { - "I", - "LTestClass;", - "Ljava/lang/Class;", - "Ljava/lang/Object;", - "[I", - "[Ljava/lang/Object;", - }; - ASSERT_EQ(arraysize(expected_types), dex_file->NumTypeIds()); - for (size_t i = 0; i != arraysize(expected_types); ++i) { - EXPECT_STREQ(expected_types[i], dex_file->GetTypeDescriptor(dex_file->GetTypeId(i))) << i; - } - - ASSERT_EQ(1u, dex_file->NumFieldIds()); - EXPECT_STREQ("[I TestClass.intField", PrettyField(0u, *dex_file).c_str()); - - ASSERT_EQ(2u, dex_file->NumProtoIds()); - ASSERT_EQ(2u, dex_file->NumMethodIds()); - EXPECT_STREQ("TestClass TestClass.bar(java.lang.Object, java.lang.Object[])", - PrettyMethod(0u, *dex_file).c_str()); - EXPECT_STREQ("int TestClass.foo()", - PrettyMethod(1u, *dex_file).c_str()); - - EXPECT_EQ(0u, builder.GetStringIdx("Arbitrary string")); - EXPECT_EQ(2u, builder.GetTypeIdx("Ljava/lang/Class;")); - EXPECT_EQ(0u, builder.GetFieldIdx("LTestClass;", "[I", "intField")); - EXPECT_EQ(1u, builder.GetMethodIdx("LTestClass;", "()I", "foo")); - EXPECT_EQ(0u, builder.GetMethodIdx("LTestClass;", "(Ljava/lang/Object;[Ljava/lang/Object;)LTestClass;", "bar")); -} - -} // namespace art |