summaryrefslogtreecommitdiffstats
path: root/compiler/dex/type_inference.h
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/dex/type_inference.h')
-rw-r--r--compiler/dex/type_inference.h443
1 files changed, 443 insertions, 0 deletions
diff --git a/compiler/dex/type_inference.h b/compiler/dex/type_inference.h
new file mode 100644
index 0000000000..c9b29bf7aa
--- /dev/null
+++ b/compiler/dex/type_inference.h
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEX_TYPE_INFERENCE_H_
+#define ART_COMPILER_DEX_TYPE_INFERENCE_H_
+
+#include "base/logging.h"
+#include "base/arena_object.h"
+#include "base/scoped_arena_containers.h"
+
+namespace art {
+
+class ArenaBitVector;
+class BasicBlock;
+struct CompilationUnit;
+class DexFile;
+class MirFieldInfo;
+class MirMethodInfo;
+class MIR;
+class MIRGraph;
+
+/**
+ * @brief Determine the type of SSA registers.
+ *
+ * @details
+ * Because Dalvik's bytecode is not fully typed, we have to do some work to figure
+ * out the sreg type. For some operations it is clear based on the opcode (i.e.
+ * ADD_FLOAT v0, v1, v2), but for others (MOVE), we may never know the "real" type.
+ *
+ * We perform the type inference operation in two phases:
+ * 1. First, we make one pass over all insns in the topological sort order and
+ * extract known type information from all insns for their defs and uses.
+ * 2. Then we repeatedly go through the graph to process insns that can propagate
+ * types from inputs to outputs and vice versa. These insns are just the MOVEs,
+ * AGET/APUTs, IF_ccs and Phis (including pseudo-Phis, see below).
+ *
+ * Since the main purpose is to determine the basic FP/core/reference type, we don't
+ * need to record the precise reference type, we only record the array type to determine
+ * the result types of agets and source type of aputs.
+ *
+ * One complication is the check-cast instruction that effectively defines a new
+ * virtual register that has a different type than the original sreg. We need to
+ * track these virtual sregs and insert pseudo-phis where they merge.
+ *
+ * Another problems is with null references. The same zero constant can be used
+ * as differently typed null and moved around with move-object which would normally
+ * be an ill-formed assignment. So we need to keep track of values that can be null
+ * and values that cannot.
+ *
+ * Note that it's possible to have the same sreg show multiple defined types because dx
+ * treats constants as untyped bit patterns. We disable register promotion in that case.
+ */
+class TypeInference : public DeletableArenaObject<kArenaAllocMisc> {
+ public:
+ TypeInference(MIRGraph* mir_graph, ScopedArenaAllocator* alloc);
+
+ bool Apply(BasicBlock* bb);
+ void Finish();
+
+ private:
+ struct Type {
+ static Type Unknown() {
+ return Type(0u);
+ }
+
+ static Type NonArrayRefType() {
+ return Type(kFlagLowWord | kFlagNarrow | kFlagRef);
+ }
+
+ static Type ObjectArrayType() {
+ return Type(kFlagNarrow | kFlagRef | kFlagLowWord |
+ (1u << kBitArrayDepthStart) | kFlagArrayNarrow | kFlagArrayRef);
+ }
+
+ static Type WideArrayType() {
+ // Core or FP unknown.
+ return Type(kFlagNarrow | kFlagRef | kFlagLowWord |
+ (1u << kBitArrayDepthStart) | kFlagArrayWide);
+ }
+
+ static Type NarrowArrayType() {
+ // Core or FP unknown.
+ return Type(kFlagNarrow | kFlagRef | kFlagLowWord |
+ (1u << kBitArrayDepthStart) | kFlagArrayNarrow);
+ }
+
+ static Type NarrowCoreArrayType() {
+ return Type(kFlagNarrow | kFlagRef | kFlagLowWord |
+ (1u << kBitArrayDepthStart) | kFlagArrayNarrow | kFlagArrayCore);
+ }
+
+ static Type UnknownArrayType() {
+ return Type(kFlagNarrow | kFlagRef | kFlagLowWord | (1u << kBitArrayDepthStart));
+ }
+
+ static Type ArrayType(uint32_t array_depth, Type nested_type);
+ static Type ArrayTypeFromComponent(Type component_type);
+ static Type ShortyType(char shorty);
+ static Type DexType(const DexFile* dex_file, uint32_t type_idx);
+
+ bool IsDefined() {
+ return raw_bits_ != 0u;
+ }
+
+ bool SizeConflict() const {
+ // NOTE: Ignore array element conflicts that don't propagate to direct conflicts.
+ return (Wide() && Narrow()) || (HighWord() && LowWord());
+ }
+
+ bool TypeConflict() const {
+ // NOTE: Ignore array element conflicts that don't propagate to direct conflicts.
+ return (raw_bits_ & kMaskType) != 0u && !IsPowerOfTwo(raw_bits_ & kMaskType); // 2+ bits.
+ }
+
+ void MarkSizeConflict() {
+ SetBits(kFlagLowWord | kFlagHighWord);
+ }
+
+ void MarkTypeConflict() {
+ // Mark all three type bits so that merging any other type bits will not change this type.
+ SetBits(kFlagFp | kFlagCore | kFlagRef);
+ }
+
+ void CheckPureRef() const {
+ DCHECK_EQ(raw_bits_ & (kMaskWideAndType | kMaskWord), kFlagNarrow | kFlagRef | kFlagLowWord);
+ }
+
+ // If reference, don't treat as possible null and require precise type.
+ //
+ // References without this flag are allowed to have a type conflict and their
+ // type will not be propagated down. However, for simplicity we allow propagation
+ // of other flags up as it will affect only other null references; should those
+ // references be marked non-null later, we would have to do it anyway.
+ // NOTE: This is a negative "non-null" flag rather then a positive "is-null"
+ // to simplify merging together with other non-array flags.
+ bool NonNull() const {
+ return IsBitSet(kFlagNonNull);
+ }
+
+ bool Wide() const {
+ return IsBitSet(kFlagWide);
+ }
+
+ bool Narrow() const {
+ return IsBitSet(kFlagNarrow);
+ }
+
+ bool Fp() const {
+ return IsBitSet(kFlagFp);
+ }
+
+ bool Core() const {
+ return IsBitSet(kFlagCore);
+ }
+
+ bool Ref() const {
+ return IsBitSet(kFlagRef);
+ }
+
+ bool LowWord() const {
+ return IsBitSet(kFlagLowWord);
+ }
+
+ bool HighWord() const {
+ return IsBitSet(kFlagHighWord);
+ }
+
+ uint32_t ArrayDepth() const {
+ return raw_bits_ >> kBitArrayDepthStart;
+ }
+
+ Type NestedType() const {
+ DCHECK_NE(ArrayDepth(), 0u);
+ return Type(kFlagLowWord | ((raw_bits_ & kMaskArrayWideAndType) >> kArrayTypeShift));
+ }
+
+ Type ComponentType() const {
+ DCHECK_NE(ArrayDepth(), 0u);
+ Type temp(raw_bits_ - (1u << kBitArrayDepthStart)); // array_depth - 1u;
+ return (temp.ArrayDepth() != 0u) ? temp.AsNull() : NestedType();
+ }
+
+ void SetWide() {
+ SetBits(kFlagWide);
+ }
+
+ void SetNarrow() {
+ SetBits(kFlagNarrow);
+ }
+
+ void SetFp() {
+ SetBits(kFlagFp);
+ }
+
+ void SetCore() {
+ SetBits(kFlagCore);
+ }
+
+ void SetRef() {
+ SetBits(kFlagRef);
+ }
+
+ void SetLowWord() {
+ SetBits(kFlagLowWord);
+ }
+
+ void SetHighWord() {
+ SetBits(kFlagHighWord);
+ }
+
+ Type ToHighWord() const {
+ DCHECK_EQ(raw_bits_ & (kMaskWide | kMaskWord), kFlagWide | kFlagLowWord);
+ return Type(raw_bits_ ^ (kFlagLowWord | kFlagHighWord));
+ }
+
+ bool MergeHighWord(Type low_word_type) {
+ // NOTE: low_word_type may be also Narrow() or HighWord().
+ DCHECK(low_word_type.Wide() && low_word_type.LowWord());
+ return MergeBits(Type(low_word_type.raw_bits_ | kFlagHighWord),
+ kMaskWideAndType | kFlagHighWord);
+ }
+
+ bool Copy(Type type) {
+ if (raw_bits_ != type.raw_bits_) {
+ raw_bits_ = type.raw_bits_;
+ return true;
+ }
+ return false;
+ }
+
+ // Merge non-array flags.
+ bool MergeNonArrayFlags(Type src_type) {
+ return MergeBits(src_type, kMaskNonArray);
+ }
+
+ // Merge array flags for conflict.
+ bool MergeArrayConflict(Type src_type);
+
+ // Merge all flags.
+ bool MergeStrong(Type src_type);
+
+ // Merge all flags.
+ bool MergeWeak(Type src_type);
+
+ // Get the same type but mark that it should not be treated as null.
+ Type AsNonNull() const {
+ return Type(raw_bits_ | kFlagNonNull);
+ }
+
+ // Get the same type but mark that it can be treated as null.
+ Type AsNull() const {
+ return Type(raw_bits_ & ~kFlagNonNull);
+ }
+
+ private:
+ enum FlagBits {
+ kBitNonNull = 0,
+ kBitWide,
+ kBitNarrow,
+ kBitFp,
+ kBitCore,
+ kBitRef,
+ kBitLowWord,
+ kBitHighWord,
+ kBitArrayWide,
+ kBitArrayNarrow,
+ kBitArrayFp,
+ kBitArrayCore,
+ kBitArrayRef,
+ kBitArrayDepthStart,
+ };
+ static constexpr size_t kArrayDepthBits = sizeof(uint32_t) * 8u - kBitArrayDepthStart;
+
+ static constexpr uint32_t kFlagNonNull = 1u << kBitNonNull;
+ static constexpr uint32_t kFlagWide = 1u << kBitWide;
+ static constexpr uint32_t kFlagNarrow = 1u << kBitNarrow;
+ static constexpr uint32_t kFlagFp = 1u << kBitFp;
+ static constexpr uint32_t kFlagCore = 1u << kBitCore;
+ static constexpr uint32_t kFlagRef = 1u << kBitRef;
+ static constexpr uint32_t kFlagLowWord = 1u << kBitLowWord;
+ static constexpr uint32_t kFlagHighWord = 1u << kBitHighWord;
+ static constexpr uint32_t kFlagArrayWide = 1u << kBitArrayWide;
+ static constexpr uint32_t kFlagArrayNarrow = 1u << kBitArrayNarrow;
+ static constexpr uint32_t kFlagArrayFp = 1u << kBitArrayFp;
+ static constexpr uint32_t kFlagArrayCore = 1u << kBitArrayCore;
+ static constexpr uint32_t kFlagArrayRef = 1u << kBitArrayRef;
+
+ static constexpr uint32_t kMaskWide = kFlagWide | kFlagNarrow;
+ static constexpr uint32_t kMaskType = kFlagFp | kFlagCore | kFlagRef;
+ static constexpr uint32_t kMaskWord = kFlagLowWord | kFlagHighWord;
+ static constexpr uint32_t kMaskArrayWide = kFlagArrayWide | kFlagArrayNarrow;
+ static constexpr uint32_t kMaskArrayType = kFlagArrayFp | kFlagArrayCore | kFlagArrayRef;
+ static constexpr uint32_t kMaskWideAndType = kMaskWide | kMaskType;
+ static constexpr uint32_t kMaskArrayWideAndType = kMaskArrayWide | kMaskArrayType;
+
+ static constexpr size_t kArrayTypeShift = kBitArrayWide - kBitWide;
+ static_assert(kArrayTypeShift == kBitArrayNarrow - kBitNarrow, "shift mismatch");
+ static_assert(kArrayTypeShift == kBitArrayFp - kBitFp, "shift mismatch");
+ static_assert(kArrayTypeShift == kBitArrayCore - kBitCore, "shift mismatch");
+ static_assert(kArrayTypeShift == kBitArrayRef - kBitRef, "shift mismatch");
+ static_assert((kMaskWide << kArrayTypeShift) == kMaskArrayWide, "shift mismatch");
+ static_assert((kMaskType << kArrayTypeShift) == kMaskArrayType, "shift mismatch");
+ static_assert((kMaskWideAndType << kArrayTypeShift) == kMaskArrayWideAndType, "shift mismatch");
+
+ static constexpr uint32_t kMaskArrayDepth = static_cast<uint32_t>(-1) << kBitArrayDepthStart;
+ static constexpr uint32_t kMaskNonArray = ~(kMaskArrayWideAndType | kMaskArrayDepth);
+
+ // The maximum representable array depth. If we exceed the maximum (which can happen
+ // only with an absurd nested array type in a dex file which would presumably cause
+ // OOM while being resolved), we can report false conflicts.
+ static constexpr uint32_t kMaxArrayDepth = static_cast<uint32_t>(-1) >> kBitArrayDepthStart;
+
+ explicit Type(uint32_t raw_bits) : raw_bits_(raw_bits) { }
+
+ bool IsBitSet(uint32_t flag) const {
+ return (raw_bits_ & flag) != 0u;
+ }
+
+ void SetBits(uint32_t flags) {
+ raw_bits_ |= flags;
+ }
+
+ bool MergeBits(Type src_type, uint32_t mask) {
+ uint32_t new_bits = raw_bits_ | (src_type.raw_bits_ & mask);
+ if (new_bits != raw_bits_) {
+ raw_bits_ = new_bits;
+ return true;
+ }
+ return false;
+ }
+
+ uint32_t raw_bits_;
+ };
+
+ struct MethodSignature {
+ Type return_type;
+ size_t num_params;
+ Type* param_types;
+ };
+
+ struct SplitSRegData {
+ int32_t current_mod_s_reg;
+ int32_t* starting_mod_s_reg; // Indexed by BasicBlock::id.
+ int32_t* ending_mod_s_reg; // Indexed by BasicBlock::id.
+
+ // NOTE: Before AddPseudoPhis(), def_phi_blocks_ marks the blocks
+ // with check-casts and the block with the original SSA reg.
+ // After AddPseudoPhis(), it marks blocks with pseudo-phis.
+ ArenaBitVector* def_phi_blocks_; // Indexed by BasicBlock::id.
+ };
+
+ class CheckCastData : public DeletableArenaObject<kArenaAllocMisc> {
+ public:
+ CheckCastData(MIRGraph* mir_graph, ScopedArenaAllocator* alloc);
+
+ size_t NumSRegs() const {
+ return num_sregs_;
+ }
+
+ void AddCheckCast(MIR* check_cast, Type type);
+ void AddPseudoPhis();
+ void InitializeCheckCastSRegs(Type* sregs) const;
+ void MergeCheckCastConflicts(Type* sregs) const;
+ void MarkPseudoPhiBlocks(uint64_t* bb_df_attrs) const;
+
+ void Start(BasicBlock* bb);
+ bool ProcessPseudoPhis(BasicBlock* bb, Type* sregs);
+ void ProcessCheckCast(MIR* mir);
+
+ SplitSRegData* GetSplitSRegData(int32_t s_reg);
+
+ private:
+ BasicBlock* FindDefBlock(MIR* check_cast);
+ BasicBlock* FindTopologicallyEarliestPredecessor(BasicBlock* bb);
+ bool IsSRegLiveAtStart(BasicBlock* bb, int v_reg, int32_t s_reg);
+
+ MIRGraph* const mir_graph_;
+ ScopedArenaAllocator* const alloc_;
+ const size_t num_blocks_;
+ size_t num_sregs_;
+
+ // Map check-cast mir to special sreg and type.
+ struct CheckCastMapValue {
+ int32_t modified_s_reg;
+ Type type;
+ };
+ ScopedArenaSafeMap<MIR*, CheckCastMapValue> check_cast_map_;
+ ScopedArenaSafeMap<int32_t, SplitSRegData> split_sreg_data_;
+ };
+
+ static Type FieldType(const DexFile* dex_file, uint32_t field_idx);
+ static Type* PrepareIFieldTypes(const DexFile* dex_file, MIRGraph* mir_graph,
+ ScopedArenaAllocator* alloc);
+ static Type* PrepareSFieldTypes(const DexFile* dex_file, MIRGraph* mir_graph,
+ ScopedArenaAllocator* alloc);
+ static MethodSignature Signature(const DexFile* dex_file, uint32_t method_idx, bool is_static,
+ ScopedArenaAllocator* alloc);
+ static MethodSignature* PrepareSignatures(const DexFile* dex_file, MIRGraph* mir_graph,
+ ScopedArenaAllocator* alloc);
+ static CheckCastData* InitializeCheckCastData(MIRGraph* mir_graph, ScopedArenaAllocator* alloc);
+
+ void InitializeSRegs();
+
+ int32_t ModifiedSReg(int32_t s_reg);
+ int32_t PhiInputModifiedSReg(int32_t s_reg, BasicBlock* bb, size_t pred_idx);
+
+ bool UpdateSRegFromLowWordType(int32_t mod_s_reg, Type low_word_type);
+
+ MIRGraph* const mir_graph_;
+ CompilationUnit* const cu_;
+
+ // The type inference propagates types also backwards but this must not happen across
+ // check-cast. So we need to effectively split an SSA reg into two at check-cast and
+ // keep track of the types separately.
+ std::unique_ptr<CheckCastData> check_cast_data_;
+
+ size_t num_sregs_; // Number of SSA regs or modified SSA regs, see check-cast.
+ const Type* const ifields_; // Indexed by MIR::meta::ifield_lowering_info.
+ const Type* const sfields_; // Indexed by MIR::meta::sfield_lowering_info.
+ const MethodSignature* const signatures_; // Indexed by MIR::meta::method_lowering_info.
+ const MethodSignature current_method_signature_;
+ Type* const sregs_; // Indexed by SSA reg or modified SSA reg, see check-cast.
+ uint64_t* const bb_df_attrs_; // Indexed by BasicBlock::id.
+
+ friend class TypeInferenceTest;
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_DEX_TYPE_INFERENCE_H_