blob: c93fe2050b6ab3f836157ee2da93f4d9b7ce7e0f [file] [log] [blame]
/*
* 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"
#include "utils.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) {
/*
* Each dalvik register definition should be used either as a reference, or an
* integer or a floating point value. We don't normally expect to see a Dalvik
* register definition used in two or three of these roles though technically it
* could happen with constants (0 for all three roles, non-zero for integer and
* FP). Detect this situation and disable optimizations that rely on correct
* typing, i.e. register promotion, GVN/LVN and GVN-based DCE.
*/
LOG(WARNING) << PrettyMethod(cu_->method_idx, *cu_->dex_file)
<< " has type conflict block for sreg " << conflict_s_reg
<< ", disabling register promotion.";
cu_->disable_opt |=
(1u << kPromoteRegs) |
(1u << kGlobalValueNumbering) |
(1u << kGvnDeadCodeElimination) |
(1u << kLocalValueNumbering);
}
}
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());
/* Treat ArtMethod* specially since they are pointer sized */
sregs_[mir_graph_->GetMethodSReg()] = Type::ArtMethodType(cu_->target64);
// 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