| /* |
| * 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 */, |
| ScopedNullHandle<mirror::DexCache>())); |
| 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), |
| kArenaAllocSuccessors)); |
| successor_block_info->block = j; |
| successor_block_info->key = 0u; // Not used by class init check elimination. |
| bb->successor_blocks.push_back(successor_block_info); |
| } |
| } |
| bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors); |
| if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) { |
| bb->data_flow_info = static_cast<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)); |
| cu_.mir_graph->method_sreg_ = kMaxSsaRegs - 1u; |
| cu_.mir_graph->reg_location_[cu_.mir_graph->GetMethodSReg()].location = kLocCompilerTemp; |
| // 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 |